package com.gravspace.bases;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import scala.concurrent.Future;
import scala.concurrent.Promise;
import scala.concurrent.duration.Duration;
import akka.actor.ActorRef;
import akka.actor.UntypedActorContext;
import akka.dispatch.Futures;
import akka.dispatch.OnComplete;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.pattern.Patterns;
import akka.util.Timeout;
import com.gravspace.messages.CalculationMessage;
import com.gravspace.messages.ComponentMessage;
import com.gravspace.messages.PersistanceMessage;
import com.gravspace.messages.RenderMessage;
import com.gravspace.messages.TaskMessage;
import com.gravspace.util.Layers;
public class ConcurrantCallable {
private LoggingAdapter log = null;
protected ActorRef coordinatingActor;
protected UntypedActorContext actorContext;
protected List<Future<Object>> taskList;
protected Set<Promise<Object>> awaitListeners;
protected Map<Layers, ActorRef> routers;
public ConcurrantCallable(final Map<Layers, ActorRef> routers, final ActorRef coordinatingActor, final UntypedActorContext actorContext){
this.coordinatingActor = coordinatingActor;
setActorContext(actorContext);
this.routers = routers;
taskList = Collections.synchronizedList(new ArrayList<Future<Object>>());
awaitListeners = new CopyOnWriteArraySet<Promise<Object>>();
}
public LoggingAdapter getLogger(){
if (log == null){
log = Logging.getLogger(getActorContext().system(), this);
}
return log;
}
public Future<Object> await(){
final Promise<Object> waiter = Futures.promise();
if (taskList.isEmpty()){
waiter.success(null);
}
awaitListeners.add(waiter);
return waiter.future();
}
public Future<Object> ask(RenderMessage message){
// getLogger().info("Asking");
Timeout timeout = new Timeout(Duration.create(1, "minute"));
final Future<Object> future = Patterns.ask(routers.get(Layers.RENDERER), message, timeout);
// getLogger().info("Asking"+future.toString());
addTaskToMonitoredList(future);
return future;
}
protected void addTaskToMonitoredList(final Future<Object> future) {
taskList.add(future);
monitorForCompletion(future);
}
public Future<Object> ask(ComponentMessage message){
// getLogger().info("Asking");
Timeout timeout = new Timeout(Duration.create(1, "minute"));
final Future<Object> future = Patterns.ask(routers.get(Layers.COMPONENT), message, timeout);
// getLogger().info("Asking"+future.toString());
addTaskToMonitoredList(future);
return future;
}
public Future<Object> ask(PersistanceMessage message){
// getLogger().info("Asking");
Timeout timeout = new Timeout(Duration.create(1, "minute"));
final Future<Object> future = Patterns.ask(routers.get(Layers.DATA_ACCESS), message, timeout);
// getLogger().info("Asking"+future.toString());
addTaskToMonitoredList(future);
return future;
}
public Future<Object> ask(CalculationMessage message){
// getLogger().info("Asking");
Timeout timeout = new Timeout(Duration.create(1, "minute"));
final Future<Object> future = Patterns.ask(routers.get(Layers.CALCULATION), message, timeout);
// getLogger().info("Asking"+future.toString());
addTaskToMonitoredList(future);
return future;
}
public void call(TaskMessage message){
// getLogger().info("Calling");
routers.get(Layers.TASK).tell(message, ActorRef.noSender());
}
protected void monitorForCompletion(final Future<Object> future) {
future.onComplete(new OnComplete<Object>() {
@Override
public void onComplete(Throwable exception, Object response)
throws Throwable {
if (exception != null){
notifyWaitersOfFailure(exception);
} else {
taskList.remove(future);
notifyWaiters(future);
}
}
}, getActorContext().dispatcher());
}
protected void notifyWaitersOfFailure(Throwable exception) {
for (Promise<Object> waiter: awaitListeners){
if (!waiter.isCompleted()){
waiter.failure(exception);
}
}
taskList.clear();
}
protected void notifyWaiters(Future<Object> future) {
if (taskList.isEmpty()){
for (Promise<Object> waiter: awaitListeners){
if (!waiter.isCompleted()){
waiter.success(future);
}
}
}
}
public ActorRef getCoordinatingActor() {
return coordinatingActor;
}
public UntypedActorContext getActorContext() {
return actorContext;
}
public void setActorContext(UntypedActorContext context) {
actorContext = context;
}
}