Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2 » [javadoc | source]
    1   /*
    2    * Copyright (c) 2002-2006 by OpenSymphony
    3    * All rights reserved.
    4    */
    5   package com.opensymphony.xwork2;
    6   
    7   import java.lang.reflect.InvocationTargetException;
    8   import java.lang.reflect.Method;
    9   import java.util.ArrayList;
   10   import java.util.Iterator;
   11   import java.util.List;
   12   import java.util.Map;
   13   
   14   import com.opensymphony.xwork2.config.ConfigurationException;
   15   import com.opensymphony.xwork2.config.entities.ActionConfig;
   16   import com.opensymphony.xwork2.config.entities.InterceptorMapping;
   17   import com.opensymphony.xwork2.config.entities.ResultConfig;
   18   import com.opensymphony.xwork2.inject.Container;
   19   import com.opensymphony.xwork2.inject.Inject;
   20   import com.opensymphony.xwork2.interceptor.PreResultListener;
   21   import com.opensymphony.xwork2.util.ValueStack;
   22   import com.opensymphony.xwork2.util.ValueStackFactory;
   23   import com.opensymphony.xwork2.util.logging.Logger;
   24   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   25   import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
   26   
   27   
   28   /**
   29    * The Default ActionInvocation implementation
   30    *
   31    * @author Rainer Hermanns
   32    * @author tmjee
   33    * 
   34    * @version $Date: 2008-03-28 18:12:29 +0100 (Fri, 28 Mar 2008) $ $Id: DefaultActionInvocation.java 1766 2008-03-28 17:12:29Z rainerh $
   35    * 
   36    * @see com.opensymphony.xwork2.DefaultActionProxy
   37    */
   38   public class DefaultActionInvocation implements ActionInvocation {
   39       
   40   	private static final long serialVersionUID = -585293628862447329L;
   41   
   42       //static {
   43       //    if (ObjectFactory.getContinuationPackage() != null) {
   44       //        continuationHandler = new ContinuationHandler();
   45       //    }
   46       //}
   47       private static final Logger LOG = LoggerFactory.getLogger(DefaultActionInvocation.class);
   48   
   49       protected Object action;
   50       protected ActionProxy proxy;
   51       protected List preResultListeners;
   52       protected Map extraContext;
   53       protected ActionContext invocationContext;
   54       protected Iterator interceptors;
   55       protected ValueStack stack;
   56       protected Result result;
   57       protected Result explicitResult;
   58       protected String resultCode;
   59       protected boolean executed = false;
   60       protected boolean pushAction = true;
   61       protected ObjectFactory objectFactory;
   62       protected ActionEventListener actionEventListener;
   63       protected ValueStackFactory valueStackFactory;
   64       protected Container container;                             
   65       protected UnknownHandler unknownHandler;
   66   
   67       public DefaultActionInvocation(final Map extraContext, final boolean pushAction) {
   68           DefaultActionInvocation.this.extraContext = extraContext;
   69           DefaultActionInvocation.this.pushAction = pushAction;
   70       }
   71       
   72       @Inject
   73       public void setValueStackFactory(ValueStackFactory fac) {
   74           this.valueStackFactory = fac;
   75       }
   76       
   77       @Inject
   78       public void setObjectFactory(ObjectFactory fac) {
   79           this.objectFactory = fac;
   80       }
   81       
   82       @Inject
   83       public void setContainer(Container cont) {
   84           this.container = cont;
   85       }
   86       
   87       @Inject(required=false)
   88       public void setUnknownHandler(UnknownHandler hand) {
   89           this.unknownHandler = hand;
   90       }
   91       
   92       @Inject(required=false)
   93       public void setActionEventListener(ActionEventListener listener) {
   94           this.actionEventListener = listener;
   95       }
   96   
   97       public Object getAction() {
   98           return action;
   99       }
  100   
  101       public boolean isExecuted() {
  102           return executed;
  103       }
  104   
  105       public ActionContext getInvocationContext() {
  106           return invocationContext;
  107       }
  108   
  109       public ActionProxy getProxy() {
  110           return proxy;
  111       }
  112   
  113       /**
  114        * If the DefaultActionInvocation has been executed before and the Result is an instance of ActionChainResult, this method
  115        * will walk down the chain of ActionChainResults until it finds a non-chain result, which will be returned. If the
  116        * DefaultActionInvocation's result has not been executed before, the Result instance will be created and populated with
  117        * the result params.
  118        *
  119        * @return a Result instance
  120        * @throws Exception
  121        */
  122       public Result getResult() throws Exception {
  123           Result returnResult = result;
  124   
  125           // If we've chained to other Actions, we need to find the last result
  126           while (returnResult instanceof ActionChainResult) {
  127               ActionProxy aProxy = ((ActionChainResult) returnResult).getProxy();
  128   
  129               if (aProxy != null) {
  130                   Result proxyResult = aProxy.getInvocation().getResult();
  131   
  132                   if ((proxyResult != null) && (aProxy.getExecuteResult())) {
  133                       returnResult = proxyResult;
  134                   } else {
  135                       break;
  136                   }
  137               } else {
  138                   break;
  139               }
  140           }
  141   
  142           return returnResult;
  143       }
  144   
  145       public String getResultCode() {
  146           return resultCode;
  147       }
  148   
  149       public void setResultCode(String resultCode) {
  150           if (isExecuted())
  151               throw new IllegalStateException("Result has already been executed.");
  152   
  153           this.resultCode = resultCode;
  154       }
  155   
  156   
  157       public ValueStack getStack() {
  158           return stack;
  159       }
  160   
  161       /**
  162        * Register a com.opensymphony.xwork2.interceptor.PreResultListener to be notified after the Action is executed and before the
  163        * Result is executed. The ActionInvocation implementation must guarantee that listeners will be called in the order
  164        * in which they are registered. Listener registration and execution does not need to be thread-safe.
  165        *
  166        * @param listener
  167        */
  168       public void addPreResultListener(PreResultListener listener) {
  169           if (preResultListeners == null) {
  170               preResultListeners = new ArrayList(1);
  171           }
  172   
  173           preResultListeners.add(listener);
  174       }
  175   
  176       public Result createResult() throws Exception {
  177   
  178       	if (explicitResult != null) {
  179       	    Result ret = explicitResult;
  180       	    explicitResult = null;;
  181       		return ret;
  182       	}
  183           ActionConfig config = proxy.getConfig();
  184           Map results = config.getResults();
  185   
  186           ResultConfig resultConfig = null;
  187   
  188           synchronized (config) {
  189               try {
  190                   resultConfig = (ResultConfig) results.get(resultCode);
  191               } catch (NullPointerException e) {
  192               }
  193               if (resultConfig == null) {
  194                   // If no result is found for the given resultCode, try to get a wildcard '*' match.
  195                   resultConfig = (ResultConfig) results.get("*");
  196               }
  197           }
  198   
  199           if (resultConfig != null) {
  200               try {
  201                   Result result = objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
  202                   return result;
  203               } catch (Exception e) {
  204                   LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
  205                   throw new XWorkException(e, resultConfig);
  206               }
  207           } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandler != null) {
  208               return unknownHandler.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
  209           }
  210           return null;
  211       }
  212   
  213       /**
  214        * @throws ConfigurationException If no result can be found with the returned code
  215        */
  216       public String invoke() throws Exception {
  217       	String profileKey = "invoke: ";
  218       	try {
  219       		UtilTimerStack.push(profileKey);
  220       		
  221       		if (executed) {
  222       			throw new IllegalStateException("Action has already executed");
  223       		}
  224   
  225       		if (interceptors.hasNext()) {
  226       			final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
  227       			UtilTimerStack.profile("interceptor: "+interceptor.getName(), 
  228       					new UtilTimerStack.ProfilingBlock<String>() {
  229   							public String doProfiling() throws Exception {
  230   				    			resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
  231   				    			return null;
  232   							}
  233       			});
  234       		} else {
  235       			resultCode = invokeActionOnly();
  236       		}
  237   
  238       		// this is needed because the result will be executed, then control will return to the Interceptor, which will
  239       		// return above and flow through again
  240       		if (!executed) {
  241       			if (preResultListeners != null) {
  242       				for (Iterator iterator = preResultListeners.iterator();
  243       					iterator.hasNext();) {
  244       					PreResultListener listener = (PreResultListener) iterator.next();
  245       					
  246       					String _profileKey="preResultListener: ";
  247       					try {
  248       						UtilTimerStack.push(_profileKey);
  249       						listener.beforeResult(this, resultCode);
  250       					}
  251       					finally {
  252       						UtilTimerStack.pop(_profileKey);
  253       					}
  254       				}
  255       			}
  256   
  257       			// now execute the result, if we're supposed to
  258       			if (proxy.getExecuteResult()) {
  259       				executeResult();
  260       			}
  261   
  262       			executed = true;
  263       		}
  264   
  265       		return resultCode;
  266       	}
  267       	finally {
  268       		UtilTimerStack.pop(profileKey);
  269       	}
  270       }
  271   
  272       public String invokeActionOnly() throws Exception {
  273       	return invokeAction(getAction(), proxy.getConfig());
  274       }
  275   
  276       protected void createAction(Map contextMap) {
  277           // load action
  278           String timerKey = "actionCreate: "+proxy.getActionName();
  279           try {
  280               UtilTimerStack.push(timerKey);
  281               action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
  282           } catch (InstantiationException e) {
  283               throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());
  284           } catch (IllegalAccessException e) {
  285               throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
  286           } catch (Exception e) {
  287               String gripe = "";
  288   
  289               if (proxy == null) {
  290                   gripe = "Whoa!  No ActionProxy instance found in current ActionInvocation.  This is bad ... very bad";
  291               } else if (proxy.getConfig() == null) {
  292                   gripe = "Sheesh.  Where'd that ActionProxy get to?  I can't find it in the current ActionInvocation!?";
  293               } else if (proxy.getConfig().getClassName() == null) {
  294                   gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
  295               } else {
  296                   gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ",  defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
  297               }
  298   
  299               gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
  300               throw new XWorkException(gripe, e, proxy.getConfig());
  301           } finally {
  302               UtilTimerStack.pop(timerKey);
  303           }
  304   
  305           if (actionEventListener != null) {
  306               action = actionEventListener.prepare(action, stack);
  307           }
  308       }
  309   
  310       protected Map createContextMap() {
  311           Map contextMap;
  312   
  313           if ((extraContext != null) && (extraContext.containsKey(ActionContext.VALUE_STACK))) {
  314               // In case the ValueStack was passed in
  315               stack = (ValueStack) extraContext.get(ActionContext.VALUE_STACK);
  316   
  317               if (stack == null) {
  318                   throw new IllegalStateException("There was a null Stack set into the extra params.");
  319               }
  320   
  321               contextMap = stack.getContext();
  322           } else {
  323               // create the value stack
  324               // this also adds the ValueStack to its context
  325               stack = valueStackFactory.createValueStack();
  326   
  327               // create the action context
  328               contextMap = stack.getContext();
  329           }
  330   
  331           // put extraContext in
  332           if (extraContext != null) {
  333               contextMap.putAll(extraContext);
  334           }
  335   
  336           //put this DefaultActionInvocation into the context map
  337           contextMap.put(ActionContext.ACTION_INVOCATION, this);
  338           contextMap.put(ActionContext.CONTAINER, container);
  339   
  340           return contextMap;
  341       }
  342   
  343       /**
  344        * Uses getResult to get the final Result and executes it
  345        * 
  346        * @throws ConfigurationException If not result can be found with the returned code
  347        */
  348       private void executeResult() throws Exception {
  349           result = createResult();
  350   
  351           String timerKey = "executeResult: "+getResultCode();
  352           try {
  353               UtilTimerStack.push(timerKey);
  354               if (result != null) {
  355                   result.execute(this);
  356               } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
  357                   throw new ConfigurationException("No result defined for action " + getAction().getClass().getName() 
  358                           + " and result " + getResultCode(), proxy.getConfig());
  359               } else {
  360                   if (LOG.isDebugEnabled()) {
  361                       LOG.debug("No result returned for action "+getAction().getClass().getName()+" at "+proxy.getConfig().getLocation());
  362                   }
  363               }
  364           } finally {
  365               UtilTimerStack.pop(timerKey);
  366           }
  367       }
  368   
  369       public void init(ActionProxy proxy) {
  370           this.proxy = proxy;
  371           Map contextMap = createContextMap();
  372   
  373           // Setting this so that other classes, like object factories, can use the ActionProxy and other
  374           // contextual information to operate
  375           ActionContext actionContext = ActionContext.getContext();
  376   
  377           if(actionContext != null) {
  378               actionContext.setActionInvocation(this);
  379           }
  380           
  381           createAction(contextMap);
  382   
  383           if (pushAction) {
  384               stack.push(action);
  385               contextMap.put("action", action);
  386           }
  387   
  388           invocationContext = new ActionContext(contextMap);
  389           invocationContext.setName(proxy.getActionName());
  390   
  391           // get a new List so we don't get problems with the iterator if someone changes the list
  392           List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());
  393           interceptors = interceptorList.iterator();
  394       }
  395   
  396       protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
  397           String methodName = proxy.getMethod();
  398   
  399           if (LOG.isDebugEnabled()) {
  400               LOG.debug("Executing action method = " + actionConfig.getMethodName());
  401           }
  402   
  403           String timerKey = "invokeAction: "+proxy.getActionName();
  404           try {
  405               UtilTimerStack.push(timerKey);
  406               
  407               boolean methodCalled = false;
  408               Object methodResult = null;
  409               Method method = null;
  410               try {
  411                   method = getAction().getClass().getMethod(methodName, new Class[0]);
  412               } catch (NoSuchMethodException e) {
  413                   // hmm -- OK, try doXxx instead
  414                   try {
  415                       String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
  416                       method = getAction().getClass().getMethod(altMethodName, new Class[0]);
  417                   } catch (NoSuchMethodException e1) {
  418                   	// well, give the unknown handler a shot
  419                   	if (unknownHandler != null) {
  420   	                	try {
  421   	                		methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);
  422   	                		methodCalled = true;
  423   	                	} catch (NoSuchMethodException e2) {
  424   	                		// throw the original one
  425   	                		throw e;
  426   	                	}
  427                   	} else {
  428   	            		throw e;
  429   	            	}
  430                   }
  431               }
  432           	
  433           	if (!methodCalled) {
  434           		methodResult = method.invoke(action, new Object[0]);
  435           	}
  436           	
  437               if (methodResult instanceof Result) {
  438               	this.explicitResult = (Result) methodResult;
  439               	return null;
  440               } else {
  441               	return (String) methodResult;
  442               }
  443           } catch (NoSuchMethodException e) {
  444               throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
  445           } catch (InvocationTargetException e) {
  446               // We try to return the source exception.
  447               Throwable t = e.getTargetException();
  448   
  449               if (actionEventListener != null) {
  450                   String result = actionEventListener.handleException(t, getStack());
  451                   if (result != null) {
  452                       return result;
  453                   }
  454               }
  455               if (t instanceof Exception) {
  456                   throw(Exception) t;
  457               } else {
  458                   throw e;
  459               }
  460           } finally {
  461               UtilTimerStack.pop(timerKey);
  462           }
  463       }
  464       
  465       
  466       
  467   }

Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2 » [javadoc | source]