001: /*
002: * This software is OSI Certified Open Source Software.
003: * OSI Certified is a certification mark of the Open Source Initiative.
004: *
005: * The license (Mozilla version 1.0) can be read at the MMBase site.
006: * See http://www.MMBase.org/license
007: */
008: package org.mmbase.core.event;
009:
010: import java.io.IOException;
011: import java.util.*;
012: import java.net.URL;
013:
014: import org.mmbase.util.*;
015: import org.mmbase.util.logging.Logger;
016: import org.mmbase.util.logging.Logging;
017: import org.mmbase.util.xml.DocumentReader;
018: import org.w3c.dom.Document;
019: import org.w3c.dom.Element;
020: import org.xml.sax.SAXException;
021:
022: import java.util.concurrent.CopyOnWriteArraySet;
023:
024: /**
025: * This class manages all event related stuff. it is the place to register event brokers, and it
026: * will propagate all events. The class is set up as a singleton. When the manager is instantiated,
027: * event brokers are added for Event, NodeEvent and RelationEvent
028: *
029: * @author Ernst Bunders
030: * @since MMBase-1.8
031: * @version $Id: EventManager.java,v 1.25 2007/09/18 12:04:05 michiel Exp $
032: */
033: public class EventManager {
034:
035: private static final Logger log = Logging
036: .getLoggerInstance(EventManager.class);
037:
038: public static final String PUBLIC_ID_EVENTMANAGER = "-//MMBase//DTD eventmanager config 1.0//EN";
039: public static final String DTD_EVENTMANAGER = "eventmanager_1_0.dtd";
040:
041: static {
042: org.mmbase.util.XMLEntityResolver.registerPublicID(
043: PUBLIC_ID_EVENTMANAGER, DTD_EVENTMANAGER,
044: EventManager.class);
045: }
046:
047: /**
048: * the instance that this singleton will manage
049: */
050: private static final EventManager eventManager = new EventManager();
051:
052: /**
053: * The collection of event brokers. There is one for every event type that can be sent/received
054: */
055: private final Set<EventBroker> eventBrokers = new CopyOnWriteArraySet<EventBroker>();
056:
057: private long numberOfPropagatedEvents = 0;
058: private long duration = 0;
059:
060: /**
061: * use this metod to get an instance of the event manager
062: */
063: public static EventManager getInstance() {
064: return eventManager;
065: }
066:
067: private static EventBroker findInstance(String className) {
068: if (className == null || "".equals(className))
069: return null;
070: try {
071: Class<?> aClass = Class.forName(className);
072: return (EventBroker) aClass.newInstance();
073: } catch (ClassNotFoundException e) {
074: log.error("could not find class with name '" + className
075: + "'", e);
076: } catch (InstantiationException e) {
077: log.error("could not instantiate class with name '"
078: + className + "'", e);
079: } catch (IllegalAccessException e) {
080: log.error("the constructor of '" + className
081: + "' is not accessible", e);
082: } catch (ClassCastException e) {
083: log.error("'" + className
084: + "' is not a AbstractEventBroker", e);
085: }
086: return null;
087: }
088:
089: protected ResourceWatcher watcher = new ResourceWatcher() {
090: public void onChange(String w) {
091: configure(w);
092: }
093: };
094:
095: private EventManager() {
096: watcher.add("eventmanager.xml");
097: watcher.onChange();
098: watcher.start();
099: }
100:
101: protected void configure(String resource) {
102: log.service("Configuring the event manager");
103: eventBrokers.clear();
104: for (URL url : ResourceLoader.getConfigurationRoot()
105: .getResourceList(resource)) {
106: try {
107: if (url.openConnection().getDoInput()) {
108:
109: Document config = ResourceLoader.getDocument(url,
110: true, EventManager.class);
111: DocumentReader configReader = new DocumentReader(
112: config);
113:
114: // find the event brokers
115: for (Element element : configReader
116: .getChildElements("eventmanager.brokers",
117: "broker")) {
118: String className = element
119: .getAttribute("class");
120: EventBroker broker = findInstance(className);
121: if (broker != null) {
122: if (log.isDebugEnabled()) {
123: log.debug("adding event broker: "
124: + broker);
125: }
126: addEventBroker(broker);
127: }
128: }
129: }
130: } catch (SAXException e1) {
131: log.error(
132: "Something went wrong configuring the event system ("
133: + url + "): " + e1.getMessage(), e1);
134: } catch (IOException e1) {
135: log.error(
136: "something went wrong configuring the event system ("
137: + url + "): " + e1.getMessage(), e1);
138:
139: }
140: }
141: if (eventBrokers.size() == 0) {
142: log
143: .fatal("No event brokers could not be found. This means that query-invalidation does not work correctly now. Proceeding anyway.");
144: return;
145: }
146: }
147:
148: /**
149: * @since MMBase-1.8.5
150: */
151:
152: public Collection<EventBroker> getBrokers() {
153: return Collections.unmodifiableSet(eventBrokers);
154: }
155:
156: /**
157: * add an event broker for a specific type of event
158: * @param broker
159: */
160: public void addEventBroker(EventBroker broker) {
161: //we want only one instance of each broker
162: if (!eventBrokers.contains(broker)) {
163: if (log.isDebugEnabled()) {
164: log.debug("adding broker " + broker.toString());
165: }
166: eventBrokers.add(broker);
167: } else {
168: if (log.isDebugEnabled()) {
169: log.debug("broker " + broker.toString()
170: + "was already registered: rejected.");
171: }
172: }
173: }
174:
175: /**
176: * remove a broker for a specific type of event
177: * @param broker
178: */
179: public void removeEventBroker(EventBroker broker) {
180: eventBrokers.remove(broker);
181: }
182:
183: /**
184: * @param listener
185: */
186: public void addEventListener(EventListener listener) {
187: BrokerIterator i = findBrokers(listener);
188: while (i.hasNext()) {
189: EventBroker broker = i.next();
190: if (broker.addListener(listener)) {
191: if (log.isDebugEnabled()) {
192: log.debug("listener " + listener
193: + " added to broker " + broker);
194: }
195: }
196: }
197: }
198:
199: /**
200: * @param listener
201: */
202: public void removeEventListener(EventListener listener) {
203: if (log.isDebugEnabled()) {
204: log.debug("removing listener of type: "
205: + listener.getClass().getName());
206: }
207: BrokerIterator i = findBrokers(listener);
208: while (i.hasNext()) {
209: i.next().removeListener(listener);
210: }
211: }
212:
213: /**
214: * This method will propagate the given event to all the aproprate listeners. what makes a
215: * listener apropriate is determined by it's type (class) and by possible constraint properties
216: * (if the handling broker supports those
217: * @see AbstractEventBroker
218: * @param event
219: */
220: public void propagateEvent(Event event) {
221: if (log.isTraceEnabled()) {
222: log.trace("Propagating events to " + eventBrokers);
223: }
224: long startTime = System.nanoTime();
225: for (EventBroker broker : eventBrokers) {
226: if (broker.canBrokerForEvent(event)) {
227: broker.notifyForEvent(event);
228: if (log.isDebugEnabled()) {
229: if (log.isTraceEnabled()) {
230: log.trace("event from '" + event.getMachine()
231: + "': " + event
232: + " has been accepted by broker "
233: + broker);
234: } else {
235: log.debug("event from '" + event.getMachine()
236: + "' has been accepted by broker "
237: + broker);
238: }
239: }
240: } else {
241: if (log.isDebugEnabled()) {
242: if (log.isTraceEnabled()) {
243: log.trace("event from '" + event.getMachine()
244: + "': " + event
245: + " has been rejected by broker "
246: + broker);
247: } else {
248: log.debug("event from '" + event.getMachine()
249: + "' has been rejected by broker "
250: + broker);
251: }
252: }
253: }
254: }
255: numberOfPropagatedEvents++;
256: duration += (System.nanoTime() - startTime);
257: }
258:
259: /**
260: * @since MMBase-1.8.1
261: */
262: public long getNumberOfPropagatedEvents() {
263: return numberOfPropagatedEvents;
264: }
265:
266: /**
267: * @since MMBase-1.8.1
268: */
269: public long getPropagationCost() {
270: return duration / 1000000;
271: }
272:
273: /**
274: * @since MMBase-1.9
275: */
276: public long getPropagationCostNs() {
277: return duration;
278: }
279:
280: /**
281: * @param listener
282: */
283: private BrokerIterator findBrokers(final EventListener listener) {
284: if (log.isDebugEnabled()) {
285: log.debug("try to find broker for "
286: + listener.getClass().getName());
287: }
288: return new BrokerIterator(eventBrokers.iterator(), listener);
289: }
290:
291: /**
292: * @since MMBase-1.9
293: */
294: public void shutdown() {
295: log.service("Shutting down event manager");
296: eventBrokers.clear();
297: watcher.exit();
298: }
299:
300: private static class BrokerIterator implements
301: Iterator<EventBroker> {
302: EventBroker next;
303: final Iterator<EventBroker> i;
304: final EventListener listener;
305:
306: BrokerIterator(final Iterator<EventBroker> i,
307: final EventListener listener) {
308: this .i = i;
309: this .listener = listener;
310: findNext();
311: }
312:
313: public void remove() {
314: throw new UnsupportedOperationException();
315: }
316:
317: public EventBroker next() {
318: if (next == null)
319: throw new NoSuchElementException();
320: EventBroker n = next;
321: findNext();
322: return n;
323: }
324:
325: public boolean hasNext() {
326: return next != null;
327: }
328:
329: protected void findNext() {
330: while (i.hasNext()) {
331: EventBroker broker = i.next();
332: if (broker.canBrokerForListener(listener)) {
333: if (log.isDebugEnabled()) {
334: log.debug("broker " + broker
335: + " can broker for eventlistener "
336: + listener.getClass().getName());
337: }
338: next = broker;
339: return;
340: } else if (log.isDebugEnabled()) {
341: log.debug("broker " + broker
342: + " cannot boker for eventlistener."
343: + listener.getClass().getName());
344: }
345: }
346: next = null;
347: }
348:
349: }
350:
351: }
|