/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.lang.op;
import java.util.concurrent.locks.ReentrantLock;
import divconq.hub.Hub;
import divconq.lang.TimeoutPlan;
import divconq.scheduler.ISchedule;
import divconq.work.IWork;
import divconq.work.Task;
import divconq.work.TaskRun;
/**
* Provides the same function support as @see OperationResult, however used with
* callbacks when the method called is asynchronous.
*
* @author Andy
*
*/
abstract public class OperationCallback extends OperationResult {
protected boolean called = false;
protected ISchedule timeout = null;
protected ReentrantLock oplock = new ReentrantLock();
public OperationCallback() {
super();
}
public OperationCallback(TimeoutPlan plan) {
this();
Task timeouttask = new Task()
.withSubContext()
.withWork(new IWork() {
@Override
public void run(TaskRun task) {
OperationCallback.this.abandon();
task.complete();
}
});
this.timeout = Hub.instance.getScheduler().runIn(timeouttask, plan.getSeconds());
}
public OperationCallback(OperationContext ctx) {
super(ctx);
}
public OperationCallback(OperationContext ctx, TimeoutPlan plan) {
this(ctx);
Task timeouttask = new Task()
.withContext(ctx)
.withWork(new IWork() {
@Override
public void run(TaskRun task) {
OperationCallback.this.abandon();
task.complete();
}
});
this.timeout = Hub.instance.getScheduler().runIn(timeouttask, plan.getSeconds());
}
public void resetCalledFlag() {
this.called = false;
}
// override if need to do something on timeout/giveup on operation
// return true if timeout occurred, false if already completed
public boolean abandon() {
// courtesy only, no need to lock if we do know called is true, real called check below
if (this.called)
return false;
this.oplock.lock();
try {
if (this.called)
return false;
this.errorTr(218, this.opcontext.freezeToSafeRecord());
}
finally {
this.oplock.unlock();
}
this.complete();
return true;
}
public void useContext() {
OperationContext.set(this.opcontext);
}
public void complete() {
// courtesy only, no need to look if we do know called is true, real called check below
if (this.called)
return;
this.oplock.lock();
try {
// check for race condition
if (this.called)
return;
this.called = true;
}
finally {
this.oplock.unlock();
}
// be sure we restore the context
OperationContext ctx = OperationContext.get();
try {
OperationContext.set(this.opcontext);
if (this.timeout != null)
this.timeout.cancel();
OperationContext.set(this.opcontext);
this.markEnd();
this.callback();
// TODO review, this may not be useful
//this.opcontext.fireEvent(OperationEvents.COMPLETED, null);
}
finally {
OperationContext.set(ctx);
}
}
abstract public void callback();
}