表现层的cache

1.Cache类

/*** $RCSfile: Cache.java,v $* $Revision: 1.3 $* $Date: 2001/10/03 21:55:12 $** Copyright (C) 1999-2001 CoolServlets, Inc. All rights reserved.** This software is the proprietary information of CoolServlets, Inc.* Use is subject to license terms.*/package com.jivesoftware.util;import java.util.*;
import com.jivesoftware.util.LinkedList;/*** General purpose cache implementation. It stores objects associated with* unique keys in memory for fast access. All objects added to the cache must* implement the Cacheable interface, which requires objects to know their* size in memory. This restrictions allows the cache to never grow larger* than a specified amount.<p>** If the cache does grow too large, objects will be removed such that those* that are accessed least frequently are removed first. Because expiration* happens automatically, the cache makes <b>no</b> gaurantee as to how long* an object will remain in cache after it is put in. The cache will return* null if the requested object is not found.<p>** Optionally, a maximum lifetime for all objects can be specified. In that* case, objects will be deleted from cache after that amount of time, even* if they are frequently accessed. This feature is useful if objects put in* cache represent data that should be periodically refreshed; for example,* information from a database.<p>** Cache is optimized for fast data access. The getObject() method has 0(n)* performance regardless of cache size. The other cache operations also* perform quite fast.<p>** Cache objects are thread safe.<p>** The algorithm for cache is as follows: a HashMap is maintained for fast* object lookup. Two linked lists are maintained: one keeps objects in the* order they are accessed from cache, the other keeps objects in the order* they were originally added to cache. When objects are added to cache, they* are first wrapped by a CacheObject which maintains the following pieces* of information:<ul>*    <li> The size of the object (in bytes).*    <li> A pointer to the node in the linked list that maintains accessed*         order for the object. Keeping a reference to the node lets us avoid*         linear scans of the linked list.*    <li> A pointer to the node in the linked list that maintains the age*         of the object in cache. Keeping a reference to the node lets us avoid*         linear scans of the linked list.</ul>** To get an object from cache, a hash lookup is performed to get a reference* to the CacheObject that wraps the real object we are looking for.* The object is subsequently moved to the front of the accessed linked list* and any necessary cache cleanups are performed. Cache deletion and expiration* is performed as needed.** @see Cacheable*/
public class Cache implements Cacheable {/*** One of the major potential bottlenecks of the cache is performing* System.currentTimeMillis() for every cache get operation. Instead,* we maintain a global timestamp that gets updated once a second. This* means that cache expirations can be no more than one second accurate.*/protected static long currentTime = CacheTimer.currentTime;/*** Maintains the hash of cached objects. Hashing provides the best* performance for fast lookups.*/protected HashMap cachedObjectsHash;/*** Linked list to maintain order that cache objects are accessed* in, most used to least used.*/protected LinkedList lastAccessedList;/*** Linked list to maintain time that cache objects were initially added* to the cache, most recently added to oldest added.*/protected LinkedList ageList;/*** Maximum size in bytes that the cache can grow to. Default* maximum size is 128 K.*/protected int maxSize =  128 * 1024;/*** Maintains the current size of the cache in bytes.*/protected int size = 0;/*** Maximum length of time objects can exist in cache before expiring.* Default is that objects have no maximum lifetime.*/protected long maxLifetime = -1;/*** Maintain the number of cache hits and misses. A cache hit occurs every* time the get method is called and the cache contains the requested* object. A cache miss represents the opposite occurence.<p>** Keeping track of cache hits and misses lets one measure how efficient* the cache is; the higher the percentage of hits, the more efficient.*/protected long cacheHits, cacheMisses = 0L;/*** Create a new cache with default values. Default cache size is 128K with* no maximum lifetime.*/public Cache() {// Our primary data structure is a hash map. The default capacity of 11// is too small in almost all cases, so we set it bigger.cachedObjectsHash = new HashMap(103);lastAccessedList = new LinkedList();ageList = new LinkedList();}/*** Create a new cache and specify the maximum size for the cache in bytes.* Items added to the cache will have no maximum lifetime.** @param maxSize the maximum size of the cache in bytes.*/public Cache(int maxSize) {this();this.maxSize = maxSize;}/*** Create a new cache and specify the maximum lifetime of objects. The* time should be specified in milleseconds. The minimum lifetime of any* cache object is 1000 milleseconds (1 second). Additionally, cache* expirations have a 1000 millesecond resolution, which means that all* objects are guaranteed to be expired within 1000 milliseconds of their* maximum lifetime.** @param maxLifetime the maximum amount of time objects can exist in*    cache before being deleted.*/public Cache(long maxLifetime) {this();this.maxLifetime = maxLifetime;}/*** Create a new cache and specify the maximum size of for the cache in* bytes, and the maximum lifetime of objects.** @param maxSize the maximum size of the cache in bytes.* @param maxLifetime the maximum amount of time objects can exist in*    cache before being deleted.*/public Cache(int maxSize, long maxLifetime) {this();this.maxSize = maxSize;this.maxLifetime = maxLifetime;}/*** Returns the current size of the cache in bytes.** @return the size of the cache in bytes.*/public int getSize() {return size;}/*** Returns the maximum size of the cache in bytes. If the cache grows too* large, the least frequently used items will automatically be deleted so* that the cache size doesn't exceed the maximum.** @return the maximum size of the cache in bytes.*/public int getMaxSize() {return maxSize;}/*** Sets the maximum size of the cache in bytes. If the cache grows too* large, the least frequently used items will automatically be deleted so* that the cache size doesn't exceed the maximum.** @param maxSize the maximum size of the cache in bytes.*/public void setMaxSize(int maxSize) {this.maxSize = maxSize;// It's possible that the new max size is smaller than our current cache// size. If so, we need to delete infrequently used items.cullCache();}/*** Returns the number of objects in the cache.** @return the number of objects in the cache.*/public synchronized int getNumElements() {return cachedObjectsHash.size();}/*** Adds a new Cacheable object to the cache. The key must be unique.** @param key a unique key for the object being put into cache.* @param object the Cacheable object to put into cache.*/public synchronized void add(Object key, Cacheable object) {// Delete an old entry if it exists.remove(key);int objectSize = object.getSize();// If the object is bigger than the entire cache, simply don't add it.if (objectSize > maxSize * .90) {return;}size += objectSize;CacheObject cacheObject = new CacheObject(object, objectSize);cachedObjectsHash.put(key, cacheObject);// Make an entry into the cache order list.LinkedListNode lastAccessedNode = lastAccessedList.addFirst(key);// Store the cache order list entry so that we can get back to it// during later lookups.cacheObject.lastAccessedListNode = lastAccessedNode;// Add the object to the age listLinkedListNode ageNode = ageList.addFirst(key);// We make an explicit call to currentTimeMillis() so that total accuracy// of lifetime calculations is better than one second.ageNode.timestamp = System.currentTimeMillis();cacheObject.ageListNode = ageNode;// If cache is too full, remove least used cache entries until it is// not too full.cullCache();}/*** Gets an object from cache. This method will return null under two* conditions:<ul>*    <li>The object referenced by the key was never added to cache.*    <li>The object referenced by the key has expired from cache.</ul>** @param key the unique key of the object to get.* @return the Cacheable object corresponding to unique key.*/public synchronized Cacheable get(Object key) {// First, clear all entries that have been in cache longer than the// maximum defined age.deleteExpiredEntries();CacheObject cacheObject = (CacheObject)cachedObjectsHash.get(key);if (cacheObject == null) {// The object didn't exist in cache, so increment cache misses.cacheMisses++;return null;}// The object exists in cache, so increment cache hits.cacheHits++;// Remove the object from it's current place in the cache order list,// and re-insert it at the front of the list.return cacheObject.object;}/*** Removes an object from cache.** @param key the unique key of the object to remove.*/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 mapcachedObjectsHash.remove(key);// remove from the cache order listcacheObject.lastAccessedListNode.remove();cacheObject.ageListNode.remove();// remove references to linked list nodescacheObject.ageListNode = null;cacheObject.lastAccessedListNode = null;// removed the object, so subtract its size from the total.size -= cacheObject.size;}/*** Clears the cache of all objects. The size of the cache is reset to 0.*/public synchronized void clear() {Object [] keys = cachedObjectsHash.keySet().toArray();for (int i=0; i<keys.length; i++) {remove(keys[i]);}// Now, reset all containers.cachedObjectsHash.clear();cachedObjectsHash = new HashMap(103);lastAccessedList.clear();lastAccessedList = new LinkedList();ageList.clear();ageList = new LinkedList();size = 0;cacheHits = 0;cacheMisses = 0;}/*** Returns an array of the keys contained in the cache.** @return an array of the keys present in the cache.*/public Object [] keys() {return cachedObjectsHash.keySet().toArray();}/*** Returns an array of the values contained in the cache.** @return a array of the cache entries.*/public Object [] values() {Object [] cacheObjects = cachedObjectsHash.values().toArray();Object [] values = new Object[cacheObjects.length];for (int i=0; i<cacheObjects.length; i++) {values[i] = ((CacheObject)cacheObjects[i]).object;}return values;}/*** Returns the number of cache hits. A cache hit occurs every* time the get method is called and the cache contains the requested* object.<p>** Keeping track of cache hits and misses lets one measure how efficient* the cache is; the higher the percentage of hits, the more efficient.** @return the number of cache hits.*/public long getCacheHits() {return cacheHits;}/*** Returns the number of cache misses. A cache miss occurs every* time the get method is called and the cache does not contain the* requested object.<p>** Keeping track of cache hits and misses lets one measure how efficient* the cache is; the higher the percentage of hits, the more efficient.** @return the number of cache hits.*/public long getCacheMisses() {return cacheMisses;}/*** Clears all entries out of cache where the entries are older than the* maximum defined age.*/private final void deleteExpiredEntries() {//Check if expiration is turned on.if (maxLifetime <= 0) {return;}// Remove all old entries. To do this, we remove objects from the end// of the linked list until they are no longer too old. We get to avoid// any hash lookups or looking at any more objects than is strictly// neccessary.LinkedListNode node = ageList.getLast();//If there are no entries in the age list, return.if (node == null) {return;}// Determine the expireTime, which is the moment in time that elements// should expire from cache. Then, we can do an easy to check to see// if the expire time is greater than the expire time.long expireTime = currentTime - maxLifetime;while(expireTime > node.timestamp) {// Remove the objectremove(node.object);// Get the next node.node = ageList.getLast();// If there are no more entries in the age list, return.if (node == null) {return;}}}/*** Removes objects from cache if the cache is too full. "Too full" is* defined as within 3% of the maximum cache size. Whenever the cache is* is too big, the least frequently used elements are deleted until the* cache is at least 10% empty.*/private final void cullCache() {// See if the cache size is within 3% of being too big. If so, clean out// cache until it's 10% free.if (size >= maxSize * .97) {// First, delete any old entries to see how much memory that frees.deleteExpiredEntries();int desiredSize = (int)(maxSize * .90);while (size > desiredSize) {// Get the key and invoke the remove method on it.remove(lastAccessedList.getLast().object);}}}
}
 
#################################################################################################################
2.LinkedList的addFirst方法
   public LinkedListNode addFirst(LinkedListNode node) {node.next = head.next;node.previous = head;node.previous.next = node;node.next.previous = node;return node;}
 

3.LinkedListNode的remove方法

 public void remove() {previous.next = next;next.previous = previous;}
 
 
数据层的cache:
 
4.DatabaseCacheManager
/*** $RCSfile: DatabaseCacheManager.java,v $* $Revision: 1.7 $* $Date: 2001/09/24 02:54:33 $** Copyright (C) 1999-2001 CoolServlets, Inc. All rights reserved.** This software is the proprietary information of CoolServlets, Inc.* Use is subject to license terms.*/package com.jivesoftware.forum.database;import com.jivesoftware.util.*;
import com.jivesoftware.forum.*;/*** Central cache management of all caches used by Jive. Cache sizes are stored* as the following Jive following Jive property values: <ul>**      <li> <tt>cache.userCache.size</tt>*      <li> <tt>cache.groupCache.size</tt>*      <li> <tt>cache.forumCache.size</tt>*      <li> <tt>cache.threadCache.size</tt>*      <li> <tt>cache.messageCache.size</tt> </ul>** All values should be in bytes. Cache can also be globally enabled or disabled.* This value is stored as the <tt>cache.enabled</tt> Jive property.*/
public class DatabaseCacheManager {public static final int USER_CACHE = 0;public static final int GROUP_CACHE = 1;public static final int FORUM_CACHE = 2;public static final int THREAD_CACHE = 3;public static final int MESSAGE_CACHE = 4;public static final int USER_PERMS_CACHE = 5;public UserCache userCache;public GroupCache groupCache;public ForumCache forumCache;public ForumThreadCache threadCache;public ForumMessageCache messageCache;public UserPermissionsCache userPermsCache;private boolean cacheEnabled = true;private DbForumFactory factory;/*** Creates a new cache manager.*/public DatabaseCacheManager(DbForumFactory factory) {this.factory = factory;// See if cache is supposed to be enabled.String enabled = JiveGlobals.getJiveProperty("cache.enabled");if (enabled != null) {try { cacheEnabled = Boolean.valueOf(enabled).booleanValue(); }catch (Exception e) { }}// Default cache sizes (small)int forumCacheSize = 512*1024;        // 1/2 MBint threadCacheSize = 512*1024;       // 1/2 MBint messageCacheSize = 1152*1024;     // 5/4 MBint userCacheSize = 512*1024;         // 1/2 MBint userPermCacheSize = 256*1024;     // 1/4 MBint groupCacheSize = 128*1024;        // 1/8 MB//Now, see if properties were set.String fCacheSize = JiveGlobals.getJiveProperty("cache.forumCache.size");if (fCacheSize != null) {try {  forumCacheSize = Integer.parseInt(fCacheSize);  }catch (Exception e) { }}String tCacheSize = JiveGlobals.getJiveProperty("cache.threadCache.size");if (tCacheSize != null) {try {  threadCacheSize = Integer.parseInt(tCacheSize);  }catch (Exception e) { }}String mCacheSize = JiveGlobals.getJiveProperty("cache.messageCache.size");if (mCacheSize != null) {try {  messageCacheSize = Integer.parseInt(mCacheSize);  }catch (Exception e) { }}String uCacheSize = JiveGlobals.getJiveProperty("cache.userCache.size");if (uCacheSize != null) {try {  userCacheSize = Integer.parseInt(uCacheSize);  }catch (Exception e) { }}String upCacheSize = JiveGlobals.getJiveProperty("cache.userPermCache.size");if (upCacheSize != null) {try {  userPermCacheSize = Integer.parseInt(upCacheSize);  }catch (Exception e) { }}String gCacheSize = JiveGlobals.getJiveProperty("cache.groupCache.size");if (gCacheSize != null) {try {  groupCacheSize = Integer.parseInt(gCacheSize);  }catch (Exception e) { }}int MINUTE = 1000*60;int HOUR = MINUTE*60;//Initialize all cache structuresforumCache = new ForumCache(new LongCache(forumCacheSize, 6*HOUR), factory);threadCache = new ForumThreadCache(new LongCache(threadCacheSize, 6*HOUR), factory);messageCache = new ForumMessageCache(new LongCache(messageCacheSize, 6*HOUR), factory);userCache = new UserCache(new LongCache(userCacheSize, 6*HOUR), factory);groupCache = new GroupCache(new LongCache(groupCacheSize, 6*HOUR), factory);//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.userPermsCache = new UserPermissionsCache(new UserPermsCache(userPermCacheSize, 24*HOUR), factory);}public void clear(int cacheType) {getCache(cacheType).clear();}public long getHits(int cacheType) {return getCache(cacheType).getCacheHits();}public long getMisses(int cacheType) {return getCache(cacheType).getCacheMisses();}public int getSize(int cacheType) {return getCache(cacheType).getSize();}public int getMaxSize(int cacheType) {return getCache(cacheType).getMaxSize();}public void setMaxSize(int cacheType, int size) {getCache(cacheType).setMaxSize(size);// Save the size of the cache as a jive propertyswitch (cacheType) {case FORUM_CACHE:JiveGlobals.setJiveProperty("cache.forumCache.size",String.valueOf(size));break;case THREAD_CACHE:JiveGlobals.setJiveProperty("cache.threadCache.size",String.valueOf(size));break;case MESSAGE_CACHE:JiveGlobals.setJiveProperty("cache.messageCache.size",String.valueOf(size));break;case USER_CACHE:JiveGlobals.setJiveProperty("cache.userCache.size",String.valueOf(size));break;case USER_PERMS_CACHE:JiveGlobals.setJiveProperty("cache.userPermsCache.size",String.valueOf(size));break;case GROUP_CACHE:JiveGlobals.setJiveProperty("cache.groupCache.size",String.valueOf(size));break;default:throw new IllegalArgumentException("Invalid cache type: " + cacheType);}}public int getNumElements(int cacheType) {return getCache(cacheType).getNumElements();}private DatabaseCache getCache(int cacheType) {switch (cacheType) {case FORUM_CACHE:return forumCache;case THREAD_CACHE:return threadCache;case MESSAGE_CACHE:return messageCache;case USER_CACHE:return userCache;case USER_PERMS_CACHE:return userPermsCache;case GROUP_CACHE:return groupCache;default:throw new IllegalArgumentException("Invalid cache type: " + cacheType);}}public boolean isCacheEnabled() {return cacheEnabled;}public void setCacheEnabled(boolean cacheEnabled) {// If we're setting cacheEnabled to false, clear all cachesif (cacheEnabled == false) {// Iterate through each of the five caches.for (int i=USER_CACHE; i<USER_PERMS_CACHE; i++) {clear(i);}}this.cacheEnabled = cacheEnabled;JiveGlobals.setJiveProperty("cache.enabled", String.valueOf(cacheEnabled));}
}
 
###############################################################################################################
 
5.DatabaseCache
/*** $RCSfile: DatabaseCache.java,v $* $Revision: 1.3 $* $Date: 2001/07/31 05:38:47 $** Copyright (C) 1999-2001 CoolServlets, Inc. All rights reserved.** This software is the proprietary information of CoolServlets, Inc.* Use is subject to license terms.*/package com.jivesoftware.forum.database;import com.jivesoftware.util.LongCache;/*** A base class that defines the basic functionality needed to wrap the general* purpose CoolServlets cache classes to caches for specific Jive database* objects.** @see LongCache*/
public class DatabaseCache {protected LongCache cache;protected DbForumFactory factory;/*** Creates a new database cache object.** @param cache a cache object to wrap.* @param factory a DbForumFactory to be used to perform Jive operations.*/public DatabaseCache(LongCache cache, DbForumFactory factory) {this.cache = cache;this.factory = factory;}/*** Pass-thru method for LongCache.remove(long).** @see LongCache#remove(long)*/public void remove(long key) {cache.remove(key);}/*** Pass-thru method for LongCache.getCacheHits().** @see LongCache#getCacheHits()*/public long getCacheHits() {return cache.getCacheHits();}/*** Pass-thru for LongCache.getCacheMisses().** @see LongCache#getCacheMisses()*/public long getCacheMisses() {return cache.getCacheMisses();}public int getSize() {return cache.getSize();}public void setMaxSize(int maxSize) {cache.setMaxSize(maxSize);}public int getMaxSize() {return cache.getMaxSize();}public int getNumElements() {return cache.getNumElements();}public void clear() {cache.clear();}public boolean isEnabled() {return factory.cacheManager.isCacheEnabled();}
}
 
##############################################################################################################
 
6.ForumCache---
/*** $RCSfile: ForumCache.java,v $* $Revision: 1.4 $* $Date: 2001/09/18 16:40:03 $** Copyright (C) 1999-2001 CoolServlets, Inc. All rights reserved.** This software is the proprietary information of CoolServlets, Inc.* Use is subject to license terms.*/package com.jivesoftware.forum.database;import com.jivesoftware.util.*;
import com.jivesoftware.forum.util.*;
import com.jivesoftware.forum.*;/*** Cache for DbForum objects.*/{protected Cache forumIDCache = new Cache(128*1024, 6*JiveGlobals.HOUR);public ForumCache(LongCache cache, DbForumFactory forumFactory) {super(cache, forumFactory);}public DbForum get(long forumID) throws ForumNotFoundException {if (!isEnabled()) {}// Cache is enabled.DbForum forum = (DbForum)cache.get(forumID);if (forum == null) {// -- CACHE WARMUP DISABLED. SEEMS TO DO MORE HARM THAN GOOD. -- //// Finally, schedule a warmup of the cache for the forum.//TaskEngine.addTask(new ForumCacheWarmupTask(forum),//    Thread.NORM_PRIORITY);}return forum;}public Forum get(String name) throws ForumNotFoundException {// If cache is not enabled, do a new lookup of objectif (!isEnabled()) {long forumID = factory.getForumID(name);return new DbForum(forumID, factory);}// Cache is enabled.CacheableLong forumIDLong = (CacheableLong)forumIDCache.get(name);// If id wan't found in cache, load it up and put it there.if (forumIDLong == null) {long forumID = factory.getForumID(name);forumIDLong = new CacheableLong(forumID);forumIDCache.add(name, forumIDLong);}return get(forumIDLong.getLong());}public void remove(long key) {DbForum forum = (DbForum)cache.get(key);if (forum == null) {return;}// Find the name of the forum and remove it from the cache.try {String name = forum.getName();forumIDCache.remove(name);}catch (Exception e) {e.printStackTrace();}forum.reset();// Now, expire relevant objects out of the factory.factory.popularForums = null;factory.popularThreads = null;}
}
 
#####################################################################################################################
 
7.DBForum
/*** $RCSfile: DbForum.java,v $* $Revision: 1.25 $* $Date: 2001/10/12 23:02:15 $** Copyright (C) 1999-2001 CoolServlets, Inc. All rights reserved.** This software is the proprietary information of CoolServlets, Inc.* Use is subject to license terms.*/package com.jivesoftware.forum.database;import java.util.*;
import java.util.Date;
import java.sql.*;
import java.io.*;import com.jivesoftware.forum.*;
import com.jivesoftware.util.*;
import com.jivesoftware.forum.gateway.*;/*** Database implementation of the Forum interface. It loads and stores forum* information from a a database.** @see Forum*/
public class DbForum implements Forum, Cacheable {/** DATABASE QUERIES **/private static final String ALL_THREADS ="SELECT threadID from jiveThread WHERE forumID=?";private static final String ADD_THREAD ="UPDATE jiveThread set forumID=? WHERE threadID=?";private static final String MOVE_MESSAGES ="UPDATE jiveMessage set forumID=? WHERE threadID=?";protected static final String DELETE_THREAD ="DELETE FROM jiveThread WHERE threadID=?";private static final String DELETE_THREAD_PROPERTIES ="DELETE FROM jiveThreadProp WHERE threadID=?";private static final String LOAD_PROPERTIES ="SELECT name, propValue FROM jiveForumProp WHERE forumID=?";private static final String DELETE_PROPERTY ="DELETE FROM jiveForumProp WHERE forumID=? AND name=?";private static final String DELETE_PROPERTIES ="DELETE FROM jiveForumProp WHERE forumID=?";private static final String INSERT_PROPERTY ="INSERT INTO jiveForumProp(forumID,name,propValue) VALUES(?,?,?)";private static final String LOAD_FORUM ="SELECT forumID, name, description, modDefaultThreadVal, " +"modDefaultMsgVal, modMinThreadVal, modMinMsgVal, modifiedDate, " +"creationDate FROM jiveForum WHERE forumID=?";private static final String ADD_FORUM ="INSERT INTO jiveForum(forumID, name, description, modDefaultThreadVal, " +"modDefaultMsgVal, modMinThreadVal, modMinMsgVal, modifiedDate, creationDate)" +" VALUES (?,?,?,?,?,?,?,?,?)";private static final String SAVE_FORUM ="UPDATE jiveForum SET name=?, description=?, modDefaultThreadVal=?, " +"modDefaultMsgVal=?, modMinThreadVal=?, modMinMsgVal=?, " +"modifiedDate=?, creationDate=? WHERE forumID=?";private static final String UPDATE_FORUM_MODIFIED_DATE ="UPDATE jiveForum SET modifiedDate=? WHERE forumID=?";private static final String POPULAR_THREADS ="SELECT threadID, count(1) AS msgCount FROM jiveMessage WHERE " +"modifiedDate > ? AND forumID=? GROUP BY threadID ORDER BY msgCount DESC";private static final String POPULAR_THREADS_ORACLE ="SELECT /*+ INDEX (jiveMessage jiveMessage_mDate_idx) */ threadID, " +"count(1) AS msgCount FROM jiveMessage WHERE modifiedDate > ? " +"AND forumID=? GROUP BY threadID ORDER BY msgCount DESC";/*// Note, the above query includes hints for Oracle, which are necessary// so that modified date index will always be used. This is a great// tradeoff when the time window you're looking at is not excessively// large. MySQL also handles the query very quickly. If your own db// needs a hint, you may want to edit the sql logic to add it.*//*** Controls whether extended properties should be lazily loaded (not loaded* until requested). If the properties are infrequently used, this provides* a great speedup in initial object loading time. However, if your* application does use extended properties all the time, you may wish to* turn lazy loading off, as it's actually faster in total db lookup time* to load everything at once.*/private static final boolean LAZY_PROP_LOADING = true;/*** Number of threadID's per cache block.*/public static final int THREAD_BLOCK_SIZE = 200;/*** Number of messageID's per cache block.*/public static final int MESSAGE_BLOCK_SIZE = 100;// Constant for an empty bock. This is returned in the case that there are// no results when trying to load a thread or message block.private static final long[] EMPTY_BLOCK = new long[0];// A ResultFilter is used to filter and sort the values that are returned// from the threads() and messages() methods. We use a defaultprivate static final ResultFilter DEFAULT_THREAD_FILTER =ResultFilter.createDefaultThreadFilter();private static final ResultFilter DEFAULT_MESSAGE_FILTER =ResultFilter.createDefaultMessageFilter();/*** Cache for lists of thread id's. The default size is 16K, which should* let us hold about 2000 thread id's in memory at once. If you have a lot* of memory and very large forums, you may wish to make the size of this* cache considerably larger.*/protected Cache threadListCache = new Cache(16*1024, JiveGlobals.HOUR * 6);/*** Cache for lists of message id's. The default size is 8K, which should* let us hold about 100 message id's in memory at once. If you have a lot* of memory and very large forums, you may wish to make the size of this* cache considerably larger.*/protected Cache messageListCache = new Cache(8*1024, JiveGlobals.HOUR * 6);/*** Cache for thread counts. Default size is 256 bytes.*/protected Cache threadCountCache = new Cache(256, JiveGlobals.HOUR * 6);/*** Cache for message counts. Default size is 256 bytes.*/protected Cache messageCountCache = new Cache(256, JiveGlobals.HOUR * 6);private long id = -1;private String name;private String description;private java.util.Date creationDate;private java.util.Date modifiedDate;private int modDefaultThreadValue = 1;private int modDefaultMessageValue = 1;private int modMinThreadValue = 1 ;private int modMinMessageValue = 1;private Map properties;private DbForumFactory factory;private long[] popularThreads = null;private DbFilterManager filterManager;/*** Creates a new forum with the specified name and description.** @param name the name of the forum.* @param description the description of the forum.* @param factory the DbForumFactory the forum is a part of.*/protected DbForum(String name, String description, DbForumFactory factory) {this.id = SequenceManager.nextID(JiveGlobals.FORUM);this.name = name;this.description = description;long now = System.currentTimeMillis();creationDate = new java.util.Date(now);modifiedDate = new java.util.Date(now);this.factory = factory;insertIntoDb();properties = new Hashtable();init();}{// If the new name is the same as the current name, do nothing.if (this.name.equals(name)) {return;}// If a forum with the new name already exists, throw an exception.try {// Check to make sure that's it's not just a case change. If we// don't do this check, the db lookup may give us a false-positive// that the name is already in use.if (!this.name.toLowerCase().equals(name.toLowerCase())) {Forum forum = factory.getForum(name);// If we get here, the forum must already exist, so throw exception.throw new ForumAlreadyExistsException();}}catch (ForumNotFoundException e) { }// This is a special case since names of forums and id's are linked// and cached. Before changing the name of the forum to the new name,// we need to clear from cache to remove the old name to id mapping.this.name = name;saveToDb();// Finally, expire from cache again to revflect name change.factory.cacheManager.forumCache.remove(id);}{this.description = description;saveToDb();}{this.creationDate = creationDate;saveToDb();}throws UnauthorizedException{this.modifiedDate = modifiedDate;saveToDb();}{this.modDefaultMessageValue = value;saveToDb();}{this.modMinThreadValue = value;saveToDb();}{this.modMinMessageValue = value;saveToDb();}{if (LAZY_PROP_LOADING) {if (properties == null) {loadPropertiesFromDb();}}properties.put(name, value);savePropertiesToDb();}{if (LAZY_PROP_LOADING) {if (properties == null) {loadPropertiesFromDb();}}properties.remove(name);deletePropertyFromDb(name);}{boolean abortTransaction = false;// Add thread to forum table.Connection con = null;PreparedStatement pstmt = null;try {con = ConnectionManager.getTransactionConnection();try {pstmt = con.prepareStatement(ADD_THREAD);pstmt.setLong(1,id);pstmt.setLong(2,thread.getID());pstmt.executeUpdate();}finally {try {  pstmt.close();   }catch (Exception e) { e.printStackTrace(); }}// Now, insert the thread into the database. Depending on if this// method was called internally or not, the thread object might// be wrapped by a proxy or not.if (thread instanceof ForumThreadProxy) {ForumThreadProxy proxyThread = (ForumThreadProxy)thread;DbForumThread dbThread = (DbForumThread)proxyThread.getProxiedForumThread();dbThread.insertIntoDb(this, con);}else {DbForumThread dbThread = (DbForumThread)thread;dbThread.insertIntoDb(this, con);}// Check the moderation value of the thread. If the value is above// the visible threshold for the forum, then update the modified// date of the forum. Otherwise, we'll wait to update the modified// date to when the thread is moderated to be visible.if (thread.getModerationValue() >= getModerationMinThreadValue()) {updateModifiedDate(thread.getModifiedDate().getTime(), con);}}catch(Exception e) {e.printStackTrace();abortTransaction = true;}finally {ConnectionManager.closeTransactionConnection(con, abortTransaction);}// Expire the userMessageCountCache if the root message was not posted// anonymously.ForumMessage message = thread.getRootMessage();if (!message.isAnonymous()) {factory.userMessageCountCache.remove(message.getUser().getID());}}{boolean abortTransaction = false;Connection con = null;try {con = ConnectionManager.getTransactionConnection();DbForumThread dbThread = (DbForumThread)getThread(thread.getID());deleteThread(dbThread, con);}catch (Exception e) {e.printStackTrace();abortTransaction = true;}finally {ConnectionManager.closeTransactionConnection(con, abortTransaction);}// Now, delete thread and forum from cache}throws UnauthorizedException{// Ensure that thread belongs to this forumif (thread.getForum().getID() != this.id) {throw new IllegalArgumentException("The thread does not belong to this forum.");}// Read all messageIDs of the thread into an array so that we can expire// each of them later.ResultFilter ignoreModerationFilter = ResultFilter.createDefaultMessageFilter();ignoreModerationFilter.setModerationRangeMin(Integer.MIN_VALUE+1);LongList messageIDList = new LongList();Iterator iter = thread.messages(ignoreModerationFilter);while (iter.hasNext()) {long messageID = ((ForumMessage)iter.next()).getID();messageIDList.add(messageID);}// Modify the SQL record. Only the thread table has information about// forumID, so we only need to modify that record. The message records// underneath the thread can be left alone.boolean abortTransaction = false;Connection con = null;try {con = ConnectionManager.getTransactionConnection();PreparedStatement pstmt = null;try {pstmt = con.prepareStatement(ADD_THREAD);pstmt.setLong(1,forum.getID());pstmt.setLong(2,thread.getID());pstmt.executeUpdate();pstmt.close();// Move all messages in thread to new forum.pstmt = con.prepareStatement(MOVE_MESSAGES);pstmt.setLong(1,forum.getID());pstmt.setLong(2,thread.getID());pstmt.executeUpdate();}finally {try {  pstmt.close(); }catch (Exception e) { e.printStackTrace(); }}}catch( SQLException sqle ) {sqle.printStackTrace();abortTransaction = true;return;}finally {ConnectionManager.closeTransactionConnection(con, abortTransaction);}DatabaseCacheManager cacheManager = factory.cacheManager;SearchManager searchManager = factory.getSearchManager();// Update the last modified date of both forums to the most recently// updated thread (this may have changed during the move thread operation).ResultFilter newestThreadFilter = ResultFilter.createDefaultThreadFilter();newestThreadFilter.setNumResults(1);Iterator threadIter = threads(newestThreadFilter);if (threadIter.hasNext()) {ForumThread newestThread = (ForumThread)threadIter.next();if (newestThread != null) {setModifiedDate(newestThread.getModifiedDate());}}// Updated modified date of other forum.newestThreadFilter = ResultFilter.createDefaultThreadFilter();newestThreadFilter.setNumResults(1);threadIter = forum.threads(newestThreadFilter);if (threadIter.hasNext()) {ForumThread newestThread = (ForumThread)threadIter.next();if (newestThread != null) {forum.setModifiedDate(newestThread.getModifiedDate());}}// Remove thread from cache.// Loop through all messages in thread and delete from cache, reset// entry in the search index to new thread.long [] messageIDArray = messageIDList.toArray();for (int i=0; i<messageIDArray.length; i++) {long messageID = messageIDArray[i];cacheManager.messageCache.remove(messageID);try {ForumMessage message = thread.getMessage(messageID);searchManager.removeFromIndex(message);searchManager.addToIndex(message);}catch (ForumMessageNotFoundException e)  { }}}{String query = getThreadListSQL(resultFilter, true);CacheableInt count = (CacheableInt)threadCountCache.get(query);// If already in cache, return the count.if (count != null) {return count.getInt();}// Otherwise, we have to load the count from the db.else {int threadCount = 0;Connection con = null;Statement stmt = null;try {con = ConnectionManager.getConnection();stmt = con.createStatement();ResultSet rs = stmt.executeQuery(query);rs.next();threadCount = rs.getInt(1);}catch( SQLException sqle ) {sqle.printStackTrace();}finally {try {  stmt.close(); }catch (Exception e) { e.printStackTrace(); }try {  con.close();   }catch (Exception e) { e.printStackTrace(); }}// Add the thread count to cachereturn threadCount;}}{String query = getMessageListSQL(resultFilter, true);CacheableInt count = (CacheableInt)messageCountCache.get(query);// If already in cache, return the count.if (count != null) {return count.getInt();}// Otherwise, we have to load the count from the db.else {int messageCount = 0;Connection con = null;Statement stmt = null;try {con = ConnectionManager.getConnection();stmt = con.createStatement();ResultSet rs = stmt.executeQuery(query);rs.next();messageCount = rs.getInt(1);}catch( SQLException sqle ) {sqle.printStackTrace();}finally {try {  stmt.close(); }catch (Exception e) { e.printStackTrace(); }try {  con.close();   }catch (Exception e) { e.printStackTrace(); }}// Add the thread count to cachereturn messageCount;}}public int hashCode() {return (int)id;}public boolean equals(Object object) {if (this == object) {return true;}if (object != null && object instanceof DbForum) {return id == ((DbForum)object).getID();}else {return false;}}/*** Updates the modified date. It accepts a Connection so that it can* participate in trasactions.*/throws SQLException{this.modifiedDate.setTime(date);PreparedStatement pstmt = null;try {pstmt = con.prepareStatement(UPDATE_FORUM_MODIFIED_DATE);pstmt.setString(1, StringUtils.dateToMillis(modifiedDate));pstmt.setLong(2, id);pstmt.executeUpdate();}finally {try {  pstmt.close(); }catch (Exception e) { e.printStackTrace(); }}}/*** Does the actual work of deleteting a thread. It accepts a Connection so* that it can participate in transactions.*/throws SQLException, ForumMessageNotFoundException{// Delete all messages from the thread. Deleting the root// message will delete all submessages.thread.deleteMessage(thread.getRootMessage(), con);// Delete any watches on the thread.factory.watchManager.deleteThreadWatches(thread, con);// Now delete thread db entry and thread properties.PreparedStatement pstmt = null;try {pstmt = con.prepareStatement(DELETE_THREAD_PROPERTIES);pstmt.setLong(1,thread.getID());pstmt.execute();pstmt.close();pstmt = con.prepareStatement(DELETE_THREAD);pstmt.setLong(1,thread.getID());pstmt.execute();}finally {try {  pstmt.close(); }catch (Exception e) { e.printStackTrace(); }}}/*** Returns a block of threadID's from a query and performs transparent* caching of those blocks. The two parameters specify a database query* and a startIndex for the results in that query.** @param query the SQL thread list query to cache blocks from.* @param startIndex the startIndex in the list to get a block for.*/{// First, discover what block number the results will be in.int blockID = startIndex / THREAD_BLOCK_SIZE;int blockStart = blockID * THREAD_BLOCK_SIZE;// Now, check cache to see if the block is already cached. The key is// simply the query plus the blockID.String key = query + blockID;CacheableLongArray longArray = (CacheableLongArray)threadListCache.get(key);//If already in cache, return the block.if (longArray != null) {/*** The actual block may be smaller than THREAD_BLOCK_SIZE. If that's* the case, it means two things:*  1) We're at the end boundary of all the results.*  2) If the start index is greater than the length of the current*     block, than there aren't really any results to return.*/long [] threads = longArray.getLongArray();if (startIndex >= blockStart + threads.length) {// Return an empty arrayreturn EMPTY_BLOCK;}else {return threads;}}// Otherwise, we have to load up the block from the database.else {LongList threadsList = new LongList(THREAD_BLOCK_SIZE);Connection con = null;Statement stmt = null;try {con = ConnectionManager.getConnection();stmt = con.createStatement();// Set the maxium number of rows to end at the end of this block.ConnectionManager.setMaxRows(stmt, THREAD_BLOCK_SIZE * (blockID+1));ResultSet rs = stmt.executeQuery(query);// Grab THREAD_BLOCK_ROWS rows at a time.ConnectionManager.setFetchSize(rs, THREAD_BLOCK_SIZE);// Many JDBC drivers don't implement scrollable cursors the real// way, but instead load all results into memory. Looping through// the results ourselves is more efficient.for (int i=0; i<blockStart; i++) {rs.next();}// Keep reading results until the result set is exaughsted or// we come to the end of the block.int count = 0;while (rs.next() && count < THREAD_BLOCK_SIZE) {threadsList.add(rs.getLong(1));count++;}}catch( SQLException sqle ) {sqle.printStackTrace();}finally {try {  stmt.close(); }catch (Exception e) { e.printStackTrace(); }try {  con.close();   }catch (Exception e) { e.printStackTrace(); }}long [] threads = threadsList.toArray();// Add the thread block to cachethreadListCache.add(key, new CacheableLongArray(threads));/*** The actual block may be smaller than THREAD_BLOCK_SIZE. If that's* the case, it means two things:*  1) We're at the end boundary of all the results.*  2) If the start index is greater than the length of the current*     block, than there aren't really any results to return.*/if (startIndex >= blockStart + threads.length) {// Return an empty arrayreturn EMPTY_BLOCK;}else {return threads;}}}/*** Returns a block of messageID's from a query and performs transparent* caching of those blocks. The two parameters specify a database query* and a startIndex for the results in that query.** @param query the SQL message list query to cache blocks from.* @param startIndex the startIndex in the list to get a block for.*/{// First, discover what block number the results will be in.int blockID = startIndex / MESSAGE_BLOCK_SIZE;int blockStart = blockID * MESSAGE_BLOCK_SIZE;// Now, check cache to see if the block is already cached. The key is// simply the query plus the blockID.String key = query + blockID;CacheableLongArray longArray = (CacheableLongArray)messageListCache.get(key);// If already in cache, return the block.if (longArray != null) {/*** The actual block may be smaller than MESSAGE_BLOCK_SIZE. If that's* the case, it means two things:*  1) We're at the end boundary of all the results.*  2) If the start index is greater than the length of the current*     block, than there aren't really any results to return.*/long [] messages = longArray.getLongArray();if (startIndex >= blockStart + messages.length) {// Return an empty arrayreturn EMPTY_BLOCK;}else {return messages;}}// Otherwise, we have to load up the block from the database.else {LongList messagesList = new LongList(MESSAGE_BLOCK_SIZE);Connection con = null;Statement stmt = null;try {con = ConnectionManager.getConnection();stmt = con.createStatement();// Set the maxium number of rows to end at the end of this block.ConnectionManager.setMaxRows(stmt, MESSAGE_BLOCK_SIZE * (blockID+1));ResultSet rs = stmt.executeQuery(query);// Grab MESSAGE_BLOCK_ROWS rows at a time.ConnectionManager.setFetchSize(rs, MESSAGE_BLOCK_SIZE);// Many JDBC drivers don't implement scrollable cursors the real// way, but instead load all results into memory. Looping through// the results ourselves is more efficient.for (int i=0; i<blockStart; i++) {rs.next();}// Keep reading results until the result set is exaughsted or// we come to the end of the block.int count = 0;while (rs.next() && count < MESSAGE_BLOCK_SIZE) {messagesList.add(rs.getLong(1));count++;}}catch( SQLException sqle ) {sqle.printStackTrace();}finally {try {  stmt.close(); }catch (Exception e) { e.printStackTrace(); }try {  con.close();   }catch (Exception e) { e.printStackTrace(); }}long [] messages = messagesList.toArray();// Add the message block to cachemessageListCache.add(key, new CacheableLongArray(messages));/*** The actual block may be smaller than MESSAGE_BLOCK_SIZE. If that's* the case, it means two things:*  1) We're at the end boundary of all the results.*  2) If the start index is greater than the length of the current*     block, than there aren't really any results to return.*/if (startIndex >= blockStart + messages.length) {//Return an empty arrayreturn EMPTY_BLOCK;}else {return messages;}}}}
 
维护缓存与原对象的一致性
/*** $RCSfile: DbForumThread.java,v $* $Revision: 1.23 $* $Date: 2001/09/23 18:54:08 $** Copyright (C) 1999-2001 CoolServlets, Inc. All rights reserved.** This software is the proprietary information of CoolServlets, Inc.* Use is subject to license terms.*/{// Get the underlying DbForumMessage object.DbForumMessage dbMessage = null;if (newMessage instanceof ForumMessageProxy) {ForumMessageProxy proxyMessage = (ForumMessageProxy)newMessage;dbMessage = (DbForumMessage)proxyMessage.getProxiedForumMessage();}else {dbMessage = (DbForumMessage)newMessage;}DbForum dbForum = null;boolean abortTransaction = false;Connection con = null;try {con = ConnectionManager.getTransactionConnection();// Insert the message into the database.dbMessage.insertIntoDb(this, parentMessage.getID(), con);// Check the moderation value of the message. If the value is above// the visible threshold for the forum, then update the modified// date of the thread and forum. Otherwise, we'll wait to update// the modified dates to when the message is moderated to be visible.dbForum = factory.cacheManager.forumCache.get(forumID);if (newMessage.getModerationValue() >=dbForum.getModerationMinMessageValue()){long modifiedDate = newMessage.getModifiedDate().getTime();updateModifiedDate(modifiedDate, con);dbForum.updateModifiedDate(modifiedDate, con);}}catch( Exception e ) {e.printStackTrace();abortTransaction = true;}finally {ConnectionManager.closeTransactionConnection(con, abortTransaction);}// Expire the userMessageCountCache if this message was not posted// anonymously.if (!newMessage.isAnonymous()) {factory.userMessageCountCache.remove(newMessage.getUser().getID());}// If above the moderation threshold...if (newMessage.getModerationValue() >=dbForum.getModerationMinMessageValue()){// Notify the watch manager that the thread has been updated.factory.watchManager.notifyWatches(this);// Notify the gateway manager of a new message.dbForum.getGatewayManager().exportData(dbMessage);}}}

转载于:https://www.cnblogs.com/cxccbv/archive/2009/04/12/1434070.html

Jive学习(四)--Jive缓存相关推荐

  1. git使用学习四、git add忽略指定文件夹与文件

    git使用学习四.git add忽略指定文件夹与文件 前言 .gitignore 解决提前git commit导致.gitignore失效的问题 解决错误添加数据集导致.git文件夹太大的问题 前言 ...

  2. C#多线程学习(四) 多线程的自动管理(线程池) (转载系列)——继续搜索引擎研究...

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  3. python学习四(处理数据)

    python学习四(处理数据) head first python中的一个数据处理的例子 有四个U10选手的600米成绩,请取出每个选手跑的最快的3个时间.以下是四位选手的9次成绩 James 2-3 ...

  4. PyTorch框架学习四——计算图与动态图机制

    PyTorch框架学习四--计算图与动态图机制 一.计算图 二.动态图与静态图 三.torch.autograd 1.torch.autograd.backward() 2.torch.autogra ...

  5. Docker学习四:Docker 网络

    前言 本次学习来自于datawhale组队学习: 教程地址为: https://github.com/datawhalechina/team-learning-program/tree/master/ ...

  6. (转)SpringMVC学习(四)——Spring、MyBatis和SpringMVC的整合

    http://blog.csdn.net/yerenyuan_pku/article/details/72231763 之前我整合了Spring和MyBatis这两个框架,不会的可以看我的文章MyBa ...

  7. 算法学习四:算法性能分析理论基础——函数增长与渐进分析

    算法学习四:算法性能分析理论基础--函数增长与渐进分析 在算法性能分析过程中,特别是在算法运行效率分析中,我们经常使用渐渐分析法,它使我们在分析算法性能时不必纠结于不同硬件平台的差异性,着重考虑算法的 ...

  8. Tensorflow学习四---高阶操作

    Tensorflow学习四-高阶操作 Merge and split 1.tf.concat 拼接 a = tf.ones([4,32,8]) b = tf.ones([2,32,8]) print( ...

  9. [jQuery学习系列四 ]4-Jquery学习四-事件操作

    [jQuery学习系列四 ]4-Jquery学习四-事件操作 前言: 今天看知乎偶然看到中国有哪些类似于TED的节目, 回答中的一些推荐我给记录下来了, 顺便也在这里贴一下: 一席 云集 听道 推酷 ...

  10. AVI音视频封装格式学习(四)——linux系统C语言AVI格式音视频封装应用

    拖了很久的AVI音视频封装实例,花了一天时间终于调完了,兼容性不是太好,但作为参考学习使用应该没有问题.RIFF和AVI以及WAV格式,可以参考前面的一些文章.这里详细介绍将一个H264视频流和一个2 ...

最新文章

  1. wp7中的fill_parent
  2. 如何理解subplot绘制不规则子图的参数设置
  3. OpenCASCADE:构建配置文件
  4. Framework 1.0/1.1中NotifyIcon的不足
  5. moodle架构分析---表现层的设计(二)
  6. Windows 10 开启 Num Lock
  7. OpenGL基础56:OpenGL简单调试
  8. Android-两种方式实现走马灯效果
  9. 中国智慧建造行业投资前景分析与项目投资建议报告2021-2027年版
  10. oracle的临时表
  11. surfer10 地学计算机制图 pdf,Surfer 10地学计算机制图
  12. IDEA maven process terminated
  13. macos 软件清单
  14. OpenCV教程(5)函数整理
  15. java计算机毕业设计淮安城市开放大学实习实训管理系统源码+mysql数据库+系统+lw文档+部署
  16. Linux命令:lp
  17. Apache Hadoop集群设置示例(带虚拟机)
  18. 程序员接私活一定要知道的事情,我走的弯路你们都别走了
  19. php汉字验证码,PHP中实现中文汉字验证码 源代码
  20. Google推出免费中秋祝福短信服务

热门文章

  1. robotframework ie浏览器中click button不可用_RobotFramework自动化Selenium2Library库常用关键字...
  2. eclipse新建一个java_Eclipse中新建一个java源文件的步骤
  3. linux内核丢弃udp报文,c++ Linux UDP数据包丢失的原因
  4. html5 list css,使用HTML5的classList属性操做CSS类
  5. RandomAccessFile r rw rws rwd之间的区别
  6. nginx 只允许/不允许 其他域名直接访问
  7. 为什么要重写hashcode( )和equals( )?
  8. 差速移动机器人之轨迹跟踪
  9. 与班尼特·胡迪一起找简单规律(HZOJ-2262)
  10. java课堂疑问解答与思考1