View Javadoc

1   /*
2    * $Id: TilesRequestProcessor.java 471754 2006-11-06 14:55:09Z husted $
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 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          // Retrieve and set factory for this modules
87          definitionsFactory =
88              (
89                  (TilesUtilStrutsImpl) TilesUtil
90                      .getTilesUtil())
91                      .getDefinitionsFactory(
92                  getServletContext(),
93                  moduleConfig);
94  
95          if (definitionsFactory == null) { // problem !
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         // 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         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); // original behavior
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         // Required by struts contract
320         if (forward == null) {
321             return;
322         }
323 
324         if (log.isDebugEnabled()) {
325             log.debug(
326                 "processForwardConfig("
327                     + forward.getPath()
328                     + ")");
329         }
330 
331         // Try to process the definition.
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         // forward doesn't contain a definition, let parent do processing
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 }