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.concurrent.ThreadSafe;
22  
23  import de.smartics.properties.api.config.domain.ConfigurationLoadingException;
24  import de.smartics.properties.api.config.domain.ConfigurationNotFoundException;
25  import de.smartics.properties.api.config.domain.Property;
26  import de.smartics.properties.api.config.domain.PropertyCollection;
27  import de.smartics.properties.api.config.domain.SerializableConfigurationPropertiesManagement;
28  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
29  import de.smartics.properties.api.core.domain.PropertyDescriptorRegistry;
30  import de.smartics.properties.api.core.domain.PropertyValidationException;
31  import de.smartics.properties.api.core.domain.ReadOnlyPropertyException;
32  import de.smartics.util.lang.Arguments;
33  import de.smartics.util.lang.NullArgumentException;
34  
35  /**
36   * An implementation that stores all properties in-memory.
37   */
38  @ThreadSafe
39  public abstract class AbstractInMemoryConfigurationProperties extends
40      AbstractConfigurationPropertiesManagement implements
41      SerializableConfigurationPropertiesManagement
42  { // NOPMD
43    // ********************************* Fields *********************************
44  
45    // --- constants ------------------------------------------------------------
46  
47    // --- members --------------------------------------------------------------
48  
49    /**
50     * The class version identifier.
51     */
52    private static final long serialVersionUID = 1L;
53  
54    /**
55     * The management of the properties.
56     *
57     * @serial
58     */
59    private final InMemoryPropertiesManager properties;
60  
61    /**
62     * The flag controls the behavior on not finding default properties on the
63     * classpath. If the value is <code>true</code> not finding the properties
64     * file on the class path signals an exception, if set to <code>false</code>
65     * the properties are not required to exist. In the latter case the properties
66     * may be set purely programatically.
67     */
68    private final boolean requiresDefaultOnClassPath;
69  
70    // ****************************** Initializer *******************************
71  
72    // ****************************** Constructors ******************************
73  
74    /**
75     * Convenience constructor requiring that a properties file with the same name
76     * as the properties set class exists on the class path.
77     *
78     * @param key the key that identifies the configuration.
79     * @param registry the registry to resolve property descriptors.
80     * @throws NullArgumentException if {@code key} or {@code registry} is
81     *           <code>null</code>.
82     */
83    public AbstractInMemoryConfigurationProperties(final ConfigurationKey key,
84        final PropertyDescriptorRegistry registry) throws NullArgumentException
85    {
86      this(key, registry, true);
87    }
88  
89    /**
90     * Default constructor requiring that a properties file with the same name as
91     * the properties set class exists on the class path.
92     *
93     * @param key the key that identifies the configuration.
94     * @param registry the registry to resolve property descriptors.
95     * @param requiresDefaultOnClassPath the flag controls the behavior on not
96     *          finding default properties on the classpath.
97     * @throws NullArgumentException if {@code key} or {@code registry} is
98     *           <code>null</code>.
99     */
100   public AbstractInMemoryConfigurationProperties(final ConfigurationKey key,
101       final PropertyDescriptorRegistry registry,
102       final boolean requiresDefaultOnClassPath) throws NullArgumentException
103   {
104     super(key, registry);
105 
106     properties = new InMemoryPropertiesManager(key);
107     this.requiresDefaultOnClassPath = requiresDefaultOnClassPath;
108   }
109 
110   // ****************************** Inner Classes *****************************
111 
112   // ********************************* Methods ********************************
113 
114   // --- init -----------------------------------------------------------------
115 
116   // --- get&set --------------------------------------------------------------
117 
118   // --- business -------------------------------------------------------------
119 
120   @Override
121   protected final Property getPropertyFromStore(final String name)
122   {
123     try
124     {
125       return properties.getProperty(name);
126     }
127     catch (final IllegalArgumentException e)
128     {
129       return new Property(properties.getSourceId(), name, null);
130     }
131   }
132 
133   @Override
134   public final Property setPropertyToStore(final String key, final String value)
135     throws PropertyValidationException, ReadOnlyPropertyException
136   {
137     final Property property = ensureProperty(key, value);
138     final Property oldValue = properties.setProperty(property);
139     return oldValue;
140   }
141 
142   @Override
143   protected final Property deletePropertyInStore(final String name)
144   {
145     return properties.removeProperty(name);
146   }
147 
148   @Override
149   protected final PropertyCollection getPropertyCollectionFromStore()
150   {
151     return properties.getProperties();
152   }
153 
154   /**
155    * Loads the properties for the given properties interface from the class
156    * path.
157    * <p>
158    * The properties file is expected to be at the same location with a name
159    * identical to the interface plus the file extension <code>.properties</code>
160    * .
161    * </p>
162    *
163    * @param propertiesInterface the properties defining interface whose
164    *          properties are loaded.
165    * @return a reference to this configuration for chaining.
166    * @throws NullArgumentException if {@code propertiesInterface} is
167    *           <code>null</code>.
168    * @throws ConfigurationLoadingException if the properties file cannot be
169    *           loaded.
170    */
171   public final AbstractInMemoryConfigurationProperties addClassPathProperties(
172       final Class<?> propertiesInterface) throws NullArgumentException,
173     ConfigurationLoadingException
174   {
175     Arguments.checkNotNull("propertiesInterface", propertiesInterface);
176 
177     final Properties properties = loadDefault(propertiesInterface);
178     addDescriptors(propertiesInterface);
179     addDefinitions(properties);
180     return this;
181   }
182 
183   private Properties loadDefault(final Class<?> descriptorDefinition)
184     throws ConfigurationLoadingException
185   {
186     final String name = descriptorDefinition.getSimpleName() + ".properties";
187 
188     final InputStream in = descriptorDefinition.getResourceAsStream(name);
189     if (in != null)
190     {
191       final PropertiesHelper utils = new PropertiesHelper(getKey());
192       return utils.readProperties(name, in);
193     }
194 
195     if (requiresDefaultOnClassPath)
196     {
197       throw new ConfigurationNotFoundException(getKey(), name);
198     }
199     else
200     {
201       return new Properties();
202     }
203   }
204 
205   private Property ensureProperty(final String name, final Object value)
206   {
207     final Property property;
208     if (value instanceof Property)
209     {
210       property = (Property) value;
211     }
212     else
213     {
214       property = new Property(properties.getSourceId(), name, value);
215     }
216     return property;
217   }
218 
219   // --- object basics --------------------------------------------------------
220 
221   /**
222    * Returns the string representation of the object.
223    *
224    * @return the string representation of the object.
225    */
226   @Override
227   public String toString()
228   {
229     return super.toString() + "Properties:\n" + properties.toString();
230   }
231 }