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}