new Context<COMPONENT>(startPoint.context)
first!
* @param environment the static execution environment to use
*/
protected Execution(Chain extends Processor> chain, int startIndex, Trace trace, Environment extends Processor> environment) {
if (chain == null) throw new NullPointerException("Chain cannot be null");
this.chain = chain;
this.processorIndex = startIndex;
this.trace = trace;
this.environment = environment;
}
/**
* Calls process on the next processor in this chain. If there is no next, an empty response is returned.
*/
public Response process(Request request) {
Processor processor = next();
if (processor == null)
return defaultResponse(request);
Response response = null;
try {
nextProcessor();
onInvoking(request, processor);
response = processor.process(request, this);
if (response == null)
throw new NullPointerException(processor + " returned null, not a Response object");
return response;
} finally {
previousProcessor();
onReturning(request, processor, response);
}
}
/**
* Returns the index into the chain of processors which is currently next
*/
protected int nextIndex() {
return processorIndex;
}
/**
* A hook called when a processor is to be invoked. Overriding methods must call super.onInvoking
*/
protected void onInvoking(Request request, Processor next) {
if (Trace.Level.Step.includes(trace.getTraceLevel()) || trace.getForceTimestamps()) {
int traceAt = trace.getForceTimestamps() ? 1 : trace.getTraceLevel();
trace.trace("Invoke " + next, traceAt);
}
if (Trace.Level.Dependencies.includes(trace.getTraceLevel()))
trace.trace(next.getId() + " " + next.getDependencies().toString(), trace.getTraceLevel());
}
/**
* A hook called when a processor returns, either normally or by throwing.
* Overriding methods must call super.onReturning
*
* @param request the processing request
* @param processor the processor which returned
* @param response the response returned, or null if the processor returned by throwing
*/
protected void onReturning(Request request, Processor processor, Response response) {
if (Trace.Level.Step.includes(trace.getTraceLevel()) || trace.getForceTimestamps()) {
int traceAt = trace.getForceTimestamps() ? 1 : trace.getTraceLevel();
trace.trace("Return " + processor, traceAt);
}
}
/** Move this execution to the previous processor */
protected void previousProcessor() {
processorIndex--;
}
/** Move this execution to the next processor */
protected void nextProcessor() {
processorIndex++;
}
/** Returns the next searcher to be invoked in this chain, or null if we are at the last */
protected Processor next() {
if (chain.components().size() <= processorIndex) return null;
return chain.components().get(processorIndex);
}
/** Returns the chain this executes */
public Chain extends Processor> chain() {
return chain;
}
/**
* Creates the default response to return from this kind of execution when there are no further processors.
* If this is overridden, make sure to propagate any freezeListener from this to the returned response
* top-level DataList.
*/
protected Response defaultResponse(Request request) {
return new Response(request);
}
public String toString() {
return "execution of chain '" + chain.getId() + "'";
}
public Trace trace() {
return trace;
}
public Environment extends Processor> environment() {
return environment;
}
/**
* Holds the static execution environment for the duration of an execution
*/
public static class Environment* This method have a time complexity which is proportional to * the number of trace nodes in the tree * * @return the value of this property, or null if none */ public Object getProperty(String name) { return accept(new PropertyValueVisitor(name)).foundValue(); } /** * Returns the trace node peer of this */ public TraceNode traceNode() { return traceNode; } /** * Returns a short string description of this */ @Override public String toString() { return "trace: " + traceNode; } private static long timestamp(int traceLevel, boolean forceTimestamps) { return (forceTimestamps || Level.Timestamp.includes(traceLevel)) ? System.currentTimeMillis() : 0; } /** * Visits all trace nodes to collect the last set value of a particular property in a trace tree */ private static class PropertyValueVisitor extends TraceVisitor { /** * The name of the property to find */ private final String name; private Object foundValue = null; public PropertyValueVisitor(String name) { this.name = name; } @Override public void visit(TraceNode node) { if (node.payload() == null) return; if (!(node.payload() instanceof Pair property)) return; if (!property.getFirst().equals(name)) return; foundValue = property.getSecond(); } public Object foundValue() { return foundValue; } } /** * An immutable access log value added to the trace */ public static class LogValue { private final String key; private final String value; public LogValue(String key, String value) { this.key = key; this.value = value; } public String getKey() { return key; } public String getValue() { return value; } @Override public String toString() { return key + "=" + value; } } /** * Defines what information is added at which trace level */ public enum Level { /** * Every processing step initiated is traced */ Step(4), /** * All trace messages are timestamped */ Timestamp(6), /** * The before/after dependencies of each processing step is traced on every invocation */ Dependencies(7); /** * The smallest trace level at which this information will be traced */ private final int value; Level(int value) { this.value = value; } public int value() { return value; } /** * Returns whether this level includes the given level, i.e whether traceLevel is this.value() or more */ public boolean includes(int traceLevel) { return traceLevel >= this.value(); } } } }