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     */
022    package org.granite.gravity;
023    
024    import javax.servlet.ServletConfig;
025    import javax.servlet.ServletContext;
026    import javax.servlet.ServletException;
027    import javax.servlet.http.HttpServlet;
028    
029    import org.granite.config.ConfigProvider;
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.util.TypeUtil;
036    
037    /**
038     * @author Franck WOLFF
039     */
040    public class GravityManager {
041    
042            private static final String GRAVITY_KEY = Gravity.class.getName();
043            
044            /**
045             * Parse gravity configuration (granite-config.xml), start gravity by using the specified factory and put it
046             * in ServletContext. If Gravity is already started, returns the previous instance from the servlet context.
047             * <br><br>
048             * This method is intended to be used in {@link HttpServlet#init(ServletConfig)} methods only and
049             * synchronizes on the current ServletContext instance.
050             * 
051             * @param servletConfig the servlet config passed in HttpServlet.init(ServletConfig config) method.
052             * @return a newly created and started Gravity instance or previously started one.
053             * @throws ServletException if something goes wrong (GravityFactory not found, Gravity.start() error, etc.)
054             */
055        public static Gravity start(ServletConfig servletConfig) throws ServletException {
056            return start(servletConfig.getServletContext());
057        }
058        
059        public static Gravity start(ServletContext context) throws ServletException {
060            Gravity gravity = null;
061            
062            synchronized (context) {
063                    
064                    gravity = (Gravity)context.getAttribute(GRAVITY_KEY);
065                    
066                    if (gravity == null) {
067                            GraniteConfig graniteConfig = ServletGraniteConfig.loadConfig(context);
068                            ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(context);
069                        
070                            GravityServiceConfigurator serviceConfigurator = (GravityServiceConfigurator)context.getAttribute(GraniteConfigListener.GRANITE_CONFIG_ATTRIBUTE);
071                            if (serviceConfigurator != null)
072                                    serviceConfigurator.configureGravityServices(context);
073                        
074                            GravityConfig gravityConfig = new GravityConfig(graniteConfig);
075                            
076                            String gravityFactory = gravityConfig.getGravityFactory();
077                            try {
078                                            GravityFactory factory = TypeUtil.newInstance(gravityFactory, GravityFactory.class);
079                                            gravity = factory.newGravity(gravityConfig, servicesConfig, graniteConfig, GraniteConfigListener.getSharedContext(context));
080                                    } 
081                            catch (Exception e) {
082                                            throw new ServletException("Could not create Gravity instance with factory: " + gravityFactory, e);
083                                    }
084                    
085                            try {
086                                gravity.start();
087                                context.setAttribute(GRAVITY_KEY, gravity);
088    
089                        if (context.getAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE) != null)
090                            ((ConfigProvider)context.getAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE)).initGravity(gravity);
091    
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        public static interface GravityServiceConfigurator {
105            
106            public void configureGravityServices(ServletContext context) throws ServletException;
107        }
108        
109        
110        /**
111         * Reconfigure gravity with the new supplied configuration (after reloading granite-config.xml).
112         * <br><br>
113         * Only these configuration options are taken into account when reconfiguring Gravity:
114         * <ul>
115         *  <li>channelIdleTimeoutMillis</li>
116         *  <li>longPollingTimeout</li>
117         *  <li>retryOnError</li>
118         *  <li>maxMessagesQueuedPerChannel</li>
119         *  <li>corePoolSize</li>
120         *  <li>maximumPoolSize</li>
121         *  <li>keepAliveTimeMillis</li>
122         * </ul>
123         * 
124         * @param context the ServletContext where the gravity instance is registered.
125         * @param gravityConfig the new (reloaded) GravityConfig. 
126         */
127        public static void reconfigure(ServletContext context, GravityConfig gravityConfig) {
128            synchronized (context) {
129                    Gravity gravity = getGravity(context);
130                    gravity.reconfigure(gravityConfig, ServletGraniteConfig.getConfig(context));
131            }
132        }
133        
134        /**
135         * Returns a previously started Gravity instance. This method isn't synchronized and should be used in
136         * HttpServlet.doPost(...) methods only.
137         * 
138         * @param context the ServletContext from which to retrieve the Gravity instance. 
139         * @return the unique and started Gravity instance (or null if {@link #start(ServletConfig)}
140         *          has never been called).
141         */
142        public static Gravity getGravity(ServletContext context) {
143            return (Gravity)context.getAttribute(GRAVITY_KEY);
144        }
145        
146    }