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.