alpine.event.framework.BaseEventService.java Source code

Java tutorial

Introduction

Here is the source code for alpine.event.framework.BaseEventService.java

Source

/*
 * This file is part of Alpine.
 *
 * 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.
 *
 * Copyright (c) Steve Springett. All Rights Reserved.
 */
package alpine.event.framework;

import alpine.logging.Logger;
import alpine.model.EventServiceLog;
import alpine.persistence.AlpineQueryManager;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * A publish/subscribe (pub/sub) event service that provides the ability to publish events and
 * asynchronously inform all subscribers to subscribed events.
 *
 * Defaults to a single thread event system when extending this class. This can be changed by
 * specifying an alternative executor service.
 *
 * @author Steve Springett
 * @since 1.0.0
 */
public abstract class BaseEventService implements IEventService {

    private Logger logger = Logger.getLogger(BaseEventService.class);
    private Map<Class<? extends Event>, ArrayList<Class<? extends Subscriber>>> subscriptionMap = new ConcurrentHashMap<>();
    private Map<UUID, ArrayList<UUID>> chainTracker = new ConcurrentHashMap<>();
    private ExecutorService executor = Executors.newFixedThreadPool(1,
            new BasicThreadFactory.Builder().namingPattern("Alpine-BaseEventService-%d")
                    .uncaughtExceptionHandler(new LoggableUncaughtExceptionHandler()).build());
    private final ExecutorService dynamicExecutor = Executors.newWorkStealingPool();

    /**
     * @param executor an ExecutorService instance
     * @since 1.0.0
     */
    protected void setExecutorService(ExecutorService executor) {
        this.executor = executor;
    }

    /**
     * @param logger the logger instance to use for the executed event
     * @since 1.0.0
     */
    protected void setLogger(Logger logger) {
        this.logger = logger;
    }

    /**
     * {@inheritDoc}
     * @since 1.0.0
     */
    public void publish(Event event) {
        logger.debug("Dispatching event: " + event.getClass().toString());
        final ArrayList<Class<? extends Subscriber>> subscriberClasses = subscriptionMap.get(event.getClass());
        if (subscriberClasses == null) {
            logger.debug("No subscribers to inform from event: " + event.getClass().getName());
            return;
        }
        for (Class<? extends Subscriber> clazz : subscriberClasses) {
            logger.debug("Alerting subscriber " + clazz.getName());

            if (event instanceof ChainableEvent) {
                addTrackedEvent((ChainableEvent) event);
            }

            // Check to see if the Event is Unblocked. If so, use a separate executor pool from normal events
            final ExecutorService executorService = event instanceof UnblockedEvent ? dynamicExecutor : executor;

            executorService.execute(() -> {
                try (AlpineQueryManager qm = new AlpineQueryManager()) {
                    final EventServiceLog eventServiceLog = qm.createEventServiceLog(clazz);
                    final Subscriber subscriber = clazz.getDeclaredConstructor().newInstance();
                    subscriber.inform(event);
                    qm.updateEventServiceLog(eventServiceLog);
                    if (event instanceof ChainableEvent) {
                        ChainableEvent chainableEvent = (ChainableEvent) event;
                        logger.debug("Calling onSuccess");
                        for (ChainLink chainLink : chainableEvent.onSuccess()) {
                            if (chainLink.getSuccessEventService() != null) {
                                Method method = chainLink.getSuccessEventService().getMethod("getInstance");
                                IEventService es = (IEventService) method.invoke(chainLink.getSuccessEventService(),
                                        new Object[0]);
                                es.publish(chainLink.getSuccessEvent());
                            } else {
                                Event.dispatch(chainLink.getSuccessEvent());
                            }
                        }
                    }
                } catch (NoSuchMethodException | InvocationTargetException | InstantiationException
                        | IllegalAccessException | SecurityException e) {
                    logger.error("An error occurred while informing subscriber: " + e);
                    if (event instanceof ChainableEvent) {
                        ChainableEvent chainableEvent = (ChainableEvent) event;
                        logger.debug("Calling onFailure");
                        for (ChainLink chainLink : chainableEvent.onFailure()) {
                            if (chainLink.getFailureEventService() != null) {
                                try {
                                    Method method = chainLink.getFailureEventService().getMethod("getInstance");
                                    IEventService es = (IEventService) method
                                            .invoke(chainLink.getFailureEventService(), new Object[0]);
                                    es.publish(chainLink.getFailureEvent());
                                } catch (NoSuchMethodException | InvocationTargetException
                                        | IllegalAccessException ex) {
                                    logger.error("Exception while calling onFailure callback", ex);
                                }
                            } else {
                                Event.dispatch(chainLink.getFailureEvent());
                            }
                        }
                    }
                }

                if (event instanceof ChainableEvent) {
                    removeTrackedEvent((ChainableEvent) event);
                }

            });
        }
    }

    /**
     * {@inheritDoc}
     * @since 1.4.0
     */
    public synchronized boolean isEventBeingProcessed(ChainableEvent event) {
        return isEventBeingProcessed(event.getChainIdentifier());
    }

    /**
     * {@inheritDoc}
     * @since 1.4.0
     */
    public synchronized boolean isEventBeingProcessed(UUID chainIdentifier) {
        ArrayList<UUID> eventIdentifiers = chainTracker.get(chainIdentifier);
        return eventIdentifiers != null && eventIdentifiers.size() != 0;
    }

    private synchronized void addTrackedEvent(ChainableEvent event) {
        ArrayList<UUID> eventIdentifiers = chainTracker.get(event.getChainIdentifier());
        if (eventIdentifiers == null) {
            eventIdentifiers = new ArrayList<>();
        }
        eventIdentifiers.add(event.getEventIdentifier());
        chainTracker.put(event.getChainIdentifier(), eventIdentifiers);
    }

    private synchronized void removeTrackedEvent(ChainableEvent event) {
        ArrayList<UUID> eventIdentifiers = chainTracker.get(event.getChainIdentifier());
        if (eventIdentifiers == null) {
            return;
        }
        eventIdentifiers.remove(event.getEventIdentifier());
        chainTracker.put(event.getChainIdentifier(), eventIdentifiers);
    }

    /**
     * {@inheritDoc}
     * @since 1.0.0
     */
    public void subscribe(Class<? extends Event> eventType, Class<? extends Subscriber> subscriberType) {
        if (!subscriptionMap.containsKey(eventType)) {
            subscriptionMap.put(eventType, new ArrayList<>());
        }
        final ArrayList<Class<? extends Subscriber>> subscribers = subscriptionMap.get(eventType);
        if (!subscribers.contains(subscriberType)) {
            subscribers.add(subscriberType);
        }
    }

    /**
     * {@inheritDoc}
     * @since 1.0.0
     */
    public void unsubscribe(Class<? extends Subscriber> subscriberType) {
        for (ArrayList<Class<? extends Subscriber>> list : subscriptionMap.values()) {
            list.remove(subscriberType);
        }
    }

    /**
     * {@inheritDoc}
     * @since 1.2.0
     */
    public boolean hasSubscriptions(Event event) {
        final ArrayList<Class<? extends Subscriber>> subscriberClasses = subscriptionMap.get(event.getClass());
        return subscriberClasses != null;
    }

    /**
     * {@inheritDoc}
     * @since 1.0.0
     */
    public void shutdown() {
        logger.info("Shutting down EventService");
        executor.shutdown();
        dynamicExecutor.shutdown();
    }

}