/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2014, 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.LifecycleMessage;
import co.paralleluniverse.actors.MailboxConfig;
import co.paralleluniverse.actors.ShutdownMessage;
import co.paralleluniverse.common.util.Exceptions;
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 org.slf4j.Logger;
/**
* A general behavior-actor class, extended by all behaviors. Behaviors are actor templates encapsulating common useful actor patterns.
* This provides standard, sane, actor lifecycle methods, as well as other useful services (like a logger object).
*
* @author pron
*/
public abstract class BehaviorActor extends Actor<Object, Void> implements java.io.Serializable {
private Initializer initializer;
private boolean run;
/**
* Creates a new behavior 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.
*/
protected BehaviorActor(String name, Initializer initializer, Strand strand, MailboxConfig mailboxConfig) {
super(strand, name, mailboxConfig);
this.initializer = ActorLoader.getReplacementFor(initializer);
this.run = true;
}
//<editor-fold defaultstate="collapsed" desc="Behavior boilerplate">
/////////// Behavior boilerplate ///////////////////////////////////
@Override
protected Behavior makeRef(ActorRef<Object> ref) {
return new Behavior(ref);
}
@Override
public Behavior ref() {
return (Behavior) super.ref();
}
@Override
protected Behavior self() {
return ref();
}
@Override
public Behavior spawn(StrandFactory sf) {
return (Behavior) super.spawn(sf);
}
@Override
public Behavior spawn(FiberFactory ff) {
return (Behavior) super.spawn(ff);
}
@Override
public Behavior spawn() {
return (Behavior) super.spawn();
}
@Override
public Behavior spawnThread() {
return (Behavior) super.spawnThread();
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="Constructors">
/////////// Constructors ///////////////////////////////////
/**
* Creates a new behavior 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 BehaviorActor(String name, Initializer initializer, MailboxConfig mailboxConfig) {
this(name, initializer, null, mailboxConfig);
}
/**
* Creates a new behavior 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 BehaviorActor(String name, Initializer initializer) {
this(name, initializer, null, null);
}
/**
* Creates a new behavior 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 BehaviorActor(Initializer initializer, MailboxConfig mailboxConfig) {
this(null, initializer, null, mailboxConfig);
}
/**
* Creates a new behavior actor.
*
* @param initializer an optional delegate object that will be run upon actor initialization and termination. May be {@code null}.
*/
public BehaviorActor(Initializer initializer) {
this(null, initializer, null, null);
}
/**
* Creates a new behavior actor.
*
* @param name the actor name (may be {@code null}).
* @param mailboxConfig this actor's mailbox settings.
*/
public BehaviorActor(String name, MailboxConfig mailboxConfig) {
this(name, null, null, mailboxConfig);
}
/**
* Creates a new behavior actor.
*
* @param name the actor name (may be {@code null}).
*/
public BehaviorActor(String name) {
this(name, null, null, null);
}
/**
* Creates a new behavior actor.
*
* @param mailboxConfig this actor's mailbox settings.
*/
public BehaviorActor(MailboxConfig mailboxConfig) {
this(null, null, null, mailboxConfig);
}
/**
* Creates a new behavior actor.
*/
public BehaviorActor() {
this(null, null, null, null);
}
//</editor-fold>
/**
* Causes this actor to shut down.
*/
protected void shutdown() {
verifyInActor();
log().debug("Shutdown requested.");
run = false;
getStrand().interrupt();
}
/**
* The {@link Initializer initializer} passed at construction which performs initialization and termination.
*/
protected Initializer getInitializer() {
return initializer;
}
/**
* This method is called by the {@link BehaviorActor} at the beginning of {@link #doRun()}.
* By default, this method calls {@link #init()}.
*/
protected void onStart() throws InterruptedException, SuspendExecution {
init();
}
/**
* This method is called by the {@link BehaviorActor} at the end of {@link #doRun()}.
* By default, this method calls {@link #terminate(Throwable) terminate()}.
*/
protected void onTerminate(Throwable cause) throws InterruptedException, SuspendExecution {
log().info("{} shutting down.", this);
terminate(cause);
}
/**
* Called by {@link #onStart() onStart} to initialize the actor. By default, this method calls {@link #getInitializer() initializer}.{@link Initializer#init() init()}
* if the initializer in non-null; otherwise it does nothing.
*/
protected void init() throws InterruptedException, SuspendExecution {
if (initializer != null)
initializer.init();
}
/**
* Called by {@link #onTerminate(Throwable) onTerminate} to terminate the actor. By default, this method calls {@link #getInitializer() initializer}.{@link Initializer#terminate(java.lang.Throwable) terminate}
* if the initializer in non-null; otherwise it does nothing.
*/
protected void terminate(Throwable cause) throws SuspendExecution {
if (initializer != null)
initializer.terminate(cause);
}
/**
* The {@link Logger} object associated with this actor.
*/
public abstract Logger log();
/**
* Called by {@link #doRun()} as the body of the logic. By default, this implementation runs code similar to:
* <pre> {@code
* while (isRunning())
* handleMessage(receive());
* }</pre>
*/
protected void behavior() throws InterruptedException, SuspendExecution {
while (isRunning()) {
final Object m1 = receive();
handleMessage(m1);
}
}
/**
* Called by the default {@link #behavior()} method to handle each incoming message.
* By default, this method does nothing.
*
* @param message the message received by the actor.
*/
protected void handleMessage(Object message) throws InterruptedException, SuspendExecution {
}
@Override
protected void checkCodeSwap() throws SuspendExecution {
verifyInActor();
Initializer _initializer = ActorLoader.getReplacementFor(initializer);
if (_initializer != initializer)
log().info("Upgraded behavior implementation: {}", _initializer);
this.initializer = _initializer;
super.checkCodeSwap();
}
public boolean isRunning() {
return run;
}
/**
* {@inheritDoc}
* <p/>
* This implementation calls {@link #onStart()} when it begins, {@link #behavior()} for the body, and
* {@link #onTerminate(Throwable) onTerminate()} upon termination. The implementation runs code similar to the following:
* <pre> {@code
* try {
* onStart();
* behavior();
* } catch (InterruptedException e) {
* if (shutdownCalled) {
* onTerminate(null);
* return null;
* } else {
* onTerminate(e);
* throw e;
* }
* } catch (Exception e) {
* log().info("Exception!", e);
* onTerminate(e);
* throw Exceptions.rethrow(e);
* }
* onTerminate(null);
* }</pre>
*/
@Override
protected Void doRun() throws InterruptedException, SuspendExecution {
try {
onStart();
behavior();
} catch (InterruptedException e) {
if (run == false) {
onTerminate(null);
return null;
} else {
onTerminate(e);
throw e;
}
} catch (Exception e) {
log().info("Exception!", e);
onTerminate(e);
throw Exceptions.rethrow(e);
}
onTerminate(null);
return null;
}
/**
* {@inheritDoc}
* <p/>
* This implementation respects {@link ShutdownMessage} and, upon receiving it, calls {@link #shutdown() shutdown()}.
*/
@Override
protected Object handleLifecycleMessage(LifecycleMessage m) {
if (m instanceof ShutdownMessage) {
shutdown();
return null;
} else
return super.handleLifecycleMessage(m);
}
}