Convert a String to a function call

Hi,

I'm doubting this is even possible, but I wanted to check before I gave up all hope :)

Is it possible to convert a String to an actual function call in Java?

For example if I have the following:

String functionName = "classname.myFunction()";

is there a way to actually call the function classname.myFunction(); within my program from that String?

The reason I need to do this is rather complicated, but maybe one of you will have a better solution if there is no way to convert a String to a function:

I am using a JSP form to collect data from a user. I have already created a Java Bean to do data validation of the data submitted on the form. This bean checks the data when the form is submitted. If the data is correct, the form data will be forwarded to another JSP form that processes the data and inserts it into the database. If the data is incorrect the user is sent back to the form with all of the data they already entered still filled in on the form so they can correct the problems. I accomplish this by setting the "value" of each form element on the form to call the "get" function from the form validation bean.

On the form I am creating I have a rather complex combination of form elements. One of which is a table with 5 rows that the user must fill in. The data in the far left column is just a label for each row pulled from the database and the following three columns are either text boxes or drop down menus that pull their contents from the database. I have created another Java Bean to prepare all of the data for this table (including automatically generating a unique form element "name" for each of the form elements in the table). The problem is I need to somehow (hopefully automatically) generate corresponding "get" function calls to the form validation bean for each of the form element that corresponds to my automatically generated form element names. This is where I was hoping I could convert the String into a function call to access the "get" functions of my Validation bean.

Also, If possible I'd love to have the form validation bean automatically generate the correct "get" and "set" functions for each of the form elements - sort of creating a Bean on the fly, but that's a whole other can of worms.

Any suggestions would be greatly appreciated.

Thanks in advance!

[2395 byte] By [intltravelnut] at [2007-9-30 20:33:45]
# 1

Yes. You use reflection. You need a bit of string parsing, and then reflection handles the rest.

[code]

public Object invoke(String target) throws Exception {

// Parse out the class and method names (I have not checked these substrings, so do some testing first)

int posClassDelimiter = target.indexOf(".");

String className = target.substring(0, posClassDelimiter);

int posMethodDelimiter = target.indexOf("(");

String methodName = target.substring(posClassDelimiter + 1, posMethodDelimiter - posClassDelimiter);

// Now get the class

Class theClass = null;

theClass = Class.forName(className);

// Now get the method

Object caller = theClass.newInstance();

Method method = theClass.getMethod(methodName, null); // if you have params, see the API docs

// Return the results

return method.invoke(caller, null); // if you have params, see the API docs

}

Saish at 2007-7-7 1:23:13 > top of Java-index,Administration Tools,Sun Connection...
# 2
Yes I agree with you
MohdSleem at 2007-7-7 1:23:14 > top of Java-index,Administration Tools,Sun Connection...
# 3

Thanks so much for your help!

I'm working on testing the approach and have run into a problem.

I've created a Java Bean called MethodCaller which includes the "invoke" fuction you provided above with one modification:

I changed:

String methodName = target.substring(posClassDelimiter + 1, posMethodDelimiter - posClassDelimiter);

to:

String methodName = target.substring(posClassDelimiter + 1, posMethodDelimiter);

Then I tried using the method as follows (The data type DataPair is a class that I created in a Java Bean. the method I try to call with your "invoke" function called getDescription() should return the "Hello World" portion of the DataPair called tester.):

DataPair tester = new DataPair();

tester.put("1","Hello World");

String target = "tester.getDescription()";

MethodCaller mc = new MethodCaller();

String result = (String)mc.invoke(target);

When I run this code I get the error message below. I'm I missunderstanding how to use the "invoke" method? It appears that it is not possible to pass a particular instance of a class to the "invoke" method. Is that correct?

THANKS!

:

java.lang.ClassNotFoundException: tester

at com.caucho.util.DynamicClassLoader.loadClass(DynamicClassLoader.java:538)

at java.lang.ClassLoader.loadClass(ClassLoader.java:235)

at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)

at java.lang.Class.forName0(Native Method)

at java.lang.Class.forName(Class.java:141)

at teska.methodcaller.MethodCaller.invoke(MethodCaller.java:43)

at _test._Hashtable_0Vector_0Test__jsp._jspService(/home/teska/public_html/rusd/test/Hashtable_Vector_Test.jsp:120)

at com.caucho.jsp.JavaPage.service(JavaPage.java:75)

at com.caucho.jsp.Page.subservice(Page.java:506)

at com.caucho.server.http.FilterChainPage.doFilter(FilterChainPage.java:182)

at com.caucho.server.http.Invocation.service(Invocation.java:315)

at com.caucho.server.http.RunnerRequest.handleRequest(RunnerRequest.java:346)

at com.caucho.server.http.RunnerRequest.handleConnection(RunnerRequest.java:274)

at com.caucho.server.TcpConnection.run(TcpConnection.java:139)

at java.lang.Thread.run(Thread.java:534)

intltravelnut at 2007-7-7 1:23:14 > top of Java-index,Administration Tools,Sun Connection...
# 4

Your problem is in trying to invoke "tester.getDescription()". You should re-write it as DataPair.getDescription(). Remember that the methodology we are using instantiates a class by name (not an instance name or an instance). It also calls Class.newInstance() creating an object and calling its no-argument constructor. You could use the instance of testor in a reflective call, having tester passed as a parameter to an overloaded version of MethodHelper's invoke method.

It would look something like:

public Object invoke(String className, String methodName, Object caller) throws Exception

// Use normal way to create the class and get a reference to the desired method

Method method = Class.forName(className).getMethod(methodName, null);

// This time use the instance of caller you passed in instead of calling newInstance() on the class.

return method.invoke(caller, null); // caller is the instance of tester

I'm not saying you have to do it one way or the other, that will in large part depend on what you are trying to achieve. But that is how you could use an actual instance in the reflective call (as the caller). However, you cannot use the instance name for className (or methodName either).

Hope that clears it up a bit.

- Saish

"My karma ran over your dogma." - Anon

Saish at 2007-7-7 1:23:14 > top of Java-index,Administration Tools,Sun Connection...
# 5

Hi Saish ,

Thanks again for your help! I've tried rewritig my function in my Java Bean and the code and parameters in my JSP page that call that function, but I'm wrunning into a strange error. I've looked everywhere I can think of for what is causeing the problem, but I've given up. I'm hoping you will have some more insights!

Below is my Java Bean:

================================================================================

package teska.methods;

import java.util.*;

import java.lang.reflect.*;

import teska.helper.*;

public class MethodCaller {

/*

=================================================================================================

CONSTRUCTOR - INSTANTIATE BEAN AND SET VARIABLES

=================================================================================================

*/

public MethodCaller() {

}

/*

=================================================================================================

invoke a method based on a String passing in the String and an Object that the method should be

called on

=================================================================================================

*/

public Object runMethod(String target, Object caller) throws Exception {

// Parse out the class and method names

int posClassDelimiter = target.indexOf(".");

String className = target.substring(0, posClassDelimiter);

int posMethodDelimiter = target.indexOf("(");

String methodName = target.substring(posClassDelimiter + 1, posMethodDelimiter);

// Use normal way to create the class and get a reference to the desired method

Method method = Class.forName(className).getMethod(methodName, null);

// ********** The line below is #43 - as referenced in the error message below ***************

// the same error occurs if I include the 2 lines below (converting the caller from an Object to a

// DataPair) or not.

DataPair dp = new DataPair();

dp = (DataPair)caller;

// This time use the instance of caller you passed in instead of calling newInstance() on the class.

return method.invoke(dp, null); // caller is the instance of tester

}

/*

=================================================================================================

invoke a method based on a String passing in the String and creating a new Object to run it on

=================================================================================================

*/

public Object runMethod(String target) throws Exception {

// Parse out the class and method names (I have not checked these substrings, so do some testing first)

int posClassDelimiter = target.indexOf(".");

String className = target.substring(0, posClassDelimiter);

int posMethodDelimiter = target.indexOf("(");

String methodName = target.substring(posClassDelimiter + 1, posMethodDelimiter);

// Now get the class

Class theClass = null;

theClass = Class.forName(className);

// Now get the method

Object caller = theClass.newInstance();

Method method = theClass.getMethod(methodName, null); // if you have params, see the API docs

// Return the results

return method.invoke(caller, null); // if you have params, see the API

}

} // close class

================================================================================

Below is the code in my JSP page to call the Java Bean Method:

================================================================================

<%@ page language="java" %>

<html>

<head>

<title>Test</title>

</head>

<%@ page import="java.util.*" %>

<%@ page import= "teska.helper.*" %>

<%@ page import="teska.methods.*" %>

<jsp:useBean id="DataPair" class="DataPair" scope="page" />

<jsp:useBean id="MethodCaller" class="MethodCaller" scope="page" />

<body>

CONVERT A STRING TO A FUNCTION CALL USING REFLECTION

<%

// Declare an empty DataPair (from teska.helper import) called tester

DataPair tester = new DataPair();

// Insert strings into tester

tester.put("1","Hello World");

// wrap the tester DataPair in an Object wrapper

Object caller = (Object)tester;

// create a string to represent the text of the method I want to call

String target = "DataPair.getDescription()";

// Create a new MethodCaller object (from the teska.methods import) called mc

MethodCaller mc = new MethodCaller();

// Create a string called "result" to hold the returned value from the getDescription() method called using

// the overloaded runMethod function in the MethodCaller Java Ban.

// ********** The line below is #121 - as referenced in the error message below ***************

String result = (String)mc.runMethod(target, caller);

%>

The description of the DataPair is: <%= result %>

</body>

<script language="JavaScript">

<!--

window.open = SymRealWinOpen;

//-->

</script>

</html>

================================================================================

The error message I get when I view the JSP page is:

================================================================================

java.lang.ClassNotFoundException: DataPair

at com.caucho.util.DynamicClassLoader.loadClass(DynamicClassLoader.java:538)

at java.lang.ClassLoader.loadClass(ClassLoader.java:235)

at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)

at java.lang.Class.forName0(Native Method)

at java.lang.Class.forName(Class.java:141)

at teska.methods.MethodCaller.runMethod(MethodCaller.java:43)

at _test._Hashtable_0Vector_0Test__jsp._jspService(/home/teska/public_html/rusd/test/Hashtable_Vector_Test.jsp:121)

at com.caucho.jsp.JavaPage.service(JavaPage.java:75)

at com.caucho.jsp.Page.subservice(Page.java:506)

at com.caucho.server.http.FilterChainPage.doFilter(FilterChainPage.java:182)

at com.caucho.server.http.Invocation.service(Invocation.java:315)

at com.caucho.server.http.RunnerRequest.handleRequest(RunnerRequest.java:346)

at com.caucho.server.http.RunnerRequest.handleConnection(RunnerRequest.java:274)

at com.caucho.server.TcpConnection.run(TcpConnection.java:139)

at java.lang.Thread.run(Thread.java:534)

================================================================================

I'm not understanding why it is not able to find the DataPair class. As you can see the DataPair is imported into both the JSP page and the MethodCaller Java Bean from the teska.helper import line.

Thanks in advance!

intltravelnut at 2007-7-7 1:23:14 > top of Java-index,Administration Tools,Sun Connection...
# 6

my application is in java. it converts data from foxpro( dbf file) to sql server . but gives me error message like "error coverting datetime type from character datatype." please tell me what to do? suppose column in foxpro database is set as date and in sql server it is datetime but when that date column is blank it gives me above error.

trupti at 2007-7-7 1:23:14 > top of Java-index,Administration Tools,Sun Connection...