Hi,

We are using Jython 2.1 and seem to be running into a problem accessing methods of an interface. The attachment provides more details:
Background
==========
A common design pattern that we've used throughout our application is that the implementation packages (such as com.tcsi.ospclient.types) don't export as public symbols their own classes. Instead, the factory methods inside the package call the constructors, and return references to the public interface that this class implements. 

For example, the com.tcsi.osp.types contains this public interface:

  public interface IntList extends java.util.List {
	public add(int value);
	.....
  }


The implementation package,com.tcsi.ospclient.types contains the following class:

  class IntList implements com.tcsi.osp.types.IntList {
	public add(int value) {
		....
	}
	.....
  }



Note that the class has the default package-level permissions, whilst the methods are public. This is totally legal Java, and works fine from Java code. The fact that the implementation class definition is not visible outside of its package means that ALL code is forced to deal purely with the public interface definition, com.tcsi.osp.types.IntList. If someone is tempted to do a dodgy cast to the implementation, Java simply won't let them.



The problem
===========
The problem comes with Jython. Try the following:
import com
com.tcsi.osp.OSP.init()
i = com.tcsi.osp.types.Types.newIntList()
This all works fine. I get back an instance of com.tcsi.ospclient.types.IntList, since that's the native Java implementation of that type. 
Now, here's where the fun starts. Try this:
i.add(1)
Instead of working, I get an IllegalAccessException:
>>> i.add(1)
Traceback (innermost last):
File "<console>", line 1, in ?
java.lang.IllegalAccessException: Class org.python.core.PyReflectedFunction
can not access a member of class com.tcsi.ospclient.types.IntList with
modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
at java.lang.reflect.Method.invoke(Method.java:317)
at
org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java)
at org.python.core.PyMethod.__call__(PyMethod.java)
at org.python.core.PyObject.__call__(PyObject.java)
at org.python.core.PyInstance.invoke(PyInstance.java)
at org.python.pycode._pyx55.f$0(<console>:1)
at org.python.pycode._pyx55.call_function(<console>)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyCode.call(PyCode.java)
at org.python.core.Py.runCode(Py.java)
at org.python.core.Py.exec(Py.java)
at org.python.util.PythonInterpreter.exec(PythonInterpreter.java)
at
org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java)
at
org.python.util.InteractiveInterpreter.runsource(InteractiveInterpreter.java)
at
org.python.util.InteractiveInterpreter.runsource(InteractiveInterpreter.java)
at org.python.util.InteractiveConsole.push(InteractiveConsole.java)
at
org.python.util.InteractiveConsole.interact(InteractiveConsole.java)
at org.python.util.jython.main(jython.java)

java.lang.IllegalAccessException: java.lang.IllegalAccessException: Class org.python.core.PyReflectedFunction can not access a member of class com.tcsi.ospclient.types.IntList with modifiers "public"
Theory
======
The reason for this is simple: the implementation class is not visible, and it fails. It seems to have forgotten about the fact that it's an implementation of a public interface, and essentially ignores it. I suspect that it's similar to writing the following java code:
Object i = com.tcsi.osp.types.Types.newIntList();
Method m = i.getClass().getMethod("add", new Class{Integer.TYPE});
m.invoke(i,new Object[]{new Integer(1)});
This also fails with the exactly the same exception, so for the sake of argument, let's say that Jython is implemented like this.

Now, clearly Jython doesn't have the concept of variable typing (or does it do this in some hidden way???). With this in mind, I was kind of ready to assume that this is just a limitation of Jython, EXCEPT..... for the fact that the problem does not appear to be universal. Some types, such as OidString have the same structure, and its getString() method works fine (perhaps because it's using the beans pattern, and Jython seems deals with that separately as a special case - look at the list of methods against supported by the Jython class). It does affect at least 3 of the list types though (intList, getList, stringList).

Also, I'm able to work around the above issue code with the following (essentially, using a different route to get numbers into the list):
import java
import com
numbers = java.util.ArrayList()
numbers.add(1)
i = com.tcsi.osp.types.IntList()
i.addAll(numbers)
This works. Interestingly addAll() is one of the methods inherited from the base list type. add(int) is defined locally. 

One view is that this is a bug in Jython. This can probably be fixed by changing the above code to something like this (basically looking for a public method with the right signature in ALL the interfaces supported by the class, and letting Java do the work):
Object i = com.tcsi.osp.types.Types.newIntList();
Method m = i.getClass().getMethod("add", new Class{Integer.TYPE});
try {
m.invoke(i,new Object[]{new Integer(1)});
}
catch (IllegalAccessException iae) {
Class[] intefaces = i.getClass().getInterfaces();
for (index=0;index<interfaces.length;index++) {
try {
m = interfaces[index].getMethod("add");
m.invoke(i,new Object[]{new Integer(1)});
break;
}
catch (NoSuchMethodException nsme) {
}
}
}
I concur this may be a bug in Jython. If so, is there a patch available to address the a forementioned scenario? If not, is this fixed in Jython 2.2 (and is this a stable version to use)? If not fixed in 2.2 (and it's not yet stable), I appreciate any pointers you can give us to fix the Jython source code. While we can probably put the a forementioned fix in PyReflectedFunction and PyJavaClass (as well?) and it may be technically right, is it correct from an engineering perspective?

thanks,
Shyam

Meghshyam (Shyam) Jagannath
mailto:shyam@rocketsoftware.com

OSS Business Unit (Formerly TCSI Corporation)
Rocket Software
http://www.rocketsoftware.com/oss