001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.servlet;
018:
019: import java.util.Enumeration;
020: import java.util.HashSet;
021: import java.util.Set;
022:
023: import javax.servlet.ServletConfig;
024: import javax.servlet.ServletContext;
025: import javax.servlet.ServletException;
026: import javax.servlet.http.HttpServlet;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030:
031: import org.springframework.beans.BeanWrapper;
032: import org.springframework.beans.BeanWrapperImpl;
033: import org.springframework.beans.BeansException;
034: import org.springframework.beans.MutablePropertyValues;
035: import org.springframework.beans.PropertyValue;
036: import org.springframework.beans.PropertyValues;
037: import org.springframework.core.io.Resource;
038: import org.springframework.core.io.ResourceEditor;
039: import org.springframework.core.io.ResourceLoader;
040: import org.springframework.util.StringUtils;
041: import org.springframework.web.context.support.ServletContextResourceLoader;
042:
043: /**
044: * Simple extension of {@link javax.servlet.http.HttpServlet} which treats
045: * its config parameters (<code>init-param</code> entries within the
046: * <code>servlet</code> tag in <code>web.xml</code>) as bean properties.
047: *
048: * <p>A handy superclass for any type of servlet. Type conversion of config
049: * parameters is automatic, with the corresponding setter method getting
050: * invoked with the converted value. It is also possible for subclasses to
051: * specify required properties. Parameters without matching bean property
052: * setter will simply be ignored.
053: *
054: * <p>This servlet leaves request handling to subclasses, inheriting the default
055: * behavior of HttpServlet (<code>doGet</code>, <code>doPost</code>, etc).
056: *
057: * <p>This generic servlet base class has no dependency on the Spring
058: * {@link org.springframework.context.ApplicationContext} concept. Simple
059: * servlets usually don't load their own context but rather access service
060: * beans from the Spring root application context, accessible via the
061: * filter's {@link #getServletContext() ServletContext} (see
062: * {@link org.springframework.web.context.support.WebApplicationContextUtils}).
063: *
064: * <p>The {@link FrameworkServlet} class is a more specific servlet base
065: * class which loads its own application context. FrameworkServlet serves
066: * as direct base class of Spring's full-fledged {@link DispatcherServlet}.
067: *
068: * @author Rod Johnson
069: * @author Juergen Hoeller
070: * @see #addRequiredProperty
071: * @see #initServletBean
072: * @see #doGet
073: * @see #doPost
074: */
075: public abstract class HttpServletBean extends HttpServlet {
076:
077: /** Logger available to subclasses */
078: protected final Log logger = LogFactory.getLog(getClass());
079:
080: /**
081: * Set of required properties (Strings) that must be supplied as
082: * config parameters to this servlet.
083: */
084: private final Set requiredProperties = new HashSet();
085:
086: /**
087: * Subclasses can invoke this method to specify that this property
088: * (which must match a JavaBean property they expose) is mandatory,
089: * and must be supplied as a config parameter. This should be called
090: * from the constructor of a subclass.
091: * <p>This method is only relevant in case of traditional initialization
092: * driven by a ServletConfig instance.
093: * @param property name of the required property
094: */
095: protected final void addRequiredProperty(String property) {
096: this .requiredProperties.add(property);
097: }
098:
099: /**
100: * Map config parameters onto bean properties of this servlet, and
101: * invoke subclass initialization.
102: * @throws ServletException if bean properties are invalid (or required
103: * properties are missing), or if subclass initialization fails.
104: */
105: public final void init() throws ServletException {
106: if (logger.isDebugEnabled()) {
107: logger.debug("Initializing servlet '" + getServletName()
108: + "'");
109: }
110:
111: // Set bean properties from init parameters.
112: try {
113: PropertyValues pvs = new ServletConfigPropertyValues(
114: getServletConfig(), this .requiredProperties);
115: BeanWrapper bw = new BeanWrapperImpl(this );
116: ResourceLoader resourceLoader = new ServletContextResourceLoader(
117: getServletContext());
118: bw.registerCustomEditor(Resource.class, new ResourceEditor(
119: resourceLoader));
120: initBeanWrapper(bw);
121: bw.setPropertyValues(pvs, true);
122: } catch (BeansException ex) {
123: logger.error("Failed to set bean properties on servlet '"
124: + getServletName() + "'", ex);
125: throw ex;
126: }
127:
128: // Let subclasses do whatever initialization they like.
129: initServletBean();
130:
131: if (logger.isDebugEnabled()) {
132: logger.debug("Servlet '" + getServletName()
133: + "' configured successfully");
134: }
135: }
136:
137: /**
138: * Initialize the BeanWrapper for this HttpServletBean,
139: * possibly with custom editors.
140: * <p>This default implementation is empty.
141: * @param bw the BeanWrapper to initialize
142: * @throws BeansException if thrown by BeanWrapper methods
143: * @see org.springframework.beans.BeanWrapper#registerCustomEditor
144: */
145: protected void initBeanWrapper(BeanWrapper bw)
146: throws BeansException {
147: }
148:
149: /**
150: * Overridden method that simply returns <code>null</code> when no
151: * ServletConfig set yet.
152: * @see #getServletConfig()
153: */
154: public final String getServletName() {
155: return (getServletConfig() != null ? getServletConfig()
156: .getServletName() : null);
157: }
158:
159: /**
160: * Overridden method that simply returns <code>null</code> when no
161: * ServletConfig set yet.
162: * @see #getServletConfig()
163: */
164: public final ServletContext getServletContext() {
165: return (getServletConfig() != null ? getServletConfig()
166: .getServletContext() : null);
167: }
168:
169: /**
170: * Subclasses may override this to perform custom initialization.
171: * All bean properties of this servlet will have been set before this
172: * method is invoked.
173: * <p>This default implementation is empty.
174: * @throws ServletException if subclass initialization fails
175: */
176: protected void initServletBean() throws ServletException {
177: }
178:
179: /**
180: * PropertyValues implementation created from ServletConfig init parameters.
181: */
182: private static class ServletConfigPropertyValues extends
183: MutablePropertyValues {
184:
185: /**
186: * Create new ServletConfigPropertyValues.
187: * @param config ServletConfig we'll use to take PropertyValues from
188: * @param requiredProperties set of property names we need, where
189: * we can't accept default values
190: * @throws ServletException if any required properties are missing
191: */
192: public ServletConfigPropertyValues(ServletConfig config,
193: Set requiredProperties) throws ServletException {
194:
195: Set missingProps = (requiredProperties != null && !requiredProperties
196: .isEmpty()) ? new HashSet(requiredProperties)
197: : null;
198:
199: Enumeration en = config.getInitParameterNames();
200: while (en.hasMoreElements()) {
201: String property = (String) en.nextElement();
202: Object value = config.getInitParameter(property);
203: addPropertyValue(new PropertyValue(property, value));
204: if (missingProps != null) {
205: missingProps.remove(property);
206: }
207: }
208:
209: // Fail if we are still missing properties.
210: if (missingProps != null && missingProps.size() > 0) {
211: throw new ServletException(
212: "Initialization from ServletConfig for servlet '"
213: + config.getServletName()
214: + "' failed; the following required properties were missing: "
215: + StringUtils
216: .collectionToDelimitedString(
217: missingProps, ", "));
218: }
219: }
220: }
221:
222: }
|