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