001/*
002 * JPPF.
003 * Copyright (C) 2005-2016 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 ReferenceQueue<V> refQueue;
046  /**
047   * The underlying map that backs this soft map.
048   */
049  private Map<K, SoftValue<K, V>> map;
050
051  /**
052   * Default constructor.
053   */
054  public SoftReferenceValuesMap() {
055    refQueue = new ReferenceQueue<>();
056    map = createMap();
057  }
058
059  /**
060   * Create the underlying map of soft references.
061   * @return a new {@code Map} instance.
062   */
063  Map<K, SoftValue<K, V>> createMap() {
064    return new HashMap<>();
065  }
066
067  @Override
068  public int size() {
069    cleanup();
070    return map.size();
071  }
072
073  @Override
074  public boolean isEmpty() {
075    cleanup();
076    return map.isEmpty();
077  }
078
079  @Override
080  public V get(final Object key) {
081    cleanup();
082    SoftReference<V> ref = map.get(key);
083    return ref == null ? null : ref.get();
084  }
085
086  @Override
087  @SuppressWarnings("unchecked")
088  public V put(final K key, final V value) {
089    cleanup();
090    SoftReference<V> ref = map.put(key, new SoftValue(key, value, refQueue));
091    return ref == null ? null : ref.get();
092  }
093
094  @Override
095  public V remove(final Object key) {
096    cleanup();
097    SoftReference<V> ref = map.remove(key);
098    return ref == null ? null : ref.get();
099  }
100
101  @Override
102  public Set<Map.Entry<K, V>> entrySet() {
103    throw new UnsupportedOperationException("This operation is not implemented");
104  }
105
106  @Override
107  public void clear() {
108    cleanup();
109    map.clear();
110  }
111
112  /**
113   * Cleanup the reference queue, by removing entries whose value was garbage collected.
114   */
115  @SuppressWarnings("unchecked")
116  private void cleanup() {
117    SoftValue<K, V> ref;
118    while ((ref = (SoftValue) refQueue.poll()) != null) {
119      // NPE on this line ==>
120      K key = ref.key;
121      if (key == null) continue;
122      if (traceEnabled) log.trace("removing entry for key=" + key);
123      map.remove(key);
124      ref.key = null;
125    }
126  }
127
128  /**
129   * Extension of SoftReference that holds the map key, so the corresponding entry
130   * can be removed from the map.
131   * @param <K> the type of the key.
132   * @param <V> the type of the value.
133   */
134  static class SoftValue<K, V> extends SoftReference<V> {
135    /**
136     * The associated key.
137     */
138    private K key;
139
140    /**
141     * Initialize this reference with the specified key and value.
142     * @param key the key.
143     * @param value the value.
144     * @param queue the reference queue to use.
145     */
146    public SoftValue(final K key, final V value, final ReferenceQueue<V> queue) {
147      super(value, queue);
148      this.key = key;
149    }
150  }
151}