1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.web.servlet.view.tiles;
  17. import javax.servlet.ServletException;
  18. import javax.servlet.http.HttpServletRequest;
  19. import javax.servlet.http.HttpServletResponse;
  20. import org.apache.struts.tiles.ComponentContext;
  21. import org.apache.struts.tiles.ComponentDefinition;
  22. import org.apache.struts.tiles.Controller;
  23. import org.apache.struts.tiles.DefinitionsFactory;
  24. import org.apache.struts.tiles.TilesUtilImpl;
  25. import org.springframework.context.ApplicationContextException;
  26. import org.springframework.web.servlet.view.InternalResourceView;
  27. /**
  28. * View implementation that retrieves a Tiles definition.
  29. * The "url" property is interpreted as name of a Tiles definition.
  30. *
  31. * <p>TilesJstlView with JSTL support is a separate class,
  32. * mainly to avoid JSTL dependencies in this class.
  33. *
  34. * <p>Depends on a Tiles DefinitionsFactory which must be available
  35. * in the ServletContext. This factory is typically set up via a
  36. * TilesConfigurer bean definition in the application context.
  37. *
  38. * <p>A component controller specified in the Tiles definition will receive
  39. * a reference to the current Spring ApplicationContext if it implements
  40. * ApplicationContextAware. The ComponentControllerSupport class provides
  41. * a convenient base class for such Spring-aware component controllers.
  42. *
  43. * @author Alef Arendsen
  44. * @author Juergen Hoeller
  45. * @see #setUrl
  46. * @see TilesJstlView
  47. * @see TilesConfigurer
  48. * @see ComponentControllerSupport
  49. * @see org.springframework.context.ApplicationContextAware
  50. */
  51. public class TilesView extends InternalResourceView {
  52. /**
  53. * Name of the attribute that will override the path of the layout page
  54. * to render. A Tiles component controller can set such an attribute
  55. * to dynamically switch the look and feel of a Tiles page.
  56. * @see #setPath
  57. */
  58. public static final String PATH_ATTRIBUTE = TilesView.class.getName() + ".PATH";
  59. /**
  60. * Set the path of the layout page to render.
  61. * @param request current HTTP request
  62. * @param path the path of the layout page
  63. * @see #PATH_ATTRIBUTE
  64. */
  65. public static void setPath(HttpServletRequest request, String path) {
  66. request.setAttribute(PATH_ATTRIBUTE, path);
  67. }
  68. private DefinitionsFactory definitionsFactory;
  69. protected void initApplicationContext() throws ApplicationContextException {
  70. super.initApplicationContext();
  71. // get definitions factory
  72. this.definitionsFactory = (DefinitionsFactory) getServletContext().getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY);
  73. if (this.definitionsFactory == null) {
  74. throw new ApplicationContextException("Tiles definitions factory not found: TilesConfigurer not defined?");
  75. }
  76. }
  77. /**
  78. * Prepare for rendering the Tiles definition: Execute the associated
  79. * component controller if any, and determine the request dispatcher path.
  80. */
  81. protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
  82. throws Exception {
  83. // get component definition
  84. ComponentDefinition definition = getComponentDefinition(this.definitionsFactory, request);
  85. if (definition == null) {
  86. throw new ServletException("No Tiles definition found for name '" + getUrl() + "'");
  87. }
  88. // get current component context
  89. ComponentContext context = getComponentContext(definition, request);
  90. // execute component controller associated with definition, if any
  91. Controller controller = getController(definition, request);
  92. if (controller != null) {
  93. if (logger.isDebugEnabled()) {
  94. logger.debug("Executing Tiles controller [" + controller + "]");
  95. }
  96. executeController(controller, context, request, response);
  97. }
  98. // determine the path of the definition
  99. String path = getDispatcherPath(definition, request);
  100. if (path == null) {
  101. throw new ServletException("Could not determine a path for Tiles definition '" +
  102. definition.getName() + "'");
  103. }
  104. return path;
  105. }
  106. /**
  107. * Determine the Tiles component definition for the given Tiles
  108. * definitions factory.
  109. * @param factory the Tiles definitions factory
  110. * @param request current HTTP request
  111. * @return the component definition
  112. */
  113. protected ComponentDefinition getComponentDefinition(DefinitionsFactory factory, HttpServletRequest request)
  114. throws Exception {
  115. return factory.getDefinition(getUrl(), request, getServletContext());
  116. }
  117. /**
  118. * Determine the Tiles component context for the given Tiles definition.
  119. * @param definition the Tiles definition to render
  120. * @param request current HTTP request
  121. * @return the component context
  122. * @throws Exception if preparations failed
  123. */
  124. protected ComponentContext getComponentContext(ComponentDefinition definition, HttpServletRequest request)
  125. throws Exception {
  126. ComponentContext context = ComponentContext.getContext(request);
  127. if (context == null) {
  128. context = new ComponentContext(definition.getAttributes());
  129. ComponentContext.setContext(context, request);
  130. }
  131. else {
  132. context.addMissing(definition.getAttributes());
  133. }
  134. return context;
  135. }
  136. /**
  137. * Determine and initialize the Tiles component controller for the
  138. * given Tiles definition, if any.
  139. * @param definition the Tiles definition to render
  140. * @param request current HTTP request
  141. * @return the component controller to execute, or null if none
  142. * @throws Exception if preparations failed
  143. */
  144. protected Controller getController(ComponentDefinition definition, HttpServletRequest request)
  145. throws Exception {
  146. return definition.getOrCreateController();
  147. }
  148. /**
  149. * Execute the given Tiles controller.
  150. * @param controller the component controller to execute
  151. * @param context the component context
  152. * @param request current HTTP request
  153. * @param response current HTTP response
  154. * @throws Exception if controller execution failed
  155. */
  156. protected void executeController(Controller controller, ComponentContext context,
  157. HttpServletRequest request, HttpServletResponse response)
  158. throws Exception {
  159. controller.perform(context, request, response, getServletContext());
  160. }
  161. /**
  162. * Determine the dispatcher path for the given Tiles definition,
  163. * i.e. the request dispatcher path of the layout page.
  164. * @param definition the Tiles definition to render
  165. * @param request current HTTP request
  166. * @return the path of the layout page to render
  167. * @throws Exception if preparations failed
  168. */
  169. protected String getDispatcherPath(ComponentDefinition definition, HttpServletRequest request)
  170. throws Exception {
  171. Object pathAttr = request.getAttribute(PATH_ATTRIBUTE);
  172. return (pathAttr != null ? pathAttr.toString() : definition.getPath());
  173. }
  174. }