Community
Participate
Working Groups
3.2 (and I20060725) (will attach two example plug-ins demonstrating the problem) - have project A, exporting org.example.b, containing class SuperSamePack: ----------------- SuperSamePack.java ---------- package org.example.b; public class SuperSamePack { protected final Object method() { return null; } } ---------------------------------------------- - have project B depending on A, containing class Sub: ----------------------- Sub.java ----------------- package org.example.b; public class Sub extends SuperSamePack { Runnable fRunnable = new Runnable() { public void run() { System.out.println("Sub.fRunnable.run() before"); Object o = method(); System.out.println("Sub.fRunnable.run() after"); }; }; public void call() { fRunnable.run(); } } ---------------------------------------------------------- - start an eclipse workbench containing the two plug-ins and call Sub.call(). > expected: works, console output shows the log statements in fRunnable.run() < actual: The access to SuperSamePack::method throws an IllegalAccessError Notes: - the same works if the super class does not reside in the same package - the same works if the super class resides in the same project - callin SuperSamePack::method succeeds from a method in the body of Sub, but not from an inner class. Since this only happens in the plug-in environment, I assume the problem stems from some access checks done by OSGi. Apparently it has to do with the package split over two plug-ins.
Created attachment 47206 [details] illegal_access_plugins.zip Zip file containing two plug-ins, org.example.A and org.example.B. The latter contains an application that can be launched and that will demonstrate the problem.
Note: the problem showed up when developping the text plug-ins - we currently work around this by adding a private accessor method in Sub: public class Sub extends SuperSamePack { Runnable fRunnable = new Runnable() { public void run() { System.out.println("Sub.fRunnable.run() before"); Object o = internalMethod(); System.out.println("Sub.fRunnable.run() after"); }; }; private Object internalMethod() { return method(); } }
(In reply to comment #0) > > expected: works, console output shows the log statements in fRunnable.run() > < actual: The access to SuperSamePack::method throws an IllegalAccessError This happens because the 2 classes are loaded by 2 different classloader and are thus not part of the same package as far as the JVM is concerned. > > Notes: > - the same works if the super class does not reside in the same package This works because the compiler sees the classes are not part of the same package and so generates an accessor method to allow the method call. > - the same works if the super class resides in the same project Same classloader. > - callin SuperSamePack::method succeeds from a method in the body of Sub, but > not from an inner class. An inner class is not a subclass of the outer class and thus does not have protected access to methods from other packages. > > Since this only happens in the plug-in environment, I assume the problem stems > from some access checks done by OSGi. Apparently it has to do with the package > split over two plug-ins. > Actually just an issue with using more than one classloader. The java compiler is not aware that the super class will be loaded by a different classloader than the sub class and assumes that an accessor is not necessary.
There is nothing the Framework can do to help here. Well maybe some kind of AspectJ byte code weaving could help to add the accessor method at runtime ... kinda complicated. It may be possible for JDT to be more aware of OSGi and detect that the accessor is needed at compile time but I'm sure the JDT team would object to such a change. You may want to try opening a bug against JDT if you really want this fixed.
A simpler but equivalent case: - Have a package visible class 'PackageVisible' in plug-in A - In the same package in plug-in B, reference that class -> compiles fine - run it -> java.lang.IllegalAccessError: tried to access class org.example.b.PackageVisible from class org.example.b.Application Adding Philippe - can you comment on this from a compiler point-of-view? To me it seems really unfortunate to have code that compiles fine but fails with an error at run-time.
Keep in mind that this is the danger of split packages. This issue has been around in one form or another since Java moved to hierarchical classloaders. If you add a class to your own bundle in the javax.security package and then try to access a package protected class in the javax.security package from the VM you will get the same type of illegal access error.
(In reply to comment #5) Split packages, that is a package whose contents are not part of the same bundle (and hence loaded by the same classloader), is a bad design point. If you are writing new code, do not create a design with split packages. It is understood that an exisiting package may be split across bundles but since each bundle will have its own classloader, package private visibility will be lost across the split.
*** Bug 157381 has been marked as a duplicate of this bug. ***
I would like to reiterate Tom's comment #5. >To me >it seems really unfortunate to have code that compiles fine but fails with an >error at run-time. It was very time consuming for me that the compiler does not detect this case. I had two very similar codepaths, one working, and one not. The reason for the failing case turned out to be the split packages, which did not occur to me. The fact that the compiler flagged neither, and that only one codepath failed, was very confusing.
Sorry for late answering (I did miss it until now). Basically, until this behavior is documented in the JLS, the compiler cannot really change the lookup semantics to match this particular need. It comes from the differences between compile-time classpath and runtime classpath. One thing we could (maybe) do once we have fine grain access rules (currently only on a per type basis, but you could imagine on a per method/field basis in the future), PDE could tag the offending protected methods with certain access rules, and thus the compiler would get informed these methods are special. But strictly speaking, the compiler has currently no way to know that the compile time classpath doesn't match the runtime one.
The issue here is a bit more subtle than a difference in the classpath at development time vs runtime. Here the classpaths are calculated correctly at development time and runtime, but at runtime two classes from the same package are loaded by two different class loaders (a result of splitting a package). At runtime this prohibits package access between to two classes because you are only allowed package access if you are in the same named package AND you are loaded with the same class loader. If you add inner classes that uses a compiler generated accessor to access protected fields in the outer class then you run into more issues that can be difficult to understand. I'm no compiler expert, but it seems like this would require the compiler to be aware of component boundaries and apply the same package access rules at compile time that the OSGi framework does at runtime. Phillipe, is that something you think can be done with compiler access rules? Maybe we can have an access rule that is applied to packages exported by a project that states when another project accesses it they do not have package access?
Keep in mind fragments actually do need package level access to their host bundles. They are loaded with the same classloader, so we would have to have different rules for fragments.
I think the compiler assumes that all classes are loaded by the same classloader. If the compiler was aware that classes would be loaded by different classloaders at runtime, then it could make better decisions regarding the generated synthetic accessor methods.
Right, this is what I was trying to simplify by saying difference between compile-time and runtime CP. I hope that these subtleties could be depicted either by access rules, or by notions of component; though the latter is a bit weaker, since it really comes from classloader semantics, and this could affect a larger set of problems than components.
*** Bug 364485 has been marked as a duplicate of this bug. ***