/****************************************************************************************** MIT License Copyright (c) 2012 Benjamin Diedrichsen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ********************************************************************************************/ package net.engio.mbassy.listener; import net.engio.mbassy.common.IPredicate; import net.engio.mbassy.common.ReflectionUtils; import net.engio.mbassy.subscription.MessageEnvelope; import java.lang.reflect.Method; import java.util.*; /** * * The meta data reader is responsible for parsing and validating message handler configurations. * * @author bennidi * Date: 11/16/12 */ public class MetadataReader { // This predicate is used to find all message listeners (methods annotated with @Subscribe // private static final IPredicate<Method> AllMessageHandlers = new IPredicate<Method>() { @Override public boolean apply(Method target) { return target.getAnnotation(Listener.class) != null; } }; // cache already created filter instances private final Map<Class<? extends IMessageFilter>, IMessageFilter> filterCache = new HashMap<Class<? extends IMessageFilter>, IMessageFilter>(); // retrieve all instances of filters associated with the given subscription private IMessageFilter[] getFilter(Listener subscription){ if (subscription.filters().length == 0) return null; IMessageFilter[] filters = new IMessageFilter[subscription.filters().length]; int i = 0; for (Filter filterDef : subscription.filters()) { IMessageFilter filter = filterCache.get(filterDef.value()); if (filter == null) { try{ filter = filterDef.value().newInstance(); filterCache.put(filterDef.value(), filter); } catch (Exception e){ throw new RuntimeException(e);// propagate as runtime exception } } filters[i] = filter; i++; } return filters; } public MessageHandlerMetadata getHandlerMetadata(Method messageHandler){ Listener config = messageHandler.getAnnotation(Listener.class); return new MessageHandlerMetadata(messageHandler, getFilter(config), config); } // get all listeners defined by the given class (includes // listeners defined in super classes) public List<MessageHandlerMetadata> getMessageHandlers(Class<?> target) { // get all handlers (this will include all (inherited) methods directly annotated using @Listener List<Method> allHandlers = ReflectionUtils.getMethods(AllMessageHandlers, target); // retain only those that are at the bottom of their respective class hierarchy (deepest overriding method) List<Method> bottomMostHandlers = new LinkedList<Method>(); for(Method handler : allHandlers){ if(!ReflectionUtils.containsOverridingMethod(allHandlers, handler)){ bottomMostHandlers.add(handler); } } List<MessageHandlerMetadata> filteredHandlers = new LinkedList<MessageHandlerMetadata>(); // for each handler there will be no overriding method that specifies @Listener // but an overriding method does inherit the listener configuration of the overwritten method for(Method handler : bottomMostHandlers){ Listener listener = handler.getAnnotation(Listener.class); if(!listener.enabled() || !isValidMessageHandler(handler)) continue; // disabled or invalid listeners are ignored Method overriddenHandler = ReflectionUtils.getOverridingMethod(handler, target); // if a handler is overwritten it inherits the configuration of its parent method MessageHandlerMetadata handlerMetadata = new MessageHandlerMetadata(overriddenHandler == null ? handler : overriddenHandler, getFilter(listener), listener); filteredHandlers.add(handlerMetadata); } return filteredHandlers; } public <T> MessageListenerMetadata<T> getMessageListener(Class<T> target) { return new MessageListenerMetadata(getMessageHandlers(target), target); } private boolean isValidMessageHandler(Method handler) { if(handler == null || handler.getAnnotation(Listener.class) == null){ return false; } if (handler.getParameterTypes().length != 1) { // a messageHandler only defines one parameter (the message) System.out.println("Found no or more than one parameter in messageHandler [" + handler.getName() + "]. A messageHandler must define exactly one parameter"); return false; } Enveloped envelope = handler.getAnnotation(Enveloped.class); if(envelope != null && !MessageEnvelope.class.isAssignableFrom(handler.getParameterTypes()[0])){ System.out.println("Message envelope configured but no subclass of MessageEnvelope found as parameter"); return false; } if(envelope != null && envelope.messages().length == 0){ System.out.println("Message envelope configured but message types defined for handler"); return false; } return true; } }