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.config.servlet3;
023
024 import java.io.InputStream;
025 import java.lang.annotation.Annotation;
026 import java.lang.reflect.Field;
027 import java.lang.reflect.Method;
028 import java.math.BigDecimal;
029 import java.math.BigInteger;
030 import java.util.*;
031
032 import javax.servlet.FilterRegistration;
033 import javax.servlet.Servlet;
034 import javax.servlet.ServletContainerInitializer;
035 import javax.servlet.ServletContext;
036 import javax.servlet.ServletException;
037 import javax.servlet.ServletRegistration;
038 import javax.servlet.annotation.HandlesTypes;
039
040 import org.granite.config.ConfigProvider;
041 import org.granite.config.GraniteConfig;
042 import org.granite.config.GraniteConfigListener;
043 import org.granite.config.GraniteConfigListener.ServiceConfigurator;
044 import org.granite.config.ServletGraniteConfig;
045 import org.granite.config.flex.Channel;
046 import org.granite.config.flex.Destination;
047 import org.granite.config.flex.EndPoint;
048 import org.granite.config.flex.Factory;
049 import org.granite.config.flex.Service;
050 import org.granite.config.flex.ServicesConfig;
051 import org.granite.config.flex.ServletServicesConfig;
052 import org.granite.gravity.GravityManager.GravityServiceConfigurator;
053 import org.granite.gravity.config.AbstractActiveMQTopicDestination;
054 import org.granite.gravity.config.AbstractJmsTopicDestination;
055 import org.granite.gravity.config.AbstractMessagingDestination;
056 import org.granite.gravity.config.servlet3.ActiveMQTopicDestination;
057 import org.granite.gravity.config.servlet3.JmsTopicDestination;
058 import org.granite.gravity.config.servlet3.MessagingDestination;
059 import org.granite.gravity.security.GravityDestinationSecurizer;
060 import org.granite.logging.Logger;
061 import org.granite.messaging.amf.io.util.externalizer.BigDecimalExternalizer;
062 import org.granite.messaging.amf.io.util.externalizer.BigIntegerExternalizer;
063 import org.granite.messaging.amf.io.util.externalizer.Externalizer;
064 import org.granite.messaging.amf.io.util.externalizer.LongExternalizer;
065 import org.granite.messaging.amf.process.AMF3MessageInterceptor;
066 import org.granite.messaging.service.ExceptionConverter;
067 import org.granite.messaging.service.ServiceFactory;
068 import org.granite.messaging.service.SimpleServiceFactory;
069 import org.granite.messaging.service.security.RemotingDestinationSecurizer;
070 import org.granite.messaging.service.security.SecurityService;
071 import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher;
072 import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher;
073 import org.granite.messaging.service.tide.TideComponentNameMatcher;
074 import org.granite.messaging.service.tide.TideComponentTypeMatcher;
075 import org.granite.messaging.webapp.AMFMessageFilter;
076 import org.granite.messaging.webapp.AMFMessageServlet;
077 import org.granite.util.TypeUtil;
078 import org.granite.util.XMap;
079
080 /**
081 * @author William DRAI
082 */
083 @HandlesTypes({ServerFilter.class})
084 public class GraniteServlet3Initializer implements ServletContainerInitializer {
085
086 public void onStartup(Set<Class<?>> scannedClasses, ServletContext servletContext) throws ServletException {
087 Set<Class<?>> classes = new HashSet<Class<?>>();
088 if (scannedClasses != null) {
089 classes.addAll(scannedClasses);
090 classes.remove(ServerFilter.class); // JBossWeb adds the annotation ???
091 }
092 if (classes.size() > 1)
093 throw new ServletException("Application must have only one ServerFilter configuration class");
094
095 if (!classes.isEmpty()) {
096 // Configure GraniteDS only if we find a config class annotated with @ServerFilter
097 Class<?> clazz = classes.iterator().next();
098 ServerFilter serverFilter = clazz.getAnnotation(ServerFilter.class);
099
100 servletContext.setAttribute(GraniteConfigListener.GRANITE_CONFIG_ATTRIBUTE, new Servlet3ServiceConfigurator(clazz));
101
102 servletContext.addListener(new GraniteConfigListener());
103
104 if (servletContext.getFilterRegistration("AMFMessageFilter") == null) {
105 FilterRegistration.Dynamic graniteFilter = servletContext.addFilter("AMFMessageFilter", AMFMessageFilter.class);
106 graniteFilter.addMappingForUrlPatterns(null, true, serverFilter.graniteUrlMapping());
107 }
108 if (servletContext.getServletRegistration("AMFMessageServlet") == null) {
109 ServletRegistration.Dynamic graniteServlet = servletContext.addServlet("AMFMessageServlet", AMFMessageServlet.class);
110 graniteServlet.setLoadOnStartup(1);
111 graniteServlet.addMapping(serverFilter.graniteUrlMapping());
112 }
113
114 try {
115 if (servletContext.getServletRegistration("GravityServlet") == null) {
116 Class<? extends Servlet> gravityAsyncServletClass = TypeUtil.forName("org.granite.gravity.servlet3.GravityAsyncServlet", Servlet.class);
117 ServletRegistration.Dynamic gravityServlet = servletContext.addServlet("GravityServlet", gravityAsyncServletClass);
118 gravityServlet.setLoadOnStartup(1);
119 gravityServlet.setAsyncSupported(true);
120 gravityServlet.addMapping(serverFilter.gravityUrlMapping());
121 }
122 }
123 catch (ClassNotFoundException e) {
124 servletContext.log("Could not setup GravityAsyncServlet", e);
125 }
126 }
127 }
128
129
130 public static class Servlet3ServiceConfigurator implements ServiceConfigurator, GravityServiceConfigurator {
131
132 private static final Logger log = Logger.getLogger(Servlet3ServiceConfigurator.class);
133
134 private final Class<?> serverFilterClass;
135
136 public Servlet3ServiceConfigurator(Class<?> serverFilterClass) {
137 this.serverFilterClass = serverFilterClass;
138 }
139
140 public void initialize(ServletContext servletContext) {
141 servletContext.setAttribute(ServletGraniteConfig.GRANITE_CONFIG_DEFAULT_KEY, "org/granite/config/servlet3/granite-config-servlet3.xml");
142 }
143
144 public void configureServices(ServletContext servletContext) throws ServletException {
145 GraniteConfig graniteConfig = ServletGraniteConfig.loadConfig(servletContext);
146 ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(servletContext, true);
147
148 ServerFilter serverFilter = serverFilterClass.getAnnotation(ServerFilter.class);
149
150 ConfigProvider configProvider = null;
151
152 boolean useTide = serverFilter.tide();
153 String type = serverFilter.type();
154 Class<?> factoryClass = null;
155 Set<Class<?>> tideInterfaces = new HashSet<Class<?>>(Arrays.asList(serverFilter.tideInterfaces()));
156 Set<Class<? extends Annotation>> tideAnnotations = new HashSet<Class<? extends Annotation>>(Arrays.asList(serverFilter.tideAnnotations()));
157
158 if (!serverFilter.configProviderClass().equals(ConfigProvider.class)) {
159 try {
160 configProvider = TypeUtil.newInstance(serverFilter.configProviderClass(), new Class[] { ServletContext.class }, new Object[] { servletContext });
161
162 servletContext.setAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE, configProvider);
163
164 if (configProvider.useTide() != null)
165 useTide = configProvider.useTide();
166
167 if (configProvider.getType() != null)
168 type = configProvider.getType();
169
170 factoryClass = configProvider.getFactoryClass();
171
172 if (configProvider.getTideInterfaces() != null)
173 tideInterfaces.addAll(Arrays.asList(configProvider.getTideInterfaces()));
174
175 if (configProvider.getTideAnnotations() != null)
176 tideAnnotations.addAll(Arrays.asList(configProvider.getTideAnnotations()));
177 }
178 catch (Exception e) {
179 log.error(e, "Could not set config provider of type %s", serverFilter.configProviderClass().getName());
180 }
181 }
182
183 if (!serverFilter.factoryClass().equals(ServiceFactory.class))
184 factoryClass = serverFilter.factoryClass();
185 else if (!serverFilter.factoryClassName().equals("")) {
186 try {
187 factoryClass = TypeUtil.forName(serverFilter.factoryClassName(), ServiceFactory.class);
188 }
189 catch (ClassNotFoundException e) {
190 throw new ServletException("Could not find service factory class " + serverFilter.factoryClassName(), e);
191 }
192 }
193
194 if (factoryClass == null) {
195 factoryClass = SimpleServiceFactory.class;
196 useTide = false;
197 }
198
199 for (Class<?> ti : tideInterfaces) {
200 try {
201 graniteConfig.getTideComponentMatchers().add(new TideComponentInstanceOfMatcher(ti.getName(), false));
202 log.debug("Enabled components implementing %s for Tide remoting", ti);
203 }
204 catch (Exception e) {
205 log.error(e, "Could not add tide-component interface %s", ti);
206 }
207 }
208 for (Class<? extends Annotation> ta : tideAnnotations) {
209 try {
210 graniteConfig.getTideComponentMatchers().add(new TideComponentAnnotatedWithMatcher(ta.getName(), false));
211 log.debug("Enabled components annotated with %s for Tide remoting", ta);
212 }
213 catch (Exception e) {
214 log.error(e, "Could not add tide-component annotation %s", ta);
215 }
216 }
217 for (String tn : serverFilter.tideNames()) {
218 try {
219 graniteConfig.getTideComponentMatchers().add(new TideComponentNameMatcher(tn, false));
220 log.debug("Enabled components with name %s for Tide remoting", tn);
221 }
222 catch (Exception e) {
223 log.error(e, "Could not add tide-component name %s", tn);
224 }
225 }
226 for (String tt : serverFilter.tideTypes()) {
227 try {
228 graniteConfig.getTideComponentMatchers().add(new TideComponentTypeMatcher(tt, false));
229 log.debug("Enabled components with type %s for Tide remoting", tt);
230 }
231 catch (Exception e) {
232 log.error(e, "Could not add tide-component type %s", tt);
233 }
234 }
235
236 for (Class<? extends ExceptionConverter> ec : serverFilter.exceptionConverters()) {
237 graniteConfig.registerExceptionConverter(ec, true);
238 log.debug("Registered exception converter %s", ec);
239 }
240 if (configProvider != null) {
241 for (ExceptionConverter ec : configProvider.findInstances(ExceptionConverter.class)) {
242 graniteConfig.registerExceptionConverter(ec, true);
243 log.debug("Registered exception converter %s", ec.getClass());
244 }
245 }
246
247 if (configProvider != null) {
248 for (Externalizer ext : configProvider.findInstances(Externalizer.class)) {
249 graniteConfig.registerExternalizer(ext);
250 log.debug("Registered externalizer %s", ext.getClass());
251 }
252 }
253
254 if (serverFilter.useBigDecimal())
255 graniteConfig.setExternalizersByType(BigDecimal.class.getName(), BigDecimalExternalizer.class.getName());
256
257 if (serverFilter.useBigInteger())
258 graniteConfig.setExternalizersByType(BigInteger.class.getName(), BigIntegerExternalizer.class.getName());
259
260 if (serverFilter.useLong())
261 graniteConfig.setExternalizersByType(Long.class.getName(), LongExternalizer.class.getName());
262
263 if (!serverFilter.securityServiceClass().equals(SecurityService.class)) {
264 try {
265 graniteConfig.setSecurityService(TypeUtil.newInstance(serverFilter.securityServiceClass(), SecurityService.class));
266 }
267 catch (Exception e) {
268 throw new ServletException("Could not setup security service", e);
269 }
270 }
271 else if (graniteConfig.getSecurityService() == null && configProvider != null) {
272 SecurityService securityService = configProvider.findInstance(SecurityService.class);
273 graniteConfig.setSecurityService(securityService);
274 }
275 if (graniteConfig.getSecurityService() == null) {
276 String securityServiceClassName = null;
277 // Try auto-detect
278 try {
279 TypeUtil.forName("org.mortbay.jetty.Request");
280 securityServiceClassName = "org.granite.messaging.service.security.Jetty6SecurityService";
281 }
282 catch (ClassNotFoundException e1) {
283 try {
284 TypeUtil.forName("org.eclipse.jetty.server.Request");
285 securityServiceClassName = "org.granite.messaging.service.security.Jetty7SecurityService";
286 }
287 catch (ClassNotFoundException e1b) {
288 try {
289 TypeUtil.forName("weblogic.servlet.security.ServletAuthentication");
290 securityServiceClassName = "org.granite.messaging.service.security.WebLogicSecurityService";
291 }
292 catch (ClassNotFoundException e2) {
293 try {
294 TypeUtil.forName("com.sun.appserv.server.LifecycleEvent");
295 securityServiceClassName = "org.granite.messaging.service.security.GlassFishV3SecurityService";
296 }
297 catch (ClassNotFoundException e3) {
298 securityServiceClassName = "org.granite.messaging.service.security.Tomcat7SecurityService";
299 }
300 }
301 try {
302 graniteConfig.setSecurityService((SecurityService)TypeUtil.newInstance(securityServiceClassName));
303 }
304 catch (Exception e) {
305 throw new ServletException("Could not setup security service " + securityServiceClassName, e);
306 }
307 }
308 }
309 }
310
311 if (!serverFilter.amf3MessageInterceptor().equals(AMF3MessageInterceptor.class)) {
312 try {
313 graniteConfig.setAmf3MessageInterceptor(TypeUtil.newInstance(serverFilter.amf3MessageInterceptor(), AMF3MessageInterceptor.class));
314 }
315 catch (Exception e) {
316 throw new ServletException("Could not setup amf3 message interceptor", e);
317 }
318 }
319 else if (graniteConfig.getAmf3MessageInterceptor() == null && configProvider != null) {
320 AMF3MessageInterceptor amf3MessageInterceptor = configProvider.findInstance(AMF3MessageInterceptor.class);
321 graniteConfig.setAmf3MessageInterceptor(amf3MessageInterceptor);
322 }
323
324 Channel channel = servicesConfig.findChannelById("graniteamf");
325 if (channel == null) {
326 channel = new Channel("graniteamf", "mx.messaging.channels.AMFChannel",
327 new EndPoint("http://{server.name}:{server.port}/{context.root}/graniteamf/amf", "flex.messaging.endpoints.AMFEndpoint"),
328 new XMap());
329 servicesConfig.addChannel(channel);
330 }
331
332 XMap factoryProperties = new XMap();
333 String lookup = null;
334 if (useTide) {
335 lookup = "java:global/{context.root}/{capitalized.component.name}Bean";
336 if (isJBoss6())
337 lookup = "{capitalized.component.name}Bean/local";
338 if (!("".equals(serverFilter.ejbLookup())))
339 lookup = serverFilter.ejbLookup();
340 }
341 else {
342 lookup = "java:global/{context.root}/{capitalized.destination.id}Bean";
343 if (isJBoss6())
344 lookup = "{capitalized.destination.id}Bean/local";
345 if (!("".equals(serverFilter.ejbLookup())))
346 lookup = serverFilter.ejbLookup();
347 }
348 if (lookup.indexOf("{context.root}") >= 0) {
349 try {
350 // Call by reflection because of JDK 1.4
351 Method m = servletContext.getClass().getMethod("getContextPath");
352 m.setAccessible(true);
353 String contextPath = (String)m.invoke(servletContext);
354 lookup = lookup.replace("{context.root}", contextPath.substring(1));
355 }
356 catch (Exception e) {
357 log.error(e, "Could not get context path, please define lookup manually in @ServerFilter");
358 }
359 }
360 factoryProperties.put("lookup", lookup);
361
362 if (useTide) {
363 Factory factory = servicesConfig.findFactoryById("tide-" + type + "-factory");
364 if (factory == null) {
365 factory = new Factory("tide-" + type + "-factory", factoryClass.getName(), factoryProperties);
366 servicesConfig.addFactory(factory);
367 }
368
369 Service service = servicesConfig.findServiceById("granite-service");
370 if (service == null) {
371 service = new Service("granite-service", "flex.messaging.services.RemotingService",
372 "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
373 List<String> channelIds = new ArrayList<String>();
374 channelIds.add("graniteamf");
375 List<String> tideRoles = serverFilter.tideRoles().length == 0 ? null : Arrays.asList(serverFilter.tideRoles());
376 Destination destination = new Destination(type, channelIds, new XMap(), tideRoles, null, null);
377 destination.getProperties().put("factory", "tide-" + type + "-factory");
378 if (!("".equals(serverFilter.entityManagerFactoryJndiName())))
379 destination.getProperties().put("entity-manager-factory-jndi-name", serverFilter.entityManagerFactoryJndiName());
380 else if (!("".equals(serverFilter.entityManagerJndiName())))
381 destination.getProperties().put("entity-manager-jndi-name", serverFilter.entityManagerJndiName());
382 if (!("".equals(serverFilter.validatorClassName())))
383 destination.getProperties().put("validator-class-name", serverFilter.validatorClassName());
384 service.getDestinations().put(type, destination);
385
386 if (destination.getSecurizer() == null && configProvider != null) {
387 RemotingDestinationSecurizer securizer = configProvider.findInstance(RemotingDestinationSecurizer.class);
388 destination.setSecurizer(securizer);
389 }
390
391 servicesConfig.addService(service);
392 }
393
394 if (factoryClass.getName().equals("org.granite.tide.ejb.EjbServiceFactory"))
395 servicesConfig.scan(null);
396
397 log.info("Registered Tide " + factoryClass + " service factory and " + type + " destination");
398 }
399 else {
400 Factory factory = new Factory(type + "-factory", factoryClass.getName(), factoryProperties);
401 servicesConfig.addFactory(factory);
402
403 Service service = new Service("granite-service", "flex.messaging.services.RemotingService",
404 "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
405 servicesConfig.addService(service);
406
407 servicesConfig.scan(null);
408
409 log.info("Registered " + factoryClass + " service factory");
410 }
411 }
412
413 private static boolean isJBoss6() {
414 try {
415 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/org/jboss/version.properties");
416 if (is != null)
417 return true;
418 }
419 catch (Throwable t) {
420 }
421 return false;
422 }
423
424 private static void initSecurizer(AbstractMessagingDestination messagingDestination, Class<? extends GravityDestinationSecurizer> securizerClass, ConfigProvider configProvider) {
425 if (securizerClass != GravityDestinationSecurizer.class) {
426 if (configProvider != null)
427 messagingDestination.setSecurizer(configProvider.findInstance(securizerClass));
428 if (messagingDestination.getSecurizer() == null)
429 messagingDestination.setSecurizerClassName(securizerClass.getName());
430 }
431 }
432
433
434 public void configureGravityServices(ServletContext servletContext) throws ServletException {
435 ServicesConfig servicesConfig = ServletServicesConfig.loadConfig(servletContext);
436
437 ConfigProvider configProvider = (ConfigProvider)servletContext.getAttribute(GraniteConfigListener.GRANITE_CONFIG_PROVIDER_ATTRIBUTE);
438
439 for (Field field : serverFilterClass.getDeclaredFields()) {
440 if (field.isAnnotationPresent(MessagingDestination.class)) {
441 MessagingDestination md = field.getAnnotation(MessagingDestination.class);
442 AbstractMessagingDestination messagingDestination = new AbstractMessagingDestination();
443 messagingDestination.setId(field.getName());
444 messagingDestination.setNoLocal(md.noLocal());
445 messagingDestination.setSessionSelector(md.sessionSelector());
446 initSecurizer(messagingDestination, md.securizer(), configProvider);
447 messagingDestination.initServices(servicesConfig);
448 }
449 else if (field.isAnnotationPresent(JmsTopicDestination.class)) {
450 JmsTopicDestination md = field.getAnnotation(JmsTopicDestination.class);
451 AbstractJmsTopicDestination messagingDestination = new AbstractJmsTopicDestination();
452 messagingDestination.setId(field.getName());
453 messagingDestination.setNoLocal(md.noLocal());
454 messagingDestination.setSessionSelector(md.sessionSelector());
455 initSecurizer(messagingDestination, md.securizer(), configProvider);
456 messagingDestination.initServices(servicesConfig);
457 messagingDestination.setName(md.name());
458 messagingDestination.setTextMessages(md.textMessages());
459 messagingDestination.setAcknowledgeMode(md.acknowledgeMode());
460 messagingDestination.setConnectionFactory(md.connectionFactory());
461 messagingDestination.setTransactedSessions(md.transactedSessions());
462 messagingDestination.setJndiName(md.topicJndiName());
463 messagingDestination.initServices(servicesConfig);
464 }
465 else if (field.isAnnotationPresent(ActiveMQTopicDestination.class)) {
466 ActiveMQTopicDestination md = field.getAnnotation(ActiveMQTopicDestination.class);
467 AbstractActiveMQTopicDestination messagingDestination = new AbstractActiveMQTopicDestination();
468 messagingDestination.setId(field.getName());
469 messagingDestination.setNoLocal(md.noLocal());
470 messagingDestination.setSessionSelector(md.sessionSelector());
471 initSecurizer(messagingDestination, md.securizer(), configProvider);
472 messagingDestination.initServices(servicesConfig);
473 messagingDestination.setName(md.name());
474 messagingDestination.setTextMessages(md.textMessages());
475 messagingDestination.setAcknowledgeMode(md.acknowledgeMode());
476 messagingDestination.setConnectionFactory(md.connectionFactory());
477 messagingDestination.setTransactedSessions(md.transactedSessions());
478 messagingDestination.setJndiName(md.topicJndiName());
479 messagingDestination.setBrokerUrl(md.brokerUrl());
480 messagingDestination.setCreateBroker(md.createBroker());
481 messagingDestination.setDurable(md.durable());
482 messagingDestination.setWaitForStart(md.waitForStart());
483 messagingDestination.setFileStoreRoot(md.fileStoreRoot());
484 messagingDestination.initServices(servicesConfig);
485 }
486 }
487 }
488 }
489 }