package xdi2.client.impl;
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.messaging.Message;
import xdi2.messaging.MessageEnvelope;
import xdi2.messaging.operations.Operation;
/**
* Manipulators can use the ManipulationContext to store and share state. The ManipulationContext
* is created before a message is sent. It contains the current position in the manipulation process,
* and attributes associated with various stages.
*/
public final class ManipulationContext implements Serializable {
private static final long serialVersionUID = -4765997602206764806L;
private static Logger log = LoggerFactory.getLogger(ManipulationContext.class.getName());
/**
* This map is never reset.
*/
private Map<String, Object> manipulationContextAttributes;
/**
* This map is reset before manipulating a MessageEnvelope.
*/
private Map<String, Object> messageEnvelopeAttributes;
/**
* This map is reset before manipulating a Message in a MessageEnvelope.
*/
private Map<String, Object> messageAttributes;
/**
* The exception that happened during manipulation.
*/
private Throwable ex;
/**
* Timestamp of the first push.
*/
private long firstPush;
/**
* The current manipulation position.
* This is either a MessageEnvelope, or a Message.
*/
private ManipulationPosition<?> currentManipulationPosition, topManipulationPosition, exceptionManipulationPosition;
private ManipulationContext() {
this.manipulationContextAttributes = new HashMap<String, Object> ();
this.messageEnvelopeAttributes = new HashMap<String, Object> ();
this.messageAttributes = new HashMap<String, Object> ();
this.ex = null;
this.firstPush = -1;
this.currentManipulationPosition = new ManipulationPosition<ManipulationContext> (null, this, null);
this.topManipulationPosition = this.currentManipulationPosition;
}
public static ManipulationContext createManipulationContext() {
return new ManipulationContext();
}
/*
* Attributes
*/
public Object getManipulationContextAttribute(String key) {
return this.manipulationContextAttributes.get(key);
}
public void putManipulationContextAttribute(String key, Object value) {
if (value == null)
this.manipulationContextAttributes.remove(key);
else
this.manipulationContextAttributes.put(key, value);
}
public Map<String, Object> getManipulationContextAttributes() {
return this.manipulationContextAttributes;
}
public void setManipulationContextAttributes(Map<String, Object> manipulationContextAttributes) {
this.manipulationContextAttributes = manipulationContextAttributes;
}
public void resetManipulationContextAttributes() {
this.manipulationContextAttributes = 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> ();
}
/*
* Exception
*/
public Throwable getException() {
return this.ex;
}
public Throwable processException(Throwable ex) {
assert(ex != null);
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 = ex;
this.exceptionManipulationPosition = this.currentManipulationPosition;
return this.ex;
}
/*
* Manipulation positions
*/
public void pushMessageEnvelope(MessageEnvelope messageEnvelope, String comment) {
this.pushManipulationPosition(messageEnvelope, comment);
}
public void pushMessage(Message message, String comment) {
this.pushManipulationPosition(message, comment);
}
public void popMessageEnvelope() {
this.popManipulationPosition(MessageEnvelope.class);
}
public void popMessage() {
this.popManipulationPosition(Message.class);
}
public MessageEnvelope getCurrentMessageEnvelope() {
ManipulationPosition<MessageEnvelope> manipulationPosition = this.findManipulationPosition(this.currentManipulationPosition, MessageEnvelope.class);
return manipulationPosition == null ? null : manipulationPosition.manipulationObject;
}
public List<MessageEnvelope> getCurrentMessageEnvelopes() {
List<ManipulationPosition<MessageEnvelope>> manipulationPositions = this.findManipulationPositions(this.currentManipulationPosition, MessageEnvelope.class);
List<MessageEnvelope> messageEnvelopes = new ArrayList<MessageEnvelope> ();
for (ManipulationPosition<MessageEnvelope> manipulationPosition : manipulationPositions) messageEnvelopes.add(manipulationPosition.manipulationObject);
return messageEnvelopes;
}
public Message getCurrentMessage() {
ManipulationPosition<Message> manipulationPosition = this.findManipulationPosition(this.currentManipulationPosition, Message.class);
return manipulationPosition == null ? null : manipulationPosition.manipulationObject;
}
public List<Message> getCurrentMessages() {
List<ManipulationPosition<Message>> manipulationPositions = this.findManipulationPositions(this.currentManipulationPosition, Message.class);
List<Message> messages = new ArrayList<Message> ();
for (ManipulationPosition<Message> manipulationPosition : manipulationPositions) messages.add(manipulationPosition.manipulationObject);
return messages;
}
public Operation getCurrentOperation() {
ManipulationPosition<Operation> manipulationPosition = this.findManipulationPosition(this.currentManipulationPosition, Operation.class);
return manipulationPosition == null ? null : manipulationPosition.manipulationObject;
}
public MessageEnvelope getExceptionMessageEnvelope() {
ManipulationPosition<MessageEnvelope> manipulationPosition = this.findManipulationPosition(this.exceptionManipulationPosition, MessageEnvelope.class);
return manipulationPosition == null ? null : manipulationPosition.manipulationObject;
}
public Message getExceptionMessage() {
ManipulationPosition<Message> manipulationPosition = this.findManipulationPosition(this.exceptionManipulationPosition, Message.class);
return manipulationPosition == null ? null : manipulationPosition.manipulationObject;
}
public boolean isTopManipulationPosition() {
return this.currentManipulationPosition == this.topManipulationPosition;
}
private <T> void pushManipulationPosition(T object, String comment) {
if (object == null) throw new NullPointerException();
this.currentManipulationPosition = new ManipulationPosition<T> (this.currentManipulationPosition, object, comment);
this.currentManipulationPosition.push = System.currentTimeMillis();
if (this.firstPush == -1) this.firstPush = this.currentManipulationPosition.push;
}
private <T> void popManipulationPosition(Class<? extends T> clazz) {
this.currentManipulationPosition.pop = System.currentTimeMillis();
if (this.currentManipulationPosition == this.topManipulationPosition) throw new IllegalStateException("No more manipulation positions.");
if (! clazz.isAssignableFrom(this.currentManipulationPosition.manipulationObject.getClass())) throw new IllegalStateException("Unexpected manipulation position class: " + this.currentManipulationPosition.manipulationObject.getClass().getSimpleName() + " (should be " + clazz.getSimpleName() + ").");
this.currentManipulationPosition = this.currentManipulationPosition.parentManipulationPosition;
}
@SuppressWarnings("unchecked")
private <T> List<ManipulationPosition<T>> findManipulationPositions(ManipulationPosition<?> startManipulationPosition, Class<? extends T> clazz) {
List<ManipulationPosition<T>> manipulationPositions = new ArrayList<ManipulationPosition<T>> ();
for (ManipulationPosition<?> manipulationPosition = startManipulationPosition; manipulationPosition != this.topManipulationPosition; ) {
if (clazz.isAssignableFrom(manipulationPosition.manipulationObject.getClass())) manipulationPositions.add((ManipulationPosition<T>) manipulationPosition);
manipulationPosition = manipulationPosition.parentManipulationPosition;
}
return manipulationPositions;
}
@SuppressWarnings("unchecked")
private <T> ManipulationPosition<T> findManipulationPosition(ManipulationPosition<?> startManipulationPosition, Class<? extends T> clazz) {
for (ManipulationPosition<?> manipulationPosition = startManipulationPosition; manipulationPosition != this.topManipulationPosition; ) {
if (clazz.isAssignableFrom(manipulationPosition.manipulationObject.getClass())) return (ManipulationPosition<T>) manipulationPosition;
manipulationPosition = manipulationPosition.parentManipulationPosition;
}
return null;
}
/*
* Tracing
*/
public String getTraceLine() {
StringBuffer buffer = new StringBuffer();
for (ManipulationPosition<?> manipulationPosition = this.exceptionManipulationPosition; manipulationPosition != this.topManipulationPosition; ) {
Object manipulationObject = manipulationPosition.manipulationObject;
buffer.insert(0, manipulationObject.getClass().getSimpleName());
if (manipulationPosition.parentManipulationPosition != this.topManipulationPosition) buffer.insert(0, "-->");
manipulationPosition = manipulationPosition.parentManipulationPosition;
}
return buffer.toString();
}
public String getTraceBlock() {
return this.getTraceBlock(0, this.topManipulationPosition);
}
private String getTraceBlock(int depth, ManipulationPosition<?> parentManipulationPosition) {
StringBuffer buffer = new StringBuffer();
buffer.append("\n");
for (int i=0; i<depth; i++) buffer.append(" ");
buffer.append(parentManipulationPosition.toString());
if (parentManipulationPosition == this.currentManipulationPosition) buffer.append(" <-- (CURRENT)");
if (parentManipulationPosition == this.exceptionManipulationPosition) buffer.append(" <-- (EXCEPTION)");
for (ManipulationPosition<?> manipulationPosition : parentManipulationPosition.childManipulationPositions) {
buffer.append(this.getTraceBlock(depth + 1, manipulationPosition));
}
return buffer.toString();
}
/*
* Helper classes
*/
private final class ManipulationPosition<T> {
private ManipulationPosition<?> parentManipulationPosition;
private T manipulationObject;
private String comment;
private long push;
private long pop;
private List<ManipulationPosition<?>> childManipulationPositions;
private ManipulationPosition(ManipulationPosition<?> parentManipulationPosition, T manipulationObject, String comment) {
this.parentManipulationPosition = parentManipulationPosition;
this.manipulationObject = manipulationObject;
this.comment = comment;
this.push = -1;
this.pop = -1;
this.childManipulationPositions = new ArrayList<ManipulationPosition<?>> ();
if (parentManipulationPosition != null) parentManipulationPosition.childManipulationPositions.add(this);
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
if (ManipulationContext.this.firstPush != -1 && this.push != -1 && this.pop != -1) buffer.append("[" + (this.push - ManipulationContext.this.firstPush) + "-"+ (this.pop - ManipulationContext.this.firstPush) + "ms] ");
buffer.append(this.manipulationObject.getClass().getSimpleName());
if (this.comment != null) buffer.append(" (" + this.comment + ")");
return buffer.toString();
}
}
}