/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.core.context.notification;
import org.mule.runtime.core.api.context.notification.ServerNotification;
import org.mule.runtime.core.api.context.notification.ServerNotificationListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* For a particular configuration, this describes what events should be delivered where. It is read-only and a lazy instance is
* cached by the {@link Configuration}
*/
class Policy {
// map from event to set of senders
private Map<Class<? extends ServerNotification>, Collection<Sender>> eventToSenders =
new HashMap<>();
// these are cumulative - set values should never change, they are just a cache of known info
// they are co and contra-variant wrt to exact event type (see code below).
private ConcurrentMap knownEventsExact = new ConcurrentHashMap();
private ConcurrentMap knownEventsSuper = new ConcurrentHashMap();
/**
* For each listener, we check each interface and see what events can be delivered.
*/
Policy(Map<Class<? extends ServerNotificationListener>, Set<Class<? extends ServerNotification>>> interfaceToEvents,
Set<ListenerSubscriptionPair> listenerSubscriptionPairs,
Set<Class<? extends ServerNotificationListener>> disabledInterfaces,
Set<Class<? extends ServerNotification>> disabledEvents) {
for (ListenerSubscriptionPair pair : listenerSubscriptionPairs) {
ServerNotificationListener listener = pair.getListener();
for (Class<? extends ServerNotificationListener> iface : interfaceToEvents.keySet()) {
if (notASubclassOfAnyClassInSet(disabledInterfaces, iface)) {
if (iface.isAssignableFrom(listener.getClass())) {
Set<Class<? extends ServerNotification>> events = interfaceToEvents.get(iface);
for (Class<? extends ServerNotification> event : events) {
if (notASubclassOfAnyClassInSet(disabledEvents, event)) {
knownEventsExact.put(event, Boolean.TRUE);
knownEventsSuper.put(event, Boolean.TRUE);
if (!eventToSenders.containsKey(event)) {
// use a collection with predictable iteration order
eventToSenders.put(event, new ArrayList<Sender>());
}
eventToSenders.get(event).add(new Sender(pair));
}
}
}
}
}
}
}
protected static boolean notASubclassOfAnyClassInSet(Set set, Class clazz) {
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
Class disabled = (Class) iterator.next();
if (disabled.isAssignableFrom(clazz)) {
return false;
}
}
return true;
}
protected static boolean notASuperclassOfAnyClassInSet(Set set, Class clazz) {
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
Class disabled = (Class) iterator.next();
if (clazz.isAssignableFrom(disabled)) {
return false;
}
}
return true;
}
void dispatch(ServerNotification notification, NotifierCallback notifier) {
if (null != notification) {
Class notfnClass = notification.getClass();
// search if we don't know about this event, or if we do know it is used
if (!knownEventsExact.containsKey(notfnClass)) {
boolean found = doDispatch(notification, notfnClass, notifier);
knownEventsExact.put(notfnClass, Boolean.valueOf(found));
} else if (((Boolean) knownEventsExact.get(notfnClass)).booleanValue()) {
boolean found = doDispatch(notification, notfnClass, notifier);
// reduce contention on the map by not writing the same value over and over again.
if (!found) {
knownEventsExact.put(notfnClass, Boolean.valueOf(found));
}
}
}
}
protected boolean doDispatch(ServerNotification notification, Class<? extends ServerNotification> notfnClass,
NotifierCallback notifier) {
boolean found = false;
for (Class<? extends ServerNotification> event : eventToSenders.keySet()) {
if (event.isAssignableFrom(notfnClass)) {
found = true;
for (Sender sender : eventToSenders.get(event)) {
sender.dispatch(notification, notifier);
}
}
}
return found;
}
/**
* This returns a very "conservative" value - it is true if the notification or any subclass would be accepted. So if it returns
* false then you can be sure that there is no need to send the notification. On the other hand, if it returns true there is no
* guarantee that the notification "really" will be dispatched to any listener.
*
* @param notfnClass Either the notification class being generated or some superclass
* @return false if there is no need to dispatch the notification
*/
boolean isNotificationEnabled(Class notfnClass) {
if (!knownEventsSuper.containsKey(notfnClass)) {
boolean found = false;
// this is exhaustive because we initialise to include all events handled.
for (Iterator events = knownEventsSuper.keySet().iterator(); events.hasNext() && !found;) {
Class event = (Class) events.next();
found = ((Boolean) knownEventsSuper.get(event)).booleanValue() && notfnClass.isAssignableFrom(event);
}
knownEventsSuper.put(notfnClass, Boolean.valueOf(found));
}
if (!knownEventsExact.containsKey(notfnClass)) {
boolean found = false;
for (Iterator events = eventToSenders.keySet().iterator(); events.hasNext() && !found;) {
Class event = (Class) events.next();
found = event.isAssignableFrom(notfnClass);
}
knownEventsExact.put(notfnClass, Boolean.valueOf(found));
}
return ((Boolean) knownEventsSuper.get(notfnClass)).booleanValue()
|| ((Boolean) knownEventsExact.get(notfnClass)).booleanValue();
}
}