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 */
022package org.granite.gravity;
023
024import javax.naming.InitialContext;
025import javax.servlet.ServletConfig;
026import javax.servlet.ServletContext;
027import javax.servlet.ServletException;
028import javax.servlet.http.HttpServlet;
029
030import org.granite.config.ConfigProvider;
031import org.granite.config.GraniteConfig;
032import org.granite.config.GraniteConfigListener;
033import org.granite.config.ServletGraniteConfig;
034import org.granite.config.flex.ServicesConfig;
035import org.granite.config.flex.ServletServicesConfig;
036import org.granite.logging.Logger;
037import org.granite.util.TypeUtil;
038
039import java.lang.reflect.Method;
040
041/**
042 * @author Franck WOLFF
043 */
044public class GravityManager {
045
046    private static final Logger log = Logger.getLogger(GravityManager.class);
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         * @return a newly created and started Gravity instance or previously started one.
059         * @throws ServletException if something goes wrong (GravityFactory not found, Gravity.start() error, etc.)
060         */
061    public static Gravity start(ServletConfig servletConfig) throws ServletException {
062        return start(servletConfig.getServletContext());
063    }
064    
065    public static Gravity start(ServletContext context) throws ServletException {
066        Gravity gravity = null;
067        
068        synchronized (context) {
069                
070                gravity = (Gravity)context.getAttribute(GRAVITY_KEY);
071                
072                if (gravity == null) {
073                        GraniteConfig graniteConfig = ServletGraniteConfig.loadConfig(context);
074                        ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(context);
075                    
076                        GravityServiceConfigurator serviceConfigurator = (GravityServiceConfigurator)context.getAttribute(GraniteConfigListener.GRANITE_CONFIG_ATTRIBUTE);
077                        if (serviceConfigurator != null)
078                                serviceConfigurator.configureGravityServices(context);
079                    
080                        GravityConfig gravityConfig = new GravityConfig(graniteConfig);
081                        
082                        String gravityFactory = gravityConfig.getGravityFactory();
083                        try {
084                                        GravityFactory factory = TypeUtil.newInstance(gravityFactory, GravityFactory.class);
085                                        gravity = factory.newGravity(gravityConfig, servicesConfig, graniteConfig, GraniteConfigListener.getSharedContext(context));
086                                } 
087                        catch (Exception e) {
088                                        throw new ServletException("Could not create Gravity instance with factory: " + gravityFactory, e);
089                                }
090                
091                        try {
092                            gravity.start();
093                            context.setAttribute(GRAVITY_KEY, gravity);
094
095                    if (context.getAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE) != null)
096                        ((ConfigProvider)context.getAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE)).initGravity(gravity);
097
098                            GraniteConfigListener.registerShutdownListener(context, gravity);
099                        }
100                        catch (Exception e) {
101                            throw new ServletException("Gravity initialization error", e);
102                        }
103                }
104        }
105
106        return gravity;
107    }
108    
109    
110    public static interface GravityServiceConfigurator {
111        
112        public void configureGravityServices(ServletContext context) throws ServletException;
113    }
114    
115    
116    /**
117     * Reconfigure gravity with the new supplied configuration (after reloading granite-config.xml).
118     * <br><br>
119     * Only these configuration options are taken into account when reconfiguring Gravity:
120     * <ul>
121     *  <li>channelIdleTimeoutMillis</li>
122     *  <li>longPollingTimeout</li>
123     *  <li>retryOnError</li>
124     *  <li>maxMessagesQueuedPerChannel</li>
125     *  <li>corePoolSize</li>
126     *  <li>maximumPoolSize</li>
127     *  <li>keepAliveTimeMillis</li>
128     * </ul>
129     * 
130     * @param context the ServletContext where the gravity instance is registered.
131     * @param gravityConfig the new (reloaded) GravityConfig. 
132     */
133    public static void reconfigure(ServletContext context, GravityConfig gravityConfig) {
134        synchronized (context) {
135                Gravity gravity = getGravity(context);
136                gravity.reconfigure(gravityConfig, ServletGraniteConfig.getConfig(context));
137        }
138    }
139    
140    /**
141     * Returns a previously started Gravity instance. This method isn't synchronized and should be used in
142     * HttpServlet.doPost(...) methods only.
143     * 
144     * @param context the ServletContext from which to retrieve the Gravity instance. 
145     * @return the unique and started Gravity instance (or null if {@link #start(ServletConfig)}
146     *          has never been called).
147     */
148    public static Gravity getGravity(ServletContext context) {
149        return (Gravity)context.getAttribute(GRAVITY_KEY);
150    }
151    
152}