Teach BindingNormalizedNodeCache to cache leaf type objects
[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.Set;
17 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 // A root codec classloader, binding only whatever is available StaticClassPool
22 final class RootCodecClassLoader extends CodecClassLoader {
23     private static final Logger LOG = LoggerFactory.getLogger(RootCodecClassLoader.class);
24
25     static {
26         verify(registerAsParallelCapable());
27     }
28
29     @SuppressWarnings("rawtypes")
30     private static final AtomicReferenceFieldUpdater<RootCodecClassLoader, ImmutableMap> LOADERS_UPDATER =
31             AtomicReferenceFieldUpdater.newUpdater(RootCodecClassLoader.class, ImmutableMap.class, "loaders");
32
33     private volatile ImmutableMap<ClassLoader, CodecClassLoader> loaders = ImmutableMap.of();
34
35     RootCodecClassLoader() {
36         super();
37     }
38
39     @Override
40     CodecClassLoader findClassLoader(final Class<?> bindingClass) {
41         final ClassLoader target = bindingClass.getClassLoader();
42         if (target == null) {
43             // No class loader associated ... well, let's use root then
44             return this;
45         }
46
47         // Cache for update
48         ImmutableMap<ClassLoader, CodecClassLoader> local = loaders;
49         final CodecClassLoader known = local.get(target);
50         if (known != null) {
51             return known;
52         }
53
54         // Alright, we need to determine if the class is accessible through our hierarchy (in which case we use
55         // ourselves) or we need to create a new Leaf.
56         final CodecClassLoader found;
57         if (!isOurClass(bindingClass)) {
58             StaticClassPool.verifyStaticLinkage(target);
59             found = AccessController.doPrivileged(
60                 (PrivilegedAction<CodecClassLoader>)() -> new LeafCodecClassLoader(this, target));
61         } else {
62             found = this;
63         }
64
65         // Now make sure we cache this result
66         while (true) {
67             final Builder<ClassLoader, CodecClassLoader> builder = ImmutableMap.builderWithExpectedSize(
68                 local.size() + 1);
69             builder.putAll(local);
70             builder.put(target, found);
71
72             if (LOADERS_UPDATER.compareAndSet(this, local, builder.build())) {
73                 return found;
74             }
75
76             local = loaders;
77             final CodecClassLoader recheck = local.get(target);
78             if (recheck != null) {
79                 return recheck;
80             }
81         }
82     }
83
84     @Override
85     void appendLoaders(final Set<LeafCodecClassLoader> newLoaders) {
86         // Root loader should never see the requirement for other loaders, as that would violate loop-free nature
87         // of generated code: if a binding class is hosted in root loader, all its references must be visible from
88         // the root loader and hence all the generated code ends up residing in the root loader, too.
89         throw new IllegalStateException("Attempted to extend root loader with " + newLoaders);
90     }
91
92     private boolean isOurClass(final Class<?> bindingClass) {
93         final Class<?> ourClass;
94         try {
95             ourClass = loadClass(bindingClass.getName(), false);
96         } catch (ClassNotFoundException e) {
97             LOG.debug("Failed to load {}", bindingClass, e);
98             return false;
99         }
100         return bindingClass.equals(ourClass);
101     }
102 }