package com.netflix.governator.event; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.inject.Provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; import com.google.inject.matcher.Matchers; import com.google.inject.spi.InjectionListener; import com.google.inject.spi.ProvisionListener; import com.google.inject.spi.TypeEncounter; import com.google.inject.spi.TypeListener; import com.netflix.governator.event.guava.GuavaApplicationEventModule; /** * Adds support for passing {@link ApplicationEvent}s. Default (Guava-based) implementation * can be found in {@link GuavaApplicationEventModule} * * See {@link EventListener} and {@link ApplicationEventDispatcher} for usage. */ public final class ApplicationEventModule extends AbstractModule { private static class ApplicationEventSubscribingTypeListener implements TypeListener { private static final Logger LOG = LoggerFactory.getLogger(ApplicationEventSubscribingTypeListener.class); private final Provider<ApplicationEventDispatcher> dispatcherProvider; public ApplicationEventSubscribingTypeListener(Provider<ApplicationEventDispatcher> dispatcherProvider) { this.dispatcherProvider = dispatcherProvider; } @Override public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) { final Class<?> clazz = type.getRawType(); final List<Method> handlerMethods = getAllDeclaredHandlerMethods(clazz); if(!handlerMethods.isEmpty()) { encounter.register(new InjectionListener<Object>() { @Override public void afterInjection(Object injectee) { for (final Method handlerMethod : handlerMethods) { dispatcherProvider.get().registerListener(injectee, handlerMethod, (Class<? extends ApplicationEvent>) handlerMethod.getParameterTypes()[0]); } } }); } } private List<Method> getAllDeclaredHandlerMethods(Class<?> clazz) { final List<Method> handlerMethods = new ArrayList<>(); while (clazz != null && !Collection.class.isAssignableFrom(clazz) && !clazz.isArray()) { for (final Method handlerMethod : clazz.getDeclaredMethods()) { if (handlerMethod.isAnnotationPresent(EventListener.class)) { if (handlerMethod.getReturnType().equals(Void.TYPE) && handlerMethod.getParameterTypes().length == 1 && ApplicationEvent.class.isAssignableFrom(handlerMethod.getParameterTypes()[0])) { handlerMethods.add(handlerMethod); } else { throw new IllegalArgumentException("@EventListener " + clazz.getName() + "." + handlerMethod.getName() + "skipped. Methods must be public, void, and accept exactly" + " one argument extending com.netflix.governator.event.ApplicationEvent."); } } } clazz = clazz.getSuperclass(); } return handlerMethods; } } private static class ApplicationEventSubscribingProvisionListener implements ProvisionListener { private final Provider<ApplicationEventDispatcher> dispatcherProvider; public ApplicationEventSubscribingProvisionListener(Provider<ApplicationEventDispatcher> dispatcherProvider) { this.dispatcherProvider = dispatcherProvider; } @Override public <T> void onProvision(ProvisionInvocation<T> provision) { T provisioned = provision.provision(); if (provisioned != null && provisioned instanceof ApplicationEventListener) { dispatcherProvider.get().registerListener((ApplicationEventListener)provisioned); } } } @Override protected void configure() { com.google.inject.Provider<ApplicationEventDispatcher> dispatcherProvider = binder().getProvider(ApplicationEventDispatcher.class); bindListener(Matchers.any(), new ApplicationEventSubscribingTypeListener(dispatcherProvider)); bindListener(Matchers.any(), new ApplicationEventSubscribingProvisionListener(dispatcherProvider)); } @Override public boolean equals(Object obj) { return getClass().equals(obj.getClass()); } @Override public int hashCode() { return getClass().hashCode(); } @Override public String toString() { return "ApplicationEventModule[]"; } }