/*
* NEMESIS-FORUM.
* Copyright (C) 2002 David Laurent(lithium2@free.fr). All rights reserved.
*
* Copyright (c) 2000 The Apache Software Foundation. All rights reserved.
*
* Copyright (C) 2001 Yasna.com. All rights reserved.
*
* Copyright (C) 2000 CoolServlets.com. All rights reserved.
*
* NEMESIS-FORUM. is free software; you can redistribute it and/or
* modify it under the terms of the Apache Software License, Version 1.1,
* or (at your option) any later version.
*
* NEMESIS-FORUM core framework, NEMESIS-FORUM backoffice, NEMESIS-FORUM frontoffice
* application are parts of NEMESIS-FORUM and are distributed under
* same terms of licence.
*
*
* NEMESIS-FORUM includes software developed by the Apache Software Foundation (http://www.apache.org/)
* and software developed by CoolServlets.com (http://www.coolservlets.com).
* and software developed by Yasna.com (http://www.yasna.com).
*
*/
package org.nemesis.forum.impl;
import org.nemesis.forum.util.cache.Cache;
import org.nemesis.forum.util.cache.CacheObject;
import org.nemesis.forum.util.cache.Cacheable;
/**
* Central cache management of all caches.
*/
public class DbCacheManager {
public static int USER_CACHE = 0;
public static int USER_ID_CACHE = 1;
public static int GROUP_CACHE = 2;
public static int GROUP_ID_CACHE = 3;
public static int FORUM_CACHE = 4;
public static int FORUM_ID_CACHE = 5;
public static int THREAD_CACHE = 6;
public static int MESSAGE_CACHE = 7;
public static int USER_PERMS_CACHE = 8;
protected Cache[] caches;
private boolean cacheEnabled = true;
public DbCacheManager() {
int MINUTE = 1000 * 60;
int HOUR = MINUTE * 60;
caches = new Cache[9];
//Initialize all cache structures
caches[USER_CACHE] = new Cache(256 * 1024, 6 * HOUR);
caches[USER_ID_CACHE] = new Cache(128 * 1024, 6 * HOUR);
caches[GROUP_CACHE] = new Cache(128 * 1024, 6 * HOUR);
caches[GROUP_ID_CACHE] = new Cache(128 * 1024, 6 * HOUR);
caches[FORUM_CACHE] = new Cache(128 * 1024, 6 * HOUR);
caches[FORUM_ID_CACHE] = new Cache(128 * 1024, 6 * HOUR);
caches[THREAD_CACHE] = new Cache(128 * 1024, 6 * HOUR);
caches[MESSAGE_CACHE] = new Cache(512 * 1024, 6 * HOUR);
//The user permissions cache is a special one. It's actually a Cache
//of Cache objects. Each of the cache objects in the main cache
//corresponds to a particular forum, and is used to cache the
//permissions that a user has for a forum. In order to handle this
//requirement, we use a special subclass of Cache.
caches[USER_PERMS_CACHE] = new UserPermsCache(256 * 1024, 24 * HOUR);
}
public Cache getCache(int cacheType) {
return caches[cacheType];
}
public void add(int cacheType, Object key, Cacheable object) {
caches[cacheType].add(key, object);
}
public Cacheable get(int cacheType, Object key) {
if (!cacheEnabled) {
return null;
}
return caches[cacheType].get(key);
}
public void remove(int cacheType, Object key) {
caches[cacheType].remove(key);
//when cache becomes distributed, we'd send out an expire message
//here to all other yazd servers.
}
public void removeUserPerm(Object userID) {
Object[] values = caches[USER_PERMS_CACHE].values().toArray();
for (int i = 0; i < values.length; i++) {
Cache cache = (Cache) ((CacheObject) values[i]).object;
cache.remove(userID);
}
//when cache becomes distributed, we'd send out an expire message
//here to all other yazd servers.
}
public void removeUserPerm(Object userID, Object forumID) {
Cache cache = (Cache) caches[USER_PERMS_CACHE].get(forumID);
if (cache != null) {
cache.remove(userID);
}
//when cache becomes distributed, we'd send out an expire message
//here to all other yazd servers.
}
public void clear(int cacheType) {
caches[cacheType].clear();
//when cache becomes distributed, we'd send out an expire message
//here to all other yazd servers.
}
public boolean isCacheEnabled() {
return cacheEnabled;
}
public void setCacheEnabled(boolean cacheEnabled) {
this.cacheEnabled = cacheEnabled;
}
}
/**
* Special purpose Cache to hold all of the different user permission cache
* objects. The main feature is that new caches are automatically created so
* that calling get() never returns null.
*/
class UserPermsCache extends Cache {
public UserPermsCache(int size, long expireTime) {
super(size, expireTime);
}
public synchronized Cacheable get(Object key) {
Cache subCache = (Cache) super.get(key);
if (subCache == null) {
//cache has expired, or is not there, so put a new one in there.
//Cache objects only need to last as long as a user's session
//does. Half an hour is a reasonable amount of time for this.
subCache = new Cache(2 * 1024, 30 * 1000 * 60);
add(key, subCache);
}
return subCache;
}
public synchronized void remove(Object key) {
CacheObject cacheObject = (CacheObject) cachedObjectsHash.get(key);
//If the object is not in cache, stop trying to remove it.
if (cacheObject == null) {
return;
}
//remove from the hash map
cachedObjectsHash.remove(key);
//remove from the cache order list
cacheObject.lastAccessedListNode.remove();
cacheObject.ageListNode.remove();
//remove references to linked list nodes
cacheObject.ageListNode = null;
cacheObject.lastAccessedListNode = null;
//removed the object, so subtract its size from the total.
size -= cacheObject.size;
//Finally, clear the sub-cache to make sure memory is released.
((Cache) cacheObject.object).clear();
}
/**
* Returns the current size in bytes of the cache. The base getSize() method
* does not work correctly because the sub-caches are empty when we first
* add them rather than the normal cache assumption that objects are near
* the size that they will always be.
*
* @return the size of the cache in bytes.
*/
public int getSize() {
int size = 0;
Object[] values = values().toArray();
for (int i = 0; i < values.length; i++) {
Cache cache = (Cache) values[i];
size += cache.getSize();
}
return size;
}
}