package org.jactr.core.module.retrieval.buffer;
/*
* default logging
*/
import java.util.Collection;
import java.util.concurrent.Future;
import javolution.util.FastList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.buffer.IActivationBuffer;
import org.jactr.core.buffer.delegate.AsynchronousRequestDelegate;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunktype.IChunkType;
import org.jactr.core.chunktype.ISymbolicChunkType;
import org.jactr.core.chunktype.IllegalChunkTypeStateException;
import org.jactr.core.logging.Logger;
import org.jactr.core.model.IModel;
import org.jactr.core.module.declarative.four.IDeclarativeModule4;
import org.jactr.core.module.declarative.search.filter.ActivationPolicy;
import org.jactr.core.module.retrieval.IRetrievalModule;
import org.jactr.core.module.retrieval.six.DefaultRetrievalModule6;
import org.jactr.core.production.request.ChunkRequest;
import org.jactr.core.production.request.ChunkTypeRequest;
import org.jactr.core.production.request.IRequest;
import org.jactr.core.queue.ITimedEvent;
import org.jactr.core.queue.timedevents.DelayedBufferInsertionTimedEvent;
import org.jactr.core.slot.ISlot;
public class RetrievalRequestDelegate extends AsynchronousRequestDelegate
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(RetrievalRequestDelegate.class);
private IRetrievalModule _retrievalModule;
private boolean _includeNullValues;
static public double getThreshold(IRetrievalModule module,
Collection<? extends ISlot> slots)
{
double threshold = module.getRetrievalThreshold();
for (ISlot slot : slots)
if (slot.getName().equalsIgnoreCase(
DefaultRetrievalModule6.RETRIEVAL_THRESHOLD_SLOT))
try
{
// resolve the slot value...
double value = ((Number) slot.getValue()).doubleValue();
threshold = value;
}
catch (Exception e)
{
if (LOGGER.isWarnEnabled())
LOGGER.warn(String.format(
"Failed to get threshold from %s, using default %.2f",
slot.getValue(), threshold));
}
return threshold;
}
static public ActivationPolicy getActivationPolicy(String slotName,
Collection<? extends ISlot> slots)
{
ActivationPolicy rtn = ActivationPolicy.SUMMATION;
for (ISlot slot : slots)
if (slot.getName().equalsIgnoreCase(slotName)) try
{
// resolve the slot value... for now, string?
String value = slot.getValue().toString();
rtn = ActivationPolicy.valueOf(value.toUpperCase());
}
catch (Exception e)
{
if (LOGGER.isWarnEnabled())
LOGGER.warn(String.format(
"Failed to derive activationPolicy from %s, using summation",
slot.getValue()));
}
return rtn;
}
static public boolean getBoolean(String slotName,
Collection<? extends ISlot> slots, boolean defaultValue)
{
boolean rtn = defaultValue;
for (ISlot slot : slots)
if (slot.getName().equalsIgnoreCase(slotName))
try
{
// resolve the slot value... for now, string?
String value = slot.getValue().toString();
rtn = Boolean.parseBoolean(value);
}
catch (Exception e)
{
if (LOGGER.isWarnEnabled())
LOGGER.warn(String.format(
"Failed to extract boolean from %s, using default",
slot.getValue()));
}
return rtn;
}
static public boolean isIndexRetrievalEnabled(DefaultRetrievalModule6 module,
Collection<? extends ISlot> slots)
{
boolean rtn = getBoolean(DefaultRetrievalModule6.INDEXED_RETRIEVAL_SLOT,
slots, module != null ? module.isIndexedRetrievalEnabled() : false);
return rtn;
}
static public boolean isPartialMatchEnabled(IDeclarativeModule4 module,
Collection<? extends ISlot> slots)
{
return getBoolean(DefaultRetrievalModule6.PARTIAL_MATCH_SLOT, slots,
module.isPartialMatchingEnabled());
}
public RetrievalRequestDelegate(IRetrievalModule module)
{
_retrievalModule = module;
setUseBlockingTimedEvents(false);
setAsynchronous(true);
setDelayStart(false);
}
@Override
public void clear()
{
super.clear();
ITimedEvent previous = getCurrentTimedEvent();
if (previous != null && !previous.hasAborted() && !previous.hasFired())
previous.abort();
}
private boolean indexedRetrievalsEnabled(IRequest request)
{
DefaultRetrievalModule6 rm = _retrievalModule
.getAdapter(DefaultRetrievalModule6.class);
if (rm != null)
{
FastList<ISlot> slots = FastList.newInstance();
((ChunkTypeRequest) request).getSlots(slots);
boolean rtn = RetrievalRequestDelegate.isIndexRetrievalEnabled(rm, slots);
FastList.recycle(slots);
return rtn;
}
return false;
}
/**
* when chunks are expanded into search patterns, this determines whether null
* slot values will be included in the search pattern, defaults to false
*
* @param includeNulls
*/
public void setIncludeNullValues(boolean includeNulls)
{
_includeNullValues = includeNulls;
}
/**
* test to make sure all the slots are contained in the chunktype
*/
@Override
protected boolean isValid(IRequest request, IActivationBuffer buffer)
throws IllegalArgumentException
{
IChunkType chunkType = ((ChunkTypeRequest) request).getChunkType();
ISymbolicChunkType sChunkType = chunkType.getSymbolicChunkType();
for (ISlot slot : ((ChunkTypeRequest) request).getSlots())
{
// we can ignore meta slots
boolean valid = slot.getName().startsWith(":");
try
{
if (!valid) valid = null != sChunkType.getSlot(slot.getName());
}
catch (IllegalChunkTypeStateException icse)
{
}
if (!valid)
throw new IllegalArgumentException("No slot named " + slot.getName()
+ " available in " + chunkType);
}
return true;
}
/**
* expands chunk requests into chunktype requests, unless indexed retrievals
* are enabled.
*
* @param request
* @return
*/
@Override
protected IRequest expandRequest(IRequest request)
{
if (request instanceof ChunkRequest && !indexedRetrievalsEnabled(request))
{
IChunk chunk = ((ChunkRequest) request).getChunk();
ChunkTypeRequest ctr = new ChunkTypeRequest(chunk.getSymbolicChunk()
.getChunkType());
for (ISlot slot : chunk.getSymbolicChunk().getSlots())
if (slot.getValue() != null || _includeNullValues) ctr.addSlot(slot);
request = ctr;
}
return request;
}
@Override
protected Object startRequest(IRequest request, IActivationBuffer buffer,
double requestTime)
{
IModel model = buffer.getModel();
/*
* abort previous retrieval attempt
*/
ITimedEvent previousRetrieval = getCurrentTimedEvent();
if (previousRetrieval != null && !previousRetrieval.hasAborted()
&& !previousRetrieval.hasFired())
{
previousRetrieval.abort();
/*
* log that we are aborting
*/
if (Logger.hasLoggers(model) || LOGGER.isDebugEnabled())
{
String msg = "Aborted retrieval of " + getPreviousRequest();
if (Logger.hasLoggers(model))
Logger.log(model, Logger.Stream.RETRIEVAL, msg);
if (LOGGER.isDebugEnabled()) LOGGER.debug(msg);
}
}
/*
* clear the current buffer contents
*/
buffer.removeSourceChunk(buffer.getSourceChunk());
setBusy(buffer);
/*
* indexed retrievals must be enabled, lets just return the chunk
*/
if (request instanceof ChunkRequest && indexedRetrievalsEnabled(request))
return ((ChunkRequest) request).getChunk();
ChunkTypeRequest ctRequest = (ChunkTypeRequest) request;
return _retrievalModule.retrieveChunk(ctRequest);
}
@Override
protected void abortRequest(IRequest request, IActivationBuffer buffer,
Object startValue)
{
setFree(buffer);
super.abortRequest(request, buffer, startValue);
}
@SuppressWarnings("unchecked")
@Override
protected void finishRequest(IRequest request, IActivationBuffer buffer,
Object startValue)
{
IChunk error = buffer.getModel().getDeclarativeModule().getErrorChunk();
IChunk result = error;
boolean indexed = false;
if (startValue instanceof IChunk)
{
result = (IChunk) startValue;
indexed = true;
}
else
try
{
result = ((Future<IChunk>) startValue).get();
}
catch (InterruptedException ie)
{
// bail
LOGGER.warn("Interrupted, expecting termination ", ie);
return;
}
catch (Exception e)
{
LOGGER.error("Failed to get future of retrieval request " + request, e);
result = error;
}
if (indexed)
buffer.addSourceChunk(result); // will implicitly reset state
else
{
/*
* now this is just the harvest of the result from the module. we still
* need to calculate the retrieval time and post the event to actually
* make it available
*/
double retrievalStartTime = getCurrentTimedEvent().getEndTime();
/*
* what would the retrieval time be? This call uses the retrieval time
* method that includes the possibility for partial matching.
*/
double retrievalTime = _retrievalModule.getRetrievalTimeEquation()
.computeRetrievalTime(result, (ChunkTypeRequest) request);
if (retrievalTime > 60)
if (LOGGER.isWarnEnabled())
LOGGER
.warn(String
.format(
"WARNING retrieval of %s is going to take %.2fs. Check your parameter values!",
result, retrievalTime));
IModel model = buffer.getModel();
if (Logger.hasLoggers(model) || LOGGER.isDebugEnabled())
{
String msg = "Will retrieve " + result + " in " + retrievalTime + " @ "
+ (retrievalStartTime + retrievalTime);
Logger.log(model, Logger.Stream.RETRIEVAL, msg);
LOGGER.debug(msg);
}
ITimedEvent finish = new DelayedBufferInsertionTimedEvent(buffer, result,
retrievalStartTime, retrievalStartTime + retrievalTime);
model.getTimedEventQueue().enqueue(finish);
setCurrentTimedEvent(finish);
}
}
public boolean willAccept(IRequest request)
{
// includes chunkRequest for indexed retrievals
return request instanceof ChunkTypeRequest;
}
}