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 static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.base.FinalizableReferenceQueue;
14 import com.google.common.base.FinalizableSoftReference;
15 import com.google.common.cache.Cache;
16 import java.util.concurrent.ExecutionException;
17 import javax.annotation.Nonnull;
18 import org.opendaylight.yangtools.concepts.ProductAwareBuilder;
19 import org.opendaylight.yangtools.objcache.ObjectCache;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
24 * Abstract object cache implementation. This implementation takes care
25 * of interacting with the user and manages interaction with the Garbage
26 * Collector (via soft references). Subclasses are expected to provide
27 * a backing {@link Cache} instance and provide the
29 public abstract class AbstractObjectCache implements ObjectCache {
31 * Key used when looking up a ProductAwareBuilder product. We assume
32 * the builder is not being modified for the duration of the lookup,
33 * anything else is the user's fault.
36 static final class BuilderKey {
37 private final ProductAwareBuilder<?> builder;
39 private BuilderKey(final ProductAwareBuilder<?> builder) {
40 this.builder = requireNonNull(builder);
44 public int hashCode() {
45 return builder.productHashCode();
49 public boolean equals(Object obj) {
51 * We can tolerate null objects coming our way, but we need
52 * to be on the lookout for WeakKeys, as we cannot pass them
53 * directly to productEquals().
55 if (obj instanceof SoftKey) {
56 obj = ((SoftKey<?>)obj).get();
59 return builder.productEquals(obj);
64 * Key used in the underlying map. It is essentially a soft reference, with
65 * slightly special properties.
68 * It acts as a proxy for the object it refers to and essentially delegates
69 * to it. There are three exceptions here:
72 * 1) This key needs to have a cached hash code. The requirement is that the
73 * key needs to be able to look itself up after the reference to the object
74 * has been cleared (and thus we can no longer look it up from there). One
75 * typical container where we are stored are HashMaps -- and they need it
77 * 2) This key does not tolerate checks to see if its equal to null. While we
78 * could return false, we want to catch offenders who try to store nulls
80 * 3) This key inverts the check for equality, e.g. it calls equals() on the
81 * object which was passed to its equals(). Instead of supplying itself,
82 * it supplies the referent. If the soft reference is cleared, such check
83 * will return false, which is fine as it prevents normal lookup from
84 * seeing the cleared key. Removal is handled by the explicit identity
87 protected abstract static class SoftKey<T> extends FinalizableSoftReference<T> {
88 private final int hashCode;
90 public SoftKey(final T referent, final FinalizableReferenceQueue queue) {
91 super(requireNonNull(referent), queue);
92 hashCode = referent.hashCode();
96 public boolean equals(final Object obj) {
101 // Order is important: we do not want to call equals() on ourselves!
102 return this == obj || obj.equals(get());
106 public int hashCode() {
111 private static final Logger LOG = LoggerFactory.getLogger(AbstractObjectCache.class);
112 private final FinalizableReferenceQueue queue;
113 private final Cache<SoftKey<?>, Object> cache;
115 protected AbstractObjectCache(final Cache<SoftKey<?>, Object> cache, final FinalizableReferenceQueue queue) {
116 this.queue = requireNonNull(queue);
117 this.cache = requireNonNull(cache);
120 protected <T> SoftKey<T> createSoftKey(final T object) {
122 * This may look like a race (having a soft reference and not have
123 * it in the cache). In fact this is protected by the fact we still
124 * have a strong reference on the object in our arguments and that
125 * reference survives past method return since we return it.
127 return new SoftKey<T>(object, queue) {
129 public void finalizeReferent() {
131 * NOTE: while it may be tempting to add "object" into this
132 * trace message, do not ever do that: it would retain
133 * a strong reference, preventing collection.
135 LOG.trace("Invalidating key {}", this);
136 cache.invalidate(this);
142 public final <B extends ProductAwareBuilder<P>, P> P getProduct(@Nonnull final B builder) {
143 throw new UnsupportedOperationException();
144 // LOG.debug("Looking up product for {}", builder);
146 // @SuppressWarnings("unchecked")
147 // final P ret = (P) cache.getIfPresent(new BuilderKey(builder));
148 // return ret == null ? put(Preconditions.checkNotNull(builder.toInstance())) : ret;
152 @SuppressWarnings("unchecked")
153 public final <T> T getReference(final T object) {
154 LOG.debug("Looking up reference for {}", object);
155 if (object == null) {
159 final SoftKey<T> key = createSoftKey(object);
161 return (T) cache.get(key, () -> object);
162 } catch (ExecutionException e) {
163 throw new IllegalStateException("Failed to load value", e);