001: /*
002: * $Id: TilesRequestProcessor.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: package org.apache.struts.tiles;
023:
024: import java.io.IOException;
025:
026: import javax.servlet.ServletException;
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletResponse;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.apache.struts.action.ActionServlet;
033: import org.apache.struts.action.RequestProcessor;
034: import org.apache.struts.config.ForwardConfig;
035: import org.apache.struts.config.ModuleConfig;
036:
037: /**
038: * <p><strong>RequestProcessor</strong> contains the processing logic that
039: * the Struts controller servlet performs as it receives each servlet request
040: * from the container.</p>
041: * <p>This processor subclasses the Struts RequestProcessor in order to intercept calls to forward
042: * or include. When such calls are done, the Tiles processor checks if the specified URI
043: * is a definition name. If true, the definition is retrieved and included. If
044: * false, the original URI is included or a forward is performed.
045: * <p>
046: * Actually, catching is done by overloading the following methods:
047: * <ul>
048: * <li>{@link #processForwardConfig(HttpServletRequest,HttpServletResponse,ForwardConfig)}</li>
049: * <li>{@link #internalModuleRelativeForward(String, HttpServletRequest , HttpServletResponse)}</li>
050: * <li>{@link #internalModuleRelativeInclude(String, HttpServletRequest , HttpServletResponse)}</li>
051: * </ul>
052: * </p>
053: * @since Struts 1.1
054: */
055: public class TilesRequestProcessor extends RequestProcessor {
056:
057: /**
058: * Definitions factory.
059: */
060: protected DefinitionsFactory definitionsFactory = null;
061:
062: /**
063: * Commons Logging instance.
064: */
065: protected static Log log = LogFactory
066: .getLog(TilesRequestProcessor.class);
067:
068: /**
069: * Initialize this request processor instance.
070: *
071: * @param servlet The ActionServlet we are associated with.
072: * @param moduleConfig The ModuleConfig we are associated with.
073: * @throws ServletException If an error occurs during initialization.
074: */
075: public void init(ActionServlet servlet, ModuleConfig moduleConfig)
076: throws ServletException {
077:
078: super .init(servlet, moduleConfig);
079: this .initDefinitionsMapping();
080: }
081:
082: /**
083: * Read component instance mapping configuration file.
084: * This is where we read files properties.
085: */
086: protected void initDefinitionsMapping() throws ServletException {
087: // Retrieve and set factory for this modules
088: definitionsFactory = ((TilesUtilStrutsImpl) TilesUtil
089: .getTilesUtil()).getDefinitionsFactory(
090: getServletContext(), moduleConfig);
091:
092: if (definitionsFactory == null) { // problem !
093:
094: log
095: .info("Definition Factory not found for module '"
096: + moduleConfig.getPrefix()
097: + "'. "
098: + "Have you declared the appropriate plugin in struts-config.xml ?");
099:
100: return;
101: }
102:
103: log
104: .info("Tiles definition factory found for request processor '"
105: + moduleConfig.getPrefix() + "'.");
106:
107: }
108:
109: /**
110: * Process a Tile definition name.
111: * This method tries to process the parameter <code>definitionName</code>
112: * as a definition name.
113: * It returns <code>true</code> if a definition has been processed, or
114: * <code>false</code> otherwise.
115: * This method is deprecated; the method without the
116: * <code>contextRelative</code> parameter should be used instead.
117: *
118: * @param definitionName Definition name to insert.
119: * @param contextRelative Is the definition marked contextRelative ?
120: * @param request Current page request.
121: * @param response Current page response.
122: * @return <code>true</code> if the method has processed uri as a
123: * definition name, <code>false</code> otherwise.
124: * @deprecated use processTilesDefinition(definitionName, request, response)
125: * instead. This method will be removed in a version after 1.3.0.
126: */
127: protected boolean processTilesDefinition(String definitionName,
128: boolean contextRelative, HttpServletRequest request,
129: HttpServletResponse response) throws IOException,
130: ServletException {
131:
132: return processTilesDefinition(definitionName, request, response);
133:
134: }
135:
136: /**
137: * Process a Tile definition name.
138: * This method tries to process the parameter <code>definitionName</code>
139: * as a definition name.
140: * It returns <code>true</code> if a definition has been processed, or
141: * <code>false</code> otherwise.
142: *
143: * @param definitionName Definition name to insert.
144: * @param request Current page request.
145: * @param response Current page response.
146: * @return <code>true</code> if the method has processed uri as a
147: * definition name, <code>false</code> otherwise.
148: */
149: protected boolean processTilesDefinition(String definitionName,
150: HttpServletRequest request, HttpServletResponse response)
151: throws IOException, ServletException {
152:
153: // Do we do a forward (original behavior) or an include ?
154: boolean doInclude = false;
155:
156: // Controller associated to a definition, if any
157: Controller controller = null;
158:
159: // Computed uri to include
160: String uri = null;
161:
162: ComponentContext tileContext = null;
163:
164: try {
165: // Get current tile context if any.
166: // If context exist, we will do an include
167: tileContext = ComponentContext.getContext(request);
168: doInclude = (tileContext != null);
169: ComponentDefinition definition = null;
170:
171: // Process tiles definition names only if a definition factory exist,
172: // and definition is found.
173: if (definitionsFactory != null) {
174: // Get definition of tiles/component corresponding to uri.
175: try {
176: definition = definitionsFactory.getDefinition(
177: definitionName, request,
178: getServletContext());
179: } catch (NoSuchDefinitionException ex) {
180: // Ignore not found
181: log.debug("NoSuchDefinitionException "
182: + ex.getMessage());
183: }
184: if (definition != null) { // We have a definition.
185: // We use it to complete missing attribute in context.
186: // We also get uri, controller.
187: uri = definition.getPath();
188: controller = definition.getOrCreateController();
189:
190: if (tileContext == null) {
191: tileContext = new ComponentContext(definition
192: .getAttributes());
193: ComponentContext.setContext(tileContext,
194: request);
195:
196: } else {
197: tileContext.addMissing(definition
198: .getAttributes());
199: }
200: }
201: }
202:
203: // Process definition set in Action, if any.
204: definition = DefinitionsUtil.getActionDefinition(request);
205: if (definition != null) { // We have a definition.
206: // We use it to complete missing attribute in context.
207: // We also overload uri and controller if set in definition.
208: if (definition.getPath() != null) {
209: uri = definition.getPath();
210: }
211:
212: if (definition.getOrCreateController() != null) {
213: controller = definition.getOrCreateController();
214: }
215:
216: if (tileContext == null) {
217: tileContext = new ComponentContext(definition
218: .getAttributes());
219: ComponentContext.setContext(tileContext, request);
220: } else {
221: tileContext.addMissing(definition.getAttributes());
222: }
223: }
224:
225: } catch (java.lang.InstantiationException ex) {
226:
227: log.error("Can't create associated controller", ex);
228:
229: throw new ServletException(
230: "Can't create associated controller", ex);
231: } catch (DefinitionsFactoryException ex) {
232: throw new ServletException(ex);
233: }
234:
235: // Have we found a definition ?
236: if (uri == null) {
237: return false;
238: }
239:
240: // Execute controller associated to definition, if any.
241: if (controller != null) {
242: try {
243: controller.execute(tileContext, request, response,
244: getServletContext());
245:
246: } catch (Exception e) {
247: throw new ServletException(e);
248: }
249: }
250:
251: // If request comes from a previous Tile, do an include.
252: // This allows to insert an action in a Tile.
253: if (log.isDebugEnabled()) {
254: log.debug("uri=" + uri + " doInclude=" + doInclude);
255: }
256:
257: if (doInclude) {
258: doInclude(uri, request, response);
259: } else {
260: doForward(uri, request, response); // original behavior
261: }
262:
263: return true;
264: }
265:
266: /**
267: * Do a forward using request dispatcher.
268: * Uri is a valid uri. If response has already been commited, do an include
269: * instead.
270: * @param uri Uri or Definition name to forward.
271: * @param request Current page request.
272: * @param response Current page response.
273: */
274: protected void doForward(String uri, HttpServletRequest request,
275: HttpServletResponse response) throws IOException,
276: ServletException {
277:
278: if (response.isCommitted()) {
279: this .doInclude(uri, request, response);
280:
281: } else {
282: super .doForward(uri, request, response);
283: }
284: }
285:
286: /**
287: * Overloaded method from Struts' RequestProcessor.
288: * Forward or redirect to the specified destination by the specified
289: * mechanism.
290: * This method catches the Struts' actionForward call. It checks if the
291: * actionForward is done on a Tiles definition name. If true, process the
292: * definition and insert it. If false, call the original parent's method.
293: * @param request The servlet request we are processing.
294: * @param response The servlet response we are creating.
295: * @param forward The ActionForward controlling where we go next.
296: *
297: * @exception IOException if an input/output error occurs.
298: * @exception ServletException if a servlet exception occurs.
299: */
300: protected void processForwardConfig(HttpServletRequest request,
301: HttpServletResponse response, ForwardConfig forward)
302: throws IOException, ServletException {
303:
304: // Required by struts contract
305: if (forward == null) {
306: return;
307: }
308:
309: if (log.isDebugEnabled()) {
310: log
311: .debug("processForwardConfig(" + forward.getPath()
312: + ")");
313: }
314:
315: // Try to process the definition.
316: if (processTilesDefinition(forward.getPath(), request, response)) {
317: if (log.isDebugEnabled()) {
318: log.debug(" '" + forward.getPath()
319: + "' - processed as definition");
320: }
321: return;
322: }
323:
324: if (log.isDebugEnabled()) {
325: log.debug(" '" + forward.getPath()
326: + "' - processed as uri");
327: }
328:
329: // forward doesn't contain a definition, let parent do processing
330: super .processForwardConfig(request, response, forward);
331: }
332:
333: /**
334: * Catch the call to a module relative forward.
335: * If the specified uri is a tiles definition name, insert it.
336: * Otherwise, parent processing is called.
337: * Do a module relative forward to specified uri using request dispatcher.
338: * Uri is relative to the current module. The real uri is computed by
339: * prefixing the module name.
340: * <strong>This method is used internally and is not part of the public
341: * API. It is advised to not use it in subclasses.</strong>
342: * @param uri Module-relative URI to forward to.
343: * @param request Current page request.
344: * @param response Current page response.
345: * @since Struts 1.1
346: */
347: protected void internalModuleRelativeForward(String uri,
348: HttpServletRequest request, HttpServletResponse response)
349: throws IOException, ServletException {
350:
351: if (processTilesDefinition(uri, request, response)) {
352: return;
353: }
354:
355: super .internalModuleRelativeForward(uri, request, response);
356: }
357:
358: /**
359: * Do a module relative include to specified uri using request dispatcher.
360: * Uri is relative to the current module. The real uri is computed by
361: * prefixing the module name.
362: * <strong>This method is used internally and is not part of the public
363: * API. It is advised to not use it in subclasses.</strong>
364: * @param uri Module-relative URI to forward to.
365: * @param request Current page request.
366: * @param response Current page response.
367: * @since Struts 1.1
368: */
369: protected void internalModuleRelativeInclude(String uri,
370: HttpServletRequest request, HttpServletResponse response)
371: throws IOException, ServletException {
372:
373: if (processTilesDefinition(uri, request, response)) {
374: return;
375: }
376:
377: super .internalModuleRelativeInclude(uri, request, response);
378: }
379:
380: /**
381: * Get associated definition factory.
382: */
383: public DefinitionsFactory getDefinitionsFactory() {
384: return definitionsFactory;
385: }
386:
387: }
|