/* * 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.core.impl; import java.util.List; import java.util.Map; import org.eclipse.ecr.automation.AutomationService; import org.eclipse.ecr.automation.CompiledChain; import org.eclipse.ecr.automation.ExitException; import org.eclipse.ecr.automation.InvalidChainException; import org.eclipse.ecr.automation.OperationContext; import org.eclipse.ecr.automation.OperationException; import org.eclipse.ecr.automation.OperationParameters; import org.eclipse.ecr.runtime.api.Framework; /** * An operation invocation chain. The chain is immutable (cannot be modified * after it was built). To create a new chain from a description call the * static method: {@link #buildChain(AutomationService, Class, List)} This is a * self contained object - once built it can be used at any time to invoke the * operations in the chain. * * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ class CompiledChainImpl implements CompiledChain { protected AutomationService service; protected final OperationTypeImpl op; protected final Map<String, Object> args; // argument references protected InvokableMethod method; protected CompiledChainImpl next; CompiledChainImpl(OperationTypeImpl op, Map<String, Object> args) { this(null, op, args); } CompiledChainImpl(CompiledChainImpl parent, OperationTypeImpl op, Map<String, Object> args) { if (parent != null) { parent.next = this; } this.op = op; this.args = args; } public final InvokableMethod method() { return method; } public final Map<String, Object> args() { return args; } /** * Compute the best matching path to perform the chain of operations. The * path is computed using a backtracking algorithm. */ public boolean initializePath(Class<?> in) { InvokableMethod[] methods = op.getMethodsMatchingInput(in); if (methods == null) { return false; } if (next == null) { method = methods[0]; return true; } for (InvokableMethod m : methods) { Class<?> nextIn = m.getOutputType(); if (nextIn == Void.TYPE) { // a control operation nextIn = in; // preserve last input } if (next.initializePath(nextIn)) { method = m; return true; } } return false; } public Object invoke(OperationContext ctx) throws OperationException { try { return doInvoke(ctx); } catch (ExitException e) { if (e.isRollback()) { ctx.setRollback(); } return ctx.getInput(); } catch (OperationException e) { if (e.isRollback()) { ctx.setRollback(); } throw e; } } protected Object doInvoke(OperationContext ctx) throws OperationException { // add debug info ctx.addTrace(method.op.getId() + ":" + method.method.getName()); // invoke method Object out = method.invoke(ctx, args); ctx.setInput(out); if (next != null) { return next.invoke(ctx); } else { return out; } } public static CompiledChainImpl buildChain(Class<?> in, OperationParameters[] params) throws Exception { return buildChain(Framework.getLocalService(AutomationService.class), in, params); } public static CompiledChainImpl buildChain(AutomationService service, Class<?> in, OperationParameters[] chainParams) throws Exception { if (chainParams.length == 0) { throw new InvalidChainException("Null operation chain."); } OperationParameters params = chainParams[0]; CompiledChainImpl invocation = new CompiledChainImpl( (OperationTypeImpl) service.getOperation(params.id()), params.map()); CompiledChainImpl last = invocation; for (int i = 1; i < chainParams.length; i++) { params = chainParams[i]; last = new CompiledChainImpl(last, (OperationTypeImpl) service.getOperation(params.id()), params.map()); } // find the best matching path in the chain if (!invocation.initializePath(in)) { throw new InvalidChainException( "Cannot find any valid path in operation chain"); } return invocation; } }