1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
72
73
74 private static final Log log = LogFactory.getLog(TilesPreProcessor.class);
75
76
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
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
120 log.debug("Tiles DefinitionFactory not found, so pass to next command.");
121 return false;
122 }
123 catch (NoSuchDefinitionException ex)
124 {
125
126 log.debug("NoSuchDefinitionException " + ex.getMessage());
127 }
128
129
130 boolean doInclude = false;
131 ComponentContext tileContext = null;
132
133
134
135 tileContext = ComponentContext.getContext(sacontext.getRequest());
136 doInclude = (tileContext != null || sacontext.getResponse().isCommitted());
137
138
139 Controller controller = null;
140
141
142 String uri = null;
143
144 if (definition != null)
145 {
146
147
148
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
163
164
165
166
167 definition = DefinitionsUtil.getActionDefinition(sacontext.getRequest());
168 if (definition != null) {
169
170
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
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
210
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
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 }