001/*
002 * JPPF.
003 * Copyright (C) 2005-2018 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.jppf.utils.collections;
020
021import java.lang.ref.*;
022import java.util.*;
023
024import org.slf4j.*;
025
026/**
027 * Map whose values are soft references. When a value has been garbage-collected,
028 * the corresponding map entry is removed.
029 * @param <K> the type of the keys.
030 * @param <V> the type of the values.
031 * @author Laurent Cohen
032 */
033public class SoftReferenceValuesMap<K, V> extends AbstractMap<K, V> {
034  /**
035   * Logger for this class.
036   */
037  private static Logger log = LoggerFactory.getLogger(SoftReferenceValuesMap.class);
038  /**
039   * Determines whether TRACE logging level is enabled.
040   */
041  private static boolean traceEnabled = log.isTraceEnabled();
042  /**
043   * The soft references queue.
044   */
045  private final ReferenceQueue<V> refQueue;
046  /**
047   * The underlying map that backs this soft map.
048   */
049  private final Map<K, SoftValue<K, V>> map;
050
051  /**
052   * Default constructor.
053   */
054  public SoftReferenceValuesMap() {
055    this(0);
056  }
057
058  /**
059   * Default constructor.
060   * @param capacity an optional maximum capacity.
061   */
062  public SoftReferenceValuesMap(final int capacity) {
063    refQueue = new ReferenceQueue<>();
064    map = createMap(capacity);
065  }
066
067  /**
068   * Create the underlying map of soft references.
069   * @param capacity an optional maximum capacity.
070   * @return a new {@code Map} instance.
071   */
072  Map<K, SoftValue<K, V>> createMap(final int capacity) {
073    return new HashMap<>();
074  }
075
076  @Override
077  public int size() {
078    cleanup();
079    return map.size();
080  }
081
082  @Override
083  public boolean isEmpty() {
084    cleanup();
085    return map.isEmpty();
086  }
087
088  @Override
089  public V get(final Object key) {
090    cleanup();
091    final SoftReference<V> ref = map.get(key);
092    return ref == null ? null : ref.get();
093  }
094
095  @Override
096  public V put(final K key, final V value) {
097    cleanup();
098    final SoftReference<V> ref = map.put(key, new SoftValue<>(key, value, refQueue));
099    return ref == null ? null : ref.get();
100  }
101
102  @Override
103  public V remove(final Object key) {
104    cleanup();
105    final SoftReference<V> ref = map.remove(key);
106    return ref == null ? null : ref.get();
107  }
108
109  @Override
110  public Set<Map.Entry<K, V>> entrySet() {
111    throw new UnsupportedOperationException("This operation is not implemented");
112  }
113
114  @Override
115  public void clear() {
116    cleanup();
117    map.clear();
118  }
119
120  /**
121   * Cleanup the reference queue, by removing entries whose value was garbage collected.
122   */
123  @SuppressWarnings("unchecked")
124  private void cleanup() {
125    SoftValue<K, V> ref;
126    while ((ref = (SoftValue<K, V>) refQueue.poll()) != null) {
127      // NPE on this line ==>
128      final K key = ref.key;
129      if (key == null) continue;
130      if (traceEnabled) log.trace("removing entry for key=" + key);
131      map.remove(key);
132      ref.key = null;
133    }
134  }
135
136  /**
137   * Extension of SoftReference that holds the map key, so the corresponding entry
138   * can be removed from the map.
139   * @param <K> the type of the key.
140   * @param <V> the type of the value.
141   */
142  static class SoftValue<K, V> extends SoftReference<V> {
143    /**
144     * The associated key.
145     */
146    private K key;
147
148    /**
149     * Initialize this reference with the specified key and value.
150     * @param key the key.
151     * @param value the value.
152     * @param queue the reference queue to use.
153     */
154    public SoftValue(final K key, final V value, final ReferenceQueue<V> queue) {
155      super(value, queue);
156      this.key = key;
157    }
158  }
159}