View Javadoc

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