1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springbyexample.web.servlet.view.tiles2;
18
19 import javax.servlet.ServletContext;
20 import javax.servlet.http.HttpServletRequest;
21 import javax.servlet.http.HttpServletResponse;
22
23 import org.apache.tiles.Attribute;
24 import org.apache.tiles.AttributeContext;
25 import org.apache.tiles.TilesContainer;
26 import org.apache.tiles.TilesException;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.springframework.util.StringUtils;
30 import org.springframework.web.servlet.support.JstlUtils;
31 import org.springframework.web.servlet.support.RequestContext;
32 import org.springframework.web.util.WebUtils;
33
34 /**
35 * <p>Used for rendering and processing a dynamic tiles view.</p>
36 *
37 * @author David Winterfeldt
38 */
39 public class DynamicTilesViewProcessor {
40
41 final Logger logger = LoggerFactory.getLogger(DynamicTilesViewProcessor.class);
42
43 /**
44 * Keeps Tiles definition to use once derived.
45 */
46 private String derivedDefinitionName = null;
47
48 private String tilesDefinitionName = "mainTemplate";
49 private String tilesBodyAttributeName = "content";
50 private String tilesDefinitionDelimiter = ".";
51
52 /**
53 * Main template name. The default is 'mainTemplate'.
54 *
55 * @param tilesDefinitionName Main template name used to lookup definitions.
56 */
57 public void setTilesDefinitionName(String tilesDefinitionName) {
58 this.tilesDefinitionName = tilesDefinitionName;
59 }
60
61 /**
62 * Tiles body attribute name. The default is 'body'.
63 *
64 * @param tilesBodyAttributeName Tiles body attribute name.
65 */
66 public void setTilesBodyAttributeName(String tilesBodyAttributeName) {
67 this.tilesBodyAttributeName = tilesBodyAttributeName;
68 }
69
70 /**
71 * Sets Tiles definition delimiter. For example, instead of using
72 * the request 'info/about' to lookup the template definition
73 * 'info/mainTemplate', the default delimiter of '.'
74 * would look for '.info.mainTemplate'
75 *
76 * @param tilesDefinitionDelimiter Optional delimiter to replace '/' in a url.
77 */
78 public void setTilesDefinitionDelimiter(String tilesDefinitionDelimiter) {
79 this.tilesDefinitionDelimiter = tilesDefinitionDelimiter;
80 }
81
82 /**
83 * Renders output using Tiles.
84 */
85 protected void renderMergedOutputModel(String beanName, String url,
86 ServletContext servletContext,
87 HttpServletRequest request, HttpServletResponse response,
88 TilesContainer container)
89 throws Exception {
90 JstlUtils.exposeLocalizationContext(new RequestContext(request, servletContext));
91
92 if (!response.isCommitted()) {
93
94
95
96
97
98
99 if (servletContext.getMajorVersion() == 2 && servletContext.getMinorVersion() < 5) {
100 WebUtils.exposeForwardRequestAttributes(request);
101 }
102 }
103
104 String definitionName = startDynamicDefinition(beanName, url, request, response, container);
105
106 container.render(definitionName, request, response);
107
108 endDynamicDefinition(definitionName, beanName, request, response, container);
109 }
110
111 /**
112 * Starts processing the dynamic Tiles definition by creating a temporary definition for rendering.
113 */
114 protected String startDynamicDefinition(String beanName, String url,
115 HttpServletRequest request, HttpServletResponse response,
116 TilesContainer container)
117 throws TilesException {
118 String definitionName = processTilesDefinitionName(beanName, container,
119 request, response);
120
121
122
123 if (!definitionName.equals(beanName)) {
124 Attribute attr = new Attribute();
125 attr.setName(tilesBodyAttributeName);
126 attr.setValue(url);
127
128 AttributeContext attributeContext = container.startContext(request, response);
129 attributeContext.putAttribute(tilesBodyAttributeName, attr);
130
131 logger.debug("URL used for Tiles body. url='" + url + "'.");
132 }
133
134 return definitionName;
135 }
136
137 /**
138 * Closes the temporary Tiles definition.
139 */
140 protected void endDynamicDefinition(String definitionName, String beanName,
141 HttpServletRequest request, HttpServletResponse response,
142 TilesContainer container) {
143 if (!definitionName.equals(beanName)) {
144 container.endContext(request, response);
145 }
146 }
147
148 /**
149 * Processes values to get tiles template definition name. First
150 * a Tiles definition matching the url is checked, then a
151 * url specific template is checked, and then just the
152 * default root definition is used.
153 *
154 * @throws TilesException If no valid Tiles definition is found.
155 */
156 protected String processTilesDefinitionName(String beanName,
157 TilesContainer container,
158 HttpServletRequest request,
159 HttpServletResponse response)
160 throws TilesException {
161
162
163
164 if (derivedDefinitionName != null) {
165 return derivedDefinitionName;
166 } else if (container.isValidDefinition(beanName, request, response)) {
167 derivedDefinitionName = beanName;
168
169 return beanName;
170 } else {
171 String result = null;
172
173 StringBuilder sb = new StringBuilder();
174 int lastIndex = beanName.lastIndexOf("/");
175 boolean rootDefinition = false;
176
177
178 if (StringUtils.hasLength(tilesDefinitionDelimiter)) {
179 sb.append(tilesDefinitionDelimiter);
180 }
181
182
183 if (lastIndex == -1) {
184 rootDefinition = true;
185 } else {
186 String path = (beanName != null ? beanName.substring(0, lastIndex) : "");
187
188 if (StringUtils.hasLength(tilesDefinitionDelimiter)) {
189 path = StringUtils.replace(path, "/", tilesDefinitionDelimiter);
190
191 }
192
193 sb.append(path);
194
195 if (StringUtils.hasLength(tilesDefinitionDelimiter)) {
196 sb.append(tilesDefinitionDelimiter);
197 }
198 }
199
200 sb.append(tilesDefinitionName);
201
202 if (container.isValidDefinition(sb.toString(), request, response)) {
203 result = sb.toString();
204 } else if (!rootDefinition) {
205 String root = null;
206
207 if (StringUtils.hasLength(tilesDefinitionDelimiter)) {
208 root = tilesDefinitionDelimiter;
209 }
210
211 root += tilesDefinitionName;
212
213 if (container.isValidDefinition(root, request, response)) {
214 result = root;
215 } else {
216 throw new TilesException("No defintion of found for " +
217 "'" + root +"'" +
218 " or '" + sb.toString() +"'");
219 }
220 }
221
222 derivedDefinitionName = result;
223
224 return result;
225 }
226 }
227
228 }