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 }