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

com.swirlds.common.notification.internal.Dispatcher Maven / Gradle / Ivy

Go to download

Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.

There is a newer version: 0.56.6
Show newest version
/*
 * Copyright (C) 2020-2024 Hedera Hashgraph, LLC
 *
 * 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.swirlds.common.notification.internal;

import com.swirlds.common.notification.DispatchException;
import com.swirlds.common.notification.Listener;
import com.swirlds.common.notification.Notification;
import com.swirlds.common.notification.NotificationResult;
import com.swirlds.common.threading.framework.config.ThreadConfiguration;
import com.swirlds.common.threading.manager.ThreadManager;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.function.Consumer;

public class Dispatcher {

    private static final int THREAD_STOP_WAIT_MS = 5000;
    private static final String COMPONENT_NAME = "dispatch";

    private final PriorityBlockingQueue> asyncDispatchQueue;

    private final String listenerClassName;

    private final Object mutex;

    private final List listeners;

    private volatile Thread dispatchThread;

    private volatile boolean running;

    /**
     * Responsible for creating and managing threads used by this object.
     */
    private final ThreadManager threadManager;

    /**
     * Create a new dispatcher.
     *
     * @param threadManager
     * 		responsible for creating and managing threads used for dispatches
     * @param listenerClass
     * 		the dispatch type
     */
    public Dispatcher(final ThreadManager threadManager, final Class listenerClass) {
        this.threadManager = threadManager;
        this.mutex = new Object();
        this.listeners = new CopyOnWriteArrayList<>();
        this.asyncDispatchQueue = new PriorityBlockingQueue<>();
        this.listenerClassName = listenerClass.getSimpleName();
    }

    public Object getMutex() {
        return mutex;
    }

    public synchronized boolean isRunning() {
        return running && dispatchThread != null && dispatchThread.isAlive();
    }

    public synchronized void start() {
        if (dispatchThread != null && dispatchThread.isAlive()) {
            stop();
        }

        dispatchThread = new ThreadConfiguration(threadManager)
                .setComponent(COMPONENT_NAME)
                .setThreadName(String.format("notify %s", listenerClassName))
                .setRunnable(this::worker)
                .build();

        running = true;
        dispatchThread.start();
    }

    public synchronized void stop() {
        running = false;

        if (asyncDispatchQueue.size() == 0) {
            dispatchThread.interrupt();
        }

        try {
            dispatchThread.join(THREAD_STOP_WAIT_MS);

            if (dispatchThread.isAlive() && !dispatchThread.isInterrupted()) {
                dispatchThread.interrupt();
            }

            dispatchThread = null;
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }

    public  void notifySync(
            final N notification, final Consumer> callback) {
        handleDispatch(notification, false, callback);
    }

    public  void notifyAsync(
            final N notification, final Consumer> callback) {
        if (!isRunning()) {
            start();
        }

        asyncDispatchQueue.put(new DispatchTask<>(notification, callback));
    }

    public synchronized boolean addListener(final L listener) {
        return listeners.add(listener);
    }

    public synchronized boolean removeListener(final L listener) {
        return listeners.remove(listener);
    }

    private  void handleDispatch(
            final N notification, final boolean throwOnError, final Consumer> callback) {

        final NotificationResult result = new NotificationResult<>(notification, listeners.size());

        for (final L l : listeners) {
            try {
                @SuppressWarnings("unchecked")
                final Listener listener = (Listener) l;
                listener.notify(notification);
            } catch (final Throwable ex) {
                if (throwOnError) {
                    throw new DispatchException(ex);
                }

                result.addException(ex);
            }
        }

        if (callback != null) {
            callback.accept(result);
        }
    }

    private void worker() {
        try {
            while (running || asyncDispatchQueue.size() > 0) {
                @SuppressWarnings("unchecked")
                final DispatchTask, Notification> task =
                        (DispatchTask, Notification>) asyncDispatchQueue.take();

                handleDispatch(task.getNotification(), false, task.getCallback());
            }
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy