[cvs] expresso commit by rimovm: Fixed reflection issues for:

JCorporate Ltd jcorp at jcorporate.com
Tue May 24 12:59:16 UTC 2005


Log Message:
-----------
Fixed reflection issues for:
    run*State(ServletControllerRequest,ExpressoResponse);

Fixed error where no newState Method and now state handler didn't throw an exception like it should.

Modified Files:
--------------
    expresso/expresso-web/WEB-INF/src/com/jcorporate/expresso/core/controller:
        Controller.java

Revision Data
-------------
Index: Controller.java
===================================================================
RCS file: /home/javacorp/.cvs/expresso/expresso/expresso-web/WEB-INF/src/com/jcorporate/expresso/core/controller/Controller.java,v
retrieving revision 1.115
retrieving revision 1.116
diff -Lexpresso-web/WEB-INF/src/com/jcorporate/expresso/core/controller/Controller.java -Lexpresso-web/WEB-INF/src/com/jcorporate/expresso/core/controller/Controller.java -u -r1.115 -r1.116
--- expresso-web/WEB-INF/src/com/jcorporate/expresso/core/controller/Controller.java
+++ expresso-web/WEB-INF/src/com/jcorporate/expresso/core/controller/Controller.java
@@ -116,6 +116,7 @@
 import java.util.Vector;
 import java.util.Map;
 import com.jcorporate.expresso.core.registry.RequestRegistry;
+import java.lang.reflect.Modifier;
 
 
 /**
@@ -261,6 +262,25 @@
         com.jcorporate.expresso.core.controller.ControllerResponse.class
     };
 
+    /**
+     * Used for reflection into state handlers.
+     */
+    private static final Class[] servletStateInterfaceParams = {
+        com.jcorporate.expresso.core.controller.ServletControllerRequest.class,
+        com.jcorporate.expresso.core.controller.ExpressoResponse.class
+    };
+
+    /**
+     * This is the order that the Expresso reflection mechanism looks for when
+     * it tries to find state handlers.
+     * We first try stateHandlerPrecedenceOrder[0] and if that fails, then
+     * stateHandlerPrecedenceOrder[1], until we run out of tries.
+     */
+    private static final Class[][] stateHandlerPrecedenceOrder = {
+      stateHandlerParams, newStateInterfaceParams, servletStateHandlerParams, servletStateInterfaceParams
+    };
+
+
 
     /**
      * promptStates is only updated in the constructor.  The instantiation of Controller by Struts is
@@ -1318,7 +1338,7 @@
             try {
                 Method m = null;
 
-                m = locateInvocationMethod(myRequest, nextState);
+                m = locateInvocationMethod(nextState);
 
                 ControllerResponse cr2 = (ControllerResponse) m.invoke(this, new Object[] {myRequest, cr});
 
@@ -1358,43 +1378,31 @@
                     throw new ControllerException(e);
                 }
             } catch (NoSuchMethodException nsme) {
-                try {
-
-                    // test to see if this method exists in thisClass
-                    //If newState exists as either (String,ControllerRequest)
-                    //or (String,ExpressoRequest) AND IS PUBLIC
-                    //then it will be invoked next.  Otherwise, we have
-                    //to throw an exception.
-                    try {
-                        thisClass.getMethod("newState",
-                            newStateParams);
-                    } catch(NoSuchMethodException ex2) {
-                        thisClass.getMethod("newState",
-                            newStateParamsWithInterface);
-                    }
-// it does exist, since no exception thrown.  give feedback
-                    if (log.isDebugEnabled()) {
-                        log.debug("Method '" + nextState.getHandlerName() +
-                            "' does not exist in controller class '" +
-                            getClass().getName() +
-                            "'. State must be handled by newState method.");
-                    }
-                } catch (NoSuchMethodException nsme2) {
 
+                // test to see if this method exists in thisClass
+                //If newState exists as either (String,ControllerRequest)
+                //or (String,ExpressoRequest) AND IS PUBLIC
+                //then it will be invoked next.  Otherwise, we have
+                //to throw an exception.
+                if (!isNewStateHandled(this.getClass())) {
                     // If the controller doesn't have a newState method, then
                     // we have a problem....
-                    String errMsg = ("Method '" +
-                        nextState.getHandlerName() +
-                        "' does not exist in '" +
-                        getClass().getName() +
-                        "' and there is no 'newState' method to handle " +
-                        "transitions. Unable to transition");
+                    String errMsg = ("Method '" + nextState.getHandlerName()
+                        + "' does not exist in '" + getClass().getName()
+                        + "' and there is no 'newState' method to handle "
+                        + "transitions. Unable to transition");
                     log.error(errMsg, nsme);
                     throw new ControllerException(errMsg, nsme);
+
+                }
+
+                // it does exist, since no exception thrown.  give feedback
+                if (log.isDebugEnabled()) {
+                    log.debug("Method '" + nextState.getHandlerName() + "' does not exist in controller class '"
+                        + getClass().getName() + "'. State must be handled by newExpressoState method.");
                 }
             }
         }
-
         //This is the old error handling transition logic for backward compatability.
         ErrorCollection finalErrors = cr.getErrors();
         if (finalErrors != null) {
@@ -1412,6 +1420,48 @@
     }
 
     /**
+     * Heuristic that determines if newState is handled by this particular class
+     * instance.  To determine if newState() is handled, it walks the
+     * controller hierarchy
+     * UNTIL DBController, looking for an applicable method.  If it does not
+     * find one by that point in time, it return false.
+     * @return boolean
+     */
+    private boolean isNewStateHandled(final Class startClass) {
+
+
+        if (!classHasNewStateMethod(startClass, "newState", newStateParams) &&
+            ! classHasNewStateMethod(startClass, "newExpressoState", newStateParamsWithInterface)) {
+            return false;
+        } else {
+            return true;
+        }
+
+    }
+
+    /**
+     * Method that checks if the given class has the given newstate method name
+     * with the given parameters AND that the method is public
+     * @param tryClass Class
+     * @param methodName String
+     * @param parameters Class[]
+     * @return boolean
+     */
+    private boolean classHasNewStateMethod(final Class tryClass, final String methodName, final Class[] parameters) {
+        try {
+            Method m = tryClass.getMethod(methodName, parameters);
+            if (Controller.class.equals(m.getDeclaringClass()) ||
+                DBController.class.equals(m.getDeclaringClass())) {
+                return false;
+            } else {
+                return true;
+            }
+        } catch (NoSuchMethodException ex) {
+            return false;
+        }
+    }
+
+    /**
      * Locates the state invocation method.  It first tries to find
      * run*State(ExpressoRequest,ExpressoResponse) and if that doesn't work
      * then tries run*State(ControllerRequest,ControllerResponse) and finally
@@ -1423,8 +1473,7 @@
      * @throws NoSuchMethodException if we hit java.lang.Object and still cannot
      * find invocation method.
      */
-    private Method locateInvocationMethod(final ExpressoRequest myRequest
-        , final State nextState) throws NonHandleableException, NoSuchMethodException {
+    private Method locateInvocationMethod(final State nextState) throws NonHandleableException, NoSuchMethodException {
 
         Class tryClass = this.getClass();
         Method cachedMethod = (nextState.getStateInvocationMethod());
@@ -1435,6 +1484,12 @@
         Method m = null;
         boolean methodFound = false;
 
+
+        //Use string buffer because this one isn't worth
+        //the runtime contention to get one from the pool.
+        StringBuffer triedClasses  = new StringBuffer();
+        triedClasses.append("[Tried Classes: " + this.getClass());
+
         //
         //New as of v4.1 - Attempts to walk up the class hierarchy
         //to find an applicable class to execute.  This should allow
@@ -1442,47 +1497,82 @@
         //
         while (!methodFound) {
             final String handlerName = nextState.getHandlerName();
-            try {
 
-                //We first try the old way so classes overriding old methods
-                //will work.
-                m = tryClass.getDeclaredMethod(handlerName, stateHandlerParams);
-                methodFound = true;
-                log.warn("Found state handler: " + handlerName + " (ControllerRequest,ControllerResponse)"
-                    + "Please convert the method signature to " + handlerName
-                    + " (ExpressoRequest,ExpressoResponse) " + " [Class" + tryClass.getName() + "]");
-            } catch (NoSuchMethodException nsme) {
 
-                //Now we try the new interface-based way.
+
+            //
+            //Since Expresso 5.7 (5/24/05) -- go through the stetHandlerPrecedenceOrder
+            //array to find an invokable method.
+            //
+            for (int i = 0; i < stateHandlerPrecedenceOrder.length; i++) {
                 try {
-                    m = tryClass.getDeclaredMethod(handlerName, newStateInterfaceParams);
+                    //
+                    //Try each entry in the stateHandlerPrecendenceOrder
+                    //
+                    m = tryClass.getDeclaredMethod(handlerName, stateHandlerPrecedenceOrder[i]);
                     methodFound = true;
 
+                    //Put other deprecation warnings here.
+                    if (stateHandlerPrecedenceOrder[i] == stateHandlerParams) {
+                        log.warn("Found state handler: " + handlerName + " (ControllerRequest,ControllerResponse)"
+                            + "Please convert the method signature to " + handlerName
+                            + " (ExpressoRequest,ExpressoResponse) " + " [Class" + tryClass.getName() + "]");
+                    } else if (stateHandlerPrecedenceOrder[i] == servletStateHandlerParams) {
+                        log.warn("Found state handler: " + handlerName + " (ServletControllerRequest,ControllerResponse)"
+                            + "Please convert the method signature to " + handlerName
+                            + " (ServletControllerRequest,ExpressoResponse) " + " [Class" + tryClass.getName() + "]");
+                    }
+
+                    //If we got here,  then we found the method we want.
+                    break;
+
                 } catch (NoSuchMethodException ex) {
+                    //We save the exception if this is the first time
+                    //and the stateHandlerPrecendenceOrder is the method
+                    //that we wish to bend everyone too.
 
-                    //Finally we attempt the ServletControllerRequest versions.
-                    if (myRequest instanceof ServletControllerRequest) {
-                        //As of 5.3: Now try a signature for ServletControllerRequest
-                        //instead.
-                        try {
-                            m = tryClass.getDeclaredMethod(handlerName,
-                                servletStateHandlerParams);
-                            methodFound = true;
-                        } catch (NoSuchMethodException ex2) {
-                            if (log.isDebugEnabled()) {
-                                log.debug("Didn't find ServletControllerRequest method", ex2);
-                            }
-                        }
+                    //Didn't find it, continue.
+                    if (log.isDebugEnabled()) {
+                        log.debug("Didn't find state handler using parameters", ex);
                     }
-
                 }
 
-                if (!methodFound) {
-                    tryClass = tryClass.getSuperclass();
+            }
+
+            if (!methodFound) {
+                tryClass = tryClass.getSuperclass();
+                if (tryClass == null) {
+                    triedClasses.append("]");
+                    StringBuffer errorMessage = new StringBuffer(256);
+                    errorMessage.append("Could not find valid state handler by name of '");
+                    errorMessage.append(handlerName);
+                    errorMessage.append("' with parameters: ");
+                    //Iterate throughs state handlers and print friendly name.
+                    for (int i = 0; i < stateHandlerPrecedenceOrder.length; i++) {
+                        if (i > 0) {
+                            errorMessage.append(" or ");
+                        }
 
-                    if (tryClass == null) {
-                        throw nsme;
+                        errorMessage.append("(");
+                        //Iterate through individual state handlers printing out class names
+                        for (int j = 0; j < stateHandlerPrecedenceOrder[i].length; j++) {
+                            if (j != 0) {
+                                errorMessage.append(",");
+                            }
+
+                            errorMessage.append(stateHandlerPrecedenceOrder[i][j].toString());
+                        }
+                        errorMessage.append(")");
+
+                        errorMessage.append(stateHandlerPrecedenceOrder[i].toString());
                     }
+
+                    errorMessage.append(". Tried: ");
+                    errorMessage.append(triedClasses);
+                    errorMessage.append(" to find the appropriate handler");
+                    throw new NoSuchMethodException(errorMessage.toString());
+                } else {
+                    triedClasses.append(", " + tryClass.getClass());
                 }
             }
         }
@@ -1496,8 +1586,6 @@
         return m;
     }
 
-    /* newState(String) */
-
 
     /**
      * Template Method, allowing a subclass to do an action after
@@ -1539,7 +1627,7 @@
     }
 
     /**
-     * @deprecated Use ExpressoRequest/ExpressoResponse versions intead.
+     * @deprecated Use ExpressoRequest/ExpressoResponse versions intead. (Since Expresso 5.7)
      * @param nextState State
      * @param request ControllerRequest
      * @param response ControllerResponse


More information about the cvs mailing list