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.gravity;
022    
023    import java.lang.reflect.Field;
024    
025    import javax.servlet.ServletConfig;
026    import javax.servlet.ServletContext;
027    import javax.servlet.ServletException;
028    import javax.servlet.http.HttpServlet;
029    
030    import org.granite.config.ConfigProvider;
031    import org.granite.config.GraniteConfig;
032    import org.granite.config.GraniteConfigListener;
033    import org.granite.config.ServletGraniteConfig;
034    import org.granite.config.flex.ServicesConfig;
035    import org.granite.config.flex.ServletServicesConfig;
036    import org.granite.gravity.config.AbstractActiveMQTopicDestination;
037    import org.granite.gravity.config.AbstractJmsTopicDestination;
038    import org.granite.gravity.config.AbstractMessagingDestination;
039    import org.granite.gravity.config.servlet3.ActiveMQTopicDestination;
040    import org.granite.gravity.config.servlet3.JmsTopicDestination;
041    import org.granite.gravity.config.servlet3.MessagingDestination;
042    import org.granite.gravity.security.GravityDestinationSecurizer;
043    import org.granite.util.TypeUtil;
044    
045    /**
046     * @author Franck WOLFF
047     */
048    public class GravityManager {
049    
050            private static final String GRAVITY_KEY = Gravity.class.getName();
051            
052            /**
053             * Parse gravity configuration (granite-config.xml), start gravity by using the specified factory and put it
054             * in ServletContext. If Gravity is already started, returns the previous instance from the servlet context.
055             * <br><br>
056             * This method is intended to be used in {@link HttpServlet#init(ServletConfig)} methods only and
057             * synchronizes on the current ServletContext instance.
058             * 
059             * @param servletConfig the servlet config passed in HttpServlet.init(ServletConfig config) method.
060             * @param channelFactory an implementation specific ChannelFactory instance.
061             * @return a newly created and started Gravity instance or previously started one.
062             * @throws ServletException if something goes wrong (GravityFactory not found, Gravity.start() error, etc.)
063             */
064        public static Gravity start(ServletConfig servletConfig) throws ServletException {
065            return start(servletConfig.getServletContext());
066        }
067        
068        public static Gravity start(ServletContext context) throws ServletException {
069            Gravity gravity = null;
070            
071            synchronized (context) {
072                    
073                    gravity = (Gravity)context.getAttribute(GRAVITY_KEY);
074                    
075                    if (gravity == null) {
076                            GraniteConfig graniteConfig = ServletGraniteConfig.loadConfig(context);
077                            ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(context);
078                        
079                        Class<?> serverFilterClass = (Class<?>)context.getAttribute(GraniteConfigListener.GRANITE_CONFIG_ATTRIBUTE);
080                        if (serverFilterClass != null)
081                            configureServices(context, serverFilterClass);
082                        
083                            GravityConfig gravityConfig = new GravityConfig(graniteConfig);
084                            
085                            String gravityFactory = gravityConfig.getGravityFactory();
086                            try {
087                                            GravityFactory factory = TypeUtil.newInstance(gravityFactory, GravityFactory.class);
088                                            gravity = factory.newGravity(gravityConfig, servicesConfig, graniteConfig);
089                                    } 
090                            catch (Exception e) {
091                                            throw new ServletException("Could not create Gravity instance with factory: " + gravityFactory, e);
092                                    }
093                    
094                            try {
095                                gravity.start();
096                                context.setAttribute(GRAVITY_KEY, gravity);
097                                GraniteConfigListener.registerShutdownListener(context, gravity);
098                            }
099                            catch (Exception e) {
100                                throw new ServletException("Gravity initialization error", e);
101                            }
102                    }
103            }
104    
105            return gravity;
106        }
107        
108        /**
109         * Reconfigure gravity with the new supplied configuration (after reloading granite-config.xml).
110         * <br><br>
111         * Only these configuration options are taken into account when reconfiguring Gravity:
112         * <ul>
113         *  <li>channelIdleTimeoutMillis</li>
114         *  <li>longPollingTimeout</li>
115         *  <li>retryOnError</li>
116         *  <li>maxMessagesQueuedPerChannel</li>
117         *  <li>corePoolSize</li>
118         *  <li>maximumPoolSize</li>
119         *  <li>keepAliveTimeMillis</li>
120         * </ul>
121         * 
122         * @param context the ServletContext where the gravity instance is registered.
123         * @param gravityConfig the new (reloaded) GravityConfig. 
124         */
125        public static void reconfigure(ServletContext context, GravityConfig gravityConfig) {
126            synchronized (context) {
127                    Gravity gravity = getGravity(context);
128                    gravity.reconfigure(gravityConfig, ServletGraniteConfig.getConfig(context));
129            }
130        }
131        
132        /**
133         * Returns a previously started Gravity instance. This method isn't synchronized and should be used in
134         * HttpServlet.doPost(...) methods only.
135         * 
136         * @param context the ServletContext from which to retrieve the Gravity instance. 
137         * @return the unique and started Gravity instance (or null if {@link #start(ServletConfig, ChannelFactory)}
138         *          has never been called).
139         */
140        public static Gravity getGravity(ServletContext context) {
141            return (Gravity)context.getAttribute(GRAVITY_KEY);
142        }
143        
144        
145        private static void configureServices(ServletContext servletContext, Class<?> serverFilterClass) throws ServletException {
146            ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(servletContext);
147            
148            ConfigProvider configProvider = (ConfigProvider)servletContext.getAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE);
149            
150            for (Field field : serverFilterClass.getDeclaredFields()) {
151                    if (field.isAnnotationPresent(MessagingDestination.class)) {
152                            MessagingDestination md = field.getAnnotation(MessagingDestination.class);
153                            AbstractMessagingDestination messagingDestination = new AbstractMessagingDestination();
154                            messagingDestination.setId(field.getName());
155                            messagingDestination.setNoLocal(md.noLocal());
156                            messagingDestination.setSessionSelector(md.sessionSelector());
157                            initSecurizer(messagingDestination, md.securizer(), configProvider);
158                            messagingDestination.initServices(servicesConfig);
159                    }
160                    else if (field.isAnnotationPresent(JmsTopicDestination.class)) {
161                            JmsTopicDestination md = field.getAnnotation(JmsTopicDestination.class);
162                            AbstractJmsTopicDestination messagingDestination = new AbstractJmsTopicDestination();
163                            messagingDestination.setId(field.getName());
164                            messagingDestination.setNoLocal(md.noLocal());
165                            messagingDestination.setSessionSelector(md.sessionSelector());
166                            initSecurizer(messagingDestination, md.securizer(), configProvider);
167                            messagingDestination.initServices(servicesConfig);
168                            messagingDestination.setName(md.name());
169                            messagingDestination.setTextMessages(md.textMessages());
170                            messagingDestination.setAcknowledgeMode(md.acknowledgeMode());
171                            messagingDestination.setConnectionFactory(md.connectionFactory());
172                            messagingDestination.setTransactedSessions(md.transactedSessions());
173                            messagingDestination.setJndiName(md.topicJndiName());
174                            messagingDestination.initServices(servicesConfig);
175                    }
176                    else if (field.isAnnotationPresent(ActiveMQTopicDestination.class)) {
177                            ActiveMQTopicDestination md = field.getAnnotation(ActiveMQTopicDestination.class);
178                            AbstractActiveMQTopicDestination messagingDestination = new AbstractActiveMQTopicDestination();
179                            messagingDestination.setId(field.getName());
180                            messagingDestination.setNoLocal(md.noLocal());
181                            messagingDestination.setSessionSelector(md.sessionSelector());
182                            initSecurizer(messagingDestination, md.securizer(), configProvider);
183                            messagingDestination.initServices(servicesConfig);
184                            messagingDestination.setName(md.name());
185                            messagingDestination.setTextMessages(md.textMessages());
186                            messagingDestination.setAcknowledgeMode(md.acknowledgeMode());
187                            messagingDestination.setConnectionFactory(md.connectionFactory());
188                            messagingDestination.setTransactedSessions(md.transactedSessions());
189                            messagingDestination.setJndiName(md.topicJndiName());
190                            messagingDestination.setBrokerUrl(md.brokerUrl());
191                            messagingDestination.setCreateBroker(md.createBroker());
192                            messagingDestination.setDurable(md.durable());
193                            messagingDestination.setWaitForStart(md.waitForStart());
194                            messagingDestination.setFileStoreRoot(md.fileStoreRoot());
195                            messagingDestination.initServices(servicesConfig);
196                    }
197            }
198        }
199        
200        private static void initSecurizer(AbstractMessagingDestination messagingDestination, Class<? extends GravityDestinationSecurizer> securizerClass, ConfigProvider configProvider) {
201                    if (securizerClass != GravityDestinationSecurizer.class) {
202                            if (configProvider != null)
203                                    messagingDestination.setSecurizer(configProvider.findInstance(securizerClass));
204                            if (messagingDestination.getSecurizer() == null)
205                                    messagingDestination.setSecurizerClassName(securizerClass.getName());
206                    }
207        }
208    }