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