001: /*
002: * $Id: TilesPreProcessor.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: package org.apache.struts.tiles.commands;
022:
023: import java.io.IOException;
024:
025: import javax.servlet.RequestDispatcher;
026: import javax.servlet.ServletException;
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletResponse;
029:
030: import org.apache.commons.chain.Command;
031: import org.apache.commons.chain.Context;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.apache.struts.chain.contexts.ServletActionContext;
035: import org.apache.struts.config.ForwardConfig;
036: import org.apache.struts.tiles.ComponentContext;
037: import org.apache.struts.tiles.ComponentDefinition;
038: import org.apache.struts.tiles.Controller;
039: import org.apache.struts.tiles.DefinitionsUtil;
040: import org.apache.struts.tiles.FactoryNotFoundException;
041: import org.apache.struts.tiles.NoSuchDefinitionException;
042: import org.apache.struts.tiles.TilesUtil;
043: import org.apache.struts.upload.MultipartRequestWrapper;
044:
045: /**
046: * <p>Command class intended to perform responsibilities of the
047: * TilesRequestProcessor in Struts 1.1. Does not actually dispatch requests,
048: * but simply prepares the chain context for a later forward as
049: * appropriate. Should be added to a chain before something which
050: * would handle a conventional ForwardConfig.</p>
051: *
052: * <p>This class will never have any effect on the chain unless a
053: * <code>TilesDefinitionFactory</code> can be found; however it does not
054: * consider the absence of a definition factory to be a fatal error; the
055: * command simply returns false and lets the chain continue.</p>
056: *
057: * <p>To initialize the <code>TilesDefinitionFactory</code>, use
058: * <code>org.apache.struts.chain.commands.legacy.TilesPlugin</code>. This class
059: * is a simple extension to <code>org.apache.struts.tiles.TilesPlugin</code>
060: * which simply does not interfere with your choice of <code>RequestProcessor</code>
061: * implementation.
062: * </p>
063: *
064: *
065: */
066: public class TilesPreProcessor implements Command {
067:
068: // ------------------------------------------------------ Instance Variables
069:
070: private static final Log log = LogFactory
071: .getLog(TilesPreProcessor.class);
072:
073: // ---------------------------------------------------------- Public Methods
074:
075: /**
076: * <p>If the current <code>ForwardConfig</code> is using "tiles",
077: * perform necessary pre-processing to set up the <code>TilesContext</code>
078: * and substitute a new <code>ForwardConfig</code> which is understandable
079: * to a <code>RequestDispatcher</code>.</p>
080: *
081: * <p>Note that if the command finds a previously existing
082: * <code>ComponentContext</code> in the request, then it
083: * infers that it has been called from within another tile,
084: * so instead of changing the <code>ForwardConfig</code> in the chain
085: * <code>Context</code>, the command uses <code>RequestDispatcher</code>
086: * to <em>include</em> the tile, and returns true, indicating that the processing
087: * chain is complete.</p>
088: *
089: * @param context The <code>Context</code> for the current request
090: *
091: * @return <code>false</code> in most cases, but true if we determine
092: * that we're processing in "include" mode.
093: */
094: public boolean execute(Context context) throws Exception {
095:
096: // Is there a Tiles Definition to be processed?
097: ServletActionContext sacontext = (ServletActionContext) context;
098: ForwardConfig forwardConfig = sacontext.getForwardConfig();
099: if (forwardConfig == null || forwardConfig.getPath() == null) {
100: log
101: .debug("No forwardConfig or no path, so pass to next command.");
102: return (false);
103: }
104:
105: ComponentDefinition definition = null;
106: try {
107: definition = TilesUtil.getDefinition(forwardConfig
108: .getPath(), sacontext.getRequest(), sacontext
109: .getContext());
110: } catch (FactoryNotFoundException ex) {
111: // this is not a serious error, so log at low priority
112: log
113: .debug("Tiles DefinitionFactory not found, so pass to next command.");
114: return false;
115: } catch (NoSuchDefinitionException ex) {
116: // ignore not found
117: log.debug("NoSuchDefinitionException " + ex.getMessage());
118: }
119:
120: // Do we do a forward (original behavior) or an include ?
121: boolean doInclude = false;
122: ComponentContext tileContext = null;
123:
124: // Get current tile context if any.
125: // If context exists, or if the response has already been committed we will do an include
126: tileContext = ComponentContext.getContext(sacontext
127: .getRequest());
128: doInclude = (tileContext != null || sacontext.getResponse()
129: .isCommitted());
130:
131: // Controller associated to a definition, if any
132: Controller controller = null;
133:
134: // Computed uri to include
135: String uri = null;
136:
137: if (definition != null) {
138: // We have a "forward config" definition.
139: // We use it to complete missing attribute in context.
140: // We also get uri, controller.
141: uri = definition.getPath();
142: controller = definition.getOrCreateController();
143:
144: if (tileContext == null) {
145: tileContext = new ComponentContext(definition
146: .getAttributes());
147: ComponentContext.setContext(tileContext, sacontext
148: .getRequest());
149:
150: } else {
151: tileContext.addMissing(definition.getAttributes());
152: }
153: }
154:
155: // Process definition set in Action, if any. This may override the
156: // values for uri or controller found using the ForwardConfig, and
157: // may augment the tileContext with additional attributes.
158: // :FIXME: the class DefinitionsUtil is deprecated, but I can't find
159: // the intended alternative to use.
160: definition = DefinitionsUtil.getActionDefinition(sacontext
161: .getRequest());
162: if (definition != null) { // We have a definition.
163: // We use it to complete missing attribute in context.
164: // We also overload uri and controller if set in definition.
165: if (definition.getPath() != null) {
166: log.debug("Override forward uri " + uri
167: + " with action uri " + definition.getPath());
168: uri = definition.getPath();
169: }
170:
171: if (definition.getOrCreateController() != null) {
172: log
173: .debug("Override forward controller with action controller");
174: controller = definition.getOrCreateController();
175: }
176:
177: if (tileContext == null) {
178: tileContext = new ComponentContext(definition
179: .getAttributes());
180: ComponentContext.setContext(tileContext, sacontext
181: .getRequest());
182: } else {
183: tileContext.addMissing(definition.getAttributes());
184: }
185: }
186:
187: if (uri == null) {
188: log.debug("no uri computed, so pass to next command");
189: return false;
190: }
191:
192: // Execute controller associated to definition, if any.
193: if (controller != null) {
194: log.trace("Execute controller: " + controller);
195: controller.execute(tileContext, sacontext.getRequest(),
196: sacontext.getResponse(), sacontext.getContext());
197: }
198:
199: // If request comes from a previous Tile, do an include.
200: // This allows to insert an action in a Tile.
201:
202: if (doInclude) {
203: log.info("Tiles process complete; doInclude with " + uri);
204: doInclude(sacontext, uri);
205: } else {
206: log.info("Tiles process complete; forward to " + uri);
207: doForward(sacontext, uri);
208: }
209:
210: log
211: .debug("Tiles processed, so clearing forward config from context.");
212: sacontext.setForwardConfig(null);
213: return (false);
214: }
215:
216: // ------------------------------------------------------- Protected Methods
217:
218: /**
219: * <p>Do an include of specified URI using a <code>RequestDispatcher</code>.</p>
220: *
221: * @param context a chain servlet/web context
222: * @param uri Context-relative URI to include
223: */
224: protected void doInclude(ServletActionContext context, String uri)
225: throws IOException, ServletException {
226:
227: RequestDispatcher rd = getRequiredDispatcher(context, uri);
228:
229: if (rd != null) {
230: rd.include(context.getRequest(), context.getResponse());
231: }
232: }
233:
234: /**
235: * <p>Do an include of specified URI using a <code>RequestDispatcher</code>.</p>
236: *
237: * @param context a chain servlet/web context
238: * @param uri Context-relative URI to include
239: */
240: protected void doForward(ServletActionContext context, String uri)
241: throws IOException, ServletException {
242:
243: RequestDispatcher rd = getRequiredDispatcher(context, uri);
244:
245: if (rd != null) {
246: rd.forward(context.getRequest(), context.getResponse());
247: }
248: }
249:
250: /**
251: * <p>Get the <code>RequestDispatcher</code> for the specified <code>uri</code>. If it is not found,
252: * send a 500 error as a response and return null;
253: *
254: * @param context the current <code>ServletActionContext</code>
255: * @param uri the ServletContext-relative URI of the request dispatcher to find.
256: * @return the <code>RequestDispatcher</code>, or null if none is returned from the <code>ServletContext</code>.
257: * @throws IOException if <code>getRequestDispatcher(uri)</code> has an error.
258: */
259: private RequestDispatcher getRequiredDispatcher(
260: ServletActionContext context, String uri)
261: throws IOException {
262: RequestDispatcher rd = context.getContext()
263: .getRequestDispatcher(uri);
264: if (rd == null) {
265: log.debug("No request dispatcher found for " + uri);
266: HttpServletResponse response = context.getResponse();
267: response.sendError(
268: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
269: "Error getting RequestDispatcher for " + uri);
270: }
271: return rd;
272: }
273:
274: }
|