NoSuchMethodException when reflecting ServletContext as a parameter

N

natG

Hi;
I am not new to reflection, but due to events, I feel new:).

When passing a ServletContext as a parameter to the reflected method, it
throws the NoSuchMethodException.

Here is the exact simplified code that produces it. (Please excuse the
extra Invoker class, I use it to test non-servlet related reflection.)

Code:
package ez.test;

import javax.servlet.ServletContext;

public class StaticMethodClass {

public static void methodA(String s1, String s2){
System.out.println("Hello from methodA: s1 s2. You passed "+ s1
+ " " + s2);
}
public static void methodA(ServletContext ctx, String s2){
System.out.print("Hello from methodA: ctx s2 . You passed "+  s2);
System.out.println(" from ServletContext: " + ctx.toString());
}
}

package ez.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Invoker {

Method m = null;

public Invoker(String methodName, Object[] methodParameters){
runCustomMethod(methodName,methodParameters);
}

public void runCustomMethod(String methodName, Object[] methodParams){
Class 	c = StaticMethodClass.class;
Method  m = null;
Class[]	parameterTypes = null;
if (methodParams!=null){
parameterTypes = new Class[methodParams.length];
for (int i=0;i<methodParams.length;i++){
parameterTypes[i] = methodParams[i].getClass();
}
}
try {
m = c.getDeclaredMethod(methodName,parameterTypes);
m.invoke(null,methodParams);
} catch (SecurityException e) {
System.out.println("Problem with *Security* in "
+methodName +".");
//e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("Error with the reflective
method."+methodName + ".");
//e.printStackTrace();
} catch (IllegalArgumentException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (InvocationTargetException e1) {
e1.printStackTrace();
}
}

public static void main(String[] args) {
new Invoker("methodA", new Object[]{"String-0","String-1"});
}
}

//now the real guy, the servlet.

/* Date: 08/12/2004 14:08:57 */
package test;

import ez.test.Invoker;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* @author Nat
* @version 0.01
* 08/12/2004 14:08:57
*/
public class InvokeMethodServlet extends HttpServlet {

ServletContext ctx = null;

public void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {

String s0 = "StringFromServlet-0", s1= "StringFromServlet-1";
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01
Transitional//EN\">");
out.println("<HTML>");
out.println("  <HEAD><TITLE>Test Reflection
Servlet.</TITLE></HEAD>");
out.println("  <BODY>");
out.println("<p>This servlet simply tests reflection calls.</p>");
out.println("Check server log for output from strings: " + s0 +
" " + s1);
out.println("Also Check server log for output from CTX &
string: "  + s1);
out.println("  </BODY>");
out.println("</HTML>");
out.flush();
out.close();
new Invoker("methodA", new Object[]{s0,s1});  //this works ok.
new Invoker("methodA", new Object[]{ctx,s1}); //this does NOT.
}
public void init() throws ServletException {
ctx=getServletContext();
}
}

Thank you, in advance.
-nat
 
N

Nicky

Hi

Try using getMethod(methodName, parameterTypes)
instead of getDeclaredMethod(methodName, parameterTypes).

getDeclaredMethod only searches in the actual class of the object, but not
in the superclasses.


natG said:
Hi;
I am not new to reflection, but due to events, I feel new:).

When passing a ServletContext as a parameter to the reflected method, it
throws the NoSuchMethodException.

Here is the exact simplified code that produces it. (Please excuse the
extra Invoker class, I use it to test non-servlet related reflection.)

Code:
package ez.test;

import javax.servlet.ServletContext;

public class StaticMethodClass {

public static void methodA(String s1, String s2){
System.out.println("Hello from methodA: s1 s2. You passed "+ s1 +
" " + s2);
}
public static void methodA(ServletContext ctx, String s2){
System.out.print("Hello from methodA: ctx s2 . You passed "+  s2);
System.out.println(" from ServletContext: " + ctx.toString());
}
}

package ez.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Invoker {

Method m = null;

public Invoker(String methodName, Object[] methodParameters){
runCustomMethod(methodName,methodParameters);
}

public void runCustomMethod(String methodName, Object[] methodParams){
Class c = StaticMethodClass.class;
Method  m = null;
Class[] parameterTypes = null;
if (methodParams!=null){
parameterTypes = new Class[methodParams.length];
for (int i=0;i<methodParams.length;i++){
parameterTypes[i] = methodParams[i].getClass();
}
}
try {
m = c.getDeclaredMethod(methodName,parameterTypes);
m.invoke(null,methodParams);
} catch (SecurityException e) {
System.out.println("Problem with *Security* in " +methodName
+".");
//e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("Error with the reflective
method."+methodName + ".");
//e.printStackTrace();
} catch (IllegalArgumentException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (InvocationTargetException e1) {
e1.printStackTrace();
}
}

public static void main(String[] args) {
new Invoker("methodA", new Object[]{"String-0","String-1"});
}
}

//now the real guy, the servlet.

/* Date: 08/12/2004 14:08:57 */
package test;

import ez.test.Invoker;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* @author Nat
* @version 0.01
* 08/12/2004 14:08:57
*/
public class InvokeMethodServlet extends HttpServlet {

ServletContext ctx = null;

public void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {

String s0 = "StringFromServlet-0", s1= "StringFromServlet-1";
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01
Transitional//EN\">");
out.println("<HTML>");
out.println("  <HEAD><TITLE>Test Reflection
Servlet.</TITLE></HEAD>");
out.println("  <BODY>");
out.println("<p>This servlet simply tests reflection calls.</p>");
out.println("Check server log for output from strings: " + s0 + "
" + s1);
out.println("Also Check server log for output from CTX & string: "
+ s1);
out.println("  </BODY>");
out.println("</HTML>");
out.flush();
out.close();
new Invoker("methodA", new Object[]{s0,s1});  //this works ok.
new Invoker("methodA", new Object[]{ctx,s1}); //this does NOT.
}
public void init() throws ServletException {
ctx=getServletContext();
}
}

Thank you, in advance.
-nat
 
J

John C. Bollinger

natG said:
I am not new to reflection, but due to events, I feel new:).

Well, you've hit one of the more common reflection stumbling blocks:
matching reflected method signatures to method argument types. When a
method's formal argument type is a non-final class type, you cannot just
match the class of the argument to the method signature because the
reflective method lookup methods look for an *exact match*. Such a
match will not be achieved if the class of the argument is a proper
subtype of the formal type declared by the method. In particular, you
will *never* find a match when the formal argument type is an abstract
type (such as the ServletContext interface).

Similar problems can apply to primitive arguments, too, because they can
be promoted according to method invocation conversion rules. (JLS 2ed,
section 5.3)

If you don't know in advance the signature of the method you want to
invoke then for full generality you need to implement the method
resolution procedure specified in JLS(2ed) 15.12.2 - 15.12.4. The
compiler takes care of all that for you in standard method invocations,
but you have to deal with it yourself when you use reflection.
When passing a ServletContext as a parameter to the reflected method, it
throws the NoSuchMethodException.

Because whatever the ServletContext implementation class happens to be,
it is definitely *not* javax.servlet.ServletContext.
Here is the exact simplified code that produces it. (Please excuse the
extra Invoker class, I use it to test non-servlet related reflection.)

[edited for brevity:]
public class StaticMethodClass {

public static void methodA(ServletContext ctx, String s2){
System.out.print("Hello from methodA: ctx s2 . You passed "+ s2);
System.out.println(" from ServletContext: " + ctx.toString());
}
}

public class Invoker {

Method m = null;

public Invoker(String methodName, Object[] methodParameters){
runCustomMethod(methodName,methodParameters);
}

public void runCustomMethod(String methodName, Object[] methodParams){
Class c = StaticMethodClass.class;
Method m = null;
Class[] parameterTypes = null;
if (methodParams!=null){
parameterTypes = new Class[methodParams.length];
for (int i=0;i<methodParams.length;i++){
parameterTypes = methodParams.getClass();
}
}
try {
m = c.getDeclaredMethod(methodName,parameterTypes);


You didn't say, but I can be pretty confident that the exception is
thrown during execution of the above method. The method is not found
because it is not there. To make this work reliably your invoker must
get the list of all methods of the class in question that have the
correct name, determine (according to JLS rules) which are both
"applicable" to the arguments and accessible to the invoking code, and
among those choose the "most specific" method. It is possible for there
to be no single most specific method, which is a compile-time error in
non-reflective code, but which you can handle however you want in your
own invoker.
}
}


public class InvokeMethodServlet extends HttpServlet {

ServletContext ctx = null;

public void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {

new Invoker("methodA", new Object[]{ctx,s1}); //this does NOT.

[does not work, that is.] Do not be confused between the type of a
reference variable and the class of the object to which the variable
refers. They do not need to be the same. Even if the variable type is
a final class type, the variable value can still be null (and that will
cause your Invoker to throw a NullPointerException even though there may
be a suitable method it could invoke).
public void init() throws ServletException {
ctx=getServletContext();
}
}


John Bollinger
(e-mail address removed)
 
N

natG

Nicky said:
getDeclaredMethod only searches in the actual class of the object, but not
in the superclasses.
Are you sure about this? Anyhow, I tried with the same results. No go.

Thanks,
-nat
 
N

natG

After your elaborate response (thanks!) I see that I *am* new said:
Well, you've hit one of the more common reflection stumbling blocks:
matching reflected method signatures to method argument types. When a
method's formal argument type is a non-final class type, you cannot just
match the class of the argument to the method signature because the
reflective method lookup methods look for an *exact match*. Such a
match will not be achieved if the class of the argument is a proper
subtype of the formal type declared by the method. In particular, you
will *never* find a match when the formal argument type is an abstract
type (such as the ServletContext interface).

Real new to me. Thanks.
Similar problems can apply to primitive arguments, too, because they can
be promoted according to method invocation conversion rules. (JLS 2ed,
section 5.3)

I would never dream that some sort of subclassing might be applied to
primitives, and I still don't really get it, but I'll google "JLS 2ed,
section 5.3", over the weekend to study this.
If you don't know in advance the signature of the method you want to
invoke then for full generality you need to implement the method
resolution procedure specified in JLS(2ed) 15.12.2 - 15.12.4. The
compiler takes care of all that for you in standard method invocations,
but you have to deal with it yourself when you use reflection.

Shall google this as well. Aye, you gave me good weekend homework!
Because whatever the ServletContext implementation class happens to be,
it is definitely *not* javax.servlet.ServletContext.

True. I even tried casting the object parameter with (ServletContext),
to no avail.
Here is the exact simplified code that produces it. (Please excuse
the extra Invoker class, I use it to test non-servlet related
reflection.)


[edited for brevity:]
public class StaticMethodClass {


public static void methodA(ServletContext ctx, String s2){
System.out.print("Hello from methodA: ctx s2 . You passed "+
s2);
System.out.println(" from ServletContext: " + ctx.toString());
}
}


public class Invoker {

Method m = null;

public Invoker(String methodName, Object[] methodParameters){
runCustomMethod(methodName,methodParameters);
}

public void runCustomMethod(String methodName, Object[]
methodParams){
Class c = StaticMethodClass.class;
Method m = null;
Class[] parameterTypes = null;
if (methodParams!=null){
parameterTypes = new Class[methodParams.length];
for (int i=0;i<methodParams.length;i++){
parameterTypes = methodParams.getClass();
}
}
try {
m = c.getDeclaredMethod(methodName,parameterTypes);



You didn't say, but I can be pretty confident that the exception is
thrown during execution of the above method. The method is not found
True.

because it is not there. To make this work reliably your invoker must
get the list of all methods of the class in question that have the
correct name, determine (according to JLS rules) which are both
"applicable" to the arguments and accessible to the invoking code, and
among those choose the "most specific" method. It is possible for there
to be no single most specific method, which is a compile-time error in
non-reflective code, but which you can handle however you want in your
own invoker. ....
new Invoker("methodA", new Object[]{ctx,s1}); //this does NOT.

[does not work, that is.]

You're subtle unrelated correction of my //comment has taught me more,
much more, in general, and at an abstract level, than everything this
thread topic is all about. Incredible. When I saw this correction, I
realized I had quick, clear thinking professor mentoring me. I will post
both lines below so that others understand.

"new Invoker("methodA", new Object[]{s0,s1}); //this works ok."
"new Invoker("methodA", new Object[]{ctx,s1}); //this does NOT."

(Speaking at this level, can you please explain why you used [brackets]
for the correction? (Sorry, if I off on a tangent.) )
Do not be confused between the type of a
reference variable and the class of the object to which the variable
refers. They do not need to be the same. Even if the variable type is
a final class type, the variable value can still be null (and that will
cause your Invoker to throw a NullPointerException even though there may
be a suitable method it could invoke).

There are many api methods that a null parameter is used by design. Can
I then not call these reflectively? (Other than catching NPE.)
John Bollinger
(e-mail address removed)

Thank you, again.
-nat
 
J

John C. Bollinger

natG said:
I would never dream that some sort of subclassing might be applied to
primitives, and I still don't really get it, but I'll google "JLS 2ed,
section 5.3", over the weekend to study this.

JLS = Java Language Specification. The section number cited is correct
for the 2nd edition of the spec (= 2ed). Specifically, that section
describes how actual arguments of narrower primitive types can be
matched to methods with wider formal argument types. For instance, you
can pass a short to a method with formal parameter type int, long, or
double (or float, if I recall correctly).

[...]
True. I even tried casting the object parameter with (ServletContext),
to no avail.

Casting never changes the class of an object, which is what your Invoker
relies on. The sole effect of a typecast expression is to specify the
formal type of the expression result (with associated runtime check).
It is more relevant at compile-time than at runtime.
new Invoker("methodA", new Object[]{ctx,s1}); //this does NOT.


[does not work, that is.]

You're subtle unrelated correction of my //comment has taught me more,
much more, in general, and at an abstract level, than everything this
thread topic is all about. Incredible. When I saw this correction, I
realized I had quick, clear thinking professor mentoring me. I will post
both lines below so that others understand.

Don't butter me up too much, I might get delusions of grandeur. I am
pleased that you find my comments illuminating, though.
"new Invoker("methodA", new Object[]{s0,s1}); //this works ok."
"new Invoker("methodA", new Object[]{ctx,s1}); //this does NOT."

(Speaking at this level, can you please explain why you used [brackets]
for the correction? (Sorry, if I off on a tangent.) )

I would have inserted it into the comment itself, with brackets, but I
wanted to avoid confusion about who wrote what. Nothing deep there. I
snipped the first of those two lines because I had removed the
corresponding method from the target class.
There are many api methods that a null parameter is used by design. Can
I then not call these reflectively? (Other than catching NPE.)

Sure you can, but not with your current Invoker implementation. You
would have to explicitly check each argument to see whether it was null
before invoking getClass() on it. You would have less information to
use in determining the correct method to invoke when one or more
arguments are null, and thus greater likelihood of ambiguity, but there
is no inherent showstopper there. The API docs for class Method
describe how to pass a null argument to a reflectively invoked method.

I urge you to spend more time studying how to program generically and
less time studying reflection. There are techniques to minimize or even
eliminate need for reflection, and these should be employed wherever
possible. You will have cleaner, more maintainable code.


John Bollinger
(e-mail address removed)
 
N

natG

John said:
I urge you to spend more time studying how to program generically and
less time studying reflection. There are techniques to minimize or even
eliminate need for reflection, and these should be employed wherever
possible. You will have cleaner, more maintainable code.


John Bollinger
(e-mail address removed)

Thank you much. This advice could not have come at a better time for me.
-nat
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top