package com.gravspace.bases;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.PropertyUtilsBean;
import scala.concurrent.Await;
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.dispatch.OnSuccess;
import com.gravspace.abstractions.IWidget;
import com.gravspace.handlers.SetterFailure;
import com.gravspace.messages.Null;
import com.gravspace.util.Layers;
public abstract class WidgetBase extends ConcurrantCallable implements IWidget{
public WidgetBase(Map<Layers, ActorRef> routers,
ActorRef coordinatingActor, UntypedActorContext actorContext) {
super(routers, coordinatingActor, actorContext);
}
@SuppressWarnings("unchecked")
public void set(final String field, Future<?> wrapper){
set(field, wrapper, null);
}
@SuppressWarnings("unchecked")
public void set(final String field, Future<?> source, final SetterFailure onFailure){
final Promise<Object> wait = delayUntilComplete();
set(field, source, wait, onFailure);
setIfAlreadyComplete(field, source, wait, onFailure);
}
@SuppressWarnings("unchecked")
public void add(final String field, Future<?> wrapper){
add(field, wrapper, null);
}
@SuppressWarnings("unchecked")
public void add(final String field, Future<?> source, final SetterFailure onFailure){
final Promise<Object> wait = delayUntilComplete();
add(field, source, wait, onFailure);
addIfAlreadyComplete(field, source, wait, onFailure);
}
private void setIfAlreadyComplete(final String field, Future<?> source,
final Promise<Object> wait, SetterFailure onFailure) {
if (source.isCompleted() && !wait.isCompleted()){
Object returnValue;
try {
returnValue = Await.result(source, Duration.create(0, TimeUnit.SECONDS));
if (returnValue instanceof Null){
BeanUtils.setProperty(getThis(), field, null);
} else {
BeanUtils.setProperty(getThis(), field, returnValue);
}
} catch (Exception exception) {
handleCallbackException(field, onFailure, exception, wait);
}
try {
wait.success(null);
} catch (IllegalStateException e){}
}
}
private void addIfAlreadyComplete(final String field, Future<?> source,
final Promise<Object> wait, SetterFailure onFailure) {
if (source.isCompleted() && !wait.isCompleted()){
Object returnValue;
try {
returnValue = Await.result(source, Duration.create(0, TimeUnit.SECONDS));
if (!(returnValue instanceof Null)){
((List)PropertyUtils.getProperty(getThis(), field)).add(returnValue);
}
} catch (Exception exception) {
handleCallbackException(field, onFailure, exception, wait);
}
try {
wait.success(null);
} catch (IllegalStateException e){}
}
}
public WidgetBase getThis(){
return this;
}
public Promise<Object> delayUntilComplete(){
Promise<Object> waiter = Futures.promise();
Future<Object> setterFuture = waiter.future();
addTaskToMonitoredList(setterFuture);
return waiter;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void add(final String field, Future<?> source, final Promise<Object> waiter, final SetterFailure onFailure){
source.onComplete(new OnComplete(){
@Override
public void onComplete(Throwable exception, Object returnValue)
throws Throwable {
if (exception == null){
try {
if (!(returnValue instanceof Null)){
((List)PropertyUtils.getProperty(getThis(), field)).add(returnValue);
}
} catch (IllegalAccessException | InvocationTargetException e){
getLogger().error(exception,
String.format("Exception in add callback on widget / page [%s] field [%s]",
getThis().getClass().getCanonicalName(), field));
exception = e;
}
}
if (exception != null){
handleCallbackException(field, onFailure, exception, waiter);
} else {
waiter.success(null);
}
}
}, this.getActorContext().dispatcher());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void set(final String field, Future<?> source, final Promise<Object> waiter, final SetterFailure onFailure){
source.onComplete(new OnComplete(){
@Override
public void onComplete(Throwable exception, Object returnValue)
throws Throwable {
if (exception == null){
try {
if (returnValue instanceof Null){
BeanUtils.setProperty(getThis(), field, null);
} else {
BeanUtils.setProperty(getThis(), field, returnValue);
}
} catch (IllegalAccessException | InvocationTargetException e){
getLogger().error(exception,
String.format("Exception in set callback on widget / page [%s] field [%s]",
getThis().getClass().getCanonicalName(), field));
exception = e;
}
}
if (exception != null){
handleCallbackException(field, onFailure, exception, waiter);
} else {
waiter.success(null);
}
}
}, this.getActorContext().dispatcher());
}
private void handleCallbackException(final String field,
final SetterFailure onFailure, Throwable exception, Promise<Object> promise) {
if (onFailure != null) {
onFailure.handleFailure(getThis(), field, exception, promise);
} else {
getLogger().error(exception, "Failed without failure handler, falling back.");
}
if (!promise.isCompleted()){
promise.failure(exception);
}
}
public abstract void collect();
public abstract void process();
public abstract Future<String> render() throws Exception;
public abstract void initialise(Object... args);
}