2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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 public TilesViewHandler(ViewHandler defaultViewHandler) {
82 this.defaultViewHandler = defaultViewHandler;
83 }
84
85
86
87
88
89 /***
90 * <p><code>MessageFormat</code> used to perform parameter substitution.</p>
91 */
92 private MessageFormat format = new MessageFormat("");
93
94
95 /***
96 * <p>Log instance for this class.</p>
97 */
98 private static final Log log = LogFactory.getLog(
99 TilesViewHandler.class.getName());
100 /***
101 * <p>Message resources for this class.</p>
102 */
103 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 private ViewHandler defaultViewHandler = null;
112
113
114
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 String viewId = viewToRender.getViewId();
129 String tileName = getTileName(viewId);
130 ComponentDefinition tile = getTile(tileName);
131
132 if (log.isDebugEnabled()) {
133 String message = null;
134 try {
135 message = bundle.getString("tiles.renderingView");
136 } catch (MissingResourceException e) {
137 message = "Rendering view {0}, looking for tile {1}";
138 }
139 synchronized(format) {
140 format.applyPattern(message);
141 message = format.format(new Object[] { viewId, tileName });
142 }
143 log.debug(message);
144 }
145
146 if (tile != null) {
147 if (log.isDebugEnabled()) {
148 String message = null;
149 try {
150 message = bundle.getString("tiles.dispatchingToTile");
151 } catch (MissingResourceException e) {
152 message = "Dispatching to tile {0}";
153 }
154 synchronized(format) {
155 format.applyPattern(message);
156 message = format.format(new Object[] { tileName });
157 }
158 log.debug(message);
159 }
160 dispatchToTile(facesContext.getExternalContext(), tile);
161 }
162 else {
163 if (log.isDebugEnabled()) {
164 String message = null;
165 try {
166 message = bundle.getString("tiles.dispatchingToViewHandler");
167 } catch (MissingResourceException e) {
168 message = "Dispatching {0} to the default view handler";
169 }
170 synchronized(format) {
171 format.applyPattern(message);
172 message = format.format(new Object[] { viewId });
173 }
174 log.debug(message);
175 }
176 defaultViewHandler.renderView(facesContext, viewToRender);
177 }
178 }
179
180 /***
181 * <p>Pass through to the default view handler.</p>
182 *
183 */
184 public UIViewRoot createView(FacesContext context, String viewId) {
185 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 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 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 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 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 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 defaultViewHandler.writeState(context);
240 }
241
242
243
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 if (name == null)
255 return null;
256
257 ExternalContext externalContext = FacesContext.getCurrentInstance()
258 .getExternalContext();
259 Object request = externalContext.getRequest();
260 Object context = externalContext.getContext();
261 Object response = externalContext.getResponse();
262 ComponentDefinition tile = null;
263 try {
264 TilesRequestContext tilesContext =
265 new BasicTilesContextFactory().createRequestContext(context,
266 request, response);
267 tile = TilesUtil.getDefinition(name, tilesContext);
268 } catch (NoSuchDefinitionException nsex) {
269 log.error("Couldn't find Tiles definition.", nsex);
270 } catch (DefinitionsFactoryException dex) {
271 log.error("Tiles error", dex);
272 }
273 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 int suffixIndex = viewId.lastIndexOf('.');
286 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 Object request = externalContext.getRequest();
305 Object context = externalContext.getContext();
306 Object response = externalContext.getResponse();
307 TilesRequestContext tilesContext =
308 new BasicTilesContextFactory().createRequestContext(context,
309 request, response);
310 ComponentContext tileContext = ComponentContext.getContext(tilesContext);
311 if (tileContext == null) {
312 tileContext = new ComponentContext(tile.getAttributes());
313 ComponentContext.setContext(tileContext, tilesContext);
314 }
315 else
316 tileContext.addMissing(tile.getAttributes());
317
318
319 externalContext.dispatch(tile.getPath());
320 }
321 }