001: /*
002: * Copyright 2002-2005 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.servlet.view.tiles;
018:
019: import javax.servlet.ServletException;
020: import javax.servlet.http.HttpServletRequest;
021: import javax.servlet.http.HttpServletResponse;
022:
023: import org.apache.struts.tiles.ComponentContext;
024: import org.apache.struts.tiles.ComponentDefinition;
025: import org.apache.struts.tiles.Controller;
026: import org.apache.struts.tiles.DefinitionsFactory;
027: import org.apache.struts.tiles.TilesUtilImpl;
028:
029: import org.springframework.context.ApplicationContextException;
030: import org.springframework.web.servlet.view.InternalResourceView;
031:
032: /**
033: * View implementation that retrieves a Tiles definition.
034: * The "url" property is interpreted as name of a Tiles definition.
035: *
036: * <p>TilesJstlView with JSTL support is a separate class,
037: * mainly to avoid JSTL dependencies in this class.
038: *
039: * <p>Depends on a Tiles DefinitionsFactory which must be available
040: * in the ServletContext. This factory is typically set up via a
041: * TilesConfigurer bean definition in the application context.
042: *
043: * <p>A component controller specified in the Tiles definition will receive
044: * a reference to the current Spring ApplicationContext if it implements
045: * ApplicationContextAware. The ComponentControllerSupport class provides
046: * a convenient base class for such Spring-aware component controllers.
047: *
048: * @author Alef Arendsen
049: * @author Juergen Hoeller
050: * @see #setUrl
051: * @see TilesJstlView
052: * @see TilesConfigurer
053: * @see ComponentControllerSupport
054: * @see org.springframework.context.ApplicationContextAware
055: */
056: public class TilesView extends InternalResourceView {
057:
058: /**
059: * Name of the attribute that will override the path of the layout page
060: * to render. A Tiles component controller can set such an attribute
061: * to dynamically switch the look and feel of a Tiles page.
062: * @see #setPath
063: */
064: public static final String PATH_ATTRIBUTE = TilesView.class
065: .getName()
066: + ".PATH";
067:
068: /**
069: * Set the path of the layout page to render.
070: * @param request current HTTP request
071: * @param path the path of the layout page
072: * @see #PATH_ATTRIBUTE
073: */
074: public static void setPath(HttpServletRequest request, String path) {
075: request.setAttribute(PATH_ATTRIBUTE, path);
076: }
077:
078: private DefinitionsFactory definitionsFactory;
079:
080: protected void initApplicationContext()
081: throws ApplicationContextException {
082: super .initApplicationContext();
083:
084: // get definitions factory
085: this .definitionsFactory = (DefinitionsFactory) getServletContext()
086: .getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY);
087: if (this .definitionsFactory == null) {
088: throw new ApplicationContextException(
089: "Tiles definitions factory not found: TilesConfigurer not defined?");
090: }
091: }
092:
093: /**
094: * Prepare for rendering the Tiles definition: Execute the associated
095: * component controller if any, and determine the request dispatcher path.
096: */
097: protected String prepareForRendering(HttpServletRequest request,
098: HttpServletResponse response) throws Exception {
099:
100: // get component definition
101: ComponentDefinition definition = getComponentDefinition(
102: this .definitionsFactory, request);
103: if (definition == null) {
104: throw new ServletException(
105: "No Tiles definition found for name '" + getUrl()
106: + "'");
107: }
108:
109: // get current component context
110: ComponentContext context = getComponentContext(definition,
111: request);
112:
113: // execute component controller associated with definition, if any
114: Controller controller = getController(definition, request);
115: if (controller != null) {
116: if (logger.isDebugEnabled()) {
117: logger.debug("Executing Tiles controller ["
118: + controller + "]");
119: }
120: executeController(controller, context, request, response);
121: }
122:
123: // determine the path of the definition
124: String path = getDispatcherPath(definition, request);
125: if (path == null) {
126: throw new ServletException(
127: "Could not determine a path for Tiles definition '"
128: + definition.getName() + "'");
129: }
130:
131: return path;
132: }
133:
134: /**
135: * Determine the Tiles component definition for the given Tiles
136: * definitions factory.
137: * @param factory the Tiles definitions factory
138: * @param request current HTTP request
139: * @return the component definition
140: */
141: protected ComponentDefinition getComponentDefinition(
142: DefinitionsFactory factory, HttpServletRequest request)
143: throws Exception {
144: return factory.getDefinition(getUrl(), request,
145: getServletContext());
146: }
147:
148: /**
149: * Determine the Tiles component context for the given Tiles definition.
150: * @param definition the Tiles definition to render
151: * @param request current HTTP request
152: * @return the component context
153: * @throws Exception if preparations failed
154: */
155: protected ComponentContext getComponentContext(
156: ComponentDefinition definition, HttpServletRequest request)
157: throws Exception {
158: ComponentContext context = ComponentContext.getContext(request);
159: if (context == null) {
160: context = new ComponentContext(definition.getAttributes());
161: ComponentContext.setContext(context, request);
162: } else {
163: context.addMissing(definition.getAttributes());
164: }
165: return context;
166: }
167:
168: /**
169: * Determine and initialize the Tiles component controller for the
170: * given Tiles definition, if any.
171: * @param definition the Tiles definition to render
172: * @param request current HTTP request
173: * @return the component controller to execute, or <code>null</code> if none
174: * @throws Exception if preparations failed
175: */
176: protected Controller getController(ComponentDefinition definition,
177: HttpServletRequest request) throws Exception {
178: return definition.getOrCreateController();
179: }
180:
181: /**
182: * Execute the given Tiles controller.
183: * @param controller the component controller to execute
184: * @param context the component context
185: * @param request current HTTP request
186: * @param response current HTTP response
187: * @throws Exception if controller execution failed
188: */
189: protected void executeController(Controller controller,
190: ComponentContext context, HttpServletRequest request,
191: HttpServletResponse response) throws Exception {
192: controller.perform(context, request, response,
193: getServletContext());
194: }
195:
196: /**
197: * Determine the dispatcher path for the given Tiles definition,
198: * i.e. the request dispatcher path of the layout page.
199: * @param definition the Tiles definition to render
200: * @param request current HTTP request
201: * @return the path of the layout page to render
202: * @throws Exception if preparations failed
203: */
204: protected String getDispatcherPath(ComponentDefinition definition,
205: HttpServletRequest request) throws Exception {
206: Object pathAttr = request.getAttribute(PATH_ATTRIBUTE);
207: return (pathAttr != null ? pathAttr.toString() : definition
208: .getPath());
209: }
210:
211: }
|