Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
TilesViewHandler |
|
| 2.0;2 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to you under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | package org.apache.shale.tiles; | |
19 | ||
20 | import java.io.IOException; | |
21 | import java.text.MessageFormat; | |
22 | import java.util.Locale; | |
23 | import java.util.MissingResourceException; | |
24 | import java.util.ResourceBundle; | |
25 | ||
26 | import javax.faces.FacesException; | |
27 | import javax.faces.application.ViewHandler; | |
28 | import javax.faces.component.UIViewRoot; | |
29 | import javax.faces.context.ExternalContext; | |
30 | import javax.faces.context.FacesContext; | |
31 | ||
32 | import org.apache.commons.logging.Log; | |
33 | import org.apache.commons.logging.LogFactory; | |
34 | import org.apache.tiles.ComponentContext; | |
35 | import org.apache.tiles.ComponentDefinition; | |
36 | import org.apache.tiles.DefinitionsFactoryException; | |
37 | import org.apache.tiles.NoSuchDefinitionException; | |
38 | import org.apache.tiles.TilesRequestContext; | |
39 | import org.apache.tiles.TilesUtil; | |
40 | import org.apache.tiles.context.BasicTilesContextFactory; | |
41 | ||
42 | /** | |
43 | * This view handler strips the suffix off of the view ID and looks | |
44 | * for a tile whose name matches the resulting string. For example, if the | |
45 | * view ID is /tiles/test.jsp, this view handler will look for a tile named | |
46 | * /tiles/test. If the tile is found, it is rendered; otherwise, this view handler | |
47 | * delegates to the default JSF view handler. | |
48 | * <p/> | |
49 | * To render a tile, this view handler first locates the tile by name. Then it | |
50 | * creates or accesses the Tile Context, and stores the tile's attributes | |
51 | * in the context. Finally, it dispatches the request to the tile's layout | |
52 | * by calling JSF's <code>ExternalContext.dispatch()</code>. Layouts typically | |
53 | * contain <tiles:insert> tags that include dynamic content. | |
54 | * <p/> | |
55 | * If the request does not reference a tile, this view handler delegates | |
56 | * view rendering to the default view handler. That means that URLs like this: | |
57 | * <code>http://localhost:8080/example/index.faces</code> will work as | |
58 | * expected. | |
59 | *<p/> | |
60 | * Most of the methods in this class simply delegate to the default view | |
61 | * handler, which JSF passes to this view handler's constructor. The only | |
62 | * method that has a meaningful implementation is <code>void | |
63 | * renderView(FacesContext, UIViewRoot)</code>, which renders the current | |
64 | * view in accordance with the algorithm discussed above. | |
65 | * | |
66 | * <strong>Note:</strong> This Tiles view handler is tied to the standalone | |
67 | * version of Tiles, which resides in the Struts sandbox. This view handler | |
68 | * will not work with Struts Tiles. | |
69 | */ | |
70 | public class TilesViewHandler extends ViewHandler { | |
71 | ||
72 | ||
73 | // ------------------------------------------------------------- Constructor | |
74 | ||
75 | ||
76 | /** | |
77 | * <p>Stores the reference to the default view handler for later use.</p> | |
78 | * | |
79 | * @param defaultViewHandler The default view handler | |
80 | */ | |
81 | 0 | public TilesViewHandler(ViewHandler defaultViewHandler) { |
82 | 0 | this.defaultViewHandler = defaultViewHandler; |
83 | 0 | } |
84 | ||
85 | ||
86 | // -------------------------------------------------------- Static Variables | |
87 | ||
88 | ||
89 | /** | |
90 | * <p><code>MessageFormat</code> used to perform parameter substitution.</p> | |
91 | */ | |
92 | 0 | private MessageFormat format = new MessageFormat(""); |
93 | ||
94 | ||
95 | /** | |
96 | * <p>Log instance for this class.</p> | |
97 | */ | |
98 | 0 | private static final Log log = LogFactory.getLog( |
99 | 0 | TilesViewHandler.class.getName()); |
100 | /** | |
101 | * <p>Message resources for this class.</p> | |
102 | */ | |
103 | 0 | private static ResourceBundle bundle = |
104 | ResourceBundle.getBundle("org.apache.shale.tiles.Bundle", | |
105 | Locale.getDefault(), | |
106 | TilesViewHandler.class.getClassLoader()); | |
107 | ||
108 | /** | |
109 | * <p>The default JSF view handler.</p> | |
110 | */ | |
111 | 0 | private ViewHandler defaultViewHandler = null; |
112 | ||
113 | ||
114 | // ----------------------------------------------------- ViewHandler Methods | |
115 | ||
116 | ||
117 | /** | |
118 | * <p>Render a view according to the algorithm described in this class's | |
119 | * description: Based on the view Id of the <code>viewToRender</code>, | |
120 | * this method either renders a tile or delegates rendering to the default | |
121 | * view handler, which takes care of business as usual.</p> | |
122 | * | |
123 | * @param facesContext The faces context object for this request | |
124 | * @param viewToRender The view that we're rendering | |
125 | */ | |
126 | public void renderView(FacesContext facesContext, UIViewRoot viewToRender) | |
127 | throws IOException, FacesException { | |
128 | 0 | String viewId = viewToRender.getViewId(); |
129 | 0 | String tileName = getTileName(viewId); |
130 | 0 | ComponentDefinition tile = getTile(tileName); |
131 | ||
132 | 0 | if (log.isDebugEnabled()) { |
133 | 0 | String message = null; |
134 | try { | |
135 | 0 | message = bundle.getString("tiles.renderingView"); |
136 | 0 | } catch (MissingResourceException e) { |
137 | 0 | message = "Rendering view {0}, looking for tile {1}"; |
138 | 0 | } |
139 | 0 | synchronized(format) { |
140 | 0 | format.applyPattern(message); |
141 | 0 | message = format.format(new Object[] { viewId, tileName }); |
142 | 0 | } |
143 | 0 | log.debug(message); |
144 | } | |
145 | ||
146 | 0 | if (tile != null) { |
147 | 0 | if (log.isDebugEnabled()) { |
148 | 0 | String message = null; |
149 | try { | |
150 | 0 | message = bundle.getString("tiles.dispatchingToTile"); |
151 | 0 | } catch (MissingResourceException e) { |
152 | 0 | message = "Dispatching to tile {0}"; |
153 | 0 | } |
154 | 0 | synchronized(format) { |
155 | 0 | format.applyPattern(message); |
156 | 0 | message = format.format(new Object[] { tileName }); |
157 | 0 | } |
158 | 0 | log.debug(message); |
159 | } | |
160 | 0 | dispatchToTile(facesContext.getExternalContext(), tile); |
161 | 0 | } |
162 | else { | |
163 | 0 | if (log.isDebugEnabled()) { |
164 | 0 | String message = null; |
165 | try { | |
166 | 0 | message = bundle.getString("tiles.dispatchingToViewHandler"); |
167 | 0 | } catch (MissingResourceException e) { |
168 | 0 | message = "Dispatching {0} to the default view handler"; |
169 | 0 | } |
170 | 0 | synchronized(format) { |
171 | 0 | format.applyPattern(message); |
172 | 0 | message = format.format(new Object[] { viewId }); |
173 | 0 | } |
174 | 0 | log.debug(message); |
175 | } | |
176 | 0 | defaultViewHandler.renderView(facesContext, viewToRender); |
177 | } | |
178 | 0 | } |
179 | ||
180 | /** | |
181 | * <p>Pass through to the default view handler.</p> | |
182 | * | |
183 | */ | |
184 | public UIViewRoot createView(FacesContext context, String viewId) { | |
185 | 0 | return defaultViewHandler.createView(context, viewId); |
186 | } | |
187 | ||
188 | ||
189 | /** | |
190 | * <p>Pass through to the default view handler.</p> | |
191 | * | |
192 | */ | |
193 | public Locale calculateLocale(FacesContext context) { | |
194 | 0 | return defaultViewHandler.calculateLocale(context); |
195 | } | |
196 | ||
197 | ||
198 | /** | |
199 | * <p>Pass through to the default view handler.</p> | |
200 | * | |
201 | */ | |
202 | public String calculateRenderKitId(FacesContext context) { | |
203 | 0 | return defaultViewHandler.calculateRenderKitId(context); |
204 | } | |
205 | ||
206 | ||
207 | /** | |
208 | * <p>Pass through to the default view handler.</p> | |
209 | * | |
210 | */ | |
211 | public String getActionURL(FacesContext context, String viewId) { | |
212 | 0 | return defaultViewHandler.getActionURL(context, viewId); |
213 | } | |
214 | ||
215 | ||
216 | /** | |
217 | * <p>Pass through to the default view handler.</p> | |
218 | * | |
219 | */ | |
220 | public String getResourceURL(FacesContext context, String path) { | |
221 | 0 | return defaultViewHandler.getResourceURL(context, path); |
222 | } | |
223 | ||
224 | ||
225 | /** | |
226 | * <p>Pass through to the default view handler.</p> | |
227 | * | |
228 | */ | |
229 | public UIViewRoot restoreView(FacesContext context, String viewId) { | |
230 | 0 | return defaultViewHandler.restoreView(context, viewId); |
231 | } | |
232 | ||
233 | ||
234 | /** | |
235 | * <p>Pass through to the default view handler.</p> | |
236 | * | |
237 | */ | |
238 | public void writeState(FacesContext context) throws IOException { | |
239 | 0 | defaultViewHandler.writeState(context); |
240 | 0 | } |
241 | ||
242 | ||
243 | // --------------------------------------------------------- Private Methods | |
244 | ||
245 | ||
246 | /** | |
247 | * <p>Looks up a tile, given a name. If the tile does not exist, and the | |
248 | * <code>name</code> begins with a slash ('/'), look for a tile | |
249 | * without the slash. If no tile is found, return <code>null</code>.</p> | |
250 | * | |
251 | * @param name The tile to lookup | |
252 | */ | |
253 | private ComponentDefinition getTile(String name) { | |
254 | 0 | if (name == null) |
255 | 0 | return null; |
256 | ||
257 | 0 | ExternalContext externalContext = FacesContext.getCurrentInstance() |
258 | .getExternalContext(); | |
259 | 0 | Object request = externalContext.getRequest(); |
260 | 0 | Object context = externalContext.getContext(); |
261 | 0 | Object response = externalContext.getResponse(); |
262 | 0 | ComponentDefinition tile = null; |
263 | try { | |
264 | 0 | TilesRequestContext tilesContext = |
265 | new BasicTilesContextFactory().createRequestContext(context, | |
266 | request, response); | |
267 | 0 | tile = TilesUtil.getDefinition(name, tilesContext); |
268 | 0 | } catch (NoSuchDefinitionException nsex) { |
269 | 0 | log.error("Couldn't find Tiles definition.", nsex); |
270 | 0 | } catch (DefinitionsFactoryException dex) { |
271 | 0 | log.error("Tiles error", dex); |
272 | 0 | } |
273 | 0 | return tile; |
274 | ||
275 | } | |
276 | ||
277 | /** | |
278 | * <p>Given a view ID, returns the name of the corresponding tile. For | |
279 | * example, for a view ID of /tiles/example/main.jsp, the tile name | |
280 | * returned by this method would be /tiles/example/main.</p> | |
281 | * | |
282 | * @param viewId The view ID | |
283 | */ | |
284 | private String getTileName(String viewId) { | |
285 | 0 | int suffixIndex = viewId.lastIndexOf('.'); |
286 | 0 | return suffixIndex != -1 ? viewId.substring(0, suffixIndex) |
287 | : viewId; | |
288 | } | |
289 | ||
290 | /** | |
291 | * <p>Dispatches to a tile's layout. Layouts typically contain | |
292 | * <tiles:insert> tags that include content, so dispatching | |
293 | * to the tile's layout will automatically build the tile.</p> | |
294 | * <p> | |
295 | * Before dispatching to the tile, this method sets up the Tile | |
296 | * context.</p> | |
297 | * | |
298 | * @param externalContext The JSF external context | |
299 | * @param tile The tile definition | |
300 | */ | |
301 | private void dispatchToTile(ExternalContext externalContext, | |
302 | ComponentDefinition tile) | |
303 | throws java.io.IOException { | |
304 | 0 | Object request = externalContext.getRequest(); |
305 | 0 | Object context = externalContext.getContext(); |
306 | 0 | Object response = externalContext.getResponse(); |
307 | 0 | TilesRequestContext tilesContext = |
308 | new BasicTilesContextFactory().createRequestContext(context, | |
309 | request, response); | |
310 | 0 | ComponentContext tileContext = ComponentContext.getContext(tilesContext); |
311 | 0 | if (tileContext == null) { |
312 | 0 | tileContext = new ComponentContext(tile.getAttributes()); |
313 | 0 | ComponentContext.setContext(tileContext, tilesContext); |
314 | 0 | } |
315 | else | |
316 | 0 | tileContext.addMissing(tile.getAttributes()); |
317 | ||
318 | // dispatch to the tile's layout | |
319 | 0 | externalContext.dispatch(tile.getPath()); |
320 | 0 | } |
321 | } |