001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.config.servlet3;
023
024import java.io.InputStream;
025import java.lang.annotation.Annotation;
026import java.lang.reflect.Field;
027import java.lang.reflect.Method;
028import java.math.BigDecimal;
029import java.math.BigInteger;
030import java.util.*;
031
032import javax.servlet.FilterRegistration;
033import javax.servlet.Servlet;
034import javax.servlet.ServletContainerInitializer;
035import javax.servlet.ServletContext;
036import javax.servlet.ServletException;
037import javax.servlet.ServletRegistration;
038import javax.servlet.annotation.HandlesTypes;
039
040import org.granite.config.ConfigProvider;
041import org.granite.config.GraniteConfig;
042import org.granite.config.GraniteConfigListener;
043import org.granite.config.GraniteConfigListener.ServiceConfigurator;
044import org.granite.config.ServletGraniteConfig;
045import org.granite.config.flex.Channel;
046import org.granite.config.flex.Destination;
047import org.granite.config.flex.EndPoint;
048import org.granite.config.flex.Factory;
049import org.granite.config.flex.Service;
050import org.granite.config.flex.ServicesConfig;
051import org.granite.config.flex.ServletServicesConfig;
052import org.granite.gravity.GravityManager.GravityServiceConfigurator;
053import org.granite.gravity.config.AbstractActiveMQTopicDestination;
054import org.granite.gravity.config.AbstractJmsTopicDestination;
055import org.granite.gravity.config.AbstractMessagingDestination;
056import org.granite.gravity.config.servlet3.ActiveMQTopicDestination;
057import org.granite.gravity.config.servlet3.JmsTopicDestination;
058import org.granite.gravity.config.servlet3.MessagingDestination;
059import org.granite.gravity.security.GravityDestinationSecurizer;
060import org.granite.logging.Logger;
061import org.granite.messaging.amf.io.util.externalizer.BigDecimalExternalizer;
062import org.granite.messaging.amf.io.util.externalizer.BigIntegerExternalizer;
063import org.granite.messaging.amf.io.util.externalizer.Externalizer;
064import org.granite.messaging.amf.io.util.externalizer.LongExternalizer;
065import org.granite.messaging.amf.process.AMF3MessageInterceptor;
066import org.granite.messaging.service.ExceptionConverter;
067import org.granite.messaging.service.ServiceFactory;
068import org.granite.messaging.service.SimpleServiceFactory;
069import org.granite.messaging.service.security.RemotingDestinationSecurizer;
070import org.granite.messaging.service.security.SecurityService;
071import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher;
072import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher;
073import org.granite.messaging.service.tide.TideComponentNameMatcher;
074import org.granite.messaging.service.tide.TideComponentTypeMatcher;
075import org.granite.messaging.webapp.AMFMessageFilter;
076import org.granite.messaging.webapp.AMFMessageServlet;
077import org.granite.util.TypeUtil;
078import org.granite.util.XMap;
079
080/**
081 * @author William DRAI
082 */
083@HandlesTypes({ServerFilter.class})
084public class GraniteServlet3Initializer implements ServletContainerInitializer {
085
086        public void onStartup(Set<Class<?>> scannedClasses, ServletContext servletContext) throws ServletException {
087                Set<Class<?>> classes = new HashSet<Class<?>>();
088                if (scannedClasses != null) {
089                        classes.addAll(scannedClasses);
090                        classes.remove(ServerFilter.class);     // JBossWeb adds the annotation ???
091                }
092                if (classes.size() > 1)
093                        throw new ServletException("Application must have only one ServerFilter configuration class");
094                
095                if (!classes.isEmpty()) {
096                        // Configure GraniteDS only if we find a config class annotated with @ServerFilter
097                        Class<?> clazz = classes.iterator().next();
098                        ServerFilter serverFilter = clazz.getAnnotation(ServerFilter.class);
099                        
100                        servletContext.setAttribute(GraniteConfigListener.GRANITE_CONFIG_ATTRIBUTE, new Servlet3ServiceConfigurator(clazz));
101                        
102                    servletContext.addListener(new GraniteConfigListener());
103                        
104                        if (servletContext.getFilterRegistration("AMFMessageFilter") == null) {
105                                FilterRegistration.Dynamic graniteFilter = servletContext.addFilter("AMFMessageFilter", AMFMessageFilter.class);
106                                graniteFilter.addMappingForUrlPatterns(null, true, serverFilter.graniteUrlMapping());
107                        }
108                        if (servletContext.getServletRegistration("AMFMessageServlet") == null) {
109                                ServletRegistration.Dynamic graniteServlet = servletContext.addServlet("AMFMessageServlet", AMFMessageServlet.class);
110                                graniteServlet.setLoadOnStartup(1);
111                                graniteServlet.addMapping(serverFilter.graniteUrlMapping());
112                        }
113                        
114                        try {
115                                if (servletContext.getServletRegistration("GravityServlet") == null) {
116                                        Class<? extends Servlet> gravityAsyncServletClass = TypeUtil.forName("org.granite.gravity.servlet3.GravityAsyncServlet", Servlet.class);
117                                        ServletRegistration.Dynamic gravityServlet = servletContext.addServlet("GravityServlet", gravityAsyncServletClass);
118                                        gravityServlet.setLoadOnStartup(1);
119                                        gravityServlet.setAsyncSupported(true);
120                                        gravityServlet.addMapping(serverFilter.gravityUrlMapping());
121                                }
122                        }
123                        catch (ClassNotFoundException e) {
124                                servletContext.log("Could not setup GravityAsyncServlet", e);
125                        }
126                }
127        }
128        
129        
130        public static class Servlet3ServiceConfigurator implements ServiceConfigurator, GravityServiceConfigurator {
131                
132                private static final Logger log = Logger.getLogger(Servlet3ServiceConfigurator.class);
133                
134                private final Class<?> serverFilterClass;
135                
136                public Servlet3ServiceConfigurator(Class<?> serverFilterClass) {
137                        this.serverFilterClass = serverFilterClass;
138                }
139                
140                public void initialize(ServletContext servletContext) {
141                        servletContext.setAttribute(ServletGraniteConfig.GRANITE_CONFIG_DEFAULT_KEY, "org/granite/config/servlet3/granite-config-servlet3.xml");
142                }
143                
144            public void configureServices(ServletContext servletContext) throws ServletException {
145                GraniteConfig graniteConfig = ServletGraniteConfig.loadConfig(servletContext);
146                ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(servletContext, true);
147                
148                ServerFilter serverFilter = serverFilterClass.getAnnotation(ServerFilter.class);
149                
150                ConfigProvider configProvider = null;
151                
152                boolean useTide = serverFilter.tide();
153                String type = serverFilter.type();
154                Class<?> factoryClass = null;
155                Set<Class<?>> tideInterfaces = new HashSet<Class<?>>(Arrays.asList(serverFilter.tideInterfaces()));
156                Set<Class<? extends Annotation>> tideAnnotations = new HashSet<Class<? extends Annotation>>(Arrays.asList(serverFilter.tideAnnotations()));
157                
158                if (!serverFilter.configProviderClass().equals(ConfigProvider.class)) {
159                        try {
160                                configProvider = TypeUtil.newInstance(serverFilter.configProviderClass(), new Class[] { ServletContext.class }, new Object[] { servletContext });
161                                
162                                servletContext.setAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE, configProvider);
163                                
164                                if (configProvider.useTide() != null)
165                                        useTide = configProvider.useTide();
166                                
167                                if (configProvider.getType() != null)
168                                        type = configProvider.getType();
169                                
170                                factoryClass = configProvider.getFactoryClass();
171                                
172                                if (configProvider.getTideInterfaces() != null)
173                                        tideInterfaces.addAll(Arrays.asList(configProvider.getTideInterfaces()));
174                                
175                                if (configProvider.getTideAnnotations() != null)
176                                        tideAnnotations.addAll(Arrays.asList(configProvider.getTideAnnotations()));
177                                }
178                                catch (Exception e) {
179                                        log.error(e, "Could not set config provider of type %s", serverFilter.configProviderClass().getName());
180                                }
181                }
182                
183                if (!serverFilter.factoryClass().equals(ServiceFactory.class))
184                        factoryClass = serverFilter.factoryClass();
185            else if (!serverFilter.factoryClassName().equals("")) {
186                try {
187                    factoryClass = TypeUtil.forName(serverFilter.factoryClassName(), ServiceFactory.class);
188                }
189                catch (ClassNotFoundException e) {
190                    throw new ServletException("Could not find service factory class " + serverFilter.factoryClassName(), e);
191                }
192            }
193                
194                if (factoryClass == null) {
195                        factoryClass = SimpleServiceFactory.class;
196                        useTide = false;
197                }
198                
199                for (Class<?> ti : tideInterfaces) {
200                        try {
201                                graniteConfig.getTideComponentMatchers().add(new TideComponentInstanceOfMatcher(ti.getName(), false));
202                                log.debug("Enabled components implementing %s for Tide remoting", ti);
203                        }
204                        catch (Exception e) {
205                                log.error(e, "Could not add tide-component interface %s", ti);
206                        }
207                }
208                for (Class<? extends Annotation> ta : tideAnnotations) {
209                        try {
210                                graniteConfig.getTideComponentMatchers().add(new TideComponentAnnotatedWithMatcher(ta.getName(), false));
211                                log.debug("Enabled components annotated with %s for Tide remoting", ta);
212                        }
213                        catch (Exception e) {
214                                log.error(e, "Could not add tide-component annotation %s", ta);
215                        }
216                }
217                for (String tn : serverFilter.tideNames()) {
218                        try {
219                                graniteConfig.getTideComponentMatchers().add(new TideComponentNameMatcher(tn, false));
220                                log.debug("Enabled components with name %s for Tide remoting", tn);
221                        }
222                        catch (Exception e) {
223                                log.error(e, "Could not add tide-component name %s", tn);
224                        }
225                }
226                for (String tt : serverFilter.tideTypes()) {
227                        try {
228                                graniteConfig.getTideComponentMatchers().add(new TideComponentTypeMatcher(tt, false));
229                                log.debug("Enabled components with type %s for Tide remoting", tt);
230                        }
231                        catch (Exception e) {
232                                log.error(e, "Could not add tide-component type %s", tt);
233                        }
234                }
235                
236                for (Class<? extends ExceptionConverter> ec : serverFilter.exceptionConverters()) {
237                        graniteConfig.registerExceptionConverter(ec, true);
238                                log.debug("Registered exception converter %s", ec);
239                }
240                if (configProvider != null) {
241                        for (ExceptionConverter ec : configProvider.findInstances(ExceptionConverter.class)) {
242                                graniteConfig.registerExceptionConverter(ec, true);
243                                log.debug("Registered exception converter %s", ec.getClass());
244                        }
245                }
246
247            if (configProvider != null) {
248                for (Externalizer ext : configProvider.findInstances(Externalizer.class)) {
249                    graniteConfig.registerExternalizer(ext);
250                    log.debug("Registered externalizer %s", ext.getClass());
251                }
252            }
253
254                if (serverFilter.useBigDecimal())
255                        graniteConfig.setExternalizersByType(BigDecimal.class.getName(), BigDecimalExternalizer.class.getName());
256                
257                if (serverFilter.useBigInteger())
258                        graniteConfig.setExternalizersByType(BigInteger.class.getName(), BigIntegerExternalizer.class.getName());
259                
260                if (serverFilter.useLong())
261                        graniteConfig.setExternalizersByType(Long.class.getName(), LongExternalizer.class.getName());
262                
263                if (!serverFilter.securityServiceClass().equals(SecurityService.class)) {
264                        try {
265                                graniteConfig.setSecurityService(TypeUtil.newInstance(serverFilter.securityServiceClass(), SecurityService.class));
266                        }
267                        catch (Exception e) {
268                                throw new ServletException("Could not setup security service", e);
269                        }
270                }
271                else if (graniteConfig.getSecurityService() == null && configProvider != null) {
272                                SecurityService securityService = configProvider.findInstance(SecurityService.class);
273                                graniteConfig.setSecurityService(securityService);
274                        }
275                if (graniteConfig.getSecurityService() == null) {
276                        String securityServiceClassName = null;
277                        // Try auto-detect
278                        try {
279                                TypeUtil.forName("org.mortbay.jetty.Request");
280                                securityServiceClassName = "org.granite.messaging.service.security.Jetty6SecurityService";
281                        }
282                        catch (ClassNotFoundException e1) {
283                                try {
284                                        TypeUtil.forName("org.eclipse.jetty.server.Request");
285                                        securityServiceClassName = "org.granite.messaging.service.security.Jetty7SecurityService";
286                                }
287                                catch (ClassNotFoundException e1b) {
288                                        try {
289                                                TypeUtil.forName("weblogic.servlet.security.ServletAuthentication");
290                                                securityServiceClassName = "org.granite.messaging.service.security.WebLogicSecurityService";
291                                        }
292                                        catch (ClassNotFoundException e2) {
293                                                try {
294                                                        TypeUtil.forName("com.sun.appserv.server.LifecycleEvent");
295                                                securityServiceClassName = "org.granite.messaging.service.security.GlassFishV3SecurityService";
296                                                }
297                                                catch (ClassNotFoundException e3) {
298                                                        securityServiceClassName = "org.granite.messaging.service.security.Tomcat7SecurityService";
299                                                }
300                                        }
301                                        try {
302                                                graniteConfig.setSecurityService((SecurityService)TypeUtil.newInstance(securityServiceClassName));
303                                }
304                                catch (Exception e) {
305                                        throw new ServletException("Could not setup security service " + securityServiceClassName, e);
306                                }
307                                }
308                        }
309                }
310                
311                if (!serverFilter.amf3MessageInterceptor().equals(AMF3MessageInterceptor.class)) {
312                        try {
313                                graniteConfig.setAmf3MessageInterceptor(TypeUtil.newInstance(serverFilter.amf3MessageInterceptor(), AMF3MessageInterceptor.class));
314                        }
315                        catch (Exception e) {
316                                throw new ServletException("Could not setup amf3 message interceptor", e);
317                        }
318                }
319                else if (graniteConfig.getAmf3MessageInterceptor() == null && configProvider != null) {
320                        AMF3MessageInterceptor amf3MessageInterceptor = configProvider.findInstance(AMF3MessageInterceptor.class);
321                                graniteConfig.setAmf3MessageInterceptor(amf3MessageInterceptor);
322                }
323                
324                Channel channel = servicesConfig.findChannelById("graniteamf");
325                if (channel == null) {
326                        channel = new Channel("graniteamf", "mx.messaging.channels.AMFChannel", 
327                                new EndPoint("http://{server.name}:{server.port}/{context.root}/graniteamf/amf", "flex.messaging.endpoints.AMFEndpoint"), 
328                                new XMap());
329                        servicesConfig.addChannel(channel);
330                }
331
332                XMap factoryProperties = new XMap();
333                String lookup = null;
334                if (useTide) {
335                        lookup = "java:global/{context.root}/{capitalized.component.name}Bean";
336                        if (isJBoss6())
337                                lookup = "{capitalized.component.name}Bean/local";
338                        if (!("".equals(serverFilter.ejbLookup())))
339                                lookup = serverFilter.ejbLookup();
340                }
341                else {
342                        lookup = "java:global/{context.root}/{capitalized.destination.id}Bean";
343                        if (isJBoss6())
344                                lookup = "{capitalized.destination.id}Bean/local";
345                        if (!("".equals(serverFilter.ejbLookup())))
346                                lookup = serverFilter.ejbLookup();
347                }
348                if (lookup.indexOf("{context.root}") >= 0) {
349                        try {
350                                // Call by reflection because of JDK 1.4
351                                Method m = servletContext.getClass().getMethod("getContextPath");
352                    m.setAccessible(true);
353                                String contextPath = (String)m.invoke(servletContext);
354                                lookup = lookup.replace("{context.root}", contextPath.substring(1));
355                        }
356                        catch (Exception e) {
357                                log.error(e, "Could not get context path, please define lookup manually in @ServerFilter");
358                        }
359                }
360                factoryProperties.put("lookup", lookup);
361
362                if (useTide) {
363                        Factory factory = servicesConfig.findFactoryById("tide-" + type + "-factory");
364                        if (factory == null) {
365                                factory = new Factory("tide-" + type + "-factory", factoryClass.getName(), factoryProperties);
366                                servicesConfig.addFactory(factory);
367                        }
368                        
369                        Service service = servicesConfig.findServiceById("granite-service");
370                        if (service == null) {
371                                service = new Service("granite-service", "flex.messaging.services.RemotingService", 
372                                                "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
373                                List<String> channelIds = new ArrayList<String>();
374                                channelIds.add("graniteamf");
375                                List<String> tideRoles = serverFilter.tideRoles().length == 0 ? null : Arrays.asList(serverFilter.tideRoles());
376                                Destination destination = new Destination(type, channelIds, new XMap(), tideRoles, null, null);
377                                destination.getProperties().put("factory", "tide-" + type + "-factory");
378                                if (!("".equals(serverFilter.entityManagerFactoryJndiName())))
379                                        destination.getProperties().put("entity-manager-factory-jndi-name", serverFilter.entityManagerFactoryJndiName());
380                                else if (!("".equals(serverFilter.entityManagerJndiName())))
381                                        destination.getProperties().put("entity-manager-jndi-name", serverFilter.entityManagerJndiName());
382                                if (!("".equals(serverFilter.validatorClassName())))
383                                        destination.getProperties().put("validator-class-name", serverFilter.validatorClassName());
384                                service.getDestinations().put(type, destination);
385                                
386                                if (destination.getSecurizer() == null && configProvider != null) {
387                                RemotingDestinationSecurizer securizer = configProvider.findInstance(RemotingDestinationSecurizer.class);
388                                destination.setSecurizer(securizer);
389                                }
390                                
391                                servicesConfig.addService(service);
392                        }
393                    
394                        if (factoryClass.getName().equals("org.granite.tide.ejb.EjbServiceFactory"))
395                                servicesConfig.scan(null);
396                        
397                        log.info("Registered Tide " + factoryClass + " service factory and " + type + " destination");
398                }
399                else {
400                        Factory factory = new Factory(type + "-factory", factoryClass.getName(), factoryProperties);
401                        servicesConfig.addFactory(factory);
402                        
403                        Service service = new Service("granite-service", "flex.messaging.services.RemotingService", 
404                                "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
405                        servicesConfig.addService(service);
406                    
407                    servicesConfig.scan(null);
408                        
409                        log.info("Registered " + factoryClass + " service factory");
410                }
411            }
412            
413                private static boolean isJBoss6() {
414                        try {
415                                InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/org/jboss/version.properties");
416                                if (is != null)
417                                        return true;
418                        }
419                        catch (Throwable t) {
420                        }
421                        return false;
422                }               
423            
424            private static void initSecurizer(AbstractMessagingDestination messagingDestination, Class<? extends GravityDestinationSecurizer> securizerClass, ConfigProvider configProvider) {
425                        if (securizerClass != GravityDestinationSecurizer.class) {
426                                if (configProvider != null)
427                                        messagingDestination.setSecurizer(configProvider.findInstance(securizerClass));
428                                if (messagingDestination.getSecurizer() == null)
429                                        messagingDestination.setSecurizerClassName(securizerClass.getName());
430                        }
431            }
432
433                
434                public void configureGravityServices(ServletContext servletContext) throws ServletException {
435                ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(servletContext);
436                
437                ConfigProvider configProvider = (ConfigProvider)servletContext.getAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE);
438                
439                for (Field field : serverFilterClass.getDeclaredFields()) {
440                        if (field.isAnnotationPresent(MessagingDestination.class)) {
441                                MessagingDestination md = field.getAnnotation(MessagingDestination.class);
442                                AbstractMessagingDestination messagingDestination = new AbstractMessagingDestination();
443                                messagingDestination.setId(field.getName());
444                                messagingDestination.setNoLocal(md.noLocal());
445                                messagingDestination.setSessionSelector(md.sessionSelector());
446                                initSecurizer(messagingDestination, md.securizer(), configProvider);
447                                messagingDestination.initServices(servicesConfig);
448                        }
449                        else if (field.isAnnotationPresent(JmsTopicDestination.class)) {
450                                JmsTopicDestination md = field.getAnnotation(JmsTopicDestination.class);
451                                AbstractJmsTopicDestination messagingDestination = new AbstractJmsTopicDestination();
452                                messagingDestination.setId(field.getName());
453                                messagingDestination.setNoLocal(md.noLocal());
454                                messagingDestination.setSessionSelector(md.sessionSelector());
455                                initSecurizer(messagingDestination, md.securizer(), configProvider);
456                                messagingDestination.initServices(servicesConfig);
457                                messagingDestination.setName(md.name());
458                                messagingDestination.setTextMessages(md.textMessages());
459                                messagingDestination.setAcknowledgeMode(md.acknowledgeMode());
460                                messagingDestination.setConnectionFactory(md.connectionFactory());
461                                messagingDestination.setTransactedSessions(md.transactedSessions());
462                                messagingDestination.setJndiName(md.topicJndiName());
463                                messagingDestination.initServices(servicesConfig);
464                        }
465                        else if (field.isAnnotationPresent(ActiveMQTopicDestination.class)) {
466                                ActiveMQTopicDestination md = field.getAnnotation(ActiveMQTopicDestination.class);
467                                AbstractActiveMQTopicDestination messagingDestination = new AbstractActiveMQTopicDestination();
468                                messagingDestination.setId(field.getName());
469                                messagingDestination.setNoLocal(md.noLocal());
470                                messagingDestination.setSessionSelector(md.sessionSelector());
471                                initSecurizer(messagingDestination, md.securizer(), configProvider);
472                                messagingDestination.initServices(servicesConfig);
473                                messagingDestination.setName(md.name());
474                                messagingDestination.setTextMessages(md.textMessages());
475                                messagingDestination.setAcknowledgeMode(md.acknowledgeMode());
476                                messagingDestination.setConnectionFactory(md.connectionFactory());
477                                messagingDestination.setTransactedSessions(md.transactedSessions());
478                                messagingDestination.setJndiName(md.topicJndiName());
479                                messagingDestination.setBrokerUrl(md.brokerUrl());
480                                messagingDestination.setCreateBroker(md.createBroker());
481                                messagingDestination.setDurable(md.durable());
482                                messagingDestination.setWaitForStart(md.waitForStart());
483                                messagingDestination.setFileStoreRoot(md.fileStoreRoot());
484                                messagingDestination.initServices(servicesConfig);
485                        }
486                }
487                }
488        }
489}