1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 * <servlet>
47 * <servlet-name>tiles</servlet-name>
48 * <servlet-class>org.apache.tiles.servlets.TilesServlet</servlet-class>
49 * <init-param>
50 * <param-name>definitions-config</param-name>
51 * <param-value>/WEB-INF/tiles-config.xml</param-value>
52 * </init-param>
53 * <load-on-startup>1</load-on-startup>
54 * </servlet>
55 * <!-- END SNIPPET: webxml -->
56 *
57 * <!-- START SNIPPET: strutsxml -->
58 * In struts.xml, use type="tiles" on your <result>.
59 *
60 * <action name="editUser" class="userAction" method="edit">
61 * <result name="success" type="tiles">userForm</result>
62 * <result name="input" type="tiles">userList</result>
63 * </action>
64 * <!-- END SNIPPET: strutsxml -->
65 *
66 *
67 * <!-- START SNIPPET: packageconfig -->
68 *
69 * Making this result type the default for the current package.
70 *
71 * <result-types>
72 * <result-type name="tiles"
73 * class="org.apache.struts2.views.tiles.TilesResult" default="true" />
74 * </result-types>
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
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
122 ComponentContext context = getComponentContext(definition, tilesContext);
123 ComponentContext.setContext(context, tilesContext);
124
125
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
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 }