Hi Michał, responses inline below.

On 15/02/2022 10:22 pm, Michał Kłeczek wrote:


On 15 Feb 2022, at 13:05, Peter Firmstone <peter.firmst...@zeus.net.au> wrote:


How the client knows the code needed to deserialise?

The service provides this information, typically in a services configuration,

How is this configuration provided to the client and when?

In the service configuration file, with AtomicILFactory, you specify a class, whose ClassLoader will perform class resolution for the ServerEndpoint.  You should also use a configuration entry for your service codebase string as well as keystores for certificates.  Then implement the CodebaseAccessor service, this is not implemented by the smart proxy, just the service at the server, a separate CodebaseAccessor proxy is created during service export, this is marshalled prior to the unmarshalling of the service proxy, using only local code, to allow provisioning of the codebase.

Heres an example in Reggie:

https://github.com/pfirmstone/JGDMS/blob/a774a9141e6571f1d7f9771f74b714850d447d3e/JGDMS/services/reggie/reggie-service/src/main/java/org/apache/river/reggie/RegistrarImpl.java#L659

See below for further info how the the codebase string is obtained from the CodebaseAccessor service proxy (local code).


by default this is a space separated list of URI, similar to a codebase annotation, but it doesn't have to be.  JERI manages the deserialization of code through a default ProxyCodebaseSpi implementation,

How does the default ProxyCodebaseSpi implementation know where to download code from if there are no annotations?


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/org/apache/river/api/io/ProxySerializer.java

Proxies are serialized separately from the current marshalling stream by ProxySerializer.  It identifies proxies in the stream and packages them for independant unmarshalling.   It does this because it is unlikely the current streams Endpoint ClassLoader will be able to resolve classes, it is treated as a separate concern, or independent entity.


the client applies constraints, to ensure that input validation is used as well as any other constraints, such as principals, or encryption strength. ProxyCodebaseSpi can be customized by the client, so the client may implement ProxyCodebaseSpi if it wants to do something different, eg use OSGi or Maven to manage dependency resolution.

Ok, so the client has to know in advance how to load the service code?

Yes, the client knows how to load the service code dynamically just prior to proxy unmarshalling, a default ProxyCodebaseSpi is provided for that purpose and it is used by default.

If you are using Maven, eg Rio Resolver, or OSGi, then a ProxyCodebaseSpi implementation specific to one of these should be used.  If you have a mixed environment, then you can use the codebase string to determine which to use.


Does it require to have it installed in advance? If so - how?

Only JGDMS platform and JERI.


How are service proxy classes loaded then?

ProxyCodebaseSpi::resolve does this by provisioning a ClassLoader then unmarshalling the proxy into it.

OSGi:

https://github.com/pfirmstone/JGDMS/blob/a774a9141e6571f1d7f9771f74b714850d447d3e/JGDMS/jgdms-osgi-proxy-bundle-provider/src/main/java/org/apache/river/osgi/ProxyBundleProvider.java#L131

Preferred classes:

https://github.com/pfirmstone/JGDMS/blob/a774a9141e6571f1d7f9771f74b714850d447d3e/JGDMS/jgdms-pref-class-loader/src/main/java/net/jini/loader/pref/PreferredProxyCodebaseProvider.java#L106

Basically there are different options for loading proxy classes, depending on the underlying platform used for class resolution, JGDMS is agnostic toward this.

Future work includes a Rio Resolver provider.


How is the following scenario handled:

class JavaSpaceEventPublisher implements RemoteEventListener, Serializable {
private final JavaSpace space;

//… publish event in JavaSpace implementation
}

The smart proxy class has dependencies on RemoteEventListener and on JavaSpace. How do you properly resolve classes in this case?

Typically the client has the ServiceAPI it needs already installed locally, however this may not always be the case, depending on how you want to resolve the proxy classes and how much you want to share with the client, you can include additional jar files in the annotation, and use preferred.list or you can use Maven or OSGi to resolve dependencies and provision the ClassLoader used for proxy deserialization.

I am asking about something different - the smart proxy class depends on _two_ interfaces:

RemoteEventListener <—— proxy class ——> JavaSpace

RemoteEventListener is its service interface.
But it is not known in advance what interfaces the client already has:

1) Just RemoteEventListener
2) Both RemoteEventListener and JavaSpace
3) None

How is class resolution implemented in JGDMS so that it works properly in _all_ of the above cases?

This is a responsibility of the underlying platform used for class resolution or modularity.

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.

If using Rio Resolver, it will downloaded any required dependencies dynamically using Maven.

JGDMS AtomicILFactory relies on ClassLoader's assigned to both Endpoints of a service for class resolution.  It is the ClassLoader's responsibility to resolve classes.  JGDMS makes no assumptions around how the ClassLoader will resolve classes.

JGDMS separates services into independent marshal streams, each with their own Endpoint ClassLoader's for class resolution. InvocationContraints are inherited from the stream context of the parent stream marshalling the ProxySerializer, this prevents proxies from escaping invocation constraints.

The CodebaseAccessor service also uses the same InvocationHandler used by the service proxy (AtomicInvocationHandler), which has an identity defined by the underlying ObjectEndpoint and server constraints.   So ClassLoader can be stored in a weak map, where the CodebaseAccessor represents the service identity, ensuring the correct ClassLoader is always found and that different services, even if using the same codebase string do not mix.  That is identity is based on not only the codebase, but the authenticated identity of secured connections and the object identity of the service.  ClassLoaders also provide isolation and they represent the identity of a service proxy and any permissions granted to it.

MethodConstraints are now strings in StringMethodConstraints and BasicMethodConstraints has been deprecated, to avoid (unnecessary) class loading and resolution, prior to unmarshalling of the service proxy.

The way Jini resolves classes in a mashal steam, is to use RMIClassLoaderSPI, if services are configured using BasicILFactory, then this remains unchanged.    Jini also allowed services with different identities to share the same ClassLoader if both services had the same codebase annotations, so permissions granted to an authenticated service could be obtained by simply deserializing a stream with a codebase annotation, to gain privilege escalation for gadget attacks.  At some point in time, this needs to be deprecated and removed, but I'm allowing time for people to migrate.

With JGDMS when AtomicILFactory is specified in a services configuration, proxies are removed from the marshaling stream, ProxySerializer acts as a reference placeholder, and all proxies are marshaled independently using their own stream Endpoints and ClassLoader visibility.

https://github.com/pfirmstone/JGDMS/wiki/OSGi-and-JGDMS

What I learned from the many discussions and arguments on River mailing lists, was that developers use different platforms to manage class resolution visibility or provide modularity, so I considered it important not to constrain developers and allow them to use their preferred platform, not to decide for them. ClassLoader is the common abstraction for class resolution and loading.

…[snip]

If you want to use non final classes for your service method arguments and allow clients to override these classes, then you will need to enable codebase annotations in AtomicILFactory in your configuration. The caveat is there is no guarantee, the service will be able to resolve these classes at the server endpoint, or that codebase annotation loss won't occur, it will try using existing mechanisms, such as RMIClassLoaderSPI, which is probably fine for seasoned Jini vets, but not so user friendly for the newbie, who now has to debug ClassNotFoundExceptions.




That’s why I got rid of RMIClassLoaderSPI completely - this is simply a wrong API and has to be thrown away.


Michal

Yes, RMIClassLoaderSPI was designed for RMI, it is too simple for the complex interconnecting relationships that occur within a djinn.

Cheers,

Peter.

Reply via email to