/* * Quasar: lightweight threads and actors for the JVM. * Copyright (c) 2013-2015, 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.ActorBuilder; import co.paralleluniverse.actors.ActorRef; import co.paralleluniverse.actors.LocalActor; import static co.paralleluniverse.actors.behaviors.RequestReplyHelper.call; import co.paralleluniverse.fibers.SuspendExecution; import java.util.List; import java.util.concurrent.TimeUnit; /** * An interface to a {@link SupervisorActor}. * * @author pron */ public class Supervisor extends Behavior { /** * If {@code actor} is known to be a {@link ServerActor}, creates a new {@link Server} interface to it. * Normally, you don't use this constructor, but the {@code Supervisor} instance returned by {@link SupervisorActor#spawn() }. * * @param actor a {@link ServerActor} */ public Supervisor(ActorRef<Object> actor) { super(actor); } /** * Adds a new child actor to the supervisor. If the child has not been started, it will be started by the supervisor. * This method does not block when called from within the Supervisor's context, so, in particular, it may be called by * an actor constructor, constructed by the supervisor. * * @param spec the {@link ChildSpec child's spec}. * @return the actor (possibly after it has been started by the supervisor). * @throws InterruptedException */ public final <T extends ActorRef<M>, M> T addChild(ChildSpec spec) throws SuspendExecution, InterruptedException { if (isInActor()) return (T) SupervisorActor.currentSupervisor().addChild(spec); return (T) call(this, new AddChildMessage(RequestReplyHelper.from(), null, spec)); } /** * Retrieves a child actor by its {@link ChildSpec#getId() id} * This method does not block when called from within the Supervisor's context, so, in particular, it may be called by * an actor constructor, constructed by the supervisor. * * @param id the child's {@link ChildSpec#getId() id} in the supervisor. * @return the child, if found; {@code null} if the child was not found * @throws SuspendExecution * @throws InterruptedException */ public final <T extends ActorRef<M>, M> T getChild(Object id) throws SuspendExecution, InterruptedException { if (isInActor()) return (T) SupervisorActor.currentSupervisor().getChild(id); return (T) call(this, new GetChildMessage(RequestReplyHelper.from(), null, id)); } /** * Retrieves the children actor references as an immutable list. * This method does not block when called from within the Supervisor's context, so, in particular, it may be called by * an actor constructor, constructed by the supervisor. * * @return the children {@link ActorRef}s. * @throws SuspendExecution * @throws InterruptedException */ public final List<? extends ActorRef<?>> getChildren() throws SuspendExecution, InterruptedException { if (isInActor()) return SupervisorActor.currentSupervisor().getChildren(); return (List) call(this, new GetChildrenMessage(RequestReplyHelper.from(), null)); } /** * Removes a child actor from the supervisor. * This method does not block when called from within the Supervisor's context, so, in particular, it may be called by * an actor constructor, constructed by the supervisor. * * @param id the child's {@link ChildSpec#getId() id} in the supervisor. * @param terminate whether or not the supervisor should terminate the actor * @return {@code true} if the actor has been successfully removed from the supervisor; {@code false} if the child was not found. * @throws InterruptedException */ public final boolean removeChild(Object id, boolean terminate) throws SuspendExecution, InterruptedException { if (isInActor()) return SupervisorActor.currentSupervisor().removeChild(id, terminate); return (Boolean) call(this, new RemoveChildMessage(RequestReplyHelper.from(), null, id, terminate)); } /** * Removes a child actor from the supervisor. * This method does not block when called from within the Supervisor's context, so, in particular, it may be called by * an actor constructor, constructed by the supervisor. * * @param actor the child actor * @param terminate whether or not the supervisor should terminate the actor * @return {@code true} if the actor has been successfully removed from the supervisor; {@code false} if the child was not found. * @throws InterruptedException */ public boolean removeChild(ActorRef<?> actor, boolean terminate) throws SuspendExecution, InterruptedException { if (isInActor()) return SupervisorActor.currentSupervisor().removeChild(actor, terminate); return (Boolean) call(this, new RemoveChildMessage(RequestReplyHelper.from(), null, actor, terminate)); } /** * Determines whether a child (supervised) actor should be restarted if the supervisor's {@link SupervisorActor.RestartStrategy restart strategy} * states that it should be restarted. */ public enum ChildMode { /** * The child actor should be restarted if it dies for whatever reason if the supervisor's {@link SupervisorActor.RestartStrategy restart strategy} * states that it should be restarted. */ PERMANENT, /** * The child actor should be restarted if it dies of unnatural causes (an exception) if the supervisor's {@link SupervisorActor.RestartStrategy restart strategy} * states that it should be restarted. */ TRANSIENT, /** * The child actor should never be restarted. */ TEMPORARY }; /** * Describes a child actor's configuration in a supervisor */ public static class ChildSpec { final String id; final ActorBuilder<?, ?> builder; final ChildMode mode; final int maxRestarts; final long duration; final TimeUnit unit; final long shutdownDeadline; /** * A new spec. * * @param id the child's (optional) identifier (name) * @param mode the child's {@link ChildMode mode}. * @param maxRestarts the maximum number of times the child actor is allowed to restart within the given {@code duration} before * the supervisor gives up and kills itself. * @param duration the duration in which the number of restarts is counted towards {@code maxRestarts}. * @param unit the {@link TimeUnit time unit} of the {@code duration} for {@code maxRestarts}. * @param shutdownDeadline the time in milliseconds the supervisor should wait for the child actor to terminate from the time it was requested to shutdown; * after the deadline expires, the child actor is terminated forcefully. * @param builder the child's {@link ActorBuilder builder} */ public ChildSpec(String id, ChildMode mode, int maxRestarts, long duration, TimeUnit unit, long shutdownDeadline, ActorBuilder<?, ?> builder) { this.id = id; this.builder = builder; this.mode = mode; this.maxRestarts = maxRestarts; this.duration = duration; this.unit = unit; this.shutdownDeadline = shutdownDeadline; } /** * A new spec. * This constructor takes an {@link ActorRef} to the actor rather than an {@link ActorBuilder}. If the {@link ActorRef} also implements * {@link ActorBuilder} it will be used to restart the actor. {@code ActorRef}s to local actors implement {@link ActorBuilder} using * {@code Actor}'s {@link Actor#reinstantiate() reinstantiate} method. * * @param id the child's (optional) identifier (name) * @param mode the child's {@link ChildMode mode}. * @param maxRestarts the maximum number of times the child actor is allowed to restart within the given {@code duration} before * the supervisor gives up and kills itself. * @param duration the duration in which the number of restarts is counted towards {@code maxRestarts}. * @param unit the {@link TimeUnit time unit} of the {@code duration} for {@code maxRestarts}. * @param shutdownDeadline the time in milliseconds the supervisor should wait for the child actor to terminate from the time it was requested to shutdown; * after the deadline expires, the child actor is terminated forcefully. * @param actor the child actor */ public ChildSpec(String id, ChildMode mode, int maxRestarts, long duration, TimeUnit unit, long shutdownDeadline, ActorRef<?> actor) { this(id, mode, maxRestarts, duration, unit, shutdownDeadline, LocalActor.toActorBuilder(actor)); } /** * The child's (optional) identifier (name) */ public String getId() { return id; } /** * The child's {@link ActorBuilder builder} */ public ActorBuilder<?, ?> getBuilder() { return builder; } /** * The child's {@link ChildMode mode}. */ public ChildMode getMode() { return mode; } /** * The maximum number of times the child actor is allowed to restart within a given {@link #getDuration() duration} before * the supervisor gives up and kills itself. */ public int getMaxRestarts() { return maxRestarts; } /** * The duration in which the number of restarts is counted towards the {@link #getMaxRestarts() max restarts}. */ public long getDuration() { return duration; } /** * The {@link TimeUnit time unit} of the {@link #getDuration() duration} for {@link #getMaxRestarts() max restarts}. */ public TimeUnit getDurationUnit() { return unit; } /** * The time in milliseconds the supervisor should wait for the child actor to terminate from the time it was requested to shutdown; * after the deadline expires, the child actor is terminated forcefully. */ public long getShutdownDeadline() { return shutdownDeadline; } @Override public String toString() { return "ChildSpec{" + "builder: " + builder + ", mode: " + mode + ", maxRestarts: " + maxRestarts + ", duration: " + duration + ", unit: " + unit + ", shutdownDeadline: " + shutdownDeadline + '}'; } } @Override public String toString() { return "Supervisor{" + super.toString() + "}"; } ///////// Messages static class AddChildMessage extends RequestMessage { final ChildSpec spec; public AddChildMessage(ActorRef from, Object id, ChildSpec info) { super(from, id); this.spec = info; } @Override protected String contentString() { return super.contentString() + " spec: " + spec; } } static class GetChildMessage extends RequestMessage { final Object name; public GetChildMessage(ActorRef from, Object id, Object name) { super(from, id); this.name = name; } @Override protected String contentString() { return super.contentString() + " name: " + name; } } static class GetChildrenMessage extends RequestMessage { public GetChildrenMessage(ActorRef from, Object id) { super(from, id); } @Override protected String contentString() { return super.contentString(); } } static class RemoveChildMessage extends RequestMessage { final Object name; final boolean terminate; public RemoveChildMessage(ActorRef from, Object id, Object name, boolean terminate) { super(from, id); this.name = name; this.terminate = terminate; } @Override protected String contentString() { return super.contentString() + " name: " + name + " terminate: " + terminate; } } }