package org.toobsframework.pres.doit; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Collection; import java.util.ArrayList; import javax.servlet.http.Cookie; import javax.servlet.http.HttpSession; import org.apache.commons.jxpath.JXPathContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.validation.Errors; import org.toobsframework.pres.component.config.Parameter; import org.toobsframework.pres.doit.config.Action; import org.toobsframework.pres.doit.config.Actions; import org.toobsframework.pres.doit.config.DoIt; import org.toobsframework.biz.validation.IValidator; import org.toobsframework.data.IObjectLoader; import org.toobsframework.data.beanutil.BeanMonkey; import org.toobsframework.exception.ValidationException; import org.toobsframework.pres.component.datasource.api.IDataSource; import org.toobsframework.pres.util.ComponentRequestManager; import org.toobsframework.pres.util.ParameterUtil; import org.toobsframework.pres.util.PresConstants; import org.toobsframework.search.index.ISingleIndexBuilder; import org.toobsframework.util.constants.PlatformConstants; @SuppressWarnings("unchecked") public class DoItRunner implements IDoItRunner { private static Log log = LogFactory.getLog(DoItRunner.class); private ComponentRequestManager componentRequestManager; private ISingleIndexBuilder indexBuilder; public void runDoIt(DoIt doIt, Map paramMap, Map responseMap) throws Exception { // Run Actions Action thisAction = null; if(doIt.getActions() != null) { Actions actionsObj = doIt.getActions(); String multipleActionsKey = "defaultAction"; String[] multipleActionsAry = new String[] {""}; try { if(actionsObj.getMultipleActionsKey() != null && !actionsObj.getMultipleActionsKey().equals("")) { multipleActionsKey = actionsObj.getMultipleActionsKey(); Object multActObj = paramMap.get(actionsObj.getMultipleActionsKey()); if (multActObj != null && multActObj.getClass().isArray()) { multipleActionsAry = (String[])multActObj; } else if (multActObj != null) { multipleActionsAry = new String[] {(String)multActObj}; } else { multipleActionsAry = new String[] {""}; } } for(int i = 0; i < multipleActionsAry.length; i++) { HashMap actionParams = new HashMap(paramMap); responseMap.clear(); actionParams.put(multipleActionsKey, multipleActionsAry[i]); actionParams.put(PlatformConstants.MULTI_ACTION_INSTANCE, new Integer(i)); Enumeration actions = doIt.getActions().enumerateAction(); while (actions.hasMoreElements()) { thisAction = (Action) actions.nextElement(); runAction(doIt.getName(), thisAction, actionParams, responseMap, (i == (multipleActionsAry.length - 1))); } } Iterator iter = responseMap.keySet().iterator(); while (iter.hasNext()) { Object key = iter.next(); paramMap.put((String)key, responseMap.get(key)); /* if (componentRequestManager.get().getHttpRequest() != null) { componentRequestManager.get().getHttpRequest().setAttribute((String)key, responseMap.get(key)); } */ } } // if validation errors are thrown, make sure to correctly pull // error objects.. catch (Exception e) { if (e.getCause() instanceof ValidationException) { log.warn("Caught validation exception."); this.pullErrorObjectsIntoRequest(doIt, paramMap, responseMap, multipleActionsKey, multipleActionsAry); } throw e; } } } private void runAction(String doItName, Action thisAction, Map params, Map responseParams, boolean lastAction) throws Exception { String actionType = thisAction.getActionType(); Object retObj = null; if (actionType.equalsIgnoreCase("objectAction")) { //Fix the input params using the param mapping for //this configuration. if (thisAction.getParameters() != null) { // Cant do this for now cause of the array problem //ParameterUtil.mapParameters(thisAction.getParameters().getParameter(), params, params, doItName); ParameterUtil.mapDoItParameters(thisAction.getParameters().getParameter(), params, params, true); } try { retObj = this.getDatasource().dispatchAction( thisAction.getObjectAction(), ((String[])ParameterUtil.resolveParam(thisAction.getObjectDao(), params))[0], ((String[])ParameterUtil.resolveParam(thisAction.getInputObjectType(), params))[0], thisAction.getReturnObjectType(), ((String[])ParameterUtil.resolveParam(thisAction.getGuidParam(), params))[0], thisAction.getPermissionContext(), thisAction.getIndexParam(), thisAction.getNamespace(), params, responseParams); /* TODO: Remove this later Iterator iter = responseParams.keySet().iterator(); while (iter.hasNext()) { Object key = iter.next(); params.put((String)key, responseParams.get(key)); } */ } catch (Exception e) { if (e.getCause() instanceof ValidationException) { responseParams.put("ErrorForwardParams", params.get("ErrorForwardParams")); } throw e; } } else if (actionType.equalsIgnoreCase("cookieAction")) { String cookieName = ((String[])ParameterUtil.resolveParam(params.get("cookieName"), params))[0]; String cookieValue = ((String[])ParameterUtil.resolveParam(params.get("cookieValue"), params))[0]; int maxAge = -1; try { maxAge = Integer.parseInt(((String[])ParameterUtil.resolveParam(params.get("maxAge"), params))[0]); } catch (Exception e) {} Cookie doitCookie = new Cookie(cookieName, cookieValue); doitCookie.setMaxAge(maxAge); componentRequestManager.get().getHttpResponse().addCookie(doitCookie); } else if (actionType.equalsIgnoreCase("sessionAction")) { Map sessionMap = new HashMap(); if (thisAction.getParameters() != null) { ParameterUtil.mapDoItParameters(thisAction.getParameters().getParameter(), params, sessionMap, true); } HttpSession session = componentRequestManager.get().getHttpRequest().getSession(); Iterator iter = sessionMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); session.setAttribute((String)entry.getKey(), entry.getValue()); } } else if (actionType.equalsIgnoreCase("indexAction") && lastAction) { if (this.getIndexBuilder() != null) { indexBuilder.buildIndexes(thisAction.getObjectDao()); } } else { //TODO -- Add the ability to run scripts defined in config here. } //HashMap responseParams = new HashMap(); //Add the output params into the request for //this configuration. if(thisAction.getOutputParameters() != null && retObj != null){ JXPathContext context = null; if ("delete".equalsIgnoreCase(thisAction.getObjectAction())) { context = JXPathContext.newContext(responseParams); responseParams.put("deleted", String.valueOf(((Boolean)retObj).booleanValue())); } else { context = JXPathContext.newContext(retObj); } Parameter[] paramMap = thisAction.getOutputParameters().getParameter(); for(int j = 0; j < paramMap.length; j++){ Parameter thisParam = paramMap[j]; String[] paramPath = ParameterUtil.resolveParam(thisParam.getPath(), params); String[] paramName = ParameterUtil.resolveParam(thisParam.getName(), params); Object value = null; for (int i = 0; i <paramName.length; i++) { if(thisParam.getIsStatic()){ value = thisParam.getPath(); } else { try { value = context.getValue(paramPath[i]); } catch (org.apache.commons.jxpath.JXPathException e) { if (!thisParam.getIgnoreNull()) { log.warn("Problem evaluating jxpath: " + paramName[i] + " value: " + paramPath[i] + " action: " + thisAction.getObjectDao(), e); } continue; } if (value != null && value.getClass().isArray()) { value = ((String[])value)[0]; } } responseParams.put(paramName[i], value); } } } //Add if(thisAction.getReturnAttributeName() != null && retObj != null){ JXPathContext context = JXPathContext.newContext(retObj); responseParams.put(thisAction.getReturnAttributeName(), context.getValue("./valueObject/guid")); } Iterator iter = responseParams.keySet().iterator(); while (iter.hasNext()) { Object key = iter.next(); params.put((String)key, responseParams.get(key)); } } /** * Called upon receipt of a validation error. Iterates over all the * actions for this DoIt, retrieving an object of appropriate type out * of the posted request. All such objects are put in the response parameter * mapping under the "ValidationErrorObjects" key. */ private void pullErrorObjectsIntoRequest(DoIt doIt, Map paramMap, Map responseMap, String multipleActionsKey, String[] multipleActionsAry) throws Exception { if (log.isDebugEnabled()) { log.debug("ENTER pullErrorObjectsIntoRequest"); } Action thisAction = null; Collection globalErrorObjects = new ArrayList(); Collection globalErrorMessages = new ArrayList(); BeanFactory beanFactory = BeanMonkey.getBeanFactoryInstance(); if(doIt.getActions() != null) { paramMap.remove("guid"); //Actions actionsObj = doIt.getActions(); for(int i = 0; i < multipleActionsAry.length; i++) { HashMap actionParams = new HashMap(paramMap); responseMap.clear(); actionParams.put(multipleActionsKey, multipleActionsAry[i]); actionParams.put(PlatformConstants.MULTI_ACTION_INSTANCE, new Integer(i)); Collection instanceErrorObjects = new ArrayList(); Collection instanceErrorMessages = new ArrayList(); // iterate over the actions Enumeration actions = doIt.getActions().enumerateAction(); while (actions.hasMoreElements()) { // for each, retrieve the object of appropriate type thisAction = (Action) actions.nextElement(); // get parameters from action object String actionStr = thisAction.getObjectAction(); // for now, only do error object handling for creates and updates.. if(!actionStr.startsWith("create") && !actionStr.startsWith("update")) { continue; } //retrieve the input object for this action Object inputObject = constructInputObjectFromAction(thisAction, actionParams); //if there's an input object if(inputObject != null || actionStr.endsWith("Collection")) { if (thisAction.getNamespace() != null && !"".equals(thisAction.getNamespace())) { actionParams.put("namespace", thisAction.getNamespace()); } // before calling the getSafeBean method, have to // stick this action's returnObjectType in the paramMap String objectReturnType = ((String[])ParameterUtil.resolveParam(thisAction.getReturnObjectType(), actionParams))[0]; actionParams.put("returnObjectType", objectReturnType); // use the bean monkey to populate that object // (pass false in as last arg to block validation) boolean collection = false; String className = null; if (actionStr.endsWith("Collection")) { String beanClazz = ((String[])ParameterUtil.resolveParam(thisAction.getInputObjectType(), actionParams))[0]; inputObject = BeanMonkey.populateCollection(beanClazz, thisAction.getIndexParam(), actionParams, false, thisAction.getValidationErrorMode(), instanceErrorMessages); collection = true; className = beanClazz.substring(beanClazz.lastIndexOf(".") + 1); } else { try { BeanMonkey.populate(inputObject, actionParams, instanceErrorMessages); } catch (ValidationException e) { Iterator errIter = e.getErrors().iterator(); while (errIter.hasNext()) { Errors err = (Errors)errIter.next(); instanceErrorMessages.addAll(err.getAllErrors()); } } className = inputObject.getClass().getName(); } // If there are no error messages for the object don't produce an error object //if (instanceErrorMessages.size() == 0) continue; // and get the validator for the input object IValidator v = null; className = className.substring(className.lastIndexOf(".") + 1); String validatorName = className + "Validator"; if (beanFactory.containsBean(validatorName)) { v = (IValidator) beanFactory.getBean(validatorName); } else { log.warn("No validator " + validatorName + " for " + className); } // if there's no validator, then just continue... if(v == null) continue; // call the validator's prepare method, // pipe the populated bean through the validator's getSafeBean method // and, finally dump the populated object into the error objects map actionParams.put("doit.validation.error.mode", new Boolean(true)); v.prepare(inputObject, actionParams); if (collection && inputObject != null && inputObject instanceof Collection) { Iterator iter = ((Collection)inputObject).iterator(); while (iter.hasNext()) { instanceErrorObjects.add(v.getSafeBean(iter.next(), actionParams)); } } else { instanceErrorObjects.add(v.getSafeBean(inputObject, actionParams)); } } continue; } globalErrorMessages.add(instanceErrorMessages); globalErrorObjects.add(instanceErrorObjects); } // put the populated error objs in the request scope. responseMap.put(PresConstants.VALIDATION_ERROR_OBJECTS, globalErrorObjects); responseMap.put(PresConstants.VALIDATION_ERROR_MESSAGES, globalErrorMessages); } if (log.isDebugEnabled()) { log.debug("EXIT pullErrorObjectsIntoRequest"); } } /** * Given an action, looks at parameters w.in that action to either * load an object of appropriate type using a dao, or just construct * an empty object of appropriate type. */ private Object constructInputObjectFromAction(Action action, Map paramMap) throws Exception { String objectDao = ((String[])ParameterUtil.resolveParam(action.getObjectDao(), paramMap))[0]; String objectInputType = ((String[])ParameterUtil.resolveParam(action.getInputObjectType(), paramMap))[0]; // Fix the input params using the param mapping for // this configuration. if (action.getParameters() != null) { ParameterUtil.mapDoItParameters(action.getParameters().getParameter(), paramMap, paramMap, true); } Object inputObject = null; // if the object input type was specified, construct an empty object if(objectInputType != null && !"".equals(objectInputType)) { // otherwise just instantiate a new object of the appropriate type try { Class clazz = Class.forName(objectInputType); if(clazz != null) inputObject = clazz.getConstructor().newInstance(); } catch (ClassNotFoundException cnfe) { // if class isn't found here... don't do shit.. // method will return null, and this action will be skipped in the // populated list of error objects } catch (InstantiationException ie) { // Abstract too } } //NOTE: passing leading '$' forces a lookup in the parameter map String[] objectGuidArray = ((String[])ParameterUtil.resolveParam("$" + action.getGuidParam(), paramMap)); String objectGuid = objectGuidArray == null ? null : objectGuidArray[0]; // but if guid is present, replace the empty object with db load if(objectGuid != null && !"".equals(objectGuid)) { // then use the dao to load the object from the db Object daoBean = null; //if a dao bean is specified ... load it up if(objectDao != null && !"".equals(objectDao)) { // fetch the object using a dao bean.. try { daoBean = BeanMonkey.getBeanFactoryInstance().getBean(objectDao); } catch(org.springframework.beans.factory.NoSuchBeanDefinitionException nsbde) { //if there isn't a daoBean matching that specified in the DOIT, this is so //because there is most likely a custom js script underlying the definition //so, bypass this exception by just returning null here. log.warn("No bean named " + objectDao + " is defined. Returning null."); return null; } // get the personId parameter //String personGuid = (String) paramMap.get("personId"); try { inputObject = ((IObjectLoader)daoBean).load(objectGuid); } catch(ClassCastException cce) { // if dao is not of type IBaseObjectDao, then just return null return null; } } } return inputObject; } public IDataSource getDatasource() { return (IDataSource) BeanMonkey.getBeanFactoryInstance().getBean("DefaultDatasource"); } public ComponentRequestManager getComponentRequestManager() { return componentRequestManager; } public void setComponentRequestManager( ComponentRequestManager componentRequestManager) { this.componentRequestManager = componentRequestManager; } public ISingleIndexBuilder getIndexBuilder() { return indexBuilder; } public void setIndexBuilder(ISingleIndexBuilder indexBuilder) { this.indexBuilder = indexBuilder; } }