I think you should not use any magic for calls that need classloading using a class named by a string (Like in deserialization). The only safe way is to explicitly specify a classloader to use.
Christian 2016-10-27 15:11 GMT+02:00 Christopher BROWN <[email protected]>: > Hi Scott, Hi Carsten, > > Overridding ObjectInputStream::resolveClass was what I needed. My > "fetch" method has 3 variants: > > /* uses Thread::getContextClassLoader */ > Object fetch(String id) > > /* uses type.getClass().getClassLoader(), may not work if "type" is an > interface or superclass, mainly useful for a bundle's internal types > only */ > <T> T fetchAs(String id, Class<T> type) > > /* more flexible, caller must understand how things work, eg: type can > be an interface type, the definer can be a lambda capable of providing > a reference to an implementation bundle's classloader */ > <T> T fetchAs(String id, Class<T> type, Supplier<ClassLoader> definer) > > The last signature variant makes it possible to get similar behavior > to the IClassResolver example suggested by Scott (the filter approach > is clever, but might have a higher maintenance cost ; the use of a > Supplier function makes it possible to delegate to such an > implementation however). I considered the PackageAdmin (bundle > wiring) suggestion by Carsten, but I figured it might deserialise the > wrong class version if multiple bundles define similar classes (the > first match might be incorrect, it seems non-trivial to correctly and > efficiently select a classloader without a "brute force, trial and > error" approach). > > I had also considered providing my service using a ServiceFactory, > which could access the consumer's bundle classloader automatically, > however if the code calling "store" is in a different bundle from that > calling "fetch", it wouldn't help much. Perhaps it would be possible > to combine this approach with use of WeakReferences (referring to the > classloader of the bundle that invoked "store"), automatically > invaliding the stored (serialized) object if the "storer" classloader > was de-referenced... but I felt that that approach was _too_ > aggressive (because all I can assume was that the "storer" classloader > was able to find the classloader -- via imported packages -- and not > assume that it was the actual classloader). > > For my use cases, that seems like the best trade-off for deterministic > behavior, small API surface, and flexibility (all the other "smart" > cases would seem to be vulnerable to side effects). > > Thanks for your suggestions, I hope this message (once archived) is of > use to others. > > -- > Christopher > > > > On 26 October 2016 at 20:21, Scott Lewis <[email protected]> wrote: > > Another (somewhat similar to Carsten's) approach to this is the one that > > we've implemented in ECF's Remote Services/RSA implementation [1]. > > > > We've got a service interface: IClassResolver [2] and a > ObjectInputStream > > subclass called ClassResolverObjectInputStream. The way that this works > is > > that when creating a ClassResolverInputStream one specifies a filter for > > finding (via service registry) the IClassResolver service instance to > use to > > resolve the class upon first deserialization. For example, a particular > > bundle (with version) that is to be responsible for resolving the classes > > (bundle.loadClass) for that ObjectInputStream [4]. Of course, > other/custom > > implementations of IClassResolver are possible as well, but so far we > have > > found the BundleClassResolver + ClassResolverObjectInputStream useful. > > > > Scott > > > > [1] http://wiki.eclipse.org/Eclipse_Communication_Framework_Project > > [2] > > http://download.eclipse.org/rt/ecf/latest/javadoc/org/ > eclipse/ecf/core/util/IClassResolver.html > > [3] > > http://download.eclipse.org/rt/ecf/latest/javadoc/org/ > eclipse/ecf/core/util/ClassResolverObjectInputStream.html > > [4] > > http://download.eclipse.org/rt/ecf/latest/javadoc/org/ > eclipse/ecf/core/util/BundleClassResolver.html > > > > > > On 10/26/2016 9:03 AM, Carsten Ziegeler wrote: > >> > >> Christopher Brown wrote > >>> > >>> Hello, > >>> > >>> I need to define an API for an OSGi service capable of persisting > >>> arbitrary > >>> Serializable objects as byte arrays (to disk, or over the network), and > >>> then capable of deserializing the object via the API. The objects to > be > >>> persisted will almost always be defined by another bundle, so the > bundle > >>> actually performing the serialization is almost always going to be > unable > >>> to access the classloader that originally provided the definition of > the > >>> serialized class (and any non-primitive attributes of that class). > >>> > >>> Any ideas upon how to use java.io.ObjectInputStream such that its > >>> .readObject() method can be forced to use an appropriate classloader? > I > >>> can't see how to override its default behavior. > >>> > >>> As for the API I need to implement (my service), it would be along the > >>> lines of: > >>> > >>> void service.store(String id, Object value); > >>> > >>> <T> T service.fetch(String id, Class<T> implementationType) > >>> > >>> ...where implementationType would need to be equivalent to > >>> value.getClass() > >>> (and not a superclass or implemented interface). The "fetch" method > >>> would > >>> need the "implementationType" parameter to access the CURRENT version > of > >>> the classloader (I can't store the classloader in the "store" method, > >>> first > >>> off because I can't serialize arbitrary classloaders -- via > >>> value.getClass().getClassloader() -- and also because the classloader > >>> might > >>> be the wrong version, if "value" was defined by a bundle that has since > >>> been reloaded). > >>> > >>> Even if I replaced the "implementationType" parameter with a > classloader > >>> reference (assuming the caller of the code knew which classloader to > >>> use), > >>> I still don't know how to override ObjectInputStream's default > >>> classloading > >>> behavior. > >>> > >>> Any ideas ? > >>> > >> You can create a subclass of ObjectInputStream and overwrite the > >> resolveClass method to solve the first project. > >> > >> Some time ago we wrote some code, that was using a dynamic class loader > >> in such a subclass that simply used Package Admin to find the bundle > >> providing the class and then using the corresponding class loader. Of > >> course this requires that the classes you want to load are publicly > >> exported. > >> > >> I'm not saying this is the best solution, but it did the trick for us. > >> > >> Regards > >> > >> Carsten > >> > > > > > > --------------------------------------------------------------------- > > To unsubscribe, e-mail: [email protected] > > For additional commands, e-mail: [email protected] > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [email protected] > For additional commands, e-mail: [email protected] > > -- -- Christian Schneider http://www.liquid-reality.de <https://owa.talend.com/owa/redir.aspx?C=3aa4083e0c744ae1ba52bd062c5a7e46&URL=http%3a%2f%2fwww.liquid-reality.de> Open Source Architect http://www.talend.com <https://owa.talend.com/owa/redir.aspx?C=3aa4083e0c744ae1ba52bd062c5a7e46&URL=http%3a%2f%2fwww.talend.com>

