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.Set;
034    
035    import javax.servlet.ServletContext;
036    import javax.servlet.ServletContextEvent;
037    import javax.servlet.ServletContextListener;
038    import javax.servlet.ServletException;
039    
040    import org.granite.config.flex.Channel;
041    import org.granite.config.flex.Destination;
042    import org.granite.config.flex.EndPoint;
043    import org.granite.config.flex.Factory;
044    import org.granite.config.flex.Service;
045    import org.granite.config.flex.ServicesConfig;
046    import org.granite.config.flex.ServletServicesConfig;
047    import org.granite.config.servlet3.FlexFilter;
048    import org.granite.jmx.GraniteMBeanInitializer;
049    import org.granite.logging.Logger;
050    import org.granite.messaging.amf.io.util.externalizer.BigDecimalExternalizer;
051    import org.granite.messaging.amf.io.util.externalizer.BigIntegerExternalizer;
052    import org.granite.messaging.amf.io.util.externalizer.LongExternalizer;
053    import org.granite.messaging.amf.process.AMF3MessageInterceptor;
054    import org.granite.messaging.service.ExceptionConverter;
055    import org.granite.messaging.service.ServiceFactory;
056    import org.granite.messaging.service.SimpleServiceFactory;
057    import org.granite.messaging.service.security.SecurityService;
058    import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher;
059    import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher;
060    import org.granite.messaging.service.tide.TideComponentNameMatcher;
061    import org.granite.messaging.service.tide.TideComponentTypeMatcher;
062    import org.granite.util.ClassUtil;
063    import org.granite.util.ServletParams;
064    import org.granite.util.XMap;
065    
066    
067    /**
068     * @author William DRAI
069     */
070    public class GraniteConfigListener implements ServletContextListener {
071    
072        private static final String GRANITE_CONFIG_SHUTDOWN_KEY = GraniteConfig.class.getName() + "_SHUTDOWN";
073        public static final String GRANITE_CONFIG_ATTRIBUTE = "org.granite.config.flexFilter";
074        public static final String GRANITE_MBEANS_ATTRIBUTE = "registerGraniteMBeans";
075    
076        private static final Logger log = Logger.getLogger(GraniteConfigListener.class);
077    
078        public void contextInitialized(ServletContextEvent sce) {
079            try {
080                ServletContext context = sce.getServletContext();
081    
082                log.info("Initializing GraniteDS...");
083                
084                Class<?> flexFilterClass = (Class<?>)context.getAttribute(GRANITE_CONFIG_ATTRIBUTE);
085                if (flexFilterClass != null)
086                    context.setAttribute(ServletGraniteConfig.GRANITE_CONFIG_DEFAULT_KEY, "org/granite/config/servlet3/granite-config-servlet3.xml");
087                
088                GraniteConfig gConfig = ServletGraniteConfig.loadConfig(context);
089                ServletServicesConfig.loadConfig(context);
090                
091                if (flexFilterClass != null)
092                    configureServices(context, flexFilterClass);
093                
094                if (gConfig.isRegisterMBeans()) {
095                    GraniteMBeanInitializer.registerMBeans(context, 
096                                    ServletGraniteConfig.getServletConfig(context), 
097                                    ServletServicesConfig.getServletConfig(context));
098                }
099    
100                log.info("GraniteDS initialized");
101            }
102            catch (Exception e) {
103                throw new RuntimeException("Could not initialize Granite environment", e);
104            }
105        }
106    
107        public void contextDestroyed(ServletContextEvent sce) {
108            ServletContext context = sce.getServletContext();
109    
110            log.info("Stopping GraniteDS...");
111    
112            @SuppressWarnings("unchecked")
113            List<ShutdownListener> listeners = (List<ShutdownListener>)sce.getServletContext().getAttribute(GRANITE_CONFIG_SHUTDOWN_KEY);
114            if (listeners != null) {
115                try {
116                    for (ShutdownListener listener : listeners)
117                        listener.stop();
118                }
119                catch (Exception e) {
120                    throw new RuntimeException("Could not destroy Granite environment", e);
121                }
122            }
123    
124            if (ServletParams.get(context, GRANITE_MBEANS_ATTRIBUTE, Boolean.TYPE, false))
125                    GraniteMBeanInitializer.unregisterMBeans(context);
126    
127            log.info("GraniteDS stopped");
128        }
129    
130        public static synchronized void registerShutdownListener(ServletContext context, ShutdownListener listener) {
131            @SuppressWarnings("unchecked")
132            List<ShutdownListener> listeners = (List<ShutdownListener>)context.getAttribute(GRANITE_CONFIG_SHUTDOWN_KEY);
133            if (listeners == null) {
134                listeners = new ArrayList<ShutdownListener>();
135                context.setAttribute(GRANITE_CONFIG_SHUTDOWN_KEY, listeners);
136            }
137            listeners.add(listener);
138        }
139        
140        
141        private void configureServices(ServletContext context, Class<?> flexFilterClass) throws ServletException {
142            GraniteConfig graniteConfig = ServletGraniteConfig.loadConfig(context);
143            ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(context);
144            
145            FlexFilter flexFilter = flexFilterClass.getAnnotation(FlexFilter.class);
146            
147            ConfigProvider configProvider = null;
148            
149            boolean useTide = flexFilter.tide();
150            String type = flexFilter.type();
151            Class<?> factoryClass = null;
152            Set<Class<?>> tideInterfaces = new HashSet<Class<?>>(Arrays.asList(flexFilter.tideInterfaces()));
153            Set<Class<? extends Annotation>> tideAnnotations = new HashSet<Class<? extends Annotation>>(Arrays.asList(flexFilter.tideAnnotations()));
154            
155            if (!flexFilter.configProviderClass().equals(ConfigProvider.class)) {
156                    try {
157                            configProvider = ClassUtil.newInstance(flexFilter.configProviderClass(), new Class[] { ServletContext.class }, new Object[] { context });
158                            
159                            if (configProvider.useTide() != null)
160                                    useTide = configProvider.useTide();
161                            
162                            if (configProvider.getType() != null)
163                                    type = configProvider.getType();
164                            
165                            factoryClass = configProvider.getFactoryClass();
166                            
167                            if (configProvider.getTideInterfaces() != null)
168                                    tideInterfaces.addAll(Arrays.asList(configProvider.getTideInterfaces()));
169                            
170                            if (configProvider.getTideAnnotations() != null)
171                                    tideAnnotations.addAll(Arrays.asList(configProvider.getTideAnnotations()));
172                            }
173                            catch (Exception e) {
174                                    log.error(e, "Could not set config provider of type %s", flexFilter.configProviderClass().getName());
175                            }
176            }
177            
178            if (!flexFilter.factoryClass().equals(ServiceFactory.class))
179                    factoryClass = flexFilter.factoryClass();
180            
181            if (factoryClass == null) {
182                    factoryClass = SimpleServiceFactory.class;
183                    useTide = false;
184            }
185            
186            for (Class<?> ti : tideInterfaces) {
187                    try {
188                            graniteConfig.getTideComponentMatchers().add(new TideComponentInstanceOfMatcher(ti.getName(), false));
189                            log.debug("Enabled components implementing %s for Tide remoting", ti);
190                    }
191                    catch (Exception e) {
192                            log.error(e, "Could not add tide-component interface %s", ti);
193                    }
194            }
195            for (Class<? extends Annotation> ta : tideAnnotations) {
196                    try {
197                            graniteConfig.getTideComponentMatchers().add(new TideComponentAnnotatedWithMatcher(ta.getName(), false));
198                            log.debug("Enabled components annotated with %s for Tide remoting", ta);
199                    }
200                    catch (Exception e) {
201                            log.error(e, "Could not add tide-component annotation %s", ta);
202                    }
203            }
204            for (String tn : flexFilter.tideNames()) {
205                    try {
206                            graniteConfig.getTideComponentMatchers().add(new TideComponentNameMatcher(tn, false));
207                            log.debug("Enabled components with name %s for Tide remoting", tn);
208                    }
209                    catch (Exception e) {
210                            log.error(e, "Could not add tide-component name %s", tn);
211                    }
212            }
213            for (String tt : flexFilter.tideTypes()) {
214                    try {
215                            graniteConfig.getTideComponentMatchers().add(new TideComponentTypeMatcher(tt, false));
216                            log.debug("Enabled components with type %s for Tide remoting", tt);
217                    }
218                    catch (Exception e) {
219                            log.error(e, "Could not add tide-component type %s", tt);
220                    }
221            }
222            
223            for (Class<? extends ExceptionConverter> ec : flexFilter.exceptionConverters()) {
224                    graniteConfig.registerExceptionConverter(ec, true);
225                            log.debug("Registered exception converter %s", ec);
226            }
227            if (configProvider != null) {
228                    for (ExceptionConverter ec : configProvider.findInstances(ExceptionConverter.class)) {
229                            graniteConfig.registerExceptionConverter(ec, true);
230                            log.debug("Registered exception converter %s", ec.getClass());
231                    }
232            }
233            
234            if (flexFilter.useBigDecimal())
235                    graniteConfig.setExternalizersByType(BigDecimal.class.getName(), BigDecimalExternalizer.class.getName());
236            
237            if (flexFilter.useBigInteger())
238                    graniteConfig.setExternalizersByType(BigInteger.class.getName(), BigIntegerExternalizer.class.getName());
239            
240            if (flexFilter.useLong())
241                    graniteConfig.setExternalizersByType(Long.class.getName(), LongExternalizer.class.getName());
242            
243            if (!flexFilter.securityServiceClass().equals(SecurityService.class)) {
244                    try {
245                            graniteConfig.setSecurityService(ClassUtil.newInstance(flexFilter.securityServiceClass(), SecurityService.class));
246                    }
247                    catch (Exception e) {
248                            throw new ServletException("Could not setup security service", e);
249                    }
250            }
251            else if (graniteConfig.getSecurityService() == null && configProvider != null) {
252                            SecurityService securityService = configProvider.findInstance(SecurityService.class);
253                            graniteConfig.setSecurityService(securityService);
254                    }
255            if (graniteConfig.getSecurityService() == null) {
256                    String securityServiceClassName = null;
257                    // Try auto-detect
258                    try {
259                            ClassUtil.forName("org.mortbay.jetty.Request");
260                            securityServiceClassName = "org.granite.messaging.service.security.Jetty6SecurityService";
261                    }
262                    catch (ClassNotFoundException e1) {
263                            try {
264                                    ClassUtil.forName("weblogic.servlet.security.ServletAuthentication");
265                                    securityServiceClassName = "org.granite.messaging.service.security.WebLogicSecurityService";
266                            }
267                            catch (ClassNotFoundException e2) {
268                                    try {
269                                            ClassUtil.forName("com.sun.appserv.server.LifecycleEvent");
270                                    securityServiceClassName = "org.granite.messaging.service.security.GlassFishV3SecurityService";
271                                    }
272                                    catch (ClassNotFoundException e3) {
273                                            securityServiceClassName = "org.granite.messaging.service.security.Tomcat7SecurityService";
274                                    }
275                            }
276                            try {
277                                    graniteConfig.setSecurityService((SecurityService)ClassUtil.newInstance(securityServiceClassName));
278                    }
279                    catch (Exception e) {
280                            throw new ServletException("Could not setup security service", e);
281                    }
282                    }
283            }
284            
285            if (!flexFilter.amf3MessageInterceptor().equals(AMF3MessageInterceptor.class)) {
286                    try {
287                            graniteConfig.setAmf3MessageInterceptor(ClassUtil.newInstance(flexFilter.amf3MessageInterceptor(), AMF3MessageInterceptor.class));
288                    }
289                    catch (Exception e) {
290                            throw new ServletException("Could not setup amf3 message interceptor", e);
291                    }
292            }
293            else if (graniteConfig.getAmf3MessageInterceptor() == null && configProvider != null) {
294                    AMF3MessageInterceptor amf3MessageInterceptor = configProvider.findInstance(AMF3MessageInterceptor.class);
295                            graniteConfig.setAmf3MessageInterceptor(amf3MessageInterceptor);
296            }
297            
298            Channel channel = servicesConfig.findChannelById("graniteamf");
299            if (channel == null) {
300                    channel = new Channel("graniteamf", "mx.messaging.channels.AMFChannel", 
301                            new EndPoint("http://{server.name}:{server.port}/{context.root}/graniteamf/amf", "flex.messaging.endpoints.AMFEndpoint"), 
302                            new XMap());
303                    servicesConfig.addChannel(channel);
304            }
305    
306            XMap factoryProperties = new XMap();
307            String lookup = "java:global/{context.root}/{capitalized.component.name}Bean";
308            if (isJBoss6())
309                    lookup = "{capitalized.component.name}Bean/local";
310            if (!("".equals(flexFilter.ejbLookup())))
311                    lookup = flexFilter.ejbLookup();
312            if (lookup.indexOf("{context.root}") >= 0) {
313                    try {
314                            // Call by reflection because of JDK 1.4
315                            Method m = context.getClass().getMethod("getContextPath");
316                            String contextPath = (String)m.invoke(context);
317                            lookup = lookup.replace("{context.root}", contextPath.substring(1));
318                    }
319                    catch (Exception e) {
320                            log.error(e, "Could not get context path, please define lookup manually in @FlexFilter");
321                    }
322            }
323            factoryProperties.put("lookup", lookup);
324    
325            if (useTide) {
326                    Factory factory = servicesConfig.findFactoryById("tide-" + type + "-factory");
327                    if (factory == null) {
328                            factory = new Factory("tide-" + type + "-factory", factoryClass.getName(), factoryProperties);
329                            servicesConfig.addFactory(factory);
330                    }
331                    
332                    Service service = servicesConfig.findServiceById("granite-service");
333                    if (service == null) {
334                            service = new Service("granite-service", "flex.messaging.services.RemotingService", 
335                                            "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
336                            List<String> channelIds = new ArrayList<String>();
337                            channelIds.add("graniteamf");
338                            List<String> tideRoles = flexFilter.tideRoles().length == 0 ? null : Arrays.asList(flexFilter.tideRoles());
339                            Destination destination = new Destination(type, channelIds, new XMap(), tideRoles, null, null);
340                            destination.getProperties().put("factory", "tide-" + type + "-factory");
341                            if (!("".equals(flexFilter.entityManagerFactoryJndiName())))
342                                    destination.getProperties().put("entity-manager-factory-jndi-name", flexFilter.entityManagerFactoryJndiName());
343                            else if (!("".equals(flexFilter.entityManagerJndiName())))
344                                    destination.getProperties().put("entity-manager-jndi-name", flexFilter.entityManagerJndiName());
345                            if (!("".equals(flexFilter.validatorClassName())))
346                                    destination.getProperties().put("validator-class-name", flexFilter.validatorClassName());
347                            service.getDestinations().put(type, destination);
348                            servicesConfig.addService(service);
349                    }
350                
351                    if (factoryClass.getName().equals("org.granite.tide.ejb.EjbServiceFactory"))
352                            servicesConfig.scan(null);
353                    
354                    log.info("Registered Tide " + factoryClass + " service factory and " + type + " destination");
355            }
356            else {
357                    Factory factory = new Factory(type + "-factory", factoryClass.getName(), factoryProperties);
358                    servicesConfig.addFactory(factory);
359                    
360                    Service service = new Service("granite-service", "flex.messaging.services.RemotingService", 
361                            "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
362                    servicesConfig.addService(service);
363                
364                servicesConfig.scan(null);
365                    
366                    log.info("Registered " + factoryClass + " service factory");
367            }
368        }
369            
370            private static boolean isJBoss6() {
371                    try {
372                            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/org/jboss/version.properties");
373                            if (is != null)
374                                    return true;
375                    }
376                    catch (Throwable t) {
377                    }
378                    return false;
379            }
380    }