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.transfer;
17  
18  import java.io.BufferedInputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.MalformedURLException;
22  import java.net.URL;
23  import java.net.URLClassLoader;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Properties;
30  import java.util.Set;
31  import java.util.logging.Logger;
32  
33  import org.apache.commons.io.IOUtils;
34  
35  import de.smartics.exceptions.runtime.ExceptionContextManager;
36  import de.smartics.properties.api.config.domain.PropertyLocation;
37  import de.smartics.properties.api.config.domain.PropertyProvider;
38  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
39  import de.smartics.properties.api.core.domain.PropertiesContext;
40  import de.smartics.properties.resource.domain.ArtifactRef;
41  import de.smartics.properties.resource.domain.ClassPathEnvironment;
42  import de.smartics.properties.spi.config.definition.DefinitionConfigParser;
43  import de.smartics.properties.spi.config.definition.PropertiesDefinitionContext;
44  import de.smartics.properties.spi.config.support.PropertiesHelper;
45  import de.smartics.properties.spi.config.support.PropertiesPropertyProvider;
46  import de.smartics.properties.spi.core.classpath.PropertiesFilesLoader;
47  import de.smartics.util.lang.Arg;
48  
49  /**
50   * Collects property providers to make them accessible by the transfer process
51   * as a source of property definitions.
52   */
53  public final class PropertyProviderList implements Iterable<PropertyProvider>
54  {
55    // ********************************* Fields *********************************
56  
57    // --- constants ------------------------------------------------------------
58  
59    // --- members --------------------------------------------------------------
60  
61    /**
62     * Reference to the logger for this instance.
63     */
64    private final Logger log = Logger.getLogger(ExceptionContextManager.class
65        .getName());
66  
67    /**
68     * The list of property definitions.
69     */
70    private final List<PropertyProvider> propertyProviders =
71        new ArrayList<PropertyProvider>(64);
72  
73    // ****************************** Initializer *******************************
74  
75    // ****************************** Constructors ******************************
76  
77    /**
78     * Default constructor.
79     */
80    public PropertyProviderList()
81    {
82    }
83  
84    // ****************************** Inner Classes *****************************
85  
86    // ********************************* Methods ********************************
87  
88    // --- init -----------------------------------------------------------------
89  
90    // --- get&set --------------------------------------------------------------
91  
92    // --- business -------------------------------------------------------------
93  
94    /**
95     * Adds a property provider.
96     *
97     * @param provider the provider to add.
98     * @throws NullPointerException if {@code provider} is <code>null</code>.
99     */
100   public void addPropertyProvider(final PropertyProvider provider)
101     throws NullPointerException
102   {
103     Arg.checkNotNull("provider", provider);
104     propertyProviders.add(provider);
105   }
106 
107   /**
108    * Resolves the environment to property providers and adds them.
109    *
110    * @param definitionConfigParser the parser to read
111    *          <code>definition.xml</code> files to determine the configuration
112    *          keys for properties files.
113    * @param env the environment containing properties files. Each of them will
114    *          be turned into a provider and added to the list.
115    * @throws NullPointerException if {@code env} is <code>null</code>.
116    * @throws TransferException if any of the artifacts provided in the
117    *           environment {@code env} is not valid. An artifact is not valid,
118    *           if the configuration key for a properties file cannot be
119    *           determined. Also thrown if a properties file cannot be mapped to
120    *           a configuration key.
121    */
122   public void addPropertyDefinitions(
123       final DefinitionConfigParser<?> definitionConfigParser,
124       final ClassPathEnvironment env) throws NullPointerException,
125     TransferException
126   {
127     Arg.checkNotNull("env", env);
128 
129     final List<ArtifactRef> artifactRefs = env.getArtifactRefs();
130     if (artifactRefs.isEmpty())
131     {
132       return;
133     }
134 
135     final PropertiesFilesLoader loader = new PropertiesFilesLoader();
136     for (final ArtifactRef artifactRef : artifactRefs)
137     {
138       final PropertiesDefinitionContext context =
139           readDefinition(definitionConfigParser, artifactRef);
140       if (context == null)
141       {
142         continue;
143       }
144 
145       final URL rootUrl = artifactRef.getUrl();
146       final Collection<URL> rootUrls = Collections.singleton(rootUrl);
147       final Set<String> propertiesFiles = loader.getPropertiesFiles(rootUrls);
148 
149       final ClassLoader classLoader =
150           new URLClassLoader(rootUrls.toArray(new URL[rootUrls.size()]), Thread
151               .currentThread().getContextClassLoader());
152       for (final String propertiesFile : propertiesFiles)
153       {
154         if (propertiesFile.contains("META-INF"))
155         {
156           continue;
157         }
158 
159         final ConfigurationKey<?> key = getKey(context, propertiesFile);
160         final PropertiesHelper helper = new PropertiesHelper(key);
161         // FIXME: Instead of loading all properties into memory we should read
162         // them on demand. This could be accomplished by adding a
163         // PropertyProvider that reads the properties only on demand and is able
164         // to remove them as soon as all are read.
165         final Properties properties =
166             helper.readProperties(classLoader, propertiesFile);
167         final PropertyLocation sourceId = new PropertyLocation(propertiesFile);
168         final PropertyProvider provider =
169             new PropertiesPropertyProvider(key, sourceId, properties);
170         propertyProviders.add(provider);
171       }
172     }
173   }
174 
175   private ConfigurationKey<?> getKey(final PropertiesDefinitionContext context,
176       final String propertiesFile) throws TransferException
177   {
178     final ConfigurationKey<?> key = context.getKey(propertiesFile);
179     if (key == null)
180     {
181       throw new TransferException(
182           new NoConfigurationKeyForPropertiesMessageBean(propertiesFile));
183     }
184     return key;
185   }
186 
187   private PropertiesDefinitionContext readDefinition(
188       final DefinitionConfigParser<?> definitionConfigParser,
189       final ArtifactRef artifactRef) throws IllegalArgumentException
190   {
191     InputStream input = null;
192     try
193     {
194       final URL url = calcDefinitionXmlUrl(artifactRef);
195       final String systemId =
196           artifactRef.getId().toString() + ':' + url.toExternalForm();
197 
198       input = new BufferedInputStream(url.openStream());
199       final PropertiesDefinitionContext context =
200           definitionConfigParser.parse(systemId, input);
201       return context;
202     }
203     catch (final MalformedURLException e)
204     {
205       final TransferMessageBean message =
206           DefinitionXmlMessageBean.malformedUrl(e, artifactRef);
207       log.fine(message.toString());
208       throw new TransferException(message);
209     }
210     catch (final IOException e)
211     {
212       final TransferMessageBean message =
213           DefinitionXmlMessageBean.missingDefinition(e, artifactRef);
214       log.fine(message.toString());
215       throw new TransferException(message);
216     }
217     finally
218     {
219       IOUtils.closeQuietly(input);
220     }
221   }
222 
223   private static URL calcDefinitionXmlUrl(final ArtifactRef artifactRef)
224     throws MalformedURLException
225   {
226     final URL url;
227     if (artifactRef.getUrl().toExternalForm().endsWith(".jar"))
228     {
229       url = calcDefinitionXmlUrlFromJar(artifactRef);
230     }
231     else
232     {
233       url = calcDefinitionXmlUrlForDir(artifactRef);
234     }
235     return url;
236   }
237 
238   private static URL calcDefinitionXmlUrlFromJar(final ArtifactRef artifactRef)
239     throws MalformedURLException
240   {
241     final String jarUrl = artifactRef.getUrl().toExternalForm();
242     final URL definitionXmlUrl =
243         new URL("jar:" + jarUrl + "!/" + PropertiesContext.DEFINITION_FILE);
244     return definitionXmlUrl;
245   }
246 
247   private static URL calcDefinitionXmlUrlForDir(final ArtifactRef artifactRef)
248     throws MalformedURLException
249   {
250     final String jarUrl = artifactRef.getUrl().toExternalForm();
251     final URL definitionXmlUrl =
252         new URL(jarUrl + PropertiesContext.DEFINITION_FILE);
253     return definitionXmlUrl;
254   }
255 
256   @Override
257   public Iterator<PropertyProvider> iterator()
258   {
259     return propertyProviders.iterator();
260   }
261 
262   // --- object basics --------------------------------------------------------
263 
264 }