/* * Copyright (c) 2009-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; import java.io.Serializable; import net.sf.gilead.core.PersistentBeanManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eurekastreams.commons.actions.context.PrincipalPopulator; import org.eurekastreams.commons.actions.context.service.ServiceActionContext; import org.eurekastreams.commons.actions.service.ServiceAction; import org.eurekastreams.commons.actions.service.TaskHandlerServiceAction; import org.eurekastreams.commons.client.ActionRequest; 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.server.service.ActionController; import org.springframework.context.ApplicationContext; import org.springframework.security.userdetails.UserDetails; /** * * The Action Executor class. This class will look up in Spring for the action key. Once the action is found, it will * confirm the action requires User Details and the User Details are present. Next, the params will be validated, the * user will be authorized, and the action executed. */ public class ActionExecutor { /** * Logger. */ private final Log log = LogFactory.getLog(ActionExecutor.class); /** * The context from which this service can load action beans. */ private ApplicationContext springContext = null; /** * The action object. */ @SuppressWarnings("unchecked") private final ActionRequest actionRequest; /** * The user details for this action request. */ private final UserDetails userDetails; /** * Persistent bean manager for serialization. */ private final PersistentBeanManager persistentBeanManager; /** * Principal Populator. */ private final PrincipalPopulator principalPopulator; /** * Instance of {@link ActionController} used within this executor. */ private final ActionController serviceActionController; /** * Constructor for the executor class. * * @param inSpringContext * the Spring application context. * @param inUserDetails * the user details for this action request. * @param inActionRequest * the action to execute. */ @SuppressWarnings("unchecked") public ActionExecutor(final ApplicationContext inSpringContext, final UserDetails inUserDetails, final ActionRequest inActionRequest) { userDetails = inUserDetails; actionRequest = inActionRequest; springContext = inSpringContext; persistentBeanManager = (PersistentBeanManager) springContext.getBean("persistentBeanManager"); principalPopulator = (PrincipalPopulator) springContext.getBean("principalPopulator"); serviceActionController = (ActionController) springContext.getBean("serviceActionController"); } /** * Execute method for the class. * * @return The result as a serializable object. */ @SuppressWarnings("unchecked") public ActionRequest execute() { log.debug("Starting Action: " + actionRequest.getActionKey()); String userName = getUserName(); try { Object springBean = springContext.getBean(actionRequest.getActionKey()); // //////////////////////////////////////////////// // actually perform the action Serializable result = null; // if you use "instanceof classname" where classname is a class and // not an interface, // the check and cast will fail // however, the check and cast works if you use an interface here if (springBean instanceof ServiceAction) { ServiceAction action = (ServiceAction) springBean; // grab serializable parameter object. Serializable actionParameter = actionRequest.getParam(); ServiceActionContext actionContext = new ServiceActionContext(actionParameter, principalPopulator .getPrincipal(userDetails.getUsername(), actionRequest.getSessionId())); actionContext.setActionId(actionRequest.getActionKey()); result = serviceActionController.execute(actionContext, action); } else if (springBean instanceof TaskHandlerServiceAction) { TaskHandlerServiceAction action = (TaskHandlerServiceAction) springBean; // grab serializable parameter object. Serializable actionParameter = actionRequest.getParam(); ServiceActionContext actionContext = new ServiceActionContext(actionParameter, principalPopulator .getPrincipal(userDetails.getUsername(), actionRequest.getSessionId())); actionContext.setActionId(actionRequest.getActionKey()); result = serviceActionController.execute(actionContext, action); } else { throw new IllegalArgumentException("Supplied bean is not an executable action."); } // ////////////////////////////////////////////// // set the results to be passed back // cloning here ensures that gilead makes all the objects // serializable. long startClone = System.currentTimeMillis(); actionRequest.setResponse((Serializable) persistentBeanManager.clone(result)); log.debug(actionRequest.getActionKey() + " gilead serialization time: " + (System.currentTimeMillis() - startClone) + " (ms)"); } catch (Exception ex) { // log the exception String paramString = "null parameters"; if (actionRequest.getParam() != null) { try { paramString = actionRequest.getParam().toString(); } catch (Exception pex) { paramString = "<error retrieving parameters: " + pex.getMessage() + ">"; } } log.error("Caught exception while running " + actionRequest.getActionKey() + " for user: " + userName + ". Parameters: " + paramString + ". ", ex); // By setting an exception as the response, we are effectively throwing the exception to the client. // But insure only exceptions which are serializable are returned (otherwise no response will be returned to // the client) Throwable response; if (ex instanceof ValidationException) { response = ex; } else if (ex instanceof AuthorizationException) { // Remove any nested exceptions response = (ex.getCause() == null) ? ex : new AuthorizationException(ex.getMessage()); } else if (ex instanceof GeneralException) { // Remove any nested exceptions (particularly want to insure no PersistenceExceptions get sent - they // are not serializable plus contain details that should not be exposed to users) response = (ex.getCause() == null) ? ex : new GeneralException(ex.getMessage()); } else if (ex instanceof ExecutionException) { // Remove any nested exceptions response = (ex.getCause() == null) ? ex : new ExecutionException(ex.getMessage()); } else { response = new GeneralException(ex.getMessage()); } actionRequest.setResponse(response); } // discard the params, since the client already has them actionRequest.setParam(null); log.debug("Finished Action: " + actionRequest.getActionKey()); return actionRequest; } /** * Helper for getting userName. * * @return user name. */ private String getUserName() { String userName = ""; try { userName = userDetails.getUsername(); } catch (Exception e) { // necessary in case getUserName() throws exception userName = "unavailable"; } return userName; } /** * @return Logger. */ protected Log getLog() { return log; } /** * @return Spring context. */ protected ApplicationContext getSpringContext() { return springContext; } /** * @return Requesting user. */ protected UserDetails getUserDetails() { return userDetails; } }