Coverage Report - de.smartics.properties.impl.config.cache.DependencyTrackingCache
 
Classes in this File Line Coverage Branch Coverage Complexity
DependencyTrackingCache
0%
0/92
0%
0/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  0
   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  0
   private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 104  
 
 105  
   /**
 106  
    * The read lock for synchronized access.
 107  
    *
 108  
    * @serial
 109  
    */
 110  0
   private final Lock readLock = lock.readLock();
 111  
 
 112  
   /**
 113  
    * The write lock for synchronized access.
 114  
    *
 115  
    * @serial
 116  
    */
 117  0
   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  0
   {
 135  0
     this.configurationKey =
 136  
         Arg.checkNotNull("configurationKey", configurationKey);
 137  
 
 138  0
     final String cacheName = configurationKey.toString();
 139  0
     this.cache =
 140  
         (UnawareCache<String, DescribedProperty>) CacheManagerFactory
 141  
             .getInstance().createManager().getPropertiesCache(cacheName);
 142  0
     LOG.debug("Caches: {}", CacheManagerFactory.getInstance().createManager()
 143  
         .getCacheNames());
 144  0
     this.dependencies = HashMultimap.create();
 145  0
   }
 146  
 
 147  
   // ****************************** Inner Classes *****************************
 148  
 
 149  
   // ********************************* Methods ********************************
 150  
 
 151  
   // --- init -----------------------------------------------------------------
 152  
 
 153  
   // --- get&set --------------------------------------------------------------
 154  
 
 155  
   /**
 156  
    * Returns the key to the configuration the cache is associated with.
 157  
    *
 158  
    * @return the key to the configuration the cache is associated with.
 159  
    */
 160  
   public ConfigurationKey<?> getConfigurationKey()
 161  
   {
 162  0
     return configurationKey;
 163  
   }
 164  
 
 165  
   // --- business -------------------------------------------------------------
 166  
 
 167  
   // CHECKSTYLE:OFF
 168  
   public ValidatedProperty getValidatedProperty(
 169  
       // CHECKSTYLE:ON
 170  
       final ConfigurationProperties propertySource,
 171  
       final PropertyDescriptor descriptor, final Object defaultValue)
 172  
     throws IllegalArgumentException, UnknownPropertyException,
 173  
     PropertyValidationException
 174  
   {
 175  0
     readLock.lock();
 176  0
     boolean doReadUnlock = true;
 177  0
     final String key = descriptor.getKey().toString();
 178  
     try
 179  
     {
 180  0
       final Property property = cache.get(key);
 181  0
       if (property instanceof ValidatedProperty)
 182  
       {
 183  0
         LOG.debug("Cache hit: {}", key);
 184  0
         return (ValidatedProperty) property;
 185  
       }
 186  
       else
 187  
       {
 188  0
         readLock.unlock();
 189  0
         doReadUnlock = false;
 190  
 
 191  0
         final ValidatedProperty validatedProperty =
 192  
             propertySource.getValidatedProperty(key, defaultValue);
 193  
 
 194  0
         if (validatedProperty != null)
 195  
         {
 196  0
           writeLock.lock();
 197  
           try
 198  
           {
 199  0
             putToCache(descriptor, key, validatedProperty);
 200  0
             attachDependencies(validatedProperty);
 201  
           }
 202  
           finally
 203  
           {
 204  0
             writeLock.unlock();
 205  0
           }
 206  
         }
 207  
 
 208  0
         LOG.debug("Cache miss: {}", key);
 209  0
         return validatedProperty;
 210  
       }
 211  
     }
 212  
     finally
 213  
     {
 214  0
       if (doReadUnlock)
 215  
       {
 216  0
         readLock.unlock();
 217  
       }
 218  
     }
 219  
   }
 220  
 
 221  
   private void putToCache(final PropertyDescriptor descriptor,
 222  
       final String key, final ValidatedProperty validatedProperty)
 223  
   {
 224  0
     final long updateIntervalInMs = descriptor.getUpdateIntervalInMs();
 225  0
     cache
 226  
         .put(key, validatedProperty, updateIntervalInMs, TimeUnit.MILLISECONDS);
 227  0
   }
 228  
 
 229  
   private void putToCache(final PropertyDescriptor descriptor,
 230  
       final String key, final DescribedProperty property)
 231  
   {
 232  0
     final long updateIntervalInMs = descriptor.getUpdateIntervalInMs();
 233  0
     cache.put(key, property, updateIntervalInMs, TimeUnit.MILLISECONDS);
 234  0
   }
 235  
 
 236  
   // CHECKSTYLE:OFF
 237  
   public DescribedProperty getProperty(
 238  
       final ConfigurationProperties propertySource,
 239  
       final PropertyDescriptor descriptor, final Object defaultValue)
 240  
   // CHECKSTYLE:ON
 241  
   {
 242  0
     readLock.lock();
 243  0
     boolean doReadUnlock = true;
 244  
 
 245  0
     final String key = descriptor.getKey().toString();
 246  
     try
 247  
     {
 248  0
       DescribedProperty property = cache.get(key);
 249  0
       if (property != null)
 250  
       {
 251  0
         LOG.debug("Cache hit: {}", key);
 252  0
         return property;
 253  
       }
 254  
       else
 255  
       {
 256  0
         readLock.unlock();
 257  0
         doReadUnlock = false;
 258  
 
 259  0
         property = propertySource.getProperty(key, defaultValue);
 260  
 
 261  0
         if (property != null)
 262  
         {
 263  0
           writeLock.lock();
 264  
           try
 265  
           {
 266  0
             putToCache(descriptor, key, property);
 267  0
             if (property instanceof ValidatedProperty)
 268  
             {
 269  0
               attachDependencies((ValidatedProperty) property);
 270  
             }
 271  
           }
 272  
           finally
 273  
           {
 274  0
             writeLock.unlock();
 275  0
           }
 276  
         }
 277  
 
 278  0
         LOG.debug("Cache miss: {}", key);
 279  0
         return property;
 280  
       }
 281  
     }
 282  
     finally
 283  
     {
 284  0
       if (doReadUnlock)
 285  
       {
 286  0
         readLock.unlock();
 287  
       }
 288  
     }
 289  
   }
 290  
 
 291  
   public Property removeFromCache(final String key)
 292  
   {
 293  0
     writeLock.lock();
 294  
     try
 295  
     {
 296  0
       final Property oldProperty = cache.remove(key);
 297  0
       removeDependencies(key);
 298  0
       LOG.debug("Cache revoke: {}", key);
 299  0
       return oldProperty;
 300  
     }
 301  
     finally
 302  
     {
 303  0
       writeLock.unlock();
 304  
     }
 305  
   }
 306  
 
 307  
   public void removeFromCache(final Properties properties)
 308  
   {
 309  0
     writeLock.lock();
 310  
     try
 311  
     {
 312  0
       for (final Object key : properties.keySet())
 313  
       {
 314  0
         cache.remove((String) key);
 315  0
         removeDependencies(ObjectUtils.toString(key, null));
 316  0
         LOG.debug("Cache revoke due to init: {}", key);
 317  
       }
 318  
     }
 319  
     finally
 320  
     {
 321  0
       writeLock.unlock();
 322  0
     }
 323  0
   }
 324  
 
 325  
   public void removeFromCache(final PropertyCollection collection)
 326  
   {
 327  0
     writeLock.lock();
 328  
     try
 329  
     {
 330  0
       for (final Property property : collection)
 331  
       {
 332  0
         final String key = property.getName();
 333  0
         cache.remove(key);
 334  0
         removeDependencies(ObjectUtils.toString(key, null));
 335  0
         LOG.debug("Cache revoke due to init: {}", key);
 336  0
       }
 337  
     }
 338  
     finally
 339  
     {
 340  0
       collection.close();
 341  0
       writeLock.unlock();
 342  0
     }
 343  0
   }
 344  
 
 345  
   /**
 346  
    * It is assumed that the caller already holds the write lock.
 347  
    */
 348  
   private void attachDependencies(final ValidatedProperty property)
 349  
   {
 350  0
     final String key = property.getName();
 351  
 
 352  0
     for (final String dependency : property.getDependencies())
 353  
     {
 354  0
       dependencies.put(dependency, key);
 355  
     }
 356  0
   }
 357  
 
 358  
   /**
 359  
    * It is assumed that the caller already holds the write lock.
 360  
    */
 361  
   private void removeDependencies(final String key)
 362  
   {
 363  0
     for (final String dependant : dependencies.removeAll(key))
 364  
     {
 365  0
       LOG.debug("Cache revoke due to dependency on {}: {}", key, dependant);
 366  0
       removeFromCache(dependant);
 367  
     }
 368  0
   }
 369  
 
 370  
   // --- object basics --------------------------------------------------------
 371  
 
 372  
 }