KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > tools > view > servlet > VelocityLayoutServlet


1 /*
2  * Copyright 2003 The Apache Software Foundation.
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
17 package org.apache.velocity.tools.view.servlet;
18
19
20 import javax.servlet.ServletConfig JavaDoc;
21 import javax.servlet.ServletException JavaDoc;
22 import javax.servlet.http.HttpServletRequest JavaDoc;
23 import javax.servlet.http.HttpServletResponse JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.StringWriter JavaDoc;
26 import java.io.UnsupportedEncodingException JavaDoc;
27
28 import org.apache.velocity.Template;
29 import org.apache.velocity.app.Velocity;
30 import org.apache.velocity.context.Context;
31 import org.apache.velocity.exception.MethodInvocationException;
32 import org.apache.velocity.exception.ParseErrorException;
33 import org.apache.velocity.exception.ResourceNotFoundException;
34 import org.apache.velocity.runtime.RuntimeSingleton;
35 import org.apache.velocity.tools.view.servlet.VelocityViewServlet;
36
37
38 /**
39  * Extension of the VelocityViewServlet to perform "two-pass"
40  * layout rendering and allow for a customized error screen.
41  *
42  * For a brief user-guide to this, i suggest trying to track down
43  * the VLS_README.txt file that hopefully made it into the docs
44  * somewhere.
45  *
46  * @author <a HREF="mailto:nathan@esha.com">Nathan Bubna</a>
47  * @version $Id: VelocityLayoutServlet.java,v 1.7 2004/02/18 20:07:02 nbubna Exp $
48  */

49
50 public class VelocityLayoutServlet extends VelocityViewServlet
51 {
52
53     /**
54      * The velocity.properties key for specifying the
55      * servlet's error template.
56      */

57     public static final String JavaDoc PROPERTY_ERROR_TEMPLATE =
58         "tools.view.servlet.error.template";
59
60     /**
61      * The velocity.properties key for specifying the
62      * relative directory holding layout templates.
63      */

64     public static final String JavaDoc PROPERTY_LAYOUT_DIR =
65         "tools.view.servlet.layout.directory";
66
67     /**
68      * The velocity.properties key for specifying the
69      * servlet's default layout template's filename.
70      */

71     public static final String JavaDoc PROPERTY_DEFAULT_LAYOUT =
72         "tools.view.servlet.layout.default.template";
73
74
75     /**
76      * The default error template's filename.
77      */

78     public static String JavaDoc DEFAULT_ERROR_TEMPLATE = "Error.vm";
79
80     /**
81      * The default layout directory
82      */

83     public static String JavaDoc DEFAULT_LAYOUT_DIR = "layout/";
84
85     /**
86      * The default filename for the servlet's default layout
87      */

88     public static String JavaDoc DEFAULT_DEFAULT_LAYOUT = "Default.vm";
89
90
91     //TODO? if demand for it exists, these context keys can be
92
// made configurable by the velocity.properties
93
// until such time, if anyone really needs to change these,
94
// they are public and aren't final and can be altered
95

96     /**
97      * The context key that will hold the content of the screen.
98      *
99      * This key ($screen_content) must be present in the layout
100      * template for the current screen to be rendered.
101      */

102     public static String JavaDoc KEY_SCREEN_CONTENT = "screen_content";
103
104     /**
105      * The context/parameter key used to specify an alternate
106      * layout to be used for a request instead of the default layout.
107      */

108     public static String JavaDoc KEY_LAYOUT = "layout";
109
110
111     /**
112      * The context key that holds the {@link Throwable} that
113      * broke the rendering of the requested screen.
114      */

115     public static String JavaDoc KEY_ERROR_CAUSE = "error_cause";
116
117     /**
118      * The context key that holds the stack trace of the error that
119      * broke the rendering of the requested screen.
120      */

121     public static String JavaDoc KEY_ERROR_STACKTRACE = "stack_trace";
122
123     /**
124      * The context key that holds the {@link MethodInvocationException}
125      * that broke the rendering of the requested screen.
126      *
127      * If this value is placed in the context, then $error_cause
128      * will hold the error that this invocation exception is wrapping.
129      */

130     public static String JavaDoc KEY_ERROR_INVOCATION_EXCEPTION = "invocation_exception";
131
132
133     private String JavaDoc errorTemplate;
134     private String JavaDoc layoutDir;
135     private String JavaDoc defaultLayout;
136
137
138     /**
139      * Initializes Velocity, the view servlet and checks for changes to
140      * the initial layout configuration.
141      *
142      * @param config servlet configuration parameters
143      */

144     public void init(ServletConfig JavaDoc config) throws ServletException JavaDoc
145     {
146         // first do VVS' init()
147
super.init(config);
148         
149         // check for default template path overrides
150
errorTemplate =
151             RuntimeSingleton.getString(PROPERTY_ERROR_TEMPLATE, DEFAULT_ERROR_TEMPLATE);
152         layoutDir =
153             RuntimeSingleton.getString(PROPERTY_LAYOUT_DIR, DEFAULT_LAYOUT_DIR);
154         defaultLayout =
155             RuntimeSingleton.getString(PROPERTY_DEFAULT_LAYOUT, DEFAULT_DEFAULT_LAYOUT);
156
157         // preventive error checking! directory must end in /
158
if (!layoutDir.endsWith("/"))
159         {
160             layoutDir += '/';
161         }
162
163         // log the current settings
164
Velocity.info("VelocityLayoutServlet: Error screen is '"+errorTemplate+"'");
165         Velocity.info("VelocityLayoutServlet: Layout directory is '"+layoutDir+"'");
166         Velocity.info("VelocityLayoutServlet: Default layout template is '"+defaultLayout+"'");
167
168         // for efficiency's sake, make defaultLayout a full path now
169
defaultLayout = layoutDir + defaultLayout;
170     }
171
172
173     /**
174      * Overrides VelocityViewServlet to check the request for
175      * an alternate layout
176      *
177      * @param request client request
178      * @param response client response
179      * @return the Context to fill
180      */

181     protected Context createContext(HttpServletRequest JavaDoc request,
182                                     HttpServletResponse JavaDoc response)
183     {
184
185         Context ctx = super.createContext(request, response);
186
187         // check if an alternate layout has been specified
188
// by way of the request parameters
189
String JavaDoc layout = request.getParameter(KEY_LAYOUT);
190         if (layout != null)
191         {
192             // let the template know what its new layout is
193
ctx.put(KEY_LAYOUT, layout);
194         }
195         return ctx;
196     }
197
198
199     /**
200      * Overrides VelocityViewServlet.mergeTemplate to do a two-pass
201      * render for handling layouts
202      */

203     protected void mergeTemplate(Template template,
204                                  Context context,
205                                  HttpServletResponse JavaDoc response)
206         throws ResourceNotFoundException, ParseErrorException,
207                MethodInvocationException, IOException JavaDoc,
208                UnsupportedEncodingException JavaDoc, Exception JavaDoc
209     {
210         //
211
// this section is based on Tim Colson's "two pass render"
212
//
213
// Render the screen content
214
StringWriter JavaDoc sw = new StringWriter JavaDoc();
215         template.merge(context, sw);
216         // Add the resulting content to the context
217
context.put(KEY_SCREEN_CONTENT, sw.toString());
218
219         // Check for an alternate layout
220
//
221
// we check after merging the screen template so the screen
222
// can overrule any layout set in the request parameters
223
// by doing #set( $layout = "MyLayout.vm" )
224
Object JavaDoc obj = context.get(KEY_LAYOUT);
225         String JavaDoc layout = (obj == null) ? null : obj.toString();
226         if (layout == null)
227         {
228             // no alternate, use default
229
layout = defaultLayout;
230         }
231         else
232         {
233             // make it a full(er) path
234
layout = layoutDir + layout;
235         }
236
237         try
238         {
239             //load the layout template
240
template = getTemplate(layout);
241         }
242         catch (Exception JavaDoc e)
243         {
244             Velocity.error("VelocityLayoutServlet: Can't load layout \"" +
245                            layout + "\": " + e);
246
247             // if it was an alternate layout we couldn't get...
248
if (!layout.equals(defaultLayout))
249             {
250                 // try to get the default layout
251
// if this also fails, let the exception go
252
template = getTemplate(defaultLayout);
253             }
254         }
255
256         // Render the layout template into the response
257
super.mergeTemplate(template, context, response);
258     }
259
260
261     /**
262      * Overrides VelocityViewServlet to display user's custom error template
263      */

264     protected void error(HttpServletRequest JavaDoc request,
265                          HttpServletResponse JavaDoc response,
266                          Exception JavaDoc e)
267         throws ServletException JavaDoc
268     {
269         try
270         {
271             // get a velocity context
272
Context ctx = createContext(request, response);
273
274             Throwable JavaDoc cause = e;
275
276             // if it's an MIE, i want the real cause and stack trace!
277
if (cause instanceof MethodInvocationException)
278             {
279                 // put the invocation exception in the context
280
ctx.put(KEY_ERROR_INVOCATION_EXCEPTION, e);
281                 // get the real cause
282
cause = ((MethodInvocationException)e).getWrappedThrowable();
283             }
284
285             // add the cause to the context
286
ctx.put(KEY_ERROR_CAUSE, cause);
287                 
288             // grab the cause's stack trace and put it in the context
289
StringWriter JavaDoc sw = new StringWriter JavaDoc();
290             cause.printStackTrace(new java.io.PrintWriter JavaDoc(sw));
291             ctx.put(KEY_ERROR_STACKTRACE, sw.toString());
292
293             // retrieve and render the error template
294
Template et = getTemplate(errorTemplate);
295             mergeTemplate(et, ctx, response);
296
297         }
298         catch (Exception JavaDoc e2)
299         {
300             // d'oh! log this
301
Velocity.error("VelocityLayoutServlet: " +
302                            " Error during error template rendering - " + e2);
303             // then punt the original to a higher authority
304
super.error(request, response, e);
305         }
306     }
307
308
309 }
310
Popular Tags