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.getInstance().getManager(factory.getId())
259             .getConfigurationsCache(id);
260     ConfigurationPropertiesManagement config = getCached(key, cache);
261     if (config == null)
262     {
263       config = factory.create(key);
264       putInCache(key, config);
265     }
266     return config;
267   }
268 
269   private ConfigurationPropertiesManagement getCached(
270       final ConfigurationKey<?> key,
271       final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache)
272   {
273     ConfigurationPropertiesManagement config;
274     if (!permOnly() && key.hasActiveDynamicParts())
275     {
276       config = cache.get(key);
277     }
278     else
279     {
280       config = permanentStore.get(key);
281     }
282     return config;
283   }
284 
285   // CHECKSTYLE: OFF
286   // FIXME: Use this as boot properties.
287   /**
288    * FIXME: Use this as boot properties.
289    */
290   public static final String PERM_ONLY = "store.permonly";
291 
292   private static boolean permOnly()
293   {
294     // FIXME: Use this as boot properties.
295     final boolean permOnly = "true".equals(System.getProperty(PERM_ONLY));
296     return permOnly;
297   }
298 
299   // CHECKSTYLE: ON
300 
301   @SuppressWarnings({ "unchecked", "rawtypes" })
302   private void putInCache(final ConfigurationKey<?> key,
303       final ConfigurationPropertiesManagement config)
304   {
305     if (!permOnly() && key.hasActiveDynamicParts())
306     {
307       final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
308           (Cache) CacheManagerFactory.getInstance().getManager(factory.getId())
309               .getConfigurationsCache(id);
310       cache.put(key, config);
311     }
312     else
313     {
314       permanentStore.put(key, config);
315     }
316   }
317 
318   @Override
319   public void registerProperties(final ConfigurationKey<?> key,
320       final ConfigurationPropertiesManagement configurationProperties)
321     throws NullArgumentException, DuplicateConfigurationException
322   {
323     writeLock.lock();
324     try
325     {
326       final ConfigurationPropertiesManagement current = getCachedOrCreate(key);
327       if (current != null)
328       {
329         throw new DuplicateConfigurationException(key, current,
330             configurationProperties);
331       }
332       putInCache(key, configurationProperties);
333     }
334     finally
335     {
336       writeLock.unlock();
337     }
338   }
339 
340   @SuppressWarnings("unchecked")
341   @Override
342   public ConfigurationPropertiesManagement deregisterProperties(
343       final ConfigurationKey<?> key) throws NullArgumentException
344   {
345     writeLock.lock();
346     try
347     {
348       if (!permOnly() && key.hasActiveDynamicParts())
349       {
350         final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
351             (Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement>) CacheManagerFactory
352                 .getInstance().getManager(factory.getId())
353                 .getConfigurationsCache(id);
354         return cache.remove(key);
355       }
356       else
357       {
358         return permanentStore.remove(key);
359       }
360     }
361     finally
362     {
363       writeLock.unlock();
364     }
365   }
366 
367   @SuppressWarnings({ "unchecked", "rawtypes" })
368   @Override
369   public Collection<ConfigurationKey<?>> getKeys()
370   {
371     readLock.lock();
372     try
373     {
374       final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
375           (Cache) CacheManagerFactory.getInstance().getManager(factory.getId())
376               .getConfigurationsCache(id);
377       final Set<ConfigurationKey<?>> keys =
378           new HashSet<ConfigurationKey<?>>(cache.keySet());
379       keys.addAll(permanentStore.keySet());
380       return keys;
381     }
382     finally
383     {
384       readLock.unlock();
385     }
386   }
387 
388   @Override
389   public void release()
390   {
391     CacheManagerFactory.getInstance().createManager().discard(id);
392   }
393 
394   // --- object basics --------------------------------------------------------
395 
396   /**
397    * Reads the object from the given stream.
398    *
399    * @param in the stream to read from.
400    * @throws IOException on read problems.
401    * @throws ClassNotFoundException if a class cannot be found.
402    */
403   private void readObject(final ObjectInputStream in) throws IOException,
404     ClassNotFoundException
405   {
406     in.defaultReadObject();
407   }
408 
409   /**
410    * Returns the string representation of the object.
411    *
412    * @return the string representation of the object.
413    */
414   @SuppressWarnings({ "unchecked", "rawtypes" })
415   @Override
416   public String toString()
417   {
418     readLock.lock();
419     final StringBuilder buffer = new StringBuilder();
420     final Cache<ConfigurationKey<?>, ConfigurationPropertiesManagement> cache =
421         (Cache) CacheManagerFactory.getInstance().getManager(factory.getId())
422             .getConfigurationsCache(id);
423     buffer.append("The cache contains ")
424         .append(cache.size() + permanentStore.size()).append(" entries.");
425     try
426     {
427       for (final ConfigurationKey<?> key : permanentStore.keySet())
428       {
429         buffer.append("\n  ").append(key);
430       }
431       for (final ConfigurationKey<?> key : cache.keySet())
432       {
433         buffer.append("\n  ").append(key);
434       }
435     }
436     finally
437     {
438       readLock.unlock();
439     }
440 
441     return buffer.toString();
442   }
443 }