package xdi2.messaging.container.execution; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import xdi2.core.Graph; import xdi2.core.syntax.XDIAddress; import xdi2.core.syntax.XDIStatement; import xdi2.messaging.Message; import xdi2.messaging.MessageEnvelope; import xdi2.messaging.container.MessagingContainer; import xdi2.messaging.container.contributor.Contributor; import xdi2.messaging.container.exceptions.Xdi2MessagingException; import xdi2.messaging.container.impl.graph.GraphMessagingContainer; import xdi2.messaging.container.interceptor.Interceptor; import xdi2.messaging.operations.Operation; /** * Messaging targets as well as contributors and interceptors can use the ExecutionContext * to store and share state. The ExecutionContext is created before a MessageEnvelope is * executed, and is deleted when execution of the MessageEnvelope is complete. It contains * the current position in the execution process, and attributes associated with various * stages. */ public final class ExecutionContext implements Serializable { private static final long serialVersionUID = 3238581605986543950L; private static Logger log = LoggerFactory.getLogger(ExecutionContext.class.getName()); /** * This map is never reset. */ private Map<String, Object> executionContextAttributes; /** * This map is reset before executing a MessageEnvelope. */ private Map<String, Object> messageEnvelopeAttributes; /** * This map is reset before executing a Message in a MessageEnvelope. */ private Map<String, Object> messageAttributes; /** * This map is reset before executing an Operation in a Message. */ private Map<String, Object> operationAttributes; /** * The exception that happened during execution. */ private Xdi2MessagingException ex; /** * Timestamp of the first push. */ private long firstPush; /** * The current execution position. * This is either a MessagingContainer, a MessageEnvelope, a Message, * an Operation, an Interceptor, a Contributor, an XDIAddress, * or an XDIStatement. */ private ExecutionPosition<?> currentExecutionPosition, topExecutionPosition, exceptionExecutionPosition; private ExecutionContext() { this.executionContextAttributes = new HashMap<String, Object> (); this.messageEnvelopeAttributes = new HashMap<String, Object> (); this.messageAttributes = new HashMap<String, Object> (); this.operationAttributes = new HashMap<String, Object> (); this.ex = null; this.firstPush = -1; this.currentExecutionPosition = new ExecutionPosition<ExecutionContext> (null, this, null); this.topExecutionPosition = this.currentExecutionPosition; } public static ExecutionContext createExecutionContext() { return new ExecutionContext(); } /* * Attributes */ public Object getExecutionContextAttribute(String key) { return this.executionContextAttributes.get(key); } public void putExecutionContextAttribute(String key, Object value) { if (value == null) this.executionContextAttributes.remove(key); else this.executionContextAttributes.put(key, value); } public Map<String, Object> getExecutionContextAttributes() { return this.executionContextAttributes; } public void setExecutionContextAttributes(Map<String, Object> executionContextAttributes) { this.executionContextAttributes = executionContextAttributes; } public void resetExecutionContextAttributes() { this.executionContextAttributes = new HashMap<String, Object> (); } public Object getMessageEnvelopeAttribute(String key) { return this.messageEnvelopeAttributes.get(key); } public void putMessageEnvelopeAttribute(String key, Object value) { if (value == null) this.messageEnvelopeAttributes.remove(key); else this.messageEnvelopeAttributes.put(key, value); } public Map<String, Object> getMessageEnvelopeAttributes() { return this.messageEnvelopeAttributes; } public void setMessageEnvelopeAttributes(Map<String, Object> messageEnvelopeAttributes) { this.messageEnvelopeAttributes = messageEnvelopeAttributes; } public void resetMessageEnvelopeAttributes() { this.messageEnvelopeAttributes = new HashMap<String, Object> (); } public Object getMessageAttribute(String key) { return this.messageAttributes.get(key); } public void putMessageAttribute(String key, Object value) { if (value == null) this.messageAttributes.remove(key); else this.messageAttributes.put(key, value); } public Map<String, Object> getMessageAttributes() { return this.messageAttributes; } public void setMessageAttributes(Map<String, Object> messageAttributes) { this.messageAttributes = messageAttributes; } public void resetMessageAttributes() { this.messageAttributes = new HashMap<String, Object> (); } public Object getOperationAttribute(String key) { return this.operationAttributes.get(key); } public void putOperationAttribute(String key, Object value) { if (value == null) this.operationAttributes.remove(key); else this.operationAttributes.put(key, value); } public Map<String, Object> getOperationAttributes() { return this.operationAttributes; } public void setOperationAttributes(Map<String, Object> operationAttributes) { this.operationAttributes = operationAttributes; } public void resetOperationAttributes() { this.operationAttributes = new HashMap<String, Object> (); } /* * Exception */ public Xdi2MessagingException getException() { return this.ex; } public Xdi2MessagingException processException(Throwable ex) { assert(ex != null); if (! (ex instanceof Xdi2MessagingException)) { ex = new Xdi2MessagingException(ex.getClass().getSimpleName() + " [" + ex.getMessage() + "]", ex, this); } if (log.isDebugEnabled()) { StringBuffer buffer = new StringBuffer(); buffer.append("New Exception: " + ex.getClass().getSimpleName() + " [" + ex.getMessage() + "]. "); if (this.ex != null) { buffer.append("Current: " + this.ex.getClass().getSimpleName() + " [" + this.ex.getMessage() + "]. "); buffer.append("Same? " + (ex == this.ex)); } log.debug(buffer.toString()); } if (this.ex != null) return this.ex; this.ex = (Xdi2MessagingException) ex; this.exceptionExecutionPosition = this.currentExecutionPosition; return this.ex; } /* * Execution positions */ public void pushMessagingContainer(MessagingContainer messagingContainer, String comment) { this.pushExecutionPosition(messagingContainer, comment); } public void pushMessageEnvelope(MessageEnvelope messageEnvelope, String comment) { this.pushExecutionPosition(messageEnvelope, comment); } public void pushMessage(Message message, String comment) { this.pushExecutionPosition(message, comment); } public void pushOperation(Operation operation, String comment) { this.pushExecutionPosition(operation, comment); } public void pushTargetAddress(XDIAddress targetAddress, String comment) { this.pushExecutionPosition(targetAddress, comment); } public void pushTargetStatement(XDIStatement targetStatement, String comment) { this.pushExecutionPosition(targetStatement, comment); } public void pushInterceptor(Interceptor<MessagingContainer> interceptor, String comment) { this.pushExecutionPosition(interceptor, comment); } public void pushContributor(Contributor contributor, String comment) { this.pushExecutionPosition(contributor, comment); } public void popMessagingContainer() { this.popExecutionPosition(MessagingContainer.class); } public void popMessageEnvelope() { this.popExecutionPosition(MessageEnvelope.class); } public void popMessage() { this.popExecutionPosition(Message.class); } public void popOperation() { this.popExecutionPosition(Operation.class); } public void popTargetAddress() { this.popExecutionPosition(XDIAddress.class); } public void popTargetStatement() { this.popExecutionPosition(XDIStatement.class); } public void popInterceptor() { this.popExecutionPosition(Interceptor.class); } public void popContributor() { this.popExecutionPosition(Contributor.class); } public MessagingContainer getCurrentMessagingContainer() { ExecutionPosition<MessagingContainer> executionPosition = this.findExecutionPosition(this.currentExecutionPosition, MessagingContainer.class); return executionPosition == null ? null : executionPosition.executionObject; } public List<MessagingContainer> getCurrentMessagingContainers() { List<ExecutionPosition<MessagingContainer>> executionPositions = this.findExecutionPositions(this.currentExecutionPosition, MessagingContainer.class); List<MessagingContainer> messagingContainers = new ArrayList<MessagingContainer> (); for (ExecutionPosition<MessagingContainer> executionPosition : executionPositions) messagingContainers.add(executionPosition.executionObject); return messagingContainers; } public Graph getCurrentGraph() { MessagingContainer currentMessagingContainer = this.getCurrentMessagingContainer(); return (currentMessagingContainer instanceof GraphMessagingContainer) ? ((GraphMessagingContainer) currentMessagingContainer).getGraph() : null; } public MessageEnvelope getCurrentMessageEnvelope() { ExecutionPosition<MessageEnvelope> executionPosition = this.findExecutionPosition(this.currentExecutionPosition, MessageEnvelope.class); return executionPosition == null ? null : executionPosition.executionObject; } public List<MessageEnvelope> getCurrentMessageEnvelopes() { List<ExecutionPosition<MessageEnvelope>> executionPositions = this.findExecutionPositions(this.currentExecutionPosition, MessageEnvelope.class); List<MessageEnvelope> messageEnvelopes = new ArrayList<MessageEnvelope> (); for (ExecutionPosition<MessageEnvelope> executionPosition : executionPositions) messageEnvelopes.add(executionPosition.executionObject); return messageEnvelopes; } public Message getCurrentMessage() { ExecutionPosition<Message> executionPosition = this.findExecutionPosition(this.currentExecutionPosition, Message.class); return executionPosition == null ? null : executionPosition.executionObject; } public List<Message> getCurrentMessages() { List<ExecutionPosition<Message>> executionPositions = this.findExecutionPositions(this.currentExecutionPosition, Message.class); List<Message> messages = new ArrayList<Message> (); for (ExecutionPosition<Message> executionPosition : executionPositions) messages.add(executionPosition.executionObject); return messages; } public Operation getCurrentOperation() { ExecutionPosition<Operation> executionPosition = this.findExecutionPosition(this.currentExecutionPosition, Operation.class); return executionPosition == null ? null : executionPosition.executionObject; } public List<Operation> getCurrentOperations() { List<ExecutionPosition<Operation>> executionPositions = this.findExecutionPositions(this.currentExecutionPosition, Operation.class); List<Operation> operations = new ArrayList<Operation> (); for (ExecutionPosition<Operation> executionPosition : executionPositions) operations.add(executionPosition.executionObject); return operations; } public XDIAddress getCurrentTargetAddress() { ExecutionPosition<XDIAddress> executionPosition = this.findExecutionPosition(this.currentExecutionPosition, XDIAddress.class); return executionPosition == null ? null : executionPosition.executionObject; } public XDIStatement getCurrentTargetStatement() { ExecutionPosition<XDIStatement> executionPosition = this.findExecutionPosition(this.currentExecutionPosition, XDIStatement.class); return executionPosition == null ? null : executionPosition.executionObject; } @SuppressWarnings({ "rawtypes", "unchecked" }) public Interceptor<MessagingContainer> getCurrentInterceptor() { ExecutionPosition<Interceptor> executionPosition = this.findExecutionPosition(this.currentExecutionPosition, Interceptor.class); return executionPosition == null ? null : executionPosition.executionObject; } public Contributor getCurrentContributor() { ExecutionPosition<Contributor> executionPosition = this.findExecutionPosition(this.currentExecutionPosition, Contributor.class); return executionPosition == null ? null : executionPosition.executionObject; } public MessagingContainer getExceptionMessagingContainer() { ExecutionPosition<MessagingContainer> executionPosition = this.findExecutionPosition(this.exceptionExecutionPosition, MessagingContainer.class); return executionPosition == null ? null : executionPosition.executionObject; } public MessageEnvelope getExceptionMessageEnvelope() { ExecutionPosition<MessageEnvelope> executionPosition = this.findExecutionPosition(this.exceptionExecutionPosition, MessageEnvelope.class); return executionPosition == null ? null : executionPosition.executionObject; } public Message getExceptionMessage() { ExecutionPosition<Message> executionPosition = this.findExecutionPosition(this.exceptionExecutionPosition, Message.class); return executionPosition == null ? null : executionPosition.executionObject; } public Operation getExceptionOperation() { ExecutionPosition<Operation> executionPosition = this.findExecutionPosition(this.exceptionExecutionPosition, Operation.class); return executionPosition == null ? null : executionPosition.executionObject; } public XDIAddress getExceptionTargetAddress() { ExecutionPosition<XDIAddress> executionPosition = this.findExecutionPosition(this.exceptionExecutionPosition, XDIAddress.class); return executionPosition == null ? null : executionPosition.executionObject; } public XDIStatement getExceptionTargetStatement() { ExecutionPosition<XDIStatement> executionPosition = this.findExecutionPosition(this.exceptionExecutionPosition, XDIStatement.class); return executionPosition == null ? null : executionPosition.executionObject; } @SuppressWarnings({ "rawtypes", "unchecked" }) public Interceptor<MessagingContainer> getExceptionInterceptor() { ExecutionPosition<Interceptor> executionPosition = this.findExecutionPosition(this.exceptionExecutionPosition, Interceptor.class); return executionPosition == null ? null : executionPosition.executionObject; } public Contributor getExceptionContributor() { ExecutionPosition<Contributor> executionPosition = this.findExecutionPosition(this.exceptionExecutionPosition, Contributor.class); return executionPosition == null ? null : executionPosition.executionObject; } public boolean isTopExecutionPosition() { return this.currentExecutionPosition == this.topExecutionPosition; } private <T> void pushExecutionPosition(T object, String comment) { if (object == null) throw new NullPointerException(); this.currentExecutionPosition = new ExecutionPosition<T> (this.currentExecutionPosition, object, comment); this.currentExecutionPosition.push = System.currentTimeMillis(); if (this.firstPush == -1) this.firstPush = this.currentExecutionPosition.push; } private <T> void popExecutionPosition(Class<? extends T> clazz) { this.currentExecutionPosition.pop = System.currentTimeMillis(); if (this.currentExecutionPosition == this.topExecutionPosition) throw new IllegalStateException("No more execution positions."); if (! clazz.isAssignableFrom(this.currentExecutionPosition.executionObject.getClass())) throw new IllegalStateException("Unexpected execution position class: " + this.currentExecutionPosition.executionObject.getClass().getSimpleName() + " (should be " + clazz.getSimpleName() + ")."); this.currentExecutionPosition = this.currentExecutionPosition.parentExecutionPosition; } @SuppressWarnings("unchecked") private <T> List<ExecutionPosition<T>> findExecutionPositions(ExecutionPosition<?> startExecutionPosition, Class<? extends T> clazz) { List<ExecutionPosition<T>> executionPositions = new ArrayList<ExecutionPosition<T>> (); for (ExecutionPosition<?> executionPosition = startExecutionPosition; executionPosition != this.topExecutionPosition; ) { if (clazz.isAssignableFrom(executionPosition.executionObject.getClass())) executionPositions.add((ExecutionPosition<T>) executionPosition); executionPosition = executionPosition.parentExecutionPosition; } return executionPositions; } @SuppressWarnings("unchecked") private <T> ExecutionPosition<T> findExecutionPosition(ExecutionPosition<?> startExecutionPosition, Class<? extends T> clazz) { for (ExecutionPosition<?> executionPosition = startExecutionPosition; executionPosition != this.topExecutionPosition; ) { if (clazz.isAssignableFrom(executionPosition.executionObject.getClass())) return (ExecutionPosition<T>) executionPosition; executionPosition = executionPosition.parentExecutionPosition; } return null; } /* * Tracing */ public String getTraceLine() { StringBuffer buffer = new StringBuffer(); for (ExecutionPosition<?> executionPosition = this.exceptionExecutionPosition; executionPosition != this.topExecutionPosition; ) { Object executionObject = executionPosition.executionObject; buffer.insert(0, executionObject.getClass().getSimpleName()); if (executionPosition.parentExecutionPosition != this.topExecutionPosition) buffer.insert(0, "-->"); executionPosition = executionPosition.parentExecutionPosition; } return buffer.toString(); } public String getTraceBlock() { return this.getTraceBlock(0, this.topExecutionPosition); } private String getTraceBlock(int depth, ExecutionPosition<?> parentExecutionPosition) { StringBuffer buffer = new StringBuffer(); buffer.append("\n"); for (int i=0; i<depth; i++) buffer.append(" "); buffer.append(parentExecutionPosition.toString()); if (parentExecutionPosition == this.currentExecutionPosition) buffer.append(" <-- (CURRENT)"); if (parentExecutionPosition == this.exceptionExecutionPosition) buffer.append(" <-- (EXCEPTION)"); for (ExecutionPosition<?> executionPosition : parentExecutionPosition.childExecutionPositions) { buffer.append(this.getTraceBlock(depth + 1, executionPosition)); } return buffer.toString(); } /* * Helper classes */ private final class ExecutionPosition<T> { private ExecutionPosition<?> parentExecutionPosition; private T executionObject; private String comment; private long push; private long pop; private List<ExecutionPosition<?>> childExecutionPositions; private ExecutionPosition(ExecutionPosition<?> parentExecutionPosition, T executionObject, String comment) { this.parentExecutionPosition = parentExecutionPosition; this.executionObject = executionObject; this.comment = comment; this.push = -1; this.pop = -1; this.childExecutionPositions = new ArrayList<ExecutionPosition<?>> (); if (parentExecutionPosition != null) parentExecutionPosition.childExecutionPositions.add(this); } @Override public String toString() { StringBuffer buffer = new StringBuffer(); if (ExecutionContext.this.firstPush != -1 && this.push != -1 && this.pop != -1) buffer.append("[" + (this.push - ExecutionContext.this.firstPush) + "-"+ (this.pop - ExecutionContext.this.firstPush) + "ms] "); buffer.append(this.executionObject.getClass().getSimpleName()); if (this.comment != null) buffer.append(" (" + this.comment + ")"); return buffer.toString(); } } }