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.beans.PropertyChangeListener;
19  import java.beans.PropertyChangeSupport;
20  import java.io.IOException;
21  import java.io.ObjectInputStream;
22  
23  import javax.annotation.concurrent.ThreadSafe;
24  
25  import de.smartics.properties.api.config.domain.ConfigurationProperties;
26  import de.smartics.properties.api.config.domain.ConfigurationValidationException;
27  import de.smartics.properties.api.config.domain.Property;
28  import de.smartics.properties.api.config.domain.ResolvedProperty;
29  import de.smartics.properties.api.config.domain.SerializableConfigurationProperties;
30  import de.smartics.properties.api.config.domain.UnknownPropertyException;
31  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
32  import de.smartics.properties.api.core.domain.PropertiesContext;
33  import de.smartics.properties.api.core.domain.PropertyContext;
34  import de.smartics.properties.api.core.domain.PropertyDescriptor;
35  import de.smartics.properties.api.core.domain.PropertyDescriptorRegistry;
36  import de.smartics.properties.api.core.domain.PropertyExpression;
37  import de.smartics.properties.api.core.domain.PropertyKey;
38  import de.smartics.properties.api.core.domain.PropertyValidationException;
39  import de.smartics.properties.api.core.domain.PropertyValueConversionException;
40  import de.smartics.properties.api.core.domain.PropertyValueResolveException;
41  import de.smartics.properties.spi.config.proxy.PropertyConfigurationObjectBuilder;
42  import de.smartics.properties.spi.config.resolve.PropertyValueResolver;
43  import de.smartics.properties.spi.config.resolve.ResolveConfigurationException;
44  import de.smartics.properties.spi.config.resolve.SimplePropertyValueResolver;
45  import de.smartics.properties.spi.core.context.MandatoryPropertyContext;
46  import de.smartics.properties.spi.core.convert.BeanUtilsPropertyValueConverter;
47  import de.smartics.properties.spi.core.convert.PropertyValueConverter;
48  import de.smartics.properties.spi.core.validate.PropertyValidator;
49  import de.smartics.util.lang.Arguments;
50  import de.smartics.util.lang.BlankArgumentException;
51  import de.smartics.util.lang.NullArgumentException;
52  
53  /**
54   * Abstract implementation of the
55   * {@link de.smartics.properties.api.config.domain.SerializableConfigurationProperties}
56   * interface.
57   */
58  @ThreadSafe
59  public abstract class AbstractConfigurationProperties implements
60      ConfigurationProperties, ConfigurationPropertiesSpi
61  { // NOPMD
62    // ********************************* Fields *********************************
63  
64    // --- constants ------------------------------------------------------------
65  
66    // --- members --------------------------------------------------------------
67  
68    /**
69     * The key that identifies the configuration.
70     *
71     * @serial
72     */
73    private final ConfigurationKey key;
74  
75    /**
76     * The registry to resolve property descriptors.
77     *
78     * @serial
79     */
80    private final PropertyDescriptorRegistry registry;
81  
82    /**
83     * The validator to use on values before they are returned.
84     *
85     * @serial
86     */
87    private final PropertyValidator validator = new PropertyValidator();
88  
89    /**
90     * The property value converter to and from Strings.
91     *
92     * @serial
93     */
94    private final PropertyValueConverter converter =
95        new BeanUtilsPropertyValueConverter();
96  
97    /**
98     * The resolver for property value expressions.
99     */
100   private transient PropertyValueResolver resolver;
101 
102   /**
103    * Helper to handle property change listeners.
104    *
105    * @serial
106    */
107   private final PropertyChangeSupport support = new PropertyChangeSupport(this);
108 
109   // ****************************** Initializer *******************************
110 
111   // ****************************** Constructor ******************************
112 
113   /**
114    * Constructor for serializable subclasses.
115    */
116   protected AbstractConfigurationProperties()
117   {
118     this.key = null;
119     this.registry = null;
120     this.resolver = null;
121   }
122 
123   /**
124    * Default constructor.
125    *
126    * @param key the key that identifies the configuration.
127    * @param registry the registry to resolve property descriptors.
128    * @throws NullArgumentException if {@code key} or {@code registry} is
129    *           <code>null</code>.
130    */
131   protected AbstractConfigurationProperties(final ConfigurationKey key,
132       final PropertyDescriptorRegistry registry) throws NullArgumentException
133   {
134     Arguments.checkNotNull("key", key);
135     Arguments.checkNotNull("registry", registry);
136 
137     this.key = key;
138     this.registry = registry;
139     this.resolver = createResolver();
140   }
141 
142   // ****************************** Inner Classes *****************************
143 
144   // ********************************* Methods ********************************
145 
146   // --- init -----------------------------------------------------------------
147 
148   private SimplePropertyValueResolver createResolver()
149   {
150     return new SimplePropertyValueResolver(registry, this);
151   }
152 
153   // --- get&set --------------------------------------------------------------
154 
155   @Override
156   public final ConfigurationKey getKey()
157   {
158     return key;
159   }
160 
161   @Override
162   public final PropertyContext getContext(final PropertyDescriptor descriptor)
163     throws NullPointerException
164   {
165     Arguments.checkNotNull("descriptor", descriptor);
166     return new MandatoryPropertyContext(registry.getContext(descriptor),
167         descriptor);
168   }
169 
170   @Override
171   public final PropertiesContext getContext(final Class<?> declaringType)
172     throws NullPointerException
173   {
174     Arguments.checkNotNull("declaringType", declaringType);
175     return registry.getContext(declaringType);
176   }
177 
178   /**
179    * Returns the registry to resolve property descriptors.
180    *
181    * @return the registry to resolve property descriptors.
182    */
183   public final PropertyDescriptorRegistry getRegistry()
184   {
185     return registry;
186   }
187 
188   // --- business -------------------------------------------------------------
189 
190   @Override
191   public final Property getProperty(final String key)
192     throws IllegalArgumentException, UnknownPropertyException
193   {
194     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
195     return getProperty(descriptor);
196   }
197 
198   @Override
199   public Property getProperty(final String key, final Object defaultValue)
200     throws IllegalArgumentException, UnknownPropertyException
201   {
202     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
203     return getProperty(descriptor, defaultValue);
204   }
205 
206   @Override
207   public final Property getProperty(final PropertyKey key)
208     throws IllegalArgumentException, UnknownPropertyException
209   {
210     final PropertyDescriptor descriptor = getPropertyDescriptor(key.toString());
211     return getProperty(descriptor);
212   }
213 
214   @Override
215   public final Property getProperty(final PropertyDescriptor descriptor)
216     throws IllegalArgumentException, UnknownPropertyException
217   {
218     return getProperty(descriptor, null);
219   }
220 
221   @Override
222   public final <T> T getProperties(final Class<T> propertiesInterface)
223   {
224     return getProperties(propertiesInterface, toSerializable());
225   }
226 
227   @Override
228   public final <T> T getProperties(final Class<T> propertiesInterface,
229       final SerializableConfigurationProperties configuration)
230   {
231     final PropertyConfigurationObjectBuilder builder =
232         new PropertyConfigurationObjectBuilder();
233     return builder.build(propertiesInterface, configuration);
234   }
235 
236   @Override
237   public final Object getPropertyValue(final String key)
238     throws IllegalArgumentException, UnknownPropertyException,
239     PropertyValidationException
240   {
241     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
242     return getPropertyValue(descriptor, null);
243   }
244 
245   @Override
246   public final Object getPropertyValue(final PropertyKey key)
247     throws IllegalArgumentException, UnknownPropertyException,
248     PropertyValidationException
249   {
250     final PropertyDescriptor descriptor = getPropertyDescriptor(key.toString());
251     return getPropertyValue(descriptor, null);
252   }
253 
254   @Override
255   public final Object getPropertyValue(final String key,
256       final Object defaultValue) throws NullArgumentException,
257     PropertyValidationException
258   {
259     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
260     return getPropertyValue(descriptor, defaultValue);
261   }
262 
263   @Override
264   public final String getPropertyValueAsString(final String key)
265     throws NullArgumentException, PropertyValidationException
266   {
267     return getPropertyValue(key).toString();
268   }
269 
270   @Override
271   public final Object getPropertyValue(final PropertyDescriptor descriptor,
272       final Object defaultValue) throws NullPointerException,
273     PropertyValueConversionException, PropertyValidationException,
274     UnknownPropertyException
275   {
276     return getResolvedProperty(descriptor, defaultValue).getResolvedValue();
277   }
278 
279   @Override
280   public final String getPropertyValueAsString(final String key,
281       final Object defaultValue) throws NullArgumentException,
282     PropertyValidationException
283   {
284     return getPropertyValue(key, defaultValue).toString();
285   }
286 
287   @Override
288   public final Object getPropertyValue(final PropertyDescriptor descriptor)
289     throws NullArgumentException, PropertyValidationException
290   {
291     return getPropertyValue(descriptor, null);
292   }
293 
294   @Override
295   public final String getPropertyValueAsString(
296       final PropertyDescriptor descriptor) throws NullArgumentException,
297     PropertyValidationException
298   {
299     return getPropertyValue(descriptor, null).toString();
300   }
301 
302   @Override
303   public final String getPropertyValueAsString(
304       final PropertyDescriptor descriptor, final Object defaultValue)
305     throws NullArgumentException, PropertyValidationException
306   {
307     return getPropertyValue(descriptor, defaultValue).toString();
308   }
309 
310   @Override
311   public final ResolvedProperty getResolvedProperty(final PropertyKey key,
312       final Object defaultValue) throws IllegalArgumentException,
313     UnknownPropertyException, PropertyValidationException
314   {
315     final PropertyDescriptor descriptor = getPropertyDescriptor(key.toString());
316     return getResolvedProperty(descriptor, defaultValue);
317   }
318 
319   @Override
320   public final ResolvedProperty getResolvedProperty(final String key,
321       final Object defaultValue) throws IllegalArgumentException,
322     UnknownPropertyException, PropertyValidationException
323   {
324     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
325     return getResolvedProperty(descriptor, defaultValue);
326   }
327 
328   /**
329    * Returns the property descriptor for a given key.
330    *
331    * @param key the key for which the property descriptor is needed.
332    * @return the property descriptor for the given key.
333    * @throws BlankArgumentException when the key is blank.
334    * @throws PropertyValidationException when the configuration is invalid.
335    */
336   protected final PropertyDescriptor getPropertyDescriptor(final String key)
337     throws BlankArgumentException, PropertyValidationException
338   {
339     Arguments.checkNotBlank("key", key);
340 
341     final PropertyDescriptor descriptor = registry.get(key);
342     if (descriptor == null)
343     {
344       throw new UnknownPropertyException(getKey(), key);
345     }
346     return descriptor;
347   }
348 
349   /**
350    * Resolves and converts the given value.
351    *
352    * @param descriptor the property descriptor.
353    * @param defaultValue the default value to use if {@code value} is
354    *          <code>null</code> and there is no default expression.
355    * @param value the value to resolve and convert.
356    * @return the resolved and converted value.
357    * @throws PropertyValueConversionException if the value cannot be converted
358    *           from {@link String} to the target type.
359    * @throws PropertyValidationException if at least on constraint is not met.
360    */
361   protected final Object resolveAndConvert(final PropertyDescriptor descriptor,
362       final Object defaultValue, final Object value)
363     throws PropertyValueConversionException, PropertyValidationException
364   {
365     Object currentValue = value;
366     if (currentValue == null)
367     {
368       currentValue = descriptor.getDefaultExpression();
369       if (currentValue == null)
370       {
371         currentValue = defaultValue;
372       }
373     }
374 
375     if (currentValue instanceof PropertyExpression)
376     {
377       final PropertyExpression expression = (PropertyExpression) currentValue;
378       currentValue = expression.getExpression();
379     }
380 
381     if (currentValue instanceof String)
382     {
383       currentValue = resolveValue(descriptor, currentValue);
384     }
385 
386     final Object convertedValue = convert(descriptor, currentValue);
387     validator.validate(descriptor, convertedValue);
388 
389     return convertedValue;
390   }
391 
392   private Object resolveValue(final PropertyDescriptor descriptor,
393       final Object value)
394   {
395     try
396     {
397       final Object resolvedValue = resolver.resolve((String) value);
398       return resolvedValue;
399     }
400     catch (final ResolveConfigurationException e)
401     {
402       throw new PropertyValueResolveException(e, descriptor, e.getExpression());
403     }
404   }
405 
406   private Object convert(final PropertyDescriptor descriptor,
407       final Object currentValue)
408   {
409     if (currentValue == null)
410     {
411       return null;
412     }
413 
414     return converter.convert(descriptor, currentValue);
415   }
416 
417   @Override
418   public final void validate() throws ConfigurationValidationException
419   {
420     validate(false);
421   }
422 
423   // ... change listener support ..............................................
424 
425   @Override
426   public final void addPropertyChangeListener(final PropertyKey name,
427       final PropertyChangeListener listener) throws NullPointerException
428   {
429     support.addPropertyChangeListener(name.toString(), listener);
430   }
431 
432   @Override
433   public final void removePropertyChangeListener(final PropertyKey name,
434       final PropertyChangeListener listener) throws NullPointerException
435   {
436     support.removePropertyChangeListener(name.toString(), listener);
437   }
438 
439   @Override
440   public final void addPropertyChangeListener(
441       final PropertyChangeListener listener) throws NullPointerException
442   {
443     support.addPropertyChangeListener(listener);
444   }
445 
446   @Override
447   public final void removePropertyChangeListener(
448       final PropertyChangeListener listener) throws NullPointerException
449   {
450     support.removePropertyChangeListener(listener);
451   }
452 
453   /**
454    * Fires the property change event.
455    *
456    * @param name the name of the property.
457    * @param oldValue the old value of the property that has been changed.
458    * @param newValue the new and current value of the property.
459    */
460   protected final void firePropertyChange(final String name,
461       final String oldValue, final String newValue)
462   {
463     support.firePropertyChange(name, oldValue, newValue);
464   }
465 
466   // --- object basics --------------------------------------------------------
467 
468   /**
469    * Reads the object from the given stream.
470    *
471    * @param in the stream to read from.
472    * @throws IOException on read problems.
473    * @throws ClassNotFoundException if a class cannot be found.
474    */
475   private void readObject(final ObjectInputStream in) throws IOException,
476     ClassNotFoundException
477   {
478     in.defaultReadObject();
479 
480     this.resolver = createResolver();
481   }
482 
483   /**
484    * Returns the string representation of the object.
485    *
486    * @return the string representation of the object.
487    */
488   @Override
489   public String toString()
490   {
491     final StringBuilder buffer = new StringBuilder();
492 
493     buffer.append(key).append(":\n").append("Registry:\n").append(registry);
494 
495     return buffer.toString();
496   }
497 }