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.HashMap;
22  import java.util.Map;
23  import java.util.concurrent.atomic.AtomicBoolean;
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.event.ContextEvent;
30  import at.ac.tuwien.infosys.sm4all.copal.api.event.ContextEventType;
31  import at.ac.tuwien.infosys.sm4all.copal.api.listener.ContextListener;
32  import at.ac.tuwien.infosys.sm4all.copal.api.osgi.GenericActivator;
33  import at.ac.tuwien.infosys.sm4all.copal.api.query.ProcessedEventQuery;
34  import at.ac.tuwien.infosys.sm4all.copal.api.service.ContextEventTypeRegistry;
35  import at.ac.tuwien.infosys.sm4all.copal.api.service.ContextQueryFactory;
36  import at.ac.tuwien.infosys.sm4all.copal.api.service.ContextState;
37  import at.ac.tuwien.infosys.sm4all.copal.api.util.NotRegisteredException;
38  import at.ac.tuwien.infosys.sm4all.copal.api.util.RegistryObservable.RegistrationState;
39  import at.ac.tuwien.infosys.sm4all.copal.api.util.RegistryObservable.RegistryObserver;
40  
41  /**
42   * Implementation of the {@link ContextState} interface.
43   * 
44   * @author sanjin
45   */
46  public class ContextStateImpl extends GenericActivator implements ContextState {
47  
48      private static final String NAME = "ContextState";
49      private static final ContextEvent[] EMPTY_EVENTS = new ContextEvent[0];
50      private static final Logger LOGGER = Logger.getLogger(ContextStateImpl.class);
51  
52      private final AtomicBoolean started = new AtomicBoolean(false);
53      private final Map<String, ContextEvent> events = new HashMap<String, ContextEvent>();
54      private final ReadWriteLock lock = new ReentrantReadWriteLock();
55      private final EventTypesObserver observer = new EventTypesObserver();
56      private ContextEventTypeRegistry eventTypeRegistry;
57      private ContextQueryFactory queryFactory;
58  
59      /**
60       * Creates an instance of the {@link ContextState} implementation.
61       */
62      public ContextStateImpl() {
63          super(ContextEventTypeRegistry.class.getName(),
64                  ContextQueryFactory.class.getName());
65      }
66  
67      /**
68       * Returns if this {@link ContextState} has been started by an OSGi
69       * framework.
70       * 
71       * @return if this {@link ContextState} has been started by an OSGi
72       *         framework.
73       */
74      public boolean isStarted() {
75          return this.started.get();
76      }
77  
78      @Override
79      protected void start() {
80          if (!this.started.getAndSet(true)) {
81              this.eventTypeRegistry = getDependency(ContextEventTypeRegistry.class.getName());
82              this.queryFactory = getDependency(ContextQueryFactory.class.getName());
83  
84              this.eventTypeRegistry.attach(this.observer);
85  
86              if (register(ContextState.class, this)) {
87                  if (LOGGER.isInfoEnabled()) {
88                      LOGGER.info("Successfully registered ContextState service.");
89                  }
90              } else {
91                  if (LOGGER.isEnabledFor(Level.ERROR)) {
92                      LOGGER.error("Failed to register ContextState service!");
93                  }
94              }
95          }
96      }
97  
98      @Override
99      protected void stop() {
100         if (this.started.getAndSet(false)) {
101             if (unregister(ContextState.class)) {
102                 if (LOGGER.isInfoEnabled()) {
103                     LOGGER.info("Successfully unregistered ContextState service.");
104                 }
105             }
106 
107             this.eventTypeRegistry.detach(this.observer);
108             this.lock.writeLock().lock();
109             try {
110                 this.events.clear();
111             } finally {
112                 this.lock.writeLock().unlock();
113             }
114 
115             this.eventTypeRegistry = null;
116             this.queryFactory = null;
117         }
118     }
119 
120     @Override
121     public ContextEvent[] getCurrentEvents() {
122         ContextEvent[] result = EMPTY_EVENTS;
123 
124         this.lock.readLock().lock();
125         try {
126             if (!this.events.isEmpty()) {
127                 result = this.events.values().toArray(
128                         new ContextEvent[this.events.size()]);
129             }
130         } finally {
131             this.lock.readLock().unlock();
132         }
133 
134         return result;
135     }
136 
137     @Override
138     public ContextEvent getCurrentEvent(final String eventName) {
139         ContextEvent result = null;
140 
141         this.lock.readLock().lock();
142         try {
143             if (this.events.containsKey(eventName)) {
144                 result = this.events.get(eventName);
145             }
146         } finally {
147             this.lock.readLock().unlock();
148         }
149 
150         return result;
151     }
152 
153     private class EventListener implements ContextListener {
154 
155         private final String eventName;
156 
157         private EventListener(final String eventName) {
158             super();
159             this.eventName = eventName;
160         }
161 
162         @Override
163         public String getName() {
164             return NAME;
165         }
166 
167         @SuppressWarnings("synthetic-access")
168         @Override
169         public void onEvent(final ContextEvent event) {
170             ContextStateImpl.this.lock.writeLock().lock();
171             try {
172                 ContextStateImpl.this.events.put(this.eventName, event);
173             } finally {
174                 ContextStateImpl.this.lock.writeLock().unlock();
175             }
176         }
177     }
178 
179     private class EventTypesObserver implements
180             RegistryObserver<ContextEventType> {
181 
182         private final Map<String, ProcessedEventQuery> queries = new HashMap<String, ProcessedEventQuery>();
183         private final ReadWriteLock queriesLock = new ReentrantReadWriteLock();
184 
185         protected EventTypesObserver() {
186             super();
187         }
188 
189         @Override
190         public void update(final RegistrationState state,
191                 final ContextEventType eventType) {
192             switch (state) {
193             case Registered:
194                 onRegister(eventType);
195                 break;
196             case Unregistered:
197                 onUnregister(eventType);
198                 break;
199             default:
200                 break;
201             }
202         }
203 
204         @SuppressWarnings("synthetic-access")
205         private void onRegister(final ContextEventType eventType) {
206             final String eventName = eventType.getName();
207 
208             try {
209                 final ProcessedEventQuery query = ContextStateImpl.this.queryFactory.create(
210                         eventName + ".All", eventName);
211                 query.register(new EventListener(eventName));
212 
213                 this.queriesLock.writeLock().lock();
214                 try {
215                     this.queries.put(eventName, query);
216                 } finally {
217                     this.queriesLock.writeLock().unlock();
218                 }
219             } catch (final ContextException ex) {
220                 if (LOGGER.isEnabledFor(Level.ERROR)) {
221                     LOGGER.error(
222                             MessageFormat.format(
223                                     "Could not register state listener for event ''{0}''!",
224                                     eventName), ex);
225                 }
226             }
227         }
228 
229         @SuppressWarnings("synthetic-access")
230         private void onUnregister(final ContextEventType eventType) {
231             final String eventName = eventType.getName();
232 
233             this.queriesLock.writeLock().lock();
234             try {
235                 final ProcessedEventQuery query = this.queries.remove(eventName);
236 
237                 if (null != query) {
238                     try {
239                         if (!query.isDestroyed()) {
240                             query.unregister(NAME);
241                         }
242                     } catch (final NotRegisteredException ex) {
243                         if (LOGGER.isEnabledFor(Level.WARN)) {
244                             LOGGER.warn(
245                                     MessageFormat.format(
246                                             "Could not unregister state listener from event ''{0}''! Ignoring.",
247                                             eventName), ex);
248                         }
249                     }
250                 }
251 
252                 ContextStateImpl.this.lock.writeLock().lock();
253                 try {
254                     ContextStateImpl.this.events.remove(eventType.getName());
255                 } finally {
256                     ContextStateImpl.this.lock.writeLock().unlock();
257                 }
258             } finally {
259                 this.queriesLock.writeLock().unlock();
260             }
261         }
262     }
263 }