All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.triceo.robozonky.app.Events Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 The RoboZonky Project
 *
 * 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 com.github.triceo.robozonky.app;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

import com.github.triceo.robozonky.api.Refreshable;
import com.github.triceo.robozonky.api.notifications.Event;
import com.github.triceo.robozonky.api.notifications.EventListener;
import com.github.triceo.robozonky.api.notifications.ListenerService;
import com.github.triceo.robozonky.api.notifications.SessionInfo;
import com.github.triceo.robozonky.common.extensions.ListenerServiceLoader;
import com.github.triceo.robozonky.internal.api.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Used for registering distributing events to listener registered through {@link ListenerService}.
 * 

* No guarantees are given as to the order in which the listeners will be executed. */ public enum Events { /** * Simple cheap thread-safe singleton. */ INSTANCE; private static final Logger LOGGER = LoggerFactory.getLogger(Events.class); private static final List EVENTS_FIRED = new ArrayList<>(); private static SessionInfo SESSION_INFO = null; private static class EventSpecific { private final Set>> listeners = new LinkedHashSet<>(); public void addListener(final Refreshable> eventListener) { listeners.add(eventListener); } public Stream>> getListeners() { return this.listeners.stream(); } } private static void fire(final E event, final EventListener listener, final SessionInfo info) { try { listener.handle(event, info); } catch (final RuntimeException ex) { Events.LOGGER.warn("Listener failed: {}.", listener, ex); } } final Map, Events.EventSpecific> registries = new HashMap<>(); /** * Retrieve all listeners registered for a given event type. During the first call of this method, it will use the * {@link ListenerService} to register all available listeners. Subsequent calls will only retrieve this * information, without querying the service again. * @param eventClass Class of event for which to look up listeners. * @param Ditto. * @return Listeners available for the event type in question. */ @SuppressWarnings("unchecked") private synchronized Events.EventSpecific loadListeners(final Class eventClass) { return (Events.EventSpecific) this.registries.computeIfAbsent(eventClass, key -> { Events.LOGGER.trace("Registering event listeners for {}.", key); final Events.EventSpecific eventSpecific = new Events.EventSpecific<>(); ListenerServiceLoader.load(eventClass).forEach(eventSpecific::addListener); return eventSpecific; }); } /** * Exists purely for testing purposes. Will call {@link #loadListeners(Class)}, but also add one extra listener. * * @param eventClass Will be used as argument to the {@link #loadListeners(Class)} call. * @param listener The additional listener to register. * @param Type of event to register listeners for. * @return Registered listeners. */ @SuppressWarnings("unchecked") Events.EventSpecific loadListeners(final Class eventClass, final Refreshable> listener) { final Events.EventSpecific eventSpecific = loadListeners(eventClass); if (listener != null) { eventSpecific.addListener(listener); } return eventSpecific; } @SuppressWarnings("unchecked") Stream> getListeners(final Class eventClass) { return this.loadListeners(eventClass).getListeners() .flatMap(r -> r.getLatest().map(Stream::of).orElse(Stream.empty())); } /** * Distribute a particular event to all listeners that have been added and not yet removed for that particular * event. This MUST NOT be called by users and is not part of the public API. *

* The listeners may be executed in parallel, no execution order guarantees are given. When this method returns, * all listeners' {@link EventListener#handle(Event, SessionInfo)} method will have returned. Will use the * internal {@link SessionInfo} instance for that. * @param event Event to distribute. * @param sessionInfo If not null, internal {@link SessionInfo} instance will be updated. * @param Event type to distribute. */ public synchronized static void fire(final E event, final SessionInfo sessionInfo) { if (sessionInfo != null) { Events.SESSION_INFO = sessionInfo; } final Class eventClass = (Class) event.getClass(); Events.LOGGER.debug("Firing {}.", eventClass); Events.INSTANCE.getListeners(eventClass).parallel().forEach(l -> Events.fire(event, l, Events.SESSION_INFO)); Events.LOGGER.trace("Fired {}.", event); if (Settings.INSTANCE.isDebugEventStorageEnabled()) { Events.EVENTS_FIRED.add(event); } } /** * Distribute a particular event to all listeners that have been added and not yet removed for that particular * event. This MUST NOT be called by users and is not part of the public API. *

* The listeners may be executed in parallel, no execution order guarantees are given. When this method returns, * all listeners' {@link EventListener#handle(Event, SessionInfo)} method will have returned. *

* This method will not update the internal {@link SessionInfo} instance. * @param event Event to distribute. * @param Event type to distribute. */ @SuppressWarnings("unchecked") public static void fire(final E event) { Events.fire(event, null); } /** * This only exists for testing purposes. Also see {@link Settings#isDebugEventStorageEnabled()}. * @return Events that were stored, if any. Returns the storage directly, any mutation operations will mutate the * storage. */ public static List getFired() { return Events.EVENTS_FIRED; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy