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.text.MessageFormat;
21  import java.util.LinkedList;
22  import java.util.List;
23  import java.util.concurrent.atomic.AtomicReference;
24  import java.util.concurrent.locks.ReadWriteLock;
25  import java.util.concurrent.locks.ReentrantReadWriteLock;
26  import org.apache.log4j.Level;
27  import org.apache.log4j.Logger;
28  import at.ac.tuwien.infosys.sm4all.copal.api.ContextException;
29  import at.ac.tuwien.infosys.sm4all.copal.api.util.RegistryObservable.RegistrationState;
30  
31  /**
32   * The abstract class which can be used as base class for any class that needs
33   * to register and unregister values with a {@link Registry}.
34   * 
35   * @param <K> the type of keys maintained by the {@link Registry}.
36   * @param <U> the type of values maintained by the {@link Registry}.
37   * @param <V> the type of registered values.
38   * @author sanjin
39   */
40  public abstract class Register<K, U, V extends U> {
41  
42      private static final Logger LOGGER = Logger.getLogger(Register.class);
43  
44      private final List<V> registrations = new LinkedList<V>();
45      private final ReadWriteLock lock = new ReentrantReadWriteLock();
46      private final AtomicReference<Registry<K, U>> registryReference = new AtomicReference<Registry<K, U>>(
47              null);
48  
49      /**
50       * Called when specified value changes its {@link RegistrationState}.
51       * 
52       * @param state the {@link RegistrationState}
53       * @param value the value.
54       */
55      protected abstract void update(RegistrationState state, V value);
56  
57      /**
58       * Returns the key for specified value.
59       * 
60       * @param value the value.
61       * @return the key.
62       */
63      protected abstract K getKey(V value);
64  
65      /**
66       * Register specified value when {@link Registry} becomes available (or
67       * immediately if it is already available).
68       * 
69       * @param value the value to register.
70       * @throws ContextException if registration fails.
71       */
72      public void register(final V value) throws ContextException {
73          this.lock.writeLock().lock();
74          try {
75              this.registrations.add(value);
76  
77              final Registry<K, U> registry = this.registryReference.get();
78              if (null != registry) {
79                  registry.register(value);
80                  update(RegistrationState.Registered, value);
81              }
82          } finally {
83              this.lock.writeLock().unlock();
84          }
85      }
86  
87      /**
88       * Do not register specified value when {@link Registry} becomes available
89       * (and unregister it immediately if it is already available).
90       * 
91       * @param value the value to unregister.
92       * @throws ContextException if unregistration fails.
93       */
94      public void unregister(final V value) throws ContextException {
95          this.lock.writeLock().lock();
96          try {
97              final Registry<K, U> registry = this.registryReference.get();
98              if (null != registry) {
99                  update(RegistrationState.Unregistered, value);
100                 registry.unregister(getKey(value));
101             }
102 
103             this.registrations.remove(value);
104         } finally {
105             this.lock.writeLock().unlock();
106         }
107     }
108 
109     /**
110      * Returns the {@link Registry}.
111      * 
112      * @return the {@link Registry}
113      */
114     protected Registry<K, U> getRegistry() {
115         return this.registryReference.get();
116     }
117 
118     /**
119      * Sets the new {@link Registry}, unregisters all values from previously set
120      * {@link Registry} if it was not <code>null</code> and registers all values
121      * with specified {@link Registry} if it is not <code>null</code>.
122      * 
123      * @param registry the new {@link Registry}.
124      */
125     protected void setRegistry(final Registry<K, U> registry) {
126         this.lock.readLock().lock();
127         try {
128             final Registry<K, U> oldRegistry = this.registryReference.getAndSet(registry);
129             if (null != oldRegistry) {
130                 unregisterAll(oldRegistry);
131             }
132             if (null != registry) {
133                 registerAll(registry);
134             }
135         } finally {
136             this.lock.readLock().unlock();
137         }
138     }
139 
140     private void registerAll(final Registry<K, U> registry) {
141         this.lock.readLock().lock();
142         try {
143             for (final V value : this.registrations) {
144                 try {
145                     registry.register(value);
146                     update(RegistrationState.Registered, value);
147                 } catch (final ContextException ex) {
148                     if (LOGGER.isEnabledFor(Level.WARN)) {
149                         LOGGER.warn(MessageFormat.format(
150                                 "Failed to register {0}! Ignoring.",
151                                 getKey(value)), ex);
152                     }
153                 }
154             }
155         } finally {
156             this.lock.readLock().unlock();
157         }
158     }
159 
160     private void unregisterAll(final Registry<K, U> registry) {
161         this.lock.readLock().lock();
162         try {
163             for (final V value : this.registrations) {
164                 final K key = getKey(value);
165                 try {
166                     registry.unregister(key);
167                     update(RegistrationState.Unregistered, value);
168                 } catch (final ContextException ex) {
169                     if (LOGGER.isEnabledFor(Level.WARN)) {
170                         LOGGER.warn(MessageFormat.format(
171                                 "Failed to unregister {0}! Ignoring.", key), ex);
172                     }
173                 }
174             }
175         } finally {
176             this.lock.readLock().unlock();
177         }
178     }
179 }