001package systems.dmx.core.impl;
002
003import systems.dmx.core.osgi.PluginContext;
004import systems.dmx.core.service.DMXEvent;
005import systems.dmx.core.service.EventListener;
006import systems.dmx.core.service.accesscontrol.AccessControlException;
007
008import javax.ws.rs.WebApplicationException;
009
010import java.util.ArrayList;
011import java.util.HashMap;
012import java.util.List;
013import java.util.Map;
014
015
016
017class EventManager {
018
019    // ---------------------------------------------------------------------------------------------- Instance Variables
020
021    /**
022     * The registered event listeners (key: event class name, value: event listeners).
023     */
024    private Map<String, List<EventListener>> listenerRegistry = new HashMap();
025
026    // ---------------------------------------------------------------------------------------------------- Constructors
027
028    EventManager() {
029        // Note: actually the class CoreEvent does not need to be instantiated as it contains only statics.
030        // But if not instantiated OSGi apparently does not load the class at all.
031        new CoreEvent();
032    }
033
034    // ----------------------------------------------------------------------------------------- Package Private Methods
035
036    void addListener(DMXEvent event, EventListener listener) {
037        List<EventListener> listeners = getListeners(event);
038        if (listeners == null) {
039            listeners = new ArrayList();
040            putListeners(event, listeners);
041        }
042        listeners.add(listener);
043    }
044
045    void removeListener(DMXEvent event, EventListener listener) {
046        List<EventListener> listeners = getListeners(event);
047        if (!listeners.remove(listener)) {
048            throw new RuntimeException("Removing " + listener + " from " +
049                event + " event listeners failed: not found in " + listeners);
050        }
051    }
052
053    // ---
054
055    void fireEvent(DMXEvent event, Object... params) {
056        List<EventListener> listeners = getListeners(event);
057        if (listeners != null) {
058            for (EventListener listener : listeners) {
059                dispatchEvent(listener, event, params);
060            }
061        }
062    }
063
064    // ---
065
066    /**
067     * Delivers an event to a particular plugin.
068     * If the plugin is not a listener for that event nothing is performed.
069     */
070    void dispatchEvent(PluginImpl plugin, DMXEvent event, Object... params) {
071        PluginContext pluginContext = plugin.getContext();
072        if (!isListener(pluginContext, event)) {
073            return;
074        }
075        //
076        dispatchEvent((EventListener) pluginContext, event, params);
077    }
078
079    // ------------------------------------------------------------------------------------------------- Private Methods
080
081    private void dispatchEvent(EventListener listener, DMXEvent event, Object... params) {
082        try {
083            event.dispatch(listener, params);
084        } catch (WebApplicationException e) {
085            // Note: a WebApplicationException thrown by a event listener must reach Jersey. So we re-throw here.
086            // This allow plugins to produce specific HTTP responses by throwing a WebApplicationException.
087            // Consider the Caching plugin: it produces a possible 304 (Not Modified) response this way.
088            throw e;
089        } catch (AccessControlException e) {
090            // Note: an AccessControlException thrown by a event listener must reach the caller in order to
091            // recover.
092            throw e;
093        } catch (Throwable e) {
094            // Note: here we also catch errors like NoSuchMethodError or AbstractMethodError.
095            // These occur when plugins are not yet adapted to changed Core API.
096            throw new RuntimeException("An error occurred in the " + event.getListenerInterface().getSimpleName() +
097                " of " + listener, e);
098        }
099    }
100
101    // ---
102
103    /**
104     * Returns true if the given plugin is a listener for the given event.
105     */
106    private boolean isListener(PluginContext pluginContext, DMXEvent event) {
107        return event.getListenerInterface().isAssignableFrom(pluginContext.getClass());
108    }
109
110    // ---
111
112    private List<EventListener> getListeners(DMXEvent event) {
113        return listenerRegistry.get(event.getClass().getName());
114    }
115
116    private void putListeners(DMXEvent event, List<EventListener> listeners) {
117        listenerRegistry.put(event.getClass().getName(), listeners);
118    }
119}