You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@river.apache.org by Peter <ji...@zeus.net.au> on 2017/02/07 12:14:47 UTC

OSGi - deserialization remote invocation strategy

Proposed JERI OSGi class loading strategy during deserialization.

Record caller context - this is the default bundle at the beginning of 
the stack.  It is obtained by the InvocationHandler on the
client side.  The InvocationDispatcher on the server side has the 
calling context of the Remote
implementation.  The reflection dynamic proxy must be installed in the 
client's class loader, so the
InvocationHandler knows exactly what it is, it will be passed to the 
MarshalInputStream.  Any
interfaces not found in the client's bundle can be safely shed.  For a 
smart proxy the reflection proxy will
be installed in the smart proxy loader.  The smart proxy is obtained 
either via a reflection proxy or a MarshalledInstance.
MarshalledInstance also passes in the callers loader to the 
MarshalInputStream.

The smart proxy classloader is not a child loader of the clients loader, 
instead it's a bundle that imports
service api packages, with a version range that overlaps those already 
imported by the client.

Both Invocationhandler and InvocationDispatcher utilise 
MarshalInputStream and MarshalOutputStream, for marshalling parameters 
and return values.

The codebase annotation bundle's manifest contains a list of package 
imports.

Do we need to make a list of package imports for every new bundle that 
we load?
Do we need to record the wiring and packages and their imports from the 
remote end?

I don't think so, the bundles themselves contain this information, I 
think we just need to keep the view of available classes relevant to the 
current object being deserialized.

Codebase Annotations are exact versions!  They need to be to allow the 
service to ensure the correct proxy codebase is used.  Other proxy 
codebases will be installed in the client, possibly different versions, 
but these won't be visible through the resolved dependencies, because 
the proxy codebases only import packages at the client and OSGi 
restricts visibility to the current bundle's own classes and any 
imported packages.
Instead of appending dependencies to the codebase annotation they'll 
need be defined in the proxy's bundle manifest.  Of course if an 
identical version of a proxy codebase bundle is already installed at the 
client, this will be used again.

Because a bundle generally imports packages (importing entire bundles is 
discouraged in OSGi), there may be classes
that aren't visible from those bundles, such as transient imports, but 
also including private packages that aren't exported, private
implementations need to be deserialized, but is it possible to do so 
safely, without causing package
conflicts?   Private implementation classes can be used as fields within 
an exported public object, but cannot and should not
escape their private scope, doing so risks them being resolved to a 
bundle with the version of the remote end, instead of the locally 
resolved / wired package, causing ClassClassExceptions.

Initial (naive) first pass strategy of class resolution (for each branch 
in the serialized object graph)?:
1.    Try current bundle on the stack (which will be the callers bundle 
if we haven't loaded any new bundles yet).
2.    Then use the package name of a class to determine if the package 
is loaded by any of the bundles
referenced by the callers bundle imports (to handle any private 
implementation packages
that aren't in the current imports).  Is this a good idea? Or should we 
go straight to step 3
and let the framework resolve common classes, what if we use a different 
version to the
client's imported bundle?  Should we first compare our bundle annotation 
to the currently
imported bundles and select one of those if it's a compatible version?  
Yes, this could be an
application bundle, otherwise goto 3.
3.    Load bundle from annotation (if already loaded, it will be an 
exact version match).  Place the
new bundle on top of the bundle stack, remove this bundle from the stack 
once all fields of
this object have been deserialized, returning to the previous bundle 
context.  We are relying
on the current bundle to wire itself up to the same package versions of 
the clients bundle
imports, for shared classes.  Classes that use different bundles will 
not be visible to the client,
but will need to be visible to the current object's bundle.
4.    Place a bundle reference on the stack when a new object is 
deserialized from the stream and
remove it once all fields have been deserialized. (we might need to 
remember stack depth).
5.    Don't place non bundle references on the stack.  For example 
system class loader or any
other class loader, we want resolution to occur via the OSGi resolution 
process.

What about a simpler strategy (again naive), where we don't attempt to 
resolve private implementation classes?
1.    The calling class' bundle, is given priority.
2.    Load bundle from annotation (exact version), when not found in 
calling class.
3.    No stack, what if an application bundle from server is loaded that 
conflicts with an existing
bundle resolved by the client?
4.    What about walking back through the stack?  Probably unnecessary, 
as the containing object
will reference the class by a common interface, the outer object may not 
need to reference
it at all.  But what if the outer object passed it in during construction?

Revised strategy:
1.    Attempt to load from current bundle on stack (the stack begins 
with the client's Bundle, each
node in the graph has its bundle added to the stack and is also removed 
after that node is completely deserialized.
2.    If unsuccessful, walk back through deserialized bundle reference 
stack and attempt to load class.
Why not start at the beginning of the stack?  We are expecting bundles 
to wire up to
currently loaded versions, but bundles can import different package 
versions for
implementation, safest to start with current bundle and consult parent 
if not found in the current bundle
dependency graph, ie possibly passed in during object construction or an 
handback
implemented in the client, from an earlier invocation or dependency 
injected.
3.    The client is responsible for determining compatibility with the 
service api it's interested in
from the Import Package Entry's, prior to unmarshalling a service proxy.
4.    If a bundle previously on the stack resolves a class, then this 
object's bundle reference is placed
on the top of the stack, it is removed once the current object and all 
it's fields have been completely deserialized.
5.    Load bundle from annotation (exact version).
6.    No attempt will be made to directly load from wired bundles, 
always rely on wires,
otherwise we may utilise an incompatible package / bundle.

Do we need a graph of the wiring from the remote end?
During serialization (from the remote end) do we need to determine if a 
bundle has dependants and send some sort of version range information?
When a class descriptor is read in from a stream, the class descriptor 
contains information
about fields and it's serializable supertype class (if it exists)
are also read in from the stream, before any field objects are read in, 
the declared field types
are visible from the bundle of the current object being deserialized.  
The objects that will be
assigned to those field types must also resolve to those types.  Hence 
bundles being resolved as part
of deserialization must favour already resolved packages for imports.
What if a bundle requires a specific package version?  This is why the 
bundle must be given first
attempt to resolve an objects class and rely on the bundle dependency 
resolution process.
OSGi must be allowed to wire up dependencies, we must avoid attempting 
to make decisions about
compatibility and use the current bundle wires instead (our stack).

The BundleReference stack is designed to follow the wires (dependency 
links between bundles),
to allow private classes to be resolved, as they're not visible from 
other bundles.

We can't rely on annotations to resolve private classes, because we 
can't predict the way bundle
dependency's are resolved in remote JVM's.

General recommendations for OSGi:
*    The service should use as wide a version range as possible for 
service api.
*    It is better to create new service api in a new bundle than to 
evolve in a backward compatible manner, as
an incremental change may not be compatible if additional classes and 
methods are missing
from the client, that the service proxy depends on.
*    Don't split packages.
*    Private implementation classes are ok, provided they remain within 
public exported classes and don't escape, otherwise
they may not link up properly upon deserialization.
*    The proxy should minimise the package imports it uses.
*    There must be only one compatible service api version installed 
already in the client.
*    Duplicates of incompatible versions of service api are ok.

The catch is, it may not be possible to build the bundle stack without 
some programming hooks in ObjectInputStream.

Unfortunately we don't have any control over OIS, the necessary hooks 
could however be added to AtomicMarshalInputStream.

Cheers,

Peter.

Re: OSGi - deserialization remote invocation strategy

Posted by "Michał Kłeczek (XPro Sp. z o. o.)" <mi...@xpro.biz>.
Comments inline

Niclas Hedhman wrote:
> 4. For Server(osgi)+Client(osgi), number of options goes up. In this space,
> Paremus has a lot of experience, and perhaps willing to share a bit,
> without compromising the secret sauce? Either way, Michal's talk about
> "wiring" becomes important and that wiring should possibly be
> re-established on the client side. The insistence on "must be exactly the
> same version" is to me a reflection of "we haven't cared about version
> management before", and I think it may not be in the best interest to load
> many nearly identical bundles just because they are a little off, say stuff
> like guava, commons-xyz, slf4j and many more common dependencies.
This problem is generally unsolvable because there are contradicting 
requirements here:
1. The need to transfer object graphs of (unknown) classes
2. The need to optimize the number of class versions (and the number of 
ClassLoaders) in the JVM

It might be tempting to do the resolution on the client but it is 
(AFAIR) NP-hard
- the object graph is a set of constraints on possible module (bundle) 
versions. Plus there is a whole
set of constraints originating from the modules installed in the 
container prior to graph deserialization.

So the only good solution for a library is to provide a client with an 
interface to implement:
Module resolve(Module candidate) (or Module[] resolve(Module[] candidates))
and let it decide what to do.

>
> Peter wrote;
>> This is why the bundle must be given first
>> attempt to resolve an objects class and rely on the bundle dependency
> resolution process.
>> OSGi must be allowed to wire up dependencies, we must avoid attempting to
> make decisions about
>> compatibility and use the current bundle wires instead (our stack).
>
> Well, not totally sure about that. The 'root object classloader' doesn't
> have visibility to serialized objects, and will fail if left to do it all
> by itself. And as soon as you delegate to another BundleClassLoader, you
> have made the resolution decision, not the framework. Michal's proposal to
> transfer the BundleWiring (available in runtime) from the server to the
> client, makes it somewhat possible to do the delegation. And to make
> matters worse, it is quite common that packages are exported from more than
> one bundle, so the question is what is included in the bundleWiring coming
> across the wire.
The whole issue with proposals based on the stream itself is the fact 
that to resolve properly
one have to walk the whole graph first to gather all modules and their 
dependencies.

It is much better to simply provide the module graph (wiring) first (at 
the beginning of the stream)
and only after resolution of all the modules - deserialize the objects.

Thanks,
Michal

Re: OSGi - deserialization remote invocation strategy

Posted by Gregg Wonderly <ge...@cox.net>.
> On Feb 7, 2017, at 8:56 AM, Michał Kłeczek (XPro Sp. z o. o.) <mi...@xpro.biz> wrote:
> 
> Comments inline
> 
> Niclas Hedhman wrote:
>> 4. For Server(osgi)+Client(osgi), number of options goes up. In this space,
>> Paremus has a lot of experience, and perhaps willing to share a bit,
>> without compromising the secret sauce? Either way, Michal's talk about
>> "wiring" becomes important and that wiring should possibly be
>> re-established on the client side. The insistence on "must be exactly the
>> same version" is to me a reflection of "we haven't cared about version
>> management before", and I think it may not be in the best interest to load
>> many nearly identical bundles just because they are a little off, say stuff
>> like guava, commons-xyz, slf4j and many more common dependencies.
> This problem is generally unsolvable because there are contradicting requirements here:
> 1. The need to transfer object graphs of (unknown) classes
> 2. The need to optimize the number of class versions (and the number of ClassLoaders) in the JVM
> 
> It might be tempting to do the resolution on the client but it is (AFAIR) NP-hard
> - the object graph is a set of constraints on possible module (bundle) versions. Plus there is a whole
> set of constraints originating from the modules installed in the container prior to graph deserialization.
> 
> So the only good solution for a library is to provide a client with an interface to implement:
> Module resolve(Module candidate) (or Module[] resolve(Module[] candidates))
> and let it decide what to do.
> 
>> 
>> Peter wrote;
>>> This is why the bundle must be given first
>>> attempt to resolve an objects class and rely on the bundle dependency
>> resolution process.
>>> OSGi must be allowed to wire up dependencies, we must avoid attempting to
>> make decisions about
>>> compatibility and use the current bundle wires instead (our stack).
>> 
>> Well, not totally sure about that. The 'root object classloader' doesn't
>> have visibility to serialized objects, and will fail if left to do it all
>> by itself. And as soon as you delegate to another BundleClassLoader, you
>> have made the resolution decision, not the framework. Michal's proposal to
>> transfer the BundleWiring (available in runtime) from the server to the
>> client, makes it somewhat possible to do the delegation. And to make
>> matters worse, it is quite common that packages are exported from more than
>> one bundle, so the question is what is included in the bundleWiring coming
>> across the wire.
> The whole issue with proposals based on the stream itself is the fact that to resolve properly
> one have to walk the whole graph first to gather all modules and their dependencies.
> 
> It is much better to simply provide the module graph (wiring) first (at the beginning of the stream)
> and only after resolution of all the modules - deserialize the objects.

The missing notion of versioning in classloader was meant to be solved by URLs in the annotations.  That would provide explicit versioning control and through the use of the TCCL, code and objects could be isolated from other versions.  However, that’s not perfect, and so the preferred class loading mechanism is also a path that allows a “platform” or “hosting” environment to declare the classes that it will use to  interact with a client/proxy.

Practically, it’s exactly the NP-hard problem you say though.  There are “pretty good” solutions, but realistically there is not a perfect solution until there is an exact dependency graph which allows perfect specification of dependencies.

Gregg

> 
> Thanks,
> Michal


Re: OSGi - deserialization remote invocation strategy

Posted by Niclas Hedhman <ni...@hedhman.org>.
TL;DR
1. It sounds awfully complex, because my gut says that it is not a solvable
problem, especially since I don't see 4 distinct cases;
Server(osgi)+Client(osgi), Server(osgi)+Client(plain),
Server(plain)+Client(osgi) and Server(plain)+Client(plain), where the last
one is what we currently have. I am not sure how many of those you are
discussing.

2. For Server(osgi)+Client(plain), I don't think there is a generic
solution, due to "uses" in OSGi terms. The same object graph can contain
multiple versions of the same classes in non-hierarchical order, which to
me seems to be incompatible with the Java2 classloading mechanism.
Replicating this, would effectively need (as Michal suggested) the OSGi
framework to be booted up on the client (not impossible per se).

3. For Server(plain)+Client(osgi), I think tthat the 'easy' solution is
collapse all dependencies into a single bundle and load that bundle on the
OSGi framework. The exception being the API classes, i.e. those that must
have been present on the client to be able to use the service. Exactly how
to figure that out could be complicated and I have no good answers.

4. For Server(osgi)+Client(osgi), number of options goes up. In this space,
Paremus has a lot of experience, and perhaps willing to share a bit,
without compromising the secret sauce? Either way, Michal's talk about
"wiring" becomes important and that wiring should possibly be
re-established on the client side. The insistence on "must be exactly the
same version" is to me a reflection of "we haven't cared about version
management before", and I think it may not be in the best interest to load
many nearly identical bundles just because they are a little off, say stuff
like guava, commons-xyz, slf4j and many more common dependencies.

Peter wrote;
> This is why the bundle must be given first
> attempt to resolve an objects class and rely on the bundle dependency
resolution process.
> OSGi must be allowed to wire up dependencies, we must avoid attempting to
make decisions about
> compatibility and use the current bundle wires instead (our stack).

Well, not totally sure about that. The 'root object classloader' doesn't
have visibility to serialized objects, and will fail if left to do it all
by itself. And as soon as you delegate to another BundleClassLoader, you
have made the resolution decision, not the framework. Michal's proposal to
transfer the BundleWiring (available in runtime) from the server to the
client, makes it somewhat possible to do the delegation. And to make
matters worse, it is quite common that packages are exported from more than
one bundle, so the question is what is included in the bundleWiring coming
across the wire.


HTH

On Tue, Feb 7, 2017 at 8:14 PM, Peter <ji...@zeus.net.au> wrote:

> Proposed JERI OSGi class loading strategy during deserialization.
>
> Record caller context - this is the default bundle at the beginning of the
> stack.  It is obtained by the InvocationHandler on the
> client side.  The InvocationDispatcher on the server side has the calling
> context of the Remote
> implementation.  The reflection dynamic proxy must be installed in the
> client's class loader, so the
> InvocationHandler knows exactly what it is, it will be passed to the
> MarshalInputStream.  Any
> interfaces not found in the client's bundle can be safely shed.  For a
> smart proxy the reflection proxy will
> be installed in the smart proxy loader.  The smart proxy is obtained
> either via a reflection proxy or a MarshalledInstance.
> MarshalledInstance also passes in the callers loader to the
> MarshalInputStream.
>
> The smart proxy classloader is not a child loader of the clients loader,
> instead it's a bundle that imports
> service api packages, with a version range that overlaps those already
> imported by the client.
>
> Both Invocationhandler and InvocationDispatcher utilise MarshalInputStream
> and MarshalOutputStream, for marshalling parameters and return values.
>
> The codebase annotation bundle's manifest contains a list of package
> imports.
>
> Do we need to make a list of package imports for every new bundle that we
> load?
> Do we need to record the wiring and packages and their imports from the
> remote end?
>
> I don't think so, the bundles themselves contain this information, I think
> we just need to keep the view of available classes relevant to the current
> object being deserialized.
>
> Codebase Annotations are exact versions!  They need to be to allow the
> service to ensure the correct proxy codebase is used.  Other proxy
> codebases will be installed in the client, possibly different versions, but
> these won't be visible through the resolved dependencies, because the proxy
> codebases only import packages at the client and OSGi restricts visibility
> to the current bundle's own classes and any imported packages.
> Instead of appending dependencies to the codebase annotation they'll need
> be defined in the proxy's bundle manifest.  Of course if an identical
> version of a proxy codebase bundle is already installed at the client, this
> will be used again.
>
> Because a bundle generally imports packages (importing entire bundles is
> discouraged in OSGi), there may be classes
> that aren't visible from those bundles, such as transient imports, but
> also including private packages that aren't exported, private
> implementations need to be deserialized, but is it possible to do so
> safely, without causing package
> conflicts?   Private implementation classes can be used as fields within
> an exported public object, but cannot and should not
> escape their private scope, doing so risks them being resolved to a bundle
> with the version of the remote end, instead of the locally resolved / wired
> package, causing ClassClassExceptions.
>
> Initial (naive) first pass strategy of class resolution (for each branch
> in the serialized object graph)?:
> 1.    Try current bundle on the stack (which will be the callers bundle if
> we haven't loaded any new bundles yet).
> 2.    Then use the package name of a class to determine if the package is
> loaded by any of the bundles
> referenced by the callers bundle imports (to handle any private
> implementation packages
> that aren't in the current imports).  Is this a good idea? Or should we go
> straight to step 3
> and let the framework resolve common classes, what if we use a different
> version to the
> client's imported bundle?  Should we first compare our bundle annotation
> to the currently
> imported bundles and select one of those if it's a compatible version?
> Yes, this could be an
> application bundle, otherwise goto 3.
> 3.    Load bundle from annotation (if already loaded, it will be an exact
> version match).  Place the
> new bundle on top of the bundle stack, remove this bundle from the stack
> once all fields of
> this object have been deserialized, returning to the previous bundle
> context.  We are relying
> on the current bundle to wire itself up to the same package versions of
> the clients bundle
> imports, for shared classes.  Classes that use different bundles will not
> be visible to the client,
> but will need to be visible to the current object's bundle.
> 4.    Place a bundle reference on the stack when a new object is
> deserialized from the stream and
> remove it once all fields have been deserialized. (we might need to
> remember stack depth).
> 5.    Don't place non bundle references on the stack.  For example system
> class loader or any
> other class loader, we want resolution to occur via the OSGi resolution
> process.
>
> What about a simpler strategy (again naive), where we don't attempt to
> resolve private implementation classes?
> 1.    The calling class' bundle, is given priority.
> 2.    Load bundle from annotation (exact version), when not found in
> calling class.
> 3.    No stack, what if an application bundle from server is loaded that
> conflicts with an existing
> bundle resolved by the client?
> 4.    What about walking back through the stack?  Probably unnecessary, as
> the containing object
> will reference the class by a common interface, the outer object may not
> need to reference
> it at all.  But what if the outer object passed it in during construction?
>
> Revised strategy:
> 1.    Attempt to load from current bundle on stack (the stack begins with
> the client's Bundle, each
> node in the graph has its bundle added to the stack and is also removed
> after that node is completely deserialized.
> 2.    If unsuccessful, walk back through deserialized bundle reference
> stack and attempt to load class.
> Why not start at the beginning of the stack?  We are expecting bundles to
> wire up to
> currently loaded versions, but bundles can import different package
> versions for
> implementation, safest to start with current bundle and consult parent if
> not found in the current bundle
> dependency graph, ie possibly passed in during object construction or an
> handback
> implemented in the client, from an earlier invocation or dependency
> injected.
> 3.    The client is responsible for determining compatibility with the
> service api it's interested in
> from the Import Package Entry's, prior to unmarshalling a service proxy.
> 4.    If a bundle previously on the stack resolves a class, then this
> object's bundle reference is placed
> on the top of the stack, it is removed once the current object and all
> it's fields have been completely deserialized.
> 5.    Load bundle from annotation (exact version).
> 6.    No attempt will be made to directly load from wired bundles, always
> rely on wires,
> otherwise we may utilise an incompatible package / bundle.
>
> Do we need a graph of the wiring from the remote end?
> During serialization (from the remote end) do we need to determine if a
> bundle has dependants and send some sort of version range information?
> When a class descriptor is read in from a stream, the class descriptor
> contains information
> about fields and it's serializable supertype class (if it exists)
> are also read in from the stream, before any field objects are read in,
> the declared field types
> are visible from the bundle of the current object being deserialized.  The
> objects that will be
> assigned to those field types must also resolve to those types.  Hence
> bundles being resolved as part
> of deserialization must favour already resolved packages for imports.
> What if a bundle requires a specific package version?  This is why the
> bundle must be given first
> attempt to resolve an objects class and rely on the bundle dependency
> resolution process.
> OSGi must be allowed to wire up dependencies, we must avoid attempting to
> make decisions about
> compatibility and use the current bundle wires instead (our stack).
>
> The BundleReference stack is designed to follow the wires (dependency
> links between bundles),
> to allow private classes to be resolved, as they're not visible from other
> bundles.
>
> We can't rely on annotations to resolve private classes, because we can't
> predict the way bundle
> dependency's are resolved in remote JVM's.
>
> General recommendations for OSGi:
> *    The service should use as wide a version range as possible for
> service api.
> *    It is better to create new service api in a new bundle than to evolve
> in a backward compatible manner, as
> an incremental change may not be compatible if additional classes and
> methods are missing
> from the client, that the service proxy depends on.
> *    Don't split packages.
> *    Private implementation classes are ok, provided they remain within
> public exported classes and don't escape, otherwise
> they may not link up properly upon deserialization.
> *    The proxy should minimise the package imports it uses.
> *    There must be only one compatible service api version installed
> already in the client.
> *    Duplicates of incompatible versions of service api are ok.
>
> The catch is, it may not be possible to build the bundle stack without
> some programming hooks in ObjectInputStream.
>
> Unfortunately we don't have any control over OIS, the necessary hooks
> could however be added to AtomicMarshalInputStream.
>
> Cheers,
>
> Peter.
>



-- 
Niclas Hedhman, Software Developer
http://polygene.apache.org <http://zest.apache.org> - New Energy for Java

Re: OSGi - deserialization remote invocation strategy

Posted by "Michał Kłeczek (XPro Sp. z o. o.)" <mi...@xpro.biz>.
Are you proposing to provide a bootstrap object that will download some 
meta information prior to class resolution?

How does it differ from simply changing annotations to be those 
"bootstrap objects" instead of Strings?

Thanks,
Michal

Peter wrote:
> Proposed JERI OSGi class loading strategy during deserialization.
>
> Record caller context - this is the default bundle at the beginning of 
> the stack.  It is obtained by the InvocationHandler on the
> client side.  The InvocationDispatcher on the server side has the 
> calling context of the Remote
> implementation.  The reflection dynamic proxy must be installed in the 
> client's class loader, so the
> InvocationHandler knows exactly what it is, it will be passed to the 
> MarshalInputStream.  Any
> interfaces not found in the client's bundle can be safely shed.  For a 
> smart proxy the reflection proxy will
> be installed in the smart proxy loader.  The smart proxy is obtained 
> either via a reflection proxy or a MarshalledInstance.
> MarshalledInstance also passes in the callers loader to the 
> MarshalInputStream.
>
> The smart proxy classloader is not a child loader of the clients 
> loader, instead it's a bundle that imports
> service api packages, with a version range that overlaps those already 
> imported by the client.
>
> Both Invocationhandler and InvocationDispatcher utilise 
> MarshalInputStream and MarshalOutputStream, for marshalling parameters 
> and return values.
>
> The codebase annotation bundle's manifest contains a list of package 
> imports.
>
> Do we need to make a list of package imports for every new bundle that 
> we load?
> Do we need to record the wiring and packages and their imports from 
> the remote end?
>
> I don't think so, the bundles themselves contain this information, I 
> think we just need to keep the view of available classes relevant to 
> the current object being deserialized.
>
> Codebase Annotations are exact versions!  They need to be to allow the 
> service to ensure the correct proxy codebase is used.  Other proxy 
> codebases will be installed in the client, possibly different 
> versions, but these won't be visible through the resolved 
> dependencies, because the proxy codebases only import packages at the 
> client and OSGi restricts visibility to the current bundle's own 
> classes and any imported packages.
> Instead of appending dependencies to the codebase annotation they'll 
> need be defined in the proxy's bundle manifest.  Of course if an 
> identical version of a proxy codebase bundle is already installed at 
> the client, this will be used again.
>
> Because a bundle generally imports packages (importing entire bundles 
> is discouraged in OSGi), there may be classes
> that aren't visible from those bundles, such as transient imports, but 
> also including private packages that aren't exported, private
> implementations need to be deserialized, but is it possible to do so 
> safely, without causing package
> conflicts?   Private implementation classes can be used as fields 
> within an exported public object, but cannot and should not
> escape their private scope, doing so risks them being resolved to a 
> bundle with the version of the remote end, instead of the locally 
> resolved / wired package, causing ClassClassExceptions.
>
> Initial (naive) first pass strategy of class resolution (for each 
> branch in the serialized object graph)?:
> 1.    Try current bundle on the stack (which will be the callers 
> bundle if we haven't loaded any new bundles yet).
> 2.    Then use the package name of a class to determine if the package 
> is loaded by any of the bundles
> referenced by the callers bundle imports (to handle any private 
> implementation packages
> that aren't in the current imports).  Is this a good idea? Or should 
> we go straight to step 3
> and let the framework resolve common classes, what if we use a 
> different version to the
> client's imported bundle?  Should we first compare our bundle 
> annotation to the currently
> imported bundles and select one of those if it's a compatible 
> version?  Yes, this could be an
> application bundle, otherwise goto 3.
> 3.    Load bundle from annotation (if already loaded, it will be an 
> exact version match).  Place the
> new bundle on top of the bundle stack, remove this bundle from the 
> stack once all fields of
> this object have been deserialized, returning to the previous bundle 
> context.  We are relying
> on the current bundle to wire itself up to the same package versions 
> of the clients bundle
> imports, for shared classes.  Classes that use different bundles will 
> not be visible to the client,
> but will need to be visible to the current object's bundle.
> 4.    Place a bundle reference on the stack when a new object is 
> deserialized from the stream and
> remove it once all fields have been deserialized. (we might need to 
> remember stack depth).
> 5.    Don't place non bundle references on the stack.  For example 
> system class loader or any
> other class loader, we want resolution to occur via the OSGi 
> resolution process.
>
> What about a simpler strategy (again naive), where we don't attempt to 
> resolve private implementation classes?
> 1.    The calling class' bundle, is given priority.
> 2.    Load bundle from annotation (exact version), when not found in 
> calling class.
> 3.    No stack, what if an application bundle from server is loaded 
> that conflicts with an existing
> bundle resolved by the client?
> 4.    What about walking back through the stack?  Probably 
> unnecessary, as the containing object
> will reference the class by a common interface, the outer object may 
> not need to reference
> it at all.  But what if the outer object passed it in during 
> construction?
>
> Revised strategy:
> 1.    Attempt to load from current bundle on stack (the stack begins 
> with the client's Bundle, each
> node in the graph has its bundle added to the stack and is also 
> removed after that node is completely deserialized.
> 2.    If unsuccessful, walk back through deserialized bundle reference 
> stack and attempt to load class.
> Why not start at the beginning of the stack?  We are expecting bundles 
> to wire up to
> currently loaded versions, but bundles can import different package 
> versions for
> implementation, safest to start with current bundle and consult parent 
> if not found in the current bundle
> dependency graph, ie possibly passed in during object construction or 
> an handback
> implemented in the client, from an earlier invocation or dependency 
> injected.
> 3.    The client is responsible for determining compatibility with the 
> service api it's interested in
> from the Import Package Entry's, prior to unmarshalling a service proxy.
> 4.    If a bundle previously on the stack resolves a class, then this 
> object's bundle reference is placed
> on the top of the stack, it is removed once the current object and all 
> it's fields have been completely deserialized.
> 5.    Load bundle from annotation (exact version).
> 6.    No attempt will be made to directly load from wired bundles, 
> always rely on wires,
> otherwise we may utilise an incompatible package / bundle.
>
> Do we need a graph of the wiring from the remote end?
> During serialization (from the remote end) do we need to determine if 
> a bundle has dependants and send some sort of version range information?
> When a class descriptor is read in from a stream, the class descriptor 
> contains information
> about fields and it's serializable supertype class (if it exists)
> are also read in from the stream, before any field objects are read 
> in, the declared field types
> are visible from the bundle of the current object being deserialized.  
> The objects that will be
> assigned to those field types must also resolve to those types.  Hence 
> bundles being resolved as part
> of deserialization must favour already resolved packages for imports.
> What if a bundle requires a specific package version?  This is why the 
> bundle must be given first
> attempt to resolve an objects class and rely on the bundle dependency 
> resolution process.
> OSGi must be allowed to wire up dependencies, we must avoid attempting 
> to make decisions about
> compatibility and use the current bundle wires instead (our stack).
>
> The BundleReference stack is designed to follow the wires (dependency 
> links between bundles),
> to allow private classes to be resolved, as they're not visible from 
> other bundles.
>
> We can't rely on annotations to resolve private classes, because we 
> can't predict the way bundle
> dependency's are resolved in remote JVM's.
>
> General recommendations for OSGi:
> *    The service should use as wide a version range as possible for 
> service api.
> *    It is better to create new service api in a new bundle than to 
> evolve in a backward compatible manner, as
> an incremental change may not be compatible if additional classes and 
> methods are missing
> from the client, that the service proxy depends on.
> *    Don't split packages.
> *    Private implementation classes are ok, provided they remain 
> within public exported classes and don't escape, otherwise
> they may not link up properly upon deserialization.
> *    The proxy should minimise the package imports it uses.
> *    There must be only one compatible service api version installed 
> already in the client.
> *    Duplicates of incompatible versions of service api are ok.
>
> The catch is, it may not be possible to build the bundle stack without 
> some programming hooks in ObjectInputStream.
>
> Unfortunately we don't have any control over OIS, the necessary hooks 
> could however be added to AtomicMarshalInputStream.
>
> Cheers,
>
> Peter.