
org.efaps.util.cache.AbstractCache Maven / Gradle / Ivy
/*
* Copyright 2003 - 2013 The eFaps Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Revision: $Rev: 8848 $
* Last Changed: $Date: 2013-02-19 10:49:59 -0500 (Tue, 19 Feb 2013) $
* Last Changed By: $Author: [email protected] $
*/
package org.efaps.util.cache;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The class is used to cache three not independent information which are
* completely defined once but needed many times within eFaps. An example
* is to load all types at startup and access the cached
* data instead of reading the cached information each time they are
* needed.
* If a reload of the cache is needed, the cached object could be accessed,
* but returns the "old" values until the old cache is replaced by
* the new read values.
* The class is thread save, but a cached instance could be accessed much
* faster than a synchronized hash map.
*
* @author The eFaps Team
* @param class implementing CacheObjectInterface
* @version $Id: AbstractCache.java 8848 2013-02-19 15:49:59Z [email protected] $
*/
public abstract class AbstractCache
{
/**
* Logging instance used to give logging information of this class.
*/
private static final Logger LOG = LoggerFactory.getLogger(AbstractCache.class);
/**
* Set that stores all initialized Caches.
*/
private static Set> CACHES = Collections.synchronizedSet(new HashSet>());
/**
* The map holds all cached data instances by Id. Because of the
* double-checked locking idiom, the instance variable is defined
* volatile.
*
* @see #get(Long)
*/
private volatile Map cache4Id = null;
/**
* The map holds all cached data instances by Name. Because of the
* double-checked locking idiom, the instance variable is defined
* volatile.
*
* @see #get(String)
*/
private volatile Map cache4Name = null;
/**
* The map holds all cached data instances by UUID. Because of the
* double-checked locking idiom, the instance variable is defined
* volatile.
*
* @see #get(UUID)
*/
private volatile Map cache4UUID = null;
/**
* Stores the class name of the class that initialized this cache.
*/
private String initializer;
/**
* Constructor adding this Cache to the set of Caches.
*/
protected AbstractCache()
{
AbstractCache.CACHES.add(this);
}
/**
* Returns for given key id the cached object from the cache4Id cache. If
* the cache was not initialized yet, null
is returned.
*
* @see #getCache4Id()
* @param _id id of the searched cached object
* @return cached object
*
*/
public T get(final long _id)
{
return getCache4Id() == null ? null : getCache4Id().get(new Long(_id));
}
/**
* Returns for given key id the cached object from the cache4Id cache. If
* the cache was not initialized yet, null
is returned.
*
* @see #getCache4Name()
* @param _name name the cached object
* @return cached object
*/
public T get(final String _name)
{
return getCache4Name() == null ? null : getCache4Name().get(_name);
}
/**
* Returns for given key id the cached object from the cache4Id cache. If
* the cache was not initialized yet, null
is returned.
*
* @see #getCache4UUID()
* @param _uuid UUID of the cached object
* @return cached object
*
*/
public T get(final UUID _uuid)
{
return getCache4UUID() == null ? null : getCache4UUID().get(_uuid);
}
/**
* Add an object to this Cache. This method should only be used to add some
* objects, due to the reason that it is very slow!!! Normally the values
* should be added by using abstract method
* {@link #readCache(Map, Map, Map)}.
*
* @param _object Object to be added
*/
public void addObject(final T _object)
{
final Map newCache4Id = getNewCache4Id();
final Map newCache4Name = getNewCache4Name();
final Map newCache4UUID = getNewCache4UUID();
if (getCache4Id() != null) {
newCache4Id.putAll(getCache4Id());
}
if (getCache4Name() != null) {
newCache4Name.putAll(getCache4Name());
}
if (getCache4UUID() != null) {
newCache4UUID.putAll(getCache4UUID());
}
newCache4Id.put(_object.getId(), _object);
newCache4Name.put(_object.getName(), _object);
newCache4UUID.put(_object.getUUID(), _object);
// replace old cache with new values
// it is thread save because of volatile
setCache4Id(newCache4Id);
setCache4Name(newCache4Name);
setCache4UUID(newCache4UUID);
}
/**
* The method tests, if the cache has stored some entries.
*
* @return true if the cache has some entries, otherwise
* false
*/
public boolean hasEntries()
{
return (getCache4Id() != null && !getCache4Id().isEmpty())
|| (getCache4Name() != null && !getCache4Name().isEmpty())
|| (getCache4UUID() != null && !getCache4UUID().isEmpty());
}
/**
* The method initialize the cache.
*
* @param _initializer class name of the class initializing
*
*/
public void initialize(final Class> _initializer)
{
this.initializer = _initializer.getName();
synchronized (this) {
try {
readCache();
} catch (final CacheReloadException e) {
AbstractCache.LOG.error("Unexpected error while initializing Cache for " + getClass(), e);
}
}
}
/**
* The complete cache is read and then replaces the current stored values
* in the cache. The cache must be read in this order:
*
* - create new cache map
* - read cache in the new cache map
* - replace old cache map with the new cache map
*
* This order is imported, otherwise
*
* - if the cache initialized first time, the cache is not
*
null
and returns then wrong values
* - existing and read values could not be read while a reload is
* done
*
*
* @throws CacheReloadException if the cache could not be read (the
* exception is also written into the error
* log)
*/
private void readCache()
throws CacheReloadException
{
// if cache is not initialized, the correct order is required!
// otherwise the cache is not null and returns wrong values!
final Map newCache4Id = getNewCache4Id();
final Map newCache4Name = getNewCache4Name();
final Map newCache4UUID = getNewCache4UUID();
try {
readCache(newCache4Id, newCache4Name, newCache4UUID);
} catch (final CacheReloadException e) {
AbstractCache.LOG.error("Read Cache for " + getClass() + " failed", e);
throw e;
//CHECKSTYLE:OFF
} catch (final Exception e) {
//CHECKSTYLE:ON
AbstractCache.LOG.error("Unexpected error while reading Cache for " + getClass(), e);
throw new CacheReloadException("Unexpected error while reading Cache " + "for " + getClass(), e);
}
// replace old cache with new values
// it is thread save because of volatile
this.cache4Id = newCache4Id;
this.cache4Name = newCache4Name;
this.cache4UUID = newCache4UUID;
}
/**
* Method to fill this cache with objects.
*
* @param _newCache4Id cache for id
* @param _newCache4Name cache for name
* @param _newCache4UUID cache for UUID
* @throws CacheReloadException on error during reading
*/
protected abstract void readCache(final Map _newCache4Id,
final Map _newCache4Name,
final Map _newCache4UUID)
throws CacheReloadException;
/**
* Method to get a new empty Map for the id Cache.
* The method is implemented to provide the possibility to use
* different Map types in the extending classes.
*
* @return new empty Instance of a HashMap
*/
protected Map getNewCache4Id()
{
return new HashMap();
}
/**
* Method to get a new empty Map for the id Cache.
* The method is implemented to provide the possibility to use
* different Map types in the extending classes.
*
* @return new empty Instance of a HashMap
*/
protected Map getNewCache4Name()
{
return new HashMap();
}
/**
* Method to get a new empty Map for the id Cache.
* The method is implemented to provide the possibility to use
* different Map types in the extending classes.
*
* @return new empty Instance of a HashMap
*/
protected Map getNewCache4UUID()
{
return new HashMap();
}
/**
* Getter method for instance variable {@link #cache4Id}.
*
* @return value of instance variable {@link #cache4Id}
*/
public Map getCache4Id()
{
return this.cache4Id;
}
/**
* Setter method for instance variable {@link #cache4Id}.
*
* @param _cache4Id value for instance variable {@link #cache4Id}
*/
protected void setCache4Id(final Map _cache4Id)
{
this.cache4Id = _cache4Id;
}
/**
* Getter method for instance variable {@link #cache4Name}.
*
* @return value of instance variable {@link #cache4Name}
*/
protected Map getCache4Name()
{
return this.cache4Name;
}
/**
* Setter method for instance variable {@link #cache4Name}.
*
* @param _cache4Name value for instance variable {@link #cache4Name}
*/
protected void setCache4Name(final Map _cache4Name)
{
this.cache4Name = _cache4Name;
}
/**
* Setter method for instance variable {@link #cache4UUID}.
*
* @param _cache4uuid value for instance variable {@link #cache4UUID}
*/
protected void setCache4UUID(final Map _cache4uuid)
{
this.cache4UUID = _cache4uuid;
}
/**
* Getter method for instance variable {@link #cache4UUID}.
*
* @return value of instance variable {@link #cache4UUID}
*/
protected Map getCache4UUID()
{
return this.cache4UUID;
}
/**
* Getter method for instance variable {@link #initializer}.
*
* @return value of instance variable {@link #initializer}
*/
public String getInitializer()
{
return this.initializer;
}
/**
* Clear all values of this Cache.
*/
public void clear()
{
if (getCache4Id() != null) {
getCache4Id().clear();
}
if (getCache4Name() != null) {
getCache4Name().clear();
}
if (getCache4UUID() != null) {
getCache4UUID().clear();
}
}
/**
* The static method removes all values in all caches.
*/
public static void clearCaches()
{
synchronized (AbstractCache.CACHES) {
for (final AbstractCache> cache : AbstractCache.CACHES) {
cache.getCache4Id().clear();
cache.getCache4Name().clear();
cache.getCache4UUID().clear();
}
}
}
/**
* Getter method for variable {@link #CACHES}.
*
* @return value of variable {@link #CACHES}
*/
public static Set> getCaches()
{
return AbstractCache.CACHES;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy