Hi Stuart

Well, that's interesting. To use your description of my problem (which is 
correct) the interface ObjectManagement declared in bundle A is indeed public, 
and it is also imported by B. B provides its own interface (in the code 
fragment, its passed as the argument clazz) which it passes to the service 
method in A.

So, according to your scenario, A should be able to create a proxy object with 
interfaces ObjectManagement (from A) and clazz (from B).
Running the code, it does actually appear that this works: I do get an object 
returned from the call to the factory in B (I can see it in Eclipse's debugger, 
including an InvocationHandler instance that looks ok):

Song s = (Song) ObjectFactory.getObject(Song.class, true);

However, as soon as I try to invoke methods on the object s, I get exceptions, 
for example:

Song s = (Song) ObjectFactory.getObject(Song.class, true); if(s == null) {

    System.err.println("!!! NULL");

} System.out.println("s: " + s.toString());

...the code creates the object s, it DOESN'T print "!!!NULL" but as soon as it 
gets to s.toString() (and I've tried this with other methods declared in Song 
too) I get:

java.lang.reflect.UndeclaredThrowableException    at $Proxy5.toString(Unknown 
Source)    at 
com.qualcomm.zoom.itunes.LibraryHandler.endElement(LibraryHandler.java:77)
Caused by: java.lang.ClassNotFoundException: com.qualcomm.zoom.itunes.Song    
at java.lang.ClassLoader.findClass(ClassLoader.java:413)    at 
java.lang.ClassLoader.loadClass(ClassLoader.java:316)    at 
java.lang.ClassLoader.loadClass(ClassLoader.java:251)    at 
org.eclipse.osgi.framework.internal.core.BundleLoader.findClassInternal(BundleLoader.java:429)
    at 
org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:369)
    at 
org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:357)
    at 
org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:83)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:251)    at 
java.lang.ClassLoader.loadClassInternal(ClassLoader.java:374)    at 
java.lang.Class.forName0(Native Method)    at 
java.lang.Class.forName(Class.java:164)    at 
com.qualcomm.pf.p2.remoting.Invoker.invoke(Invoker.java:145)

This looks like its the InvocationHandler that can't see the class declaration 
but that's also in the package exported by A, so I'm confused?!


On 01/08/2008 17:12, "Stuart McCulloch" <[EMAIL PROTECTED]> wrote:

2008/8/1 Jackson, Bruce <[EMAIL PROTECTED]>

> Yes, the code in ObjectFactory is basically:
>
> public static Object getObject(Class clazz) {
>
>    ...
>    obj= Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz,
> ObjectManagement.class}, new Invoker(clazz, id, tableName));
>    ...
>    return obj;
> }
>
> That's throws a ClassNotFoundException, I assume, because clazz (declared
> in B and not imported explicitly in A) is not loadable from with A.
>

I have some recent experience with this situation...

say you have a bundle A that creates proxies based on classes
from A and classes passed in from client bundles (B, C, D...)

if the classes used from A are all public, and are imported by all
the client bundles then you should be ok using the classloaders
from those client classes, because their classloaders are able to
see both public A classes and their own client classes (B, etc.).

However, if the proxy needs to see private classes from A then
you can't use the client classloaders because they cannot see
any of the private A classes

One option is then to add a "DynamicImport-Package: *" entry
to the manifest of A, and use A's classloader to create the proxy.
The DynamicImport-Package wildcard will dynamically add any
public packages from B, C, D, etc to A as required

A's classloader can then see its own classes and any public
client classes that it needs - however, this can cause leakage
of classes because once a class is in A's classloader, it can
be seen by any of the generated proxies.

Also, this won't work if the class passed in by the client bundle
is private to the client, because it's not available to be imported.

Another solution is to use a custom classloader to try to bridge
the two class spaces - the proxy bundle and the client bundle -
into one space which is then used by the generated proxy.

One such classloader is Equinox's ContextFinder, which looks
at the call stack to determine which classloader to load a class.
However, this is complex and may be overkill for this situation.

A simpler approach is used in the latest Guice code - we have
a very basic switch to delegate either to the client classloader
or the proxy bundle classloader.

note: you have to be careful when using a custom classloader
to proxy package-private classes, because the generated class
cannot access package-private classes in another classloader

more information about the Guice approach is available here:

   http://code.google.com/p/google-guice/wiki/BytecodeGeneration

and a similar approach is discussed here:

   http://techblog.andypiper.com/2008/07/classloader-hell-osgi-way.html

the actual Guice code to handle the bridging can be found here:


http://google-guice.googlecode.com/svn/trunk/src/com/google/inject/internal/BytecodeGen.java

we're also thinking about contributing this code back to CGLIB.

one last piece of advice - when debugging OSGi classloader
issues, you might find it useful to draw out the bundles and
mark what each exports/imports - you can then see whether
any classloader has access to all the necessary classes.

if no classloader has enough access, and you can't arrange
it via explicit imports, or implicit dynamic imports then you'll
need to use some sort of bridging approach.

HTH - apologies for the long email, got a bit carried away ;)

On 01/08/2008 16:25, "Alin Dreghiciu" <[EMAIL PROTECTED]> wrote:
>
> Do you use Proxy.newProxyInstance?
> The first param of this method is a class loader. So, what is the
> value you use for that param? In my view you should use the
> clazz.getClassLoader that is equal with the class loader that loaded
> Foo.
> And it should not matter that you export or not the the package that
> contains Foo.
>
> On Fri, Aug 1, 2008 at 5:17 PM, Jackson, Bruce <[EMAIL PROTECTED]>
> wrote:
> > I'm not passing any classloader with my class, just the object Foo.class.
> >
> > The issue is that bundle B (which is the caller to ObjectFactory declared
> in A) does not export Foo.class, and thus the class loader in A can't see
> it.
> > Now that's well, and good for most cases since I can always export the
> package that Foo.class is contained within, and import it in bundle A, but
> in this case, ObjectFactory becomes rather useless since its trying to offer
> a generic facility.
> >
> > I've tried getting the classloader of Foo.class, but that fails in the
> same way with a ClassNotFoundException. I could go through the pain in B of
> getting the bytes that make up the class, passing them to ObjectFactory and
> then doing a defineClass() there, but that seems to be very complex.
> >
> >
> > On 01/08/2008 16:09, "Alin Dreghiciu" <[EMAIL PROTECTED]> wrote:
> >
> > What class loader are you pasing to Proxy.newProxyInstance? You should
> > pass the one of the class you send as parameter so in your case
> > clazz.getClassLoader.
> >
> > On Fri, Aug 1, 2008 at 4:57 PM, Jackson, Bruce <[EMAIL PROTECTED]>
> wrote:
> >> Sorry, I don't think I made myself particularly clear:
> >>
> >> I have a bundle "A" which exports a package containing a class
> (ObjectFactory.class) with the method:
> >>
> >> public static Object getObject(Class clazz);
> >>
> >> This method creates a dynamic proxy object to be returned to the caller.
> >> I have bundles B, C, and D which wish to make use of this class, and
> which will call it passing an interface, for example:
> >>
> >> Foo foo = (Foo) ObjectFactory.getObject(Foo.class);
> >>
> >> However, ObjectFactory throws a ClassNotFoundException because Foo.class
> is not in the import list for bundle A. Is there a way of allowing this?
> >>
> >>
> >> On 01/08/2008 15:14, "Richard S. Hall" <[EMAIL PROTECTED]> wrote:
> >>
> >> Jackson, Bruce wrote:
> >>> Here's a question:
> >>>
> >>> I have a utility class in a bundle which will generate a dynamic proxy
> as a
> >>> service to other bundles running inside Felix. Lets call this bundle
> "A".
> >>>
> >>> Is this possible in Felix/OSGi without having having to explicitly
> declare
> >>> the import of the interface over which A will operate?
> >>>
> >>
> >> Well, you can get access to the interface class definition by calling
> >> Bundle.loadClass() on bundle A without importing from A, so then you
> >> should be able to use that to create the proxy, I would imagine.
> >>
> >> -> richard
> >>
> >>> Thanks
> >>>
> >>> Bruce
> >>>
> >>>
> >>
> >>
> >
> >
> >
> > --
> > Alin Dreghiciu
> > http://www.ops4j.org - New Energy for OSS Communities - Open
> > Participation Software.
> > http://www.qi4j.org - New Energy for Java - Domain Driven Development.
> > http://malaysia.jayway.net - New Energy for Projects - Great People
> > working on Great Projects at Great Places
> >
> >
>
>
>
> --
> Alin Dreghiciu
> http://www.ops4j.org - New Energy for OSS Communities - Open
> Participation Software.
> http://www.qi4j.org - New Energy for Java - Domain Driven Development.
> http://malaysia.jayway.net - New Energy for Projects - Great People
> working on Great Projects at Great Places
>
>


--
Cheers, Stuart

Reply via email to