I'm using the standard Oracle JDBC Driver (JDK6 Thin Driver) and attempting to call a method on PreparedStatement via Reflection. I can call the method directly, but when I attempt to call (what I believe is) the same method via Reflection, I get an IllegalAccessException indicating that the method I'm attempting to call does not have public access. Checking the Method object returned by the class, it shows it is a publci method (i.e. Modifier=1).
The same test using other JDBC Drivers (IBM's DB2, Microsoft's SQLServer, PostgreSQL etc.) all work as expected.
As this code is ultimately used in a JEE app in a Glassfish container, I'm using DataSource rather than a simple connection.
Here's a snippet from a short test program (sensitive items removed) that illustrates the problem:-
System.out.println("Creating DataSource");
DataSource ds = new OracleDataSource();
((OracleDataSource) ds).setUser("HINT");
((OracleDataSource) ds).setPassword("hint");
((OracleDataSource) ds).setURL("jdbc:oracle:thin:@server-name:1521:database-name");
Connection dbCon = ds.getConnection();
dbCon.setAutoCommit(true);
System.out.println("Connection obtained : " + dbCon.getClientInfo());
PreparedStatement pstmt = (OraclePreparedStatement)dbCon.prepareStatement("SELECT * FROM \"HINT\".\"COUNTRIES1\" WHERE \"COUNTRY_ID\" = ?");
if (useReflection) // True=Fails, False=Works!
{
Class clazz = pstmt.getClass();
Method method = clazz.getMethod("setString", new Class[]{int.class, String.class});
System.out.println("Modifiers=" + method.getModifiers());
System.out.println(method.toGenericString());
String value = "EG";
Object[] pstmtParams = new Object[]{new Integer(1), value};
method.invoke(pstmt, pstmtParams); // <<< Fails here
}
else
{
pstmt.setString(1, "EG");
}
And here's the output when useReflection is set TRUE:-
Creating DataSource
Connection obtained : {}
Modifiers=1
public void oracle.jdbc.driver.OraclePreparedStatementWrapper.setString(int,java.lang.String) throws java.sql.SQLException
java.lang.IllegalAccessException: Class com.****.dataaccess.TestOracle can not access a member of class oracle.jdbc.driver.OraclePreparedStatementWrapper with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.reflect.Method.invoke(Method.java:588)
at com.rocketsoftware.ascentserver.dataaccess.TestOracle.testPreparedStatementSetters(TestOracle.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
Digging into things, it looks like the PreparedStatement object returned by the driver is a wrapper class (OraclePreparedStatementWrapper) which extends and implements various internal classes, so obviously it's doing some clever stuff under the covers. However I always thought that there should be no difference calling a method marked with public access directly or via Reflection.
There is something obviously wrong here, but I just can't see it.....
I believe the problem is that you're trying to use a method which you've asked for via a class you don't have access to. That apparently implicitly limits the method's visibility, even though the method itself is public.
Here's a similar example:
// Foo.java
public interface Foo {
void method();
}
// FooFactory.java
package foo;
public class FooFactory {
public static Foo getFoo() {
return new FooImpl();
}
}
// FooImpl.java
class FooImpl implements Foo {
@Override
public void method() {
System.out.println("FooImpl.method");
}
}
// Bar.java
package bar;
import foo.*;
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception {
Foo foo = FooFactory.getFoo();
Method method = foo.getClass().getMethod("method");
method.invoke(foo);
}
}
This fails in the same way as your code:
Exception in thread "main" java.lang.IllegalAccessException:
Class bar.Test can not access a member of class foo.FooImpl with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:109)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:261)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:253)
at java.lang.reflect.Method.invoke(Method.java:599)
If you ask for the method via the interface which you can see though, it's fine:
Method method = Foo.class.getMethod("method");
So likewise in your code, you just need to change this line:
Class clazz = pstmt.getClass();
to:
// TODO: Change it to Class<?> to avoid the raw type :)
Class clazz = PreparedStatement.class;
Or just inline it into the next statement:
Method method = PreparedStatement.class.getMethod(...);
See more on this question at Stackoverflow