Add binding/dom codec class loader support
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / 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.loader;
9
10 import static com.google.common.base.Verify.verify;
11
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableMap.Builder;
14 import java.security.AccessController;
15 import java.security.PrivilegedAction;
16 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 // A root codec classloader, binding only whatever is available StaticClassPool
21 final class RootCodecClassLoader extends CodecClassLoader {
22     private static final Logger LOG = LoggerFactory.getLogger(RootCodecClassLoader.class);
23
24     static {
25         verify(registerAsParallelCapable());
26     }
27
28     @SuppressWarnings("rawtypes")
29     private static final AtomicReferenceFieldUpdater<RootCodecClassLoader, ImmutableMap> LOADERS_UPDATER =
30             AtomicReferenceFieldUpdater.newUpdater(RootCodecClassLoader.class, ImmutableMap.class, "loaders");
31
32     private volatile ImmutableMap<ClassLoader, CodecClassLoader> loaders = ImmutableMap.of();
33
34     RootCodecClassLoader() {
35         super();
36     }
37
38     @Override
39     CodecClassLoader findClassLoader(final Class<?> bindingClass) {
40         final ClassLoader target = bindingClass.getClassLoader();
41         if (target == null) {
42             // No class loader associated ... well, let's use root then
43             return this;
44         }
45
46         // Cache for update
47         ImmutableMap<ClassLoader, CodecClassLoader> local = loaders;
48         final CodecClassLoader known = local.get(target);
49         if (known != null) {
50             return known;
51         }
52
53         // Alright, we need to determine if the class is accessible through our hierarchy (in which case we use
54         // ourselves) or we need to create a new Leaf.
55         final CodecClassLoader found;
56         if (!isOurClass(bindingClass)) {
57             StaticClassPool.verifyStaticLinkage(target);
58             found = AccessController.doPrivileged(
59                 (PrivilegedAction<CodecClassLoader>)() -> new LeafCodecClassLoader(this, target));
60         } else {
61             found = this;
62         }
63
64         // Now make sure we cache this result
65         while (true) {
66             final Builder<ClassLoader, CodecClassLoader> builder = ImmutableMap.builderWithExpectedSize(
67                 local.size() + 1);
68             builder.putAll(local);
69             builder.put(target, found);
70
71             if (LOADERS_UPDATER.compareAndSet(this, local, builder.build())) {
72                 return found;
73             }
74
75             local = loaders;
76             final CodecClassLoader recheck = local.get(target);
77             if (recheck != null) {
78                 return recheck;
79             }
80         }
81     }
82
83     private boolean isOurClass(final Class<?> bindingClass) {
84         final Class<?> ourClass;
85         try {
86             ourClass = loadClass(bindingClass.getName(), false);
87         } catch (ClassNotFoundException e) {
88             LOG.debug("Failed to load {}", bindingClass, e);
89             return false;
90         }
91         return bindingClass.equals(ourClass);
92     }
93 }