/*
* 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.ActorRef;
import co.paralleluniverse.actors.LocalActor;
import static co.paralleluniverse.actors.behaviors.RequestReplyHelper.from;
import static co.paralleluniverse.actors.behaviors.RequestReplyHelper.makeId;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.Timeout;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* An interface to a {@link ServerActor}.
*
* @author pron
*/
public class Server<CallMessage, V, CastMessage> extends Behavior {
private volatile long defaultTimeoutNanos = -1;
/**
* 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 Server} instance returned by {@link ServerActor#spawn() }.
*
* @param actor a {@link ServerActor}
*/
public Server(ActorRef<Object> actor) {
super(actor);
}
/**
* Sets a default timeout for non-timed {@link #call(Object) call}s on this server reference.
* Non-timed calls that take longer than the default timeout, will throw a {@link TimeoutException}
* wrapped in a {@link RuntimeException}. Timed calls (those that take a timeout parameter) will not be affected.
* <p/>
* This method only affects calls made through this particular server actor reference and not calls to the same server actor
* through other references.
* <p/>
* <b>This method has nothing to do with {@link ServerActor#setTimeout(long, TimeUnit) ServerActor.setTimeout}</b>
*
* @param timeout the timeout duration
* @param unit the time unit of the timeout, or {@code null} to unset.
*/
public void setDefaultTimeout(long timeout, TimeUnit unit) {
if (unit == null)
defaultTimeoutNanos = -1;
else
defaultTimeoutNanos = unit.toNanos(timeout);
}
/**
* Sends a synchronous request to the actor, and awaits a response.
* This method will wait indefinitely for the actor to respond unless a default timeout has been set for this
* server reference with {@link #setDefaultTimeout(long, TimeUnit) setDefaultTimeout}.
* <p/>
* This method may be safely called by actors and non-actor strands alike.
*
* @param m the request
* @return the value sent as a response from the actor
* @throws RuntimeException if the actor encountered an error while processing the request
*/
public final V call(CallMessage m) throws InterruptedException, SuspendExecution {
final long timeout = defaultTimeoutNanos;
try {
if (timeout > 0)
return call(m, timeout, TimeUnit.NANOSECONDS);
else
return call(m, 0, null);
} catch (TimeoutException ex) {
if (timeout >= 0)
throw new RuntimeException(ex);
else
throw new AssertionError(ex);
}
}
/**
* Sends a synchronous request to the actor, and awaits a response, but no longer than the given timeout.
* <p/>
* This method may be safely called by actors and non-actor strands alike.
*
* @param m the request
* @param timeout the maximum duration to wait for a response.
* @param unit the time unit of the timeout
* @return the value sent as a response from the actor
* @throws RuntimeException if the actor encountered an error while processing the request
* @throws TimeoutException if the timeout expires before a response has been received.
*/
public final V call(CallMessage m, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException, SuspendExecution {
final V res = RequestReplyHelper.call(this, new ServerRequest<V>(from(), null, MessageType.CALL, m), timeout, unit);
return res;
}
/**
* Sends a synchronous request to the actor, and awaits a response, but no longer than the given timeout.
* <p/>
* This method may be safely called by actors and non-actor strands alike.
*
* @param m the request
* @param timeout the method will not block for longer than the amount remaining in the {@link Timeout}
* @return the value sent as a response from the actor
* @throws RuntimeException if the actor encountered an error while processing the request
* @throws TimeoutException if the timeout expires before a response has been received.
*/
public final V call(CallMessage m, Timeout timeout) throws TimeoutException, InterruptedException, SuspendExecution {
return call(m, timeout.nanosLeft(), TimeUnit.NANOSECONDS);
}
/**
* Sends an asynchronous request to the actor and returns immediately (may block until there's room available in the actor's mailbox).
*
* @param m the request
*/
public final void cast(CastMessage m) throws SuspendExecution {
this.send(new ServerRequest(LocalActor.self(), makeId(), MessageType.CAST, m));
}
// public static void cast(ActorRef server, Object m) throws SuspendExecution {
// server.send(new ServerRequest(ActorRef.self(), makeId(), MessageType.CAST, m));
// }
@Override
public String toString() {
return "Server{" + super.toString() + "}";
}
enum MessageType {
CALL, CAST
};
static class ServerRequest<T> extends RequestMessage<T> {
private final MessageType type;
private final Object message;
public ServerRequest(ActorRef sender, Object id, MessageType type, Object message) {
super(sender, id);
this.type = type;
this.message = message;
}
public MessageType getType() {
return type;
}
public Object getMessage() {
return message;
}
}
}