All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.yangtools.binding.loader.RootBindingClassLoader Maven / Gradle / Ivy

There is a newer version: 14.0.5
Show newest version
/*
 * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.yangtools.binding.loader;

import static com.google.common.base.Verify.verify;

import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.binding.DataContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// A root codec classloader, binding only whatever is available StaticClassPool
final class RootBindingClassLoader extends BindingClassLoader {
    private static final Logger LOG = LoggerFactory.getLogger(RootBindingClassLoader.class);
    private static final VarHandle LOADERS;

    static {
        try {
            LOADERS = MethodHandles.lookup().findVarHandle(RootBindingClassLoader.class, "loaders", ImmutableMap.class);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new ExceptionInInitializerError(e);
        }

        verify(registerAsParallelCapable());
    }

    private volatile ImmutableMap loaders = ImmutableMap.of();

    RootBindingClassLoader(final ClassLoader parentLoader, final @Nullable File dumpDir) {
        super(parentLoader, dumpDir);
    }

    @Override
    BindingClassLoader findClassLoader(final Class bindingClass) {
        final var target = bindingClass.getClassLoader();
        if (target == null) {
            // No class loader associated ... well, let's use root then
            LOG.debug("Using existing {} for {}", this, bindingClass);
            return this;
        }

        // Cache for update
        var local = loaders;
        final var known = local.get(target);
        if (known != null) {
            LOG.debug("Using existing {} for {}", known, bindingClass);
            return known;
        }

        // Alright, we need to determine if the class is accessible through our hierarchy (in which case we use
        // ourselves) or we need to create a new Leaf.
        final @NonNull BindingClassLoader found;
        if (!isOurClass(bindingClass)) {
            verifyStaticLinkage(target);
            found = AccessController.doPrivileged(
                (PrivilegedAction)() -> new LeafBindingClassLoader(this, target));
            LOG.debug("Allocated {} for {}", found, target);
        } else {
            found = this;
        }

        // Now make sure we cache this result
        while (true) {
            final var updated = ImmutableMap.builderWithExpectedSize(local.size() + 1)
                .putAll(local)
                .put(target, found)
                .build();

            final var witness = (ImmutableMap)
                LOADERS.compareAndExchange(this, local, updated);
            if (witness == local) {
                LOG.debug("Using {} for {}", found, bindingClass);
                return found;
            }
            final var recheck = witness.get(target);
            if (recheck != null) {
                LOG.debug("Using {} for {}", recheck, bindingClass);
                return recheck;
            }

            local = witness;
        }
    }

    @Override
    void appendLoaders(final Set newLoaders) {
        // Root loader should never see the requirement for other loaders, as that would violate loop-free nature
        // of generated code: if a binding class is hosted in root loader, all its references must be visible from
        // the root loader and hence all the generated code ends up residing in the root loader, too.
        throw new IllegalStateException("Attempted to extend root loader with " + newLoaders);
    }

    private boolean isOurClass(final Class bindingClass) {
        final Class ourClass;
        try {
            ourClass = loadClass(bindingClass.getName(), false);
        } catch (ClassNotFoundException e) {
            LOG.trace("Failed to load {}", bindingClass, e);
            return false;
        }
        return bindingClass.equals(ourClass);
    }

    // Sanity check: target has to resolve yang-binding contents to the same class, otherwise we are in a pickle
    private static void verifyStaticLinkage(final ClassLoader candidate) {
        final Class targetClazz;
        try {
            targetClazz = candidate.loadClass(DataContainer.class.getName());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("ClassLoader " + candidate + " cannot load " + DataContainer.class, e);
        }
        verify(DataContainer.class.equals(targetClazz),
            "Class mismatch on DataContainer. Ours is from %s, target %s has %s from %s",
            DataContainer.class.getClassLoader(), candidate, targetClazz, targetClazz.getClassLoader());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy