001: /*
002: * Copyright 2002-2006 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.handler.metadata;
018:
019: import org.springframework.beans.BeansException;
020: import org.springframework.beans.factory.BeanInitializationException;
021: import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
022: import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
023: import org.springframework.context.ConfigurableApplicationContext;
024: import org.springframework.core.Constants;
025: import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
026:
027: /**
028: * Abstract implementation of the HandlerMapping interface that recognizes
029: * metadata attributes of type PathMap on application Controllers and automatically
030: * wires them into the current servlet's WebApplicationContext.
031: *
032: * <p>The path must be mapped to the relevant Spring DispatcherServlet in /WEB-INF/web.xml.
033: * It's possible to have multiple PathMap attributes on the one controller class.
034: *
035: * <p>Controllers instantiated by this class may have dependencies on middle tier
036: * objects, expressed via JavaBean properties or constructor arguments. These will
037: * be resolved automatically.
038: *
039: * <p>You will normally use this HandlerMapping with at most one DispatcherServlet in your
040: * web application. Otherwise you'll end with one instance of the mapped controller for
041: * each DispatcherServlet's context. You <i>might</i> want this -- for example, if
042: * one's using a .pdf mapping and a PDF view, and another a JSP view, or if
043: * using different middle tier objects, but should understand the implications. All
044: * Controllers with attributes will be picked up by each DispatcherServlet's context.
045: *
046: * @author Rod Johnson
047: * @author Juergen Hoeller
048: */
049: public abstract class AbstractPathMapHandlerMapping extends
050: AbstractUrlHandlerMapping {
051:
052: /** Constants instance for AutowireCapableBeanFactory */
053: private static final Constants constants = new Constants(
054: AutowireCapableBeanFactory.class);
055:
056: private int autowireMode = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
057:
058: private boolean dependencyCheck = true;
059:
060: /**
061: * Set the autowire mode for handlers, by the name of the corresponding constant
062: * in the AutowireCapableBeanFactory interface, e.g. "AUTOWIRE_BY_NAME".
063: * @param constantName name of the constant
064: * @throws java.lang.IllegalArgumentException if an invalid constant was specified
065: * @see #setAutowireMode
066: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_NAME
067: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_TYPE
068: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_CONSTRUCTOR
069: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_AUTODETECT
070: */
071: public void setAutowireModeName(String constantName)
072: throws IllegalArgumentException {
073: setAutowireMode(constants.asNumber(constantName).intValue());
074: }
075:
076: /**
077: * Set the autowire mode for handlers. This determines whether any automagical
078: * detection and setting of bean references will happen.
079: * <p>Default is AUTOWIRE_AUTODETECT, which means either constructor autowiring or
080: * autowiring by type (depending on the constructors available in the class).
081: * @param autowireMode the autowire mode to set.
082: * Must be one of the constants defined in the AutowireCapableBeanFactory interface.
083: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_NAME
084: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_TYPE
085: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_CONSTRUCTOR
086: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_AUTODETECT
087: */
088: public void setAutowireMode(int autowireMode) {
089: this .autowireMode = autowireMode;
090: }
091:
092: /**
093: * Set whether to perform a dependency check for objects on autowired handlers.
094: * Not applicable to autowiring a constructor, thus ignored there.
095: * <p>Default is "true".
096: */
097: public void setDependencyCheck(boolean dependencyCheck) {
098: this .dependencyCheck = dependencyCheck;
099: }
100:
101: /**
102: * Calls the <code>detectAndCreateHandlers</code> method in addition
103: * to the superclass's initialization.
104: * @see #detectAndCreateHandlers
105: */
106: public void initApplicationContext() throws BeansException {
107: super .initApplicationContext();
108:
109: if (!(getApplicationContext() instanceof ConfigurableApplicationContext)) {
110: throw new IllegalStateException(
111: "["
112: + getClass().getName()
113: + "] needs to run in a ConfigurableApplicationContext");
114: }
115: ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) getApplicationContext())
116: .getBeanFactory();
117: detectAndCreateHandlers(beanFactory);
118: }
119:
120: /**
121: * Look for all classes with a PathMap class attribute, instantiate them in
122: * the owning ApplicationContext, and register them as MVC handlers usable
123: * by the current DispatcherServlet.
124: * @param beanFactory the ConfigurableListableBeanFactory to register the
125: * created handler instances with
126: * @throws BeansException if handler detection or creation failed
127: * @see PathMap
128: * @see #getClassesWithPathMapAttributes()
129: * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#createBean
130: * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerSingleton
131: */
132: protected void detectAndCreateHandlers(
133: ConfigurableListableBeanFactory beanFactory)
134: throws BeansException {
135: try {
136: Class[] handlerClasses = getClassesWithPathMapAttributes();
137: if (logger.isDebugEnabled()) {
138: logger.debug("Found " + handlerClasses.length
139: + " attribute-targeted handlers");
140: }
141:
142: // for each Class returned by the Commons Attribute indexer
143: for (int i = 0; i < handlerClasses.length; i++) {
144: Class handlerClass = handlerClasses[i];
145:
146: // Autowire the given handler class via AutowireCapableBeanFactory.
147: // Either autowires a constructor or by type, depending on the
148: // constructors available in the given class.
149: Object handler = beanFactory.createBean(handlerClass,
150: this .autowireMode, this .dependencyCheck);
151:
152: // We now have an "autowired" handler, that may reference beans in the
153: // application context. We now add the new handler to the factory.
154: // This isn't necessary for the handler to work, but is useful if we want
155: // to enumerate controllers in the factory etc.
156: beanFactory.registerSingleton(handlerClass.getName(),
157: handler);
158:
159: // There may be multiple paths mapped to this handler.
160: PathMap[] pathMaps = getPathMapAttributes(handlerClass);
161: registerHandler(pathMaps, handler);
162: }
163: } catch (BeansException ex) {
164: throw ex;
165: } catch (Exception ex) {
166: throw new BeanInitializationException(
167: "Could not retrieve PathMap attributes", ex);
168: }
169: }
170:
171: /**
172: * Register the given handler for the URL paths indicated by the given PathMaps.
173: * @param pathMaps the PathMap attributes for the handler class
174: * @param handler the handler instance
175: * @throws BeansException if the handler couldn't be registered
176: * @throws IllegalStateException if there is a conflicting handler registered
177: */
178: protected void registerHandler(PathMap[] pathMaps, Object handler)
179: throws BeansException, IllegalStateException {
180: for (int j = 0; j < pathMaps.length; j++) {
181: PathMap pathMap = pathMaps[j];
182: String path = pathMap.getUrl();
183: if (!path.startsWith("/")) {
184: path = "/" + path;
185: }
186: registerHandler(path, handler);
187: }
188: }
189:
190: /**
191: * Use an attribute index to get a Collection of Class objects
192: * with the required PathMap attribute.
193: * @return a array of Class objects
194: */
195: protected abstract Class[] getClassesWithPathMapAttributes()
196: throws Exception;
197:
198: /**
199: * Use Attributes API to find PathMap attributes for the given handler class.
200: * We know there's at least one, as the getClassNamesWithPathMapAttributes
201: * method return this class name.
202: * @param handlerClass the handler class to look for
203: * @return an array of PathMap objects
204: */
205: protected abstract PathMap[] getPathMapAttributes(Class handlerClass)
206: throws Exception;
207:
208: }
|