View Javadoc
1   /*
2    * $Id$
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  
22  package org.apache.struts.tiles2;
23  
24  import java.io.IOException;
25  
26  import org.apache.struts.action.ActionServlet;
27  import org.apache.struts.action.RequestProcessor;
28  import org.apache.struts.config.ForwardConfig;
29  import org.apache.struts.config.ModuleConfig;
30  import org.apache.tiles.TilesContainer;
31  import org.apache.tiles.TilesException;
32  import org.apache.tiles.access.TilesAccess;
33  import org.apache.tiles.request.ApplicationContext;
34  import org.apache.tiles.request.Request;
35  import org.apache.tiles.request.jakarta.servlet.ServletRequest;
36  import org.apache.tiles.request.jakarta.servlet.ServletUtil;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  import jakarta.servlet.ServletContext;
41  import jakarta.servlet.ServletException;
42  import jakarta.servlet.http.HttpServletRequest;
43  import jakarta.servlet.http.HttpServletResponse;
44  
45  /**
46   * <p><strong>RequestProcessor</strong> contains the processing logic that
47   * the Struts controller servlet performs as it receives each servlet request
48   * from the container.</p>
49   * <p>This processor subclasses the Struts RequestProcessor in order to intercept calls to forward
50   * or include. When such calls are done, the Tiles processor checks if the specified URI
51   * is a definition name. If true, the definition is retrieved and included. If
52   * false, the original URI is included or a forward is performed.
53   * <p>
54   * Actually, catching is done by overloading the following methods:
55   * <ul>
56   * <li>{@link #processForwardConfig(HttpServletRequest,HttpServletResponse,ForwardConfig)}</li>
57   * <li>{@link #internalModuleRelativeForward(String, HttpServletRequest , HttpServletResponse)}</li>
58   * <li>{@link #internalModuleRelativeInclude(String, HttpServletRequest , HttpServletResponse)}</li>
59   * </ul>
60   * </p>
61   * @since Struts 1.1
62   */
63  public class TilesRequestProcessor extends RequestProcessor {
64      private static final long serialVersionUID = 6212184689866961850L;
65  
66      /**
67       * The {@code Log} instance for this class.
68       */
69      private transient final Logger log =
70          LoggerFactory.getLogger(TilesRequestProcessor.class);
71  
72      /**
73       * The used servlet context.
74       */
75      protected ServletContext servletContext;
76  
77      /**
78       * Initialize this request processor instance.
79       *
80       * @param servlet The ActionServlet we are associated with.
81       * @param moduleConfig The ModuleConfig we are associated with.
82       * @throws ServletException If an error occurs during initialization.
83       */
84      public void init(ActionServlet servlet, ModuleConfig moduleConfig)
85          throws ServletException {
86  
87          super.init(servlet, moduleConfig);
88      }
89  
90      /**
91       * Process a Tile definition name.
92       * This method tries to process the parameter <code>definitionName</code>
93       * as a definition name.
94       * It returns <code>true</code> if a definition has been processed, or
95       * <code>false</code> otherwise.
96       *
97       * @param definitionName Definition name to insert.
98       * @param req Current page request.
99       * @param res Current page response.
100      * @throws IOException If something goes wrong during writing the
101      * definition.
102      * @throws ServletException If something goes wrong during the evaluation
103      * of the definition
104      * @return <code>true</code> if the method has processed uri as a
105      * definition name, <code>false</code> otherwise.
106      */
107     protected boolean processTilesDefinition(
108         String definitionName,
109         HttpServletRequest req,
110         HttpServletResponse res)
111         throws IOException, ServletException {
112 
113         ApplicationContext applicationContext = ServletUtil
114                 .getApplicationContext(getServletContext());
115         Request request = new ServletRequest(applicationContext,
116                 req, res);
117         TilesContainer container = TilesAccess.getContainer(applicationContext);
118         if (container == null) {
119             log.debug("Tiles container not found, so pass to next command.");
120             return false;
121         }
122 
123         boolean retValue = false;
124 
125         if (container.isValidDefinition(definitionName, request)) {
126             retValue = true;
127             try {
128                 container.render(definitionName, request);
129             } catch (TilesException e) {
130                 throw new ServletException("Cannot render definition '"
131                         + definitionName + "'");
132             }
133         } else {
134             // ignore not found
135             log.debug("Cannot find definition '{}'", definitionName);
136         }
137 
138         return retValue;
139     }
140 
141     /**
142      * Do a forward using request dispatcher.
143      * Uri is a valid uri. If response has already been commited, do an include
144      * instead.
145      * @param uri Uri or Definition name to forward.
146      * @param request Current page request.
147      * @param response Current page response.
148      * @throws IOException If something goes wrong during writing the
149      * definition.
150      * @throws ServletException If something goes wrong during the evaluation
151      * of the definition
152      */
153     protected void doForward(
154         String uri,
155         HttpServletRequest request,
156         HttpServletResponse response)
157         throws IOException, ServletException {
158 
159         if (response.isCommitted()) {
160             this.doInclude(uri, request, response);
161 
162         } else {
163             super.doForward(uri, request, response);
164         }
165     }
166 
167     /**
168      * Overloaded method from Struts' RequestProcessor.
169      * Forward or redirect to the specified destination by the specified
170      * mechanism.
171      * This method catches the Struts' actionForward call. It checks if the
172      * actionForward is done on a Tiles definition name. If true, process the
173      * definition and insert it. If false, call the original parent's method.
174      * @param request The servlet request we are processing.
175      * @param response The servlet response we are creating.
176      * @param forward The ActionForward controlling where we go next.
177      *
178      * @exception IOException if an input/output error occurs.
179      * @exception ServletException if a servlet exception occurs.
180      */
181     protected void processForwardConfig(
182         HttpServletRequest request,
183         HttpServletResponse response,
184         ForwardConfig forward)
185         throws IOException, ServletException {
186 
187         // Required by struts contract
188         if (forward == null) {
189             return;
190         }
191 
192         log.debug("processForwardConfig({})", forward.getPath());
193 
194         // Try to process the definition.
195         if (processTilesDefinition(forward.getPath(),
196                 request, response)) {
197             log.debug("  '{}' - processed as definition", forward.getPath());
198             return;
199         }
200 
201         log.debug("  '{}' - processed as uri", forward.getPath());
202 
203         // forward doesn't contain a definition, let parent do processing
204         super.processForwardConfig(request, response, forward);
205     }
206 
207     /**
208      * Catch the call to a module relative forward.
209      * If the specified uri is a tiles definition name, insert it.
210      * Otherwise, parent processing is called.
211      * Do a module relative forward to specified uri using request dispatcher.
212      * Uri is relative to the current module. The real uri is computed by
213      * prefixing the module name.
214      * <strong>This method is used internally and is not part of the public
215      * API. It is advised to not use it in subclasses.</strong>
216      * @param uri Module-relative URI to forward to.
217      * @param request Current page request.
218      * @param response Current page response.
219      * @throws IOException If something goes wrong during writing the
220      * definition.
221      * @throws ServletException If something goes wrong during the evaluation
222      * of the definition
223      * @since Struts 1.1
224      */
225     protected void internalModuleRelativeForward(
226         String uri,
227         HttpServletRequest request,
228         HttpServletResponse response)
229         throws IOException, ServletException {
230 
231         if (processTilesDefinition(uri, request, response)) {
232             return;
233         }
234 
235         super.internalModuleRelativeForward(uri, request, response);
236     }
237 
238     /**
239      * Do a module relative include to specified uri using request dispatcher.
240      * Uri is relative to the current module. The real uri is computed by
241      * prefixing the module name.
242      * <strong>This method is used internally and is not part of the public
243      * API. It is advised to not use it in subclasses.</strong>
244      * @param uri Module-relative URI to forward to.
245      * @param request Current page request.
246      * @param response Current page response.
247      * @throws IOException If something goes wrong during writing the
248      * definition.
249      * @throws ServletException If something goes wrong during the evaluation
250      * of the definition
251      * @since Struts 1.1
252      */
253     protected void internalModuleRelativeInclude(
254         String uri,
255         HttpServletRequest request,
256         HttpServletResponse response)
257         throws IOException, ServletException {
258 
259         if (processTilesDefinition(uri, request, response)) {
260             return;
261         }
262 
263         super.internalModuleRelativeInclude(uri, request, response);
264     }
265 }