/* * Copyright (c) 2011 Lockheed Martin Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.eurekastreams.commons.actions; import java.io.Serializable; import java.util.Map.Entry; import org.eurekastreams.commons.actions.context.ActionContext; import org.eurekastreams.commons.actions.context.Principal; import org.eurekastreams.commons.actions.context.PrincipalActionContext; import org.eurekastreams.commons.actions.context.TaskHandlerActionContext; import org.eurekastreams.commons.actions.context.TaskHandlerActionContextImpl; import org.eurekastreams.commons.actions.context.service.ServiceActionContext; import org.eurekastreams.commons.exceptions.AuthorizationException; import org.eurekastreams.commons.exceptions.ExecutionException; import org.eurekastreams.commons.exceptions.InvalidActionException; import org.eurekastreams.commons.exceptions.ValidationException; import org.eurekastreams.commons.logging.LogFactory; import org.eurekastreams.commons.task.TaskHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class which allows actions to be executed synchronously from other actions. (The various controllers cannot be used * to do this, since 1) they set up a transaction block, but we are already within a transaction, and 2) they handle * queued action requests within the call, but we want any queued action requests generated by the child action to be * handled with those generated by the parent action.) * * Note that the task handler specified by a TaskHandler action is not honored -- since queued action requests are * placed in the parent action's request list, it is the parent action's task handler that is used for them. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class InlineActionExecutor { /** Log. */ private final Logger log = LoggerFactory.getLogger(LogFactory.getClassName()); /** If which action to run is supplied from an external source. */ private final boolean externalActionSelection; /** * Constructor. * * @param inExternalActionSelection * If which action to run is supplied from an external source. */ public InlineActionExecutor(final boolean inExternalActionSelection) { externalActionSelection = inExternalActionSelection; } /** * Executes a non-TaskHandler action. * * @param action * Action to execute. * @param context * Parent action's context. * @param params * Parameters for the action. * @return Return value from execution strategy. */ public Serializable execute(final Action action, final TaskHandlerActionContext< ? extends ActionContext> context, final Serializable params) { // So we don't have to repeat the execution logic, adapt non-task-handler actions to task-handler actions then // execute them as task-handler actions. This also requires adapting the plain execution strategy to a // task-handler execution strategy. return execute(new TaskHandlerAction() { public ValidationStrategy getValidationStrategy() { return action.getValidationStrategy(); } public AuthorizationStrategy getAuthorizationStrategy() { return action.getAuthorizationStrategy(); } public TaskHandlerExecutionStrategy getExecutionStrategy() { return new TaskHandlerExecutionStrategy<ActionContext>() { public Serializable execute(final TaskHandlerActionContext<ActionContext> inActionContext) { return action.getExecutionStrategy().execute(inActionContext.getActionContext()); } }; } public boolean isReadOnly() { return action.isReadOnly(); } public TaskHandler getTaskHandler() { return null; } }, context, params); } /** * Executes a TaskHandler action. * * @param action * Action to execute. * @param context * Parent action's context. * @param params * Parameters for the action. * @return Return value from execution strategy. */ public Serializable execute(final TaskHandlerAction action, final TaskHandlerActionContext< ? extends ActionContext> context, final Serializable params) { // ---- determine if action is OK to be run ---- // For now, consider any action without an authorizer (which presently are the AsyncActions) as inappropriate to // execute where an external source supplies the name of the action to execute. // In the future, have a SystemOnly property on actions that would define whether they can be executed where an // external source supplies the name of the action to execute. For non-SystemOnly authorizer-less actions, use // a controller-supplied default authorizer (probably isSystemAdmin). if (externalActionSelection && action.getAuthorizationStrategy() == null) { throw new InvalidActionException( "Action must have an authorizer to run when external action selection is allowed."); } // Since there's no good way to tell if an action actually needs/uses a principal (just because it's a // ServiceAction doesn't mean it actually does anything with a principal), the check to prevent // principal-requiring actions from running where there is no principal has been omitted. // In the future, have a PrincipalRequired property on actions, and check whether a principal was provided. // ---- run ---- try { Principal principal = null; ActionContext innerContext = context.getActionContext(); boolean sourceHasPrincipal = innerContext instanceof PrincipalActionContext; if (sourceHasPrincipal) { principal = ((PrincipalActionContext) innerContext).getPrincipal(); } PrincipalActionContext childContext = new ServiceActionContext(params, principal); action.getValidationStrategy().validate(childContext); if (sourceHasPrincipal && action.getAuthorizationStrategy() != null) { action.getAuthorizationStrategy().authorize(childContext); } return action.getExecutionStrategy().execute( new TaskHandlerActionContextImpl(childContext, context.getUserActionRequests())); } catch (ValidationException ex) { log.warn("Validation failed for the child action.", ex); for (Entry<String, String> currentError : ex.getErrors().entrySet()) { log.warn("Validation key: {}, value: {}", currentError.getKey(), currentError.getValue()); } throw ex; } catch (AuthorizationException ex) { log.warn("Authorization failed for the child action.", ex); throw ex; } catch (ExecutionException ex) { log.error("Error occurred during child action execution.", ex); throw ex; } } }