...
At runtime the Ignite user installs all required bundles (including ignite-core, any optional Ignite bundles as well as the application bundles) using either the standard mechanisms defined by the OSGi spec, or relying on the container's implementation-specific capabilities. For example, Apache Karaf (an OSGi implementation) offers a packaging/deloyment concept called "Feature" which roughly speaking is a list of bundles to automatically deploy when the OSGi Framework starts.
Before discussing the proposed marshalling enhancements, it's worth making Ignite's use cases more explicit:
Clearly use case 2 subsumes the case 1, hence the proposal is to target the use case 2.
The fundamental The main problem we need to solve in order to allow Ignite OSGi enablement is the marshalling. More specifically the issue is with deserialization of the classes that are provided by the bundles other than the JDK and the Ignite bundle itself.
When the Ignite transport layer receives a message it needs to figure out how to deserialize the bytes and for that it needs to know the bundle that provides the class to be deserailized. To make things more complex, the class may contain other classes that come from other bundles, and so on recursively. What In general, what is needed then is a way to map an FQN of a class to the its bundle (and hence to the class loader) which provides the class.
And this is where ClassLoaderCodec
comes into to play.
On the high level the proposal is as follows:
ClassLoaderCodec.encodeClassLoader(cls)
with the class to be serialized as its only parameter. The implementation of the method may return an arbitrary object that in some way (up to the implementation) represents the class loader of the class. The encoded representation of the class loader will be serialized along with the rest of the message data. The returned object may be a primitive, a serializable POJO, or a null
.encodeClassLoader()
call during serialization) as well as the FQN of the class being deserialized are passed into ClassLoaderCodec.decodeClassLoader(fqn, encodedClassLoader)
method. The implementation of the method is expected to decode and return an instance of the class loader to use for loading the class with the given FQN.It's responsibility of the implementation to ensure that the encoded representation is sufficient to unambiguously identify the correct bundle during deserialization.
The ClassLoaderCodec
should The ClassLoaderResolver
should be called for every Object during serialization and deserialization and should be part of the IgniteConfiguraiton
:
Code Block |
---|
public interface ClassLoaderResolverClassLoaderCodec { @Nullable public Object encodeClassLoader(Class<?> cls) throws IgniteException; public ClassLoader decodeClassLoader(String fqn, @Nullable Object objencodedClsLdr) throws IgniteException; } |
In general in OSGi, the same package may be exported by multiple bundles and therefore an FQN may not be sufficient to look up the correct class loader. In such cases, the codec implementation must employ a pessimistic approach and encode enough information (for example, the bundle symbolic name, plus the bundle version) for the deserializer to be able to resolve the FQN to the correct class loader. Such implementation will work for all use cases, but it introduces some overhead and increases the size of the serialized messages.
However, for the applications that can enforce one-to-one mapping of packages to bundles, a simplified (optimistic) approach can be used instead. With this approach, no encoding of the class loader is required (encodeClassLoader()
returns null), and only the FQN is used for decoding of the class loader.
Here's how the pessimistic codec implementation might look like (in pseudo-code)We should also provide OSGI class loader resolver out of the box:
Code Block |
---|
public class OsgiClassLoaderResolver { PessimisticClassLoaderCodec implements ClassLoaderCodec { public PessimisticClassLoaderCodec() {} //@Nullable Manifest entry names public to look up bundles during deserialization.Object encodeClassLoader(Class<?> cls) throws IgniteException { private Collection<String> mfEntryNames; // TODO } public OsgiClassLoaderResolver() {} ClassLoader decodeClassLoader(String fqn, @Nullable Object encodedClsLdr) throws IgniteException { // TODO } } |
Here's how the optimistic (opportunistic :)))) codec implementation might look like:
Code Block | ||
---|---|---|
| ||
public OsgiClassLoaderResolver(Collection<String> mfEntryNames) class OptimisticClassLoaderCodec implements ClassLoaderCodec { public OptimisticClassLoaderCodec() ...{} }@Nullable public Object encodeClassLoader(ClassLoaderClass<?> clsLdrcls) throws IgniteException { // TODO } public ClassLoader decodeClassLoader(String fqn, @Nullable Object objencodedClsLdr) throws IgniteException { // TODO } } |
We need a clear path to support Ignite optional dependencies in OSGI environments.
TODO: use OSGI fragments?
...