View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.spi.config.definition;
17  
18  import java.io.BufferedInputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.URL;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.commons.io.IOUtils;
29  import org.apache.commons.lang.StringUtils;
30  import org.jdom.Document;
31  import org.jdom.Element;
32  import org.jdom.JDOMException;
33  import org.jdom.Namespace;
34  import org.jdom.input.SAXBuilder;
35  
36  import de.smartics.properties.api.config.domain.key.ApplicationId;
37  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
38  import de.smartics.properties.api.config.domain.key.EnvironmentId;
39  import de.smartics.properties.api.core.domain.ConfigException;
40  import de.smartics.properties.api.core.domain.PropertiesContext;
41  import de.smartics.properties.spi.core.util.ClassLoaderUtils;
42  import de.smartics.util.lang.ClassPathContext;
43  import de.smartics.util.lang.NullArgumentException;
44  
45  /**
46   * Parses the <code>definition.xml</code> file for properties.
47   */
48  public final class DefinitionConfigParser
49  {
50    // ********************************* Fields *********************************
51  
52    // --- constants ------------------------------------------------------------
53  
54    // --- members --------------------------------------------------------------
55  
56    /**
57     * The namespace of definition files parsed by this parser.
58     */
59    private static final Namespace NS = Namespace
60        .getNamespace("http://smartics.de/properties/definition/1");
61  
62    // ****************************** Initializer *******************************
63  
64    // ****************************** Constructors ******************************
65  
66    // ****************************** Inner Classes *****************************
67  
68    // ********************************* Methods ********************************
69  
70    // --- init -----------------------------------------------------------------
71  
72    // --- get&set --------------------------------------------------------------
73  
74    // --- business -------------------------------------------------------------
75  
76    /**
77     * Default constructor.
78     */
79    public DefinitionConfigParser()
80    {
81    }
82  
83    // --- object basics --------------------------------------------------------
84  
85    /**
86     * Convenience method using a class' class loader to locate the configuration
87     * file to parse the configuration file at
88     * {@link PropertiesContext#DEFINITION_FILE}.
89     *
90     * @param type the type whose class loader to use to locate the configuration
91     *          file.
92     * @return the read configuration, never <code>null</code>.
93     * @throws ConfigException on any problem loading the file.
94     */
95    public PropertiesDefinitionContext parse(final Class<?> type)
96      throws ConfigException
97    {
98      final ClassLoader loader = type.getClassLoader();
99      final String path = ClassLoaderUtils.calcArchivePath(type);
100     return parse(new ClassPathContext(loader, path));
101   }
102 
103   /**
104    * Parses the configuration file at {@link PropertiesContext#DEFINITION_FILE}.
105    *
106    * @param context the class loader with its context to use to locate the
107    *          configuration file.
108    * @return the read configuration, never <code>null</code>.
109    * @throws ConfigException on any problem loading the file.
110    */
111   public PropertiesDefinitionContext parse(final ClassPathContext context)
112     throws ConfigException
113   {
114     final URL url = context.getResource(PropertiesContext.DEFINITION_FILE);
115     if (url != null)
116     {
117       final String systemId = url.toExternalForm();
118       InputStream in = null;
119       try
120       {
121         in =
122             new BufferedInputStream(
123                 context.getResourceAsStream(PropertiesContext.DEFINITION_FILE));
124         return parse(systemId, in);
125       }
126       finally
127       {
128         IOUtils.closeQuietly(in);
129       }
130     }
131     throw new ConfigException(PropertiesContext.DEFINITION_FILE);
132   }
133 
134   /**
135    * Parses the <code>definition.xml</code> from the given stream.
136    *
137    * @param systemId the identifier of the resource on the stream.
138    * @param input the <code>definition.xml</code> on the stream.
139    * @return the parsed context.
140    * @throws ConfigException on any problem reading from the stream.
141    */
142   public PropertiesDefinitionContext parse(final String systemId,
143       final InputStream input) throws ConfigException
144   {
145     try
146     {
147       final SAXBuilder parser = new SAXBuilder();
148       final Document document = parser.build(input, systemId);
149 
150       final Element rootNode = document.getRootElement();
151       final Set<String> tlds = readSet(rootNode, "tlds", "tld", null);
152       final Set<String> environments =
153           readSet(rootNode, "environments", "environment");
154       final Set<String> nodes = readSet(rootNode, "nodes", "node");
155       final Set<String> groups = readSet(rootNode, "groups", "group");
156       final Map<String, ConfigurationKey> files = readFiles(rootNode);
157 
158       final PropertiesDefinitionContext context;
159       if (tlds == null)
160       {
161         context =
162             new PropertiesDefinitionContext(environments, nodes, groups, files);
163       }
164       else
165       {
166         context =
167             new PropertiesDefinitionContext(tlds, environments, nodes, groups,
168                 files);
169       }
170       return context;
171     }
172     catch (final JDOMException e)
173     {
174       throw new ConfigException(systemId, e);
175     }
176     catch (final IOException e)
177     {
178       throw new ConfigException(systemId, e);
179     }
180   }
181 
182   private Set<String> readSet(final Element rootNode, final String setGi,
183       final String elementGi)
184   {
185     return readSet(rootNode, setGi, elementGi, new HashSet<String>());
186   }
187 
188   @SuppressWarnings("unchecked")
189   private Set<String> readSet(final Element rootNode, final String setGi,
190       final String elementGi, final Set<String> defaultSet)
191   {
192     final Element set = rootNode.getChild(setGi, NS);
193     if (set != null)
194     {
195       final Set<String> collection = new HashSet<String>();
196       final List<Element> elements = set.getChildren(elementGi, NS);
197       for (final Element element : elements)
198       {
199         final String text = element.getTextNormalize();
200         collection.add(text);
201       }
202 
203       return collection;
204     }
205 
206     return defaultSet;
207   }
208 
209   @SuppressWarnings("unchecked")
210   private Map<String, ConfigurationKey> readFiles(final Element rootNode)
211   {
212     final Map<String, ConfigurationKey> keys =
213         new HashMap<String, ConfigurationKey>();
214 
215     final Element keySet = rootNode.getChild("key-set", NS);
216     if (keySet != null)
217     {
218       final List<Element> elements = keySet.getChildren("key", NS);
219       for (final Element element : elements)
220       {
221         final ConfigurationKey key = readKey(element);
222         final Element files = element.getChild("files", NS);
223         if (files != null)
224         {
225           final List<Element> fileElements = files.getChildren("file", NS);
226           for (final Element file : fileElements)
227           {
228             final String text = file.getTextNormalize();
229             keys.put(text, key);
230           }
231         }
232         else
233         {
234           keys.put(null, key);
235         }
236       }
237     }
238     return keys;
239   }
240 
241   private ConfigurationKey readKey(final Element element)
242     throws NullArgumentException
243   {
244     final EnvironmentId environmentId = readEnvironment(element);
245     final ApplicationId applicationId = readApplication(element);
246     final ConfigurationKey key =
247         new ConfigurationKey(environmentId, applicationId);
248     return key;
249   }
250 
251   private EnvironmentId readEnvironment(final Element element)
252   {
253     final Element environment = element.getChild("environment", NS);
254 
255     final EnvironmentId environmentId;
256     if (environment != null)
257     {
258       final String name = norm(environment.getChildTextNormalize("name", NS));
259       final String node = norm(environment.getChildTextNormalize("node", NS));
260 
261       environmentId = new EnvironmentId(name, node);
262     }
263     else
264     {
265       environmentId = EnvironmentId.ANY_ENV;
266     }
267     return environmentId;
268   }
269 
270   private ApplicationId readApplication(final Element element)
271   {
272     final Element application = element.getChild("application", NS);
273 
274     final ApplicationId applicationId;
275     if (application != null)
276     {
277       final String group = norm(application.getChildTextNormalize("group", NS));
278       final String artifact =
279           norm(application.getChildTextNormalize("artifact", NS));
280       final String version =
281           norm(application.getChildTextNormalize("version", NS));
282       applicationId = new ApplicationId(group, artifact, version);
283     }
284     else
285     {
286       applicationId = ApplicationId.ANY_APP;
287     }
288     return applicationId;
289   }
290 
291   private static String norm(final String text)
292   {
293     if (StringUtils.isBlank(text))
294     {
295       return null;
296     }
297     return text;
298   }
299 }