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.core.internal;
19  
20  import java.text.MessageFormat;
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.concurrent.atomic.AtomicBoolean;
27  import java.util.concurrent.atomic.AtomicReference;
28  import java.util.concurrent.locks.ReadWriteLock;
29  import java.util.concurrent.locks.ReentrantReadWriteLock;
30  import org.apache.log4j.Level;
31  import org.apache.log4j.Logger;
32  import org.osgi.framework.BundleActivator;
33  import org.osgi.framework.BundleContext;
34  import org.osgi.framework.ServiceRegistration;
35  import at.ac.tuwien.infosys.sm4all.copal.api.query.ActionQuery;
36  import at.ac.tuwien.infosys.sm4all.copal.api.query.ContextQuery;
37  import at.ac.tuwien.infosys.sm4all.copal.api.query.ProcessedEventQuery;
38  import at.ac.tuwien.infosys.sm4all.copal.api.query.QueryDestroyedException;
39  import at.ac.tuwien.infosys.sm4all.copal.api.query.QueryObserver;
40  import at.ac.tuwien.infosys.sm4all.copal.api.query.QueryState;
41  import at.ac.tuwien.infosys.sm4all.copal.api.service.ContextQueryFactory;
42  import at.ac.tuwien.infosys.sm4all.copal.api.service.RedefinitionOfQueryException;
43  import at.ac.tuwien.infosys.sm4all.copal.api.util.BaseObservable;
44  
45  /**
46   * Implementation of the {@link ContextQueryFactory} interface.
47   * 
48   * @author sanjin
49   */
50  public class ContextQueryFactoryImpl extends
51          BaseObservable<QueryState, ContextQuery, QueryObserver> implements
52          ContextQueryFactory, BundleActivator {
53  
54      private static final Logger LOGGER = Logger.getLogger(ContextQueryFactoryImpl.class);
55  
56      private final Map<String, ContextQuery> queries = new HashMap<String, ContextQuery>();
57      private final ReadWriteLock lock = new ReentrantReadWriteLock();
58      private final AtomicBoolean started = new AtomicBoolean(false);
59      private final AtomicReference<BundleContext> bundleContextRef = new AtomicReference<BundleContext>();
60      private ServiceRegistration registration;
61  
62      /**
63       * Returns if this {@link ContextQueryFactory} has been started by an OSGi
64       * framework.
65       * 
66       * @return if this {@link ContextQueryFactory} has been started by an OSGi
67       *         framework.
68       */
69      public boolean isStarted() {
70          return this.started.get();
71      }
72  
73      @Override
74      public void start(final BundleContext bundleContext) {
75          if (!this.started.getAndSet(true)) {
76              this.bundleContextRef.set(bundleContext);
77              super.attach(new OnDestroyRemoveQuery());
78  
79              this.registration = bundleContext.registerService(
80                      ContextQueryFactory.class.getName(), this, null);
81              if (null == this.registration) {
82                  if (LOGGER.isEnabledFor(Level.ERROR)) {
83                      LOGGER.error("Failed to register service ContextQueryFactory!");
84                  }
85              } else {
86                  if (LOGGER.isInfoEnabled()) {
87                      LOGGER.info("Successfully registered ContextQueryFactory service.");
88                  }
89              }
90          }
91      }
92  
93      @Override
94      public void stop(final BundleContext bundleContext) {
95          if (this.started.getAndSet(false)) {
96              this.registration.unregister();
97              this.registration = null;
98  
99              destroyAll();
100             detachAll();
101 
102             this.bundleContextRef.set(null);
103 
104             if (LOGGER.isInfoEnabled()) {
105                 LOGGER.info("Successfully unregistered ContextQueryFactory service.");
106             }
107         }
108     }
109 
110     @Override
111     public void attach(final QueryObserver observer) {
112         this.lock.readLock().lock();
113         try {
114             if (this.started.get()) {
115                 super.attach(observer);
116                 for (final ContextQuery query : this.queries.values()) {
117                     query.attach(observer);
118                 }
119             }
120         } finally {
121             this.lock.readLock().unlock();
122         }
123     }
124 
125     @Override
126     public void detach(final QueryObserver observer) {
127         this.lock.readLock().lock();
128         try {
129             if (this.started.get()) {
130                 for (final ContextQuery query : this.queries.values()) {
131                     query.detach(observer);
132                 }
133                 super.detach(observer);
134             }
135         } finally {
136             this.lock.readLock().unlock();
137         }
138     }
139 
140     @Override
141     public ActionQuery createActionQuery(final String listenedType,
142             final String actionName) {
143         ActionQuery result = new ActionQuery(listenedType, actionName);
144         final String name = result.getName();
145 
146         this.lock.writeLock().lock();
147         try {
148             if (this.queries.containsKey(name)) {
149                 result = (ActionQuery) this.queries.get(name);
150             } else {
151                 this.queries.put(name, result);
152                 for (final QueryObserver observer : getObservers()) {
153                     result.attach(observer);
154                 }
155                 if (this.started.get()) {
156                     result.start(this.bundleContextRef.get());
157                 }
158             }
159         } finally {
160             this.lock.writeLock().unlock();
161         }
162 
163         return result;
164     }
165 
166     @Override
167     public ProcessedEventQuery create(final String name,
168             final String listenedType) throws RedefinitionOfQueryException {
169         final ProcessedEventQuery result;
170 
171         this.lock.writeLock().lock();
172         try {
173             if (this.queries.containsKey(name)) {
174                 result = (ProcessedEventQuery) this.queries.get(name);
175 
176                 if (!result.equals(new ProcessedEventQuery(name, listenedType))) {
177                     throw new RedefinitionOfQueryException(result);
178                 }
179             } else {
180                 result = new ProcessedEventQuery(name, listenedType);
181                 this.queries.put(name, result);
182                 for (final QueryObserver observer : getObservers()) {
183                     result.attach(observer);
184                 }
185                 if (this.started.get()) {
186                     result.start(this.bundleContextRef.get());
187                 }
188             }
189         } finally {
190             this.lock.writeLock().unlock();
191         }
192 
193         return result;
194     }
195 
196     @Override
197     public ProcessedEventQuery create(final String name,
198             final String listenedType, final String criteria)
199             throws RedefinitionOfQueryException {
200         final ProcessedEventQuery result;
201 
202         this.lock.writeLock().lock();
203         try {
204             if (this.queries.containsKey(name)) {
205                 result = (ProcessedEventQuery) this.queries.get(name);
206 
207                 if (!result.equals(new ProcessedEventQuery(name, listenedType,
208                         criteria))) {
209                     throw new RedefinitionOfQueryException(result);
210                 }
211             } else {
212                 result = new ProcessedEventQuery(name, listenedType, criteria);
213                 this.queries.put(name, result);
214                 for (final QueryObserver observer : getObservers()) {
215                     result.attach(observer);
216                 }
217                 if (this.started.get()) {
218                     result.start(this.bundleContextRef.get());
219                 }
220             }
221         } finally {
222             this.lock.writeLock().unlock();
223         }
224 
225         return result;
226     }
227 
228     @Override
229     public ContextQuery[] getQueries() {
230         final ContextQuery[] result;
231 
232         this.lock.readLock().lock();
233         try {
234             final Collection<ContextQuery> queryCollection = this.queries.values();
235             result = queryCollection.toArray(new ContextQuery[queryCollection.size()]);
236         } finally {
237             this.lock.readLock().unlock();
238         }
239 
240         return result;
241     }
242 
243     @Override
244     public ContextQuery getQuery(final String name) {
245         ContextQuery result = null;
246 
247         this.lock.readLock().lock();
248         try {
249             if (this.queries.containsKey(name)) {
250                 result = this.queries.get(name);
251             }
252         } finally {
253             this.lock.readLock().unlock();
254         }
255 
256         return result;
257     }
258 
259     @Override
260     public ContextQuery[] getQueries(final String eventType) {
261         final List<ContextQuery> result = new LinkedList<ContextQuery>();
262 
263         this.lock.readLock().lock();
264         try {
265             for (final ContextQuery query : this.queries.values()) {
266                 if (eventType.equals(query.getListenedType())) {
267                     result.add(query);
268                 }
269             }
270         } finally {
271             this.lock.readLock().unlock();
272         }
273 
274         return result.toArray(new ContextQuery[result.size()]);
275     }
276 
277     /**
278      * Remove query with specified name.
279      * 
280      * @param queryName the name of the query.
281      */
282     protected void remove(final String queryName) {
283         if (LOGGER.isDebugEnabled()) {
284             LOGGER.debug(MessageFormat.format("Removing ''{0}'' query.",
285                     queryName));
286         }
287 
288         this.lock.writeLock().lock();
289         try {
290             if (this.queries.containsKey(queryName)) {
291                 final ContextQuery query = this.queries.remove(queryName);
292                 query.stop(this.bundleContextRef.get());
293             }
294         } finally {
295             this.lock.writeLock().unlock();
296         }
297 
298         if (LOGGER.isInfoEnabled()) {
299             LOGGER.info(MessageFormat.format("''{0}'' query removed!",
300                     queryName));
301         }
302     }
303 
304     private void destroyAll() {
305         if (LOGGER.isDebugEnabled()) {
306             LOGGER.debug("Destroying all context queries.");
307         }
308 
309         this.lock.writeLock().lock();
310         try {
311             for (final ContextQuery query : this.queries.values()) {
312                 try {
313                     query.destroy();
314                 } catch (final QueryDestroyedException ex) {
315                     if (LOGGER.isEnabledFor(Level.WARN)) {
316                         LOGGER.warn(
317                                 MessageFormat.format(
318                                         "Query ''{0}'' is unexpectadly already destroyed! Removing it.",
319                                         query.getName()), ex);
320                     }
321                 }
322             }
323 
324             this.queries.clear();
325         } finally {
326             this.lock.writeLock().unlock();
327         }
328 
329         if (LOGGER.isInfoEnabled()) {
330             LOGGER.info("All context queries destroyed!");
331         }
332     }
333 
334     private class OnDestroyRemoveQuery implements QueryObserver {
335 
336         protected OnDestroyRemoveQuery() {
337             super();
338         }
339 
340         @Override
341         public void update(final QueryState state, final ContextQuery query) {
342             switch (state) {
343             case Destroyed:
344                 remove(query.getName());
345                 break;
346             default:
347                 break;
348             }
349         }
350     }
351 }