001/*
002 * Copyright (c) 2006-2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     bstefanescu
011 *     Vladimir Pasquier <vpasquier@nuxeo.com>
012 */
013package org.nuxeo.ecm.automation.core.operations.execution;
014
015import java.util.Arrays;
016import java.util.Collection;
017import java.util.HashMap;
018import java.util.Map;
019
020import org.nuxeo.ecm.automation.AutomationService;
021import org.nuxeo.ecm.automation.OperationContext;
022import org.nuxeo.ecm.automation.OperationException;
023import org.nuxeo.ecm.automation.core.Constants;
024import org.nuxeo.ecm.automation.core.annotations.Context;
025import org.nuxeo.ecm.automation.core.annotations.Operation;
026import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
027import org.nuxeo.ecm.automation.core.annotations.Param;
028import org.nuxeo.ecm.automation.core.util.Properties;
029import org.nuxeo.ecm.core.api.CoreSession;
030import org.nuxeo.ecm.core.api.DocumentModel;
031
032/**
033 * Run an embedded operation chain using the current input. The output is undefined (Void)
034 *
035 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
036 * @since 5.5
037 */
038@Operation(id = RunOperationOnList.ID, category = Constants.CAT_SUBCHAIN_EXECUTION, label = "Run For Each", description = "Run an operation for each element from the list defined by the 'list' parameter. The 'list' parameter is pointing to a context variable that represents the list which will be iterated. The 'item' parameter represents the name of the context variable which will point to the current element in the list at each iteration. You can use the 'isolate' parameter to specify whether or not the evalution context is the same as the parent context or a copy of it. If the 'isolate' parameter is 'true' then a copy of the current context is used and so that modifications in this context will not affect the parent context. Any input is accepted. The input is returned back as output when operation terminates. The 'parameters' injected are accessible in the subcontext ChainParameters. For instance, @{ChainParameters['parameterKey']}.", aliases = { "Context.RunOperationOnList" })
039public class RunOperationOnList {
040
041    public static final String ID = "RunOperationOnList";
042
043    @Context
044    protected OperationContext ctx;
045
046    @Context
047    protected AutomationService service;
048
049    @Context
050    protected CoreSession session;
051
052    @Param(name = "id")
053    protected String chainId;
054
055    @Param(name = "list")
056    protected String listName;
057
058    @Param(name = "item", required = false, values = "item")
059    protected String itemName = "item";
060
061    @Param(name = "isolate", required = false, values = "true")
062    protected boolean isolate = true;
063
064    @Param(name = "parameters", description = "Accessible in the subcontext " + "ChainParameters. For instance, "
065            + "@{ChainParameters['parameterKey']}.", required = false)
066    protected Properties chainParameters;
067
068    /**
069     * @since 6.0 Define if the chain in parameter should be executed in new transaction.
070     */
071    @Param(name = "newTx", required = false, values = "false", description = "Define if the chain in parameter should be "
072            + "executed in new transaction.")
073    protected boolean newTx = false;
074
075    /**
076     * @since 6.0 Define transaction timeout (default to 60 sec).
077     */
078    @Param(name = "timeout", required = false, description = "Define " + "transaction timeout (default to 60 sec).")
079    protected Integer timeout = 60;
080
081    /**
082     * @since 6.0 Define if transaction should rollback or not (default to true).
083     */
084    @Param(name = "rollbackGlobalOnError", required = false, values = "true", description = "Define if transaction should rollback or not "
085            + "(default to true)")
086    protected boolean rollbackGlobalOnError = true;
087
088    @OperationMethod
089    @SuppressWarnings("unchecked")
090    public void run() throws OperationException {
091        // Handle isolation option
092        Map<String, Object> vars = isolate ? new HashMap<>(ctx.getVars()) : ctx.getVars();
093        OperationContext subctx = ctx.getSubContext(isolate, ctx.getInput());
094
095        // Running chain/operation for each list elements
096        Collection<?> list = null;
097        if (ctx.get(listName) instanceof Object[]) {
098            list = Arrays.asList((Object[]) ctx.get(listName));
099        } else if (ctx.get(listName) instanceof Collection<?>) {
100            list = (Collection<?>) ctx.get(listName);
101        } else {
102            throw new UnsupportedOperationException(ctx.get(listName).getClass() + " is not a Collection");
103        }
104        for (Object value : list) {
105            subctx.put(itemName, value);
106            // Running chain/operation
107            if (newTx) {
108                service.runInNewTx(subctx, chainId, chainParameters, timeout, rollbackGlobalOnError);
109            } else {
110                service.run(subctx, chainId, (Map) chainParameters);
111            }
112        }
113
114        // reconnect documents in the context
115        if (!isolate) {
116            for (String varName : vars.keySet()) {
117                if (!ctx.getVars().containsKey(varName)) {
118                    ctx.put(varName, vars.get(varName));
119                } else {
120                    Object value = vars.get(varName);
121                    if (session != null && value != null && value instanceof DocumentModel) {
122                        ctx.getVars().put(varName, session.getDocument(((DocumentModel) value).getRef()));
123                    } else {
124                        ctx.getVars().put(varName, value);
125                    }
126                }
127            }
128        }
129    }
130}