/* * Copyright (c) 2009-2012 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.InvalidActionException; import org.eurekastreams.commons.server.service.ActionController; import org.eurekastreams.server.persistence.mappers.cache.Transformer; import org.springframework.beans.factory.BeanFactory; 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 final BeanFactory beanFactory; /** Instance of {@link ActionController} used within this executor. */ private final ActionController serviceActionController; /** Principal populator. */ private final PrincipalPopulator principalPopulator; /** Persistent bean manager for serialization. */ private final PersistentBeanManager persistentBeanManager; /** Prepares exceptions for returning to the client. */ private final Transformer<Exception, Exception> exceptionSanitizer; /** * Constructor. * * @param inBeanFactory * The context from which this service can load action beans. * @param inServiceActionController * Instance of {@link ActionController} used within this executor. * @param inPrincipalPopulator * Principal populator. * @param inPersistentBeanManager * Persistent bean manager for serialization. * @param inExceptionSanitizer * Prepares exceptions for returning to the client. */ public ActionExecutor(final BeanFactory inBeanFactory, final ActionController inServiceActionController, final PrincipalPopulator inPrincipalPopulator, final PersistentBeanManager inPersistentBeanManager, final Transformer<Exception, Exception> inExceptionSanitizer) { beanFactory = inBeanFactory; serviceActionController = inServiceActionController; principalPopulator = inPrincipalPopulator; persistentBeanManager = inPersistentBeanManager; exceptionSanitizer = inExceptionSanitizer; } /** * Execute method for the class. * * @param actionRequest * the action to execute. * @param userDetails * the user details for this action request. * @return The result as a serializable object. */ @SuppressWarnings("unchecked") public ActionRequest execute(final ActionRequest actionRequest, final UserDetails userDetails) { log.debug("Starting Action: " + actionRequest.getActionKey()); String userName = getUserName(userDetails); try { Object springBean = beanFactory.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 InvalidActionException("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 GWT-serializable are returned (otherwise no response will be // returned to the client) actionRequest.setResponse(exceptionSanitizer.transform(ex)); } // discard the params, since the client already has them actionRequest.setParam(null); log.debug("Finished Action: " + actionRequest.getActionKey()); return actionRequest; } /** * Helper for getting userName. * * @param userDetails * User details. * @return user name. */ private String getUserName(final UserDetails userDetails) { try { return userDetails.getUsername(); } catch (Exception e) { return "unavailable"; } } }