/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat, Inc. and/or its affiliates, and individual * contributors by the @authors tag. See the copyright.txt in the * distribution for a full listing of individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.seam.jms; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.Resource; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.AfterBeanDiscovery; import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedParameter; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessAnnotatedType; import javax.inject.Inject; import javax.inject.Qualifier; import javax.jms.Destination; import org.jboss.solder.logging.Logger; import org.jboss.seam.jms.annotations.EventRouting; import org.jboss.seam.jms.annotations.Inbound; import org.jboss.seam.jms.annotations.Outbound; import org.jboss.seam.jms.annotations.Routing; import org.jboss.seam.jms.bridge.EgressRoutingObserver; import org.jboss.seam.jms.bridge.Route; import org.jboss.seam.jms.bridge.RouteImpl; import org.jboss.seam.jms.bridge.RouteManager; import org.jboss.seam.jms.bridge.RouteManagerImpl; import org.jboss.seam.jms.bridge.RouteType; import org.jboss.solder.core.VersionLoggerUtil; import org.jboss.solder.literal.NamedLiteral; /** * Seam 3 JMS Portable Extension * * @author Jordan Ganoff * @author <a href="john.d.ament@gmail.com">John Ament</a> */ public class Seam3JmsExtension implements Extension { private static final Logger log = Logger.getLogger(Seam3JmsExtension.class); private List<Route> ingressRoutes = new ArrayList<Route>(); private List<Route> egressRoutes = new ArrayList<Route>(); private List<EgressRoutingObserver> observerMethods = new ArrayList<EgressRoutingObserver>(); private Set<AnnotatedType<?>> eventRoutingRegistry = new HashSet<AnnotatedType<?>>(); private Set<AnnotatedMethod<?>> observerMethodRegistry = new HashSet<AnnotatedMethod<?>>(); private boolean readyToRoute = false; public void buildRoutes(@Observes final AfterBeanDiscovery abd, final BeanManager bm) { log.debug("Building JMS Routes."); RouteManager routeManager = new RouteManagerImpl(); for (AnnotatedType<?> at : eventRoutingRegistry) { Object instance = null; try { instance = at.getJavaClass().newInstance(); } catch (InstantiationException ex) { abd.addDefinitionError(ex); break; } catch (IllegalAccessException ex) { abd.addDefinitionError(ex); break; } for (AnnotatedMethod<?> am : at.getMethods()) { Object result = null; try { result = am.getJavaMember().invoke(instance, routeManager); } catch (IllegalAccessException ex) { abd.addDefinitionError(ex); } catch (IllegalArgumentException ex) { abd.addDefinitionError(ex); } catch (InvocationTargetException ex) { abd.addDefinitionError(ex); } if (result != null) { if (Collection.class.isAssignableFrom(result.getClass())) { @SuppressWarnings("unchecked") Collection<Route> routes = Collection.class.cast(result); for (Route route : routes) { if (route == null) { log.warn("No routes found for " + am); } else { addRoute(route); } } } else if (Route.class.isAssignableFrom(result.getClass())) { addRoute(Route.class.cast(result)); } else { abd.addDefinitionError(new IllegalArgumentException("Unsupported route configuration type: " + result)); } } } } /* when going through the observer method registry, we pick up cases where we dynamically build routes based on * observer interfaces. An example method of this can be seen in the test case ObserverInterface. */ for (AnnotatedMethod<?> m : observerMethodRegistry) { for (AnnotatedParameter<?> ap : m.getParameters()) { log.debug("In method " + m.getJavaMember().getName() + " with param type " + ap.getBaseType()); } Class<?> intfClazz = m.getJavaMember().getDeclaringClass(); String methodName = m.getJavaMember().getName(); String routeId = intfClazz.getCanonicalName() + "." + methodName; RouteType routeType = null; if(m.isAnnotationPresent(Inbound.class)) { routeType = RouteType.INGRESS; } else if(m.isAnnotationPresent(Outbound.class)) { routeType = RouteType.EGRESS; } else if (m.isAnnotationPresent(Routing.class)) { Routing routing = m.getAnnotation(Routing.class); routeType = routing.value(); } else { log.debug("Routing not found on method " + m.getJavaMember().getName()); routeType = RouteType.BOTH; } Route route = new RouteImpl(routeType).id(routeId); boolean isResourced = m.isAnnotationPresent(Resource.class); if (isResourced) { Resource r = m.getAnnotation(Resource.class); log.debug("Loading resource " + r.mappedName()); route.addDestinationJndiName(r.mappedName()); } /* in this case, each method has one non destination and multiple * destinations. this can take the form of resources or qualified * resources. */ try { for (AnnotatedParameter<?> ap : m.getParameters()) { if (ap.getBaseType() instanceof Class) { Class<?> clazz = (Class<?>) ap.getBaseType(); if (Destination.class.isAssignableFrom(clazz)) { log.debug("Found another type of qualifier."); //route.addDestinationQualifiers(ap.getAnnotations()); route.addAnnotatedParameter(ap); } else if (ap.isAnnotationPresent(Observes.class)) { route.setType(ap.getBaseType()); route.getQualifiers().addAll(getQualifiersFrom(ap.getAnnotations())); } } } } catch (Exception e) { log.warn("Exception mapping for method " + m.getJavaMember().getDeclaringClass() + "." + m.getJavaMember().getName() + ", ", e); } addRoute(route); } for (Route egress : this.egressRoutes) { EgressRoutingObserver ero = new EgressRoutingObserver(egress, this); abd.addObserverMethod(ero); this.observerMethods.add(ero); } } /** Test to remove. public <X> void decorateAnnotatedType(@Observes ProcessAnnotatedType<X> pat) { log.info("Calling decorate annotated type"); pat.setAnnotatedType(JmsAnnotatedTypeWrapper.decorate(pat.getAnnotatedType())); }**/ public void setBeanManager(BeanManager beanManager) { log.debug("Handling AfterDeploymentValidation, loading active bean manager into all beans."); VersionLoggerUtil.logVersionInformation(this.getClass()); if (!this.readyToRoute) { for (EgressRoutingObserver ero : this.observerMethods) { log.debug("Setting observer method beanmanager. " + beanManager); ero.setBeanManager(beanManager); } this.readyToRoute = true; } log.debug("EgressRoutingObservers: " + this.observerMethods); log.debug("Ingress routes: " + this.ingressRoutes); } public boolean isReadyToRoute() { return this.readyToRoute; } /** * Generates the observer method registry for all interfaces that have observer methods. * * @param pat */ public void registerObserverMethods(@Observes ProcessAnnotatedType<?> pat) { if (pat.getAnnotatedType().getJavaClass().isInterface()) { log.debug("Found a possible interface... " + pat.getAnnotatedType().getJavaClass()); for (AnnotatedMethod<?> m : pat.getAnnotatedType().getMethods()) { this.observerMethodRegistry.add(m); } } else { Set<AnnotatedMethod<?>> sams = new HashSet<AnnotatedMethod<?>>(); for (AnnotatedMethod<?> m : pat.getAnnotatedType().getMethods()) { if (m.isAnnotationPresent(EventRouting.class)) { sams.add(m); } } if (!sams.isEmpty()) { pat.veto(); this.eventRoutingRegistry.add(pat.getAnnotatedType()); } } } private void addRoute(Route route) { log.debug("RouteType is: " + route.getType()); if (route.validate()) { if (route.getType() == RouteType.EGRESS) { this.egressRoutes.add(route); } else if (route.getType() == RouteType.INGRESS) { this.ingressRoutes.add(route); } else { log.debug("Adding both types of routes."); this.egressRoutes.add(route); this.ingressRoutes.add(route); } } else { log.debugf("Not adding route %s to routes, it was not valid.", route); } } public List<Route> getIngressRoutes() { return this.ingressRoutes; } public List<Route> getEgressRoutes() { return this.egressRoutes; } private static Set<Annotation> getQualifiersFrom(Set<Annotation> annotations) { Set<Annotation> q = new HashSet<Annotation>(); log.debug("Annotations in getQualifiersFrom: " + annotations); for (Annotation a : annotations) { if (a.annotationType().isAnnotationPresent(Qualifier.class)) { q.add(a); } else { log.infof("Skipping annotation %s", a); } } return q; } }