Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright 2005-2007 Bushe Enterprises, Inc., Hopkinton, MA, USA, www.bushe.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bushe.swing.event;
import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Collections;
import java.util.Comparator;
import java.util.regex.Pattern;
import org.bushe.swing.event.Logger.Level;
import org.bushe.swing.event.annotation.ReferenceStrength;
import org.bushe.swing.exception.SwingException;
/**
* A thread-safe EventService implementation.
*
Multithreading
*
* This implementation is not Swing thread-safe. If publication occurs on a thread other than the Swing
* EventDispatchThread, subscribers will receive the event on the calling thread, and not the EDT. Swing components
* should use the SwingEventService instead, which is the implementation used by the EventBus.
*
* Two threads may be accessing the ThreadSafeEventService at the same time, one unsubscribing a
* listener for topic "A" and the other publishing on topic "A". If the unsubscribing thread gets the lock first,
* then it is unsubscribed, end of story. If the publisher gets the lock first, then a snapshot copy of the current
* subscribers is made during the publication, the lock is released and the subscribers are called. Between the time
* the lock is released and the time that the listener is called, the unsubscribing thread can unsubscribe, resulting
* in an unsubscribed object receiving notifiction of the event after it was unsubscribed (but just once).
*
* On event publication, subscribers are called in the order in which they subscribed.
*
* Events and/or topic data can be cached, but are not by default. To cache events or topic data, call
* {@link #setDefaultCacheSizePerClassOrTopic(int)}, {@link #setCacheSizeForEventClass(Class, int)}, or
* {@link #setCacheSizeForTopic(String, int)}, {@link #setCacheSizeForTopic(Pattern, int)}. Retrieve cached values
* with {@link #getLastEvent(Class)}, {@link #getLastTopicData(String)}, {@link #getCachedEvents(Class)}, or
* {@link #getCachedTopicData(String)}. Using caching while subscribing
* is most likely to make sense only if you subscribe and publish on the same thread (so caching is very useful for
* Swing applications since both happen on the EDT in a single-threaded manner). In multithreaded applications, you
* never know if your subscriber has handled an event while it was being subscribed (before the subscribe() method
* returned) that is newer or older than the retrieved cached value (taked before or after subscribe() respectively).
*
* To deal with subscribers that take too long (a concern in Swing applications), the EventService can be made to issue
* {@link SubscriberTimingEvent}s when subscribers exceed a certain time. This does not interrupt subscriber processing
* and is published after the subscriber finishes. The service can log a warning for SubscriberTimingEvents, see the
* constructor {@link ThreadSafeEventService (long, boolean)}. The timing is checked for veto subscribers too.
*
*
Logging
*
* All logging goes through the {@link Logger}. The Logger is configurable and supports multiple logging systems.
*
* Exceptions are logged by default, override {@link #handleException(String,Object,String,Object,Throwable,
* StackTraceElement[],String)} to handleException exceptions in another way. Each call to a subscriber is wrapped in
* a try block to ensure one listener does not interfere with another.
*
*
Cleanup of Stale WeakReferences and Stale Annotation Proxies
*
* The EventService may need to clean up stale WeakReferences and ProxySubscribers created for EventBus annotations. (Aside: EventBus
* Annotations are handled by the creation of proxies to the annotated objects. Since the annotations create weak references
* by default, annotation proxies must held strongly by the EventService, otherwise the proxy is garbage collected.) When
* a WeakReference's referent or an ProxySubscriber's proxiedObject (the annotated object) is claimed by the garbage collector,
* the EventService still holds onto the actual WeakReference or ProxySubscriber subscribed to the EventService (which are pretty tiny).
*
* There are two ways that these stale WeakReferences and ProxySubscribers are cleaned up.
*
*
On every publish, subscribe and unsubscribe, every subscriber and veto subscriber to a class or topic is checked to see
* if it is a stale WeakReference or a stale ProxySubscriber (one whose getProxySubscriber() returns null). If the subscriber
* is stale, it is unsubscribed from the EventService immediately. If it is a ProxySubscriber, it's proxyUnsubscribed()
* method is called after it is unsubscribed. (This isn't as expecive as it sounds, since checks to avoid double subscription is
* necessary anyway).
*
Another cleanup thread may get started to clean up remaining stale subscribers. This cleanup thread only comes into
* play for subscribers to topic or classes that haven't been used (published/subscribed/unsibscribed to). A detailed description
* of the cleanup thread follows.
*
*
The Cleanup Thread
* If a topic or class is never published to again, WeakReferences and ProxySubscribers can be left behind if they
* are not cleaned up. To prevent loitering stale subscribers, the ThreadSafeEventService may periodically run through
* all the EventSubscribers and VetoSubscribers for all topics and classes and clean up stale proxies. Proxies for
* Annotations that have a ReferenceStrength.STRONG are never cleaned up in normal usage. (By specifying
* ReferenceStrength.STRONG, the programmer is buying into unsubscribing annotated objects themselves. There is
* one caveat: If getProxiedSubscriber() returns null, even for a ProxySubscriber with a STRONG reference strength, that proxy
* is cleaned up as it is assumed it is stale or just wrong. This would not occur normally in EventBus usage, but only
* if someone is implementing their own custom ProxySubscriber and/or AnnotationProcessor.)
*
* Cleanup is pretty rare in general. Not only are stale subscribers cleaned up with regular usage, stale
* subscribers on abandonded topics and classes do not take up a lot of memory, hence, they are allowed to build up to a certain degree.
* Cleanup does not occur until the number of WeakReferences and SubscriptionsProxy's with WeakReference strength
* subscribed to an EventService for all the EventService's subscriptions in total exceed the cleanupStartThreshhold,
* which is set to CLEANUP_START_THRESHOLD_DEFAULT (500) by default. The default is overridable in the constructor
* or via #setCleanupStartThreshhold(Integer). If set to null, cleanup will never start.
*
* Once the cleanup start threshold is exceeded, a java.util.Timer is started to clean up stale subscribers periodically
* in another thread. The timer will fire every cleanupPeriodMS milliseconds, which is set to the
* CLEANUP_PERIOD_MS_DEFAULT (20 minutes) by default. The default is overridable in the constructor or
* via #setCleanupPeriodMS(Integer). If set to null, cleanup will not start. This is implemented with a java.util.Timer,
* so Timer's warnings apply - setting this too low will cause cleanups to bunch up and hog the cleanup thread.
*
* After a cleanup cycle completes, if the number of stale subscribers falls at or below the cleanupStopThreshhold
* cleanup stops until the cleanupStartThreshhold is exceeded again. The cleanupStopThreshhold is set
* to CLEANUP_STOP_THRESHOLD_DEFAULT (100) by default. The default is overridable in the constructor or via
* #setCleanupStopThreshhold(Integer). If set to null or 0, cleanup will not stop if it is ever started.
*
* Cleanup can be monitored by subscribing to the {@link CleanupEvent} class.
*
* All cleanup parameters are tunable "live" and checked after each subscription and after each cleanup cycle.
* To make cleanup never run, set cleanupStartThreshhold to Integer.MAX_VALUE and cleanupPeriodMS to null.
* To get cleanup to run continuously, set set cleanupStartThreshhold to 0 and cleanupPeriodMS to some reasonable value,
* perhaps 1000 (1 second) or so (not recommended, cleanup is conducted with regular usage and the cleanup thread is
* rarely created or invoked).
*
* Cleanup is not run in a daemon thread, and thus will not stop the JVM from exiting.
*
*
* @author Michael Bushe [email protected]
* @todo (param) a JMS-like selector (can be done in base classes by implements like a commons filter
* @see EventService for a complete description of the API
*/
@SuppressWarnings({"unchecked", "ForLoopReplaceableByForEach"})
public class ThreadSafeEventService implements EventService {
public static final Integer CLEANUP_START_THRESHOLD_DEFAULT = 250;
public static final Integer CLEANUP_STOP_THRESHOLD_DEFAULT = 100;
public static final Long CLEANUP_PERIOD_MS_DEFAULT = 20L*60L*1000L;
protected static final Logger LOG = Logger.getLogger(EventService.class.getName());
//Making these generic collections is a bad idea, it doesn't compile since it's better to have all the maps
//go through the same set of code to do all the real publish and subscribe work
private Map subscribersByEventType = new HashMap();
private Map subscribersByEventClass = new HashMap();
private Map subscribersByExactEventClass = new HashMap();
private Map subscribersByTopic = new HashMap();
private Map subscribersByTopicPattern = new HashMap();
private Map vetoListenersByClass = new HashMap();
private Map vetoListenersByExactClass = new HashMap();
private Map vetoListenersByTopic = new HashMap();
private Map vetoListenersByTopicPattern = new HashMap();
private final Object listenerLock = new Object();
private final Object cacheLock = new Object();
private Long timeThresholdForEventTimingEventPublication;
private Map cacheByEvent = new HashMap();
private int defaultCacheSizePerClassOrTopic = 0;
private Map cacheSizesForEventClass;
private Map rawCacheSizesForEventClass;
private boolean rawCacheSizesForEventClassChanged;
private Map cacheByTopic = new HashMap();
private Map cacheSizesForTopic;
private Map rawCacheSizesForTopic;
private boolean rawCacheSizesForTopicChanged;
private Map rawCacheSizesForPattern;
private boolean rawCacheSizesForPatternChanged;
private Integer cleanupStartThreshhold;
private Integer cleanupStopThreshold;
private Long cleanupPeriodMS;
private int weakRefPlusProxySubscriberCount;
private Timer cleanupTimer;
private TimerTask cleanupTimerTask;
private static final Comparator PRIORITIZED_SUBSCRIBER_COMPARATOR = new PrioritizedSubscriberComparator();
private boolean hasEverUsedPrioritized;
/** Creates a ThreadSafeEventService that does not monitor timing of handlers. */
public ThreadSafeEventService() {
this(null, false, null, null, null);
}
/**
* Creates a ThreadSafeEventService while providing time monitoring options.
*
* @param timeThresholdForEventTimingEventPublication the longest time a subscriber should spend handling an event,
* The service will publish an SubscriberTimingEvent after listener processing if the time was exceeded. If null, no
* EventSubscriberTimingEvent will be issued.
*/
public ThreadSafeEventService(Long timeThresholdForEventTimingEventPublication) {
this(timeThresholdForEventTimingEventPublication, false, null, null, null);
}
/**
* Creates a ThreadSafeEventService while providing time monitoring options.
*
* @param timeThresholdForEventTimingEventPublication the longest time a subscriber should spend handling an event,
* The service will publish an SubscriberTimingEvent after listener processing if the time was exceeded. If null, no
* EventSubscriberTimingEvent will be issued.
* @param subscribeTimingEventsInternally add a subscriber to the SubscriberTimingEvent internally and call the
* protected subscribeTiming() method when they occur. This logs a warning to the {@link Logger} by
* default.
*/
public ThreadSafeEventService(Long timeThresholdForEventTimingEventPublication, boolean subscribeTimingEventsInternally) {
this(timeThresholdForEventTimingEventPublication, subscribeTimingEventsInternally, null, null, null);
}
/**
* Creates a ThreadSafeEventService while providing proxy cleanup customization.
* Proxies are used with Annotations.
*
* @param cleanupStartThreshold see class javadoc.
* @param cleanupStopThreshold see class javadoc.
* @param cleanupPeriodMS see class javadoc.
*/
public ThreadSafeEventService(Integer cleanupStartThreshold,
Integer cleanupStopThreshold, Long cleanupPeriodMS) {
this(null, false, cleanupStartThreshold,
cleanupStopThreshold, cleanupPeriodMS);
}
/**
* Creates a ThreadSafeEventService while providing time monitoring options.
*
* @param timeThresholdForEventTimingEventPublication the longest time a subscriber should spend handling an event.
* The service will publish an SubscriberTimingEvent after listener processing if the time was exceeded. If null, no
* SubscriberTimingEvent will be issued.
* @param subscribeTimingEventsInternally add a subscriber to the SubscriberTimingEvent internally and call the
* protected subscribeTiming() method when they occur. This logs a warning to the {@link Logger} by
* default.
* @param cleanupStartThreshold see class javadoc.
* @param cleanupStopThreshold see class javadoc.
* @param cleanupPeriodMS see class javadoc.
*
* @throws IllegalArgumentException if timeThresholdForEventTimingEventPublication is null and
* subscribeTimingEventsInternally is true.
*/
public ThreadSafeEventService(Long timeThresholdForEventTimingEventPublication,
boolean subscribeTimingEventsInternally, Integer cleanupStartThreshold,
Integer cleanupStopThreshold, Long cleanupPeriodMS) {
if (timeThresholdForEventTimingEventPublication == null && subscribeTimingEventsInternally) {
throw new IllegalArgumentException("null, true in constructor is not valid. If you want to send timing messages for all events and subscribe them internally, pass 0, true");
}
this.timeThresholdForEventTimingEventPublication = timeThresholdForEventTimingEventPublication;
if (subscribeTimingEventsInternally) {
//Listen to timing events and log them
subscribeStrongly(SubscriberTimingEvent.class, new EventSubscriber() {
public void onEvent(Object event) {
subscribeTiming((SubscriberTimingEvent) event);
}
});
}
if (cleanupStartThreshold == null) {
this.cleanupStartThreshhold = CLEANUP_START_THRESHOLD_DEFAULT;
} else {
this.cleanupStartThreshhold = cleanupStartThreshold;
}
if (cleanupStopThreshold == null) {
this.cleanupStopThreshold = CLEANUP_STOP_THRESHOLD_DEFAULT;
} else {
this.cleanupStopThreshold = cleanupStopThreshold;
}
if (cleanupPeriodMS == null) {
this.cleanupPeriodMS = CLEANUP_PERIOD_MS_DEFAULT;
} else {
this.cleanupPeriodMS = cleanupPeriodMS;
}
}
/**
* Gets the threshold above which cleanup starts. See the class javadoc on cleanup.
* @return the threshold at which cleanup starts
*/
public Integer getCleanupStartThreshhold() {
synchronized (listenerLock) {
return cleanupStartThreshhold;
}
}
/**
* Sets the threshold above which cleanup starts. See the class javadoc on cleanup.
* @param cleanupStartThreshhold threshold at which cleanup starts
*/
public void setCleanupStartThreshhold(Integer cleanupStartThreshhold) {
synchronized (listenerLock) {
this.cleanupStartThreshhold = cleanupStartThreshhold;
}
}
/**
* Gets the threshold below which cleanup stops. See the class javadoc on cleanup.
* @return threshold at which cleanup stops (it may start again)
*/
public Integer getCleanupStopThreshold() {
synchronized (listenerLock) {
return cleanupStopThreshold;
}
}
/**
* Sets the threshold below which cleanup stops. See the class javadoc on cleanup.
* @param cleanupStopThreshold threshold at which cleanup stops (it may start again).
*/
public void setCleanupStopThreshold(Integer cleanupStopThreshold) {
synchronized (listenerLock) {
this.cleanupStopThreshold = cleanupStopThreshold;
}
}
/**
* Get the cleanup interval. See the class javadoc on cleanup.
* @return interval in milliseconds between cleanup runs.
*/
public Long getCleanupPeriodMS() {
synchronized (listenerLock) {
return cleanupPeriodMS;
}
}
/**
* Sets the cleanup interval. See the class javadoc on cleanup.
* @param cleanupPeriodMS interval in milliseconds between cleanup runs. Passing null
* stops cleanup.
*/
public void setCleanupPeriodMS(Long cleanupPeriodMS) {
synchronized (listenerLock) {
this.cleanupPeriodMS = cleanupPeriodMS;
}
}
/** @see EventService#subscribe(Class,EventSubscriber) */
public boolean subscribe(Class cl, EventSubscriber eh) {
if (cl == null) {
throw new IllegalArgumentException("Event class must not be null");
}
if (eh == null) {
throw new IllegalArgumentException("Event subscriber must not be null");
}
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("Subscribing by class, class:" + cl + ", subscriber:" + eh);
}
return subscribe(cl, subscribersByEventClass, new WeakReference(eh));
}
/** @see EventService#subscribe(java.lang.reflect.Type, EventSubscriber) */
public boolean subscribe(Type type, EventSubscriber eh) {
return subscribe(type, subscribersByEventType, new WeakReference(eh));
}
/** @see EventService#subscribeExactly(Class,EventSubscriber) */
public boolean subscribeExactly(Class cl, EventSubscriber eh) {
if (cl == null) {
throw new IllegalArgumentException("Event class must not be null");
}
if (eh == null) {
throw new IllegalArgumentException("Event subscriber must not be null");
}
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("Subscribing by class, class:" + cl + ", subscriber:" + eh);
}
return subscribe(cl, subscribersByExactEventClass, new WeakReference(eh));
}
/** @see EventService#subscribe(String,EventTopicSubscriber) */
public boolean subscribe(String topic, EventTopicSubscriber eh) {
if (topic == null) {
throw new IllegalArgumentException("Topic must not be null");
}
if (eh == null) {
throw new IllegalArgumentException("Event topic subscriber must not be null");
}
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("Subscribing by topic name, name:" + topic + ", subscriber:" + eh);
}
return subscribe(topic, subscribersByTopic, new WeakReference(eh));
}
/** @see EventService#subscribe(Pattern,EventTopicSubscriber) */
public boolean subscribe(Pattern pat, EventTopicSubscriber eh) {
if (pat == null) {
throw new IllegalArgumentException("Pattern must not be null");
}
if (eh == null) {
throw new IllegalArgumentException("Event subscriber must not be null");
}
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("Subscribing by pattern, pattern:" + pat + ", subscriber:" + eh);
}
PatternWrapper patternWrapper = new PatternWrapper(pat);
return subscribe(patternWrapper, subscribersByTopicPattern, new WeakReference(eh));
}
/** @see EventService#subscribeStrongly(Class,EventSubscriber) */
public boolean subscribeStrongly(Class cl, EventSubscriber eh) {
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("Subscribing weakly by class, class:" + cl + ", subscriber:" + eh);
}
if (eh == null) {
throw new IllegalArgumentException("Subscriber cannot be null.");
}
return subscribe(cl, subscribersByEventClass, eh);
}
/** @see EventService#subscribeExactlyStrongly(Class,EventSubscriber) */
public boolean subscribeExactlyStrongly(Class cl, EventSubscriber eh) {
if (cl == null) {
throw new IllegalArgumentException("Event class must not be null");
}
if (eh == null) {
throw new IllegalArgumentException("Event subscriber must not be null");
}
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("Subscribing by class, class:" + cl + ", subscriber:" + eh);
}
return subscribe(cl, subscribersByExactEventClass, eh);
}
/** @see EventService#subscribeStrongly(String,EventTopicSubscriber) */
public boolean subscribeStrongly(String name, EventTopicSubscriber eh) {
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("Subscribing weakly by topic name, name:" + name + ", subscriber:" + eh);
}
if (eh == null) {
throw new IllegalArgumentException("Subscriber cannot be null.");
}
return subscribe(name, subscribersByTopic, eh);
}
/** @see EventService#subscribeStrongly(Pattern,EventTopicSubscriber) */
public boolean subscribeStrongly(Pattern pat, EventTopicSubscriber eh) {
if (pat == null) {
throw new IllegalArgumentException("Pattern must not be null");
}
if (eh == null) {
throw new IllegalArgumentException("Event subscriber must not be null");
}
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("Subscribing by pattern, pattern:" + pat + ", subscriber:" + eh);
}
PatternWrapper patternWrapper = new PatternWrapper(pat);
return subscribe(patternWrapper, subscribersByTopicPattern, eh);
}
/** @see org.bushe.swing.event.EventService#clearAllSubscribers() */
public void clearAllSubscribers() {
synchronized (listenerLock) {
unsubscribeAllInMap(subscribersByEventType);
unsubscribeAllInMap(subscribersByEventClass);
unsubscribeAllInMap(subscribersByExactEventClass);
unsubscribeAllInMap(subscribersByTopic);
unsubscribeAllInMap(subscribersByTopicPattern);
unsubscribeAllInMap(vetoListenersByClass);
unsubscribeAllInMap(vetoListenersByExactClass);
unsubscribeAllInMap(vetoListenersByTopic);
unsubscribeAllInMap(vetoListenersByTopicPattern);
}
}
private void unsubscribeAllInMap(Map subscriberMap) {
synchronized (listenerLock) {
Set subscriptionKeys = subscriberMap.keySet();
for (Object key : subscriptionKeys) {
List subscribers = (List) subscriberMap.get(key);
while (!subscribers.isEmpty()) {
unsubscribe(key, subscriberMap, subscribers.get(0));
}
}
}
}
/** @see EventService#subscribeVetoListener(Class,VetoEventListener) */
public boolean subscribeVetoListener(Class eventClass, VetoEventListener vetoListener) {
if (vetoListener == null) {
throw new IllegalArgumentException("VetoEventListener cannot be null.");
}
if (eventClass == null) {
throw new IllegalArgumentException("eventClass cannot be null.");
}
return subscribeVetoListener(eventClass, vetoListenersByClass, new WeakReference(vetoListener));
}
/** @see EventService#subscribeVetoListenerExactly(Class,VetoEventListener) */
public boolean subscribeVetoListenerExactly(Class eventClass, VetoEventListener vetoListener) {
if (vetoListener == null) {
throw new IllegalArgumentException("VetoEventListener cannot be null.");
}
if (eventClass == null) {
throw new IllegalArgumentException("eventClass cannot be null.");
}
return subscribeVetoListener(eventClass, vetoListenersByExactClass, new WeakReference(vetoListener));
}
/** @see EventService#subscribeVetoListener(String,VetoTopicEventListener) */
public boolean subscribeVetoListener(String topic, VetoTopicEventListener vetoListener) {
if (vetoListener == null) {
throw new IllegalArgumentException("VetoEventListener cannot be null.");
}
if (topic == null) {
throw new IllegalArgumentException("topic cannot be null.");
}
return subscribeVetoListener(topic, vetoListenersByTopic, new WeakReference(vetoListener));
}
/** @see EventService#subscribeVetoListener(Pattern,VetoTopicEventListener) */
public boolean subscribeVetoListener(Pattern topicPattern, VetoTopicEventListener vetoListener) {
if (vetoListener == null) {
throw new IllegalArgumentException("VetoEventListener cannot be null.");
}
if (topicPattern == null) {
throw new IllegalArgumentException("topicPattern cannot be null.");
}
PatternWrapper patternWrapper = new PatternWrapper(topicPattern);
return subscribeVetoListener(patternWrapper, vetoListenersByTopicPattern, new WeakReference(vetoListener));
}
/** @see EventService#subscribeVetoListenerStrongly(Class,VetoEventListener) */
public boolean subscribeVetoListenerStrongly(Class eventClass, VetoEventListener vetoListener) {
if (vetoListener == null) {
throw new IllegalArgumentException("VetoEventListener cannot be null.");
}
if (eventClass == null) {
throw new IllegalArgumentException("eventClass cannot be null.");
}
return subscribeVetoListener(eventClass, vetoListenersByClass, vetoListener);
}
/** @see EventService#subscribeVetoListenerExactlyStrongly(Class,VetoEventListener) */
public boolean subscribeVetoListenerExactlyStrongly(Class eventClass, VetoEventListener vetoListener) {
if (vetoListener == null) {
throw new IllegalArgumentException("VetoEventListener cannot be null.");
}
if (eventClass == null) {
throw new IllegalArgumentException("eventClass cannot be null.");
}
return subscribeVetoListener(eventClass, vetoListenersByExactClass, vetoListener);
}
/** @see EventService#subscribeVetoListenerStrongly(String,VetoTopicEventListener) */
public boolean subscribeVetoListenerStrongly(String topic, VetoTopicEventListener vetoListener) {
if (vetoListener == null) {
throw new IllegalArgumentException("VetoListener cannot be null.");
}
if (topic == null) {
throw new IllegalArgumentException("topic cannot be null.");
}
return subscribeVetoListener(topic, vetoListenersByTopic, vetoListener);
}
/** @see EventService#subscribeVetoListenerStrongly(Pattern,VetoTopicEventListener) */
public boolean subscribeVetoListenerStrongly(Pattern topicPattern, VetoTopicEventListener vetoListener) {
if (vetoListener == null) {
throw new IllegalArgumentException("VetoTopicEventListener cannot be null.");
}
if (topicPattern == null) {
throw new IllegalArgumentException("topicPattern cannot be null.");
}
PatternWrapper patternWrapper = new PatternWrapper(topicPattern);
return subscribeVetoListener(patternWrapper, vetoListenersByTopicPattern, vetoListener);
}
/**
* All veto subscriptions methods call this method. Extending classes only have to override this method to subscribe
* all veto subscriptions.
*
* @param subscription the topic, Pattern, or event class to subsribe to
* @param vetoListenerMap the internal map of veto listeners to use (by topic of class)
* @param vetoListener the veto listener to subscribe, may be a VetoEventListener or a WeakReference to one
*
* @return boolean if the veto listener is subscribed (was not subscribed).
*
* @throws IllegalArgumentException if vl or o is null
*/
protected boolean subscribeVetoListener(final Object subscription, final Map vetoListenerMap, final Object vetoListener) {
if (LOG.isLoggable(Level.DEBUG)) {
LOG.debug("subscribeVetoListener(" + subscription + "," + vetoListener + ")");
}
if (vetoListener == null) {
throw new IllegalArgumentException("Can't subscribe null veto listener to " + subscription);
}
if (subscription == null) {
throw new IllegalArgumentException("Can't subscribe veto listener to null.");
}
return subscribe(subscription, vetoListenerMap, vetoListener);
}
/**
* All subscribe methods call this method, including veto subscriptions.
* Extending classes only have to override this method to subscribe all
* subscriber subscriptions.
*
* Overriding this method is only for the adventurous. This basically gives you just enough rope to hang yourself.
*
* @param classTopicOrPatternWrapper the topic String, event Class, or PatternWrapper to subscribe to
* @param subscriberMap the internal map of subscribers to use (by topic or class)
* @param subscriber the EventSubscriber or EventTopicSubscriber to subscribe, or a WeakReference to either
*
* @return boolean if the subscriber is subscribed (was not subscribed).
*
* @throws IllegalArgumentException if subscriber or topicOrClass is null
*/
protected boolean subscribe(final Object classTopicOrPatternWrapper, final Map