1 /*
2 * Copyright (c) 2002-2006 by OpenSymphony
3 * All rights reserved.
4 */
5 package com.opensymphony.webwork.views.tiles;
6
7 import com.opensymphony.webwork.ServletActionContext;
8 import com.opensymphony.webwork.dispatcher.ServletDispatcherResult;
9 import com.opensymphony.xwork.ActionInvocation;
10 import com.opensymphony.xwork.LocaleProvider;
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.apache.tiles;
14
15 import javax.servlet.ServletContext;
16 import javax.servlet.ServletException;
17 import javax.servlet.http.HttpServletRequest;
18 import javax.servlet.http.HttpServletResponse;
19 import java.util.Locale;
20
21 /**
22 * <!-- START SNIPPET: description -->
23 * Renders a view using struts-tiles.
24 * <!-- END SNIPPET: description -->
25 *
26 * <!-- START SNIPPET: webxml -->
27 * In your web.xml file, you need to add a servlet entry for TilesServlet to load the tiles definitions into the ServletContext.
28 *
29 * <servlet>
30 * <servlet-name>tiles</servlet-name>
31 * <servlet-class>org.apache.tiles.servlets.TilesServlet</servlet-class>
32 * <init-param>
33 * <param-name>definitions-config</param-name>
34 * <param-value>/WEB-INF/tiles-config.xml</param-value>
35 * </init-param>
36 * <load-on-startup>1</load-on-startup>
37 * </servlet>
38 * <!-- END SNIPPET: webxml -->
39 *
40 * <!-- START SNIPPET: xworkxml -->
41 * In xwork.xml, use type="tiles" on your <result>.
42 *
43 * <action name="editUser" class="userAction" method="edit">
44 * <result name="success" type="tiles">userForm</result>
45 * <result name="input" type="tiles">userList</result>
46 * </action>
47 * <!-- END SNIPPET: xworkxml -->
48 *
49 *
50 * <!-- START SNIPPET: packageconfig -->
51 *
52 * Making this result type the default for the current package.
53 *
54 * <result-types>
55 * <result-type name="tiles" class="com.opensymphony.webwork.views.tiles.TilesResult" default="true" />
56 * </result-types>
57 * <!-- END SNIPPET: packageconfig -->
58 *
59 * @author Matt Raible
60 * @author Rainer Hermanns
61 * @version $Id: TilesResult.java 2207 2006-02-17 19:06:03Z rainerh $
62 */
63 public class TilesResult extends ServletDispatcherResult {
64
65 private static final Log log = LogFactory.getLog(TilesResult.class);
66
67 protected ActionInvocation invocation;
68 private DefinitionsFactory definitionsFactory;
69
70 /**
71 * Dispatches to the given location. Does its forward via a RequestDispatcher. If the
72 * dispatch fails a 404 error will be sent back in the http response.
73 *
74 * @param location the location to dispatch to.
75 * @param invocation the execution state of the action
76 * @throws Exception if an error occurs. If the dispatch fails the error will go back via the
77 * HTTP request.
78 */
79 public void doExecute(String location, ActionInvocation invocation) throws Exception {
80 this.location = location;
81 this.invocation = invocation;
82
83 HttpServletRequest request = ServletActionContext.getRequest();
84 HttpServletResponse response = ServletActionContext.getResponse();
85 ServletContext servletContext = ServletActionContext.getServletContext();
86
87 this.definitionsFactory =
88 (DefinitionsFactory) servletContext.getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY);
89
90 // get component definition
91 ComponentDefinition definition = getComponentDefinition(this.definitionsFactory, request);
92 if (definition == null) {
93 throw new ServletException("No Tiles definition found for name '" + location + "'");
94 }
95
96 // get current component context
97 ComponentContext context = getComponentContext(definition, request);
98 ComponentContext.setContext(context, request);
99
100 // execute component controller associated with definition, if any
101 Controller controller = getController(definition, request);
102 if (controller != null) {
103 if (log.isDebugEnabled()) {
104 log.debug("Executing Tiles controller [" + controller + "]");
105 }
106 executeController(controller, context, request, response);
107 }
108
109 // determine the path of the definition
110 String path = getDispatcherPath(definition, request);
111 if (path == null) {
112 throw new ServletException(
113 "Could not determine a path for Tiles definition '" + definition.getName() + "'");
114 }
115
116 super.doExecute(path, invocation);
117 }
118
119 protected Locale deduceLocale(HttpServletRequest request) {
120 if (invocation.getAction() instanceof LocaleProvider) {
121 return ((LocaleProvider) invocation.getAction()).getLocale();
122 } else {
123 return request.getLocale();
124 }
125 }
126
127 /**
128 * Determine the Tiles component definition for the given Tiles
129 * definitions factory.
130 *
131 * @param factory the Tiles definitions factory
132 * @param request current HTTP request
133 * @return the component definition
134 */
135 protected ComponentDefinition getComponentDefinition(DefinitionsFactory factory, HttpServletRequest request)
136 throws Exception {
137 ComponentDefinitions definitions = factory.readDefinitions();
138 return definitions.getDefinition(location, deduceLocale(request));
139 }
140
141 /**
142 * Determine the Tiles component context for the given Tiles definition.
143 *
144 * @param definition the Tiles definition to render
145 * @param request current HTTP request
146 * @return the component context
147 * @throws Exception if preparations failed
148 */
149 protected ComponentContext getComponentContext(ComponentDefinition definition, HttpServletRequest request)
150 throws Exception {
151 ComponentContext context = ComponentContext.getContext(request);
152 if (context == null) {
153 context = new ComponentContext(definition.getAttributes());
154 ComponentContext.setContext(context, request);
155 } else {
156 context.addMissing(definition.getAttributes());
157 }
158 return context;
159 }
160
161 /**
162 * Determine and initialize the Tiles component controller for the
163 * given Tiles definition, if any.
164 *
165 * @param definition the Tiles definition to render
166 * @param request current HTTP request
167 * @return the component controller to execute, or <code>null</code> if none
168 * @throws Exception if preparations failed
169 */
170 protected Controller getController(ComponentDefinition definition, HttpServletRequest request)
171 throws Exception {
172 return definition.getOrCreateController();
173 }
174
175 /**
176 * Execute the given Tiles controller.
177 *
178 * @param controller the component controller to execute
179 * @param context the component context
180 * @param request current HTTP request
181 * @param response current HTTP response
182 * @throws Exception if controller execution failed
183 */
184 protected void executeController(
185 Controller controller, ComponentContext context, HttpServletRequest request, HttpServletResponse response)
186 throws Exception {
187 controller.execute(context, request, response, ServletActionContext.getServletContext());
188 }
189
190 /**
191 * Determine the dispatcher path for the given Tiles definition,
192 * i.e. the request dispatcher path of the layout page.
193 * @param definition the Tiles definition to render
194 * @param request current HTTP request
195 * @return the path of the layout page to render
196 * @throws Exception if preparations failed
197 */
198 protected String getDispatcherPath(ComponentDefinition definition, HttpServletRequest request)
199 throws Exception {
200 Object pathAttr = null;
201 return (pathAttr != null ? pathAttr.toString() : definition.getPath());
202 }
203 }