/*
* 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;
}
}
}