/* * Quasar: lightweight threads and actors for the JVM. * Copyright (c) 2013-2016, Parallel Universe Software Co. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 3.0 * as published by the Free Software Foundation. */ package co.paralleluniverse.actors.behaviors; import co.paralleluniverse.actors.Actor; import co.paralleluniverse.actors.ActorLoader; import co.paralleluniverse.actors.ActorRef; import co.paralleluniverse.actors.MailboxConfig; import static co.paralleluniverse.actors.behaviors.RequestReplyHelper.reply; import static co.paralleluniverse.actors.behaviors.RequestReplyHelper.replyError; import co.paralleluniverse.fibers.FiberFactory; import co.paralleluniverse.fibers.FiberScheduler; import co.paralleluniverse.fibers.SuspendExecution; import co.paralleluniverse.strands.Strand; import co.paralleluniverse.strands.StrandFactory; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A behavior actor that can be notified of *event* messages, which are delivered to *event handlers* which may be registered with the actor. * The event handlers are called synchronously on the same strand as the actor's, so they may delay processing by other handlers if they block the strand. * * @author pron */ public class EventSourceActor<Event> extends BehaviorActor { private static final Logger LOG = LoggerFactory.getLogger(EventSourceActor.class); private final List<EventHandler<Event>> handlers = new ArrayList<>(); private final List<EventHandler<Event>> nonUpgradedHandlers = new ArrayList<>(); /** * Creates a new event-source actor. * * @param name the actor name (may be {@code null}). * @param initializer an optional delegate object that will be run upon actor initialization and termination. May be {@code null}. * @param strand this actor's strand. * @param mailboxConfig this actor's mailbox settings. */ public EventSourceActor(String name, Initializer initializer, Strand strand, MailboxConfig mailboxConfig) { super(name, initializer, strand, mailboxConfig); } //<editor-fold defaultstate="collapsed" desc="Behavior boilerplate"> /////////// Behavior boilerplate /////////////////////////////////// @Override protected EventSource<Event> makeRef(ActorRef<Object> ref) { return new EventSource<Event>(ref); } @Override public EventSource<Event> ref() { return (EventSource<Event>) super.ref(); } @Override protected EventSource<Event> self() { return ref(); } @Override public EventSource<Event> spawn(StrandFactory sf) { return (EventSource<Event>) super.spawn(sf); } @Override public EventSource<Event> spawn(FiberFactory ff) { return (EventSource<Event>) super.spawn(ff); } @Override public EventSource<Event> spawn() { return (EventSource<Event>) super.spawn(); } @Override public EventSource<Event> spawnThread() { return (EventSource<Event>) super.spawnThread(); } public static <Event> EventSourceActor<Event> currentEventSourceActor() { return (EventSourceActor<Event>) Actor.<Object, Void>currentActor(); } @Override public Logger log() { return LOG; } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Constructors"> /////////// Constructors /////////////////////////////////// /** * Creates a new event-source actor. * * @param name the actor name (may be {@code null}). * @param initializer an optional delegate object that will be run upon actor initialization and termination. May be {@code null}. * @param mailboxConfig this actor's mailbox settings. */ public EventSourceActor(String name, Initializer initializer, MailboxConfig mailboxConfig) { this(name, initializer, null, mailboxConfig); } /** * Creates a new event-source actor. * * @param name the actor name (may be {@code null}). * @param initializer an optional delegate object that will be run upon actor initialization and termination. May be {@code null}. */ public EventSourceActor(String name, Initializer initializer) { this(name, initializer, null, null); } /** * Creates a new event-source actor. * * @param initializer an optional delegate object that will be run upon actor initialization and termination. May be {@code null}. * @param mailboxConfig this actor's mailbox settings. */ public EventSourceActor(Initializer initializer, MailboxConfig mailboxConfig) { this(null, initializer, null, mailboxConfig); } /** * Creates a new event-source actor. * * @param initializer an optional delegate object that will be run upon actor initialization and termination. May be {@code null}. */ public EventSourceActor(Initializer initializer) { this(null, initializer, null, null); } /** * Creates a new event-source actor. * * @param name the actor name (may be {@code null}). * @param mailboxConfig this actor's mailbox settings. */ public EventSourceActor(String name, MailboxConfig mailboxConfig) { this(name, null, null, mailboxConfig); } /** * Creates a new event-source actor. * * @param name the actor name (may be {@code null}). */ public EventSourceActor(String name) { this(name, null, null, null); } /** * Creates a new event-source actor. * * @param mailboxConfig this actor's mailbox settings. */ public EventSourceActor(MailboxConfig mailboxConfig) { this(null, null, null, mailboxConfig); } /** * Creates a new event-source actor. */ public EventSourceActor() { this(null, null, null, null); } //</editor-fold> protected boolean addHandler(EventHandler<Event> handler) throws SuspendExecution, InterruptedException { verifyInActor(); EventHandler<Event> _handler = ActorLoader.getReplacementFor(handler); log().info("{} adding handler {}", this, _handler); boolean res = handlers.add(_handler); nonUpgradedHandlers.add(handler); return res; } protected boolean removeHandler(EventHandler<Event> handler) throws SuspendExecution, InterruptedException { verifyInActor(); log().info("{} removing handler {}", this, handler); int index = -1; index = handlers.indexOf(handler); if (index == -1) index = nonUpgradedHandlers.indexOf(handler); if (index == -1) return false; handlers.remove(index); nonUpgradedHandlers.remove(index); return true; } protected void notify(Event event) throws SuspendExecution { ref().send(event); } @Override protected final void handleMessage(Object m1) throws InterruptedException, SuspendExecution { if (m1 instanceof RequestMessage) { final RequestMessage req = (RequestMessage) m1; try { if (m1 instanceof HandlerMessage) { final HandlerMessage m = (HandlerMessage) m1; if (m.add) reply(req, addHandler(m.handler)); else reply(req, removeHandler(m.handler)); } } catch (Exception e) { replyError(req, e); } } else notifyHandlers((Event) m1); } @Override protected void onTerminate(Throwable cause) throws SuspendExecution, InterruptedException { super.onTerminate(cause); handlers.clear(); } private void notifyHandlers(Event event) throws InterruptedException, SuspendExecution { log().debug("{} Got event {}", this, event); for (ListIterator<EventHandler<Event>> it = handlers.listIterator(); it.hasNext();) { EventHandler<Event> handler = it.next(); EventHandler<Event> _handler = ActorLoader.getReplacementFor(handler); if (_handler != handler) { log().info("Upgraded event handler implementation: {}", _handler); it.set(_handler); } _handler.handleEvent(event); } } static class HandlerMessage<Event> extends RequestMessage { final EventHandler<Event> handler; final boolean add; public HandlerMessage(ActorRef<?> from, Object id, EventHandler<Event> handler, boolean add) { super(from, id); this.handler = handler; this.add = add; } @Override protected String contentString() { return super.contentString() + " handler: " + handler + " add: " + add; } } }