Refactor RpcRoutingStrategy
[mdsal.git] / binding / mdsal-binding-loader / src / main / java / org / opendaylight / mdsal / binding / loader / RootBindingClassLoader.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.loader;
9
10 import static com.google.common.base.Verify.verify;
11
12 import com.google.common.collect.ImmutableMap;
13 import java.io.File;
14 import java.security.AccessController;
15 import java.security.PrivilegedAction;
16 import java.util.Set;
17 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
18 import org.eclipse.jdt.annotation.Nullable;
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 RootBindingClassLoader extends BindingClassLoader {
25     private static final Logger LOG = LoggerFactory.getLogger(RootBindingClassLoader.class);
26
27     static {
28         verify(registerAsParallelCapable());
29     }
30
31     @SuppressWarnings("rawtypes")
32     private static final AtomicReferenceFieldUpdater<RootBindingClassLoader, ImmutableMap> LOADERS_UPDATER =
33             AtomicReferenceFieldUpdater.newUpdater(RootBindingClassLoader.class, ImmutableMap.class, "loaders");
34
35     private volatile ImmutableMap<ClassLoader, BindingClassLoader> loaders = ImmutableMap.of();
36
37     RootBindingClassLoader(final ClassLoader parentLoader, final @Nullable File dumpDir) {
38         super(parentLoader, dumpDir);
39     }
40
41     @Override
42     BindingClassLoader findClassLoader(final Class<?> bindingClass) {
43         final var target = bindingClass.getClassLoader();
44         if (target == null) {
45             // No class loader associated ... well, let's use root then
46             return this;
47         }
48
49         // Cache for update
50         var local = loaders;
51         final var known = local.get(target);
52         if (known != null) {
53             return known;
54         }
55
56         // Alright, we need to determine if the class is accessible through our hierarchy (in which case we use
57         // ourselves) or we need to create a new Leaf.
58         final BindingClassLoader found;
59         if (!isOurClass(bindingClass)) {
60             verifyStaticLinkage(target);
61             found = AccessController.doPrivileged(
62                 (PrivilegedAction<BindingClassLoader>)() -> new LeafBindingClassLoader(this, target));
63         } else {
64             found = this;
65         }
66
67         // Now make sure we cache this result
68         while (true) {
69             final var builder = ImmutableMap.<ClassLoader, BindingClassLoader>builderWithExpectedSize(local.size() + 1);
70             builder.putAll(local);
71             builder.put(target, found);
72
73             if (LOADERS_UPDATER.compareAndSet(this, local, builder.build())) {
74                 return found;
75             }
76
77             local = loaders;
78             final var recheck = local.get(target);
79             if (recheck != null) {
80                 return recheck;
81             }
82         }
83     }
84
85     @Override
86     void appendLoaders(final Set<LeafBindingClassLoader> newLoaders) {
87         // Root loader should never see the requirement for other loaders, as that would violate loop-free nature
88         // of generated code: if a binding class is hosted in root loader, all its references must be visible from
89         // the root loader and hence all the generated code ends up residing in the root loader, too.
90         throw new IllegalStateException("Attempted to extend root loader with " + newLoaders);
91     }
92
93     private boolean isOurClass(final Class<?> bindingClass) {
94         final Class<?> ourClass;
95         try {
96             ourClass = loadClass(bindingClass.getName(), false);
97         } catch (ClassNotFoundException e) {
98             LOG.debug("Failed to load {}", bindingClass, e);
99             return false;
100         }
101         return bindingClass.equals(ourClass);
102     }
103
104     // Sanity check: target has to resolve yang-binding contents to the same class, otherwise we are in a pickle
105     private static void verifyStaticLinkage(final ClassLoader candidate) {
106         final Class<?> targetClazz;
107         try {
108             targetClazz = candidate.loadClass(DataContainer.class.getName());
109         } catch (ClassNotFoundException e) {
110             throw new IllegalStateException("ClassLoader " + candidate + " cannot load " + DataContainer.class, e);
111         }
112         verify(DataContainer.class.equals(targetClazz),
113             "Class mismatch on DataContainer. Ours is from %s, target %s has %s from %s",
114             DataContainer.class.getClassLoader(), candidate, targetClazz, targetClazz.getClassLoader());
115     }
116 }