1 /*
2 * Copyright 2003 - 2013 The eFaps Team
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 * Revision: $Rev$
17 * Last Changed: $Date$
18 * Last Changed By: $Author$
19 */
20
21 package org.efaps.util.cache;
22
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.UUID;
29
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34 * <p>The class is used to cache three not independent information which are
35 * completely defined once but needed many times within eFaps. An example
36 * is to load all types at startup and access the cached
37 * data instead of reading the cached information each time they are
38 * needed.</p>
39 * <p>If a reload of the cache is needed, the cached object could be accessed,
40 * but returns the "old" values until the old cache is replaced by
41 * the new read values.</p>
42 * <p>The class is thread save, but a cached instance could be accessed much
43 * faster than a synchronized hash map.</p>
44 *
45 * @author The eFaps Team
46 * @param <T> class implementing CacheObjectInterface
47 * @version $Id$
48 */
49 public abstract class AbstractCache<T extends CacheObjectInterface>
50 {
51 /**
52 * Logging instance used to give logging information of this class.
53 */
54 private static final Logger LOG = LoggerFactory.getLogger(AbstractCache.class);
55
56 /**
57 * Set that stores all initialized Caches.
58 */
59 private static Set<AbstractCache<?>> CACHES = Collections.synchronizedSet(new HashSet<AbstractCache<?>>());
60
61 /**
62 * The map holds all cached data instances by Id. Because of the
63 * double-checked locking idiom, the instance variable is defined
64 * <i>volatile</i>.
65 *
66 * @see #get(Long)
67 */
68 private volatile Map<Long, T> cache4Id = null;
69
70 /**
71 * The map holds all cached data instances by Name. Because of the
72 * double-checked locking idiom, the instance variable is defined
73 * <i>volatile</i>.
74 *
75 * @see #get(String)
76 */
77 private volatile Map<String, T> cache4Name = null;
78
79 /**
80 * The map holds all cached data instances by UUID. Because of the
81 * double-checked locking idiom, the instance variable is defined
82 * <i>volatile</i>.
83 *
84 * @see #get(UUID)
85 */
86 private volatile Map<UUID, T> cache4UUID = null;
87
88 /**
89 * Stores the class name of the class that initialized this cache.
90 */
91 private String initializer;
92
93 /**
94 * Constructor adding this Cache to the set of Caches.
95 */
96 protected AbstractCache()
97 {
98 AbstractCache.CACHES.add(this);
99 }
100
101 /**
102 * Returns for given key id the cached object from the cache4Id cache. If
103 * the cache was not initialized yet, <code>null</code> is returned.
104 *
105 * @see #getCache4Id()
106 * @param _id id of the searched cached object
107 * @return cached object
108 *
109 */
110 public T get(final long _id)
111 {
112 return getCache4Id() == null ? null : getCache4Id().get(new Long(_id));
113 }
114
115 /**
116 * Returns for given key id the cached object from the cache4Id cache. If
117 * the cache was not initialized yet, <code>null</code> is returned.
118 *
119 * @see #getCache4Name()
120 * @param _name name the cached object
121 * @return cached object
122 */
123 public T get(final String _name)
124 {
125 return getCache4Name() == null ? null : getCache4Name().get(_name);
126 }
127
128 /**
129 * Returns for given key id the cached object from the cache4Id cache. If
130 * the cache was not initialized yet, <code>null</code> is returned.
131 *
132 * @see #getCache4UUID()
133 * @param _uuid UUID of the cached object
134 * @return cached object
135 *
136 */
137 public T get(final UUID _uuid)
138 {
139 return getCache4UUID() == null ? null : getCache4UUID().get(_uuid);
140 }
141
142 /**
143 * Add an object to this Cache. This method should only be used to add some
144 * objects, due to the reason that it is very slow!!! Normally the values
145 * should be added by using abstract method
146 * {@link #readCache(Map, Map, Map)}.
147 *
148 * @param _object Object to be added
149 */
150 public void addObject(final T _object)
151 {
152 final Map<Long, T> newCache4Id = getNewCache4Id();
153 final Map<String, T> newCache4Name = getNewCache4Name();
154 final Map<UUID, T> newCache4UUID = getNewCache4UUID();
155
156 if (getCache4Id() != null) {
157 newCache4Id.putAll(getCache4Id());
158 }
159
160 if (getCache4Name() != null) {
161 newCache4Name.putAll(getCache4Name());
162 }
163
164 if (getCache4UUID() != null) {
165 newCache4UUID.putAll(getCache4UUID());
166 }
167
168 newCache4Id.put(_object.getId(), _object);
169 newCache4Name.put(_object.getName(), _object);
170 newCache4UUID.put(_object.getUUID(), _object);
171
172 // replace old cache with new values
173 // it is thread save because of volatile
174 setCache4Id(newCache4Id);
175 setCache4Name(newCache4Name);
176 setCache4UUID(newCache4UUID);
177 }
178
179 /**
180 * The method tests, if the cache has stored some entries.
181 *
182 * @return <i>true</i> if the cache has some entries, otherwise
183 * <i>false</i>
184 */
185 public boolean hasEntries()
186 {
187 return (getCache4Id() != null && !getCache4Id().isEmpty())
188 || (getCache4Name() != null && !getCache4Name().isEmpty())
189 || (getCache4UUID() != null && !getCache4UUID().isEmpty());
190 }
191
192 /**
193 * The method initialize the cache.
194 *
195 * @param _initializer class name of the class initializing
196 *
197 */
198 public void initialize(final Class<?> _initializer)
199 {
200 this.initializer = _initializer.getName();
201 synchronized (this) {
202 try {
203 readCache();
204 } catch (final CacheReloadException e) {
205 AbstractCache.LOG.error("Unexpected error while initializing Cache for " + getClass(), e);
206 }
207 }
208 }
209
210 /**
211 * The complete cache is read and then replaces the current stored values
212 * in the cache. The cache must be read in this order:
213 * <ul>
214 * <li>create new cache map</li>
215 * <li>read cache in the new cache map</li>
216 * <li>replace old cache map with the new cache map</li>
217 * </ul>
218 * This order is imported, otherwise
219 * <ul>
220 * <li>if the cache initialized first time, the cache is not
221 * <code>null</code> and returns then wrong values</li>
222 * <li>existing and read values could not be read while a reload is
223 * done</li>
224 * </ul>
225 *
226 * @throws CacheReloadException if the cache could not be read (the
227 * exception is also written into the error
228 * log)
229 */
230 private void readCache()
231 throws CacheReloadException
232 {
233 // if cache is not initialized, the correct order is required!
234 // otherwise the cache is not null and returns wrong values!
235 final Map<Long, T> newCache4Id = getNewCache4Id();
236 final Map<String, T> newCache4Name = getNewCache4Name();
237 final Map<UUID, T> newCache4UUID = getNewCache4UUID();
238 try {
239 readCache(newCache4Id, newCache4Name, newCache4UUID);
240 } catch (final CacheReloadException e) {
241 AbstractCache.LOG.error("Read Cache for " + getClass() + " failed", e);
242 throw e;
243 //CHECKSTYLE:OFF
244 } catch (final Exception e) {
245 //CHECKSTYLE:ON
246 AbstractCache.LOG.error("Unexpected error while reading Cache for " + getClass(), e);
247 throw new CacheReloadException("Unexpected error while reading Cache " + "for " + getClass(), e);
248 }
249 // replace old cache with new values
250 // it is thread save because of volatile
251 this.cache4Id = newCache4Id;
252 this.cache4Name = newCache4Name;
253 this.cache4UUID = newCache4UUID;
254 }
255
256 /**
257 * Method to fill this cache with objects.
258 *
259 * @param _newCache4Id cache for id
260 * @param _newCache4Name cache for name
261 * @param _newCache4UUID cache for UUID
262 * @throws CacheReloadException on error during reading
263 */
264 protected abstract void readCache(final Map<Long, T> _newCache4Id,
265 final Map<String, T> _newCache4Name,
266 final Map<UUID, T> _newCache4UUID)
267 throws CacheReloadException;
268
269 /**
270 * Method to get a new empty Map for the id Cache.
271 * The method is implemented to provide the possibility to use
272 * different Map types in the extending classes.
273 *
274 * @return new empty Instance of a HashMap
275 */
276 protected Map<Long, T> getNewCache4Id()
277 {
278 return new HashMap<Long, T>();
279 }
280
281 /**
282 * Method to get a new empty Map for the id Cache.
283 * The method is implemented to provide the possibility to use
284 * different Map types in the extending classes.
285 *
286 * @return new empty Instance of a HashMap
287 */
288 protected Map<String, T> getNewCache4Name()
289 {
290 return new HashMap<String, T>();
291 }
292
293 /**
294 * Method to get a new empty Map for the id Cache.
295 * The method is implemented to provide the possibility to use
296 * different Map types in the extending classes.
297 *
298 * @return new empty Instance of a HashMap
299 */
300 protected Map<UUID, T> getNewCache4UUID()
301 {
302 return new HashMap<UUID, T>();
303 }
304
305 /**
306 * Getter method for instance variable {@link #cache4Id}.
307 *
308 * @return value of instance variable {@link #cache4Id}
309 */
310 public Map<Long, T> getCache4Id()
311 {
312 return this.cache4Id;
313 }
314
315 /**
316 * Setter method for instance variable {@link #cache4Id}.
317 *
318 * @param _cache4Id value for instance variable {@link #cache4Id}
319 */
320 protected void setCache4Id(final Map<Long, T> _cache4Id)
321 {
322 this.cache4Id = _cache4Id;
323 }
324
325 /**
326 * Getter method for instance variable {@link #cache4Name}.
327 *
328 * @return value of instance variable {@link #cache4Name}
329 */
330 protected Map<String, T> getCache4Name()
331 {
332 return this.cache4Name;
333 }
334
335 /**
336 * Setter method for instance variable {@link #cache4Name}.
337 *
338 * @param _cache4Name value for instance variable {@link #cache4Name}
339 */
340 protected void setCache4Name(final Map<String, T> _cache4Name)
341 {
342 this.cache4Name = _cache4Name;
343 }
344
345 /**
346 * Setter method for instance variable {@link #cache4UUID}.
347 *
348 * @param _cache4uuid value for instance variable {@link #cache4UUID}
349 */
350 protected void setCache4UUID(final Map<UUID, T> _cache4uuid)
351 {
352 this.cache4UUID = _cache4uuid;
353 }
354
355 /**
356 * Getter method for instance variable {@link #cache4UUID}.
357 *
358 * @return value of instance variable {@link #cache4UUID}
359 */
360 protected Map<UUID, T> getCache4UUID()
361 {
362 return this.cache4UUID;
363 }
364
365 /**
366 * Getter method for instance variable {@link #initializer}.
367 *
368 * @return value of instance variable {@link #initializer}
369 */
370 public String getInitializer()
371 {
372 return this.initializer;
373 }
374
375 /**
376 * Clear all values of this Cache.
377 */
378 public void clear()
379 {
380 if (getCache4Id() != null) {
381 getCache4Id().clear();
382 }
383 if (getCache4Name() != null) {
384 getCache4Name().clear();
385 }
386 if (getCache4UUID() != null) {
387 getCache4UUID().clear();
388 }
389 }
390
391 /**
392 * The static method removes all values in all caches.
393 */
394 public static void clearCaches()
395 {
396 synchronized (AbstractCache.CACHES) {
397 for (final AbstractCache<?> cache : AbstractCache.CACHES) {
398 cache.getCache4Id().clear();
399 cache.getCache4Name().clear();
400 cache.getCache4UUID().clear();
401 }
402 }
403 }
404
405 /**
406 * Getter method for variable {@link #CACHES}.
407 *
408 * @return value of variable {@link #CACHES}
409 */
410 public static Set<AbstractCache<?>> getCaches()
411 {
412 return AbstractCache.CACHES;
413 }
414 }