Coverage Report - de.smartics.properties.impl.config.cache.DependencyTrackingCache
 
Classes in this File Line Coverage Branch Coverage Complexity
DependencyTrackingCache
60%
56/92
50%
11/22
2,455
 
 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.impl.config.cache;
 17  
 
 18  
 import java.io.Serializable;
 19  
 import java.util.Properties;
 20  
 import java.util.concurrent.TimeUnit;
 21  
 import java.util.concurrent.locks.Lock;
 22  
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 23  
 
 24  
 import javax.annotation.concurrent.ThreadSafe;
 25  
 
 26  
 import org.apache.commons.lang.ObjectUtils;
 27  
 import org.slf4j.Logger;
 28  
 import org.slf4j.LoggerFactory;
 29  
 
 30  
 import com.google.common.collect.HashMultimap;
 31  
 import com.google.common.collect.Multimap;
 32  
 
 33  
 import de.smartics.properties.api.config.domain.ConfigurationProperties;
 34  
 import de.smartics.properties.api.config.domain.DescribedProperty;
 35  
 import de.smartics.properties.api.config.domain.Property;
 36  
 import de.smartics.properties.api.config.domain.PropertyCollection;
 37  
 import de.smartics.properties.api.config.domain.UnknownPropertyException;
 38  
 import de.smartics.properties.api.config.domain.ValidatedProperty;
 39  
 import de.smartics.properties.api.config.domain.key.ConfigurationKey;
 40  
 import de.smartics.properties.api.core.domain.PropertyDescriptor;
 41  
 import de.smartics.properties.api.core.domain.PropertyValidationException;
 42  
 import de.smartics.properties.spi.config.cache.CacheManagerFactory;
 43  
 import de.smartics.properties.spi.config.cache.UnawareCache;
 44  
 import de.smartics.util.lang.Arg;
 45  
 
 46  
 /**
 47  
  * A cache implementation that tracks dependencies via placeholder contained in
 48  
  * property values and property default expressions. If a property is
 49  
  * invalidated, all properties that refer to this property (even transitively)
 50  
  * will also be invalidated. Invalidation implies, that the property is revoked
 51  
  * from the cache.
 52  
  */
 53  
 @ThreadSafe
 54  
 final class DependencyTrackingCache implements Serializable
 55  
 {
 56  
   // ********************************* Fields *********************************
 57  
 
 58  
   // --- constants ------------------------------------------------------------
 59  
 
 60  
   /**
 61  
    * The class version identifier.
 62  
    */
 63  
   private static final long serialVersionUID = 1L;
 64  
 
 65  
   /**
 66  
    * Reference to the logger for this class.
 67  
    */
 68  1
   private static final Logger LOG = LoggerFactory
 69  
       .getLogger(DependencyTrackingCache.class);
 70  
 
 71  
   // --- members --------------------------------------------------------------
 72  
 
 73  
   /**
 74  
    * The key to the configuration the cache is associated with.
 75  
    *
 76  
    * @serial
 77  
    */
 78  
   private final ConfigurationKey<?> configurationKey;
 79  
 
 80  
   /**
 81  
    * The synchronized cache. The property stored may be a property or a resolved
 82  
    * property.
 83  
    *
 84  
    * @serial
 85  
    */
 86  
   private final UnawareCache<String, DescribedProperty> cache;
 87  
 
 88  
   /**
 89  
    * The dependencies of key to cascade the revoking of elements from the cache.
 90  
    * The key is a property name the values depend upon. If the property with the
 91  
    * given name is revoked from the cache, all the dependent properties of its
 92  
    * list must also be revoked.
 93  
    *
 94  
    * @serial
 95  
    */
 96  
   private final Multimap<String, String> dependencies;
 97  
 
 98  
   /**
 99  
    * The lock for synchronized access.
 100  
    *
 101  
    * @serial
 102  
    */
 103  12
   private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 104  
 
 105  
   /**
 106  
    * The read lock for synchronized access.
 107  
    *
 108  
    * @serial
 109  
    */
 110  12
   private final Lock readLock = lock.readLock();
 111  
 
 112  
   /**
 113  
    * The write lock for synchronized access.
 114  
    *
 115  
    * @serial
 116  
    */
 117  12
   private final Lock writeLock = lock.writeLock();
 118  
 
 119  
   // ****************************** Initializer *******************************
 120  
 
 121  
   // ****************************** Constructors ******************************
 122  
 
 123  
   /**
 124  
    * Default constructor.
 125  
    *
 126  
    * @param configurationKey the key to the configuration the cache is
 127  
    *          associated with.
 128  
    * @throws NullPointerException if {@code configurationKey} is
 129  
    *           <code>null</code>.
 130  
    */
 131  
   @SuppressWarnings("unchecked")
 132  
   public DependencyTrackingCache(final ConfigurationKey<?> configurationKey)
 133  
     throws NullPointerException
 134  12
   {
 135  12
     this.configurationKey =
 136  
         Arg.checkNotNull("configurationKey", configurationKey);
 137  
 
 138  12
     final String cacheName = configurationKey.toString();
 139  12
     this.cache =
 140  
         (UnawareCache<String, DescribedProperty>) CacheManagerFactory
 141  
             .getPropertiesCache(cacheName);
 142  12
     LOG.debug("Caches: {}", CacheManagerFactory.getCacheNames());
 143  12
     this.dependencies = HashMultimap.create();
 144  12
   }
 145  
 
 146  
   // ****************************** Inner Classes *****************************
 147  
 
 148  
   // ********************************* Methods ********************************
 149  
 
 150  
   // --- init -----------------------------------------------------------------
 151  
 
 152  
   // --- get&set --------------------------------------------------------------
 153  
 
 154  
   /**
 155  
    * Returns the key to the configuration the cache is associated with.
 156  
    *
 157  
    * @return the key to the configuration the cache is associated with.
 158  
    */
 159  
   public ConfigurationKey<?> getConfigurationKey()
 160  
   {
 161  0
     return configurationKey;
 162  
   }
 163  
 
 164  
   // --- business -------------------------------------------------------------
 165  
 
 166  
   // CHECKSTYLE:OFF
 167  
   public ValidatedProperty getValidatedProperty(
 168  
       // CHECKSTYLE:ON
 169  
       final ConfigurationProperties propertySource,
 170  
       final PropertyDescriptor descriptor, final Object defaultValue)
 171  
     throws IllegalArgumentException, UnknownPropertyException,
 172  
     PropertyValidationException
 173  
   {
 174  32
     readLock.lock();
 175  32
     boolean doReadUnlock = true;
 176  32
     final String key = descriptor.getKey().toString();
 177  
     try
 178  
     {
 179  32
       final Property property = cache.get(key);
 180  32
       if (property instanceof ValidatedProperty)
 181  
       {
 182  2
         LOG.debug("Cache hit: {}", key);
 183  2
         return (ValidatedProperty) property;
 184  
       }
 185  
       else
 186  
       {
 187  30
         readLock.unlock();
 188  30
         doReadUnlock = false;
 189  
 
 190  30
         final ValidatedProperty validatedProperty =
 191  
             propertySource.getValidatedProperty(key, defaultValue);
 192  
 
 193  30
         if (validatedProperty != null)
 194  
         {
 195  30
           writeLock.lock();
 196  
           try
 197  
           {
 198  30
             putToCache(descriptor, key, validatedProperty);
 199  30
             attachDependencies(validatedProperty);
 200  
           }
 201  
           finally
 202  
           {
 203  30
             writeLock.unlock();
 204  30
           }
 205  
         }
 206  
 
 207  30
         LOG.debug("Cache miss: {}", key);
 208  30
         return validatedProperty;
 209  
       }
 210  
     }
 211  
     finally
 212  
     {
 213  32
       if (doReadUnlock)
 214  
       {
 215  2
         readLock.unlock();
 216  
       }
 217  
     }
 218  
   }
 219  
 
 220  
   private void putToCache(final PropertyDescriptor descriptor,
 221  
       final String key, final ValidatedProperty validatedProperty)
 222  
   {
 223  30
     final long updateIntervalInMs = descriptor.getUpdateIntervalInMs();
 224  30
     cache
 225  
         .put(key, validatedProperty, updateIntervalInMs, TimeUnit.MILLISECONDS);
 226  30
   }
 227  
 
 228  
   private void putToCache(final PropertyDescriptor descriptor,
 229  
       final String key, final DescribedProperty property)
 230  
   {
 231  0
     final long updateIntervalInMs = descriptor.getUpdateIntervalInMs();
 232  0
     cache.put(key, property, updateIntervalInMs, TimeUnit.MILLISECONDS);
 233  0
   }
 234  
 
 235  
   // CHECKSTYLE:OFF
 236  
   public DescribedProperty getProperty(final ConfigurationProperties propertySource,
 237  
       final PropertyDescriptor descriptor, final Object defaultValue)
 238  
   // CHECKSTYLE:ON
 239  
   {
 240  0
     readLock.lock();
 241  0
     boolean doReadUnlock = true;
 242  
 
 243  0
     final String key = descriptor.getKey().toString();
 244  
     try
 245  
     {
 246  0
       DescribedProperty property = cache.get(key);
 247  0
       if (property != null)
 248  
       {
 249  0
         LOG.debug("Cache hit: {}", key);
 250  0
         return property;
 251  
       }
 252  
       else
 253  
       {
 254  0
         readLock.unlock();
 255  0
         doReadUnlock = false;
 256  
 
 257  0
         property = propertySource.getProperty(key, defaultValue);
 258  
 
 259  0
         if (property != null)
 260  
         {
 261  0
           writeLock.lock();
 262  
           try
 263  
           {
 264  0
             putToCache(descriptor, key, property);
 265  0
             if (property instanceof ValidatedProperty)
 266  
             {
 267  0
               attachDependencies((ValidatedProperty) property);
 268  
             }
 269  
           }
 270  
           finally
 271  
           {
 272  0
             writeLock.unlock();
 273  0
           }
 274  
         }
 275  
 
 276  0
         LOG.debug("Cache miss: {}", key);
 277  0
         return property;
 278  
       }
 279  
     }
 280  
     finally
 281  
     {
 282  0
       if (doReadUnlock)
 283  
       {
 284  0
         readLock.unlock();
 285  
       }
 286  
     }
 287  
   }
 288  
 
 289  
   public Property removeFromCache(final String key)
 290  
   {
 291  15
     writeLock.lock();
 292  
     try
 293  
     {
 294  15
       final Property oldProperty = cache.remove(key);
 295  15
       removeDependencies(key);
 296  15
       LOG.debug("Cache revoke: {}", key);
 297  15
       return oldProperty;
 298  
     }
 299  
     finally
 300  
     {
 301  15
       writeLock.unlock();
 302  
     }
 303  
   }
 304  
 
 305  
   public void removeFromCache(final Properties properties)
 306  
   {
 307  4
     writeLock.lock();
 308  
     try
 309  
     {
 310  4
       for (final Object key : properties.keySet())
 311  
       {
 312  5
         cache.remove((String) key);
 313  5
         removeDependencies(ObjectUtils.toString(key, null));
 314  5
         LOG.debug("Cache revoke due to init: {}", key);
 315  
       }
 316  
     }
 317  
     finally
 318  
     {
 319  4
       writeLock.unlock();
 320  4
     }
 321  4
   }
 322  
 
 323  
   public void removeFromCache(final PropertyCollection collection)
 324  
   {
 325  0
     writeLock.lock();
 326  
     try
 327  
     {
 328  0
       for (final Property property : collection)
 329  
       {
 330  0
         final String key = property.getName();
 331  0
         cache.remove(key);
 332  0
         removeDependencies(ObjectUtils.toString(key, null));
 333  0
         LOG.debug("Cache revoke due to init: {}", key);
 334  0
       }
 335  
     }
 336  
     finally
 337  
     {
 338  0
       collection.close();
 339  0
       writeLock.unlock();
 340  0
     }
 341  0
   }
 342  
 
 343  
   /**
 344  
    * It is assumed that the caller already holds the write lock.
 345  
    */
 346  
   private void attachDependencies(final ValidatedProperty property)
 347  
   {
 348  30
     final String key = property.getName();
 349  
 
 350  30
     for (final String dependency : property.getDependencies())
 351  
     {
 352  18
       dependencies.put(dependency, key);
 353  
     }
 354  30
   }
 355  
 
 356  
   /**
 357  
    * It is assumed that the caller already holds the write lock.
 358  
    */
 359  
   private void removeDependencies(final String key)
 360  
   {
 361  20
     for (final String dependant : dependencies.removeAll(key))
 362  
     {
 363  9
       LOG.debug("Cache revoke due to dependency on {}: {}", key, dependant);
 364  9
       removeFromCache(dependant);
 365  
     }
 366  20
   }
 367  
 
 368  
   // --- object basics --------------------------------------------------------
 369  
 
 370  
 }