package org.jactr.core.module.asynch.delegate;
/*
* default logging
*/
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.module.AbstractModule;
import org.jactr.core.module.asynch.IAsynchronousModule;
import org.jactr.core.production.condition.ChunkPattern;
import org.jactr.core.production.request.IRequest;
import org.jactr.core.queue.ITimedEvent;
import org.jactr.core.queue.timedevents.AbstractTimedEvent;
import org.jactr.core.queue.timedevents.BlockingTimedEvent;
/**
* abstract asynch delegate. This will manage the {@link BlockingTimedEvent}
* such that the synchronization will occurr minimumProcessingTime after the
* start of the request.
*
* @author harrison
* @param <M>
* @param <R>
*/
public abstract class AbstractAsynchronousModuleDelegate<M extends IAsynchronousModule, R>
implements IAsynchronousModuleDelegate<M, R>
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(AbstractAsynchronousModuleDelegate.class);
private M _module;
private double _minimumProcessingTime;
private R _errorResult;
public AbstractAsynchronousModuleDelegate(M module,
double minimumProcessingTime, R cantProcessResult)
{
_module = module;
_minimumProcessingTime = minimumProcessingTime;
_errorResult = cantProcessResult;
}
public M getModule()
{
return _module;
}
public double getMinimumProcessingTime()
{
return _minimumProcessingTime;
}
public Future<R> process(final IRequest request, double processTime,
final Object... parameters)
{
double now = processTime;
double blockAt = now + _minimumProcessingTime;
boolean canProcess = shouldProcess(request, parameters);
if (canProcess)
{
final BlockingTimedEvent synchronizationBlock = getModule()
.synchronizedTimedEvent(now, blockAt);
blockingTimedEventCreated(synchronizationBlock);
Future<R> rtn = AbstractModule.delayedFuture(new Callable<R>() {
public R call() throws Exception
{
R result = _errorResult;
try
{
result = processInternal(request, parameters);
}
catch (Exception e)
{
/**
* Error : error
*/
LOGGER.error("Failed to process pattern request ", e);
}
double startTime = synchronizationBlock.getStartTime();
double harvestAt = computeHarvestTime(request, result, startTime,
parameters);
if (LOGGER.isDebugEnabled())
LOGGER.debug(getModule() + " requested, started at " + startTime
+ " will harvest " + result + " at " + harvestAt);
processInternalCompleted(request, result, startTime, harvestAt);
ITimedEvent harvest = createHarvestTimedEvent(startTime, harvestAt, request, result, parameters);
enqueue(harvest);
/*
* processing is complete, release the block
*/
if (!synchronizationBlock.hasAborted()) synchronizationBlock.abort();
return result;
}
}, getModule().getExecutor());
return rtn;
}
else
/*
* nothing can be done, return right now
*/
return AbstractModule.immediateReturn(_errorResult);
}
protected ITimedEvent createHarvestTimedEvent(double start, double end, final IRequest request, final R result, final Object ... parameters )
{
return new AbstractTimedEvent(start, end) {
@Override
public void fire(double currentTime)
{
super.fire(currentTime);
if (LOGGER.isDebugEnabled())
LOGGER.debug(getModule() + " harvesting " + result
+ " at " + currentTime);
finalizeProcessing(request, result, parameters);
}
};
}
/**
* called on the asynch thread on the creation of the blocking timed event.
* this is useful if you need to snag a reference to it.
*
* @param bte
*/
protected void blockingTimedEventCreated(BlockingTimedEvent bte)
{
}
/**
* called on the asynch thread after processInternal has completed. primarily
* used to output some logging info..
*
* @param request
* @param result
* @param startTime
* @param harvestAt
*/
protected void processInternalCompleted(IRequest request, R result,
double startTime, double harvestAt)
{
}
/**
* method to queue the timed event. By default
* getModule().getModel().getTimedEventQueue().enqueue()
*
* @param timedEvent
*/
protected void enqueue(ITimedEvent timedEvent)
{
getModule().getModel().getTimedEventQueue().enqueue(timedEvent);
}
/**
* Called on the initiating thread (i.e. model thread), this checks the module
* and the buffers to be sure that the processing can proceed. If true, it
* will queue {@link #processInternal(ChunkPattern)} on the asynchronous
* executor and then execute {@link #finalizeProcessing(ChunkPattern, Object)}
* on the model thread once all is completed
*
* @param request
* @return
*/
abstract protected boolean shouldProcess(IRequest request,
Object... parameters);
/**
* called on the asynchronous thread, this does the actual processing,
* returning some result. This method should handle all its own exceptions.
*
* @param request
* @return
*/
abstract protected R processInternal(IRequest request,
Object... parameters);
/**
* called on the model thread, this handles the clean up
*
* @param request
* @param result
*/
abstract protected void finalizeProcessing(IRequest request, R result,
Object... parameters);
/**
* returns the time at which this result should be made available. In other
* words, after {@link #processInternal(ChunkPattern)} finishes, a new
* {@link ITimedEvent} is queued that will fire at the returned time. Upon
* firing it will call {@link #finalizeProcessing(ChunkPattern, Object)}.
* This is called on the asynch thread
*
* @param request
* @param result
* @param startTime
* @return
*/
abstract protected double computeHarvestTime(IRequest request, R result,
double startTime, Object... parameters);
}