View Javadoc

1   /**
2    * This file Copyright (c) 2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.registry;
35  
36  import org.apache.commons.lang.StringUtils;
37  
38  import java.util.ArrayList;
39  import java.util.Collection;
40  import java.util.HashMap;
41  import java.util.HashSet;
42  import java.util.Map;
43  import java.util.Set;
44  
45  /**
46   * Thread safe map intended to be used for registries. Provides an atomic operation <code>removeAndPutAll</code> that is
47   * used to remove a set of previously added values before adding a collection of new ones. Read operations are blocked
48   * until it completes guaranteeing proper visibility.
49   * <p/>
50   * It is common for entities in registries to also hold their identifier. The method <code>keyFormValue</code> can be
51   * overridden to get the identifier from the value. This removes the need to package a set of entities that should be
52   * added in a Map before calling {@link #removeAndPutAll(java.util.Collection, java.util.Map)}.
53   *
54   * @param <K> the type of keys maintained by this map
55   * @param <V> the type of mapped values
56   * @version $Id$
57   */
58  public class RegistryMap<K, V> {
59  
60      private final Map<K, V> map = new HashMap<K, V>();
61  
62      public synchronized V get(K key) {
63          return map.get(key);
64      }
65  
66      public synchronized V getRequired(K key) throws RegistrationException {
67          V value = map.get(key);
68          if (value == null) {
69              throw new RegistrationException("Entry for [" + key + "] not found in registry, available entries are: " +
70                      (map.isEmpty() ? "<none>" : StringUtils.join(map.keySet(), ", ")));
71          }
72          return value;
73      }
74  
75      public synchronized void put(K key, V value) {
76          map.put(key, value);
77      }
78  
79      public synchronized void put(V value) {
80          map.put(keyFromValue(value), value);
81      }
82  
83      public synchronized void remove(K key) {
84          map.remove(key);
85      }
86  
87      public synchronized void removeAndPutAll(Collection<K> toRemove, Map<K, V> toPut) {
88          // Fail early if the keyFromValue method hasn't been overridden
89          if (!toPut.isEmpty()) {
90              keyFromValue(toPut.values().iterator().next());
91          }
92          for (K key : toRemove) {
93              map.remove(key);
94          }
95          map.putAll(toPut);
96      }
97  
98      public synchronized Set<K> removeAndPutAll(Collection<K> toRemove, Collection<V> toPut) {
99          if (!toPut.isEmpty()) {
100             keyFromValue(toPut.iterator().next());
101         }
102         for (K key : toRemove) {
103             map.remove(key);
104         }
105         HashSet<K> keys = new HashSet<K>();
106         for (V value : toPut) {
107             K key = keyFromValue(value);
108             map.put(key, value);
109             keys.add(key);
110         }
111         return keys;
112     }
113 
114     public synchronized Collection<K> keySet() {
115         return new ArrayList<K>(map.keySet());
116     }
117 
118     public synchronized Collection<V> values() {
119         return new ArrayList<V>(map.values());
120     }
121 
122     protected K keyFromValue(V value) {
123         throw new UnsupportedOperationException("keyFromValue");
124     }
125 }