io.microsphere.event.AbstractEventDispatcher Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 io.microsphere.event;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static io.microsphere.event.EventListener.findEventType;
import static io.microsphere.util.ServiceLoaderUtils.loadServicesList;
import static java.util.Collections.sort;
import static java.util.Collections.unmodifiableList;
/**
* The abstract {@link EventDispatcher} providers the common implementation.
*
* @see EventDispatcher
* @see Listenable
* @see ServiceLoader
* @see EventListener
* @see Event
* @since 1.0.0
*/
public abstract class AbstractEventDispatcher implements EventDispatcher {
private final Object mutex = new Object();
private final ConcurrentMap, List> listenersCache = new ConcurrentHashMap<>();
private final Executor executor;
/**
* Constructor with an instance of {@link Executor}
*
* @param executor {@link Executor}
* @throws NullPointerException executor
is null
*/
protected AbstractEventDispatcher(Executor executor) {
if (executor == null) {
throw new NullPointerException("executor must not be null");
}
this.executor = executor;
this.loadEventListenerInstances();
}
@Override
public void addEventListener(EventListener> listener) throws NullPointerException, IllegalArgumentException {
Listenable.assertListener(listener);
doInListener(listener, listeners -> {
addIfAbsent(listeners, listener);
});
}
@Override
public void removeEventListener(EventListener> listener) throws NullPointerException, IllegalArgumentException {
Listenable.assertListener(listener);
doInListener(listener, listeners -> listeners.remove(listener));
}
@Override
public List> getAllEventListeners() {
List> listeners = new LinkedList<>();
sortedListeners().forEach(listener -> {
addIfAbsent(listeners, listener);
});
return unmodifiableList(listeners);
}
protected Stream sortedListeners() {
return sortedListeners(e -> true);
}
protected Stream sortedListeners(Predicate, List>> predicate) {
return listenersCache
.entrySet()
.stream()
.filter(predicate)
.map(Map.Entry::getValue)
.flatMap(Collection::stream)
.sorted();
}
private void addIfAbsent(Collection collection, E element) {
if (!collection.contains(element)) {
collection.add(element);
}
}
@Override
public void dispatch(Event event) {
Executor executor = getExecutor();
// execute in sequential or parallel execution model
executor.execute(() -> {
sortedListeners(entry -> entry.getKey().isAssignableFrom(event.getClass()))
.forEach(listener -> {
if (listener instanceof ConditionalEventListener) {
ConditionalEventListener predicateEventListener = (ConditionalEventListener) listener;
if (!predicateEventListener.accept(event)) { // No accept
return;
}
}
// Handle the event
listener.onEvent(event);
});
});
}
/**
* @return the non-null {@link Executor}
*/
@Override
public final Executor getExecutor() {
return executor;
}
protected void doInListener(EventListener> listener, Consumer> consumer) {
Class extends Event> eventType = findEventType(listener);
if (eventType != null) {
synchronized (mutex) {
List listeners = listenersCache.computeIfAbsent(eventType, e -> new LinkedList<>());
// consume
consumer.accept(listeners);
// sort
sort(listeners);
}
}
}
/**
* Default, load the instances of {@link EventListener event listeners} by {@link ServiceLoader}
*
* It could be override by the sub-class
*
* @see EventListener
* @see ServiceLoader#load(Class)
*/
protected void loadEventListenerInstances() {
ClassLoader classLoader = getClass().getClassLoader();
try {
loadServicesList(EventListener.class, classLoader)
.stream()
.sorted()
.forEach(this::addEventListener);
} catch (Throwable ignored) {
}
}
}