From: Jeff E. <JEm...@lg...> - 2003-08-27 22:27:31
|
I agree with you. I've encountered this before too. I just thought I'd offer a different workaround. You could download the lastest CVS image from sourceforge to see whether someone has fixed this. I think you can log bugs and submit patches at the sourceforge site too. I'm sure that if it hasn't been fixed yet, Samuele would appreciate it if could submit a patch. -----Original Message----- From: Meghshyam Jagannath [mailto:sh...@ro...] Sent: Wednesday, August 27, 2003 4:21 PM To: Jeff Emanuel Subject: Re: [Jython-users] Error invoking operations of a public interfac e from Jython Hi Jeff, Sure, we can do that (and we actually did). But what if we are using thirdparty jar files that have this pattern and would like to use Jython for testing/integration purposes? We cannot change that code. Hence the question. Can we fix Jython to address this scenario as well? And if so how (or if it's already fixed, where from can I pick up the fix)? And we have this pattern all over the place and it'd be improper to make the classes public where they shouldn't be. After all Java does allow this creation of such patterns. And Jython should support it. thanks, Shyam Jeff Emanuel wrote: How about if you make your classes public but make their constructors package protected so no one can instantiate them outside your package? -----Original Message----- From: Meghshyam Jagannath [ mailto:sh...@ro... <mailto:sh...@ro...> ] Sent: Wednesday, August 27, 2003 1:01 PM To: jyt...@li... <mailto:jyt...@li...> Subject: [Jython-users] Error invoking operations of a public interface from Jython 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:sh...@ro... <mailto:sh...@ro...> <mailto:sh...@ro...> <mailto:sh...@ro...> OSS Business Unit (Formerly TCSI Corporation) Rocket Software http://www.rocketsoftware.com/oss <http://www.rocketsoftware.com/oss> <http://www.rocketsoftware.com/oss> <http://www.rocketsoftware.com/oss> |