[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