View Javadoc

1   /*
2    * $Id: TilesPreProcessor.java 421151 2006-07-12 06:07:14Z wsmoak $
3    *
4    * Copyright 2004-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.struts.tiles.commands;
19  
20  import java.io.IOException;
21  
22  import javax.servlet.RequestDispatcher;
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpServletResponse;
26  
27  import org.apache.commons.chain.Command;
28  import org.apache.commons.chain.Context;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.struts.chain.contexts.ServletActionContext;
32  import org.apache.struts.config.ForwardConfig;
33  import org.apache.struts.tiles.ComponentContext;
34  import org.apache.struts.tiles.ComponentDefinition;
35  import org.apache.struts.tiles.Controller;
36  import org.apache.struts.tiles.DefinitionsUtil;
37  import org.apache.struts.tiles.FactoryNotFoundException;
38  import org.apache.struts.tiles.NoSuchDefinitionException;
39  import org.apache.struts.tiles.TilesUtil;
40  import org.apache.struts.upload.MultipartRequestWrapper;
41  
42  
43  /***
44   * <p>Command class intended to perform responsibilities of the
45   * TilesRequestProcessor in Struts 1.1.  Does not actually dispatch requests,
46   * but simply prepares the chain context for a later forward as
47   * appropriate.  Should be added to a chain before something which
48   * would handle a conventional ForwardConfig.</p>
49   *
50   * <p>This class will never have any effect on the chain unless a
51   * <code>TilesDefinitionFactory</code> can be found; however it does not
52   * consider the absence of a definition factory to be a fatal error; the
53   * command simply returns false and lets the chain continue.</p>
54   *
55   * <p>To initialize the <code>TilesDefinitionFactory</code>, use
56   * <code>org.apache.struts.chain.commands.legacy.TilesPlugin</code>.  This class
57   * is a simple extension to <code>org.apache.struts.tiles.TilesPlugin</code>
58   * which simply does not interfere with your choice of <code>RequestProcessor</code>
59   * implementation.
60   *  </p>
61   *
62   *
63   */
64  public class TilesPreProcessor implements Command
65  {
66  
67  
68      // ------------------------------------------------------ Instance Variables
69  
70  
71      private static final Log log = LogFactory.getLog(TilesPreProcessor.class);
72  
73      // ---------------------------------------------------------- Public Methods
74  
75  
76      /***
77       * <p>If the current <code>ForwardConfig</code> is using "tiles",
78       * perform necessary pre-processing to set up the <code>TilesContext</code>
79       * and substitute a new <code>ForwardConfig</code> which is understandable
80       * to a <code>RequestDispatcher</code>.</p>
81       *
82       * <p>Note that if the command finds a previously existing
83       * <code>ComponentContext</code> in the request, then it
84       * infers that it has been called from within another tile,
85       * so instead of changing the <code>ForwardConfig</code> in the chain
86       * <code>Context</code>, the command uses <code>RequestDispatcher</code>
87       * to <em>include</em> the tile, and returns true, indicating that the processing
88       * chain is complete.</p>
89       *
90       * @param context The <code>Context</code> for the current request
91       *
92       * @return <code>false</code> in most cases, but true if we determine
93       * that we're processing in "include" mode.
94       */
95      public boolean execute(Context context) throws Exception {
96  
97          // Is there a Tiles Definition to be processed?
98          ServletActionContext sacontext = (ServletActionContext) context;
99          ForwardConfig forwardConfig = sacontext.getForwardConfig();
100         if (forwardConfig == null || forwardConfig.getPath() == null)
101         {
102             log.debug("No forwardConfig or no path, so pass to next command.");
103             return (false);
104         }
105 
106 
107         ComponentDefinition definition = null;
108         try
109         {
110             definition = TilesUtil.getDefinition(forwardConfig.getPath(),
111                     sacontext.getRequest(),
112                     sacontext.getContext());
113         }
114         catch (FactoryNotFoundException ex)
115         {
116             // this is not a serious error, so log at low priority
117             log.debug("Tiles DefinitionFactory not found, so pass to next command.");
118             return false;
119         }
120         catch (NoSuchDefinitionException ex)
121         {
122             // ignore not found
123             log.debug("NoSuchDefinitionException " + ex.getMessage());
124         }
125 
126         // Do we do a forward (original behavior) or an include ?
127         boolean doInclude = false;
128         ComponentContext tileContext = null;
129 
130         // Get current tile context if any.
131         // If context exists, or if the response has already been committed we will do an include
132         tileContext = ComponentContext.getContext(sacontext.getRequest());
133         doInclude = (tileContext != null || sacontext.getResponse().isCommitted());
134 
135         // Controller associated to a definition, if any
136         Controller controller = null;
137 
138         // Computed uri to include
139         String uri = null;
140 
141         if (definition != null)
142         {
143             // We have a "forward config" definition.
144             // We use it to complete missing attribute in context.
145             // We also get uri, controller.
146             uri = definition.getPath();
147             controller = definition.getOrCreateController();
148 
149             if (tileContext == null) {
150                 tileContext =
151                         new ComponentContext(definition.getAttributes());
152                 ComponentContext.setContext(tileContext, sacontext.getRequest());
153 
154             } else {
155                 tileContext.addMissing(definition.getAttributes());
156             }
157         }
158 
159         // Process definition set in Action, if any.  This may override the
160         // values for uri or controller found using the ForwardConfig, and
161         // may augment the tileContext with additional attributes.
162         // :FIXME: the class DefinitionsUtil is deprecated, but I can't find
163         // the intended alternative to use.
164         definition = DefinitionsUtil.getActionDefinition(sacontext.getRequest());
165         if (definition != null) { // We have a definition.
166                 // We use it to complete missing attribute in context.
167                 // We also overload uri and controller if set in definition.
168                 if (definition.getPath() != null) {
169                     log.debug("Override forward uri "
170                               + uri
171                               + " with action uri "
172                               + definition.getPath());
173                         uri = definition.getPath();
174                 }
175 
176                 if (definition.getOrCreateController() != null) {
177                     log.debug("Override forward controller with action controller");
178                         controller = definition.getOrCreateController();
179                 }
180 
181                 if (tileContext == null) {
182                         tileContext =
183                                 new ComponentContext(definition.getAttributes());
184                         ComponentContext.setContext(tileContext, sacontext.getRequest());
185                 } else {
186                         tileContext.addMissing(definition.getAttributes());
187                 }
188         }
189 
190 
191         if (uri == null) {
192             log.debug("no uri computed, so pass to next command");
193             return false;
194         }
195 
196         // Execute controller associated to definition, if any.
197         if (controller != null) {
198             log.trace("Execute controller: " + controller);
199             controller.execute(
200                     tileContext,
201                     sacontext.getRequest(),
202                     sacontext.getResponse(),
203                     sacontext.getContext());
204         }
205 
206         // If request comes from a previous Tile, do an include.
207         // This allows to insert an action in a Tile.
208 
209         if (doInclude) {
210             log.info("Tiles process complete; doInclude with " + uri);
211             doInclude(sacontext, uri);
212         } else {
213             log.info("Tiles process complete; forward to " + uri);
214             doForward(sacontext, uri);
215         }
216 
217         log.debug("Tiles processed, so clearing forward config from context.");
218         sacontext.setForwardConfig( null );
219         return (false);
220     }
221 
222 
223     // ------------------------------------------------------- Protected Methods
224 
225     /***
226      * <p>Do an include of specified URI using a <code>RequestDispatcher</code>.</p>
227      *
228      * @param context a chain servlet/web context
229      * @param uri Context-relative URI to include
230      */
231     protected void doInclude(
232         ServletActionContext context,
233         String uri)
234         throws IOException, ServletException {
235 
236         RequestDispatcher rd = getRequiredDispatcher(context, uri);
237 
238         if (rd != null) {
239             rd.include(context.getRequest(), context.getResponse());
240         }
241     }
242 
243     /***
244      * <p>Do an include of specified URI using a <code>RequestDispatcher</code>.</p>
245      *
246      * @param context a chain servlet/web context
247      * @param uri Context-relative URI to include
248      */
249     protected void doForward(
250         ServletActionContext context,
251         String uri)
252         throws IOException, ServletException {
253 
254         RequestDispatcher rd = getRequiredDispatcher(context, uri);
255 
256         if (rd != null) {
257             rd.forward(context.getRequest(), context.getResponse());
258         }
259     }
260 
261     /***
262      * <p>Get the <code>RequestDispatcher</code> for the specified <code>uri</code>.  If it is not found,
263      * send a 500 error as a response and return null;
264      *
265      * @param context the current <code>ServletActionContext</code>
266      * @param uri the ServletContext-relative URI of the request dispatcher to find.
267      * @return the <code>RequestDispatcher</code>, or null if none is returned from the <code>ServletContext</code>.
268      * @throws IOException if <code>getRequestDispatcher(uri)</code> has an error.
269      */
270     private RequestDispatcher getRequiredDispatcher(ServletActionContext context, String uri) throws IOException {
271         RequestDispatcher rd = context.getContext().getRequestDispatcher(uri);
272         if (rd == null) {
273             log.debug("No request dispatcher found for " + uri);
274             HttpServletResponse response = context.getResponse();
275             response.sendError(
276                 HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
277                 "Error getting RequestDispatcher for " + uri);
278         }
279         return rd;
280     }
281 
282 }