Coverage Report - de.smartics.properties.spi.config.support.AbstractConfigurationProperties
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractConfigurationProperties
0%
0/109
0%
0/20
1.537
 
 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 org.apache.commons.lang.ObjectUtils;
 26  
 
 27  
 import de.smartics.properties.api.config.domain.ConfigurationProperties;
 28  
 import de.smartics.properties.api.config.domain.ConfigurationValidationException;
 29  
 import de.smartics.properties.api.config.domain.DescribedProperty;
 30  
 import de.smartics.properties.api.config.domain.SerializableConfigurationProperties;
 31  
 import de.smartics.properties.api.config.domain.UnknownPropertyException;
 32  
 import de.smartics.properties.api.config.domain.ValidatedProperty;
 33  
 import de.smartics.properties.api.config.domain.key.ConfigurationKey;
 34  
 import de.smartics.properties.api.core.domain.PropertiesContext;
 35  
 import de.smartics.properties.api.core.domain.PropertyContext;
 36  
 import de.smartics.properties.api.core.domain.PropertyDescriptor;
 37  
 import de.smartics.properties.api.core.domain.PropertyDescriptorRegistry;
 38  
 import de.smartics.properties.api.core.domain.PropertyExpression;
 39  
 import de.smartics.properties.api.core.domain.PropertyExpressionMessageBean;
 40  
 import de.smartics.properties.api.core.domain.PropertyKey;
 41  
 import de.smartics.properties.api.core.domain.PropertyValidationException;
 42  
 import de.smartics.properties.api.core.domain.PropertyValueConversionException;
 43  
 import de.smartics.properties.api.core.domain.PropertyValueResolveException;
 44  
 import de.smartics.properties.api.core.security.PropertyValueSecurity;
 45  
 import de.smartics.properties.api.core.security.SecurityException;
 46  
 import de.smartics.properties.spi.config.proxy.PropertyConfigurationObjectBuilder;
 47  
 import de.smartics.properties.spi.config.resolve.PropertyValueResolver;
 48  
 import de.smartics.properties.spi.config.resolve.ResolveConfigurationException;
 49  
 import de.smartics.properties.spi.config.resolve.SimplePropertyValueResolver;
 50  
 import de.smartics.properties.spi.core.context.MandatoryPropertyContext;
 51  
 import de.smartics.properties.spi.core.convert.BeanUtilsPropertyValueConverter;
 52  
 import de.smartics.properties.spi.core.convert.PropertyValueConverter;
 53  
 import de.smartics.properties.spi.core.validate.PropertyValidator;
 54  
 import de.smartics.util.lang.Arg;
 55  
 import de.smartics.util.lang.BlankArgumentException;
 56  
 import de.smartics.util.lang.NullArgumentException;
 57  
 
 58  
 /**
 59  
  * Abstract implementation of the {@link ConfigurationProperties} interface.
 60  
  */
 61  
 @ThreadSafe
 62  
 public abstract class AbstractConfigurationProperties implements
 63  
     ConfigurationProperties, ConfigurationPropertiesSpi
 64  
 { // NOPMD
 65  
   // ********************************* Fields *********************************
 66  
 
 67  
   // --- constants ------------------------------------------------------------
 68  
 
 69  
   // --- members --------------------------------------------------------------
 70  
 
 71  
   /**
 72  
    * The key that identifies the configuration.
 73  
    *
 74  
    * @serial
 75  
    */
 76  
   private final ConfigurationKey<?> key;
 77  
 
 78  
   /**
 79  
    * The registry to resolve property descriptors.
 80  
    *
 81  
    * @serial
 82  
    */
 83  
   private final PropertyDescriptorRegistry registry;
 84  
 
 85  
   /**
 86  
    * The helper to decrypt secured property values.
 87  
    *
 88  
    * @serial
 89  
    */
 90  
   private final PropertyValueSecurity decrypter;
 91  
 
 92  
   /**
 93  
    * The property value converter to and from Strings.
 94  
    *
 95  
    * @serial
 96  
    */
 97  0
   private final PropertyValueConverter converter =
 98  
       new BeanUtilsPropertyValueConverter();
 99  
 
 100  
   /**
 101  
    * The resolver to resolve property value expressions.
 102  
    */
 103  
   private transient PropertyValueResolver resolver;
 104  
 
 105  
   /**
 106  
    * Helper to handle property change listeners.
 107  
    *
 108  
    * @serial
 109  
    */
 110  0
   private final PropertyChangeSupport support = new PropertyChangeSupport(this);
 111  
 
 112  
   // ****************************** Initializer *******************************
 113  
 
 114  
   // ****************************** Constructor ******************************
 115  
 
 116  
   /**
 117  
    * Constructor for serializable subclasses.
 118  
    */
 119  
   protected AbstractConfigurationProperties()
 120  0
   {
 121  0
     this.key = null;
 122  0
     this.registry = null;
 123  0
     this.resolver = null;
 124  0
     this.decrypter = null;
 125  0
   }
 126  
 
 127  
   /**
 128  
    * Default constructor.
 129  
    *
 130  
    * @param key the key that identifies the configuration.
 131  
    * @param registry the registry to resolve property descriptors.
 132  
    * @param decrypter the helper to decrypt secured property values.
 133  
    * @throws NullArgumentException if {@code key}, {@code registry} or
 134  
    *           {@code decrypter} is <code>null</code>.
 135  
    */
 136  
   protected AbstractConfigurationProperties(final ConfigurationKey<?> key,
 137  
       final PropertyDescriptorRegistry registry,
 138  
       final PropertyValueSecurity decrypter) throws NullArgumentException
 139  0
   {
 140  0
     this.key = Arg.checkNotNull("key", key);
 141  0
     this.registry = Arg.checkNotNull("registry", registry);
 142  0
     this.decrypter = Arg.checkNotNull("decrypter", decrypter);
 143  0
     this.resolver = createResolver();
 144  0
   }
 145  
 
 146  
   // ****************************** Inner Classes *****************************
 147  
 
 148  
   // ********************************* Methods ********************************
 149  
 
 150  
   // --- init -----------------------------------------------------------------
 151  
 
 152  
   private SimplePropertyValueResolver createResolver()
 153  
   {
 154  0
     return new SimplePropertyValueResolver(registry, this);
 155  
   }
 156  
 
 157  
   // --- get&set --------------------------------------------------------------
 158  
 
 159  
   @Override
 160  
   public final ConfigurationKey<?> getKey()
 161  
   {
 162  0
     return key;
 163  
   }
 164  
 
 165  
   @Override
 166  
   public final PropertyContext getContext(final PropertyDescriptor descriptor)
 167  
     throws NullPointerException
 168  
   {
 169  0
     Arg.checkNotNull("descriptor", descriptor);
 170  0
     return new MandatoryPropertyContext(registry.getContext(descriptor),
 171  
         descriptor);
 172  
   }
 173  
 
 174  
   @Override
 175  
   public final PropertiesContext getContext(final Class<?> declaringType)
 176  
     throws NullPointerException
 177  
   {
 178  0
     Arg.checkNotNull("declaringType", declaringType);
 179  0
     return registry.getContext(declaringType);
 180  
   }
 181  
 
 182  
   /**
 183  
    * Returns the registry to resolve property descriptors.
 184  
    *
 185  
    * @return the registry to resolve property descriptors.
 186  
    */
 187  
   public final PropertyDescriptorRegistry getRegistry()
 188  
   {
 189  0
     return registry;
 190  
   }
 191  
 
 192  
   /**
 193  
    * Returns the property value security helper to en- and decrypt property
 194  
    * values.
 195  
    *
 196  
    * @return the property value security helper to en- and decrypt property
 197  
    *         values.
 198  
    */
 199  
   public final PropertyValueSecurity getPropertyValueSecurity()
 200  
   {
 201  0
     return decrypter;
 202  
   }
 203  
 
 204  
   // --- business -------------------------------------------------------------
 205  
 
 206  
   @Override
 207  
   public final DescribedProperty getProperty(final String key)
 208  
     throws IllegalArgumentException, UnknownPropertyException
 209  
   {
 210  0
     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
 211  0
     return getProperty(descriptor);
 212  
   }
 213  
 
 214  
   @Override
 215  
   public DescribedProperty getProperty(final String key,
 216  
       final Object defaultValue) throws IllegalArgumentException,
 217  
     UnknownPropertyException
 218  
   {
 219  0
     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
 220  0
     return getProperty(descriptor, defaultValue);
 221  
   }
 222  
 
 223  
   @Override
 224  
   public final DescribedProperty getProperty(final PropertyKey key)
 225  
     throws IllegalArgumentException, UnknownPropertyException
 226  
   {
 227  0
     final PropertyDescriptor descriptor = getPropertyDescriptor(key.toString());
 228  0
     return getProperty(descriptor);
 229  
   }
 230  
 
 231  
   @Override
 232  
   public final DescribedProperty getProperty(final PropertyDescriptor descriptor)
 233  
     throws IllegalArgumentException, UnknownPropertyException
 234  
   {
 235  0
     return getProperty(descriptor, null);
 236  
   }
 237  
 
 238  
   @Override
 239  
   public final <T> T getProperties(final Class<T> propertiesInterface)
 240  
   {
 241  0
     return getProperties(propertiesInterface, toSerializable());
 242  
   }
 243  
 
 244  
   @Override
 245  
   public final <T> T getProperties(final Class<T> propertiesInterface,
 246  
       final SerializableConfigurationProperties configuration)
 247  
   {
 248  0
     final PropertyConfigurationObjectBuilder builder =
 249  
         new PropertyConfigurationObjectBuilder();
 250  0
     return builder.build(propertiesInterface, configuration);
 251  
   }
 252  
 
 253  
   @Override
 254  
   public final Object getPropertyValue(final String key)
 255  
     throws IllegalArgumentException, UnknownPropertyException,
 256  
     PropertyValidationException
 257  
   {
 258  0
     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
 259  0
     return getPropertyValue(descriptor, null);
 260  
   }
 261  
 
 262  
   @Override
 263  
   public final Object getPropertyValue(final PropertyKey key)
 264  
     throws IllegalArgumentException, UnknownPropertyException,
 265  
     PropertyValidationException
 266  
   {
 267  0
     final PropertyDescriptor descriptor = getPropertyDescriptor(key.toString());
 268  0
     return getPropertyValue(descriptor, null);
 269  
   }
 270  
 
 271  
   @Override
 272  
   public final Object getPropertyValue(final String key,
 273  
       final Object defaultValue) throws NullArgumentException,
 274  
     PropertyValidationException
 275  
   {
 276  0
     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
 277  0
     return getPropertyValue(descriptor, defaultValue);
 278  
   }
 279  
 
 280  
   @Override
 281  
   public final String getPropertyValueAsString(final String key)
 282  
     throws NullArgumentException, PropertyValidationException
 283  
   {
 284  0
     return getPropertyValue(key).toString();
 285  
   }
 286  
 
 287  
   @Override
 288  
   public final Object getPropertyValue(final PropertyDescriptor descriptor,
 289  
       final Object defaultValue) throws NullPointerException,
 290  
     PropertyValueConversionException, PropertyValidationException,
 291  
     UnknownPropertyException
 292  
   {
 293  0
     return getValidatedProperty(descriptor, defaultValue).getValidatedValue();
 294  
   }
 295  
 
 296  
   @Override
 297  
   public final String getPropertyValueAsString(final String key,
 298  
       final Object defaultValue) throws NullArgumentException,
 299  
     PropertyValidationException
 300  
   {
 301  0
     return getPropertyValue(key, defaultValue).toString();
 302  
   }
 303  
 
 304  
   @Override
 305  
   public final Object getPropertyValue(final PropertyDescriptor descriptor)
 306  
     throws NullArgumentException, PropertyValidationException
 307  
   {
 308  0
     return getPropertyValue(descriptor, null);
 309  
   }
 310  
 
 311  
   @Override
 312  
   public final String getPropertyValueAsString(
 313  
       final PropertyDescriptor descriptor) throws NullArgumentException,
 314  
     PropertyValidationException
 315  
   {
 316  0
     return ObjectUtils.toString(getPropertyValue(descriptor, null), null);
 317  
   }
 318  
 
 319  
   @Override
 320  
   public final String getPropertyValueAsString(
 321  
       final PropertyDescriptor descriptor, final Object defaultValue)
 322  
     throws NullArgumentException, PropertyValidationException
 323  
   {
 324  0
     return ObjectUtils.toString(getPropertyValue(descriptor, defaultValue),
 325  
         null);
 326  
   }
 327  
 
 328  
   @Override
 329  
   public final ValidatedProperty getValidatedProperty(final PropertyKey key,
 330  
       final Object defaultValue) throws IllegalArgumentException,
 331  
     UnknownPropertyException, PropertyValidationException, SecurityException
 332  
   {
 333  0
     final PropertyDescriptor descriptor = getPropertyDescriptor(key.toString());
 334  0
     return getValidatedProperty(descriptor, defaultValue);
 335  
   }
 336  
 
 337  
   @Override
 338  
   public final ValidatedProperty getValidatedProperty(final String key,
 339  
       final Object defaultValue) throws IllegalArgumentException,
 340  
     UnknownPropertyException, PropertyValidationException, SecurityException
 341  
   {
 342  0
     final PropertyDescriptor descriptor = getPropertyDescriptor(key);
 343  0
     return getValidatedProperty(descriptor, defaultValue);
 344  
   }
 345  
 
 346  
   /**
 347  
    * Returns the property descriptor for a given key.
 348  
    *
 349  
    * @param key the key for which the property descriptor is needed.
 350  
    * @return the property descriptor for the given key.
 351  
    * @throws BlankArgumentException when the key is blank.
 352  
    * @throws PropertyValidationException when the configuration is invalid.
 353  
    */
 354  
   protected final PropertyDescriptor getPropertyDescriptor(final String key)
 355  
     throws BlankArgumentException, PropertyValidationException
 356  
   {
 357  0
     Arg.checkNotBlank("key", key);
 358  
 
 359  0
     final PropertyDescriptor descriptor = registry.get(key);
 360  0
     if (descriptor == null)
 361  
     {
 362  0
       throw new UnknownPropertyException(getKey(), key);
 363  
     }
 364  0
     return descriptor;
 365  
   }
 366  
 
 367  
   /**
 368  
    * Resolves, converts and validates the given value.
 369  
    *
 370  
    * @param descriptor the property descriptor.
 371  
    * @param defaultValue the default value to use if {@code value} is
 372  
    *          <code>null</code> and there is no default expression.
 373  
    * @param value the value to resolve, convert and validate.
 374  
    * @return the resolved, converted and validated value.
 375  
    * @throws PropertyValueConversionException if the value cannot be converted
 376  
    *           from {@link String} to the target type. If the configuration is
 377  
    *           set to admin mode, the unresolved {@code value} will be returned.
 378  
    *           In this case the type is not of the target type as described by
 379  
    *           the descriptor.
 380  
    * @throws PropertyValidationException if at least on constraint is not met.
 381  
    * @throws SecurityException on any problem decrypting an encrypted value.
 382  
    */
 383  
   protected final Object resolveAndConvertAndValidate(
 384  
       final PropertyDescriptor descriptor, final Object defaultValue,
 385  
       final Object value) throws PropertyValueConversionException,
 386  
     PropertyValidationException, SecurityException
 387  
   {
 388  
     try
 389  
     {
 390  0
       final Object convertedValue =
 391  
           resolveAndConvert(descriptor, value, defaultValue);
 392  0
       validate(descriptor, convertedValue);
 393  
 
 394  0
       return convertedValue;
 395  
     }
 396  0
     catch (final PropertyValueConversionException e)
 397  
     {
 398  0
       if (isInAdminMode())
 399  
       {
 400  0
         return value;
 401  
       }
 402  0
       throw e;
 403  
     }
 404  0
     catch (final PropertyValueResolveException e)
 405  
     {
 406  0
       if (isInAdminMode())
 407  
       {
 408  0
         return value;
 409  
       }
 410  0
       throw e;
 411  
     }
 412  
   }
 413  
 
 414  
   /**
 415  
    * Validates the given value for the given property.
 416  
    *
 417  
    * @param descriptor the descriptor to the property to use for validation.
 418  
    * @param value the property value to validate.
 419  
    * @param ifInOneOfTheseGroups the validation groups to consider in the
 420  
    *          validation process. The groups will be validated in the given
 421  
    *          order. As soon as a validation group fails, the validation process
 422  
    *          is aborted without checking the not yet processed groups.
 423  
    */
 424  
   public void validate(final PropertyDescriptor descriptor, final Object value,
 425  
       final Class<?>... ifInOneOfTheseGroups)
 426  
   {
 427  0
     final PropertyValidator validator = new PropertyValidator(isInAdminMode());
 428  0
     validator.validate(descriptor, value, ifInOneOfTheseGroups);
 429  0
   }
 430  
 
 431  
   /**
 432  
    * Resolves and converts the given value.
 433  
    *
 434  
    * @param descriptor the property descriptor.
 435  
    * @param value the value to resolve and convert.
 436  
    * @param defaultValue the default value to use if {@code value} is
 437  
    *          <code>null</code> and there is no default expression.
 438  
    * @return the resolved and converted value.
 439  
    * @throws PropertyValueResolveException if the value cannot be resolved.
 440  
    * @throws PropertyValueConversionException if the value cannot be converted
 441  
    *           from {@link String} to the target type.
 442  
    * @throws SecurityException on any problem decrypting an encrypted value.
 443  
    */
 444  
   protected final Object resolveAndConvert(final PropertyDescriptor descriptor,
 445  
       final Object value, final Object defaultValue)
 446  
     throws PropertyValueResolveException, PropertyValueConversionException,
 447  
     SecurityException
 448  
   {
 449  0
     Object currentValue = value;
 450  0
     if (currentValue == null)
 451  
     {
 452  0
       currentValue = descriptor.getDefaultExpression();
 453  0
       if (currentValue == null)
 454  
       {
 455  0
         currentValue = defaultValue;
 456  
       }
 457  
     }
 458  
 
 459  0
     if (currentValue instanceof PropertyExpression)
 460  
     {
 461  0
       final PropertyExpression expression = (PropertyExpression) currentValue;
 462  0
       currentValue = expression.getExpression();
 463  
     }
 464  
 
 465  0
     if (currentValue instanceof String)
 466  
     {
 467  0
       currentValue = resolveValue(descriptor, (String) currentValue);
 468  
     }
 469  
 
 470  0
     if (descriptor.isSecured() && currentValue instanceof String)
 471  
     {
 472  0
       currentValue = decrypter.decrypt(descriptor, (String) currentValue);
 473  
     }
 474  
 
 475  0
     final Object convertedValue = convert(descriptor, currentValue);
 476  0
     return convertedValue;
 477  
   }
 478  
 
 479  
   private String resolveValue(final PropertyDescriptor descriptor,
 480  
       final String value)
 481  
   {
 482  
     try
 483  
     {
 484  0
       final String resolvedValue = resolver.resolve((String) value);
 485  0
       return resolvedValue;
 486  
     }
 487  0
     catch (final ResolveConfigurationException e)
 488  
     {
 489  0
       throw new PropertyValueResolveException(
 490  
           new PropertyExpressionMessageBean(e, descriptor, e.getExpression()));
 491  
     }
 492  
   }
 493  
 
 494  
   private Object convert(final PropertyDescriptor descriptor,
 495  
       final Object currentValue)
 496  
   {
 497  0
     if (currentValue == null)
 498  
     {
 499  0
       return null;
 500  
     }
 501  
 
 502  0
     return converter.convert(descriptor, currentValue);
 503  
   }
 504  
 
 505  
   @Override
 506  
   public final void validate() throws ConfigurationValidationException
 507  
   {
 508  0
     validate(false);
 509  0
   }
 510  
 
 511  
   @Override
 512  
   public final void validate(final Class<?>... groups)
 513  
     throws ConfigurationValidationException
 514  
   {
 515  0
     validate(false, groups);
 516  0
   }
 517  
 
 518  
   // ... change listener support ..............................................
 519  
 
 520  
   @Override
 521  
   public final void addPropertyChangeListener(final PropertyKey name,
 522  
       final PropertyChangeListener listener) throws NullPointerException
 523  
   {
 524  0
     support.addPropertyChangeListener(name.toString(), listener);
 525  0
   }
 526  
 
 527  
   @Override
 528  
   public final void removePropertyChangeListener(final PropertyKey name,
 529  
       final PropertyChangeListener listener) throws NullPointerException
 530  
   {
 531  0
     support.removePropertyChangeListener(name.toString(), listener);
 532  0
   }
 533  
 
 534  
   @Override
 535  
   public final void addPropertyChangeListener(
 536  
       final PropertyChangeListener listener) throws NullPointerException
 537  
   {
 538  0
     support.addPropertyChangeListener(listener);
 539  0
   }
 540  
 
 541  
   @Override
 542  
   public final void removePropertyChangeListener(
 543  
       final PropertyChangeListener listener) throws NullPointerException
 544  
   {
 545  0
     support.removePropertyChangeListener(listener);
 546  0
   }
 547  
 
 548  
   /**
 549  
    * Fires the property change event.
 550  
    *
 551  
    * @param name the name of the property.
 552  
    * @param oldValue the old value of the property that has been changed.
 553  
    * @param newValue the new and current value of the property.
 554  
    */
 555  
   protected final void firePropertyChange(final String name,
 556  
       final String oldValue, final String newValue)
 557  
   {
 558  0
     support.firePropertyChange(name, oldValue, newValue);
 559  0
   }
 560  
 
 561  
   // --- object basics --------------------------------------------------------
 562  
 
 563  
   @Override
 564  
   public ConfigurationProperties toRepresentative()
 565  
   {
 566  0
     return this;
 567  
   }
 568  
 
 569  
   /**
 570  
    * Reads the object from the given stream.
 571  
    *
 572  
    * @param in the stream to read from.
 573  
    * @throws IOException on read problems.
 574  
    * @throws ClassNotFoundException if a class cannot be found.
 575  
    */
 576  
   private void readObject(final ObjectInputStream in) throws IOException,
 577  
     ClassNotFoundException
 578  
   {
 579  0
     in.defaultReadObject();
 580  
 
 581  0
     this.resolver = createResolver();
 582  0
   }
 583  
 
 584  
   /**
 585  
    * Returns the string representation of the object.
 586  
    *
 587  
    * @return the string representation of the object.
 588  
    */
 589  
   @Override
 590  
   public String toString()
 591  
   {
 592  0
     final StringBuilder buffer = new StringBuilder();
 593  
 
 594  0
     buffer.append(key).append(":\n").append("Registry:\n").append(registry);
 595  
 
 596  0
     return buffer.toString();
 597  
   }
 598  
 }