2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.objcache.spi;
10 import org.opendaylight.yangtools.concepts.ProductAwareBuilder;
11 import org.opendaylight.yangtools.objcache.ObjectCache;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
15 import com.google.common.base.FinalizableReferenceQueue;
16 import com.google.common.base.FinalizableSoftReference;
17 import com.google.common.base.Preconditions;
18 import com.google.common.cache.Cache;
21 * Abstract object cache implementation. This implementation takes care
22 * of interacting with the user and manages interaction with the Garbage
23 * Collector (via soft references). Subclasses are expected to provide
24 * a backing {@link Cache} instance and provide the
26 public abstract class AbstractObjectCache implements ObjectCache {
28 * Key used when looking up a ProductAwareBuilder product. We assume
29 * the builder is not being modified for the duration of the lookup,
30 * anything else is the user's fault.
32 private static final class BuilderKey {
33 private final ProductAwareBuilder<?> builder;
35 private BuilderKey(final ProductAwareBuilder<?> builder) {
36 this.builder = Preconditions.checkNotNull(builder);
40 public int hashCode() {
41 return builder.productHashCode();
45 public boolean equals(Object obj) {
47 * We can tolerate null objects coming our way, but we need
48 * to be on the lookout for WeakKeys, as we cannot pass them
49 * directly to productEquals().
51 if (obj != null && obj instanceof SoftKey) {
52 obj = ((SoftKey)obj).get();
55 return builder.productEquals(obj);
60 * Key used in the underlying map. It is essentially a soft reference, with
61 * slightly special properties.
63 * It acts as a proxy for the object it refers to and essentially delegates
64 * to it. There are three exceptions here:
66 * 1) This key needs to have a cached hash code. The requirement is that the
67 * key needs to be able to look itself up after the reference to the object
68 * has been cleared (and thus we can no longer look it up from there). One
69 * typical container where we are stored are HashMaps -- and they need it
71 * 2) This key does not tolerate checks to see if its equal to null. While we
72 * could return false, we want to catch offenders who try to store nulls
74 * 3) This key inverts the check for equality, e.g. it calls equals() on the
75 * object which was passed to its equals(). Instead of supplying itself,
76 * it supplies the referent. If the soft reference is cleared, such check
77 * will return false, which is fine as it prevents normal lookup from
78 * seeing the cleared key. Removal is handled by the explicit identity
81 private static abstract class SoftKey extends FinalizableSoftReference<Object> {
82 private final int hashCode;
84 public SoftKey(final Object referent, final FinalizableReferenceQueue q) {
85 super(Preconditions.checkNotNull(referent), q);
86 hashCode = referent.hashCode();
90 public boolean equals(final Object obj) {
91 Preconditions.checkState(obj != null);
93 // Order is important: we do not want to call equals() on ourselves!
94 return this == obj || obj.equals(get());
98 public int hashCode() {
103 private static final Logger LOG = LoggerFactory.getLogger(AbstractObjectCache.class);
104 private final FinalizableReferenceQueue queue;
105 private final Cache<Object, Object> cache;
107 protected AbstractObjectCache(final Cache<Object, Object> cache, final FinalizableReferenceQueue queue) {
108 this.queue = Preconditions.checkNotNull(queue);
109 this.cache = Preconditions.checkNotNull(cache);
112 private <T> T put(final T object) {
114 * This may look like a race (having a soft reference and not have
115 * it in the cache). In fact this is protected by the fact we still
116 * have a strong reference on the object in our arguments and that
117 * reference survives past method return since we return it.
119 final Object key = new SoftKey(object, queue) {
121 public void finalizeReferent() {
123 * NOTE: while it may be tempting to add "object" into this
124 * trace message, do not ever do that: it would retain
125 * a strong reference, preventing collection.
127 LOG.trace("Invalidating key {} for object {}", this);
128 cache.invalidate(this);
131 cache.put(key, object);
132 LOG.debug("Cached key {} to object {}", key, object);
137 public final <B extends ProductAwareBuilder<P>, P> P getProduct(final B builder) {
138 LOG.debug("Looking up product for {}", builder);
140 @SuppressWarnings("unchecked")
141 final P ret = (P) cache.getIfPresent(new BuilderKey(builder));
142 return ret == null ? put(Preconditions.checkNotNull(builder.toInstance())) : ret;
146 public final <T> T getReference(final T object) {
147 LOG.debug("Looking up reference for {}", object);
148 if (object == null) {
152 @SuppressWarnings("unchecked")
153 final T ret = (T) cache.getIfPresent(object);
154 return ret == null ? put(object) : ret;