001: /*
002: * Copyright 2005 Joe Walker
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: package org.directwebremoting.spring;
017:
018: import java.util.HashMap;
019: import java.util.List;
020: import java.util.Map;
021: import java.util.Map.Entry;
022:
023: import javax.servlet.ServletConfig;
024: import javax.servlet.ServletContext;
025: import javax.servlet.http.HttpServletRequest;
026: import javax.servlet.http.HttpServletResponse;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.directwebremoting.WebContextFactory.WebContextBuilder;
031: import org.directwebremoting.extend.Configurator;
032: import org.directwebremoting.impl.ContainerMap;
033: import org.directwebremoting.impl.ContainerUtil;
034: import org.directwebremoting.impl.StartupUtil;
035: import org.directwebremoting.servlet.UrlProcessor;
036: import org.directwebremoting.util.FakeServletConfig;
037: import org.springframework.beans.BeansException;
038: import org.springframework.beans.factory.BeanFactory;
039: import org.springframework.beans.factory.BeanFactoryAware;
040: import org.springframework.beans.factory.BeanNameAware;
041: import org.springframework.beans.factory.InitializingBean;
042: import org.springframework.context.ApplicationContext;
043: import org.springframework.util.Assert;
044: import org.springframework.web.servlet.ModelAndView;
045: import org.springframework.web.servlet.mvc.AbstractController;
046:
047: /**
048: * A Spring Controller that handles DWR requests. <br/>
049: * Using this controller allows you to configure DWR entirely in Spring. You do not have to create
050: * a separate <code>dwr.xml</code> configuration file when using this controller.
051: *
052: * <p>The following configuration provides a basic example of how too define this controller as a bean
053: * in your application context.
054: *
055: * <code>
056: * <pre>
057: <bean id="dwrController" class="org.directwebremoting.spring.DwrController">
058: <property name="configurators">
059: <list>
060: <ref bean="dwrConfiguration"/>
061: </list>
062: </property>
063: <property name="debug" value="true"/>
064: </bean>
065:
066: <bean id="dwrConfiguration" class="org.directwebremoting.spring.SpringConfigurator">
067: <property name="creators">
068: <map>
069: <entry key="<b>beanName</b>">
070: <bean class="org.directwebremoting.spring.CreatorConfig">
071: <property name="creator">
072: <bean class="org.directwebremoting.spring.BeanCreator">
073: <property name="bean" ref="<b>BeanName</b>"/>
074: </bean>
075: </property>
076: </bean>
077: </entry>
078: </map>
079: </property>
080: </bean>
081:
082: <-- the bean you want to remote using DWR -->
083: <bean id="<b>beanName</b>" class="BeanName"/>
084: </pre></code>
085: *
086: * In the near future we want to provide a DWR namespace for Spring, which should allow you to
087: * something like the following:
088: * <code>
089: * <pre>
090: <dwr:configuration>
091: <debug/>
092: </dwr:configuration>
093:
094: <-- the bean you want to remote using DWR -->
095: <bean id="<b>beanName</b>" class="BeanName">
096: <dwr:remote javascript="<b>beanName</b>"/>
097: </bean>
098: </pre></code>
099: * Which should be equivalent to the previous example. Please note that this is still work in progress
100: * and is therefore subject to change.</p>
101: *
102: * @author Joe Walker [joe at getahead dot ltd dot uk]
103: * @author Bram Smeets
104: */
105: public class DwrController extends AbstractController implements
106: BeanNameAware, InitializingBean, BeanFactoryAware {
107: /**
108: * Is called by the Spring container to set the bean factory. <br/>
109: * This bean factory is then used to obtain the global DWR configuration from. This global configuration is
110: * optional as DWR will provide defaults where possible.
111: * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
112: */
113: public void setBeanFactory(BeanFactory beanFactory)
114: throws BeansException {
115: container = new SpringContainer();
116: container.setBeanFactory(beanFactory);
117: }
118:
119: /**
120: * Sets whether DWR should be in debug mode (default is <code>false</code>). <br/>
121: * This allows access to the debug pages provided by DWR under <code>/[WEBAPP]/dwr/</code>.
122: * <b>NOTE</b>: make sure to not set this property to <code>true</code> in a production environment.
123: * @param debug the indication of whether to start DWR in debug mode
124: */
125: public void setDebug(boolean debug) {
126: this .debug = debug;
127: }
128:
129: /**
130: * Sets the configurators to apply to this controller. <br/>
131: * The configurators are used to set up DWR correctly.
132: * @param configurators the configurators to apply to this controller
133: */
134: public void setConfigurators(List<Configurator> configurators) {
135: this .configurators = configurators;
136: }
137:
138: /**
139: * Sets whether the default DWR configuration should be included (default is <code>true</code>). <br/>
140: * This default configuration contains all build-in creators and converters. You normally want this
141: * default configuration to be included.
142: * @param includeDefaultConfig the indication of whether to include the default configuration
143: */
144: public void setIncludeDefaultConfig(boolean includeDefaultConfig) {
145: this .includeDefaultConfig = includeDefaultConfig;
146: }
147:
148: /**
149: * Is called by the Spring container after all properties have been set. <br/>
150: * This method actually makes sure the container is correctly initialized and all configurators
151: * are processed.
152: * @throws Exception in case setting up fails
153: * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
154: */
155: public void afterPropertiesSet() throws Exception {
156: ApplicationContext parent = getApplicationContext().getParent();
157: if (parent != null) {
158: try {
159: Object parentConfigurator = parent
160: .getBean(DwrNamespaceHandler.DEFAULT_SPRING_CONFIGURATOR_ID);
161: if ((parentConfigurator != null)
162: && (!configurators.contains(parentConfigurator))) {
163: configurators
164: .add((Configurator) parentConfigurator);
165: }
166: } catch (RuntimeException rex) {
167: if (log.isDebugEnabled()) {
168: log
169: .debug("Could not detect dwr configuration in parent context");
170: }
171: }
172: }
173: ServletContext servletContext = getServletContext();
174:
175: if (logger.isDebugEnabled()) {
176: logger
177: .debug("afterPropertiesSet() called with servletContext '"
178: + servletContext + "'");
179: }
180:
181: Assert
182: .notNull(servletContext,
183: "The servlet context has not been set on the controller");
184: Assert.notNull(configurators,
185: "The required 'configurators' property should be set");
186:
187: // Use a fake servlet config as Spring 1.x does not provide ServletConfigAware functionality
188: // Now only allow Controller to be configured using parameters
189:
190: // We should remove adding the ContainerMap here, but that will break anyone
191: // who has used the spring container to configure the DwrController e.g:
192: // <bean name="pollAndCometEnabled" class="java.lang.String">
193: // <constructor-arg index="0"><value>true</value></constructor-arg>
194: // </bean>
195: // TODO: Decide if we want to get rid of this
196: ContainerMap containerMap = new ContainerMap(container, true);
197: for (Entry<String, Object> entry : containerMap.entrySet()) {
198: Object value = entry.getValue();
199: if (value instanceof String) {
200: configParams.put(entry.getKey(), (String) value);
201: }
202: }
203: configParams.put("debug", "" + debug);
204:
205: servletConfig = new FakeServletConfig(name, servletContext,
206: configParams);
207:
208: try {
209: ContainerUtil.setupDefaultContainer(container,
210: servletConfig);
211: StartupUtil.initContainerBeans(servletConfig,
212: servletContext, container);
213: webContextBuilder = container
214: .getBean(WebContextBuilder.class);
215:
216: ContainerUtil.prepareForWebContextFilter(servletContext,
217: servletConfig, container, webContextBuilder, null);
218: ContainerUtil.publishContainer(container, servletConfig);
219:
220: // The dwr.xml from within the JAR file.
221: if (includeDefaultConfig) {
222: ContainerUtil.configureFromSystemDwrXml(container);
223: }
224:
225: ContainerUtil.configure(container, configurators);
226: } catch (Exception ex) {
227: log.fatal("init failed", ex);
228: throw ex;
229: } finally {
230: webContextBuilder.unset();
231: }
232: }
233:
234: /**
235: * Handles all request to this controller. <br/>
236: * It delegates to the <code>UrlProcessor</code> and also takes case of setting and unsetting of the
237: * current <code>WebContext</code>.
238: * @param request the request to handle
239: * @param response the response to handle
240: * @throws Exception in case handling of the request fails unexpectedly
241: * @see org.directwebremoting.WebContext
242: */
243: @Override
244: protected ModelAndView handleRequestInternal(
245: HttpServletRequest request, HttpServletResponse response)
246: throws Exception {
247: try {
248: // set up the web context and delegate to the processor
249: webContextBuilder.set(request, response, servletConfig,
250: getServletContext(), container);
251:
252: UrlProcessor processor = container
253: .getBean(UrlProcessor.class);
254: processor.handle(request, response);
255: } finally {
256: webContextBuilder.unset();
257: }
258:
259: // return null to inform the dispatcher servlet the request has already been handled
260: return null;
261: }
262:
263: /**
264: * Is called by the Spring container to set the name of this bean.
265: * @param name the name of this bean in the Spring container
266: * @see BeanNameAware#setBeanName(String)
267: */
268: public void setBeanName(String name) {
269: this .name = name;
270: }
271:
272: /**
273: * Additional parameters such as pollAndCometEnabled. For a full list see:
274: * <a href="http://getahead.org/dwr/server/servlet">http://getahead.org/dwr/server/servlet</a>
275: * @param configParams the configParams to set
276: */
277: public void setConfigParams(Map<String, String> configParams) {
278: Assert.notNull(configParams, "configParams cannot be null");
279: this .configParams = configParams;
280: }
281:
282: /**
283: * How is this deployed in Spring
284: */
285: private String name;
286:
287: /**
288: * Whether to allow access to the debug pages
289: */
290: private boolean debug = false;
291:
292: /**
293: * The builder for the <code>WebContext</code> that keeps http objects local to a thread
294: * @see org.directwebremoting.WebContext
295: */
296: protected WebContextBuilder webContextBuilder;
297:
298: /**
299: * DWRs IoC container (that passes stuff to Spring in this case)
300: */
301: private SpringContainer container;
302:
303: /**
304: * The fake ServletConfig
305: */
306: private ServletConfig servletConfig;
307:
308: /**
309: * Do we prefix the list of Configurators with a default to read the system
310: * dwr.xml file?
311: */
312: private boolean includeDefaultConfig = true;
313:
314: /**
315: * What Configurators exist for us to configure ourselves.
316: */
317: private List<Configurator> configurators;
318:
319: /**
320: * Additional parameters such as pollAndCometEnabled. For a full list see:
321: * <a href="http://getahead.org/dwr/server/servlet">http://getahead.org/dwr/server/servlet</a>
322: */
323: private Map<String, String> configParams = new HashMap<String, String>();
324:
325: /**
326: * The log stream
327: */
328: private static final Log log = LogFactory
329: .getLog(DwrController.class);
330: }
|