Java tutorial
/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gwtwidgets.server.spring; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletConfig; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.web.context.ServletConfigAware; import org.springframework.web.context.ServletContextAware; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; /** * The GWTHandler implements a Spring {@link HandlerMapping} which maps RPC from * URLs to {@link RemoteService} implementations. It does so by wrapping service * beans with a {@link GWTRPCServiceExporter} dynamically proxying all * {@link RemoteService} interfaces implemented by the service and delegating * RPC to these interfaces to the service. It is possible to use custom * implementations of the {@link GWTRPCServiceExporter}, see * {@link #setServiceExporterFactory(RPCServiceExporterFactory)}. Will also pick * up any beans with an {@link GWTRequestHandlerMapping} annotation and publish * it under the specified URL. * * * @author John Chilton * @author George Georgovassilis, g.georgovassilis[at]gmail.com * */ public class GWTHandler extends AbstractUrlHandlerMapping implements HandlerMapping, InitializingBean, ServletContextAware, ServletConfigAware { // temporary mapping, void after bean initialisation private Map<String, Object> _mapping = new HashMap<String, Object>(); protected RPCServiceExporterFactory factory; protected boolean disableResponseCaching = false; protected boolean throwUndeclaredExceptionToServletContainer = false; protected boolean scanParentApplicationContext = false; protected ServletConfig servletConfig; protected boolean responseCompressionEnabled = true; protected boolean shouldCheckPermutationStrongName = false; /** * Should RPC check the permutation strong name? Disabled by default. If either the specified * {@link RPCServiceExporterFactory} or this flag is set, then checks will be enforced. * @param shouldCheckPermutationStrongName */ public void setShouldCheckPermutationStrongName(boolean shouldCheckPermutationStrongName) { this.shouldCheckPermutationStrongName = shouldCheckPermutationStrongName; } public void setResponseCompressionEnabled(boolean responseCompressionEnabled) { this.responseCompressionEnabled = responseCompressionEnabled; } /** * Scans the application context and its parents for service beans that * implement the {@link GWTRequestMapping} * * @param appContext * Application context */ private void scanForAnnotatedBeans(final ApplicationContext appContext) { if (appContext == null) { return; } for (String beanName : appContext.getBeanNamesForType(RemoteService.class)) { Object service = appContext.getBean(beanName); if (service == null) continue; final Class<?> beanClass = service.getClass(); final RemoteServiceRelativePath requestMapping = ReflectionUtils.findAnnotation(beanClass, RemoteServiceRelativePath.class); if (requestMapping == null) { continue; } // Create serviceExporter to bind to String mapping = requestMapping.value(); if (mapping.contains("/")) { mapping = mapping.substring(mapping.lastIndexOf("/")); } if (getMappings().containsKey(mapping)) logger.warn("Bean '" + mapping + "' already in mapping, skipping."); else getMappings().put(mapping, service); } if (scanParentApplicationContext) scanForAnnotatedBeans(appContext.getParent()); } /** * Recursively scan the parent application contexts for annotated beans to * publish. Beans from applications contexts that are lower in the hierarchy * overwrite beans found in parent application contexts. * * @param scanParentApplicationContext * Defaults to <code>false</code> */ public void setScanParentApplicationContext(boolean scanParentApplicationContext) { this.scanParentApplicationContext = scanParentApplicationContext; } private RPCServiceExporter initServiceInstance(RPCServiceExporter exporter, Object service, Class<RemoteService>[] serviceInterfaces) { try { exporter.setResponseCachingDisabled(disableResponseCaching); exporter.setServletContext(getServletContext()); exporter.setServletConfig(servletConfig); exporter.setService(service); exporter.setServiceInterfaces(serviceInterfaces); exporter.setThrowUndeclaredExceptionToServletContainer(throwUndeclaredExceptionToServletContainer); if (shouldCheckPermutationStrongName) exporter.setShouldCheckPermutationStrongName(true); exporter.afterPropertiesSet(); return exporter; } catch (Exception e) { throw new RuntimeException(e); } } protected Map<String, Object> getMappings() { return _mapping; } /** * Set a mapping between URLs and services * * @param mapping */ public void setMappings(Map<String, Object> mapping) { this._mapping = mapping; } /** * Invoked automatically by Spring after initialisation. */ public void afterPropertiesSet() throws Exception { if (factory == null) { DefaultRPCServiceExporterFactory defaultFactory = new DefaultRPCServiceExporterFactory(); defaultFactory.setResponseCompressionEnabled(responseCompressionEnabled); factory = defaultFactory; } scanForAnnotatedBeans(getApplicationContext()); for (Map.Entry<String, Object> entry : _mapping.entrySet()) { RPCServiceExporter exporter = factory.create(); registerHandler(entry.getKey(), initServiceInstance(exporter, entry.getValue(), ReflectionUtils.getExposedInterfaces(entry.getValue().getClass()))); } this._mapping = null; } /** * Optionally, a {@link RPCServiceExporterFactory} can be injected if a * different implementation or setup is required. Note that after * initialization, the following sequence of invocations will be performed * on the {@code serviceExporter} :<br> * <br> * <code> * exporter.setServletContext();<br> * exporter.setService();<br> * exporter.setServiceInterfaces();<br> * exporter.afterPropertiesSet();<br> *</code> * * @param factory */ public void setServiceExporterFactory(RPCServiceExporterFactory factory) { this.factory = factory; } /** * Can be used to explicitly disable caching of RPC responses in the client * by modifying the HTTP headers of the response. * * @param disableResponseCaching */ public void setDisableResponseCaching(boolean disableResponseCaching) { this.disableResponseCaching = disableResponseCaching; } /** * @see {@link RPCServiceExporter#setThrowUndeclaredExceptionToServletContainer(boolean)} * @param throwUndeclaredExceptionToServletContainer */ public void setThrowUndeclaredExceptionToServletContainer(boolean throwUndeclaredExceptionToServletContainer) { this.throwUndeclaredExceptionToServletContainer = throwUndeclaredExceptionToServletContainer; } /** * Setter for servlet configuration */ public void setServletConfig(ServletConfig servletConfig) { this.servletConfig = servletConfig; } }