KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > servlet > FrameworkServlet


1 /*
2  * Copyright 2002-2007 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
17 package org.springframework.web.servlet;
18
19 import java.io.IOException JavaDoc;
20 import java.security.Principal JavaDoc;
21
22 import javax.servlet.ServletException JavaDoc;
23 import javax.servlet.http.HttpServletRequest JavaDoc;
24 import javax.servlet.http.HttpServletResponse JavaDoc;
25
26 import org.springframework.beans.BeanUtils;
27 import org.springframework.beans.BeansException;
28 import org.springframework.context.ApplicationContextException;
29 import org.springframework.context.ConfigurableApplicationContext;
30 import org.springframework.util.StringUtils;
31 import org.springframework.web.context.ConfigurableWebApplicationContext;
32 import org.springframework.web.context.WebApplicationContext;
33 import org.springframework.web.context.support.ServletRequestHandledEvent;
34 import org.springframework.web.context.support.WebApplicationContextUtils;
35 import org.springframework.web.context.support.XmlWebApplicationContext;
36 import org.springframework.web.util.NestedServletException;
37 import org.springframework.web.util.WebUtils;
38
39 /**
40  * Base servlet for Spring's web framework. Offers integration with
41  * a Spring application context, in a JavaBean-based overall solution.
42  *
43  * <p>This class offers the following functionality:
44  * <ul>
45  * <li>Manages a {@link org.springframework.web.context.WebApplicationContext}
46  * instance per servlet. The servlet's configuration is determined by beans
47  * in the servlet's namespace.
48  * <li>Publishes events on request processing, whether or not a request is
49  * successfully handled.
50  * </ul>
51  *
52  * <p>Subclasses must implement {@link #doService} to handle requests. Because this extends
53  * {@link HttpServletBean} rather than HttpServlet directly, bean properties are
54  * automatically mapped onto it. Subclasses can override {@link #initFrameworkServlet()}
55  * for custom initialization.
56  *
57  * <p>Detects a "contextClass" parameter at the servlet init-param level,
58  * falling back to the default context class,
59  * {@link org.springframework.web.context.support.XmlWebApplicationContext},
60  * if not found. Note that, with the default FrameworkServlet,
61  * a custom context class needs to implement the
62  * {@link org.springframework.web.context.ConfigurableWebApplicationContext} interface.
63  *
64  * <p>Passes a "contextConfigLocation" servlet init-param to the context instance,
65  * parsing it into potentially multiple file paths which can be separated by any
66  * number of commas and spaces, like "test-servlet.xml, myServlet.xml".
67  * If not explicitly specified, the context implementation is supposed to build a
68  * default location from the namespace of the servlet.
69  *
70  * <p>Note: In case of multiple config locations, later bean definitions will
71  * override ones defined in earlier loaded files, at least when using Spring's
72  * default ApplicationContext implementation. This can be leveraged to
73  * deliberately override certain bean definitions via an extra XML file.
74  *
75  * <p>The default namespace is "'servlet-name'-servlet", e.g. "test-servlet" for a
76  * servlet-name "test" (leading to a "/WEB-INF/test-servlet.xml" default location
77  * with XmlWebApplicationContext). The namespace can also be set explicitly via
78  * the "namespace" servlet init-param.
79  *
80  * @author Rod Johnson
81  * @author Juergen Hoeller
82  * @see #doService
83  * @see #initFrameworkServlet
84  * @see #setContextClass
85  * @see #setContextConfigLocation
86  * @see #setNamespace
87  */

88 public abstract class FrameworkServlet extends HttpServletBean {
89
90     /**
91      * Suffix for WebApplicationContext namespaces. If a servlet of this class is
92      * given the name "test" in a context, the namespace used by the servlet will
93      * resolve to "test-servlet".
94      */

95     public static final String JavaDoc DEFAULT_NAMESPACE_SUFFIX = "-servlet";
96
97     /**
98      * Default context class for FrameworkServlet.
99      * @see org.springframework.web.context.support.XmlWebApplicationContext
100      */

101     public static final Class JavaDoc DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
102
103     /**
104      * Prefix for the ServletContext attribute for the WebApplicationContext.
105      * The completion is the servlet name.
106      */

107     public static final String JavaDoc SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";
108
109
110     /** WebApplicationContext implementation class to use */
111     private Class JavaDoc contextClass = DEFAULT_CONTEXT_CLASS;
112
113     /** Namespace for this servlet */
114     private String JavaDoc namespace;
115
116     /** Explicit context config location */
117     private String JavaDoc contextConfigLocation;
118
119     /** Should we publish the context as a ServletContext attribute? */
120     private boolean publishContext = true;
121
122     /** Should we publish a ServletRequestHandledEvent at the end of each request? */
123     private boolean publishEvents = true;
124
125     /** WebApplicationContext for this servlet */
126     private WebApplicationContext webApplicationContext;
127
128
129     /**
130      * Set a custom context class. This class must be of type
131      * {@link org.springframework.web.context.WebApplicationContext}.
132      * <p>When using the default FrameworkServlet implementation,
133      * the context class must also implement the
134      * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
135      * interface.
136      * @see #createWebApplicationContext
137      */

138     public void setContextClass(Class JavaDoc contextClass) {
139         this.contextClass = contextClass;
140     }
141
142     /**
143      * Return the custom context class.
144      */

145     public Class JavaDoc getContextClass() {
146         return this.contextClass;
147     }
148
149     /**
150      * Set a custom namespace for this servlet,
151      * to be used for building a default context config location.
152      */

153     public void setNamespace(String JavaDoc namespace) {
154         this.namespace = namespace;
155     }
156
157     /**
158      * Return the namespace for this servlet, falling back to default scheme if
159      * no custom namespace was set: e.g. "test-servlet" for a servlet named "test".
160      */

161     public String JavaDoc getNamespace() {
162         return (this.namespace != null) ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX;
163     }
164
165     /**
166      * Set the context config location explicitly, instead of relying on the default
167      * location built from the namespace. This location string can consist of
168      * multiple locations separated by any number of commas and spaces.
169      */

170     public void setContextConfigLocation(String JavaDoc contextConfigLocation) {
171         this.contextConfigLocation = contextConfigLocation;
172     }
173
174     /**
175      * Return the explicit context config location, if any.
176      */

177     public String JavaDoc getContextConfigLocation() {
178         return this.contextConfigLocation;
179     }
180
181     /**
182      * Set whether to publish this servlet's context as a ServletContext attribute,
183      * available to all objects in the web container. Default is "true".
184      * <p>This is especially handy during testing, although it is debatable whether
185      * it's good practice to let other application objects access the context this way.
186      */

187     public void setPublishContext(boolean publishContext) {
188         this.publishContext = publishContext;
189     }
190
191     /**
192      * Return whether to publish this servlet's context as a ServletContext attribute.
193      */

194     public boolean isPublishContext() {
195         return this.publishContext;
196     }
197
198     /**
199      * Set whether this servlet should publish a ServletRequestHandledEvent at the end
200      * of each request. Default is "true"; can be turned off for a slight performance
201      * improvement, provided that no ApplicationListeners rely on such events.
202      * @see org.springframework.web.context.support.ServletRequestHandledEvent
203      */

204     public void setPublishEvents(boolean publishEvents) {
205         this.publishEvents = publishEvents;
206     }
207
208     /**
209      * Return whether this servlet should publish a ServletRequestHandledEvent
210      * at the end of each request.
211      */

212     public boolean isPublishEvents() {
213         return this.publishEvents;
214     }
215
216
217     /**
218      * Overridden method of {@link HttpServletBean}, invoked after any bean properties
219      * have been set. Creates this servlet's WebApplicationContext.
220      */

221     protected final void initServletBean() throws ServletException JavaDoc, BeansException {
222         getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
223         if (logger.isInfoEnabled()) {
224             logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
225         }
226         long startTime = System.currentTimeMillis();
227
228         try {
229             this.webApplicationContext = initWebApplicationContext();
230             initFrameworkServlet();
231         }
232         catch (ServletException JavaDoc ex) {
233             logger.error("Context initialization failed", ex);
234             throw ex;
235         }
236         catch (BeansException ex) {
237             logger.error("Context initialization failed", ex);
238             throw ex;
239         }
240
241         if (logger.isInfoEnabled()) {
242             long elapsedTime = System.currentTimeMillis() - startTime;
243             logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
244                     elapsedTime + " ms");
245         }
246     }
247
248     /**
249      * Initialize and publish the WebApplicationContext for this servlet.
250      * <p>Delegates to {@link #createWebApplicationContext} for actual creation
251      * of the context. Can be overridden in subclasses.
252      * @throws BeansException if the context couldn't be initialized
253      * @see #setContextClass
254      * @see #setContextConfigLocation
255      */

256     protected WebApplicationContext initWebApplicationContext() throws BeansException {
257         WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
258         WebApplicationContext wac = createWebApplicationContext(parent);
259
260         if (isPublishContext()) {
261             // Publish the context as a servlet context attribute.
262
String JavaDoc attrName = getServletContextAttributeName();
263             getServletContext().setAttribute(attrName, wac);
264             if (logger.isDebugEnabled()) {
265                 logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
266                         "' as ServletContext attribute with name [" + attrName + "]");
267             }
268         }
269
270         return wac;
271     }
272
273     /**
274      * Instantiate the WebApplicationContext for this servlet, either a default
275      * {@link org.springframework.web.context.support.XmlWebApplicationContext}
276      * or a {@link #setContextClass custom context class}, if set.
277      * <p>This implementation expects custom contexts to implement the
278      * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
279      * interface. Can be overridden in subclasses.
280      * @param parent the parent ApplicationContext to use, or <code>null</code> if none
281      * @return the WebApplicationContext for this servlet
282      * @throws BeansException if the context couldn't be initialized
283      * @see org.springframework.web.context.support.XmlWebApplicationContext
284      */

285     protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
286             throws BeansException {
287
288         if (logger.isDebugEnabled()) {
289             logger.debug("Servlet with name '" + getServletName() +
290                     "' will try to create custom WebApplicationContext context of class '" +
291                     getContextClass().getName() + "'" + ", using parent context [" + parent + "]");
292         }
293         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(getContextClass())) {
294             throw new ApplicationContextException(
295                     "Fatal initialization error in servlet with name '" + getServletName() +
296                     "': custom WebApplicationContext class [" + getContextClass().getName() +
297                     "] is not of type ConfigurableWebApplicationContext");
298         }
299
300         ConfigurableWebApplicationContext wac =
301                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());
302         wac.setParent(parent);
303         wac.setServletContext(getServletContext());
304         wac.setServletConfig(getServletConfig());
305         wac.setNamespace(getNamespace());
306         if (getContextConfigLocation() != null) {
307             wac.setConfigLocations(
308                 StringUtils.tokenizeToStringArray(
309                             getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
310         }
311         wac.refresh();
312         return wac;
313     }
314
315     /**
316      * Return the ServletContext attribute name for this servlet's WebApplicationContext.
317      * <p>The default implementation returns
318      * <code>SERVLET_CONTEXT_PREFIX + servlet name</code>.
319      * @see #SERVLET_CONTEXT_PREFIX
320      * @see #getServletName
321      */

322     public String JavaDoc getServletContextAttributeName() {
323         return SERVLET_CONTEXT_PREFIX + getServletName();
324     }
325
326     /**
327      * Return this servlet's WebApplicationContext.
328      */

329     public final WebApplicationContext getWebApplicationContext() {
330         return this.webApplicationContext;
331     }
332
333     /**
334      * This method will be invoked after any bean properties have been set and
335      * the WebApplicationContext has been loaded. The default implementation is empty;
336      * subclasses may override this method to perform any initialization they require.
337      * @throws ServletException in case of an initialization exception
338      * @throws BeansException if thrown by ApplicationContext methods
339      */

340     protected void initFrameworkServlet() throws ServletException JavaDoc, BeansException {
341     }
342
343
344     /**
345      * Delegate GET requests to processRequest/doService.
346      * <p>Will also be invoked by HttpServlet's default implementation of <code>doHead</code>,
347      * with a <code>NoBodyResponse</code> that just captures the content length.
348      * @see #doService
349      * @see #doHead
350      */

351     protected final void doGet(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
352         throws ServletException JavaDoc, IOException JavaDoc {
353
354         processRequest(request, response);
355     }
356
357     /**
358      * Delegate POST requests to {@link #processRequest}.
359      * @see #doService
360      */

361     protected final void doPost(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
362         throws ServletException JavaDoc, IOException JavaDoc {
363
364         processRequest(request, response);
365     }
366
367     /**
368      * Delegate PUT requests to {@link #processRequest}.
369      * @see #doService
370      */

371     protected final void doPut(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
372         throws ServletException JavaDoc, IOException JavaDoc {
373
374         processRequest(request, response);
375     }
376
377     /**
378      * Delegate DELETE requests to {@link #processRequest}.
379      * @see #doService
380      */

381     protected final void doDelete(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
382         throws ServletException JavaDoc, IOException JavaDoc {
383
384         processRequest(request, response);
385     }
386
387     /**
388      * Process this request, publishing an event regardless of the outcome.
389      * <p>The actual event handling is performed by the abstract
390      * {@link #doService} template method.
391      */

392     protected final void processRequest(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
393             throws ServletException JavaDoc, IOException JavaDoc {
394
395         long startTime = System.currentTimeMillis();
396         Throwable JavaDoc failureCause = null;
397
398         try {
399             doService(request, response);
400         }
401         catch (ServletException JavaDoc ex) {
402             failureCause = ex;
403             throw ex;
404         }
405         catch (IOException JavaDoc ex) {
406             failureCause = ex;
407             throw ex;
408         }
409         catch (Throwable JavaDoc ex) {
410             failureCause = ex;
411             throw new NestedServletException("Request processing failed", ex);
412         }
413
414         finally {
415             if (failureCause != null) {
416                 logger.debug("Could not complete request", failureCause);
417             }
418             else {
419                 logger.debug("Successfully completed request");
420             }
421             if (isPublishEvents()) {
422                 // Whether or not we succeeded, publish an event.
423
long processingTime = System.currentTimeMillis() - startTime;
424                 this.webApplicationContext.publishEvent(
425                         new ServletRequestHandledEvent(this,
426                                 request.getRequestURI(), request.getRemoteAddr(),
427                                 request.getMethod(), getServletConfig().getServletName(),
428                                 WebUtils.getSessionId(request), getUsernameForRequest(request),
429                                 processingTime, failureCause));
430             }
431         }
432     }
433
434     /**
435      * Determine the username for the given request.
436      * Default implementation takes the name of the UserPrincipal, if any.
437      * Can be overridden in subclasses.
438      * @param request current HTTP request
439      * @return the username, or <code>null</code> if none
440      * @see javax.servlet.http.HttpServletRequest#getUserPrincipal
441      */

442     protected String JavaDoc getUsernameForRequest(HttpServletRequest JavaDoc request) {
443         Principal JavaDoc userPrincipal = request.getUserPrincipal();
444         return (userPrincipal != null ? userPrincipal.getName() : null);
445     }
446
447     /**
448      * Subclasses must implement this method to do the work of request handling,
449      * receiving a centralized callback for GET, POST, PUT and DELETE.
450      * <p>The contract is essentially the same as that for the commonly overridden
451      * <code>doGet</code> or <code>doPost</code> methods of HttpServlet.
452      * <p>This class intercepts calls to ensure that exception handling and
453      * event publication takes place.
454      * @param request current HTTP request
455      * @param response current HTTP response
456      * @throws Exception in case of any kind of processing failure
457      * @see javax.servlet.http.HttpServlet#doGet
458      * @see javax.servlet.http.HttpServlet#doPost
459      */

460     protected abstract void doService(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
461         throws Exception JavaDoc;
462
463
464     /**
465      * Close the WebApplicationContext of this servlet.
466      * @see org.springframework.context.ConfigurableApplicationContext#close()
467      */

468     public void destroy() {
469         getServletContext().log(
470                 "Destroying Spring FrameworkServlet '" + getServletName() + "'");
471         if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
472             ((ConfigurableApplicationContext) this.webApplicationContext).close();
473         }
474     }
475
476 }
477
Popular Tags