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