001: /**
002: * Copyright (c) 2003-2007, David A. Czarnecki
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * Redistributions of source code must retain the above copyright notice, this list of conditions and the
009: * following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
011: * following disclaimer in the documentation and/or other materials provided with the distribution.
012: * Neither the name of "David A. Czarnecki" and "blojsom" nor the names of its contributors may be used to
013: * endorse or promote products derived from this software without specific prior written permission.
014: * Products derived from this software may not be called "blojsom", nor may "blojsom" appear in their name,
015: * without prior written permission of David A. Czarnecki.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
018: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
019: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
021: * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
022: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
025: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
029: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030: */package org.blojsom.event;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034:
035: import java.util.*;
036:
037: /**
038: * SimpleEventBroadcaster.
039: * <p></p>
040: * Events are broadcast to each event in a separate thread so that the broadcaster is not a bottleneck.
041: * No defined order is set for how each event will receive an event, so you should not assume any order
042: * in listeners being called. No steps are taken to ensure a event does not receive an event if it is
043: * removed at the same time an event is being broadcast.
044: * <p></p>
045: * The addition of the {@link #processEvent(Event)} method adds the capability for components to have an
046: * event processed after the call instead of asynchronously as with the {@link #broadcastEvent(Event)} method.
047: *
048: * @author David Czarnecki
049: * @version $Id: SimpleEventBroadcaster.java,v 1.2 2007/01/17 02:35:17 czarneckid Exp $
050: * @since blojsom 3.0
051: */
052: public class SimpleEventBroadcaster implements EventBroadcaster {
053:
054: private Log _logger = LogFactory
055: .getLog(SimpleEventBroadcaster.class);
056:
057: private static Set _listeners;
058: private static Map _listenerToHandler;
059:
060: /**
061: * Default constructor.
062: */
063: public SimpleEventBroadcaster() {
064: if (_listeners == null) {
065: _listeners = new HashSet();
066: } else {
067: // @todo Can Spring support a singleton bean across servlets/application contexts?
068: if (_logger.isDebugEnabled()) {
069: _logger.debug("Using shared listeners map");
070: }
071: }
072:
073: if (_listenerToHandler == null) {
074: _listenerToHandler = new HashMap();
075: } else {
076: // @todo Can Spring support a singleton bean across servlets/application contexts?
077: if (_logger.isDebugEnabled()) {
078: _logger.debug("Using shared listener to handler map");
079: }
080: }
081:
082: if (_logger.isDebugEnabled()) {
083: _logger.debug("Initialized simple event broadcaster");
084: }
085: }
086:
087: /**
088: * Add a event to this event broadcaster
089: *
090: * @param listener {@link Listener}
091: */
092: public void addListener(Listener listener) {
093: EventHandler handler = new EventHandler(listener, new Filter() {
094: /**
095: * Determines whether or not a particular event should be processed
096: *
097: * @param event {@link Event} to be processed
098: * @return <code>true</code> if the event should be processed, <code>false</code> otherwise
099: */
100: public boolean processEvent(Event event) {
101: return true;
102: }
103: });
104:
105: if (!_listenerToHandler.containsKey(listener.getClass()
106: .getName())) {
107: _listeners.add(handler);
108: _listenerToHandler.put(listener.getClass().getName(),
109: handler);
110: if (_logger.isDebugEnabled()) {
111: _logger.debug("Added event listener: "
112: + listener.getClass().getName()
113: + " with process all events filter");
114: }
115: }
116: }
117:
118: /**
119: * Add a event to this event broadcaster. Events are filtered using the {@link org.blojsom.event.Filter} instance
120: * passed to this method.
121: *
122: * @param listener {@link Listener}
123: * @param filter {@link Filter} used to filter events
124: */
125: public void addListener(Listener listener, Filter filter) {
126: EventHandler handler = new EventHandler(listener, filter);
127:
128: if (!_listenerToHandler.containsKey(listener.getClass()
129: .getName())) {
130: _listeners.add(handler);
131: _listenerToHandler.put(listener.getClass().getName(),
132: handler);
133: if (_logger.isDebugEnabled()) {
134: _logger.debug("Added event listener: "
135: + listener.getClass().getName()
136: + " with filter: "
137: + filter.getClass().getName());
138: }
139: }
140: }
141:
142: /**
143: * Remove a event from this event broadcaster
144: *
145: * @param listener {@link Listener}
146: */
147: public void removeListener(Listener listener) {
148: if (_listenerToHandler.containsKey(listener.getClass()
149: .getName())) {
150: EventHandler handler = (EventHandler) _listenerToHandler
151: .get(listener.getClass().getName());
152: _listeners.remove(handler);
153: _listenerToHandler.remove(listener.getClass().getName());
154: }
155:
156: if (_logger.isDebugEnabled()) {
157: _logger.debug("Removed event listener: "
158: + listener.getClass().getName());
159: }
160: }
161:
162: /**
163: * Broadcast an event to all listeners
164: *
165: * @param event {@link Event} to be broadcast to all listeners
166: */
167: public void broadcastEvent(Event event) {
168: Thread eventBroadcaster = new Thread(
169: new AsynchronousEventBroadcaster(event));
170: eventBroadcaster.setDaemon(true);
171: eventBroadcaster.start();
172: }
173:
174: /**
175: * Process an event with all listeners
176: *
177: * @param event {@link Event} to be processed by all listeners
178: */
179: public void processEvent(Event event) {
180: Iterator handlerIterator = _listeners.iterator();
181: while (handlerIterator.hasNext()) {
182: EventHandler eventHandler = (EventHandler) handlerIterator
183: .next();
184: if (eventHandler._filter.processEvent(event)) {
185: eventHandler._listener.processEvent(event);
186: }
187: }
188: }
189:
190: /**
191: * Event handler helper class.
192: */
193: protected class EventHandler {
194:
195: protected Listener _listener;
196: protected Filter _filter;
197:
198: /**
199: * Create a new event handler with event and filter instances.
200: *
201: * @param listener {@link Listener}
202: * @param filter {@link Filter}
203: */
204: protected EventHandler(Listener listener, Filter filter) {
205: _listener = listener;
206: _filter = filter;
207: }
208: }
209:
210: /**
211: * Thread to handle broadcasting an event to registered listeners.
212: */
213: private class AsynchronousEventBroadcaster implements Runnable {
214:
215: private Event _event;
216:
217: public AsynchronousEventBroadcaster(Event event) {
218: _event = event;
219: }
220:
221: /**
222: * Iterates over the set of {@link EventHandler} registered with this broadcaster and calls
223: * the {@link Listener#handleEvent(Event)} method with the
224: * {@link Event}.
225: */
226: public void run() {
227: Iterator handlerIterator = _listeners.iterator();
228: while (handlerIterator.hasNext()) {
229: EventHandler eventHandler = (EventHandler) handlerIterator
230: .next();
231: if (eventHandler._filter.processEvent(_event)) {
232: eventHandler._listener.handleEvent(_event);
233: }
234: }
235: }
236: }
237: }
|