package org.esigate.events; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The event manager. * <p> * Listener can register and unregister to specific events. * <p> * Events can be fired and propagated to listeners. * <p> * The following events are supported : * <p> * Proxy events : ESIGate process an incoming request (ESIGate configured as a proxy). * <uL> * <li>{@link EventManager#EVENT_PROXY_PRE} : before processing an incoming request.</li> * <li>{@link EventManager#EVENT_PROXY_POST} : after processing an incoming request.</li> * </ul> * <p> * Fragment events : A fragment is required for inclusion (esi:include). ESIGate will try to use its cache or fallback * to an http call to the remote backend. * * <ul> * <li>{@link EventManager#EVENT_FRAGMENT_PRE} : before retrieving a fragment.</li> * <li>{@link EventManager#EVENT_FRAGMENT_POST} : after retrieving a fragment.</li> * </ul> * <p> * Fetch events : An HTTP call is made to a remote backend. * <ul> * <li>{@link EventManager#EVENT_FETCH_PRE} : before creating the HTTP call.</li> * <li>{@link EventManager#EVENT_FETCH_POST} : after we receive the response.</li> * </ul> * <p> * Render events : Renderers are applied to the current page. This event can be used to inject additional renderers. * <ul> * <li>{@link EventManager#EVENT_RENDER_PRE} : before applying renderers</li> * <li>{@link EventManager#EVENT_RENDER_POST} : after applying renderers</li> * </ul> * <p> * Encoding event : response is read using the charset declared by HTTP headers. * <ul> * <li>{@link EventManager#EVENT_READ_ENTITY} : after reading response using the default encoding</li> * </ul> * * @author Nicolas Richeton */ public class EventManager { public static final EventDefinition EVENT_FRAGMENT_PRE = new EventDefinition("org.esigate.fragment-pre", EventDefinition.TYPE_DEFAULT); public static final EventDefinition EVENT_FRAGMENT_POST = new EventDefinition("org.esigate.fragment-post", EventDefinition.TYPE_POST); public static final EventDefinition EVENT_FETCH_PRE = new EventDefinition("org.esigate.fetch-pre", EventDefinition.TYPE_DEFAULT); public static final EventDefinition EVENT_FETCH_POST = new EventDefinition("org.esigate.fetch-post", EventDefinition.TYPE_POST); public static final EventDefinition EVENT_PROXY_PRE = new EventDefinition("org.esigate.proxy-pre", EventDefinition.TYPE_DEFAULT); public static final EventDefinition EVENT_PROXY_POST = new EventDefinition("org.esigate.proxy-post", EventDefinition.TYPE_POST); public static final EventDefinition EVENT_RENDER_PRE = new EventDefinition("org.esigate.render-pre", EventDefinition.TYPE_DEFAULT); public static final EventDefinition EVENT_RENDER_POST = new EventDefinition("org.esigate.render-post", EventDefinition.TYPE_POST); public static final EventDefinition EVENT_READ_ENTITY = new EventDefinition("org.esigate.readEntity.", EventDefinition.TYPE_DEFAULT); public static final EventDefinition EVENT_HTTP_BUILDER_INITIALIZATION = new EventDefinition( "org.esigate.httpBuilderInitialization.", EventDefinition.TYPE_POST); private static final Logger LOG = LoggerFactory.getLogger(EventManager.class); private final String driverId; public EventManager(String driverId) { this.driverId = driverId; } /** * Listener mappings. This saves times when an event is fired. */ private Map<EventDefinition, List<IEventListener>> listeners = new HashMap<>(); /** * Post events are stored in reverse order. This allows an extension to enclose the whole processing. */ private Map<EventDefinition, List<IEventListener>> listenersPost = new HashMap<>(); private void register(Map<EventDefinition, List<IEventListener>> listenerMappings, EventDefinition eventDefinition, IEventListener listener, boolean reverseOrder) { List<IEventListener> eventListeners = listenerMappings.get(eventDefinition); // Create listener list for this event if (eventListeners == null) { eventListeners = new ArrayList<>(); listenerMappings.put(eventDefinition, eventListeners); } if (!eventListeners.contains(listener)) { // Add listener if (reverseOrder) { eventListeners.add(eventListeners.size(), listener); } else { eventListeners.add(listener); } if (LOG.isInfoEnabled()) { LOG.info("Registered {} on event {}/{}", listener.getClass().getName(), driverId, eventDefinition); } } } /** * Start listening to an event. * * @param eventDefinition * @param listener */ public void register(EventDefinition eventDefinition, IEventListener listener) { if (eventDefinition.getType() == EventDefinition.TYPE_POST) { register(listenersPost, eventDefinition, listener, true); } else { register(listeners, eventDefinition, listener, false); } } /** * Fire a new event and run all the listeners. * * @param eventDefinition * @param eventDetails */ public void fire(EventDefinition eventDefinition, Event eventDetails) { if (eventDefinition.getType() == EventDefinition.TYPE_POST) { fire(listenersPost, eventDefinition, eventDetails); } else { fire(listeners, eventDefinition, eventDetails); } } private void fire(Map<EventDefinition, List<IEventListener>> listenerMappings, EventDefinition eventDefinition, Event eventDetails) { List<IEventListener> eventListeners = listenerMappings.get(eventDefinition); // No listeners at all for this event if (eventListeners == null) { return; } // Loop on listeners for (IEventListener el : eventListeners) { if (LOG.isDebugEnabled()) { LOG.debug("Running {} on event {}/{}", el, driverId, eventDefinition); } if (!el.event(eventDefinition, eventDetails)) { return; } } } /** * Stop listening to an event. * * @param eventDefinition * @param eventListener */ public void unregister(EventDefinition eventDefinition, IEventListener eventListener) { if (eventDefinition.getType() == EventDefinition.TYPE_POST) { unregister(listenersPost, eventDefinition, eventListener); } else { unregister(listeners, eventDefinition, eventListener); } } private void unregister(Map<EventDefinition, List<IEventListener>> listenerMappings, EventDefinition eventDefinition, IEventListener listener) { List<IEventListener> eventListeners = listenerMappings.get(eventDefinition); // Not listeners at all for this event if (eventListeners == null) { return; } boolean removed = eventListeners.remove(listener); if (LOG.isInfoEnabled() && removed) { LOG.info("Unregistered {} on event {}/{}", listener.getClass().getName(), driverId, eventDefinition); } } }