/*
* Copyright (C) 2015 Actor LLC. <https://actor.im>
*/
package im.actor.runtime.actors;
import java.util.ArrayList;
import java.util.HashMap;
import im.actor.runtime.actors.dispatch.Mailbox;
import im.actor.runtime.actors.messages.DeadLetter;
import im.actor.runtime.threading.SimpleDispatcher;
import im.actor.runtime.threading.ThreadDispatcher;
/**
* Actor object
*/
public class Actor {
private final SimpleDispatcher dispatcher = runnable -> self().post(runnable);
private Scheduler scheduler;
private String path;
private ActorContext context;
private Mailbox mailbox;
private ArrayList<Receiver> receivers;
private HashMap<Integer, ArrayList<StashedMessage>> stashed;
public Actor() {
}
/**
* <p>INTERNAL API</p>
*/
public SimpleDispatcher getDispatcher() {
return dispatcher;
}
/**
* <p>INTERNAL API</p>
* Initialization of actor
*
* @param path path of actor
* @param context context of actor
* @param mailbox mailbox of actor
*/
public final void initActor(String path, ActorContext context, Mailbox mailbox) {
this.path = path;
this.context = context;
this.mailbox = mailbox;
}
/**
* <p>INTERNAL API</p>
* Handling of a message in Actor
*
* @param message message
*/
public final void handleMessage(Object message, ActorRef sender) {
intHandle(message, sender);
}
public void stash() {
stash(0);
}
public void stash(int index) {
if (stashed == null) {
stashed = new HashMap<>();
}
ArrayList<StashedMessage> stashedMessages = stashed.get(index);
if (stashedMessages == null) {
stashedMessages = new ArrayList<>();
stashed.put(index, stashedMessages);
}
stashedMessages.add(new StashedMessage(context.message(), context.sender()));
}
public void unstashAll() {
unstashAll(0);
}
public void unstashAll(int index) {
if (stashed == null) {
return;
}
ArrayList<StashedMessage> stashedMessages = stashed.get(index);
if (stashedMessages == null || stashedMessages.size() == 0) {
return;
}
StashedMessage stashedMessage;
for (int i = stashedMessages.size() - 1; i >= 0; i--) {
stashedMessage = stashedMessages.get(i);
self().sendFirst(stashedMessage.getMessage(), stashedMessage.getSender());
}
stashedMessages.clear();
}
public void become(Receiver receiver) {
if (receivers == null) {
receivers = new ArrayList<>();
}
receivers.add(receiver);
}
public void unbecome() {
if (receivers == null) {
receivers = new ArrayList<>();
}
receivers.remove(receivers.size() - 1);
}
private void intHandle(Object message, ActorRef sender) {
ThreadDispatcher.pushDispatcher(dispatcher);
context.setSender(sender);
context.setMessage(message);
try {
if (receivers != null && receivers.size() > 0) {
receivers.get(receivers.size() - 1).onReceive(message);
return;
}
if (message instanceof Runnable) {
((Runnable) message).run();
return;
}
onReceive(message);
} finally {
ThreadDispatcher.popDispatcher();
context.setSender(null);
context.setMessage(null);
}
}
/**
* Actor System
*
* @return Actor System
*/
public final ActorSystem system() {
return context.getSystem();
}
/**
* Self actor reference
*
* @return self reference
*/
public final ActorRef self() {
return context.getSelf();
}
// /**
// * Actor context
// *
// * @return context
// */
// protected final ActorContext context() {
// return context;
// }
/**
* Sender of last received message
*
* @return sender's ActorRef
*/
public final ActorRef sender() {
return context.sender();
}
/**
* Actor path
*
* @return path
*/
protected final String getPath() {
return path;
}
/**
* Actor mailbox
*
* @return mailbox
*/
public final Mailbox getMailbox() {
return mailbox;
}
/**
* Called before first message receiving
*/
public void preStart() {
}
/**
* Receiving of message
*
* @param message message
*/
public void onReceive(Object message) {
drop(message);
}
/**
* Called after actor shutdown
*/
public void postStop() {
}
/**
* Reply message to sender of last message
*
* @param message reply message
*/
public void reply(Object message) {
if (context.sender() != null) {
context.sender().send(message, self());
}
}
/**
* Dropping of message
*
* @param message message for dropping
*/
public void drop(Object message) {
if (system().getTraceInterface() != null) {
system().getTraceInterface().onDrop(sender(), message, this);
}
reply(new DeadLetter(message));
}
public void forward(ActorRef dest) {
dest.send(context.message(), context.sender());
}
public void halt(String message) {
halt(message, null);
}
public void halt(String message, Exception e) {
throw new ActorHalterException(message, e);
}
// public <T> Promise<T> ask(final ActorRef dest, final AskMessage<T> msg) {
// return new Promise<T>(new PromiseFunc<T>() {
// @Override
// public void exec(PromiseResolver<T> executor) {
// dest.send(new AskIntRequest(msg, executor));
// }
// });
// }
//
// public void ask(ActorRef dest, Object message) {
// ask(dest, message, null);
// }
//
// public void ask(final ActorRef dest, final Object message, final AskCallback callback) {
// new Promise<>(new PromiseFunc<Object>() {
// @Override
// public void exec(@NonNull final PromiseResolver<Object> executor) {
// become(new Receiver() {
// @Override
// public void onReceive(Object message) {
// if (message instanceof PromiseDispatch) {
// PromiseDispatch dispatch = (PromiseDispatch) message;
// if (dispatch.getPromise() == executor.getPromise()) {
// dispatch.run();
// } else {
// stash();
// }
// } else {
// stash();
// }
// }
// });
// dest.send(new AskIntRequest(message, executor));
// }
// }).then(new Consumer<Object>() {
// @Override
// public void apply(Object o) {
// unbecome();
// unstashAll();
//
// if (callback != null) {
// callback.onResult(o);
// }
// }
// }).failure(new Consumer<Exception>() {
// @Override
// public void apply(Exception e) {
// unbecome();
// unstashAll();
//
// if (callback != null) {
// callback.onError(e);
// }
// }
// }).done(self());
// }
public ActorCancellable schedule(final Object obj, long delay) {
if (scheduler == null) {
scheduler = new Scheduler(self());
}
if (obj instanceof Runnable) {
return scheduler.schedule((Runnable) obj, delay);
} else {
return scheduler.schedule(() -> handleMessage(obj, self()), delay);
}
}
}