Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagejava
public class ClassLoaderOptimisticCodec implements ClassLoaderCodec {

    public ClassLoaderOptimisticCodec() {}
 
    @Nullable public Object encodeClassLoader(Class<?> cls, ClassLoader clsLdr) throws IgniteException {
        return null;
    }

	public ClassLoader decodeClassLoader(String fqn, @Nullable Object encodedClsLdr) throws IgniteException {
        // TODO:
        // Iterate through all the bundles and pick the first one
        // that can load the class. Once found, cache the class loader
        // for faster lookups going forward.
        ...
    }
}

Implementation strategies

Assumptions

First of all the both approaches imply that your cluster is consistent and contains the same version of the bundles on all the nodes. This can be see a a valid assumption in order to ensure the consistency of your computation tasks. If you want to be able to work it in a more non deterministic approach then we have to introduce yet another strategy. But first let focus assume that the bundles are equals on the entire cluster.

Supported version of OSCi

TBD

Pessimistic Codec

On the write side this approach require you to capture the bundle symbolic name and its version. This is something easy to do as in OSGi all classloader except the system classloader implements the BundleReference. The pessimis codec can look like that:

Code Block
languagejava
public class ClassLoaderPessimisticCodec implements ClassLoaderCodec {

    private static final byte FRAMEWORK_CLASS_LOADER_ID = 0;
    private static final byte IGNITE_CLASS_LOADER_ID = 1;
    private static final byte BOOT_CLASS_LOADER_ID = 2;
    private static final byte BUNDLE_CLASS_LOADER_ID = 4;

    private static final ClassLoader FRAMEWOR_CLASS_LOADER = Bundle.class.getClassLoader();


    private final PackageAdmin packageAdmin;

    public ClassLoaderPessimisticCodec(PackageAdmin packageAdmin) {
        this.packageAdmin = packageAdmin;
    }

    @Nullable
    @Override
    public Object encodeClassLoader(Class<?> cls) throws IgniteException {
        ClassLoader classLoader = cls.getClassLoader();

        if (isIgniteClass(classLoader)) {
            return ClassLoaderDesc.newIgniteClassLoaderDesc();
        }

        if (isFrameworkClassLoader(cls.getClassLoader())) {
            return ClassLoaderDesc.newFrameworkClassLoader();
        }
        Bundle bundle = FrameworkUtil.getBundle(cls);

        if (bundle != null) {
            return ClassLoaderDesc.newBundleClassLoaderDesc(bundle);
        }

        return ClassLoaderDesc.newBootClassLoader();
    }

    @Nullable
    private Bundle getBundleExt(Class<?> cls) {

        // maybe handle SecurityManager

        Bundle bundle = FrameworkUtil.getBundle(cls);

        if (bundle == null && isFrameworkClassLoader(cls.getClassLoader())) {
            bundle = FrameworkUtil.getBundle(Bundle.class);
        }
        return bundle;
    }

    @Override
    public ClassLoader decodeClassLoader(String fqn, ClassLoader clsLdr, @Nullable Object encodedClsLdr)
            throws IgniteException {
        ClassLoaderDesc classLoaderDesc = (ClassLoaderDesc) encodedClsLdr;
        switch (classLoaderDesc.classLoaderId) {
            case BOOT_CLASS_LOADER_ID:
                return clsLdr;
            case FRAMEWORK_CLASS_LOADER_ID:
                return FRAMEWOR_CLASS_LOADER;
            case IGNITE_CLASS_LOADER_ID:
                return ClassLoaderCodec.class.getClassLoader();
            case BUNDLE_CLASS_LOADER_ID:
                //strict version but we can think about an different strategy here like minor or micro version range
                packageAdmin.getBundles(classLoaderDesc.bsn, classLoaderDesc.version);
        }
        return null;
    }

    static final class ClassLoaderDesc implements Externalizable {

        private String version;
        private String bsn;
        private byte classLoaderId;

        public ClassLoaderDesc(byte classLoaderId) {
            this.classLoaderId = classLoaderId;
        }

        public ClassLoaderDesc(Bundle bundle) {
            this.classLoaderId = BUNDLE_CLASS_LOADER_ID;
            this.bsn = bundle.getSymbolicName();
            this.version = bundle.getVersion().toString();
        }

        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.write(classLoaderId);
            if (classLoaderId == BUNDLE_CLASS_LOADER_ID) {
                out.writeUTF(bsn);
                //can be optimized
                out.writeUTF(version);
            }
        }

        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            classLoaderId = in.readByte();
            if (classLoaderId == BUNDLE_CLASS_LOADER_ID) {

            }
        }

        static ClassLoaderDesc newIgniteClassLoaderDesc() {
            return new ClassLoaderDesc(IGNITE_CLASS_LOADER_ID);
        }

        public static ClassLoaderDesc newBundleClassLoaderDesc(Bundle bundle) {
            return new ClassLoaderDesc(bundle);
        }

        public static ClassLoaderDesc newFrameworkClassLoader() {
            return new ClassLoaderDesc(FRAMEWORK_CLASS_LOADER_ID);
        }

        public static ClassLoaderDesc newBootClassLoader() {
            return new ClassLoaderDesc(BOOT_CLASS_LOADER_ID);
        }
    }
}