Some final thoughts on using OSGi with Jini.

https://www.artima.com/weblogs/viewpost.jsp?thread=202304

This was always a contentious issue historically on Jini lists, I expect there will be vastly differing opinions on the subject, so I haven't made any decisions that would constrain developers options to any particular framework.   While I respectfully disagree with most of Michał's assessments and opinions regarding JGDMS on this occasion, I haven't made any changes that would prevent him from marshaling Objects, ClassLoaders or bytecode to replace codebase annotations in streams.  I still respect that Michał has an inquiring mind and is willing to experiment with some very complex issues.

Work to support OSGi in JGDMS is ongoing and doesn't require developers to install or use OSGi.

To dispel any confusion that might have arisen as a result of this discussion, and for anyone reading the email archives, JGDMS avoids annotating marshaling streams with codebase strings and uses a CodebaseAccessor service instead, which also provides signer certificates, not just space separated URI strings, these certificates may be self signed, they are dynamically granted permission as they have come from an authenticated service via a secure connection.   CodebaseAccessor is also used to authenticate the service, prior to unmarshaling its proxy.   ProxyCodebaseSpi allows you to use the modular framework of your choice for the provisioning of ClassLoaders and wiring of their dependencies, CodebaseAccessor provides information about the proxy's codebase requirements prior to its unmarshaling.   Atomic marshaling streams make no assumptions about the module structures of systems at either endpoint.   The atomic marshal stream only requires that identical proxy codebases are loaded at either endpoint, that is all, so that all classes the service uses will be resolved by the ClassLoader at the remote endpoint.  This is why all services are given their own independent marshaling streams to manage class visibility and avoid incorrect class resolution, which results in codebase annotation loss.  This is why I am not using the atomic marshaling framework to control or dictate ClassLoader hierarchies, and allow it instead to only focus on marshaling and unmarshaling object bytes securely.

This also solves a long standing issue with unmarshaling in OSGi.  Now OSGi (or other frameworks) can manage class resolution, rather than the object marshaling framework taking on this responsbility and trying to replicate the functionality of each or forcing developers to chose.   Marshaling objects has been uncoupled from class resolution in JGDMS.  The ClassLoader is only assigned once to the Endpoint, from that point on, all class resolution decisions are made by the ClassLoader.  This is unlike Jini and RMI marshalling of codebase annotations, where an attempt is made for each class to find its ClassLoader using stream annotations, if present during unmarshaling.

This work has taken many years to complete and a lot of research has gone into the decision making process.

JGDMS is modular and all jar file artifacts are also OSGi bundles.  AtomicILFactory, uses ClassLoaders at Endpoints, doesnt utilise the thread context ClassLoader, making it more compatible with modular frameworks.

JGDMS provides a RFC 3986 compliant URI that supports RFC 5952 normalization of IPv6 addresses, this provides a huge performance benefit, as DNS calls are not required to check for address equality.  I am still using space separated URI strings for codebases.   This URI class is not serializable, parsing URI strings instead is good security practice. https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/org/apache/river/api/net/Uri.java

One of the problems that I am currently working on in the support of OSGi, is that of proxy identity, there are many proxies that use the same bundle version, eg all common services, and these have static fields that shouldn't be shared among global providers.   For example different lookup services from different providers on the internet shouldn't share implementation classes.

I am currently investigating using Apache Aries and OSGi subsystems, so that service proxies remain independent and isolated from each other when utilizing OSGi as the underlying framework only cooperating on service API.   It is a non goal to make incompatible versions of Service APIs interoperate, for example if someone changes an Entry in a way that breaks compatibility, then I expect that it will use a different non compatible service version, and these will be considered different services.

Any assistance from anyone who is familiar with OSGi and Jini, would be much appreciated.

Regards,

Peter.

On 16/02/2022 9:32 pm, Peter Firmstone wrote:
Hi Michał,

I didn't take it personal, and don't expect you to take it personal either when I say, I'm pretty sure everyone here is aware of River / Jini limitations.

Anyway River has been a lot of fun, all the best for the future everyone, hope to see you around from time to time.

Cheers,

Peter.

On 16/02/2022 7:53 pm, Michał Kłeczek wrote:
Hi Peter,

On 16 Feb 2022, at 10:01, Peter Firmstone <peter.firmst...@zeus.net.au> wrote:

Inline below.

On 16/02/2022 5:24 pm, Michał Kłeczek wrote:

On 16 Feb 2022, at 04:25, Peter Firmstone <peter.firmst...@zeus.net.au> wrote:

From the CodebaseAccessor service.

The CodebaseAccessor proxy (local code) is passed as a parameter along with a MarshalledInstance of the proxy, by ProxySerializer to ProxyCodebaseSpi.

https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/net/jini/export/CodebaseAccessor.java <https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/net/jini/export/CodebaseAccessor.java>
Ok, so you have introduced a level of indirection to retrieve String codebase annotations. Why not go one step further and instead of:

interface CodebaseAccessor {
  String getClassAnnotation() throws IOException;
}

have something along the lines of (conceptually):

interface Codebase extends RemoteMethodControl {
  ClassLoader getClassLoader() throws IOException;
}


I personally wouldn't take this step, because CodebaseAccessor is a Remote interface and ClassLoader is a class of high privilege, so it presents a security risk.

Not really as implementation is constrained by the same security rules as any other code: you can constrain it via policy that only grants create ClassLoader permission to specific implementations.

See above:
you can move this code from the client to the service and let it provide the implementation of class resolution algorithm.


I would advise against that, the remote service JVM knows little about the client JVM, both are likely to have completely different ClassLoader hierarchies.

To be able to communicate they have to understand each other class loading mechanism anyway. In particular the class annotation String syntax and semantics has to be the same for both.

[...]


If using OSGi, OSGi will resolve the required dependencies and download them if not already present on the client, OSGi will give preference if a compatible version of the bundle dependencies already loaded at the client.   If using preferred classes the preferred class list will determine the order of preference, whether classes are loaded from the proxy codebase or the client ClassLoader (the parent loader of the proxy ClassLoader) first.
I also tried this route and it is a dead end because

* it is not possible to statically (ie. as part of the software package like OSGi manifest) provide dependency resolution constraints to be able to exchange arbitrarily constructed object graphs *


This is a limitation and compromise I have accepted, JGDMS doesn't attempt to load arbitrarily constructed object graphs, instead it ensures that both endpoints of a Service have the same class resolution view, the same proxy bundle version is used at the Server and client.

This breaks once there are multiple parties (services) involved because it means _all_ of them have to have their software versions synchronised in advance - which in turn makes the excercise moot: if you have to do that there is no need for mobile code anymore. What is really IMHO needed is a practical way of independent evolution of system components.

At the server, the proxy bundle is depended upon by the service implementation, but  nothing at the client depends upon the proxy bundle loaded there, instead the proxy bundle depends on the api loaded by the client. That is why JGDMS discourages marshaling of client subclasses that override service method parameter classes, because they cannot be exported as remote objects and are subject to codebase annotation loss and class resolution problems.

There is no subclassing in my example of RemoteEventSpacePublisher.

Sometimes, less is more, I've chosen this compromise in this instance to avoid complexity.  I saw little to be gained from the added complexity, it can be worked around by improving service api design and practices.

It cannot. How would you improve RemoteEventListener or JavaSpace API to avoid these issues?

  Instead the client can export method parameter interface types as remote objects, with independent ClassLoader visibility, that will resolve to common super interface types.

If there is a versioning problem at the client, where it uses an incompatible API with the client, ServiceDiscoveryManager will recognise the service is the incorrect type and discard it.

The whole point of my example is that this is not the issue of compatibility between client and service interface but it is inherent to existing class loading mechanism.


I recognise my own limitations, I'm not smart enough to solve the problems of de-serialization of arbitrary object graphs, so I have left it as a task for someone either smarter or more determined than myself and focused on what I believe is an acceptable and reliable compromise. It is because of my own limitations, that I reduced complexity, so that I (as well as inexperienced users) didn't have to worry about codebase annotation loss or class resolution issues.

I'm not saying it can't be solved, or that you won't succeed, simply that after a lot of thought, I decided it would be easier to reduce complexity and accept some limitations as this seems like the best compromise, there are also other things which have a higher priority for my time.

It wasn’t my intent to make this really interesting technical discussion personal but rather try to discuss the limitations of Jini/River and the possible directions.

I personally find the idea of mobile objects really interesting and very influential to the way how systems are implemented. Having a single uniform way of exchanging both code and data opens up a lot of possibilities.

Think of loggers being services implemented by mobile objects that can be replaced with new versions easily - log4shell would not be such a huge nightmare to handle.

What’s more - mobile code is something you are doing anyway - but in an ad-hoc ways that are incompatible with each other.

Michal

Reply via email to