View Javadoc

1   /**
2    * Copyright 2008 Atlassian Pty Ltd 
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package com.atlassian.util.concurrent;
18  
19  import static com.atlassian.util.concurrent.Assertions.notNull;
20  import net.jcip.annotations.GuardedBy;
21  import net.jcip.annotations.ThreadSafe;
22  
23  import java.io.Serializable;
24  import java.util.Collection;
25  import java.util.Iterator;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.concurrent.ConcurrentMap;
29  import java.util.concurrent.locks.Lock;
30  import java.util.concurrent.locks.ReentrantLock;
31  
32  /**
33   * Abstract base class for COW {@link Map} implementations that delegate to an
34   * internal map.
35   * 
36   * @param <K> The key type
37   * @param <V> The value type
38   * @param <M> the internal {@link Map} or extension for things like sorted and
39   * navigable maps.
40   */
41  @ThreadSafe
42  abstract class AbstractCopyOnWriteMap<K, V, M extends Map<K, V>> implements ConcurrentMap<K, V>, Serializable {
43      private static final long serialVersionUID = 4508989182041753878L;
44  
45      @GuardedBy("lock")
46      private volatile M delegate;
47      private final transient EntrySet entrySet = new EntrySet();
48      private final transient KeySet keySet = new KeySet();
49      private final transient Values values = new Values();
50  
51      // import edu.umd.cs.findbugs.annotations.@SuppressWarnings
52      private final transient Lock lock = new ReentrantLock();
53  
54      /**
55       * Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to
56       * initialize the values and the {@link CopyFunction} for creating our
57       * actual delegate instances.
58       * 
59       * @param map the initial map to initialize with
60       * @param factory the copy function
61       */
62      protected <N extends Map<? extends K, ? extends V>> AbstractCopyOnWriteMap(final N map) {
63          this.delegate = notNull("delegate", copy(notNull("map", map)));
64      }
65  
66      /**
67       * Copy function, implemented by sub-classes.
68       * 
69       * @param <N> the map to copy and return.
70       * @param map the initial values of the newly created map.
71       * @return a new map. Will never be modified after construction.
72       */
73      @GuardedBy("lock")
74      abstract <N extends Map<? extends K, ? extends V>> M copy(N map);
75  
76      //
77      // mutable operations
78      //
79  
80      public final void clear() {
81          lock.lock();
82          try {
83              final M map = copy();
84              map.clear();
85              set(map);
86          } finally {
87              lock.unlock();
88          }
89      }
90  
91      public final V remove(final Object key) {
92          lock.lock();
93          try {
94              // short circuit if key doesn't exist
95              if (!delegate.containsKey(key)) {
96                  return null;
97              }
98              final M map = copy();
99              final V result = map.remove(key);
100             set(map);
101             return result;
102         } finally {
103             lock.unlock();
104         }
105     }
106 
107     public boolean remove(final Object key, final Object value) {
108         lock.lock();
109         try {
110             if (delegate.containsKey(key) && equals(value, delegate.get(key))) {
111                 final M map = copy();
112                 map.remove(key);
113                 set(map);
114                 return true;
115             } else {
116                 return false;
117             }
118         } finally {
119             lock.unlock();
120         }
121     }
122 
123     public boolean replace(final K key, final V oldValue, final V newValue) {
124         lock.lock();
125         try {
126             if (delegate.containsKey(key) && equals(oldValue, delegate.get(key))) {
127                 final M map = copy();
128                 map.put(key, newValue);
129                 set(map);
130                 return true;
131             } else {
132                 return false;
133             }
134         } finally {
135             lock.unlock();
136         }
137     };
138 
139     public V replace(final K key, final V value) {
140         lock.lock();
141         try {
142             if (delegate.containsKey(key)) {
143                 final M map = copy();
144                 try {
145                     return map.put(key, value);
146                 } finally {
147                     set(map);
148                 }
149             } else {
150                 return null;
151             }
152         } finally {
153             lock.unlock();
154         }
155     };
156 
157     public final V put(final K key, final V value) {
158         lock.lock();
159         try {
160             final M map = copy();
161             final V result = map.put(key, value);
162             set(map);
163             return result;
164         } finally {
165             lock.unlock();
166         }
167     }
168 
169     public V putIfAbsent(final K key, final V value) {
170         lock.lock();
171         try {
172             if (!delegate.containsKey(key)) {
173                 final M map = copy();
174                 final V result = map.put(key, value);
175                 set(map);
176                 return result;
177             }
178             return delegate.get(key);
179         } finally {
180             lock.unlock();
181         }
182     };
183 
184     public final void putAll(final Map<? extends K, ? extends V> t) {
185         lock.lock();
186         try {
187             final M map = copy();
188             map.putAll(t);
189             set(map);
190         } finally {
191             lock.unlock();
192         }
193     }
194 
195     protected M copy() {
196         lock.lock();
197         try {
198             return copy(delegate);
199         } finally {
200             lock.unlock();
201         }
202     }
203 
204     @GuardedBy("lock")
205     protected void set(final M map) {
206         delegate = map;
207     }
208 
209     //
210     // Collection views
211     //
212 
213     public final Set<Map.Entry<K, V>> entrySet() {
214         return entrySet;
215     }
216 
217     public final Set<K> keySet() {
218         return keySet;
219     }
220 
221     public final Collection<V> values() {
222         return values;
223     }
224 
225     //
226     // delegate operations
227     //
228 
229     public final boolean containsKey(final Object key) {
230         return delegate.containsKey(key);
231     }
232 
233     public final boolean containsValue(final Object value) {
234         return delegate.containsValue(value);
235     }
236 
237     public final V get(final Object key) {
238         return delegate.get(key);
239     }
240 
241     public final boolean isEmpty() {
242         return delegate.isEmpty();
243     }
244 
245     public final int size() {
246         return delegate.size();
247     }
248 
249     @Override
250     public final boolean equals(final Object o) {
251         return delegate.equals(o);
252     }
253 
254     @Override
255     public final int hashCode() {
256         return delegate.hashCode();
257     }
258 
259     protected final M getDelegate() {
260         return delegate;
261     }
262 
263     @Override
264     public String toString() {
265         return delegate.toString();
266     }
267 
268     //
269     // inner classes
270     //
271 
272     private class KeySet extends CollectionView<K> implements Set<K> {
273 
274         @Override
275         Collection<K> getDelegate() {
276             return delegate.keySet();
277         }
278 
279         //
280         // mutable operations
281         //
282 
283         public void clear() {
284             lock.lock();
285             try {
286                 final M map = copy();
287                 map.keySet().clear();
288                 set(map);
289             } finally {
290                 lock.unlock();
291             }
292         }
293 
294         public boolean remove(final Object o) {
295             return AbstractCopyOnWriteMap.this.remove(o) != null;
296         }
297 
298         public boolean removeAll(final Collection<?> c) {
299             lock.lock();
300             try {
301                 final M map = copy();
302                 final boolean result = map.keySet().removeAll(c);
303                 set(map);
304                 return result;
305             } finally {
306                 lock.unlock();
307             }
308         }
309 
310         public boolean retainAll(final Collection<?> c) {
311             lock.lock();
312             try {
313                 final M map = copy();
314                 final boolean result = map.keySet().retainAll(c);
315                 set(map);
316                 return result;
317             } finally {
318                 lock.unlock();
319             }
320         }
321     }
322 
323     private final class Values extends CollectionView<V> {
324 
325         @Override
326         Collection<V> getDelegate() {
327             return delegate.values();
328         }
329 
330         public void clear() {
331             lock.lock();
332             try {
333                 final M map = copy();
334                 map.values().clear();
335                 set(map);
336             } finally {
337                 lock.unlock();
338             }
339         }
340 
341         public boolean remove(final Object o) {
342             lock.lock();
343             try {
344                 if (!contains(o)) {
345                     return false;
346                 }
347                 final M map = copy();
348                 final boolean result = map.values().remove(o);
349                 set(map);
350                 return result;
351             } finally {
352                 lock.unlock();
353             }
354         }
355 
356         public boolean removeAll(final Collection<?> c) {
357             lock.lock();
358             try {
359                 final M map = copy();
360                 final boolean result = map.values().removeAll(c);
361                 set(map);
362                 return result;
363             } finally {
364                 lock.unlock();
365             }
366         }
367 
368         public boolean retainAll(final Collection<?> c) {
369             lock.lock();
370             try {
371                 final M map = copy();
372                 final boolean result = map.values().retainAll(c);
373                 set(map);
374                 return result;
375             } finally {
376                 lock.unlock();
377             }
378         }
379     }
380 
381     private class EntrySet extends CollectionView<Entry<K, V>> implements Set<Map.Entry<K, V>> {
382 
383         @Override
384         Collection<java.util.Map.Entry<K, V>> getDelegate() {
385             return delegate.entrySet();
386         }
387 
388         public void clear() {
389             lock.lock();
390             try {
391                 final M map = copy();
392                 map.entrySet().clear();
393                 set(map);
394             } finally {
395                 lock.unlock();
396             }
397         }
398 
399         public boolean remove(final Object o) {
400             lock.lock();
401             try {
402                 if (!contains(o)) {
403                     return false;
404                 }
405                 final M map = copy();
406                 final boolean result = map.entrySet().remove(o);
407                 set(map);
408                 return result;
409             } finally {
410                 lock.unlock();
411             }
412         }
413 
414         public boolean removeAll(final Collection<?> c) {
415             lock.lock();
416             try {
417                 final M map = copy();
418                 final boolean result = map.entrySet().removeAll(c);
419                 set(map);
420                 return result;
421             } finally {
422                 lock.unlock();
423             }
424         }
425 
426         public boolean retainAll(final Collection<?> c) {
427             lock.lock();
428             try {
429                 final M map = copy();
430                 final boolean result = map.entrySet().retainAll(c);
431                 set(map);
432                 return result;
433             } finally {
434                 lock.unlock();
435             }
436         }
437     }
438 
439     private static class UnmodifiableIterator<T> implements Iterator<T> {
440         private final Iterator<T> delegate;
441 
442         public UnmodifiableIterator(final Iterator<T> delegate) {
443             this.delegate = delegate;
444         }
445 
446         public boolean hasNext() {
447             return delegate.hasNext();
448         }
449 
450         public T next() {
451             return delegate.next();
452         }
453 
454         public void remove() {
455             throw new UnsupportedOperationException();
456         }
457     }
458 
459     protected static abstract class CollectionView<E> implements Collection<E> {
460 
461         abstract Collection<E> getDelegate();
462 
463         //
464         // delegate operations
465         //
466 
467         public final boolean contains(final Object o) {
468             return getDelegate().contains(o);
469         }
470 
471         public final boolean containsAll(final Collection<?> c) {
472             return getDelegate().containsAll(c);
473         }
474 
475         public final Iterator<E> iterator() {
476             return new UnmodifiableIterator<E>(getDelegate().iterator());
477         }
478 
479         public final boolean isEmpty() {
480             return getDelegate().isEmpty();
481         }
482 
483         public final int size() {
484             return getDelegate().size();
485         }
486 
487         public final Object[] toArray() {
488             return getDelegate().toArray();
489         }
490 
491         public final <T> T[] toArray(final T[] a) {
492             return getDelegate().toArray(a);
493         }
494 
495         @Override
496         public int hashCode() {
497             return getDelegate().hashCode();
498         }
499 
500         @Override
501         public boolean equals(final Object obj) {
502             return getDelegate().equals(obj);
503         }
504 
505         @Override
506         public String toString() {
507             return getDelegate().toString();
508         }
509 
510         //
511         // unsupported operations
512         //
513 
514         public final boolean add(final E o) {
515             throw new UnsupportedOperationException();
516         }
517 
518         public final boolean addAll(final Collection<? extends E> c) {
519             throw new UnsupportedOperationException();
520         }
521     }
522 
523     private boolean equals(final Object o1, final Object o2) {
524         if (o1 == null) {
525             return o2 == null;
526         }
527         return o1.equals(o2);
528     }
529 }