Added tests for yang.model.util
[yangtools.git] / common / object-cache-api / src / main / java / org / opendaylight / yangtools / objcache / spi / AbstractObjectCache.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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.yangtools.objcache.spi;
9
10 import java.util.concurrent.Callable;
11 import java.util.concurrent.ExecutionException;
12
13 import org.opendaylight.yangtools.concepts.ProductAwareBuilder;
14 import org.opendaylight.yangtools.objcache.ObjectCache;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 import com.google.common.annotations.VisibleForTesting;
19 import com.google.common.base.FinalizableReferenceQueue;
20 import com.google.common.base.FinalizableSoftReference;
21 import com.google.common.base.Preconditions;
22 import com.google.common.cache.Cache;
23
24 /**
25  * Abstract object cache implementation. This implementation takes care
26  * of interacting with the user and manages interaction with the Garbage
27  * Collector (via soft references). Subclasses are expected to provide
28  * a backing {@link Cache} instance and provide the
29  */
30 public abstract class AbstractObjectCache implements ObjectCache {
31     /**
32      * Key used when looking up a ProductAwareBuilder product. We assume
33      * the builder is not being modified for the duration of the lookup,
34      * anything else is the user's fault.
35      */
36     @VisibleForTesting
37     static final class BuilderKey {
38         private final ProductAwareBuilder<?> builder;
39
40         private BuilderKey(final ProductAwareBuilder<?> builder) {
41             this.builder = Preconditions.checkNotNull(builder);
42         }
43
44         @Override
45         public int hashCode() {
46             return builder.productHashCode();
47         }
48
49         @Override
50         public boolean equals(Object obj) {
51             /*
52              * We can tolerate null objects coming our way, but we need
53              * to be on the lookout for WeakKeys, as we cannot pass them
54              * directly to productEquals().
55              */
56             if (obj != null && obj instanceof SoftKey) {
57                 obj = ((SoftKey<?>)obj).get();
58             }
59
60             return builder.productEquals(obj);
61         }
62     }
63
64     /**
65      * Key used in the underlying map. It is essentially a soft reference, with
66      * slightly special properties.
67      *
68      * It acts as a proxy for the object it refers to and essentially delegates
69      * to it. There are three exceptions here:
70      *
71      * 1) This key needs to have a cached hash code. The requirement is that the
72      *    key needs to be able to look itself up after the reference to the object
73      *    has been cleared (and thus we can no longer look it up from there). One
74      *    typical container where we are stored are HashMaps -- and they need it
75      *    to be constant.
76      * 2) This key does not tolerate checks to see if its equal to null. While we
77      *    could return false, we want to catch offenders who try to store nulls
78      *    in the cache.
79      * 3) This key inverts the check for equality, e.g. it calls equals() on the
80      *    object which was passed to its equals(). Instead of supplying itself,
81      *    it supplies the referent. If the soft reference is cleared, such check
82      *    will return false, which is fine as it prevents normal lookup from
83      *    seeing the cleared key. Removal is handled by the explicit identity
84      *    check.
85      */
86     protected abstract static class SoftKey<T> extends FinalizableSoftReference<T> {
87         private final int hashCode;
88
89         public SoftKey(final T referent, final FinalizableReferenceQueue q) {
90             super(Preconditions.checkNotNull(referent), q);
91             hashCode = referent.hashCode();
92         }
93
94         @Override
95         public boolean equals(final Object obj) {
96             if (obj == null) {
97                 return false;
98             }
99
100             // Order is important: we do not want to call equals() on ourselves!
101             return this == obj || obj.equals(get());
102         }
103
104         @Override
105         public int hashCode() {
106             return hashCode;
107         }
108     }
109
110     private static final Logger LOG = LoggerFactory.getLogger(AbstractObjectCache.class);
111     private final FinalizableReferenceQueue queue;
112     private final Cache<SoftKey<?>, Object> cache;
113
114     protected AbstractObjectCache(final Cache<SoftKey<?>, Object> cache, final FinalizableReferenceQueue queue) {
115         this.queue = Preconditions.checkNotNull(queue);
116         this.cache = Preconditions.checkNotNull(cache);
117     }
118
119     protected <T> SoftKey<T> createSoftKey(final T object) {
120         /*
121          * This may look like a race (having a soft reference and not have
122          * it in the cache). In fact this is protected by the fact we still
123          * have a strong reference on the object in our arguments and that
124          * reference survives past method return since we return it.
125          */
126         return new SoftKey<T>(object, queue) {
127             @Override
128             public void finalizeReferent() {
129                 /*
130                  * NOTE: while it may be tempting to add "object" into this
131                  *       trace message, do not ever do that: it would retain
132                  *       a strong reference, preventing collection.
133                  */
134                 LOG.trace("Invalidating key {}", this);
135                 cache.invalidate(this);
136             }
137         };
138     }
139
140     @Override
141     public final <B extends ProductAwareBuilder<P>, P> P getProduct(final B builder) {
142         throw new UnsupportedOperationException();
143 //        LOG.debug("Looking up product for {}", builder);
144 //
145 //        @SuppressWarnings("unchecked")
146 //        final P ret = (P) cache.getIfPresent(new BuilderKey(builder));
147 //        return ret == null ? put(Preconditions.checkNotNull(builder.toInstance())) : ret;
148     }
149
150     @Override
151     @SuppressWarnings("unchecked")
152     public final <T> T getReference(final T object) {
153         LOG.debug("Looking up reference for {}", object);
154         if (object == null) {
155             return null;
156         }
157
158         final SoftKey<T> key = createSoftKey(object);
159         try {
160             return (T) cache.get(key, new Callable<T>() {
161                 @Override
162                 public T call() {
163                     return object;
164                 }
165             });
166         } catch (ExecutionException e) {
167             throw new IllegalStateException("Failed to load value", e);
168         }
169     }
170 }