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>

Reply via email to