001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    
021    package org.granite.config;
022    
023    import java.io.InputStream;
024    import java.lang.annotation.Annotation;
025    import java.lang.reflect.Method;
026    import java.math.BigDecimal;
027    import java.math.BigInteger;
028    import java.util.ArrayList;
029    import java.util.Arrays;
030    import java.util.HashMap;
031    import java.util.HashSet;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.Set;
035    import java.util.concurrent.ConcurrentHashMap;
036    
037    import javax.servlet.ServletContext;
038    import javax.servlet.ServletContextEvent;
039    import javax.servlet.ServletContextListener;
040    import javax.servlet.http.HttpSession;
041    import javax.servlet.http.HttpSessionEvent;
042    import javax.servlet.http.HttpSessionListener;
043    import javax.servlet.ServletException;
044    
045    import org.granite.config.flex.Channel;
046    import org.granite.config.flex.Destination;
047    import org.granite.config.flex.EndPoint;
048    import org.granite.config.flex.Factory;
049    import org.granite.config.flex.Service;
050    import org.granite.config.flex.ServicesConfig;
051    import org.granite.config.flex.ServletServicesConfig;
052    import org.granite.config.servlet3.ServerFilter;
053    import org.granite.jmx.GraniteMBeanInitializer;
054    import org.granite.logging.Logger;
055    import org.granite.messaging.amf.io.util.externalizer.BigDecimalExternalizer;
056    import org.granite.messaging.amf.io.util.externalizer.BigIntegerExternalizer;
057    import org.granite.messaging.amf.io.util.externalizer.LongExternalizer;
058    import org.granite.messaging.amf.process.AMF3MessageInterceptor;
059    import org.granite.messaging.service.ExceptionConverter;
060    import org.granite.messaging.service.ServiceFactory;
061    import org.granite.messaging.service.SimpleServiceFactory;
062    import org.granite.messaging.service.security.RemotingDestinationSecurizer;
063    import org.granite.messaging.service.security.SecurityService;
064    import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher;
065    import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher;
066    import org.granite.messaging.service.tide.TideComponentNameMatcher;
067    import org.granite.messaging.service.tide.TideComponentTypeMatcher;
068    import org.granite.util.TypeUtil;
069    import org.granite.util.ServletParams;
070    import org.granite.util.XMap;
071    
072    
073    /**
074     * @author William DRAI
075     */
076    public class GraniteConfigListener implements ServletContextListener, HttpSessionListener {
077    
078        private static final String GRANITE_CONFIG_SHUTDOWN_KEY = GraniteConfig.class.getName() + "_SHUTDOWN";
079        public static final String GRANITE_CONFIG_ATTRIBUTE = "org.granite.config.serverFilter";
080        public static final String GRANITE_CONFIG_PROVIDER_ATTRIBUTE = "org.granite.config.configProvider";
081        public static final String GRANITE_MBEANS_ATTRIBUTE = "registerGraniteMBeans";
082        
083        public static final String GRANITE_SESSION_TRACKING = "org.granite.config.sessionTracking";
084        public static final String GRANITE_SESSION_MAP = "org.granite.config.sessionMap";
085    
086        private static final Logger log = Logger.getLogger(GraniteConfigListener.class);
087    
088        public void contextInitialized(ServletContextEvent sce) {
089            try {
090                ServletContext context = sce.getServletContext();
091    
092                log.info("Initializing GraniteDS...");
093                
094                Class<?> serverFilterClass = (Class<?>)context.getAttribute(GRANITE_CONFIG_ATTRIBUTE);
095                if (serverFilterClass != null)
096                    context.setAttribute(ServletGraniteConfig.GRANITE_CONFIG_DEFAULT_KEY, "org/granite/config/servlet3/granite-config-servlet3.xml");
097                
098                GraniteConfig gConfig = ServletGraniteConfig.loadConfig(context);
099                ServletServicesConfig.loadConfig(context);
100                
101                if (serverFilterClass != null)
102                    configureServices(context, serverFilterClass);
103                
104                if ("true".equals(sce.getServletContext().getInitParameter(GRANITE_SESSION_TRACKING))) {
105                        Map<String, HttpSession> sessionMap = new ConcurrentHashMap<String, HttpSession>(200);
106                        sce.getServletContext().setAttribute(GRANITE_SESSION_MAP, sessionMap);
107                }
108                
109                if (gConfig.isRegisterMBeans()) {
110                    GraniteMBeanInitializer.registerMBeans(context, 
111                                    ServletGraniteConfig.getServletConfig(context), 
112                                    ServletServicesConfig.getServletConfig(context));
113                }
114    
115                log.info("GraniteDS initialized");
116            }
117            catch (Exception e) {
118                throw new RuntimeException("Could not initialize Granite environment", e);
119            }
120        }
121    
122        public void contextDestroyed(ServletContextEvent sce) {
123            ServletContext context = sce.getServletContext();
124    
125            log.info("Stopping GraniteDS...");
126    
127            @SuppressWarnings("unchecked")
128            List<ShutdownListener> listeners = (List<ShutdownListener>)sce.getServletContext().getAttribute(GRANITE_CONFIG_SHUTDOWN_KEY);
129            if (listeners != null) {
130                try {
131                    for (ShutdownListener listener : listeners)
132                        listener.stop();
133                }
134                catch (Exception e) {
135                    throw new RuntimeException("Could not destroy Granite environment", e);
136                }
137            }
138    
139            if (ServletParams.get(context, GRANITE_MBEANS_ATTRIBUTE, Boolean.TYPE, false))
140                    GraniteMBeanInitializer.unregisterMBeans(context);
141    
142            log.info("GraniteDS stopped");
143        }
144    
145        public static synchronized void registerShutdownListener(ServletContext context, ShutdownListener listener) {
146            @SuppressWarnings("unchecked")
147            List<ShutdownListener> listeners = (List<ShutdownListener>)context.getAttribute(GRANITE_CONFIG_SHUTDOWN_KEY);
148            if (listeners == null) {
149                listeners = new ArrayList<ShutdownListener>();
150                context.setAttribute(GRANITE_CONFIG_SHUTDOWN_KEY, listeners);
151            }
152            listeners.add(listener);
153        }
154        
155        
156        private void configureServices(ServletContext servletContext, Class<?> serverFilterClass) throws ServletException {
157            GraniteConfig graniteConfig = ServletGraniteConfig.loadConfig(servletContext);
158            ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(servletContext);
159            
160            ServerFilter serverFilter = serverFilterClass.getAnnotation(ServerFilter.class);
161            
162            ConfigProvider configProvider = null;
163            
164            boolean useTide = serverFilter.tide();
165            String type = serverFilter.type();
166            Class<?> factoryClass = null;
167            Set<Class<?>> tideInterfaces = new HashSet<Class<?>>(Arrays.asList(serverFilter.tideInterfaces()));
168            Set<Class<? extends Annotation>> tideAnnotations = new HashSet<Class<? extends Annotation>>(Arrays.asList(serverFilter.tideAnnotations()));
169            
170            if (!serverFilter.configProviderClass().equals(ConfigProvider.class)) {
171                    try {
172                            configProvider = TypeUtil.newInstance(serverFilter.configProviderClass(), new Class[] { ServletContext.class }, new Object[] { servletContext });
173                            
174                            servletContext.setAttribute(GRANITE_CONFIG_PROVIDER_ATTRIBUTE, configProvider);
175                            
176                            if (configProvider.useTide() != null)
177                                    useTide = configProvider.useTide();
178                            
179                            if (configProvider.getType() != null)
180                                    type = configProvider.getType();
181                            
182                            factoryClass = configProvider.getFactoryClass();
183                            
184                            if (configProvider.getTideInterfaces() != null)
185                                    tideInterfaces.addAll(Arrays.asList(configProvider.getTideInterfaces()));
186                            
187                            if (configProvider.getTideAnnotations() != null)
188                                    tideAnnotations.addAll(Arrays.asList(configProvider.getTideAnnotations()));
189                            }
190                            catch (Exception e) {
191                                    log.error(e, "Could not set config provider of type %s", serverFilter.configProviderClass().getName());
192                            }
193            }
194            
195            if (!serverFilter.factoryClass().equals(ServiceFactory.class))
196                    factoryClass = serverFilter.factoryClass();
197            
198            if (factoryClass == null) {
199                    factoryClass = SimpleServiceFactory.class;
200                    useTide = false;
201            }
202            
203            for (Class<?> ti : tideInterfaces) {
204                    try {
205                            graniteConfig.getTideComponentMatchers().add(new TideComponentInstanceOfMatcher(ti.getName(), false));
206                            log.debug("Enabled components implementing %s for Tide remoting", ti);
207                    }
208                    catch (Exception e) {
209                            log.error(e, "Could not add tide-component interface %s", ti);
210                    }
211            }
212            for (Class<? extends Annotation> ta : tideAnnotations) {
213                    try {
214                            graniteConfig.getTideComponentMatchers().add(new TideComponentAnnotatedWithMatcher(ta.getName(), false));
215                            log.debug("Enabled components annotated with %s for Tide remoting", ta);
216                    }
217                    catch (Exception e) {
218                            log.error(e, "Could not add tide-component annotation %s", ta);
219                    }
220            }
221            for (String tn : serverFilter.tideNames()) {
222                    try {
223                            graniteConfig.getTideComponentMatchers().add(new TideComponentNameMatcher(tn, false));
224                            log.debug("Enabled components with name %s for Tide remoting", tn);
225                    }
226                    catch (Exception e) {
227                            log.error(e, "Could not add tide-component name %s", tn);
228                    }
229            }
230            for (String tt : serverFilter.tideTypes()) {
231                    try {
232                            graniteConfig.getTideComponentMatchers().add(new TideComponentTypeMatcher(tt, false));
233                            log.debug("Enabled components with type %s for Tide remoting", tt);
234                    }
235                    catch (Exception e) {
236                            log.error(e, "Could not add tide-component type %s", tt);
237                    }
238            }
239            
240            for (Class<? extends ExceptionConverter> ec : serverFilter.exceptionConverters()) {
241                    graniteConfig.registerExceptionConverter(ec, true);
242                            log.debug("Registered exception converter %s", ec);
243            }
244            if (configProvider != null) {
245                    for (ExceptionConverter ec : configProvider.findInstances(ExceptionConverter.class)) {
246                            graniteConfig.registerExceptionConverter(ec, true);
247                            log.debug("Registered exception converter %s", ec.getClass());
248                    }
249            }
250            
251            if (serverFilter.useBigDecimal())
252                    graniteConfig.setExternalizersByType(BigDecimal.class.getName(), BigDecimalExternalizer.class.getName());
253            
254            if (serverFilter.useBigInteger())
255                    graniteConfig.setExternalizersByType(BigInteger.class.getName(), BigIntegerExternalizer.class.getName());
256            
257            if (serverFilter.useLong())
258                    graniteConfig.setExternalizersByType(Long.class.getName(), LongExternalizer.class.getName());
259            
260            if (!serverFilter.securityServiceClass().equals(SecurityService.class)) {
261                    try {
262                            graniteConfig.setSecurityService(TypeUtil.newInstance(serverFilter.securityServiceClass(), SecurityService.class));
263                    }
264                    catch (Exception e) {
265                            throw new ServletException("Could not setup security service", e);
266                    }
267            }
268            else if (graniteConfig.getSecurityService() == null && configProvider != null) {
269                            SecurityService securityService = configProvider.findInstance(SecurityService.class);
270                            graniteConfig.setSecurityService(securityService);
271                    }
272            if (graniteConfig.getSecurityService() == null) {
273                    String securityServiceClassName = null;
274                    // Try auto-detect
275                    try {
276                            TypeUtil.forName("org.mortbay.jetty.Request");
277                            securityServiceClassName = "org.granite.messaging.service.security.Jetty6SecurityService";
278                    }
279                    catch (ClassNotFoundException e1) {
280                            try {
281                                    TypeUtil.forName("org.eclipse.jetty.server.Request");
282                                    securityServiceClassName = "org.granite.messaging.service.security.Jetty7SecurityService";
283                            }
284                            catch (ClassNotFoundException e1b) {
285                                    try {
286                                            TypeUtil.forName("weblogic.servlet.security.ServletAuthentication");
287                                            securityServiceClassName = "org.granite.messaging.service.security.WebLogicSecurityService";
288                                    }
289                                    catch (ClassNotFoundException e2) {
290                                            try {
291                                                    TypeUtil.forName("com.sun.appserv.server.LifecycleEvent");
292                                            securityServiceClassName = "org.granite.messaging.service.security.GlassFishV3SecurityService";
293                                            }
294                                            catch (ClassNotFoundException e3) {
295                                                    securityServiceClassName = "org.granite.messaging.service.security.Tomcat7SecurityService";
296                                            }
297                                    }
298                                    try {
299                                            graniteConfig.setSecurityService((SecurityService)TypeUtil.newInstance(securityServiceClassName));
300                            }
301                            catch (Exception e) {
302                                    throw new ServletException("Could not setup security service " + securityServiceClassName, e);
303                            }
304                            }
305                    }
306            }
307            
308            if (!serverFilter.amf3MessageInterceptor().equals(AMF3MessageInterceptor.class)) {
309                    try {
310                            graniteConfig.setAmf3MessageInterceptor(TypeUtil.newInstance(serverFilter.amf3MessageInterceptor(), AMF3MessageInterceptor.class));
311                    }
312                    catch (Exception e) {
313                            throw new ServletException("Could not setup amf3 message interceptor", e);
314                    }
315            }
316            else if (graniteConfig.getAmf3MessageInterceptor() == null && configProvider != null) {
317                    AMF3MessageInterceptor amf3MessageInterceptor = configProvider.findInstance(AMF3MessageInterceptor.class);
318                            graniteConfig.setAmf3MessageInterceptor(amf3MessageInterceptor);
319            }
320            
321            Channel channel = servicesConfig.findChannelById("graniteamf");
322            if (channel == null) {
323                    channel = new Channel("graniteamf", "mx.messaging.channels.AMFChannel", 
324                            new EndPoint("http://{server.name}:{server.port}/{context.root}/graniteamf/amf", "flex.messaging.endpoints.AMFEndpoint"), 
325                            new XMap());
326                    servicesConfig.addChannel(channel);
327            }
328    
329            XMap factoryProperties = new XMap();
330            String lookup = null;
331            if (useTide) {
332                    lookup = "java:global/{context.root}/{capitalized.component.name}Bean";
333                    if (isJBoss6())
334                            lookup = "{capitalized.component.name}Bean/local";
335                    if (!("".equals(serverFilter.ejbLookup())))
336                            lookup = serverFilter.ejbLookup();
337            }
338            else {
339                    lookup = "java:global/{context.root}/{capitalized.destination.id}Bean";
340                    if (isJBoss6())
341                            lookup = "{capitalized.destination.id}Bean/local";
342                    if (!("".equals(serverFilter.ejbLookup())))
343                            lookup = serverFilter.ejbLookup();
344            }
345            if (lookup.indexOf("{context.root}") >= 0) {
346                    try {
347                            // Call by reflection because of JDK 1.4
348                            Method m = servletContext.getClass().getMethod("getContextPath");
349                            String contextPath = (String)m.invoke(servletContext);
350                            lookup = lookup.replace("{context.root}", contextPath.substring(1));
351                    }
352                    catch (Exception e) {
353                            log.error(e, "Could not get context path, please define lookup manually in @FlexFilter");
354                    }
355            }
356            factoryProperties.put("lookup", lookup);
357    
358            if (useTide) {
359                    Factory factory = servicesConfig.findFactoryById("tide-" + type + "-factory");
360                    if (factory == null) {
361                            factory = new Factory("tide-" + type + "-factory", factoryClass.getName(), factoryProperties);
362                            servicesConfig.addFactory(factory);
363                    }
364                    
365                    Service service = servicesConfig.findServiceById("granite-service");
366                    if (service == null) {
367                            service = new Service("granite-service", "flex.messaging.services.RemotingService", 
368                                            "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
369                            List<String> channelIds = new ArrayList<String>();
370                            channelIds.add("graniteamf");
371                            List<String> tideRoles = serverFilter.tideRoles().length == 0 ? null : Arrays.asList(serverFilter.tideRoles());
372                            Destination destination = new Destination(type, channelIds, new XMap(), tideRoles, null, null);
373                            destination.getProperties().put("factory", "tide-" + type + "-factory");
374                            if (!("".equals(serverFilter.entityManagerFactoryJndiName())))
375                                    destination.getProperties().put("entity-manager-factory-jndi-name", serverFilter.entityManagerFactoryJndiName());
376                            else if (!("".equals(serverFilter.entityManagerJndiName())))
377                                    destination.getProperties().put("entity-manager-jndi-name", serverFilter.entityManagerJndiName());
378                            if (!("".equals(serverFilter.validatorClassName())))
379                                    destination.getProperties().put("validator-class-name", serverFilter.validatorClassName());
380                            service.getDestinations().put(type, destination);
381                            
382                            if (destination.getSecurizer() == null && configProvider != null) {
383                            RemotingDestinationSecurizer securizer = configProvider.findInstance(RemotingDestinationSecurizer.class);
384                            destination.setSecurizer(securizer);
385                            }
386                            
387                            servicesConfig.addService(service);
388                    }
389                
390                    if (factoryClass.getName().equals("org.granite.tide.ejb.EjbServiceFactory"))
391                            servicesConfig.scan(null);
392                    
393                    log.info("Registered Tide " + factoryClass + " service factory and " + type + " destination");
394            }
395            else {
396                    Factory factory = new Factory(type + "-factory", factoryClass.getName(), factoryProperties);
397                    servicesConfig.addFactory(factory);
398                    
399                    Service service = new Service("granite-service", "flex.messaging.services.RemotingService", 
400                            "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
401                    servicesConfig.addService(service);
402                
403                servicesConfig.scan(null);
404                    
405                    log.info("Registered " + factoryClass + " service factory");
406            }
407        }
408            
409            private static boolean isJBoss6() {
410                    try {
411                            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/org/jboss/version.properties");
412                            if (is != null)
413                                    return true;
414                    }
415                    catch (Throwable t) {
416                    }
417                    return false;
418            }
419    
420            
421            public void sessionCreated(HttpSessionEvent se) {
422                    @SuppressWarnings("unchecked")
423                    Map<String, HttpSession> sessionMap = (Map<String, HttpSession>)se.getSession().getServletContext().getAttribute(GRANITE_SESSION_MAP);
424                    if (sessionMap != null)
425                            sessionMap.put(se.getSession().getId(), se.getSession());
426            }
427    
428            public void sessionDestroyed(HttpSessionEvent se) {
429                    @SuppressWarnings("unchecked")
430                    Map<String, HttpSession> sessionMap = (Map<String, HttpSession>)se.getSession().getServletContext().getAttribute(GRANITE_SESSION_MAP);
431                    if (sessionMap != null)
432                            sessionMap.remove(se.getSession().getId());
433            }
434    }