001/* 002 * Copyright 2010 Anyware Services 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 */ 016package org.ametys.plugins.workflow.cocoon; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.avalon.framework.activity.Initializable; 025import org.apache.avalon.framework.parameters.Parameters; 026import org.apache.cocoon.acting.ServiceableAction; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Redirector; 029import org.apache.cocoon.environment.Request; 030import org.apache.cocoon.environment.SourceResolver; 031import org.apache.commons.lang3.StringUtils; 032 033import org.ametys.core.authentication.AuthenticateAction; 034import org.ametys.core.cocoon.ActionResultGenerator; 035import org.ametys.plugins.workflow.AbstractWorkflowComponent; 036import org.ametys.plugins.workflow.component.CheckRightsCondition; 037import org.ametys.plugins.workflow.support.WorkflowProvider; 038import org.ametys.runtime.parameter.Errors; 039 040import com.opensymphony.workflow.InvalidActionException; 041import com.opensymphony.workflow.InvalidInputException; 042import com.opensymphony.workflow.WorkflowException; 043 044/** 045 * Abstract action for managing a workflow instance. 046 * The following parameters are supported: 047 * <dl> 048 * <dt>actionId 049 * <dd>the id of the action to fire 050 * </dl> 051 */ 052public abstract class AbstractWorkflowAction extends ServiceableAction implements Initializable 053{ 054 /** Workflow provider */ 055 protected WorkflowProvider _workflowProvider; 056 057 @Override 058 public void initialize() throws Exception 059 { 060 _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE); 061 } 062 063 @Override 064 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 065 { 066 int actionId = _getActionId(objectModel, source, parameters); 067 Map inputs = _getInputs(redirector, objectModel, source, parameters); 068 069 try 070 { 071 Map result = _act(redirector, objectModel, source, parameters, actionId, inputs); 072 073 @SuppressWarnings("unchecked") 074 Map<String, Object> resultMap = (Map<String, Object>) inputs.get(AbstractWorkflowComponent.RESULT_MAP_KEY); 075 resultMap.put("success", true); 076 077 return result; 078 } 079 catch (InvalidActionException e) 080 { 081 return _processInvalidActionException(redirector, objectModel, source, parameters, actionId, inputs, e); 082 } 083 catch (WorkflowException e) 084 { 085 return _processWorkflowException(redirector, objectModel, source, parameters, actionId, e); 086 } 087 } 088 089 /** 090 * Provide the action id to use. 091 * Default implementation uses parameter <code>actionId</code>. 092 * @param objectModel the current object model. 093 * @param source the current source. 094 * @param parameters the current parameters. 095 * @return the action id. 096 * @throws Exception if an error occurs. 097 */ 098 protected int _getActionId(Map objectModel, String source, Parameters parameters) throws Exception 099 { 100 return parameters.getParameterAsInteger("actionId", Integer.MIN_VALUE); 101 } 102 103 /** 104 * Provide the inputs to use. 105 * Default implementation provide the redirector. 106 * @param redirector the redirector. 107 * @param objectModel the current object model. 108 * @param source the current source. 109 * @param parameters the current parameters. 110 * @return the inputs to use. 111 * @throws Exception if an error occurs. 112 */ 113 protected Map<String, Object> _getInputs(Redirector redirector, Map objectModel, String source, Parameters parameters) throws Exception 114 { 115 Map<String, Object> inputs = new HashMap<>(); 116 inputs.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, objectModel.get(ObjectModelHelper.PARENT_CONTEXT)); 117 inputs.put(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY, new ArrayList<String>()); 118 119 Request request = ObjectModelHelper.getRequest(objectModel); 120 if (request.getAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_INTERNAL_ALLOWED) != null) 121 { 122 inputs.put(CheckRightsCondition.FORCE, true); 123 } 124 125 // Provide a map for providing data to the generator 126 Map<String, Object> result = new HashMap<>(); 127 request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, result); 128 inputs.put(AbstractWorkflowComponent.RESULT_MAP_KEY, result); 129 130 return inputs; 131 } 132 133 /** 134 * Initialize or act on a workflow instance. 135 * @param redirector the redirector. 136 * @param objectModel the current object model. 137 * @param source the current source. 138 * @param parameters the current parameters. 139 * @param actionId the action id to use. 140 * @param inputs the inputs to use 141 * @return the action result. 142 * @throws InvalidInputException if the action id is not valid. 143 * @throws WorkflowException if the action failed. 144 */ 145 protected abstract Map _act(Redirector redirector, Map objectModel, String source, Parameters parameters, int actionId, Map inputs) throws InvalidInputException, WorkflowException; 146 147 /** 148 * Called when the current action is invalid. 149 * Default implementation throw an exception. 150 * @param redirector the redirector. 151 * @param objectModel the current object model. 152 * @param source the current source. 153 * @param parameters the current parameters. 154 * @param actionId the invalid action id. 155 * @param inputs the inputs 156 * @param e the invalid action exception. 157 * @return the action result. 158 * @throws Exception in order to stop the current pipeline. 159 */ 160 protected Map _processInvalidActionException(Redirector redirector, Map objectModel, String source, Parameters parameters, long actionId, Map inputs, InvalidActionException e) throws Exception 161 { 162 if (_getWorkflowErrors(inputs) != null) 163 { 164 // Errors was traited in result map 165 getLogger().error(String.format("Invalid action: '%d' for %s. %s", actionId, _getExceptionContext(objectModel, source, parameters), _getFailConditions(inputs)), e); 166 return Collections.EMPTY_MAP; 167 } 168 else 169 { 170 // The error was not traited 171 throw new Exception(String.format("Invalid action: '%d' for %s. %s", actionId, _getExceptionContext(objectModel, source, parameters), _getFailConditions(inputs)), e); 172 } 173 } 174 175 /** 176 * Get the fail conditions as a String 177 * @param inputs the inputs 178 * @return the fail conditions 179 */ 180 protected String _getFailConditions (Map inputs) 181 { 182 if (inputs.containsKey(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY)) 183 { 184 @SuppressWarnings("unchecked") 185 List<String> failConditions = (List<String>) inputs.get(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY); 186 if (failConditions.size() > 0) 187 { 188 return String.format("Fail conditions are: %s", StringUtils.join(failConditions, " / ")); 189 } 190 } 191 192 return ""; 193 } 194 195 /** 196 * Get the workflow errors 197 * @param inputs The inputs 198 * @return The workflow errors or null if not found 199 */ 200 protected Errors _getWorkflowErrors (Map inputs) 201 { 202 @SuppressWarnings("unchecked") 203 Map<String, Object> result = (Map<String, Object>) inputs.get(AbstractWorkflowComponent.RESULT_MAP_KEY); 204 205 if (result.containsKey(AbstractWorkflowComponent.WORKFLOW_ERRORS_KEY)) 206 { 207 return (Errors) result.get(AbstractWorkflowComponent.WORKFLOW_ERRORS_KEY); 208 } 209 210 return null; 211 } 212 213 /** 214 * Get the workflow warnings 215 * @param inputs The inputs 216 * @return The workflow warnings or null if not found 217 */ 218 protected Errors _getWorkflowWarns (Map inputs) 219 { 220 @SuppressWarnings("unchecked") 221 Map<String, Object> result = (Map<String, Object>) inputs.get(AbstractWorkflowComponent.RESULT_MAP_KEY); 222 223 if (result.containsKey(AbstractWorkflowComponent.WORKFLOW_WARNS_KEY)) 224 { 225 return (Errors) result.get(AbstractWorkflowComponent.WORKFLOW_WARNS_KEY); 226 } 227 228 return null; 229 } 230 231 /** 232 * Called when the current action has thrown a {@link WorkflowException}. 233 * Default implementation throw an exception. 234 * @param redirector the redirector. 235 * @param objectModel the current object model. 236 * @param source the current source. 237 * @param parameters the current parameters. 238 * @param actionId the invalid action id. 239 * @param e the workflow exception. 240 * @return the action result. 241 * @throws Exception in order to stop the current pipeline. 242 */ 243 protected Map _processWorkflowException(Redirector redirector, Map objectModel, String source, Parameters parameters, long actionId, WorkflowException e) throws Exception 244 { 245 throw new Exception(String.format("Unable to perform action: '%d' for %s", actionId, _getExceptionContext(objectModel, source, parameters)), e); 246 } 247 248 /** 249 * Provides the exception context for a more friendly error message. 250 * @param objectModel the current object model. 251 * @param source the current source. 252 * @param parameters the current parameters. 253 * @return the message describing the exception context. 254 */ 255 protected abstract String _getExceptionContext(Map objectModel, String source, Parameters parameters); 256}