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.config.servlet3; 023 024import java.io.InputStream; 025import java.lang.annotation.Annotation; 026import java.lang.reflect.Field; 027import java.lang.reflect.Method; 028import java.math.BigDecimal; 029import java.math.BigInteger; 030import java.util.*; 031 032import javax.servlet.FilterRegistration; 033import javax.servlet.Servlet; 034import javax.servlet.ServletContainerInitializer; 035import javax.servlet.ServletContext; 036import javax.servlet.ServletException; 037import javax.servlet.ServletRegistration; 038import javax.servlet.annotation.HandlesTypes; 039 040import org.granite.config.ConfigProvider; 041import org.granite.config.GraniteConfig; 042import org.granite.config.GraniteConfigListener; 043import org.granite.config.GraniteConfigListener.ServiceConfigurator; 044import org.granite.config.ServletGraniteConfig; 045import org.granite.config.flex.Channel; 046import org.granite.config.flex.Destination; 047import org.granite.config.flex.EndPoint; 048import org.granite.config.flex.Factory; 049import org.granite.config.flex.Service; 050import org.granite.config.flex.ServicesConfig; 051import org.granite.config.flex.ServletServicesConfig; 052import org.granite.gravity.GravityManager.GravityServiceConfigurator; 053import org.granite.gravity.config.AbstractActiveMQTopicDestination; 054import org.granite.gravity.config.AbstractJmsTopicDestination; 055import org.granite.gravity.config.AbstractMessagingDestination; 056import org.granite.gravity.config.servlet3.ActiveMQTopicDestination; 057import org.granite.gravity.config.servlet3.JmsTopicDestination; 058import org.granite.gravity.config.servlet3.MessagingDestination; 059import org.granite.gravity.security.GravityDestinationSecurizer; 060import org.granite.logging.Logger; 061import org.granite.messaging.amf.io.util.externalizer.BigDecimalExternalizer; 062import org.granite.messaging.amf.io.util.externalizer.BigIntegerExternalizer; 063import org.granite.messaging.amf.io.util.externalizer.Externalizer; 064import org.granite.messaging.amf.io.util.externalizer.LongExternalizer; 065import org.granite.messaging.amf.process.AMF3MessageInterceptor; 066import org.granite.messaging.service.ExceptionConverter; 067import org.granite.messaging.service.ServiceFactory; 068import org.granite.messaging.service.SimpleServiceFactory; 069import org.granite.messaging.service.security.RemotingDestinationSecurizer; 070import org.granite.messaging.service.security.SecurityService; 071import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher; 072import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher; 073import org.granite.messaging.service.tide.TideComponentNameMatcher; 074import org.granite.messaging.service.tide.TideComponentTypeMatcher; 075import org.granite.messaging.webapp.AMFMessageFilter; 076import org.granite.messaging.webapp.AMFMessageServlet; 077import org.granite.util.TypeUtil; 078import org.granite.util.XMap; 079 080/** 081 * @author William DRAI 082 */ 083@HandlesTypes({ServerFilter.class}) 084public 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}