/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.config.servlet3; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import javax.servlet.FilterRegistration; import javax.servlet.Servlet; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import javax.servlet.annotation.HandlesTypes; import org.granite.clustering.CreateSessionFilter; import org.granite.config.ConfigProvider; import org.granite.config.GraniteConfig; import org.granite.config.GraniteConfigListener; import org.granite.config.GraniteConfigListener.ServiceConfigurator; import org.granite.config.ServletGraniteConfig; import org.granite.config.flex.Channel; import org.granite.config.flex.Destination; import org.granite.config.flex.EndPoint; import org.granite.config.flex.Factory; import org.granite.config.flex.Service; import org.granite.config.flex.ServicesConfig; import org.granite.config.flex.ServletServicesConfig; import org.granite.gravity.GravityManager.GravityServiceConfigurator; import org.granite.gravity.config.AbstractActiveMQTopicDestination; import org.granite.gravity.config.AbstractJmsTopicDestination; import org.granite.gravity.config.AbstractMessagingDestination; import org.granite.gravity.config.servlet3.ActiveMQTopicDestination; import org.granite.gravity.config.servlet3.JmsTopicDestination; import org.granite.gravity.config.servlet3.MessagingDestination; import org.granite.gravity.security.GravityDestinationSecurizer; import org.granite.logging.Logger; import org.granite.messaging.amf.io.util.externalizer.BigDecimalExternalizer; import org.granite.messaging.amf.io.util.externalizer.BigIntegerExternalizer; import org.granite.messaging.amf.io.util.externalizer.Externalizer; import org.granite.messaging.amf.io.util.externalizer.LongExternalizer; import org.granite.messaging.amf.process.AMF3MessageInterceptor; import org.granite.messaging.service.ExceptionConverter; import org.granite.messaging.service.ServiceFactory; import org.granite.messaging.service.security.RemotingDestinationSecurizer; import org.granite.messaging.service.security.SecurityService; import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher; import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher; import org.granite.messaging.service.tide.TideComponentNameMatcher; import org.granite.messaging.service.tide.TideComponentTypeMatcher; import org.granite.messaging.webapp.AMFMessageFilter; import org.granite.messaging.webapp.AMFMessageServlet; import org.granite.tide.simple.SimpleServiceFactory; import org.granite.util.TypeUtil; import org.granite.util.XMap; /** * @author William DRAI */ @HandlesTypes({ServerFilter.class}) public class GraniteServlet3Initializer implements ServletContainerInitializer { public void onStartup(Set<Class<?>> scannedClasses, ServletContext servletContext) throws ServletException { Set<Class<?>> classes = new HashSet<Class<?>>(); if (scannedClasses != null) { classes.addAll(scannedClasses); classes.remove(ServerFilter.class); // JBossWeb adds the annotation ??? } if (classes.size() > 1) throw new ServletException("Application must have only one ServerFilter configuration class"); if (!classes.isEmpty()) { // Configure GraniteDS only if we find a config class annotated with @ServerFilter Class<?> clazz = classes.iterator().next(); ServerFilter serverFilter = clazz.getAnnotation(ServerFilter.class); servletContext.setAttribute(GraniteConfigListener.GRANITE_CONFIG_ATTRIBUTE, new Servlet3ServiceConfigurator(clazz)); servletContext.addListener(new GraniteConfigListener()); if (serverFilter.forceCreateSession() && servletContext.getFilterRegistration("CreateSessionFilter") == null) { FilterRegistration.Dynamic createSessionFilter = servletContext.addFilter("CreateSessionFilter", CreateSessionFilter.class); createSessionFilter.setAsyncSupported(true); createSessionFilter.addMappingForUrlPatterns(null, true, serverFilter.graniteUrlMapping(), serverFilter.gravityUrlMapping()); } if (servletContext.getFilterRegistration("AMFMessageFilter") == null) { FilterRegistration.Dynamic graniteFilter = servletContext.addFilter("AMFMessageFilter", AMFMessageFilter.class); graniteFilter.addMappingForUrlPatterns(null, true, serverFilter.graniteUrlMapping()); } if (servletContext.getServletRegistration("AMFMessageServlet") == null) { ServletRegistration.Dynamic graniteServlet = servletContext.addServlet("AMFMessageServlet", AMFMessageServlet.class); graniteServlet.setLoadOnStartup(1); graniteServlet.addMapping(serverFilter.graniteUrlMapping()); } try { if (servletContext.getServletRegistration("GravityServlet") == null) { Class<? extends Servlet> gravityAsyncServletClass = TypeUtil.forName("org.granite.gravity.servlet3.GravityAsyncServlet", Servlet.class); ServletRegistration.Dynamic gravityServlet = servletContext.addServlet("GravityServlet", gravityAsyncServletClass); gravityServlet.setLoadOnStartup(1); gravityServlet.setAsyncSupported(true); gravityServlet.addMapping(serverFilter.gravityUrlMapping()); } } catch (ClassNotFoundException e) { servletContext.log("Could not setup GravityAsyncServlet", e); } if (servletContext.getServletRegistration("WebSocketServlet") == null) { try { TypeUtil.forName("javax.websocket.server.ServerEndpointConfig"); try { servletContext.addListener(TypeUtil.forName("org.granite.gravity.websocket.GravityWebSocketDeployer", ServletContextListener.class)); } catch (ClassNotFoundException f) { servletContext.log("Could not setup WebSocket deployer", f); } } catch (ClassNotFoundException e) { // No JSR 356 websocket support detected } } } } public static class Servlet3ServiceConfigurator implements ServiceConfigurator, GravityServiceConfigurator { private static final Logger log = Logger.getLogger(Servlet3ServiceConfigurator.class); private final Class<?> serverFilterClass; public Servlet3ServiceConfigurator(Class<?> serverFilterClass) { this.serverFilterClass = serverFilterClass; } public void initialize(ServletContext servletContext) { servletContext.setAttribute(ServletGraniteConfig.GRANITE_CONFIG_DEFAULT_KEY, "org/granite/config/servlet3/granite-config-servlet3.xml"); } public void configureServices(ServletContext servletContext) throws ServletException { GraniteConfig graniteConfig = ServletGraniteConfig.loadConfig(servletContext); ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(servletContext, true); ServerFilter serverFilter = serverFilterClass.getAnnotation(ServerFilter.class); ConfigProvider configProvider = null; boolean useTide = serverFilter.tide(); String type = serverFilter.type(); Class<?> factoryClass = null; Set<Class<?>> tideInterfaces = new HashSet<Class<?>>(Arrays.asList(serverFilter.tideInterfaces())); Set<Class<? extends Annotation>> tideAnnotations = new HashSet<Class<? extends Annotation>>(Arrays.asList(serverFilter.tideAnnotations())); if (!serverFilter.configProviderClass().equals(ConfigProvider.class)) { try { configProvider = TypeUtil.newInstance(serverFilter.configProviderClass(), new Class[] { ServletContext.class }, new Object[] { servletContext }); servletContext.setAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE, configProvider); if (configProvider.useTide() != null) useTide = configProvider.useTide(); if (configProvider.getType() != null) type = configProvider.getType(); factoryClass = configProvider.getFactoryClass(); if (configProvider.getTideInterfaces() != null) tideInterfaces.addAll(Arrays.asList(configProvider.getTideInterfaces())); if (configProvider.getTideAnnotations() != null) tideAnnotations.addAll(Arrays.asList(configProvider.getTideAnnotations())); } catch (Exception e) { log.error(e, "Could not set config provider of type %s", serverFilter.configProviderClass().getName()); } } if (!serverFilter.factoryClass().equals(ServiceFactory.class)) factoryClass = serverFilter.factoryClass(); else if (!serverFilter.factoryClassName().equals("")) { try { factoryClass = TypeUtil.forName(serverFilter.factoryClassName(), ServiceFactory.class); } catch (ClassNotFoundException e) { throw new ServletException("Could not find service factory class " + serverFilter.factoryClassName(), e); } } if (factoryClass == null) factoryClass = useTide ? SimpleServiceFactory.class : org.granite.messaging.service.SimpleServiceFactory.class; for (Class<?> ti : tideInterfaces) { try { graniteConfig.getTideComponentMatchers().add(new TideComponentInstanceOfMatcher(ti.getName(), false)); log.debug("Enabled components implementing %s for Tide remoting", ti); } catch (Exception e) { log.error(e, "Could not add tide-component interface %s", ti); } } for (Class<? extends Annotation> ta : tideAnnotations) { try { graniteConfig.getTideComponentMatchers().add(new TideComponentAnnotatedWithMatcher(ta.getName(), false)); log.debug("Enabled components annotated with %s for Tide remoting", ta); } catch (Exception e) { log.error(e, "Could not add tide-component annotation %s", ta); } } for (String tn : serverFilter.tideNames()) { try { graniteConfig.getTideComponentMatchers().add(new TideComponentNameMatcher(tn, false)); log.debug("Enabled components with name %s for Tide remoting", tn); } catch (Exception e) { log.error(e, "Could not add tide-component name %s", tn); } } for (String tt : serverFilter.tideTypes()) { try { graniteConfig.getTideComponentMatchers().add(new TideComponentTypeMatcher(tt, false)); log.debug("Enabled components with type %s for Tide remoting", tt); } catch (Exception e) { log.error(e, "Could not add tide-component type %s", tt); } } for (Class<? extends ExceptionConverter> ec : serverFilter.exceptionConverters()) { graniteConfig.registerExceptionConverter(ec, true); log.debug("Registered exception converter %s", ec); } if (configProvider != null) { for (ExceptionConverter ec : configProvider.findInstances(ExceptionConverter.class)) { graniteConfig.registerExceptionConverter(ec, true); log.debug("Registered exception converter %s", ec.getClass()); } } if (configProvider != null) { for (Externalizer ext : configProvider.findInstances(Externalizer.class)) { graniteConfig.registerExternalizer(ext); log.debug("Registered externalizer %s", ext.getClass()); } } if (serverFilter.useBigDecimal()) graniteConfig.setExternalizersByType(BigDecimal.class.getName(), BigDecimalExternalizer.class.getName()); if (serverFilter.useBigInteger()) graniteConfig.setExternalizersByType(BigInteger.class.getName(), BigIntegerExternalizer.class.getName()); if (serverFilter.useLong()) graniteConfig.setExternalizersByType(Long.class.getName(), LongExternalizer.class.getName()); if (!serverFilter.securityServiceClass().equals(SecurityService.class)) { try { graniteConfig.setSecurityService(TypeUtil.newInstance(serverFilter.securityServiceClass(), SecurityService.class)); } catch (Exception e) { throw new ServletException("Could not setup security service", e); } } else if (graniteConfig.getSecurityService() == null && configProvider != null) { SecurityService securityService = configProvider.findInstance(SecurityService.class); graniteConfig.setSecurityService(securityService); } if (graniteConfig.getSecurityService() == null) { String securityServiceClassName = null; // Try auto-detect for (Map.Entry<String, String> entry : securityServicesMap.entrySet()) { try { TypeUtil.forName(entry.getKey()); securityServiceClassName = entry.getValue(); break; } catch (ClassNotFoundException e) { } } try { if (securityServiceClassName != null) graniteConfig.setSecurityService((SecurityService)TypeUtil.newInstance(securityServiceClassName)); } catch (Exception e) { log.error(e, "Could not setup security service " + securityServiceClassName); } } if (!serverFilter.amf3MessageInterceptor().equals(AMF3MessageInterceptor.class)) { try { graniteConfig.setAmf3MessageInterceptor(TypeUtil.newInstance(serverFilter.amf3MessageInterceptor(), AMF3MessageInterceptor.class)); } catch (Exception e) { throw new ServletException("Could not setup amf3 message interceptor", e); } } else if (graniteConfig.getAmf3MessageInterceptor() == null && configProvider != null) { AMF3MessageInterceptor amf3MessageInterceptor = configProvider.findInstance(AMF3MessageInterceptor.class); graniteConfig.setAmf3MessageInterceptor(amf3MessageInterceptor); } Channel channel = servicesConfig.findChannelById("graniteamf"); if (channel == null) { channel = new Channel("graniteamf", "mx.messaging.channels.AMFChannel", new EndPoint("http://{server.name}:{server.port}/{context.root}/graniteamf/amf", "flex.messaging.endpoints.AMFEndpoint"), new XMap()); servicesConfig.addChannel(channel); } XMap factoryProperties = new XMap(); String lookup = null; if (useTide) { lookup = "java:global{context.root}/{capitalized.component.name}Bean"; if (isJBoss6()) lookup = "{capitalized.component.name}Bean/local"; if (!("".equals(serverFilter.ejbLookup()))) lookup = serverFilter.ejbLookup(); } else { lookup = "java:global{context.root}/{capitalized.destination.id}Bean"; if (isJBoss6()) lookup = "{capitalized.destination.id}Bean/local"; if (!("".equals(serverFilter.ejbLookup()))) lookup = serverFilter.ejbLookup(); } if (lookup.indexOf("{context.root}") >= 0) { try { // Call by reflection because of JDK 1.4 Method m = servletContext.getClass().getMethod("getContextPath"); m.setAccessible(true); String contextPath = (String)m.invoke(servletContext); lookup = lookup.replace("{context.root}", contextPath); } catch (Exception e) { log.error(e, "Could not get context path, please define lookup manually in @ServerFilter"); } } factoryProperties.put("lookup", lookup); factoryProperties.put("enable-exception-logging", String.valueOf(serverFilter.enableExceptionLogging())); if (useTide) { Factory factory = servicesConfig.findFactoryById("tide-" + type + "-factory"); if (factory == null) { factory = new Factory("tide-" + type + "-factory", factoryClass.getName(), factoryProperties); servicesConfig.addFactory(factory); } Service service = servicesConfig.findServiceById("granite-service"); if (service == null) { service = new Service("granite-service", "flex.messaging.services.RemotingService", "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>()); List<String> channelIds = new ArrayList<String>(); channelIds.add("graniteamf"); List<String> tideRoles = serverFilter.tideRoles().length == 0 ? null : Arrays.asList(serverFilter.tideRoles()); Destination destination = new Destination(type, channelIds, new XMap(), tideRoles, null, null); destination.getProperties().put("factory", "tide-" + type + "-factory"); if (!("".equals(serverFilter.entityManagerFactoryJndiName()))) destination.getProperties().put("entity-manager-factory-jndi-name", serverFilter.entityManagerFactoryJndiName()); else if (!("".equals(serverFilter.entityManagerJndiName()))) destination.getProperties().put("entity-manager-jndi-name", serverFilter.entityManagerJndiName()); if (!("".equals(serverFilter.validatorClassName()))) destination.getProperties().put("validator-class-name", serverFilter.validatorClassName()); service.getDestinations().put(type, destination); if (destination.getSecurizer() == null && configProvider != null) { RemotingDestinationSecurizer securizer = configProvider.findInstance(RemotingDestinationSecurizer.class); destination.setSecurizer(securizer); } servicesConfig.addService(service); } if (factoryClass.getName().equals("org.granite.tide.ejb.EjbServiceFactory") || factoryClass.getName().equals("org.granite.tide.simple.SimpleServiceFactory")) servicesConfig.scan(null); log.info("Registered Tide " + factoryClass + " service factory and " + type + " destination"); } else { Factory factory = new Factory(type + "-factory", factoryClass.getName(), factoryProperties); servicesConfig.addFactory(factory); Service service = new Service("granite-service", "flex.messaging.services.RemotingService", "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>()); servicesConfig.addService(service); servicesConfig.scan(null); log.info("Registered " + factoryClass + " service factory"); } } private static boolean isJBoss6() { try { InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/org/jboss/version.properties"); if (is != null) return true; } catch (Throwable t) { } return false; } private static Map<String, String> securityServicesMap = new LinkedHashMap<String, String>(); static { securityServicesMap.put("org.mortbay.jetty.Request", "org.granite.messaging.service.security.Jetty6SecurityService"); securityServicesMap.put("org.eclipse.jetty.websocket.servlet.PostUpgradedHttpServletRequest", "org.granite.messaging.service.security.Jetty9SecurityService"); securityServicesMap.put("org.eclipse.jetty.server.Authentication", "org.granite.messaging.service.security.Jetty8SecurityService"); securityServicesMap.put("weblogic.servlet.security.ServletAuthentication", "org.granite.messaging.service.security.WebLogicSecurityService"); securityServicesMap.put("com.sun.appserv.server.LifecycleEvent", "org.granite.messaging.service.security.GlassFishV3SecurityService"); securityServicesMap.put("io.undertow.server.HttpServerExchange", "org.granite.messaging.service.security.UndertowSecurityService"); securityServicesMap.put("org.apache.catalina.connector.Request", "org.granite.messaging.service.security.Tomcat7SecurityService"); } public void configureGravityServices(ServletContext servletContext) throws ServletException { ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(servletContext); ConfigProvider configProvider = (ConfigProvider)servletContext.getAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE); ServiceLoader<Servlet3Configurator> configurators = ServiceLoader.load(Servlet3Configurator.class); for (Field field : serverFilterClass.getDeclaredFields()) { if (field.isAnnotationPresent(MessagingDestination.class)) { MessagingDestination md = field.getAnnotation(MessagingDestination.class); AbstractMessagingDestination messagingDestination = new AbstractMessagingDestination(); messagingDestination.setId(field.getName()); messagingDestination.setNoLocal(md.noLocal()); messagingDestination.setSessionSelector(md.sessionSelector()); if (md.securityRoles().length > 0) messagingDestination.setRoles(Arrays.asList(md.securityRoles())); initSecurizer(messagingDestination, md.securizer(), configProvider); messagingDestination.initServices(servicesConfig); } else if (field.isAnnotationPresent(JmsTopicDestination.class)) { JmsTopicDestination md = field.getAnnotation(JmsTopicDestination.class); AbstractJmsTopicDestination messagingDestination = new AbstractJmsTopicDestination(); messagingDestination.setId(field.getName()); messagingDestination.setNoLocal(md.noLocal()); messagingDestination.setSessionSelector(md.sessionSelector()); if (md.securityRoles().length > 0) messagingDestination.setRoles(Arrays.asList(md.securityRoles())); initSecurizer(messagingDestination, md.securizer(), configProvider); messagingDestination.initServices(servicesConfig); messagingDestination.setName(md.name()); messagingDestination.setTextMessages(md.textMessages()); messagingDestination.setAcknowledgeMode(md.acknowledgeMode()); messagingDestination.setConnectionFactory(md.connectionFactory()); messagingDestination.setTransactedSessions(md.transactedSessions()); messagingDestination.setJndiName(md.topicJndiName()); messagingDestination.initServices(servicesConfig); } else if (field.isAnnotationPresent(ActiveMQTopicDestination.class)) { ActiveMQTopicDestination md = field.getAnnotation(ActiveMQTopicDestination.class); AbstractActiveMQTopicDestination messagingDestination = new AbstractActiveMQTopicDestination(); messagingDestination.setId(field.getName()); messagingDestination.setNoLocal(md.noLocal()); messagingDestination.setSessionSelector(md.sessionSelector()); if (md.securityRoles().length > 0) messagingDestination.setRoles(Arrays.asList(md.securityRoles())); initSecurizer(messagingDestination, md.securizer(), configProvider); messagingDestination.initServices(servicesConfig); messagingDestination.setName(md.name()); messagingDestination.setTextMessages(md.textMessages()); messagingDestination.setAcknowledgeMode(md.acknowledgeMode()); messagingDestination.setConnectionFactory(md.connectionFactory()); messagingDestination.setTransactedSessions(md.transactedSessions()); messagingDestination.setJndiName(md.topicJndiName()); messagingDestination.setBrokerUrl(md.brokerUrl()); messagingDestination.setCreateBroker(md.createBroker()); messagingDestination.setDurable(md.durable()); messagingDestination.setWaitForStart(md.waitForStart()); messagingDestination.setFileStoreRoot(md.fileStoreRoot()); messagingDestination.initServices(servicesConfig); } else { for (Iterator<Servlet3Configurator> iconf = configurators.iterator(); iconf.hasNext(); ) { Servlet3Configurator conf = iconf.next(); if (conf.handleField(field, configProvider, servicesConfig)) break; } } } } } public static void initSecurizer(AbstractMessagingDestination messagingDestination, Class<? extends GravityDestinationSecurizer> securizerClass, ConfigProvider configProvider) { if (securizerClass != GravityDestinationSecurizer.class) { if (configProvider != null) messagingDestination.setSecurizer(configProvider.findInstance(securizerClass)); if (messagingDestination.getSecurizer() == null) messagingDestination.setSecurizerClassName(securizerClass.getName()); } } }