Remove unneeded doc dependency
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / loader / RootCodecClassLoader.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl.loader;
9
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
12
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableMap.Builder;
15 import java.security.AccessController;
16 import java.security.PrivilegedAction;
17 import java.util.Set;
18 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
19 import org.opendaylight.yangtools.yang.binding.DataContainer;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 // A root codec classloader, binding only whatever is available StaticClassPool
24 final class RootCodecClassLoader extends CodecClassLoader {
25     private static final Logger LOG = LoggerFactory.getLogger(RootCodecClassLoader.class);
26     private static final ClassLoader LOADER = verifyNotNull(RootCodecClassLoader.class.getClassLoader());
27
28     static {
29         verify(registerAsParallelCapable());
30     }
31
32     @SuppressWarnings("rawtypes")
33     private static final AtomicReferenceFieldUpdater<RootCodecClassLoader, ImmutableMap> LOADERS_UPDATER =
34             AtomicReferenceFieldUpdater.newUpdater(RootCodecClassLoader.class, ImmutableMap.class, "loaders");
35
36     private volatile ImmutableMap<ClassLoader, CodecClassLoader> loaders = ImmutableMap.of();
37
38     RootCodecClassLoader() {
39         super(LOADER);
40     }
41
42     @Override
43     CodecClassLoader findClassLoader(final Class<?> bindingClass) {
44         final ClassLoader target = bindingClass.getClassLoader();
45         if (target == null) {
46             // No class loader associated ... well, let's use root then
47             return this;
48         }
49
50         // Cache for update
51         ImmutableMap<ClassLoader, CodecClassLoader> local = loaders;
52         final CodecClassLoader known = local.get(target);
53         if (known != null) {
54             return known;
55         }
56
57         // Alright, we need to determine if the class is accessible through our hierarchy (in which case we use
58         // ourselves) or we need to create a new Leaf.
59         final CodecClassLoader found;
60         if (!isOurClass(bindingClass)) {
61             verifyStaticLinkage(target);
62             found = AccessController.doPrivileged(
63                 (PrivilegedAction<CodecClassLoader>)() -> new LeafCodecClassLoader(this, target));
64         } else {
65             found = this;
66         }
67
68         // Now make sure we cache this result
69         while (true) {
70             final Builder<ClassLoader, CodecClassLoader> builder = ImmutableMap.builderWithExpectedSize(
71                 local.size() + 1);
72             builder.putAll(local);
73             builder.put(target, found);
74
75             if (LOADERS_UPDATER.compareAndSet(this, local, builder.build())) {
76                 return found;
77             }
78
79             local = loaders;
80             final CodecClassLoader recheck = local.get(target);
81             if (recheck != null) {
82                 return recheck;
83             }
84         }
85     }
86
87     @Override
88     void appendLoaders(final Set<LeafCodecClassLoader> newLoaders) {
89         // Root loader should never see the requirement for other loaders, as that would violate loop-free nature
90         // of generated code: if a binding class is hosted in root loader, all its references must be visible from
91         // the root loader and hence all the generated code ends up residing in the root loader, too.
92         throw new IllegalStateException("Attempted to extend root loader with " + newLoaders);
93     }
94
95     private boolean isOurClass(final Class<?> bindingClass) {
96         final Class<?> ourClass;
97         try {
98             ourClass = loadClass(bindingClass.getName(), false);
99         } catch (ClassNotFoundException e) {
100             LOG.debug("Failed to load {}", bindingClass, e);
101             return false;
102         }
103         return bindingClass.equals(ourClass);
104     }
105
106     // Sanity check: target has to resolve yang-binding contents to the same class, otherwise we are in a pickle
107     private static void verifyStaticLinkage(final ClassLoader candidate) {
108         final Class<?> targetClazz;
109         try {
110             targetClazz = candidate.loadClass(DataContainer.class.getName());
111         } catch (ClassNotFoundException e) {
112             throw new IllegalStateException("ClassLoader " + candidate + " cannot load " + DataContainer.class, e);
113         }
114         verify(DataContainer.class.equals(targetClazz),
115             "Class mismatch on DataContainer. Ours is from %s, target %s has %s from %s",
116             DataContainer.class.getClassLoader(), candidate, targetClazz, targetClazz.getClassLoader());
117     }
118 }