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.text.MessageFormat;
21  import java.util.concurrent.locks.ReadWriteLock;
22  import java.util.concurrent.locks.ReentrantReadWriteLock;
23  import org.apache.log4j.Logger;
24  import org.w3c.dom.Document;
25  import at.ac.tuwien.infosys.sm4all.copal.api.ContextException;
26  import at.ac.tuwien.infosys.sm4all.copal.api.ContextPublisher;
27  import at.ac.tuwien.infosys.sm4all.copal.api.MalformedDocumentException;
28  import at.ac.tuwien.infosys.sm4all.copal.api.event.ContextEventType;
29  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.XMLContextEvent;
30  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.XMLContextEventType;
31  import at.ac.tuwien.infosys.sm4all.copal.service.event.PublishingService;
32  
33  /**
34   * Helper class for implementing {@link ContextPublisher}s.
35   * 
36   * @author sanjin
37   */
38  public abstract class AbstractXMLPublisher implements ContextPublisher {
39  
40      private static final Logger LOGGER = Logger.getLogger(AbstractXMLPublisher.class);
41  
42      private final ReadWriteLock lock = new ReentrantReadWriteLock();
43      private final String sourceID;
44      private final String eventTypeName;
45      private PublishingService publishingService;
46      private XMLContextEventType eventType;
47      private boolean started = false;
48  
49      /**
50       * Use specified source ID and given event type as return values for
51       * {@link #getSourceID()} and {@link #getType()} methods respectively.
52       * 
53       * @param sourceID the source ID of context publisher.
54       * @param type the name of {@link ContextEventType} for published events.
55       */
56      protected AbstractXMLPublisher(final String sourceID, final String type) {
57          this.sourceID = sourceID;
58          this.eventTypeName = type;
59      }
60  
61      /**
62       * This method is called when {@link PublishingService} becomes available
63       * for publishing events of the specified {@link XMLContextEventType}. This
64       * method should be implemented by specific publishers and should be used as
65       * a notification that publisher can, from now on, publish events.
66       * 
67       * @param type the {@link XMLContextEventType} of published events.
68       */
69      protected abstract boolean start(XMLContextEventType type);
70  
71      /**
72       * This method is called when {@link PublishingService} becomes unavailable
73       * for publishing events of the specified {@link XMLContextEventType}. This
74       * method should be implemented by specific publishers and should be used as
75       * a notification that publisher should not publish any more events.
76       * 
77       * @param type the {@link XMLContextEventType} of published events.
78       */
79      protected abstract void stop(XMLContextEventType type);
80  
81      @Override
82      public final String getSourceID() {
83          return this.sourceID;
84      }
85  
86      @Override
87      public final String getEventType() {
88          return this.eventTypeName;
89      }
90  
91      /**
92       * Returns if this {@link AbstractXMLPublisher} is started and can start
93       * publish events.
94       * 
95       * @return if this {@link AbstractXMLPublisher} is started and can start
96       *         publish events.
97       */
98      public boolean isStarted() {
99          return this.started;
100     }
101 
102     @Override
103     public boolean start(final PublishingService publisher,
104             final XMLContextEventType type) {
105         if (LOGGER.isDebugEnabled())
106             LOGGER.debug(MessageFormat.format("Starting publisher ''{0}''.",
107                     this.sourceID));
108         boolean result = false;
109 
110         this.lock.writeLock().lock();
111         try {
112             if (!this.started) {
113                 this.publishingService = publisher;
114                 this.eventType = type;
115 
116                 if (start(this.eventType)) {
117                     this.started = true;
118                     result = true;
119 
120                     if (LOGGER.isInfoEnabled())
121                         LOGGER.info(MessageFormat.format(
122                                 "Successfully started publisher ''{0}''!",
123                                 this.sourceID));
124                 } else {
125                     this.publishingService = null;
126                     this.eventType = null;
127 
128                     LOGGER.error(MessageFormat.format(
129                             "Publisher ''{0}'' could not be started!",
130                             this.sourceID));
131                 }
132             }
133         } finally {
134             this.lock.writeLock().unlock();
135         }
136 
137         return result;
138     }
139 
140     @Override
141     public final void stop(final PublishingService publisher,
142             final XMLContextEventType type) {
143         if (LOGGER.isDebugEnabled())
144             LOGGER.debug(MessageFormat.format("Stopping publisher ''{0}''.",
145                     this.sourceID));
146 
147         this.lock.writeLock().lock();
148         try {
149             if (this.started) {
150                 if ((this.publishingService.equals(publisher))
151                         && (this.eventType.equals(type))) {
152                     stop(type);
153                     this.eventType = null;
154                     this.publishingService = null;
155                     this.started = false;
156 
157                     if (LOGGER.isInfoEnabled())
158                         LOGGER.info(MessageFormat.format(
159                                 "Successfully stopped publisher ''{0}''!",
160                                 this.sourceID));
161                 } else
162                     LOGGER.warn(MessageFormat.format(
163                             "Publisher ''{0}'' was not started for specified publishing service and event type! Ignoring",
164                             this.sourceID));
165             } else
166                 LOGGER.warn(MessageFormat.format(
167                         "Publisher ''{0}'' was never started! Ignoring",
168                         this.sourceID));
169         } finally {
170             this.lock.writeLock().unlock();
171         }
172     }
173 
174     /**
175      * Publish specified {@link Document} as an event.
176      * 
177      * @param document the {@link Document} to be published.
178      * @return <code>true</code>> if publishing was successful;
179      *         <code>false</code> otherwise.
180      * @throws MalformedDocumentException if specified {@link Document} is
181      *         malformed for published {@link ContextEventType}.
182      */
183     protected final boolean publish(final Document document)
184             throws MalformedDocumentException {
185         return publish(new XMLContextEvent(this.eventType, this.sourceID,
186                 document));
187     }
188 
189     /**
190      * Publish given {@link XMLContextEvent}.
191      * 
192      * @param event the {@link XMLContextEvent} to be published.
193      * @return <code>true</code>> if publishing was successful;
194      *         <code>false</code> otherwise.
195      */
196     protected final boolean publish(final XMLContextEvent event) {
197         final boolean result = false;
198 
199         PublishingService currentPublishingService = null;
200         this.lock.readLock().lock();
201         try {
202             if (this.started) {
203                 if (this.eventType.equals(event.getType()))
204                     currentPublishingService = this.publishingService;
205                 else
206                     LOGGER.error(MessageFormat.format(
207                             "Failed to publish event from ''{0}''! Publisher not register to publish ''{1}'' events.",
208                             this.sourceID, event.getType().getName()));
209             } else
210                 LOGGER.error(MessageFormat.format(
211                         "Failed to publish event from ''{0}''! Publisher not registered.",
212                         this.sourceID));
213         } finally {
214             this.lock.readLock().unlock();
215         }
216 
217         if (currentPublishingService != null)
218             try {
219                 currentPublishingService.publish(event);
220                 if (LOGGER.isInfoEnabled())
221                     LOGGER.info(MessageFormat.format(
222                             "Successfully publish event from ''{0}''!",
223                             this.sourceID));
224             } catch (final ContextException ex) {
225                 LOGGER.error(
226                         MessageFormat.format(
227                                 "Failed to publish event from ''{0}''!",
228                                 this.sourceID), ex);
229             }
230 
231         return result;
232     }
233 }