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 }