package xdi2.messaging.container.execution; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import xdi2.core.Graph; import xdi2.core.exceptions.Xdi2RuntimeException; import xdi2.core.features.error.XdiError; import xdi2.core.features.nodetypes.XdiCommonRoot; import xdi2.core.features.nodetypes.XdiPeerRoot; import xdi2.core.impl.memory.MemoryGraphFactory; import xdi2.core.syntax.XDIAddress; import xdi2.core.syntax.XDIArc; import xdi2.core.util.CopyUtil; import xdi2.messaging.Message; import xdi2.messaging.MessageEnvelope; import xdi2.messaging.container.MessagingContainer; import xdi2.messaging.container.exceptions.Xdi2MessagingException; import xdi2.messaging.operations.Operation; import xdi2.messaging.response.FullMessagingResponse; import xdi2.messaging.response.LightMessagingResponse; /** * This class represent a result of a message envelope that has been executed * against a messaging container. Basically this encapsulates the results of all * the individual operations in the message envelope. * * The merged result graphs of the individual operations constitute the * overall result graph. * * This can be used to create a messaging response which a responder would * return to a requester. */ public final class ExecutionResult { private static final Logger log = LoggerFactory.getLogger(ExecutionResult.class); private Map<Message, Graph> messageDeferredPushResultGraphs; private Map<Operation, Graph> operationResultGraphs; private Throwable ex; private Exception resultGraphFinishedEx; private ExecutionResult(Map<Message, Graph> messagePushResultGraphs, Map<Operation, Graph> operationResultGraphs) { this.messageDeferredPushResultGraphs = messagePushResultGraphs; this.operationResultGraphs = operationResultGraphs; this.ex = null; this.resultGraphFinishedEx = null; } /* * Static methods */ public static ExecutionResult createExecutionResult(MessageEnvelope messageEnvelope) { if (messageEnvelope == null) throw new NullPointerException(); // set up message deferred push result graphs Map<Message, Graph> messageDeferredPushResultGraphs = new HashMap<Message, Graph> (); for (Message message : messageEnvelope.getMessages()) messageDeferredPushResultGraphs.put(message, null); // set up operation result graphs Map<Operation, Graph> operationResultGraphs = new HashMap<Operation, Graph> (); for (Operation operation : messageEnvelope.getOperations()) operationResultGraphs.put(operation, null); // create execution result ExecutionResult executionResult = new ExecutionResult(messageDeferredPushResultGraphs, operationResultGraphs); // done return executionResult; } /* * Instance methods */ public Graph createMessageDeferredPushResultGraph(Message message) { if (message == null) throw new NullPointerException(); if (this.isFinished()) throw new Xdi2RuntimeException("Execution result has already been finished.", this.resultGraphFinishedEx); if (! this.messageDeferredPushResultGraphs.containsKey(message)) throw new Xdi2RuntimeException("No message deferred push result graph for message" + message); if (this.messageDeferredPushResultGraphs.get(message) != null) throw new Xdi2RuntimeException("Message deferred push result graph for message " + message + " has already been created."); Graph messageDeferredPushResultGraph = MemoryGraphFactory.getInstance().openGraph(); this.messageDeferredPushResultGraphs.put(message, messageDeferredPushResultGraph); return messageDeferredPushResultGraph; } public Graph createOperationResultGraph(Operation operation) { if (operation == null) throw new NullPointerException(); if (this.isFinished()) throw new Xdi2RuntimeException("Execution result has already been finished.", this.resultGraphFinishedEx); if (! this.operationResultGraphs.containsKey(operation)) throw new Xdi2RuntimeException("No operation result graph for operation " + operation); if (this.operationResultGraphs.get(operation) != null) throw new Xdi2RuntimeException("Operation result graph for operation " + operation + " has already been created."); Graph operationResultGraph = MemoryGraphFactory.getInstance().openGraph(); this.operationResultGraphs.put(operation, operationResultGraph); return operationResultGraph; } public void addException(Throwable ex) { if (ex == null) throw new NullPointerException(); if (this.isFinished()) throw new Xdi2RuntimeException("Execution result has already been finished.", this.resultGraphFinishedEx); if (this.ex != null) throw new Xdi2RuntimeException("Already have an exception."); this.ex = ex; } public Graph getFinishedMessageDeferredPushResultGraph(Message message) { if (message == null) throw new NullPointerException(); if (! this.isFinished()) throw new Xdi2RuntimeException("Execution result has not been finished yet.", this.resultGraphFinishedEx); if (! this.messageDeferredPushResultGraphs.containsKey(message)) throw new Xdi2RuntimeException("No message deferred push result graph for message " + message); return this.messageDeferredPushResultGraphs.get(message); } public Graph getFinishedOperationResultGraph(Operation operation) { if (operation == null) throw new NullPointerException(); if (! this.isFinished()) throw new Xdi2RuntimeException("Execution result has not been finished yet.", this.resultGraphFinishedEx); if (! this.operationResultGraphs.containsKey(operation)) throw new Xdi2RuntimeException("No operation result graph for operation " + operation); return this.operationResultGraphs.get(operation); } public boolean isFinished() { return this.resultGraphFinishedEx != null; } /* * Helper methods */ public final LightMessagingResponse makeLightMessagingResponse() { if (! this.isFinished()) throw new Xdi2RuntimeException("Execution result has not been finished yet.", this.resultGraphFinishedEx); // result graph Graph resultGraph = MemoryGraphFactory.getInstance().openGraph(); for (Graph operationResultGraph : this.operationResultGraphs.values()) { if (operationResultGraph == null) continue; CopyUtil.copyGraph(operationResultGraph, resultGraph, null); } // create messaging response LightMessagingResponse lightMessagingResponse = LightMessagingResponse.fromResultGraph(resultGraph); // done return lightMessagingResponse; } public final FullMessagingResponse makeFullMessagingResponse(MessageEnvelope messageEnvelope, MessagingContainer messagingContainer) { if (! this.isFinished()) throw new Xdi2RuntimeException("Execution result has not been finished yet.", this.resultGraphFinishedEx); // create messaging response MessageEnvelope responseMessageEnvelope = new MessageEnvelope(); for (Message message : messageEnvelope.getMessages()) { XDIArc toPeerRootXDIArc = message.getFromPeerRootXDIArc(); XDIArc fromPeerRootXDIArc = message.getToPeerRootXDIArc(); XDIAddress senderXDIAddress = XdiPeerRoot.getXDIAddressOfPeerRootXDIArc(messagingContainer.getOwnerPeerRootXDIArc()); Message responseMessage = responseMessageEnvelope.createMessage(senderXDIAddress); responseMessage.setFromPeerRootXDIArc(fromPeerRootXDIArc); responseMessage.setToPeerRootXDIArc(toPeerRootXDIArc); responseMessage.setTimestamp(new Date()); responseMessage.setCorrelationXDIAddress(message.getContextNode().getXDIAddress()); for (Operation operation : message.getOperations()) { Graph operationResultGraph = this.getFinishedOperationResultGraph(operation); if (operationResultGraph != null) { responseMessage.createOperationResult(operation.getOperationXDIAddress(), operationResultGraph); } } Graph messageDeferredPushResultGraph = this.getFinishedMessageDeferredPushResultGraph(message); if (messageDeferredPushResultGraph != null) { responseMessage.createMessageDeferredPushResult(messageDeferredPushResultGraph); } } FullMessagingResponse fullMessagingResponse = FullMessagingResponse.fromMessageEnvelope(responseMessageEnvelope); // done return fullMessagingResponse; } public void finish() { if (this.isFinished()) throw new Xdi2RuntimeException("Execution result has already been finished."); // finish exception this.finishException(); // done this.resultGraphFinishedEx = new Exception(); if (log.isInfoEnabled()) log.info("Execution result finished."); } private void finishException() { if (this.ex == null) return; // error string String errorString = this.ex.getMessage(); if (errorString == null) errorString = this.ex.getClass().getSimpleName(); if (log.isInfoEnabled()) log.info("Error string: " + errorString); // look for exception operation Message exceptionMessage = null; Operation exceptionOperation = null; if (this.ex instanceof Xdi2MessagingException) { ExecutionContext executionContext = ((Xdi2MessagingException) this.ex).getExecutionContext(); exceptionMessage = executionContext == null ? null : executionContext.getExceptionMessage(); exceptionOperation = executionContext == null ? null : executionContext.getExceptionOperation(); if (! this.operationResultGraphs.containsKey(exceptionOperation)) exceptionOperation = null; } if (log.isInfoEnabled()) log.info("Exception message: " + exceptionMessage + " - Exception operation: " + exceptionOperation); // look at all operations for (Map.Entry<Operation, Graph> entry : this.operationResultGraphs.entrySet()) { Operation operation = entry.getKey(); Graph operationResultGraph = entry.getValue(); boolean setErrorForThisOperation = false; if (exceptionOperation != null && exceptionOperation.equals(operation)) setErrorForThisOperation = true; if (exceptionOperation == null && exceptionMessage != null && exceptionMessage.equals(operation.getMessage())) setErrorForThisOperation = true; if (exceptionOperation == null && exceptionMessage == null) setErrorForThisOperation = true; if (! setErrorForThisOperation) continue; // write into operation result graph Graph exceptionOperationResultGraph = MemoryGraphFactory.getInstance().openGraph(); XdiError xdiError = XdiError.findXdiError(XdiCommonRoot.findCommonRoot(exceptionOperationResultGraph), true); xdiError.setErrorString(errorString); xdiError.setErrorTimestamp(new Date()); if (log.isDebugEnabled()) log.debug("For operation " + operation + " we have exception operation result graph " + exceptionOperationResultGraph); if (operationResultGraph != null) CopyUtil.copyGraph(exceptionOperationResultGraph, operationResultGraph, null); else this.operationResultGraphs.put(operation, exceptionOperationResultGraph); } } /* * Object methods */ @Override public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("" + this.operationResultGraphs + " / "); buffer.append("" + this.ex); return buffer.toString(); } }