View Javadoc

1   /*
2    * $Id: TilesResult.java 454455 2006-10-09 18:49:38Z mrdon $
3    *
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.struts2.views.tiles;
19  
20  import java.util.Locale;
21  
22  import javax.servlet.ServletContext;
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpServletResponse;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.struts2.ServletActionContext;
30  import org.apache.struts2.dispatcher.ServletDispatcherResult;
31  import org.apache.tiles.*;
32  import org.apache.tiles.context.servlet.ServletTilesContext;
33  
34  import com.opensymphony.xwork2.ActionInvocation;
35  import com.opensymphony.xwork2.LocaleProvider;
36  
37  /***
38   * <!-- START SNIPPET: description -->
39   * Renders a view using struts-tiles.
40   * <!-- END SNIPPET: description -->
41   *
42   * <!-- START SNIPPET: webxml -->
43   * In your web.xml file, you need to add a servlet entry for TilesServlet to load the tiles
44   * definitions into the ServletContext.
45   *
46   * &lt;servlet&gt;
47   *      &lt;servlet-name&gt;tiles&lt;/servlet-name&gt;
48   *      &lt;servlet-class&gt;org.apache.tiles.servlets.TilesServlet&lt;/servlet-class&gt;
49   *      &lt;init-param&gt;
50   *          &lt;param-name&gt;definitions-config&lt;/param-name&gt;
51   *          &lt;param-value&gt;/WEB-INF/tiles-config.xml&lt;/param-value&gt;
52   *      &lt;/init-param&gt;
53   *      &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
54   * &lt;/servlet&gt;
55   * <!-- END SNIPPET: webxml -->
56   *
57   * <!-- START SNIPPET: strutsxml -->
58   * In struts.xml, use type="tiles" on your &lt;result&gt;.
59   *
60   * &lt;action name="editUser" class="userAction" method="edit"&gt;
61   *      &lt;result name="success" type="tiles"&gt;userForm&lt;/result&gt;
62   *      &lt;result name="input" type="tiles"&gt;userList&lt;/result&gt;
63   * &lt;/action&gt;
64   * <!-- END SNIPPET: strutsxml -->
65   *
66   *
67   * <!-- START SNIPPET: packageconfig -->
68   *
69   * Making this result type the default for the current package.
70   *
71   * &lt;result-types&gt;
72   *      &lt;result-type name="tiles"
73   * class="org.apache.struts2.views.tiles.TilesResult" default="true" /&gt;
74   * &lt;/result-types&gt;
75   * <!-- END SNIPPET: packageconfig -->
76   *
77   */
78  public class TilesResult extends ServletDispatcherResult {
79  
80  	private static final long serialVersionUID = -3806939435493086243L;
81  
82  	private static final Log log = LogFactory.getLog(TilesResult.class);
83  
84      protected ActionInvocation invocation;
85      private DefinitionsFactory definitionsFactory;
86  
87      public TilesResult() {
88      	super();
89      }
90      
91      public TilesResult(String location) {
92      	super(location);
93      }
94      /***
95       * Dispatches to the given location. Does its forward via a RequestDispatcher. If the
96       * dispatch fails a 404 error will be sent back in the http response.
97       *
98       * @param location the location to dispatch to.
99       * @param invocation    the execution state of the action
100      * @throws Exception if an error occurs. If the dispatch fails the error will go back via the
101      *                   HTTP request.
102      */
103     public void doExecute(String location, ActionInvocation invocation) throws Exception {
104         setLocation(location);
105         this.invocation = invocation;
106 
107         HttpServletRequest request = ServletActionContext.getRequest();
108         HttpServletResponse response = ServletActionContext.getResponse();
109         ServletContext servletContext = ServletActionContext.getServletContext();
110         TilesContext tilesContext = new ServletTilesContext(servletContext, request, response);
111 
112         this.definitionsFactory =
113                 (DefinitionsFactory) servletContext.getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY);
114 
115         // get component definition
116         ComponentDefinition definition = getComponentDefinition(location, this.definitionsFactory, request);
117         if (definition == null) {
118             throw new ServletException("No Tiles definition found for name '" + location + "'");
119         }
120 
121         // get current component context
122         ComponentContext context = getComponentContext(definition, tilesContext);
123         ComponentContext.setContext(context, tilesContext);
124 
125         // execute component controller associated with definition, if any
126         Controller controller = getController(definition, request);
127         if (controller != null) {
128             if (log.isDebugEnabled()) {
129                 log.debug("Executing Tiles controller [" + controller + "]");
130             }
131             executeController(controller, context, tilesContext);
132         }
133 
134         // determine the path of the definition
135         String path = getDispatcherPath(definition, request);
136         if (path == null) {
137             throw new ServletException(
138                     "Could not determine a path for Tiles definition '" + definition.getName() + "'");
139         }
140 
141         super.doExecute(path, invocation);
142     }
143 
144     protected Locale deduceLocale(HttpServletRequest request) {
145         if (invocation.getAction() instanceof LocaleProvider) {
146             return ((LocaleProvider) invocation.getAction()).getLocale();
147         } else {
148             return request.getLocale();
149         }
150     }
151 
152     /***
153      * Determine the Tiles component definition for the given Tiles
154      * definitions factory.
155      *
156      * @param factory the Tiles definitions factory
157      * @param request current HTTP request
158      * @return the component definition
159      */
160     protected ComponentDefinition getComponentDefinition(String location, DefinitionsFactory factory, HttpServletRequest request)
161             throws Exception {
162         ComponentDefinitions definitions = factory.readDefinitions();
163         return definitions.getDefinition(location, deduceLocale(request));
164     }
165 
166     /***
167      * Determine the Tiles component context for the given Tiles definition.
168      *
169      * @param definition the Tiles definition to render
170      * @param tilesContext    current TilesContext
171      * @return the component context
172      * @throws Exception if preparations failed
173      */
174     protected ComponentContext getComponentContext(ComponentDefinition definition, TilesContext tilesContext)
175             throws Exception {
176         ComponentContext context = ComponentContext.getContext(tilesContext);
177         if (context == null) {
178             context = new ComponentContext(definition.getAttributes());
179             ComponentContext.setContext(context, tilesContext);
180         } else {
181             context.addMissing(definition.getAttributes());
182         }
183         return context;
184     }
185 
186     /***
187      * Determine and initialize the Tiles component controller for the
188      * given Tiles definition, if any.
189      *
190      * @param definition the Tiles definition to render
191      * @param request    current HTTP request
192      * @return the component controller to execute, or <code>null</code> if none
193      * @throws Exception if preparations failed
194      */
195     protected Controller getController(ComponentDefinition definition, HttpServletRequest request)
196             throws Exception {
197         return definition.getOrCreateController();
198     }
199 
200     /***
201      * Execute the given Tiles controller.
202      *
203      * @param controller the component controller to execute
204      * @param context    the component context
205      * @param tilesContext   current tilesContext
206      * @throws Exception if controller execution failed
207      */
208     protected void executeController(
209             Controller controller, ComponentContext context, TilesContext tilesContext)
210             throws Exception {
211         controller.execute(tilesContext, context);
212     }
213 
214     /***
215      * Determine the dispatcher path for the given Tiles definition,
216      * i.e. the request dispatcher path of the layout page.
217      * @param definition the Tiles definition to render
218      * @param request current HTTP request
219      * @return the path of the layout page to render
220      * @throws Exception if preparations failed
221      */
222     protected String getDispatcherPath(ComponentDefinition definition, HttpServletRequest request)
223             throws Exception {
224         Object pathAttr = null;
225         return (pathAttr != null ? pathAttr.toString() : definition.getPath());
226 	}
227 }