[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
Re: [hudson-dev] Going to a event driven architecture?
|
2012/3/8 Winston Prakash <winston.prakash@xxxxxxxxx>:
>>> Having a Event Registry does makes sense, if the consumers of the events
>>> are
>>> known like RSS
>>
>> Why only of the consumers are known?,
>>
>> I am asking since the main consumers of events will most likely be 3.
>> party plugins which are not "known". or have I misunderstood you?
>
> See your point. How do the consumer (3rd party plugins) know about the
> event. Something like
>
> - The plugin that publishes the events documents the events?
> - Centralized UI to view the available events and their usage?
While I was coding my example I stumbled across hudson-service and
noticed that it also had a small event framework, so we should
properly evaluate both my attempt and the "current".
The current one work by having a single list of consumers which is
made up of @Named components implementing the interface
"EventConsumer" which exposes a method consume(EventObject), Producers
can then create a EventObject or subclass and have that published to
all subscribers. The benefit of this approach is that consumers only
have to create a implementation of said interface, but on the other
hand consumers must receive all event types and filter using
instanceof
My approach is more like swing events, where producers register a
listener interface returning a publisher. The publisher object has the
same interface as the listener so the producer simply calls the right
method on it. On the consumer side the client create a class
implementing the listener interface, and register that object with the
EventRegistry
I have attached a file with the code from my prototype. The prototype
works on my machine, but I know it is not complete as I have only
tested events produced and consumed in the same plugin and because
there most likely is something with classloader handling in the
reflection proxy.
Best regards
Henrik
org.eclipse.hudson.events.EventRegistry:
----------------------------------------
@ImplementedBy(value=EventRegistryImpl.class)
public interface EventRegistry {
public EventListener registerPublisher(Class<? extends EventListener> listener);
public void registerListener(EventListener listener);
}
org.eclipse.hudson.events.EventListener:
----------------------------------------
public interface EventListener { }
org.eclipse.hudson.events.internal.EventListener
------------------------------------------------
@Named
@Singleton
public class EventRegistryImpl implements EventRegistry {
private static class ProxyHolder {
public Class<? extends EventListener> listenerClass;
public EventListener listener;
public EventInvocationHandler handler;
}
private final Map<Class, ProxyHolder> eventType;
@Inject
public EventRegistryImpl() {
eventType = new ConcurrentHashMap<Class, ProxyHolder>();
}
public synchronized EventListener registerPublisher(Class<? extends EventListener> listener) {
ProxyHolder holder = getFromCache(listener);
return holder.listener;
}
public synchronized void registerListener(EventListener listener) {
log.info("register Listener: " + listener.getClass().getName());
Class<?>[] interfaces = listener.getClass().getInterfaces();
// Need to register for each event type
for (Class implementInterface : interfaces) {
// if the class implements other interfaces skip those
if (EventListener.class.isAssignableFrom(implementInterface)) {
ProxyHolder holder = getFromCache(implementInterface);
holder.handler.consumers.add(listener);
}
}
}
private ProxyHolder getFromCache(Class<? extends EventListener> listener) {
ProxyHolder holder = eventType.get(listener);
if (holder == null) {
holder = createProxy(listener);
eventType.put(listener, holder);
}
return holder;
}
private ProxyHolder createProxy(Class<? extends EventListener> listener) {
Class[] interfaces = {listener};
EventInvocationHandler handler = new EventInvocationHandler();
EventListener newProxyInstance = (EventListener) Proxy.newProxyInstance(listener.getClassLoader(), interfaces, handler);
ProxyHolder result = new ProxyHolder();
result.listenerClass = listener;
result.listener = newProxyInstance;
result.handler = handler;
return result;
}
}
org.eclipse.hudson.events.internal.EventInvocationHandler
----------------------------------------------------------
class EventInvocationHandler<V extends EventListener> implements java.lang.reflect.InvocationHandler {
CopyOnWriteArrayList<V> consumers = new CopyOnWriteArrayList<V>();
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.err.println("I have "+consumers.size() +" consumers");
for (V consumer : consumers) {
try {
method.invoke(consumer, args);
} catch (InvocationTargetException e) {
System.out.println("Failed to call event listener");
}
}
return null;
}
}
mavenplugin.MavenEvent
----------------------
public interface MavenEvent extends EventListener {
public void added(String groupId,String artifact);
public void removed(String groupId,String artifact);
}
mavenplugin.MavenEventListener
------------------------------
public class MavenEventListener implements MavenEvent{
public void added(String groupId, String artifact) {
System.err.println("Got a added event "+ artifact);
}
public void removed(String groupId, String artifact) {
System.err.print("Got a removed event");
}
}
mavenplugin.MavenPlugin
-----------------------
public class MavenPlugin {
@Inject
public MavenPlugin(EventRegistry reg) {
checkNotNull(reg);
// first register ourself as publisher
MavenEvent publisher = (MavenEvent) reg.registerPublisher(MavenEvent.class);
// register a listener
reg.registerListener(new MavenEventListener());
// send a event
publisher.added("org.eclipse.hudson", "hudson-core");
}
}