1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.struts.tiles;
23
24 import java.io.IOException;
25
26 import javax.servlet.ServletException;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.struts.action.ActionServlet;
33 import org.apache.struts.action.RequestProcessor;
34 import org.apache.struts.config.ForwardConfig;
35 import org.apache.struts.config.ModuleConfig;
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
57 /**
58 * Definitions factory.
59 */
60 protected DefinitionsFactory definitionsFactory = null;
61
62 /**
63 * Commons Logging instance.
64 */
65 protected static Log log = LogFactory.getLog(TilesRequestProcessor.class);
66
67 /**
68 * Initialize this request processor instance.
69 *
70 * @param servlet The ActionServlet we are associated with.
71 * @param moduleConfig The ModuleConfig we are associated with.
72 * @throws ServletException If an error occurs during initialization.
73 */
74 public void init(ActionServlet servlet, ModuleConfig moduleConfig)
75 throws ServletException {
76
77 super.init(servlet, moduleConfig);
78 this.initDefinitionsMapping();
79 }
80
81 /**
82 * Read component instance mapping configuration file.
83 * This is where we read files properties.
84 */
85 protected void initDefinitionsMapping() throws ServletException {
86
87 definitionsFactory =
88 (
89 (TilesUtilStrutsImpl) TilesUtil
90 .getTilesUtil())
91 .getDefinitionsFactory(
92 getServletContext(),
93 moduleConfig);
94
95 if (definitionsFactory == null) {
96
97 log.info(
98 "Definition Factory not found for module '"
99 + moduleConfig.getPrefix()
100 + "'. "
101 + "Have you declared the appropriate plugin in struts-config.xml ?");
102
103 return;
104 }
105
106 log.info(
107 "Tiles definition factory found for request processor '"
108 + moduleConfig.getPrefix()
109 + "'.");
110
111 }
112
113 /**
114 * Process a Tile definition name.
115 * This method tries to process the parameter <code>definitionName</code>
116 * as a definition name.
117 * It returns <code>true</code> if a definition has been processed, or
118 * <code>false</code> otherwise.
119 * This method is deprecated; the method without the
120 * <code>contextRelative</code> parameter should be used instead.
121 *
122 * @param definitionName Definition name to insert.
123 * @param contextRelative Is the definition marked contextRelative ?
124 * @param request Current page request.
125 * @param response Current page response.
126 * @return <code>true</code> if the method has processed uri as a
127 * definition name, <code>false</code> otherwise.
128 * @deprecated use processTilesDefinition(definitionName, request, response)
129 * instead. This method will be removed in a version after 1.3.0.
130 */
131 protected boolean processTilesDefinition(
132 String definitionName,
133 boolean contextRelative,
134 HttpServletRequest request,
135 HttpServletResponse response)
136 throws IOException, ServletException {
137
138 return processTilesDefinition(definitionName, request, response);
139
140 }
141
142 /**
143 * Process a Tile definition name.
144 * This method tries to process the parameter <code>definitionName</code>
145 * as a definition name.
146 * It returns <code>true</code> if a definition has been processed, or
147 * <code>false</code> otherwise.
148 *
149 * @param definitionName Definition name to insert.
150 * @param request Current page request.
151 * @param response Current page response.
152 * @return <code>true</code> if the method has processed uri as a
153 * definition name, <code>false</code> otherwise.
154 */
155 protected boolean processTilesDefinition(
156 String definitionName,
157 HttpServletRequest request,
158 HttpServletResponse response)
159 throws IOException, ServletException {
160
161
162 boolean doInclude = false;
163
164
165 Controller controller = null;
166
167
168 String uri = null;
169
170 ComponentContext tileContext = null;
171
172 try {
173
174
175 tileContext = ComponentContext.getContext(request);
176 doInclude = (tileContext != null);
177 ComponentDefinition definition = null;
178
179
180
181 if (definitionsFactory != null) {
182
183 try {
184 definition =
185 definitionsFactory.getDefinition(
186 definitionName,
187 request,
188 getServletContext());
189 } catch (NoSuchDefinitionException ex) {
190
191 log.debug("NoSuchDefinitionException " + ex.getMessage());
192 }
193 if (definition != null) {
194
195
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
211 definition = DefinitionsUtil.getActionDefinition(request);
212 if (definition != null) {
213
214
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
244 if (uri == null) {
245 return false;
246 }
247
248
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
263
264 if (log.isDebugEnabled()) {
265 log.debug("uri=" + uri + " doInclude=" + doInclude);
266 }
267
268 if (doInclude) {
269 doInclude(uri, request, response);
270 } else {
271 doForward(uri, request, response);
272 }
273
274 return true;
275 }
276
277 /**
278 * Do a forward using request dispatcher.
279 * Uri is a valid uri. If response has already been commited, do an include
280 * instead.
281 * @param uri Uri or Definition name to forward.
282 * @param request Current page request.
283 * @param response Current page response.
284 */
285 protected void doForward(
286 String uri,
287 HttpServletRequest request,
288 HttpServletResponse response)
289 throws IOException, ServletException {
290
291 if (response.isCommitted()) {
292 this.doInclude(uri, request, response);
293
294 } else {
295 super.doForward(uri, request, response);
296 }
297 }
298
299 /**
300 * Overloaded method from Struts' RequestProcessor.
301 * Forward or redirect to the specified destination by the specified
302 * mechanism.
303 * This method catches the Struts' actionForward call. It checks if the
304 * actionForward is done on a Tiles definition name. If true, process the
305 * definition and insert it. If false, call the original parent's method.
306 * @param request The servlet request we are processing.
307 * @param response The servlet response we are creating.
308 * @param forward The ActionForward controlling where we go next.
309 *
310 * @exception IOException if an input/output error occurs.
311 * @exception ServletException if a servlet exception occurs.
312 */
313 protected void processForwardConfig(
314 HttpServletRequest request,
315 HttpServletResponse response,
316 ForwardConfig forward)
317 throws IOException, ServletException {
318
319
320 if (forward == null) {
321 return;
322 }
323
324 if (log.isDebugEnabled()) {
325 log.debug(
326 "processForwardConfig("
327 + forward.getPath()
328 + ")");
329 }
330
331
332 if (processTilesDefinition(forward.getPath(),
333 request,
334 response)) {
335 if (log.isDebugEnabled()) {
336 log.debug(
337 " '" + forward.getPath() + "' - processed as definition");
338 }
339 return;
340 }
341
342 if (log.isDebugEnabled()) {
343 log.debug(" '" + forward.getPath() + "' - processed as uri");
344 }
345
346
347 super.processForwardConfig(request, response, forward);
348 }
349
350 /**
351 * Catch the call to a module relative forward.
352 * If the specified uri is a tiles definition name, insert it.
353 * Otherwise, parent processing is called.
354 * Do a module relative forward to specified uri using request dispatcher.
355 * Uri is relative to the current module. The real uri is computed by
356 * prefixing the module name.
357 * <strong>This method is used internally and is not part of the public
358 * API. It is advised to not use it in subclasses.</strong>
359 * @param uri Module-relative URI to forward to.
360 * @param request Current page request.
361 * @param response Current page response.
362 * @since Struts 1.1
363 */
364 protected void internalModuleRelativeForward(
365 String uri,
366 HttpServletRequest request,
367 HttpServletResponse response)
368 throws IOException, ServletException {
369
370 if (processTilesDefinition(uri, request, response)) {
371 return;
372 }
373
374 super.internalModuleRelativeForward(uri, request, response);
375 }
376
377 /**
378 * Do a module relative include to specified uri using request dispatcher.
379 * Uri is relative to the current module. The real uri is computed by
380 * prefixing the module name.
381 * <strong>This method is used internally and is not part of the public
382 * API. It is advised to not use it in subclasses.</strong>
383 * @param uri Module-relative URI to forward to.
384 * @param request Current page request.
385 * @param response Current page response.
386 * @since Struts 1.1
387 */
388 protected void internalModuleRelativeInclude(
389 String uri,
390 HttpServletRequest request,
391 HttpServletResponse response)
392 throws IOException, ServletException {
393
394 if (processTilesDefinition(uri, request, response)) {
395 return;
396 }
397
398 super.internalModuleRelativeInclude(uri, request, response);
399 }
400
401 /**
402 * Get associated definition factory.
403 */
404 public DefinitionsFactory getDefinitionsFactory() {
405 return definitionsFactory;
406 }
407
408 }