/* * Copyright (c) 2010 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.server.service; import java.io.Serializable; import java.util.ArrayList; import java.util.Map.Entry; import org.apache.commons.logging.Log; import org.eurekastreams.commons.actions.TaskHandlerAction; import org.eurekastreams.commons.actions.context.TaskHandlerActionContext; import org.eurekastreams.commons.actions.context.service.ServiceActionContext; import org.eurekastreams.commons.actions.service.ServiceAction; import org.eurekastreams.commons.exceptions.AuthorizationException; import org.eurekastreams.commons.exceptions.ExecutionException; import org.eurekastreams.commons.exceptions.GeneralException; import org.eurekastreams.commons.exceptions.ValidationException; import org.eurekastreams.commons.logging.LogFactory; import org.eurekastreams.commons.server.UserActionRequest; import org.eurekastreams.commons.task.TaskHandler; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; /** * This class provides the business logic that controls the execution of a {@link ServiceAction}. * * Transaction management, and Validation, Authorization, and Execution strategy sequences are also controlled in here. * * In the case of a {@link TaskHandlerAction}, the List of UserActionRequest objects are collected and submitted through * the action. This is necessary, because UserActionRequest submissions to the queue through JMS is currently not * covered under the configured transaction strategy. When distributed transactions are available within EurekaStreams * this behavior will change. * * The order of execution for the strategies contained within the {@link ServiceAction} is controlled by this class and * executed in the following order: * * ValidationStrategy AuthorizationStrategy ExecutionStrategy * * Optionally: * * submitAsyncRequests will be called in the case of a {@link TaskHandlerAction}. * * Exception handling also occurs within this controller. - {@link GeneralException} will be thrown when an unwrapped * exception is encountered. - {@link ValidationException} will be logged and passed to the client when encountered. - * {@link AuthorizationException} will be logged and passed to the client when encountered. - {@link ExecutionException} * will be logged and passed to the client when encountered. * */ public class ServiceActionController implements ActionController { /** * Local instance of the logger. */ private final Log logger = LogFactory.make(); /** * Instance of the configured platform transaction manager. */ private final PlatformTransactionManager transMgr; /** * Constructor. * * @param inTransMgr * - instance of the {@link PlatformTransactionManager} configured for this controller. */ public ServiceActionController(final PlatformTransactionManager inTransMgr) { transMgr = inTransMgr; } /** * Execute the supplied {@link ServiceAction} with the given {@link ServiceActionContext}. * * @param inServiceActionContext * - instance of the {@link ServiceActionContext} with which to execution the {@link ServiceAction}. * @param inServiceAction * - instance of the {@link ServiceAction} to execute. * @return - results from the execution of the ServiceAction. * * - GeneralException - when an unexpected error occurs. - ValidationException - when a * {@link ValidationException} occurs. - AuthorizationException - when an {@link AuthorizationException} * occurs. - ExecutionException - when an {@link ExecutionException} occurs. */ public Serializable execute(final ServiceActionContext inServiceActionContext, final ServiceAction inServiceAction) { Serializable results = null; DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); transDef.setName(inServiceAction.toString()); transDef.setReadOnly(inServiceAction.isReadOnly()); TransactionStatus transStatus = transMgr.getTransaction(transDef); try { inServiceAction.getValidationStrategy().validate(inServiceActionContext); inServiceAction.getAuthorizationStrategy().authorize(inServiceActionContext); results = inServiceAction.getExecutionStrategy().execute(inServiceActionContext); transMgr.commit(transStatus); } catch (ValidationException vex) { onException(transStatus); logger.warn("Validation failed for the current action.", vex); for (Entry<String, String> currentError : vex.getErrors().entrySet()) { logger.warn("Validation key: " + currentError.getKey() + ", value: " + currentError.getValue()); } throw vex; } catch (AuthorizationException aex) { onException(transStatus); logger.warn("Authorization failed for the current action.", aex); throw aex; } catch (ExecutionException eex) { onException(transStatus); logger.error("Error occurred during execution.", eex); throw eex; } catch (Exception ex) { onException(transStatus); logger.error("Error occurred performing transaction.", ex); throw new GeneralException(ex); } return results; } /** * This method executes a {@link TaskHandlerAction} with the supplied {@link ServiceActionContext}. * * @param inServiceActionContext * - instance of the {@link ServiceActionContext} associated with this request. * @param inTaskHandlerAction * - instance of the {@link TaskHandlerAction}. * @return - results of the execution. * * - GeneralException - when an unexpected error occurs. - ValidationException - when a * {@link ValidationException} occurs. - AuthorizationException - when an {@link AuthorizationException} * occurs. - ExecutionException - when an {@link ExecutionException} occurs. */ public Serializable execute(final ServiceActionContext inServiceActionContext, final TaskHandlerAction inTaskHandlerAction) { Serializable results = null; DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); transDef.setName(inTaskHandlerAction.toString()); transDef.setReadOnly(inTaskHandlerAction.isReadOnly()); TransactionStatus transStatus = transMgr.getTransaction(transDef); TaskHandlerActionContext<ServiceActionContext> taskHandlerContext = // \n new TaskHandlerActionContext<ServiceActionContext>(inServiceActionContext, new ArrayList<UserActionRequest>()); try { inTaskHandlerAction.getValidationStrategy().validate(inServiceActionContext); inTaskHandlerAction.getAuthorizationStrategy().authorize(inServiceActionContext); results = inTaskHandlerAction.getExecutionStrategy().execute(taskHandlerContext); transMgr.commit(transStatus); } catch (ValidationException vex) { onException(transStatus); logger.warn("Validation failed for the current action.", vex); for (Entry<String, String> currentError : vex.getErrors().entrySet()) { logger.warn("Validation key: " + currentError.getKey() + ", value: " + currentError.getValue()); } throw vex; } catch (AuthorizationException aex) { onException(transStatus); logger.warn("Authorization failed for the current action.", aex); throw aex; } catch (ExecutionException eex) { onException(transStatus); logger.error("Error occurred during execution.", eex); throw eex; } catch (Exception ex) { onException(transStatus); logger.error("Error occurred performing transaction.", ex); throw new GeneralException(ex); } // Submit the TaskRequests gathered from the execution strategy into the TaskHandlerContext to the TaskHandler. try { TaskHandler currentTaskHandler = inTaskHandlerAction.getTaskHandler(); for (UserActionRequest currentRequest : taskHandlerContext.getUserActionRequests()) { currentTaskHandler.handleTask(currentRequest); } } catch (Exception ex) { logger.error("Error occurred posting UserActionRequests to the queue.", ex); throw (new GeneralException("Error occurred posting UserActionRequests to the queue.", ex)); } return results; } /** * Helper class that encapsulates transaction rollback when an exception is encountered. * * @param inTransStatus * - instance of the {@link TransactionStatus} to go along with the currect block. */ private void onException(final TransactionStatus inTransStatus) { if (!inTransStatus.isCompleted()) { transMgr.rollback(inTransStatus); } // TODO: Perform undo operations here. } }