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.context;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.net.URL;
23  import java.text.MessageFormat;
24  import java.util.Arrays;
25  import java.util.LinkedList;
26  import java.util.List;
27  import javax.xml.XMLConstants;
28  import javax.xml.parsers.DocumentBuilderFactory;
29  import javax.xml.parsers.ParserConfigurationException;
30  import javax.xml.validation.Schema;
31  import javax.xml.validation.SchemaFactory;
32  import org.apache.log4j.Logger;
33  import org.w3c.dom.Document;
34  import org.w3c.dom.Element;
35  import org.xml.sax.SAXException;
36  import at.ac.tuwien.infosys.sm4all.copal.api.event.ContextEventType;
37  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.XMLContextEventType;
38  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.type.XMLContextEventTypeUnmarshaller;
39  import at.ac.tuwien.infosys.sm4all.copal.api.query.ContextQuery;
40  import at.ac.tuwien.infosys.sm4all.copal.api.query.ProcessedEventQuery;
41  import at.ac.tuwien.infosys.sm4all.copal.api.query.xml.ProcessedEventQueryUnmarshaller;
42  import at.ac.tuwien.infosys.sm4all.copal.api.service.ContextEventTypeRegistry;
43  import at.ac.tuwien.infosys.sm4all.copal.api.service.ContextQueryFactory;
44  import at.ac.tuwien.infosys.sm4all.copal.api.util.AlreadyRegisteredException;
45  import at.ac.tuwien.infosys.sm4all.copal.api.util.FailedUnmarshallingException;
46  import at.ac.tuwien.infosys.sm4all.copal.api.xml.Append;
47  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ListUnmarshaller;
48  import at.ac.tuwien.infosys.sm4all.copal.api.xml.StringAttribute;
49  import static at.ac.tuwien.infosys.sm4all.copal.api.query.xml.ProcessedEventQueryUnmarshaller.QUERY_ELEMENT;
50  
51  /**
52   * This class is used to read definitions of {@link ContextEventType}s and
53   * {@link ContextQuery}s from a XML configuration file that can be found in
54   * classpath.
55   * 
56   * @author sanjin
57   */
58  public class Context {
59  
60      private static final String[] EMPTY_STRING_ARRAY = new String[0];
61      private static final XMLContextEventType[] EMPTY_EVENT_TYPE_ARRAY = new XMLContextEventType[0];
62      private static final Logger LOGGER = Logger.getLogger(Context.class);
63      private static final Schema CONTEXT_SCHEMA;
64  
65      static {
66          Schema schema = null;
67          try {
68              final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
69              final URL configurationSchemaURL = Context.class.getClassLoader().getResource(
70                      "Configuration.xsd");
71              if (configurationSchemaURL == null)
72                  LOGGER.warn("Could not find Configuration.xsd! Ignoring.");
73              else
74                  schema = schemaFactory.newSchema(configurationSchemaURL);
75          } catch (final SAXException ex) {
76              LOGGER.warn("Could not parse Configuration.xsd! Ignoring.", ex);
77          }
78          CONTEXT_SCHEMA = schema;
79      }
80  
81      private final ClassLoader classLoader;
82      private final Document document;
83      private final XMLContextEventType[] eventTypes;
84      private final String[] definedQueries;
85  
86      /**
87       * Use specified <code>ClassLoader</code> to find the XML configuration file
88       * with specified name.
89       * 
90       * @param classLoader the class loader.
91       * @param fileName the name of the configuration file.
92       */
93      public Context(final ClassLoader classLoader, final String fileName) {
94          super();
95  
96          this.classLoader = classLoader;
97          this.document = getDocument(fileName, CONTEXT_SCHEMA);
98          if (this.document == null) {
99              this.eventTypes = EMPTY_EVENT_TYPE_ARRAY;
100             this.definedQueries = EMPTY_STRING_ARRAY;
101         } else {
102             final Element context = this.document.getDocumentElement();
103             List<XMLContextEventType> list = new LinkedList<XMLContextEventType>();
104             try {
105                 list = XMLContextEventTypeUnmarshaller.getListBuilder(
106                         classLoader).withElement(context).build().unmarshal();
107             } catch (final FailedUnmarshallingException ex) {
108                 LOGGER.error("Could not unmarshal event definitions!", ex);
109             }
110             this.eventTypes = list.toArray(new XMLContextEventType[list.size()]);
111 
112             List<String> queryNames = new LinkedList<String>();
113             try {
114                 queryNames = new ListUnmarshaller<String>(
115                         context,
116                         QUERY_ELEMENT,
117                         new StringAttribute.Builder().withName(ProcessedEventQueryUnmarshaller.NAME_ATTRIBUTE),
118                         new Append()).unmarshal();
119             } catch (final FailedUnmarshallingException ex) {
120                 LOGGER.error("Could not unmarshal query definitions!", ex);
121             }
122             this.definedQueries = queryNames.toArray(new String[queryNames.size()]);
123         }
124     }
125 
126     /**
127      * Returns the defined {@link XMLContextEventType}s in the XML configuration
128      * file or an empty array if there was problem reading the file.
129      * 
130      * @return the defined {@link XMLContextEventType}s in the XML configuration
131      *         file or an empty array if there was problem reading the file.
132      */
133     public XMLContextEventType[] getDefinedEventTypes() {
134         return Arrays.copyOf(this.eventTypes, this.eventTypes.length);
135     }
136 
137     /**
138      * Returns the defined {@link ContextQuery}s in the XML configuration file
139      * or empty array if there was problem reading the file.
140      * 
141      * @return the defined {@link ContextQuery}s in the XML configuration file
142      *         or empty array if there was problem reading the file.
143      */
144     public String[] getDefinedQueries() {
145         return this.definedQueries;
146     }
147 
148     /**
149      * Registers all defined {@link XMLContextEventType}s with specified
150      * {@link ContextEventTypeRegistry} and return all
151      * {@link XMLContextEventType}s that were successfully registered.
152      * 
153      * @param eventTypeRegistry the event type registry with which to register
154      *        defined {@link XMLContextEventType}s.
155      * @return successfully registered {@link XMLContextEventType}s.
156      */
157     public XMLContextEventType[] registerEventTypes(
158             final ContextEventTypeRegistry eventTypeRegistry) {
159         final List<XMLContextEventType> result = new LinkedList<XMLContextEventType>();
160 
161         for (final XMLContextEventType eventType : this.eventTypes)
162             try {
163                 eventTypeRegistry.register(eventType);
164                 result.add(eventType);
165             } catch (final AlreadyRegisteredException ex) {
166                 LOGGER.warn(MessageFormat.format(
167                         "Could not register event type ''{0}''! Ignoring.",
168                         eventType.getName()), ex);
169             }
170 
171         return result.toArray(new XMLContextEventType[result.size()]);
172     }
173 
174     /**
175      * Creates all defined {@link ProcessedEventQuery}s with specified
176      * {@link ContextQueryFactory}.
177      * 
178      * @param factory the {@link ContextQueryFactory} with which to create
179      *        defined {@link ProcessedEventQuery}s.
180      * @return created {@link ProcessedEventQuery}s.
181      */
182     public ProcessedEventQuery[] createQueries(final ContextQueryFactory factory) {
183         List<ProcessedEventQuery> result = new LinkedList<ProcessedEventQuery>();
184 
185         if (this.document != null)
186             try {
187                 result = ProcessedEventQueryUnmarshaller.getListBuilder(factory).withElement(
188                         this.document.getDocumentElement()).build().unmarshal();
189             } catch (final FailedUnmarshallingException ex) {
190                 LOGGER.error("Could not create queries!", ex);
191             }
192 
193         return result.toArray(new ProcessedEventQuery[result.size()]);
194     }
195 
196     private Document getDocument(final String fileName, final Schema schema) {
197         Document result = null;
198 
199         final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
200         builderFactory.setNamespaceAware(true);
201         if (schema != null)
202             builderFactory.setSchema(schema);
203         final URL url = this.classLoader.getResource(fileName);
204         if (url == null)
205             LOGGER.warn(MessageFormat.format("Could not find {0}! Ignoring.",
206                     fileName));
207         else {
208             InputStream in = null;
209             try {
210                 in = url.openStream();
211                 result = builderFactory.newDocumentBuilder().parse(in);
212             } catch (final IOException ex) {
213                 LOGGER.warn("Could not read context.cfg.xml! Ignoring.", ex);
214             } catch (final SAXException ex) {
215                 LOGGER.warn("Could not parse context.cfg.xml! Ignoring.", ex);
216             } catch (final ParserConfigurationException ex) {
217                 LOGGER.warn("Could not find context.cfg.xml! Ignoring.", ex);
218             } finally {
219                 if (in != null)
220                     try {
221                         in.close();
222                     } catch (final IOException ex) {
223                         LOGGER.warn(
224                                 "Could not close context.cfg.xml! Ignoring.",
225                                 ex);
226                     }
227             }
228         }
229 
230         return result;
231     }
232 }