View Javadoc

1   /* This file is part of COPAL (COntext Provisioning for All).
2    *
3    * COPAL is a part of SM4All (Smart hoMes for All) project.
4    *
5    * COPAL is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU Lesser General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * COPAL is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public License
16   * along with COPAL. If not, see <http://www.gnu.org/licenses/>.
17   */
18  package at.ac.tuwien.infosys.sm4all.copal.api.util;
19  
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.util.concurrent.locks.ReadWriteLock;
24  import java.util.concurrent.locks.ReentrantReadWriteLock;
25  import at.ac.tuwien.infosys.sm4all.copal.api.util.RegistryObservable.RegistrationState;
26  import at.ac.tuwien.infosys.sm4all.copal.api.util.RegistryObservable.RegistryObserver;
27  
28  /**
29   * The abstract class which can be used as base class for any {@link Registry}.
30   * 
31   * @param <K> the type of keys maintained by this {@link Registry}.
32   * @param <V> the type of values maintained by this {@link Registry}.
33   * @author sanjin
34   */
35  public abstract class BaseRegistry<K, V> extends
36          BaseObservable<RegistrationState, V, RegistryObserver<V>> implements
37          Registry<K, V>, RegistryObservable<V> {
38  
39      private final Map<K, V> registrations = new HashMap<K, V>();
40      private final ReadWriteLock lock = new ReentrantReadWriteLock();
41  
42      @Override
43      public void attach(final RegistryObserver<V> observer) {
44          this.lock.readLock().lock();
45          try {
46              super.attach(observer);
47  
48              for (final V value : this.registrations.values()) {
49                  observer.update(RegistrationState.Registered, value);
50              }
51          } finally {
52              this.lock.readLock().unlock();
53          }
54      }
55  
56      @Override
57      public void detach(final RegistryObserver<V> observer) {
58          this.lock.readLock().lock();
59          try {
60              super.detach(observer);
61  
62              for (final V value : this.registrations.values()) {
63                  observer.update(RegistrationState.Unregistered, value);
64              }
65          } finally {
66              this.lock.readLock().unlock();
67          }
68      }
69  
70      /**
71       * Registers specified value. If there is already a value associated with
72       * same key the old value will remain registered and this method will throw
73       * {@link AlreadyRegisteredException}.
74       * 
75       * @param value the value.
76       * @throws NullPointerException if specified value is <code>null</code>.
77       * @throws AlreadyRegisteredException if a value with same key is already
78       *         registered.
79       * @see #set(Object...)
80       */
81      @Override
82      public void register(final V value) throws AlreadyRegisteredException {
83          if (null == value) {
84              throw new NullPointerException("Value cannot be null.");
85          }
86  
87          final K key = getKey(value);
88  
89          this.lock.writeLock().lock();
90          try {
91              if (this.registrations.containsKey(key)) {
92                  throw new AlreadyRegisteredException(key.toString());
93              }
94  
95              this.registrations.put(key, value);
96              notifyAll(RegistrationState.Registered, value);
97          } finally {
98              this.lock.writeLock().unlock();
99          }
100     }
101 
102     /**
103      * Unregisters value associated with specified key. If there is no value
104      * associated with specified key the registry remains unchanged and this
105      * method will throw {@link NotRegisteredException}.
106      * 
107      * @param key the key.
108      * @throws NullPointerException if specified key is <code>null</code>.
109      * @throws NotRegisteredException if there is no value associated with
110      *         specified key.
111      */
112     @Override
113     public void unregister(final K key) throws NotRegisteredException {
114         if (null == key) {
115             throw new NullPointerException("Key cannot be null.");
116         }
117 
118         V value = null;
119 
120         this.lock.writeLock().lock();
121         try {
122             if (!this.registrations.containsKey(key)) {
123                 throw new NotRegisteredException(key.toString());
124             }
125 
126             value = this.registrations.remove(key);
127             notifyAll(RegistrationState.Unregistered, value);
128         } finally {
129             this.lock.writeLock().unlock();
130         }
131     }
132 
133     /**
134      * Returns if there is a value registered with specified key.
135      * 
136      * @param key the key.
137      * @return if there is a value registered with specified key.
138      * @throws NullPointerException if specified key is <code>null</code>.
139      */
140     @Override
141     public boolean isRegistered(final K key) {
142         if (null == key) {
143             throw new NullPointerException("Key cannot be null.");
144         }
145 
146         final boolean result;
147 
148         this.lock.readLock().lock();
149         try {
150             result = this.registrations.containsKey(key);
151         } finally {
152             this.lock.readLock().unlock();
153         }
154 
155         return result;
156     }
157 
158     /**
159      * Returns a value registered with specified key or <code>null</code> if
160      * there is no such value.
161      * 
162      * @param key the key.
163      * @return value registered with specified key or <code>null</code> if there
164      *         is no such value.
165      * @throws NullPointerException if specified key is <code>null</code>.
166      */
167     @Override
168     public V get(final K key) {
169         if (null == key) {
170             throw new NullPointerException("Key cannot be null.");
171         }
172 
173         V result = null;
174 
175         this.lock.readLock().lock();
176         try {
177             if (this.registrations.containsKey(key)) {
178                 result = this.registrations.get(key);
179             }
180         } finally {
181             this.lock.readLock().unlock();
182         }
183 
184         return result;
185     }
186 
187     /**
188      * Return the key for specified value.
189      * 
190      * @param value the value.
191      * @return the key.
192      */
193     protected abstract K getKey(V value);
194 
195     /**
196      * Register specified values. If there is already a value associated with
197      * same key, the old value will be overwritten with new value and returned.
198      * 
199      * @param values the values.
200      * @throws NullPointerException if specified array of values or any value in
201      *         the array is <code>null</code>.
202      * @see #register(Object)
203      */
204     protected void set(final V... values) {
205         if (null == values) {
206             throw new NullPointerException("Values cannot be null.");
207         }
208         for (final V value : values) {
209             if (null == value) {
210                 throw new NullPointerException("Value cannot be null.");
211             }
212         }
213 
214         this.lock.writeLock().lock();
215         try {
216             for (final V value : values) {
217                 final K key = getKey(value);
218                 if (this.registrations.containsKey(key)) {
219                     notifyAll(RegistrationState.Unregistered,
220                             this.registrations.get(key));
221                 }
222 
223                 this.registrations.put(key, value);
224                 notifyAll(RegistrationState.Registered, value);
225             }
226         } finally {
227             this.lock.writeLock().unlock();
228         }
229     }
230 
231     /**
232      * Unregisters all registered values.
233      */
234     protected void unregisterAll() {
235         this.lock.writeLock().lock();
236         try {
237             for (final V value : this.registrations.values()) {
238                 notifyAll(RegistrationState.Unregistered, value);
239             }
240 
241             this.registrations.clear();
242         } finally {
243             this.lock.writeLock().unlock();
244         }
245     }
246 
247     /**
248      * Returns all registered values.
249      * 
250      * @return all registered values.
251      */
252     protected Collection<V> getValues() {
253         final Collection<V> result;
254 
255         this.lock.readLock().lock();
256         try {
257             result = this.registrations.values();
258         } finally {
259             this.lock.readLock().unlock();
260         }
261 
262         return result;
263     }
264 
265     /**
266      * Register specified values and remove all previously registered values.
267      * 
268      * @param values the values.
269      * @throws NullPointerException if specified array of values or any value in
270      *         the array is <code>null</code>.
271      */
272     protected void setValues(final V... values) {
273         if (null == values) {
274             throw new NullPointerException("Values cannot be null.");
275         }
276         for (final V value : values) {
277             if (null == value) {
278                 throw new NullPointerException("Value cannot be null.");
279             }
280         }
281 
282         this.lock.writeLock().lock();
283         try {
284             unregisterAll();
285 
286             for (final V value : values) {
287                 this.registrations.put(getKey(value), value);
288                 notifyAll(RegistrationState.Registered, value);
289             }
290         } finally {
291             this.lock.writeLock().unlock();
292         }
293     }
294 }