001/* 002 * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 * Vladimir Pasquier <vpasquier@nuxeo.com> 019 */ 020package org.nuxeo.ecm.automation.core.operations.execution; 021 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.HashMap; 025import java.util.Map; 026 027import org.nuxeo.ecm.automation.AutomationService; 028import org.nuxeo.ecm.automation.OperationContext; 029import org.nuxeo.ecm.automation.OperationException; 030import org.nuxeo.ecm.automation.core.Constants; 031import org.nuxeo.ecm.automation.core.annotations.Context; 032import org.nuxeo.ecm.automation.core.annotations.Operation; 033import org.nuxeo.ecm.automation.core.annotations.OperationMethod; 034import org.nuxeo.ecm.automation.core.annotations.Param; 035import org.nuxeo.ecm.automation.core.util.Properties; 036import org.nuxeo.ecm.core.api.CoreSession; 037import org.nuxeo.ecm.core.api.DocumentModel; 038 039/** 040 * Run an embedded operation chain using the current input. The output is undefined (Void) 041 * 042 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 043 * @since 5.5 044 */ 045@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 = { 046 "Context.RunOperationOnList" }) 047public class RunOperationOnList { 048 049 public static final String ID = "RunOperationOnList"; 050 051 @Context 052 protected OperationContext ctx; 053 054 @Context 055 protected AutomationService service; 056 057 @Context 058 protected CoreSession session; 059 060 @Param(name = "id") 061 protected String chainId; 062 063 @Param(name = "list") 064 protected String listName; 065 066 @Param(name = "item", required = false, values = "item") 067 protected String itemName = "item"; 068 069 @Param(name = "isolate", required = false, values = "true") 070 protected boolean isolate = true; 071 072 @Param(name = "parameters", description = "Accessible in the subcontext " + "ChainParameters. For instance, " 073 + "@{ChainParameters['parameterKey']}.", required = false) 074 protected Properties chainParameters = new Properties(); 075 076 /** 077 * @since 6.0 Define if the chain in parameter should be executed in new transaction. 078 */ 079 @Param(name = "newTx", required = false, values = "false", description = "Define if the chain in parameter should be " 080 + "executed in new transaction.") 081 protected boolean newTx = false; 082 083 /** 084 * @since 6.0 Define transaction timeout (default to 60 sec). 085 */ 086 @Param(name = "timeout", required = false, description = "Define " + "transaction timeout (default to 60 sec).") 087 protected Integer timeout = 60; 088 089 /** 090 * @since 6.0 Define if transaction should rollback or not (default to true). 091 */ 092 @Param(name = "rollbackGlobalOnError", required = false, values = "true", description = "Define if transaction should rollback or not " 093 + "(default to true)") 094 protected boolean rollbackGlobalOnError = true; 095 096 @OperationMethod 097 public void run() throws OperationException { 098 // Handle isolation option 099 Map<String, Object> vars = isolate ? new HashMap<>(ctx.getVars()) : ctx.getVars(); 100 try (OperationContext subctx = ctx.getSubContext(isolate, ctx.getInput())) { 101 102 // Running chain/operation for each list elements 103 Collection<?> list = null; 104 Object listObject = ctx.get(listName); 105 if (listObject != null) { 106 if (listObject instanceof Object[]) { 107 list = Arrays.asList((Object[]) listObject); 108 } else if (listObject instanceof Collection<?>) { 109 list = (Collection<?>) listObject; 110 } else { 111 throw new UnsupportedOperationException(listObject.getClass() + " is not a Collection"); 112 } 113 for (Object value : list) { 114 subctx.push(itemName, value); 115 // Running chain/operation 116 try { 117 if (newTx) { 118 service.runInNewTx(subctx, chainId, chainParameters, timeout, rollbackGlobalOnError); 119 } else { 120 service.run(subctx, chainId, chainParameters); 121 } 122 } finally { 123 subctx.pop(itemName); 124 } 125 } 126 } 127 128 // reconnect documents in the context 129 if (!isolate) { 130 for (String varName : vars.keySet()) { 131 if (!ctx.getVars().containsKey(varName)) { 132 ctx.put(varName, vars.get(varName)); 133 } else { 134 Object value = vars.get(varName); 135 if (session != null && value != null && value instanceof DocumentModel) { 136 ctx.getVars().put(varName, session.getDocument(((DocumentModel) value).getRef())); 137 } else { 138 ctx.getVars().put(varName, value); 139 } 140 } 141 } 142 } 143 } 144 } 145 146}