001    package de.deepamehta.core.impl;
002    
003    import de.deepamehta.core.osgi.PluginContext;
004    import de.deepamehta.core.service.DeepaMehtaEvent;
005    import de.deepamehta.core.service.DeepaMehtaService;
006    import de.deepamehta.core.service.EventListener;
007    import de.deepamehta.core.service.accesscontrol.AccessControlException;
008    
009    import javax.ws.rs.WebApplicationException;
010    
011    import java.util.ArrayList;
012    import java.util.HashMap;
013    import java.util.List;
014    import java.util.Map;
015    
016    
017    
018    class 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    }