Skip to main content

[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");    
  } 
} 

Back to the top