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

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

There is a newer version: 4.0.0-beta-5
Show newest version
/*
 * Copyright 2017 Lukáš Petrovický
 *
 * 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
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.common.extensions.ListenerServiceLoader;
import com.github.triceo.robozonky.internal.api.Defaults;
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 class EventSpecific {

        private final Set>> listeners = new HashSet<>();

        public void addListener(final Refreshable> eventListener) {
            listeners.add(eventListener);
        }

        public Collection>> getListeners() {
            return Collections.unmodifiableSet(new HashSet<>(this.listeners)); // defensive copy
        }

    }

    private static  void fire(final E event, final EventListener listener) {
        try {
            listener.handle(event);
        } catch (final RuntimeException ex) {
            Events.LOGGER.warn("Listener failed: {}.", listener, ex);
        }
    }

    final Map, Events.EventSpecific> registries = new HashMap<>();

    @SuppressWarnings("unchecked")
    private  void loadListeners(final Class eventClass) {
        this.loadListeners(eventClass, null);
    }

    @SuppressWarnings("unchecked")
    synchronized  void loadListeners(final Class eventClass,
                                                      final Refreshable> listener) {
        if (this.registries.containsKey(eventClass)) {
            return;
        }
        Events.LOGGER.trace("Registering event listeners for {}.", eventClass);
        this.registries.put(eventClass, new Events.EventSpecific());
        final Stream>> listeners = Stream.concat(
                ListenerServiceLoader.load(eventClass).stream(),
                listener == null ? Stream.empty() : Stream.of(listener)
        );
        listeners.forEach(l -> ((Events.EventSpecific) this.registries.get(eventClass)).addListener(l));
    }

    @SuppressWarnings("unchecked")
    synchronized  Stream>> getListeners(final Class eventClass) {
        this.loadListeners(eventClass);
        return ((Events.EventSpecific) this.registries.get(eventClass)).getListeners().stream();
    }

    /**
     * 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)} method will have returned. * @param event Event to distribute. * @param Event type to distribute. */ @SuppressWarnings("unchecked") public static void fire(final E event) { final Class eventClass = (Class) event.getClass(); Events.LOGGER.debug("Firing {}.", eventClass); Events.INSTANCE.getListeners(eventClass).parallel() .flatMap(r -> r.getLatest().map(Stream::of).orElse(Stream.empty())) .forEach(l -> Events.fire(event, l)); if (Defaults.isDebugEventStorageEnabled()) { Events.EVENTS_FIRED.add(event); } } /** * This only exists for testing purposes. Also see {@link Defaults#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 - 2024 Weber Informatics LLC | Privacy Policy