2 * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.yang.model.repo.spi;
10 import com.google.common.annotations.Beta;
11 import com.google.common.util.concurrent.Futures;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.lang.ref.Cleaner;
14 import java.lang.ref.Cleaner.Cleanable;
15 import java.lang.ref.Reference;
16 import java.lang.ref.SoftReference;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentMap;
19 import org.opendaylight.yangtools.concepts.Registration;
20 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
21 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
22 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
23 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
26 * A simple {@link AbstractSchemaSourceCache} maintaining soft references.
28 * @param <T> {@link SchemaSourceRepresentation} type stored in this cache
31 public final class SoftSchemaSourceCache<T extends SchemaSourceRepresentation> extends AbstractSchemaSourceCache<T>
32 implements AutoCloseable {
33 private static final Cleaner CLEANER = Cleaner.create();
35 private final ConcurrentMap<SourceIdentifier, SoftReference<T>> references = new ConcurrentHashMap<>();
36 private final ConcurrentMap<Registration, Cleanable> cleanables = new ConcurrentHashMap<>();
38 private boolean closed;
40 public SoftSchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation) {
41 super(consumer, representation, Costs.IMMEDIATE);
45 public ListenableFuture<? extends T> getSource(final SourceIdentifier sourceIdentifier) {
46 final var ref = references.get(sourceIdentifier);
48 final var src = ref.get();
51 return Futures.immediateFuture(src);
54 // Expired entry: remove it
55 references.remove(sourceIdentifier, ref);
58 return Futures.immediateFailedFuture(new MissingSchemaSourceException("Source not found", sourceIdentifier));
62 public synchronized void close() {
65 while (!cleanables.isEmpty()) {
66 cleanables.values().forEach(Cleanable::clean);
72 protected synchronized void offer(final T source) {
77 final var id = source.getIdentifier();
78 final var ref = new SoftReference<>(source);
81 final var prev = references.putIfAbsent(id, ref);
83 // We have performed a fresh insert and need to add a cleanup
87 if (prev.get() != null) {
88 // We still have a source for this identifier, no further action is needed
92 // Existing reference is dead, remove it and retry
93 references.remove(id, prev);
96 // We have populated a cache entry, register the source and a cleanup action
97 final var reg = register(id);
98 cleanables.put(reg, CLEANER.register(source, () -> {
99 cleanables.remove(reg);
101 references.remove(id, ref);
104 // Ensure 'source' is still reachable here. This is needed to ensure the cleanable action does not fire before
105 // we have had a chance to insert it into the map.
106 Reference.reachabilityFence(source);