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.IOException;
19  import java.io.ObjectInputStream;
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.UUID;
29  import java.util.concurrent.locks.Lock;
30  import java.util.concurrent.locks.ReentrantReadWriteLock;
31  
32  import javax.annotation.concurrent.ThreadSafe;
33  
34  import de.smartics.properties.api.config.domain.ConfigurationProperties;
35  import de.smartics.properties.api.config.domain.ConfigurationPropertiesManagement;
36  import de.smartics.properties.api.config.domain.ConfigurationRepositoryManagement;
37  import de.smartics.properties.api.config.domain.DuplicateConfigurationException;
38  import de.smartics.properties.api.config.domain.MissingConfigurationException;
39  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
40  import de.smartics.properties.api.config.domain.key.KeyListBuilder;
41  import de.smartics.properties.api.core.domain.PropertyDescriptorRegistry;
42  import de.smartics.properties.spi.config.cache.Cache;
43  import de.smartics.properties.spi.config.cache.CacheManagerFactory;
44  import de.smartics.properties.spi.config.domain.key.ConfigurationKeyContextManager;
45  import de.smartics.util.lang.NullArgumentException;
46  
47  /**
48   * Stores all configurations in memory.
49   */
50  @ThreadSafe
51  public final class InMemoryConfigurationRepositoryManagement implements
52      ConfigurationRepositoryManagement
53  {
54    // ********************************* Fields *********************************
55  
56    // --- constants ------------------------------------------------------------
57  
58    // --- members --------------------------------------------------------------
59  
60    /**
61     * The unique identifier of the instance.
62     *
63     * @serial
64     */
65    private final String id = createId();
66  
67    /**
68     * The registry to resolve property descriptors.
69     *
70     * @serial
71     */
72    private final PropertyDescriptorRegistry registry;
73  
74    /**
75     * The factory to create instance of {@link ConfigurationPropertiesManagement}
76     * on demand.
77     *
78     * @serial
79     */
80    private final ConfigurationPropertiesManagementFactory factory;
81  
82    /**
83     * The permanent store to store configuration keys without active dynamic key
84     * parts.
85     */
86    private final Map<ConfigurationKey<?>, ConfigurationPropertiesManagement> permanentStore =
87        Collections
88            .synchronizedMap(new HashMap<ConfigurationKey<?>, ConfigurationPropertiesManagement>());
89  
90    /**
91     * The lock for synchronized access to the cache.
92     *
93     * @serial
94     */
95    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
96  
97    /**
98     * The read lock for synchronized access to the cache.
99     *
100    * @serial
101    */
102   private final Lock readLock = lock.readLock();
103 
104   /**
105    * The write lock for synchronized access to the cache.
106    *
107    * @serial
108    */
109   private final Lock writeLock = lock.writeLock();
110 
111   // ****************************** Initializer *******************************
112 
113   // ****************************** Constructors ******************************
114 
115   /**
116    * Default constructor.
117    *
118    * @param registry the registry to resolve property descriptors.
119    * @param factory the factory to create instance of
120    *          {@link ConfigurationPropertiesManagement} on demand.
121    */
122   public InMemoryConfigurationRepositoryManagement(
123       final PropertyDescriptorRegistry registry,
124       final ConfigurationPropertiesManagementFactory factory)
125   {
126     this.registry = registry;
127     this.factory = factory;
128   }
129 
130   // ****************************** Inner Classes *****************************
131 
132   // ********************************* Methods ********************************
133 
134   // --- init -----------------------------------------------------------------
135 
136   private static String createId()
137   {
138     final UUID uuid = UUID.randomUUID();
139     final String id = "Management:ConfigurationPropertiesManagement/" + uuid;
140     return id;
141   }
142 
143   // --- get&set --------------------------------------------------------------
144 
145   /**
146    * Returns the registry to resolve property descriptors.
147    *
148    * @return the registry to resolve property descriptors.
149    */
150   @Override
151   public PropertyDescriptorRegistry getRegistry()
152   {
153     return registry;
154   }
155 
156   // --- business -------------------------------------------------------------
157 
158   @Override
159   public ConfigurationProperties getProperties(final ConfigurationKey<?> key)
160     throws NullArgumentException, MissingConfigurationException
161   {
162     return getPropertiesManagementWithDefaults(key);
163   }
164 
165   @Override
166   public ConfigurationPropertiesManagement getPropertiesManagementWithDefaults(
167       final ConfigurationKey<?> key) throws NullArgumentException,
168     MissingConfigurationException
169   {
170     writeLock.lock();
171 
172     try
173     {
174       return constructConfigurationWithDefaults(key);
175     }
176     finally
177     {
178       writeLock.unlock();
179     }
180   }
181 
182   @Override
183   public ConfigurationPropertiesManagement getPropertiesManagement(
184       final ConfigurationKey<?> key) throws NullArgumentException,
185     MissingConfigurationException
186   {
187     readLock.lock();
188 
189     try
190     {
191       final ConfigurationPropertiesManagement config = getCachedOrCreate(key);
192       if (config == null)
193       {
194         throw new MissingConfigurationException(key);
195       }
196       return config;
197     }
198     finally
199     {
200       readLock.unlock();
201     }
202   }
203 
204   @Override
205   public boolean hasPropertiesManagement(final ConfigurationKey<?> key)
206     throws NullArgumentException
207   {
208     readLock.lock();
209     try
210     {
211       final ConfigurationPropertiesManagement config = getCachedOrCreate(key);
212       return config != null;
213     }
214     finally
215     {
216       readLock.unlock();
217     }
218   }
219 
220   /**
221    * Expected to be called with write lock acquired.
222    */
223   private ConfigurationPropertiesManagement constructConfigurationWithDefaults(
224       final ConfigurationKey<?> key)
225   {
226     final KeyListBuilder builder =
227         ConfigurationKeyContextManager.INSTANCE.context().keyListBuilder();
228 
229     final List<ConfigurationKey<?>> keySet = builder.createKeyList(key);
230     final List<ConfigurationPropertiesManagement> defaults =
231         new ArrayList<ConfigurationPropertiesManagement>(keySet.size());
232     for (final ConfigurationKey<?> current : keySet)
233     {
234       final ConfigurationPropertiesManagement config =
235           getCachedOrCreate(current);
236       if (config != null)
237       {
238         defaults.add(config);
239       }
240     }
241 
242     if (defaults.isEmpty())
243     {
244       return null;
245     }
246     if (defaults.size() == 1)
247     {
248       return defaults.get(0);
249     }
250     return new ConfigurationPropertiesManagementWithDefaults(defaults);
251   }
252 
253   @SuppressWarnings({ "unchecked", "rawtypes" })
254   private ConfigurationPropertiesManagement getCachedOrCreate(
255       final ConfigurationKey<?> key)
256   {
257     final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
258         (Cache) CacheManagerFactory.getConfigurationsCache(id);
259     ConfigurationPropertiesManagement config = getCached(key, cache);
260     if (config == null)
261     {
262       config = factory.create(key);
263       putInCache(key, config);
264     }
265     return config;
266   }
267 
268   private ConfigurationPropertiesManagement getCached(
269       final ConfigurationKey<?> key,
270       final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache)
271   {
272     ConfigurationPropertiesManagement config;
273     if (!permOnly() && key.hasActiveDynamicParts())
274     {
275       config = cache.get(key);
276     }
277     else
278     {
279       config = permanentStore.get(key);
280     }
281     return config;
282   }
283 
284   // CHECKSTYLE: OFF
285   // FIXME: Use this as boot properties.
286   /**
287    * FIXME: Use this as boot properties.
288    */
289   public static final String PERM_ONLY = "store.permonly";
290 
291   private static boolean permOnly()
292   {
293     // FIXME: Use this as boot properties.
294     final boolean permOnly = "true".equals(System.getProperty(PERM_ONLY));
295     return permOnly;
296   }
297 
298   // CHECKSTYLE: ON
299 
300   @SuppressWarnings({ "unchecked", "rawtypes" })
301   private void putInCache(final ConfigurationKey<?> key,
302       final ConfigurationPropertiesManagement config)
303   {
304     if (!permOnly() && key.hasActiveDynamicParts())
305     {
306       final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
307           (Cache) CacheManagerFactory.getConfigurationsCache(id);
308       cache.put(key, config);
309     }
310     else
311     {
312       permanentStore.put(key, config);
313     }
314   }
315 
316   @Override
317   public void registerProperties(final ConfigurationKey<?> key,
318       final ConfigurationPropertiesManagement configurationProperties)
319     throws NullArgumentException, DuplicateConfigurationException
320   {
321     writeLock.lock();
322     try
323     {
324       final ConfigurationPropertiesManagement current = getCachedOrCreate(key);
325       if (current != null)
326       {
327         throw new DuplicateConfigurationException(key, current,
328             configurationProperties);
329       }
330       putInCache(key, configurationProperties);
331     }
332     finally
333     {
334       writeLock.unlock();
335     }
336   }
337 
338   @SuppressWarnings("unchecked")
339   @Override
340   public ConfigurationPropertiesManagement deregisterProperties(
341       final ConfigurationKey<?> key) throws NullArgumentException
342   {
343     writeLock.lock();
344     try
345     {
346       if (!permOnly() && key.hasActiveDynamicParts())
347       {
348         final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
349             (Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement>) CacheManagerFactory
350                 .getConfigurationsCache(id);
351         return cache.remove(key);
352       }
353       else
354       {
355         return permanentStore.remove(key);
356       }
357     }
358     finally
359     {
360       writeLock.unlock();
361     }
362   }
363 
364   @SuppressWarnings({ "unchecked", "rawtypes" })
365   @Override
366   public Collection<ConfigurationKey<?>> getKeys()
367   {
368     readLock.lock();
369     try
370     {
371       final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
372           (Cache) CacheManagerFactory.getConfigurationsCache(id);
373       final Set<ConfigurationKey<?>> keys =
374           new HashSet<ConfigurationKey<?>>(cache.keySet());
375       keys.addAll(permanentStore.keySet());
376       return keys;
377     }
378     finally
379     {
380       readLock.unlock();
381     }
382   }
383 
384   @Override
385   public void release()
386   {
387     CacheManagerFactory.discard(id);
388   }
389 
390   // --- object basics --------------------------------------------------------
391 
392   /**
393    * Reads the object from the given stream.
394    *
395    * @param in the stream to read from.
396    * @throws IOException on read problems.
397    * @throws ClassNotFoundException if a class cannot be found.
398    */
399   private void readObject(final ObjectInputStream in) throws IOException,
400     ClassNotFoundException
401   {
402     in.defaultReadObject();
403   }
404 
405   /**
406    * Returns the string representation of the object.
407    *
408    * @return the string representation of the object.
409    */
410   @SuppressWarnings({ "unchecked", "rawtypes" })
411   @Override
412   public String toString()
413   {
414     readLock.lock();
415     final StringBuilder buffer = new StringBuilder();
416     final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
417         (Cache) CacheManagerFactory.getConfigurationsCache(id);
418     buffer.append("The cache contains ")
419         .append(cache.size() + permanentStore.size()).append(" entries.");
420     try
421     {
422       for (final ConfigurationKey<?> key : permanentStore.keySet())
423       {
424         buffer.append("\n  ").append(key);
425       }
426       for (final ConfigurationKey<?> key : cache.keySet())
427       {
428         buffer.append("\n  ").append(key);
429       }
430     }
431     finally
432     {
433       readLock.unlock();
434     }
435 
436     return buffer.toString();
437   }
438 }