/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* bstefanescu
*/
package org.eclipse.ecr.automation;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ecr.core.api.CoreSession;
import org.eclipse.ecr.runtime.api.Framework;
import org.eclipse.ecr.runtime.transaction.TransactionHelper;
/**
* An operation context. Holds context objects, a context parameters map and a
* list of operations to run.
* <p>
* Context objects are:
* <ul>
* <li>The Operation Chain Input - optional. It will be used as the input for
* the first operation in the chain. If input is null then only VOID methods in
* the first operation will be matched.
* <li>A Core Session - which is optional and should be provided by the caller.
* (either at creation time as a constructor argument, either using the
* {@link #setCoreSession(CoreSession)} method. When running the operation chain
* in asynchronous mode another session will be created by preserving the
* current session credentials.
* </ul>
* <p>
* Each entry in the operation list contains the ID of the operation to be run
* and a map of operation parameters to use when initializing the operation.
* <p>
* The context parameters map can be filled with contextual information by the
* caller. Each operation will be able to access the contextual data at runtime
* and to update it if needed.
*
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class OperationContext extends HashMap<String, Object> {
private static final Log log = LogFactory.getLog(OperationContext.class);
private static final long serialVersionUID = 2944230823597903715L;
/**
* Whether to save the session at the end of the chain execution. The
* default is true.
*/
protected boolean commit = true;
protected final transient List<CleanupHandler> cleanupHandlers;
/**
* Each stack use a key the type of the objects in the stack: document,
* documents, blob or blobs
*/
protected final transient Map<String, List<Object>> stacks;
/**
* A logins stack manage multiple logins and sessions in a single chain
* execution
*/
protected transient LoginStack loginStack;
/**
* The execution input that will be updated after an operation run with the
* operation output
*/
protected Object input;
/**
* A list of trace messages useful to use in exception details.
*/
protected List<String> trace;
public OperationContext() {
this(null);
}
public OperationContext(CoreSession session) {
stacks = new HashMap<String, List<Object>>();
cleanupHandlers = new ArrayList<CleanupHandler>();
loginStack = new LoginStack(session);
trace = new ArrayList<String>();
}
public void setCoreSession(CoreSession session) {
this.loginStack.setSession(session);
}
public void setCommit(boolean commit) {
this.commit = commit;
}
public boolean isCommit() {
return commit;
}
public CoreSession getCoreSession() {
return loginStack.getSession();
}
public LoginStack getLoginStack() {
return loginStack;
}
public Principal getPrincipal() {
CoreSession session = loginStack.getSession();
return session != null ? session.getPrincipal() : null;
}
public void setInput(Object input) {
this.input = input;
}
public Object getInput() {
return input;
}
public Object peek(String type) {
List<Object> stack = stacks.get(type);
if (stack == null) {
return null;
}
return stack.isEmpty() ? null : stack.get(stack.size() - 1);
}
public void push(String type, Object obj) {
List<Object> stack = stacks.get(type);
if (stack == null) {
stack = new ArrayList<Object>();
stacks.put(type, stack);
}
stack.add(obj);
}
public Object pop(String type) {
List<Object> stack = stacks.get(type);
if (stack == null) {
return null;
}
return stack.isEmpty() ? null : stack.remove(stack.size() - 1);
}
public Object pull(String type) {
List<Object> stack = stacks.get(type);
if (stack == null) {
return null;
}
return stack.isEmpty() ? null : stack.remove(0);
}
public <T> T getAdapter(Class<T> type) {
if (type.isAssignableFrom(getClass())) {
return type.cast(this);
} else if (type.isAssignableFrom(CoreSession.class)) {
return type.cast(getCoreSession());
} else if (type.isAssignableFrom(Principal.class)) {
return type.cast(getPrincipal());
} else { // try nuxeo services
try {
return Framework.getService(type);
} catch (Exception e) {
throw new RuntimeException("Failed to lookup service: " + type,
e);
}
}
}
public void addTrace(String trace) {
this.trace.add(trace);
}
public List<String> getTrace() {
return trace;
}
public String getFormattedTrace() {
String crlf = System.getProperty("line.separator");
StringBuilder buf =new StringBuilder();
for (String t: trace) {
buf.append("> ").append(t).append(crlf);
}
return buf.toString();
}
public void addCleanupHandler(CleanupHandler handler) {
cleanupHandlers.add(handler);
}
public void removeCleanupHandler(CleanupHandler handler) {
cleanupHandlers.remove(handler);
}
public void dispose() throws OperationException {
trace.clear();
loginStack.clear();
for (CleanupHandler handler : cleanupHandlers) {
try {
handler.cleanup();
} catch (Exception e) {
log.error(e, e);
}
}
}
/**
* Set the rollback mark on the current tx. This will cause the transaction to rollback.
* Also this is setting the session commit flag on false
*/
public void setRollback() {
setCommit(false);
TransactionHelper.setTransactionRollbackOnly();
}
}