Classloading across bundles and DynamicImport-package
Does Felix support anything that carries out a similar function to buddy class loading in Equinox? I'm aware of the DynamicImport-Package, but that rather supposes that the bundle who declares this knows the bundles that it needs as helpers to load classes.
Re: Classloading across bundles and DynamicImport-package
Jackson, Bruce wrote: Does Felix support anything that carries out a similar function to buddy class loading in Equinox? I'm aware of the DynamicImport-Package, but that rather supposes that the bundle who declares this knows the bundles that it needs as helpers to load classes. Sorry, no, Felix does not support any such option. And you are right, dynamic imports are probably not exactly what you want either. Currently, it is possible that the spec will eventually define something like this, but currently there is nothing. However, this should likely only be used in legacy situations, is that your situation? - richard
Re: Classloading across bundles and DynamicImport-package
Its not exactly a legacy situation. The problem I have is one that's shared with many object persistence frameworks that would naturally provide that capability as a service in an OSGi environment. In my case, the persistence model is based on the ability of any bundle to declare an interface which is suitably annotated, and which the persistence service will automatically construct suitable tables, views etc based on the interface methods, arguments and annotations. An object factory in the provider then generates a real object usable in the caller bundle, and whose methods are interpreted by a dynamic proxy into calls to the persistence layer. The problem comes when you wish to extract information from the persistence service. How do you recreate the stored object when the interface which was used to create the dynamic proxy is not in the persistence layer's bundle? On 07/08/2008 15:31, Richard S. Hall [EMAIL PROTECTED] wrote: Jackson, Bruce wrote: Does Felix support anything that carries out a similar function to buddy class loading in Equinox? I'm aware of the DynamicImport-Package, but that rather supposes that the bundle who declares this knows the bundles that it needs as helpers to load classes. Sorry, no, Felix does not support any such option. And you are right, dynamic imports are probably not exactly what you want either. Currently, it is possible that the spec will eventually define something like this, but currently there is nothing. However, this should likely only be used in legacy situations, is that your situation? - richard
Re: Classloading across bundles and DynamicImport-package
2008/8/7 Jackson, Bruce [EMAIL PROTECTED] Its not exactly a legacy situation. The problem I have is one that's shared with many object persistence frameworks that would naturally provide that capability as a service in an OSGi environment. In my case, the persistence model is based on the ability of any bundle to declare an interface which is suitably annotated, and which the persistence service will automatically construct suitable tables, views etc based on the interface methods, arguments and annotations. An object factory in the provider then generates a real object usable in the caller bundle, and whose methods are interpreted by a dynamic proxy into calls to the persistence layer. The problem comes when you wish to extract information from the persistence service. How do you recreate the stored object when the interface which was used to create the dynamic proxy is not in the persistence layer's bundle? one possible solution that would work with all the current frameworks is to use a service to get the interface class - based on something unique in the persisted object like the interface name or serialization id client bundles would register service implementations, which return any interface class(es) they know of that match the request. The persistence bundle can then iterate over the available services until it finds one that can supply the necessary runtime class (or it could use filters to avoid iterating) alternatively... client bundles could register services to recreate actual objects based on given data / schema. a lot of the classloading issues in OSGi can disappear when you use services to delegate tasks to the bundles that have the required knowledge. The hard part is deciding what to use services for, and what functionality to push into which bundle - but you can often use standard OO techniques like CRC cards to help brainstorm ideas imho, services are the best kept secret of OSGi... On 07/08/2008 15:31, Richard S. Hall [EMAIL PROTECTED] wrote: Jackson, Bruce wrote: Does Felix support anything that carries out a similar function to buddy class loading in Equinox? I'm aware of the DynamicImport-Package, but that rather supposes that the bundle who declares this knows the bundles that it needs as helpers to load classes. Sorry, no, Felix does not support any such option. And you are right, dynamic imports are probably not exactly what you want either. Currently, it is possible that the spec will eventually define something like this, but currently there is nothing. However, this should likely only be used in legacy situations, is that your situation? - richard -- Cheers, Stuart
Re: Classloading across bundles and DynamicImport-package
Am 07.08.2008 um 18:09 schrieb Stuart McCulloch [EMAIL PROTECTED] : 2008/8/7 Jackson, Bruce [EMAIL PROTECTED] Its not exactly a legacy situation. The problem I have is one that's shared with many object persistence frameworks that would naturally provide that capability as a service in an OSGi environment. In my case, the persistence model is based on the ability of any bundle to declare an interface which is suitably annotated, and which the persistence service will automatically construct suitable tables, views etc based on the interface methods, arguments and annotations. An object factory in the provider then generates a real object usable in the caller bundle, and whose methods are interpreted by a dynamic proxy into calls to the persistence layer. The problem comes when you wish to extract information from the persistence service. How do you recreate the stored object when the interface which was used to create the dynamic proxy is not in the persistence layer's bundle? one possible solution that would work with all the current frameworks is to use a service to get the interface class - based on something unique in the persisted object like the interface name or serialization id client bundles would register service implementations, which return any interface class(es) they know of that match the request. The persistence bundle can then iterate over the available services until it finds one that can supply the necessary runtime class (or it could use filters to avoid iterating) alternatively... client bundles could register services to recreate actual objects based on given data / schema. a lot of the classloading issues in OSGi can disappear when you use services to delegate tasks to the bundles that have the required knowledge. The hard part is deciding what to use services for, and what functionality to push into which bundle - but you can often use standard OO techniques like CRC cards to help brainstorm ideas imho, services are the best kept secret of OSGi... +1 regards, Karl On 07/08/2008 15:31, Richard S. Hall [EMAIL PROTECTED] wrote: Jackson, Bruce wrote: Does Felix support anything that carries out a similar function to buddy class loading in Equinox? I'm aware of the DynamicImport-Package, but that rather supposes that the bundle who declares this knows the bundles that it needs as helpers to load classes. Sorry, no, Felix does not support any such option. And you are right, dynamic imports are probably not exactly what you want either. Currently, it is possible that the spec will eventually define something like this, but currently there is nothing. However, this should likely only be used in legacy situations, is that your situation? - richard -- Cheers, Stuart
Re: Classloading across bundles
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.UndeclaredThrowableExceptionat $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
Classloading across bundles
2008/8/4 Jackson, Bruce [EMAIL PROTECTED] 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. I've just hacked up an extremely simple project that does exactly this: the client (B) passes a class to the proxy service (A), which creates a proxy based on the given class and another interface from A that is both public and exported from A. The project deploys and runs without any exceptions - also note the proxy bundle (A) doesn't need to import anything from the client (B). So I think there must be something else in your particular setup that is causing the visibility issue - I see from the stack trace that you're also using some sort of remoting, which does a Class.forName(...) I think this is the actual problem, to confirm this you could comment out the remoting call and see if the same exception occurs. To diagnose further we'd need access to some sort of public testcase (unless your project is already open-source) that recreates the issue, because the solution depends a lot on what the remoting code needs. I'll email you my example proxy project separately, as attachments often get removed from the mailing list - to build and deploy it use: mvn clean install pax:provision HTH 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.UndeclaredThrowableExceptionat $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?! -- Cheers, Stuart
Re: Classloading across bundles
Thanks for that, I've got to the bottom of the problem. Its quite obscure: obviously, my handler does many other things and contains a helper which in the constructor stores a copy of the class name. In the invoke, the stored class name is used in a Class.forName() call, and that is what causes the exception. Thanks for your help, much appreciated On 04/08/2008 11:38, Stuart McCulloch [EMAIL PROTECTED] wrote: 2008/8/4 Jackson, Bruce [EMAIL PROTECTED] 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. I've just hacked up an extremely simple project that does exactly this: the client (B) passes a class to the proxy service (A), which creates a proxy based on the given class and another interface from A that is both public and exported from A. The project deploys and runs without any exceptions - also note the proxy bundle (A) doesn't need to import anything from the client (B). So I think there must be something else in your particular setup that is causing the visibility issue - I see from the stack trace that you're also using some sort of remoting, which does a Class.forName(...) I think this is the actual problem, to confirm this you could comment out the remoting call and see if the same exception occurs. To diagnose further we'd need access to some sort of public testcase (unless your project is already open-source) that recreates the issue, because the solution depends a lot on what the remoting code needs. I'll email you my example proxy project separately, as attachments often get removed from the mailing list - to build and deploy it use: mvn clean install pax:provision HTH 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.UndeclaredThrowableExceptionat $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?! -- Cheers, Stuart
Classloading across bundles
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? Thanks Bruce
Re: Classloading across bundles
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
Re: Classloading across bundles
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
Re: Classloading across bundles
Jackson, Bruce 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? Ok, this makes more sense. I would assume that if you tried to make a proxy to clazz inside ObjectFactory.getObject() that it would use the ClassLoader of clazz, but apparently it is not. So, perhaps you want to tell dynamic proxy to use the class loader clazz.getClassLoader() when it creates the proxy. - richard 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
Re: Classloading across bundles
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
Re: Classloading across bundles
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
Re: Classloading across bundles
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
Re: Classloading across bundles
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. 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
Re: Classloading across bundles
my guess would be it's the objectmanagment class that can not be found by the classloader of the Foo class :-) regards, Karl Am 01.08.2008 um 17:31 schrieb 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. 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
Re: Classloading across bundles
Yes, I was on the point to say that. This is from JavaDoc of Proxy: All of the interface types must be visible by name through the specified class loader. You may try to define your own class loader that will delegate to the class loader of clazz and use as parent the class loader of the bundle that defines the ObjectManagement. But you are on mined ground. On Fri, Aug 1, 2008 at 5:51 PM, Karl Pauls [EMAIL PROTECTED] wrote: my guess would be it's the objectmanagment class that can not be found by the classloader of the Foo class :-) regards, Karl Am 01.08.2008 um 17:31 schrieb 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. 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 -- 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
Re: Classloading across bundles
Interesting point... I've done a bit more investigation around this, and actually, both clazz and ObjectManagement do appear to be accessible at the point that: obj= Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz, ObjectManagement.class}, new Invoker(clazz, id, tableName)); ...is called. I actually get a proxy object returned. However, its when I call a method on the object (declared in clazz) that I get the ClassNotFound exception. I guess this could still be because the class loading doesn't really take place until methods are invoked, but its strange all the same. On 01/08/2008 16:51, Karl Pauls [EMAIL PROTECTED] wrote: my guess would be it's the objectmanagment class that can not be found by the classloader of the Foo class :-) regards, Karl Am 01.08.2008 um 17:31 schrieb 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. 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
Re: Classloading across bundles
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