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.helpers;
19  
20  import java.util.Arrays;
21  import java.util.Dictionary;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.concurrent.locks.ReadWriteLock;
27  import java.util.concurrent.locks.ReentrantReadWriteLock;
28  import org.osgi.framework.BundleActivator;
29  import org.osgi.framework.BundleContext;
30  import org.osgi.framework.ServiceReference;
31  import org.osgi.framework.ServiceRegistration;
32  import org.osgi.util.tracker.ServiceTracker;
33  
34  /**
35   * Abstract class to define OSGi {@link BundleActivator}s for services which
36   * depend on multiple other services to be registered and running in OSGi.
37   * 
38   * @author sanjin
39   */
40  public abstract class AbstractGenericActivator implements BundleActivator,
41          DependencyTrackerCustomizer {
42  
43      private final Set<String> dependencies = new HashSet<String>();
44      private final Map<String, ServiceTracker> trackers = new HashMap<String, ServiceTracker>();
45      private final Map<String, ServiceReference> references = new HashMap<String, ServiceReference>();
46      private final Map<String, Object> services = new HashMap<String, Object>();
47      private final ReadWriteLock servicesLock = new ReentrantReadWriteLock();
48      private final Map<String, ServiceRegistration> registrations = new HashMap<String, ServiceRegistration>();
49      private final ReadWriteLock registrationsLock = new ReentrantReadWriteLock();
50      private BundleContext context;
51      private boolean started = false;
52  
53      /**
54       * Creates instance of OSGi Activator for some service that depends on
55       * multiple other services to be registered and running with OSGi.
56       * 
57       * @param dependencies the names of dependent services.
58       */
59      public AbstractGenericActivator(final String... dependencies) {
60          this.dependencies.addAll(Arrays.asList(dependencies));
61      }
62  
63      /**
64       * Called when all dependencies are met.
65       */
66      protected abstract void start();
67  
68      /**
69       * Called when {@link #start()} was previously called and one of the
70       * dependencies has been unregistered.
71       */
72      protected abstract void stop();
73  
74      @Override
75      public final void start(final BundleContext bundleContext) {
76          this.context = bundleContext;
77  
78          if (this.dependencies.isEmpty()) {
79              if (!this.started) {
80                  start();
81                  this.started = true;
82              }
83          } else {
84              this.servicesLock.writeLock().lock();
85              try {
86                  ServiceTracker tracker;
87                  for (final String dependency : this.dependencies) {
88                      tracker = new ServiceTracker(bundleContext, dependency,
89                              new DependencyTracker(dependency, this));
90                      this.trackers.put(dependency, tracker);
91                      tracker.open();
92                  }
93              } finally {
94                  this.servicesLock.writeLock().unlock();
95              }
96          }
97      }
98  
99      @Override
100     public final void stop(final BundleContext bundleContext) {
101         if (this.started) {
102             stop();
103             this.started = false;
104         }
105 
106         unregisterAll();
107 
108         this.servicesLock.writeLock().lock();
109         try {
110             for (final ServiceTracker tracker : this.trackers.values())
111                 tracker.close();
112             this.trackers.clear();
113 
114             removeAllReferences();
115         } finally {
116             this.servicesLock.writeLock().unlock();
117         }
118 
119         this.context = null;
120     }
121 
122     @Override
123     public final Object addingService(final String dependencyName,
124             final ServiceReference reference) {
125         final Object service = this.context.getService(reference);
126 
127         final boolean shouldStart;
128         this.servicesLock.writeLock().lock();
129         try {
130             if (this.services.containsKey(dependencyName))
131                 removeReference(dependencyName);
132             this.references.put(dependencyName, reference);
133             this.services.put(dependencyName, service);
134 
135             shouldStart = this.services.keySet().containsAll(this.dependencies);
136         } finally {
137             this.servicesLock.writeLock().unlock();
138         }
139 
140         if ((!this.started) && (shouldStart)) {
141             start();
142             this.started = true;
143         }
144 
145         return service;
146     }
147 
148     @Override
149     public final void modifiedService(final String dependencyName,
150             final ServiceReference reference, final Object service) {
151         /* only properties of the service changed. do nothing */
152     }
153 
154     @Override
155     public final void removedService(final String dependencyName,
156             final ServiceReference reference, final Object service) {
157         removeReference(dependencyName);
158     }
159 
160     /**
161      * @param name the name of the dependency.
162      * @return instance object of dependency if it is registered;
163      *         <code>null</code> otherwise.
164      */
165     @SuppressWarnings("unchecked")
166     protected final <T> T getDependency(final String name) {
167         Object result = null;
168 
169         this.servicesLock.readLock().lock();
170         try {
171             if (this.services.containsKey(name))
172                 result = this.services.get(name);
173         } finally {
174             this.servicesLock.readLock().unlock();
175         }
176 
177         return (T) result;
178     }
179 
180     /**
181      * Register given service. If any other service has been previously
182      * registered using same <code>klass</code>, that service will be first
183      * unregistered and then will the given one be registered.
184      * 
185      * @param klass <code>Class</code> under which service will be registered
186      *        (using {@link Class#getName()} method to determine name).
187      * @param service instance of service.
188      * @return <code>true</code> if service has been successfully registered;
189      *         <code>false</code> otherwise.
190      */
191     protected final boolean register(final Class<?> klass, final Object service) {
192         return register(klass, service, null);
193     }
194 
195     /**
196      * Register given service. If any other service has been previously
197      * registered using same <code>klass</code>, that service will be first
198      * unregistered and then will the given one be registered.
199      * 
200      * @param klass <code>Class</code> under which service will be registered
201      *        (using {@link Class#getName()} method to determine name).
202      * @param service instance of service.
203      * @param properties the properties for this service.
204      * @return <code>true</code> if service has been successfully registered;
205      *         <code>false</code> otherwise.
206      */
207     protected final boolean register(final Class<?> klass,
208             final Object service, final Dictionary<?, ?> properties) {
209         final String serviceName = klass.getName();
210         boolean result = false;
211 
212         this.registrationsLock.writeLock().lock();
213         try {
214             if (this.registrations.containsKey(serviceName))
215                 unregister(klass);
216 
217             final ServiceRegistration registration = this.context.registerService(
218                     serviceName, service, properties);
219 
220             if (registration != null) {
221                 this.registrations.put(serviceName, registration);
222                 result = true;
223             }
224         } finally {
225             this.registrationsLock.writeLock().unlock();
226         }
227 
228         return result;
229     }
230 
231     /**
232      * Unregister given service registered using given <code>klass</code>.
233      * 
234      * @param klass <code>Class</code> under which service has been registered.
235      */
236     protected final void unregister(final Class<?> klass) {
237         unregister(klass.getName());
238     }
239 
240     private void removeReference(final String serviceName) {
241         if (this.started) {
242             stop();
243             this.started = false;
244         }
245 
246         this.servicesLock.writeLock().lock();
247         try {
248             final ServiceReference reference = this.references.remove(serviceName);
249             this.context.ungetService(reference);
250             this.services.remove(serviceName);
251         } finally {
252             this.servicesLock.writeLock().unlock();
253         }
254     }
255 
256     private void removeAllReferences() {
257         if (this.started) {
258             stop();
259             this.started = false;
260         }
261 
262         this.servicesLock.writeLock().lock();
263         try {
264             for (final String serviceName : this.services.keySet())
265                 removeReference(serviceName);
266         } finally {
267             this.servicesLock.writeLock().unlock();
268         }
269     }
270 
271     private void unregister(final String serviceName) {
272         this.registrationsLock.writeLock().lock();
273         try {
274             if (this.registrations.containsKey(serviceName))
275                 this.registrations.remove(serviceName).unregister();
276         } finally {
277             this.registrationsLock.writeLock().unlock();
278         }
279     }
280 
281     private void unregisterAll() {
282         this.registrationsLock.writeLock().lock();
283         try {
284             for (final String serviceName : this.registrations.keySet())
285                 unregister(serviceName);
286         } finally {
287             this.registrationsLock.writeLock().unlock();
288         }
289     }
290 }