Have we been doing codebase annotations wrong?

Could RMIClassLoader have been better conceived?

Could a simpler alternative be utilised instead?

For example, classes are resolved differently during deserialization than how classes are resolved at runtime. At runtime a ClassLoader delegates to its parent ClassLoader, or in the case of modular systems, ClassLoader’s of imported modules. RMIClassLoader, doesn’t look to resolve classes through ClassLoader hierarchies, but instead tries to locate each ClassLoader directly based on an annotation.

The problem with this approach is not all ClassLoader’s provide codebase annotations, and class resolution may be different at distinct nodes in the network.

Currently codebase annotations may change when marshalling between nodes, depending on where each class is resolved.

Refer to: http://sorcersoft.org/resources/jini/smli_tr-2006-149.pdf

A much simpler approach.

We can define the ClassLoader at each endpoint.

In JERI, a ServerEndpoint can be assigned a default ClassLoader, by passing it as a parameter to its InvocationLayerFactory. The client Endpoint’s default ClassLoader is the ClassLoader of its dynamic proxy instance (the ClassLoader where the java.lang.reflect.Proxy dynamically generated instance is loaded).

So if a service has a smart proxy, it’s codebase should be present in a ClassLoader at both the ServerEndpoint and the client Endpoint, so the default ClassLoader’s at both Endpoint’s contain that codebase.

The default ClassLoader at each Endpoint now has responsibility for resolution of classes, no longer is RMIClassLoader required. In fact codebase annotations no longer need to be annotated with every class in the stream either.

But what about client parameter objects, or exported remote handback objects passed as parameters, I hear you ask?

Simple, we use a marker interface, so these objects can identify themselves to the stream, a bootstrap proxy can be provided by the stream, that uses only local classes, present at both endpoints, the original object can be stored into a MarshalledInstance and serialized with the bootstrap proxy to the remote Endpoint, that allows the originator to be authenticated, it’s codebase provisioned into a ClassLoader which becomes the default loader of the MarshalledInstance to deserialize the object in question. The identity key of the ClassLoader will be a combination of the bootstrap proxy’s InvocationHandler identity and it’s codebase annotation, so it can be cached. If a remote object which is contained within the MarshalledInstance, its JERI Endpoint will use the default ClassLoader passed to the MarshalledInstance as its default loader.

Note: If we’re in a traditional java hierarchical ClassLoader system (not modular), we’d want the current stream’s default loader to be the parent loader of the resolved or provisioned ClassLoader passed to the MarshalledInstance.

So now you don’t get codebase annotation loss, ServerEndpoint and client Endpoint’s have ClassLoader’s with compatible class resolution. The codebase annotation becomes a configuration concern of the service.

In addition, MethodConstraints can also be applied to exported objects nested within other services. It can be passed using the stream context. This ensures that minimum principal authentication, integrity and confidentiality apply to all nested objects.

The good news is that most of the mechanisms are already present and backward compatibility can be preserved allowing eventual migration.

Remember that a smart proxy can have no real server back end communication at all (except for providing the codebase), it’s just an object that gets serialized around different nodes, in this case the bootstrap proxy is still used to provide the codebase annotation and as trust verification.

How does trust work in this system?

Provided you still trust the bootstrap proxy’s service, after method constraints that ensure confidentiality and minimum principal authentication have been applied , it provides the codebase annotation, and if integrity constraint is true, then the codebase scheme is checked for integrity, or if it’s signed, the jar is validated by a provider. (The signers can be anonymous and advised by the bootstrap proxy). You now trust that the code will validate input during deserialization, and if the de-serialized object implements RemoteMethodControl apply MethodConstraints to it as well. The object bytes in serial form may have originated from a third party (also with MethodConstraints applied, but possibly not trusted by the original node), in any case it’s important for the input to be validated during deserialization.

Note this system would also utilise a Service Provider Interface to communicate with the bootstrap proxy and preferred classes can still be supported, simply by using a PreferredClassLoader when loading the codebase.

This system also allows support for modular environments like OSGi to be relatively simple when compared to RMIClassLoaderSpi. Additonally it allows an OSGi node to interact with traditional nodes / services, provided jar files have bundle manifests and the configured codebase annotation string contains all required jar files, including dependencies (the OSGi provider can ignore the dependencies, provided the first jar is the proxy bundle).


I've currently got a prototype I'm working on if anyone's interested.

Regards,


Peter.

Reply via email to