package com.netflix.governator.event.guava;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import javax.inject.Inject;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.common.reflect.TypeToken;
import com.google.inject.AbstractModule;
import com.netflix.governator.event.ApplicationEvent;
import com.netflix.governator.event.ApplicationEventDispatcher;
import com.netflix.governator.event.ApplicationEventListener;
import com.netflix.governator.event.ApplicationEventModule;
import com.netflix.governator.event.ApplicationEventRegistration;
public final class GuavaApplicationEventModule extends AbstractModule {
@Override
protected void configure() {
install(new ApplicationEventModule());
bind(ApplicationEventDispatcher.class).to(GuavaApplicationEventDispatcher.class).asEagerSingleton();
}
@Override
public boolean equals(Object obj) {
return getClass().equals(obj.getClass());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public String toString() {
return "GuavaApplicationEventModule[]";
}
private static final class GuavaApplicationEventDispatcher implements ApplicationEventDispatcher {
private final EventBus eventBus;
private final Method eventListenerMethod;
@Inject
public GuavaApplicationEventDispatcher(EventBus eventBus) {
this.eventBus = eventBus;
try {
this.eventListenerMethod = ApplicationEventListener.class.getDeclaredMethod("onEvent", ApplicationEvent.class);
} catch (Exception e) {
throw new RuntimeException("Failed to cache ApplicationEventListener method", e);
}
}
public ApplicationEventRegistration registerListener(Object instance, Method method, Class<? extends ApplicationEvent> eventType) {
GuavaSubscriberProxy proxy = new GuavaSubscriberProxy(instance, method, eventType);
eventBus.register(proxy);
return new GuavaEventRegistration(eventBus, proxy);
}
public <T extends ApplicationEvent> ApplicationEventRegistration registerListener(Class<T> eventType, ApplicationEventListener<T> eventListener) {
GuavaSubscriberProxy proxy = new GuavaSubscriberProxy(eventListener, eventListenerMethod, eventType);
eventBus.register(proxy);
return new GuavaEventRegistration(eventBus, proxy);
}
public ApplicationEventRegistration registerListener(ApplicationEventListener<? extends ApplicationEvent> eventListener) {
Type[] genericInterfaces = eventListener.getClass().getGenericInterfaces();
for (Type type : genericInterfaces) {
if (ApplicationEventListener.class.isAssignableFrom(TypeToken.of(type).getRawType())) {
ParameterizedType ptype = (ParameterizedType) type;
Class<?> rawType = TypeToken.of(ptype.getActualTypeArguments()[0]).getRawType();
GuavaSubscriberProxy proxy = new GuavaSubscriberProxy(eventListener, eventListenerMethod, rawType);
eventBus.register(proxy);
return new GuavaEventRegistration(eventBus, proxy);
}
}
return new ApplicationEventRegistration() {
public void unregister() {} //no-op. Could not find anything to register.
};
}
private static class GuavaSubscriberProxy {
private final Object handlerInstance;
private final Method handlerMethod;
private final Class<?> acceptedType;
public GuavaSubscriberProxy(Object handlerInstance, Method handlerMethod, Class<?> acceptedType) {
this.handlerInstance = handlerInstance;
this.handlerMethod = handlerMethod;
this.acceptedType = acceptedType;
}
@Subscribe
public void invokeEventHandler(ApplicationEvent event)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (acceptedType.isAssignableFrom(event.getClass())) {
if (!handlerMethod.isAccessible()) {
handlerMethod.setAccessible(true);
}
handlerMethod.invoke(handlerInstance, event);
}
}
}
private static class GuavaEventRegistration implements ApplicationEventRegistration {
private final EventBus eventBus;
private final GuavaSubscriberProxy subscriber;
public GuavaEventRegistration(EventBus eventBus, GuavaSubscriberProxy subscriber) {
this.eventBus = eventBus;
this.subscriber = subscriber;
}
public void unregister() {
this.eventBus.unregister(subscriber);
}
}
@Override
public void publishEvent(ApplicationEvent event) {
this.eventBus.post(event);
}
}
}