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.support;
17  
18  import java.io.InputStream;
19  import java.util.Properties;
20  
21  import javax.annotation.CheckForNull;
22  import javax.annotation.concurrent.ThreadSafe;
23  
24  import de.smartics.properties.api.config.domain.ConfigurationLoadingException;
25  import de.smartics.properties.api.config.domain.ConfigurationNotFoundException;
26  import de.smartics.properties.api.config.domain.Property;
27  import de.smartics.properties.api.config.domain.PropertyCollection;
28  import de.smartics.properties.api.config.domain.PropertyLocation;
29  import de.smartics.properties.api.config.domain.PropertyProvider;
30  import de.smartics.properties.api.config.domain.PropertyStoreAccessor;
31  import de.smartics.properties.api.config.domain.SerializableConfigurationPropertiesManagement;
32  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
33  import de.smartics.properties.api.core.domain.PropertyDescriptorRegistry;
34  import de.smartics.properties.api.core.domain.PropertyValidationException;
35  import de.smartics.properties.api.core.domain.ReadOnlyPropertyException;
36  import de.smartics.properties.api.core.security.PropertyValueSecurity;
37  import de.smartics.util.lang.Arg;
38  import de.smartics.util.lang.NullArgumentException;
39  
40  /**
41   * An implementation that stores all properties in-memory.
42   */
43  @ThreadSafe
44  public abstract class AbstractInMemoryConfigurationProperties extends
45      AbstractConfigurationPropertiesManagement implements
46      SerializableConfigurationPropertiesManagement
47  { // NOPMD
48    // ********************************* Fields *********************************
49  
50    // --- constants ------------------------------------------------------------
51  
52    // --- members --------------------------------------------------------------
53  
54    /**
55     * The class version identifier.
56     * <p>
57     * The value of this constant is {@value}.
58     */
59    private static final long serialVersionUID = 1L;
60  
61    /**
62     * The management of the properties.
63     *
64     * @serial
65     */
66    private final InMemoryPropertiesManager properties;
67  
68    /**
69     * The flag controls the behavior on not finding default properties on the
70     * classpath. If the value is <code>true</code> not finding the properties
71     * file on the class path signals an exception, if set to <code>false</code>
72     * the properties are not required to exist. In the latter case the properties
73     * may be set purely programatically.
74     *
75     * @serial
76     */
77    private final boolean requiresDefaultOnClassPath;
78  
79    /**
80     * The helper to access properties in a property store directly.
81     *
82     * @serial
83     */
84    private final PropertyStoreAccessor propertyStoreAccessor =
85        new InMemoryPropertyStoreAccessor();
86  
87    // ****************************** Initializer *******************************
88  
89    // ****************************** Constructors ******************************
90  
91    /**
92     * Convenience constructor requiring that a properties file with the same name
93     * as the properties set class exists on the class path.
94     *
95     * @param key the key that identifies the configuration.
96     * @param registry the registry to resolve property descriptors.
97     * @param decrypter the helper to decrypt secured property values.
98     * @throws NullArgumentException if {@code key}, {@code registry} or
99     *           {@code decrypter} is <code>null</code>.
100    */
101   public AbstractInMemoryConfigurationProperties(final ConfigurationKey<?> key,
102       final PropertyDescriptorRegistry registry,
103       final PropertyValueSecurity decrypter) throws NullArgumentException
104   {
105     this(key, registry, true, decrypter);
106   }
107 
108   /**
109    * Default constructor requiring that a properties file with the same name as
110    * the properties set class exists on the class path.
111    *
112    * @param key the key that identifies the configuration.
113    * @param registry the registry to resolve property descriptors.
114    * @param requiresDefaultOnClassPath the flag controls the behavior on not
115    *          finding default properties on the classpath.
116    * @param decrypter the helper to decrypt secured property values.
117    * @throws NullArgumentException if {@code key}, {@code registry} or
118    *           {@code decrypter} is <code>null</code>.
119    */
120   public AbstractInMemoryConfigurationProperties(final ConfigurationKey<?> key,
121       final PropertyDescriptorRegistry registry,
122       final boolean requiresDefaultOnClassPath,
123       final PropertyValueSecurity decrypter) throws NullArgumentException
124   {
125     super(key, registry, decrypter);
126 
127     properties = new InMemoryPropertiesManager(key);
128     this.requiresDefaultOnClassPath = requiresDefaultOnClassPath;
129   }
130 
131   // ****************************** Inner Classes *****************************
132 
133   /**
134    * Provides direct access to the properties in the property store.
135    */
136   private final class InMemoryPropertyStoreAccessor implements
137       PropertyStoreAccessor
138   {
139     /**
140      * The class version identifier.
141      * <p>
142      * The value of this constant is {@value}.
143      * </p>
144      */
145     private static final long serialVersionUID = 1L;
146 
147     @Override
148     public Property getPropertyFromStore(final String name)
149     {
150       try
151       {
152         return properties.getProperty(name);
153       }
154       catch (final IllegalArgumentException e)
155       {
156         return new Property(properties.getSourceId(), name, null);
157       }
158     }
159 
160     @Override
161     public Property setPropertyToStore(final String key, final String value)
162       throws PropertyValidationException, ReadOnlyPropertyException
163     {
164       final Property property = ensureProperty(key, value);
165       final Property oldValue = properties.setProperty(property);
166       return oldValue;
167     }
168 
169     @Override
170     public Property deletePropertyInStore(final String name)
171     {
172       return properties.removeProperty(name);
173     }
174 
175     @Override
176     public PropertyCollection getPropertyCollectionFromStore()
177     {
178       return properties.getProperties();
179     }
180   }
181 
182   // ********************************* Methods ********************************
183 
184   // --- init -----------------------------------------------------------------
185 
186   // --- get&set --------------------------------------------------------------
187 
188   @Override
189   public final PropertyStoreAccessor getPropertyStoreAccessor()
190   {
191     return propertyStoreAccessor;
192   }
193 
194   // --- business -------------------------------------------------------------
195 
196   @Override
197   protected final void addDefinitionsToStore(final PropertyProvider properties)
198   {
199     setPropertiesToStore(properties);
200   }
201 
202   /**
203    * Allows to store all properties efficiently by subclasses. The default
204    * implementation simply iterates.
205    *
206    * @param provider the provider with the properties to be stored.
207    */
208   protected final void setPropertiesToStore(final PropertyProvider provider)
209   {
210     for (final Property property : provider.getProperties())
211     {
212       setPropertyToStore(property.getName(), property.getValue());
213     }
214   }
215 
216   /**
217    * Loads the properties for the given properties interface from the class
218    * path.
219    * <p>
220    * The properties file is expected to be at the same location with a name
221    * identical to the interface plus the file extension <code>.properties</code>
222    * .
223    * </p>
224    *
225    * @param propertiesInterface the properties defining interface whose
226    *          properties are loaded.
227    * @return a reference to this configuration for chaining.
228    * @throws NullArgumentException if {@code propertiesInterface} is
229    *           <code>null</code>.
230    * @throws ConfigurationLoadingException if the properties file cannot be
231    *           loaded.
232    */
233   public final AbstractInMemoryConfigurationProperties addClassPathProperties(
234       final Class<?> propertiesInterface) throws NullArgumentException,
235     ConfigurationLoadingException
236   {
237     Arg.checkNotNull("propertiesInterface", propertiesInterface);
238 
239     final PropertyProvider properties = loadDefault(propertiesInterface);
240     addDescriptors(propertiesInterface);
241     if (properties != null)
242     {
243       addDefinitions(properties);
244     }
245     return this;
246   }
247 
248   @CheckForNull
249   private PropertyProvider loadDefault(final Class<?> descriptorDefinition)
250     throws ConfigurationLoadingException
251   {
252     final String name = descriptorDefinition.getSimpleName() + ".properties";
253 
254     final InputStream in = descriptorDefinition.getResourceAsStream(name);
255     if (in != null)
256     {
257       final PropertiesHelper utils = new PropertiesHelper(getKey());
258       final Properties properties = utils.readProperties(name, in);
259       final PropertyProvider provider =
260           new PropertiesPropertyProvider(getKey(), new PropertyLocation(name),
261               properties);
262       return provider;
263     }
264 
265     if (requiresDefaultOnClassPath)
266     {
267       throw new ConfigurationNotFoundException(getKey(), name);
268     }
269     else
270     {
271       return null;
272     }
273   }
274 
275   private Property ensureProperty(final String name, final Object value)
276   {
277     final Property property;
278     if (value instanceof Property)
279     {
280       property = (Property) value;
281     }
282     else
283     {
284       property = new Property(properties.getSourceId(), name, value);
285     }
286     return property;
287   }
288 
289   // --- object basics --------------------------------------------------------
290 
291   /**
292    * Returns the string representation of the object.
293    *
294    * @return the string representation of the object.
295    */
296   @Override
297   public String toString()
298   {
299     return super.toString() + "Properties:\n" + properties.toString();
300   }
301 }