1 /*
2 * $Id$
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 package org.apache.struts.tiles;
23
24 import java.io.IOException;
25
26 import jakarta.servlet.ServletException;
27 import jakarta.servlet.http.HttpServletRequest;
28 import jakarta.servlet.http.HttpServletResponse;
29
30 import org.apache.struts.action.ActionServlet;
31 import org.apache.struts.action.RequestProcessor;
32 import org.apache.struts.config.ForwardConfig;
33 import org.apache.struts.config.ModuleConfig;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38 * <p><strong>RequestProcessor</strong> contains the processing logic that
39 * the Struts controller servlet performs as it receives each servlet request
40 * from the container.</p>
41 * <p>This processor subclasses the Struts RequestProcessor in order to intercept calls to forward
42 * or include. When such calls are done, the Tiles processor checks if the specified URI
43 * is a definition name. If true, the definition is retrieved and included. If
44 * false, the original URI is included or a forward is performed.
45 * <p>
46 * Actually, catching is done by overloading the following methods:
47 * <ul>
48 * <li>{@link #processForwardConfig(HttpServletRequest,HttpServletResponse,ForwardConfig)}</li>
49 * <li>{@link #internalModuleRelativeForward(String, HttpServletRequest , HttpServletResponse)}</li>
50 * <li>{@link #internalModuleRelativeInclude(String, HttpServletRequest , HttpServletResponse)}</li>
51 * </ul>
52 * </p>
53 * @since Struts 1.1
54 */
55 public class TilesRequestProcessor extends RequestProcessor {
56 private static final long serialVersionUID = -6522610348048179731L;
57
58 /**
59 * Definitions factory.
60 */
61 protected DefinitionsFactory definitionsFactory = null;
62
63 /**
64 * The {@code Log} instance for this class.
65 */
66 private transient final Logger log =
67 LoggerFactory.getLogger(TilesRequestProcessor.class);
68
69 /**
70 * Initialize this request processor instance.
71 *
72 * @param servlet The ActionServlet we are associated with.
73 * @param moduleConfig The ModuleConfig we are associated with.
74 * @throws ServletException If an error occurs during initialization.
75 */
76 public void init(ActionServlet servlet, ModuleConfig moduleConfig)
77 throws ServletException {
78
79 super.init(servlet, moduleConfig);
80 this.initDefinitionsMapping();
81 }
82
83 /**
84 * Read component instance mapping configuration file.
85 * This is where we read files properties.
86 */
87 protected void initDefinitionsMapping() throws ServletException {
88 // Retrieve and set factory for this modules
89 definitionsFactory =
90 (
91 (TilesUtilStrutsImpl) TilesUtil
92 .getTilesUtil())
93 .getDefinitionsFactory(
94 getServletContext(),
95 moduleConfig);
96
97 if (definitionsFactory == null) { // problem !
98
99 log.info("Definition Factory not found for module '{}'. "
100 + "Have you declared the appropriate plugin in struts-config.xml ?",
101 moduleConfig.getPrefix());
102
103 return;
104 }
105
106 log.info("Tiles definition factory found for request processor '{}'.",
107 moduleConfig.getPrefix());
108
109 }
110
111 /**
112 * Process a Tile definition name.
113 * This method tries to process the parameter <code>definitionName</code>
114 * as a definition name.
115 * It returns <code>true</code> if a definition has been processed, or
116 * <code>false</code> otherwise.
117 * This method is deprecated; the method without the
118 * <code>contextRelative</code> parameter should be used instead.
119 *
120 * @param definitionName Definition name to insert.
121 * @param contextRelative Is the definition marked contextRelative ?
122 * @param request Current page request.
123 * @param response Current page response.
124 * @return <code>true</code> if the method has processed uri as a
125 * definition name, <code>false</code> otherwise.
126 * @deprecated use processTilesDefinition(definitionName, request, response)
127 * instead. This method will be removed in a version after 1.3.0.
128 */
129 @Deprecated
130 protected boolean processTilesDefinition(
131 String definitionName,
132 boolean contextRelative,
133 HttpServletRequest request,
134 HttpServletResponse response)
135 throws IOException, ServletException {
136
137 return processTilesDefinition(definitionName, request, response);
138
139 }
140
141 /**
142 * Process a Tile definition name.
143 * This method tries to process the parameter <code>definitionName</code>
144 * as a definition name.
145 * It returns <code>true</code> if a definition has been processed, or
146 * <code>false</code> otherwise.
147 *
148 * @param definitionName Definition name to insert.
149 * @param request Current page request.
150 * @param response Current page response.
151 * @return <code>true</code> if the method has processed uri as a
152 * definition name, <code>false</code> otherwise.
153 */
154 @SuppressWarnings("deprecation")
155 protected boolean processTilesDefinition(
156 String definitionName,
157 HttpServletRequest request,
158 HttpServletResponse response)
159 throws IOException, ServletException {
160
161 // Do we do a forward (original behavior) or an include ?
162 boolean doInclude = false;
163
164 // Controller associated to a definition, if any
165 Controller controller = null;
166
167 // Computed uri to include
168 String uri = null;
169
170 ComponentContext tileContext = null;
171
172 try {
173 // Get current tile context if any.
174 // If context exist, we will do an include
175 tileContext = ComponentContext.getContext(request);
176 doInclude = (tileContext != null);
177 ComponentDefinition definition = null;
178
179 // Process tiles definition names only if a definition factory exist,
180 // and definition is found.
181 if (definitionsFactory != null) {
182 // Get definition of tiles/component corresponding to uri.
183 try {
184 definition =
185 definitionsFactory.getDefinition(
186 definitionName,
187 request,
188 getServletContext());
189 } catch (NoSuchDefinitionException ex) {
190 // Ignore not found
191 log.debug("NoSuchDefinitionException {}", ex.getMessage());
192 }
193 if (definition != null) { // We have a definition.
194 // We use it to complete missing attribute in context.
195 // We also get uri, controller.
196 uri = definition.getPath();
197 controller = definition.getOrCreateController();
198
199 if (tileContext == null) {
200 tileContext =
201 new ComponentContext(definition.getAttributes());
202 ComponentContext.setContext(tileContext, request);
203
204 } else {
205 tileContext.addMissing(definition.getAttributes());
206 }
207 }
208 }
209
210 // Process definition set in Action, if any.
211 definition = DefinitionsUtil.getActionDefinition(request);
212 if (definition != null) { // We have a definition.
213 // We use it to complete missing attribute in context.
214 // We also overload uri and controller if set in definition.
215 if (definition.getPath() != null) {
216 uri = definition.getPath();
217 }
218
219 if (definition.getOrCreateController() != null) {
220 controller = definition.getOrCreateController();
221 }
222
223 if (tileContext == null) {
224 tileContext =
225 new ComponentContext(definition.getAttributes());
226 ComponentContext.setContext(tileContext, request);
227 } else {
228 tileContext.addMissing(definition.getAttributes());
229 }
230 }
231
232 } catch (java.lang.InstantiationException ex) {
233
234 log.error("Can't create associated controller", ex);
235
236 throw new ServletException(
237 "Can't create associated controller",
238 ex);
239 } catch (DefinitionsFactoryException ex) {
240 throw new ServletException(ex);
241 }
242
243 // Have we found a definition ?
244 if (uri == null) {
245 return false;
246 }
247
248 // Execute controller associated to definition, if any.
249 if (controller != null) {
250 try {
251 controller.execute(
252 tileContext,
253 request,
254 response,
255 getServletContext());
256
257 } catch (Exception e) {
258 throw new ServletException(e);
259 }
260 }
261
262 // If request comes from a previous Tile, do an include.
263 // This allows to insert an action in a Tile.
264 log.debug("uri={} doInclude={}" , uri, doInclude);
265
266 if (doInclude) {
267 doInclude(uri, request, response);
268 } else {
269 doForward(uri, request, response); // original behavior
270 }
271
272 return true;
273 }
274
275 /**
276 * Do a forward using request dispatcher.
277 * Uri is a valid uri. If response has already been commited, do an include
278 * instead.
279 * @param uri Uri or Definition name to forward.
280 * @param request Current page request.
281 * @param response Current page response.
282 */
283 protected void doForward(
284 String uri,
285 HttpServletRequest request,
286 HttpServletResponse response)
287 throws IOException, ServletException {
288
289 if (response.isCommitted()) {
290 this.doInclude(uri, request, response);
291
292 } else {
293 super.doForward(uri, request, response);
294 }
295 }
296
297 /**
298 * Overloaded method from Struts' RequestProcessor.
299 * Forward or redirect to the specified destination by the specified
300 * mechanism.
301 * This method catches the Struts' actionForward call. It checks if the
302 * actionForward is done on a Tiles definition name. If true, process the
303 * definition and insert it. If false, call the original parent's method.
304 * @param request The servlet request we are processing.
305 * @param response The servlet response we are creating.
306 * @param forward The ActionForward controlling where we go next.
307 *
308 * @exception IOException if an input/output error occurs.
309 * @exception ServletException if a servlet exception occurs.
310 */
311 protected void processForwardConfig(
312 HttpServletRequest request,
313 HttpServletResponse response,
314 ForwardConfig forward)
315 throws IOException, ServletException {
316
317 // Required by struts contract
318 if (forward == null) {
319 return;
320 }
321
322 log.debug("processForwardConfig({})", forward.getPath());
323
324 // Try to process the definition.
325 if (processTilesDefinition(forward.getPath(),
326 request, response)) {
327 log.debug(" '{}' - processed as definition", forward.getPath());
328 return;
329 }
330
331 log.debug(" '{}' - processed as uri", forward.getPath());
332
333 // forward doesn't contain a definition, let parent do processing
334 super.processForwardConfig(request, response, forward);
335 }
336
337 /**
338 * Catch the call to a module relative forward.
339 * If the specified uri is a tiles definition name, insert it.
340 * Otherwise, parent processing is called.
341 * Do a module relative forward to specified uri using request dispatcher.
342 * Uri is relative to the current module. The real uri is computed by
343 * prefixing the module name.
344 * <strong>This method is used internally and is not part of the public
345 * API. It is advised to not use it in subclasses.</strong>
346 * @param uri Module-relative URI to forward to.
347 * @param request Current page request.
348 * @param response Current page response.
349 * @since Struts 1.1
350 */
351 protected void internalModuleRelativeForward(
352 String uri,
353 HttpServletRequest request,
354 HttpServletResponse response)
355 throws IOException, ServletException {
356
357 if (processTilesDefinition(uri, request, response)) {
358 return;
359 }
360
361 super.internalModuleRelativeForward(uri, request, response);
362 }
363
364 /**
365 * Do a module relative include to specified uri using request dispatcher.
366 * Uri is relative to the current module. The real uri is computed by
367 * prefixing the module name.
368 * <strong>This method is used internally and is not part of the public
369 * API. It is advised to not use it in subclasses.</strong>
370 * @param uri Module-relative URI to forward to.
371 * @param request Current page request.
372 * @param response Current page response.
373 * @since Struts 1.1
374 */
375 protected void internalModuleRelativeInclude(
376 String uri,
377 HttpServletRequest request,
378 HttpServletResponse response)
379 throws IOException, ServletException {
380
381 if (processTilesDefinition(uri, request, response)) {
382 return;
383 }
384
385 super.internalModuleRelativeInclude(uri, request, response);
386 }
387
388 /**
389 * Get associated definition factory.
390 */
391 public DefinitionsFactory getDefinitionsFactory() {
392 return definitionsFactory;
393 }
394 }