View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.util;
21  
22  import java.lang.ref.ReferenceQueue;
23  import java.lang.ref.SoftReference;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Comparator;
27  import java.util.LinkedHashSet;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.SortedMap;
31  import java.util.TreeMap;
32  import java.util.TreeSet;
33  
34  /**
35   * A SortedMap implementation that uses Soft Reference values
36   * internally to make it play well with the GC when in a low-memory
37   * situation. Use as a cache where you also need SortedMap functionality.
38   *
39   * @param <K> key class
40   * @param <V> value class
41   */
42  public class SoftValueSortedMap<K,V> implements SortedMap<K,V> {
43    private final SortedMap<K, SoftValue<K,V>> internalMap;
44    private final ReferenceQueue rq = new ReferenceQueue();
45  
46    /** Constructor */
47    public SoftValueSortedMap() {
48      this(new TreeMap<K, SoftValue<K,V>>());
49    }
50  
51    /**
52     * Constructor
53     * @param c comparator
54     */
55    public SoftValueSortedMap(final Comparator<K> c) {
56      this(new TreeMap<K, SoftValue<K,V>>(c));
57    }
58  
59    /** For headMap and tailMap support
60     * @param original object to wrap
61     */
62    private SoftValueSortedMap(SortedMap<K,SoftValue<K,V>> original) {
63      this.internalMap = original;
64    }
65  
66    /**
67     * Checks soft references and cleans any that have been placed on
68     * ReferenceQueue.  Call if get/put etc. are not called regularly.
69     * Internally these call checkReferences on each access.
70     * @return How many references cleared.
71     */
72    private int checkReferences() {
73      int i = 0;
74      for (Object obj; (obj = this.rq.poll()) != null;) {
75        i++;
76        //noinspection unchecked
77        this.internalMap.remove(((SoftValue<K,V>)obj).key);
78      }
79      return i;
80    }
81  
82    public synchronized V put(K key, V value) {
83      checkReferences();
84      SoftValue<K,V> oldValue = this.internalMap.put(key,
85        new SoftValue<K,V>(key, value, this.rq));
86      return oldValue == null ? null : oldValue.get();
87    }
88  
89    @SuppressWarnings("unchecked")
90    public synchronized void putAll(Map map) {
91      throw new RuntimeException("Not implemented");
92    }
93  
94    @SuppressWarnings({"SuspiciousMethodCalls"})
95    public synchronized V get(Object key) {
96      checkReferences();
97      SoftValue<K,V> value = this.internalMap.get(key);
98      if (value == null) {
99        return null;
100     }
101     if (value.get() == null) {
102       this.internalMap.remove(key);
103       return null;
104     }
105     return value.get();
106   }
107 
108   public synchronized V remove(Object key) {
109     checkReferences();
110     SoftValue<K,V> value = this.internalMap.remove(key);
111     return value == null ? null : value.get();
112   }
113 
114   public synchronized boolean containsKey(Object key) {
115     checkReferences();
116     return this.internalMap.containsKey(key);
117   }
118 
119   public synchronized boolean containsValue(Object value) {
120 /*    checkReferences();
121     return internalMap.containsValue(value);*/
122     throw new UnsupportedOperationException("Don't support containsValue!");
123   }
124 
125   public synchronized K firstKey() {
126     checkReferences();
127     return internalMap.firstKey();
128   }
129 
130   public synchronized K lastKey() {
131     checkReferences();
132     return internalMap.lastKey();
133   }
134 
135   public synchronized SoftValueSortedMap<K,V> headMap(K key) {
136     checkReferences();
137     return new SoftValueSortedMap<K,V>(this.internalMap.headMap(key));
138   }
139 
140   public synchronized SoftValueSortedMap<K,V> tailMap(K key) {
141     checkReferences();
142     return new SoftValueSortedMap<K,V>(this.internalMap.tailMap(key));
143   }
144 
145   public synchronized SoftValueSortedMap<K,V> subMap(K fromKey, K toKey) {
146     checkReferences();
147     return new SoftValueSortedMap<K,V>(this.internalMap.subMap(fromKey, toKey));
148   }
149 
150   public synchronized boolean isEmpty() {
151     checkReferences();
152     return this.internalMap.isEmpty();
153   }
154 
155   public synchronized int size() {
156     checkReferences();
157     return this.internalMap.size();
158   }
159 
160   public synchronized void clear() {
161     checkReferences();
162     this.internalMap.clear();
163   }
164 
165   public synchronized Set<K> keySet() {
166     checkReferences();
167     return this.internalMap.keySet();
168   }
169 
170   @SuppressWarnings("unchecked")
171   public synchronized Comparator comparator() {
172     return this.internalMap.comparator();
173   }
174 
175   public synchronized Set<Map.Entry<K,V>> entrySet() {
176     checkReferences();
177     Set<Map.Entry<K, SoftValue<K, V>>> entries = this.internalMap.entrySet();
178     Set<Map.Entry<K, V>> realEntries = new LinkedHashSet<Map.Entry<K, V>>();
179     for (Map.Entry<K, SoftValue<K, V>> entry : entries) {
180       realEntries.add(entry.getValue());
181     }
182     return realEntries;
183   }
184 
185   public synchronized Collection<V> values() {
186     checkReferences();
187     Collection<SoftValue<K,V>> softValues = this.internalMap.values();
188     ArrayList<V> hardValues = new ArrayList<V>();
189     for(SoftValue<K,V> softValue : softValues) {
190       hardValues.add(softValue.get());
191     }
192     return hardValues;
193   }
194 
195   private static class SoftValue<K,V> extends SoftReference<V> implements Map.Entry<K, V> {
196     final K key;
197 
198     SoftValue(K key, V value, ReferenceQueue q) {
199       super(value, q);
200       this.key = key;
201     }
202 
203     public K getKey() {
204       return this.key;
205     }
206 
207     public V getValue() {
208       return get();
209     }
210 
211     public V setValue(V value) {
212       throw new RuntimeException("Not implemented");
213     }
214   }
215 }