go4it

just do it

2009年7月9日

JdonFramework的缓存设计

JdonFramework的缓存设计

内嵌对象(Embedded Object)缓存设计

请看下面这段代码:

public class Category extends Model{

String Id;

Product product; //内嵌包含了一个Product对象

}

Category这个Model内嵌了Product这个Model,属于一种关联关系。

目前Jdon框架提供的缺省缓存是扁平式,不是嵌入式或树形的(当然也可以使用JbossCache等树形缓存替代),因此,一个ModelB对象(如Product)被嵌入到另外一个ModelA对象(如Category)中,那么这个ModelB对象随着ModelA对象被Jdon框架一起缓存。

假设实现ModelA已经在缓存中,如果客户端从缓存直接获取ModelB,缓存由于不知道ModelB被缓存到ModelA中(EJB3实体Bean中是通过Annotation标注字段),那么缓存可能告知客户端没有ModelB缓存。那么有可能客户端会再向缓存中加一个新的ModelB,这样同一个ID可能有两份ModelB,当客户端直接调用的ModelB进行其中字段更新,那么包含在ModelA中的ModelB可能未被更新(因为ModelA没有字段更新)。

有两种解决方案:

第一.最直接方式,通过手工清除ModelA缓存方式来更新,或者耐心等待ModelA缓存自动更新。手工清除缓存见下章。

注意:下面这种做法将也会导致不一致现象发生:

在DAO层读取数据库。生成ModelA时,直接读取数据库将ModelB充填。

第二.在进行ModelA和ModelB的相关操作服务设计时,就要注意保证这两种情况下ModelB指向的都是同一个。如下图:

clip_image002

为达到这个目的,只要在Service层和Dao层之间加一个缓存Decorator,服务层向Dao层调用的任何Model对象都首先经过缓存检查,缓存中保存的ModelA中的ModelB是一个只有ModelB主键的空对象,在服务层getModelA方法中,再对ModelA的ModelB进行充实,填满,根据ModelA中的ModelB的主键,首先再到缓存查询,如果有,则将缓存中ModelB充填到ModelA的ModelB中,这样上图目的就可以实现了。

相关实现可参考JiveJdon 3.0的代码,Forum/ForumMessage都属于这种情况。

Model缓存使用

Jdon框架通过两种方式使用Model缓存:

CRUD框架内部使用,如果你使用Jdon框架提供的CRUD功能,那么其已经内置Model缓存,而且会即时清除缓存。

通过CacheInterceptor缓存拦截器,如果你不使用Jdon框架的CRUD功能,缓存拦截器功能将激活,在向Service获取Model之前,首先查询当前缓存器中是否存在该Model,如果有从缓存中获取。当你的Model中数值更改后,必须注意自己需要手工清除该Model缓存,清除方法如下介绍。

Jdon框架除了提供单个Model缓存外,还在持久层Dao层提供了查询条件的缓存,例如如果你是根据某个字段按照如何排列等条件进行查询,这个查询条件将被缓存,这样,下次如果有相同查询条件,该查询条件将被提出,与其相关的满足查询条件一些结果(如符合条件总数等)也将被从缓存中提出,节省翻阅数据库的性能开销。

手工访问缓存

在一般情况下,前台表现层通过getService方法访问服务层一个服务,然后通过该服务Service获得一个Model,这种情况Jdon框架的缓存拦截器将自动首先从缓存读取。

但是,有时我们在服务层编码时,需要获得一个Model,在这种情况下,Jdon框架的缓存拦截器就不起作用,这时可能我们需要手工访问缓存。

因为所有服务类POJO都属于Jdon框架的容器内部组件,这实际是在容器内访问容器组件的问题。

使用com.jdon.container.finder. ContainerCallback,同时,该服务POJO类以ContainerCallback作为构造参数,当该POJO服务类注册到容器中时,容器的Ioc特性将会找到事先以及注册的ContainerCallback类。

通过ContainerCallback获得ContainerWrapper容器实例,然后通过ContainerWrapper下面方法:

public Object lookup(String name);

从容器中获得container.xml中注册的组件实例,这种方法每次调用获得的是同一个实例,相当于单例方式获得。

ModelManager modelManager =

(ModelManager)containerWrapper. lookup (“modelManager”);

其中“modelManager”字符串名称是从Jdon框架的jdonFramework.jar包中META-INF的container.xml中查询获知的。

获得ModelManager后,我们基本可以访问Model有关的功能安排,如ModelManager的getCache方法。

下节的手工清除缓存中,我们是通过WebAppUtil.getComponentInstance获得ModelManager实例,这是一种从容器外获得容器组件的方式,本节介绍从容器内获得容器组件的方式,这两种方式可根据我们实际需要灵活使用,关键是弄清除你需要在哪里触发组件调用?

手工清除缓存

注意:手工清除缓存不是必要的,因为缓存中对象是有存在周期的,这在Cache.xml中设置的,过一段时间缓存将自动清除那些超过配置时间不用的对象,这样你修改的数据将被从数据库重新加载。如果你等不及这些内在变化,可以手工处理:

有两种情况需要手工清除缓存,首先,在持久层的Dao类中,总是需要手工清除查询条件的缓存,只要在相应的增删改方法中调用PageIteratorSolver的clearCache方法既可。

如果你不实行这种缓存清除,那么你更改一个Model数据或新增一个新的Model数据,你在批量查询时,将看不到任何变化:Model数据没有被修改;新的Model没有出现在查询页面中。

其次,单个Model缓存在不使用Jdon框架的CRUD功能下也必须手工清除,如果你使用1.2.3以后版本,可以调用com.jdon.strutsutil.util. ModelUtil类的clearModelCache方法,该方法一般是Action中调用后台增删改服务之前被激活调用:

注意,手工清除Model缓存代码关键是:

modelManager.removeCache(keyValue);

keyValue是Model的主键值,例如User的主键userId值是”2356”,那么keyValue就是”2356”。简化代码如下:

//获得ModelManager实例

ModelManager modelManager = (ModelManager)
WebAppUtil.getComponentInstance(ComponentKeys.MODEL_MANAGER, request);

modelManager.removeCache(keyValue);

上面代码是在容器外访问获得ModelManager,使用上节容器内访问组件方式也可以获得ModelManager;前者适合在表现层使用;后者适合在服务层使用。

最后,介绍一下清除全部缓存的方式,调用ModelManager的clearCache方法(Jdon框架1.3以上版本),这样实际上将整个Jdon框架缓存全部清零。

前面两种清除缓存方式前提是首先获得ModelManager,特别是服务层需要清除缓存时,需要以容器内访问组件方式获得ModelManager,这只适合POJOService构成的服务层,如果我们使用EJB的Session Bean作为服务层实现,这时当前版本的Jdon框架容器不会在EJB容器中加载,因此,在Session Bean中无法访问到容器,无法获得ModelManager了,在这种情况下,可以通过设置Model的setModified属性为True,表示该Model已经修改更新,这样当表现层获取该Model时,Jdon框架缓存拦截器拦截时,发现该Model已经被修改,也就不会从缓存中获取。

Model还有另外一个方法setCacheble,当设置为false时,该Model将不会被保存到缓存中。如果你不希望某个Model被框架自动存入缓存,那么使用此功能,setCacheble和setModified区别是,前者一旦设置为真,相当于缓存失效,以后再也不能用缓存;而后者则是当前Model表示被修改过,这样当有任何再次(限一次)试图从缓存中读取这个Model时,都被会阻挡,从而可直接从数据库获得,然后再保存到缓存中,这样缓存中的Model数据就是新鲜的了。

明白Jdon框架setModified这个神奇作用,当你设置一个Model的setModified为真,那么你再读取缓存时,Jdon框架内部将忽略你这个读取,返回一个null;这样你就根据返回是否为空,再从数据库直接获得,获得后,别忘记再保存到缓存中,已覆盖前次修改的旧数据,保证以后每次从缓存中读取的都是新鲜数据。

=总结如下:手工缓存清除共有两种方式:

直接操作缓存,从缓存中清除;

如果无法操作到缓存体系,那么设置Model的setModified。

posted @ 2009-07-09 20:58 cxccbv 阅读(146) 评论(0) 编辑

LForum与Ehcache(五)页面缓存

因为LForum使用的是freemaker,好像没看到页面缓存,好像也没关联。

但可以参考http://hain.javaeye.com/blog/152806

细谈Ehcache页面缓存的使用

/**

*作者:张荣华

*日期:2007-9-30

**/

关于缓存的话题,在坛子里已经有很多讨论,简单的来说,如果一个应用中80%的时间内都在访问20%的数据,那么,这时候就应该使用缓存了。这个和长尾理论正好相悖,其实也不是相悖,只是不同的理论使用的场景不同。在80/20原则生效的地方,我们都应该考虑是否可以使用缓存。但即使是这样,缓存也有不同的用法,举个例子,一个网站的首页估计是被访问的次数最多的,我们可以考虑给首页做一个页面缓存,而如果在某个页面上,比如说javaeye的java版区只有前几个页面是访问最频繁的,(假设javaeye是使用hibernate,当然这只是假设,我们都知道javaeye是使用ror开发的)那么我们就可以考虑给java版区的record做二级缓存了,因为二级缓存中是按照对象的id来保存的,所以应该来说这前面几页使用的对象会一直存在于缓存之中(如何使用hibernate的二级缓存坛子上也有介绍)。由此可见不同的页面的缓存策略有可能有天壤之别。

本文的目的就是上面所讲的两种情况之一,页面缓存。毫无疑问,几乎所有的网站的首页都是访问率最高的,而首页上的数据来源又是非常广泛的,大多数来自不同的对象,而且有可能来自不同的db,所以给首页做缓存是一个不错的主意,那么主页的缓存策略是什么样子的呢,我认为应该是某个固定时间之内不变的,比如说2分钟更新一次。那么这个缓存应该做在什么地方呢,让我们来看一下,假设您的应用的结构是page-filter-action-service-dao-db,这个过程中的-的地方都是可以做缓存的地方,根据页面缓存的特征,应该把页面缓存做到尽量靠近客户的地方,就是在page和filter之间,这样的优点就是第一个用户请求之后,页面被缓存,第二个用户再来请求的时候,走到filter这个请求就结束了,无需再走后面的action-service-dao-db。带来的好处是服务器压力的减低和客户段页面响应速度的加快。

那么我们来看一下如何使用ehcache做到这一点。

在使用ehcache的页面缓存之前,我们必须要了解ehcache的几个概念,

1 timeToIdleSeconds,多长时间不访问该缓存,那么ehcache就会清除该缓存。

2 timeToLiveSeconds,缓存的存活时间,从开始创建的时间算起。

看到这里,我们知道,首页的页面缓存的存活时间,我们定的是2分钟,那么也就是说我们的timeToLiveSeconds应该设置为120,同时我们的timeToIdleSeconds最好也设置为2分钟,或者大于2分钟。我们来看一下下面这个配置,这个配置片段应该放到ehcache.xml中:

<cache name="SimplePageCachingFilter"

maxElementsInMemory="10"

maxElementsOnDisk="10"

eternal="false"

overflowToDisk="true"

diskSpoolBufferSizeMB="20"

timeToIdleSeconds="10"

timeToLiveSeconds="10"

memoryStoreEvictionPolicy="LFU"

/>

SimplePageCachingFilter是缓存的名字,maxElementsInMemory表示内存中SimplePageCachingFilter缓存中元素的最大数量为10,maxElementsOnDisk是指持久化该缓存的元素到硬盘上的最大数量也为10(),eternal=false意味着该缓存会死亡。overflowToDisk=true意思是表示当缓存中元素的数量超过限制时,就把这些元素持久化到硬盘,如果overflowToDisk是false,那么maxElementsOnDisk的设置就没有什么意义了。memoryStoreEvictionPolicy=LFU是指按照缓存的hit值来清除,也就是说缓存满了之后,新的对象需要缓存时,将会将缓存中hit值最小的对象清除出缓存,给新的对象腾出地方来了(文章最后有ehcache中自带的3种缓存清空策略的介绍)。

接着我们来看一下SimplePageCachingFilter的配置,

<filter>

<filter-name>indexCacheFilterfilter-name>

<filter-class>

net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter

filter-class>

filter>

<filter-mapping>

<filter-name>indexCacheFilterfilter-name>

<url-pattern>*index.actionurl-pattern>

filter-mapping>

就只需要这么多步骤,我们就可以给某个页面做一个缓存的,把上面这段配置放到你的web.xml中,那么当你打开首页的时候,你会发现,2分钟才会有一堆sql语句出现在控制台上。当然你也可以调成5分钟,总之一切都在控制中。

好了,缓存整个页面看上去是非常的简单,甚至都不需要写一行代码,只需要几行配置就行了,够简单吧,虽然看上去简单,但是事实上内部实现却不简单哦,有兴趣的话,大家可以看看SimplePageCachingFilter继承体系的源代码。

上面的配置针对的情况是缓存首页的全部,如果你只想缓存首页的部分内容时,你需要使用SimplePageFragmentCachingFilter这个filter。我们看一下如下片断:

<filter>

<filter-name>indexCacheFilterfilter-name>

<filter-class>

net.sf.ehcache.constructs.web.filter.SimplePageFragmentCachingFilter

filter-class>

filter>

<filter-mapping>

<filter-name>indexCacheFilterfilter-name>

<url-pattern>*/index_right.jspurl-pattern>

filter-mapping>

这个jsp需要被jsp:include到其他页面,这样就做到的局部页面的缓存。这一点貌似没有oscache的tag好用。

事实上在cachefilter中还有一个特性,就是gzip,也就是说缓存中的元素是被压缩过的,如果客户浏览器支持压缩的话,filter会直接返回压缩过的流,这样节省了带宽,把解压的工作交给了客户浏览器,如果客户的浏览器不支持gzip,那么filter会把缓存的元素拿出来解压后再返回给客户浏览器(大多数爬虫是不支持gzip的,所以filter也会解压后再返回流),这样做的优点是节省带宽,缺点就是增加了客户浏览器的负担(但是我觉得对当代的计算机而言,这个负担微乎其微)。

好了,如果你的页面正好也需要用到页面缓存,不防可以考虑一下ehcache,因为它实在是非常简单,而且易用。

总结:ehcache是一个非常轻量级的缓存实现,而且从1.2之后就支持了集群,目前的最新版本是1.3,而且是hibernate默认的缓存provider。虽然本文是介绍的是ehcache对页面缓存的支持,但是ehcache的功能远不止如此,当然要使用好缓存,对JEE中缓存的原理,使用范围,适用场景等等都需要有比较深刻的理解,这样才能用好缓存,用对缓存。

最后复习一下ehcache中缓存的3种清空策略:

1 FIFO,first in first out,这个是大家最熟的,先进先出,不多讲了

2 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。

2 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

posted @ 2009-07-09 18:10 cxccbv 阅读(201) 评论(0) 编辑

LForum与Ehcache(四)配置Hiberante

由于不大熟悉SpringSide3,找了半天没找到配置hiberante的xml文件,最后搜索一下,出来了:

在E:\MyEclipseWorkplace\LForum\webapp\WEB-INF\config\applicationContext.xml里面有一段:

<!-- Hibernate配置 -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="namingStrategy">
			<bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">DB_DIALECT</prop>
				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
				<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
				</prop>
				<prop key="hibernate.cache.provider_configuration_file_resource_path">/ehcache-hibernate.xml</prop>
			</props>
		</property>
		<property name="packagesToScan" value="com.javaeye.lonlysky.lforum.entity*,com.javaeye.lonlysky.lforum.entity.*" />
	</bean>
那么如何更新缓存?
缓存在Hibernate中主要有三个方面:一级缓存、二级缓存和查询缓存;一级缓存在Hibernate中对应的即为session范围的缓存,
也就是当 session关闭时缓存即被清除,一级缓存在Hibernate中是不可配置的部分;二级缓存在Hibernate中对应的即为 SessionFactory范围的缓存,
通常来讲SessionFactory的生命周期和应用的生命周期相同,所以可以看成是进程缓存或集群缓存,
二级缓存在Hibernate中是可以配置的:
可以通过class-cache配置类粒度级别的缓存(class-cache在class中数据发生任何变化的情况下自动更新),
同时也可通过collection-cache配置集合粒度级别的缓存(collection-cache仅在 collection中增加了元素或者删除了元素的情况下才自动更新,
也就是当collection中元素发生值的变化的情况下它是不会自动更新的),缓存自然会带来并发的访问问题,这个时候相应的就要根据应用来设置缓存所采用的事务隔离级别,
和数据库的事务隔离级别概念基本一样,没什么多介绍的, ^_^;查询缓存在Hibernate同样是可配置的,默认是关闭的,
可以通过设置cache.use_ query_cache为true来打开查询缓存。
所以在实体里面:
@Entity
@Table(name = "forums")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Forums implements java.io.Serializable {
.......
}
CacheConcurrencyStrategy.READ_WRITE,读写模式在更新缓存的时候会把缓存里面的数据换成一个锁,
其它事务如果去取相应的缓存数据,发现被锁了,直接就去数据库查询;
这里启用的是类粒度级别的缓存。
那么为什么里面会有手工removeCache的操作:
主要是那些缓存不是实体本身,而是其他一些东西或实体列表,如ForumListBoxOptions,
ForumList等。
 

posted @ 2009-07-09 17:45 cxccbv 阅读(154) 评论(0) 编辑

LForum与Ehcache(三)removeCache

package com.javaeye.lonlysky.lforum.service.admin;

import java.util.List;

import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springside.modules.orm.hibernate.SimpleHibernateTemplate;

import com.javaeye.lonlysky.lforum.GlobalsKeys;
import com.javaeye.lonlysky.lforum.cache.LForumCache;
import com.javaeye.lonlysky.lforum.comm.utils.Utils;
import com.javaeye.lonlysky.lforum.entity.forum.Admingroups;
import com.javaeye.lonlysky.lforum.entity.forum.Forums;
import com.javaeye.lonlysky.lforum.entity.forum.Moderators;
import com.javaeye.lonlysky.lforum.entity.forum.Users;
import com.javaeye.lonlysky.lforum.service.ForumManager;
import com.javaeye.lonlysky.lforum.service.UserManager;

/**
 * 后台论坛版块管理类
 * 
 * @author 黄磊
 *
 */
@Service
@Transactional
public class AdminForumManager {

	private static final Logger logger = LoggerFactory.getLogger(AdminForumManager.class);
	private SimpleHibernateTemplate<Forums, Integer> forumDAO;
	private SimpleHibernateTemplate<Moderators, Integer> moderatorDAO;

	@Autowired
	private ForumManager forumManager;

	@Autowired
	private UserManager userManager;

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		forumDAO = new SimpleHibernateTemplate<Forums, Integer>(sessionFactory, Forums.class);
		moderatorDAO = new SimpleHibernateTemplate<Moderators, Integer>(sessionFactory, Moderators.class);
	}

	/**
	 * 设置版块列表中层数(layer)和父列表(parentidlist)字段
	 */
	public void setForumslayer() {
		List<Forums> forumList = forumManager.getForumList();
		for (Forums forum : forumList) {
			int layer = 0;
			String parentidlist = "";
			int parentid = forum.getForums().getFid();

			//如果是(分类)顶层则直接更新数据库
			if (parentid == 0) {
				//				forum.setLayer(layer);
				//				forum.setParentidlist("0");
				//				forumManager.updateForum(forum);
				System.out.println("顶层直接更新数据库:" + forum.getFid());
				forumDAO
						.createQuery("update Forums set layer=?,parentidlist=? where fid=?", layer, "0", forum.getFid())
						.executeUpdate();
				continue;
			}

			do { //更新子版块的层数(layer)和父列表(parentidlist)字段
				int tmp = parentid;
				parentid = Utils.null2Int(forumDAO.findUnique("select forums.fid from Forums where fid=?", parentid));
				layer++;
				if (parentid != 0) {
					parentidlist = tmp + "," + parentidlist;
				} else {
					parentidlist = tmp + "," + parentidlist;
					//					forum.setLayer(layer);
					//					forum.setParentidlist(parentidlist.substring(0, parentidlist.length() - 1));
					//					forumManager.updateForum(forum);
					System.out.println("更新子版块:" + forum.getFid() + ",层数:" + layer + ",上级ID列表:"
							+ parentidlist.substring(0, parentidlist.length() - 1));
					forumDAO.createQuery("update Forums set layer=?,parentidlist=? where fid=?", layer,
							parentidlist.substring(0, parentidlist.length() - 1), forum.getFid()).executeUpdate();
					break;
				}
			} while (true);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("设置版块列表中层数(layer)和父列表(parentidlist)字段");
		}

	}

	public static String childNode = "0";

	/**
	 * 递归所有子节点并返回字符串
	 * @param correntfid 当前
	 * @return 子版块的集合,格式:1,2,3,4,
	 */
	@SuppressWarnings("unchecked")
	public String findChildNode(int correntfid) {
		synchronized (childNode) {

			List<Object> list = forumDAO.find("select fid from Forums where forums.fid=? order by displayorder asc",
					correntfid);

			childNode = childNode + "," + correntfid;

			if (list.size() > 0) {
				//有子节点
				for (Object object : list) {
					findChildNode(Utils.null2Int(object));
				}
			}
			if (logger.isDebugEnabled()) {
				logger.debug("递归所有子节点{}", childNode);
			}
			return childNode;
		}
	}

	/**
	 * 设置论坛字版数和显示顺序
	 */
	public void setForumsSubForumCountAndDispalyorder() {
		if (logger.isDebugEnabled()) {
			logger.debug("设置论坛字版数和显示顺序");
		}
		List<Forums> forumList = forumManager.getForumList();
		for (Forums forum : forumList) {
			int subcount = forumDAO.find("select fid from Forums where forums.fid=?", forum.getFid()).size();
			//			forumDAO.createQuery("update Forums set subforumcount=? where fid=?", subcount, forum.getFid())
			//					.executeUpdate();
			forum.setSubforumcount(subcount);
			forumManager.updateForum(forum);
			System.out.println("更新子版块数量为:" + forum.getSubforumcount() + ",板块:" + forum.getFid());
		}

		if (forumList.size() == 1)
			return;

		int displayorder = 1;
		String fidlist;
		for (Forums forum : forumManager.getForumList("forums.fid=0")) {
			if (forum.getForums().getFid() == 0) {
				childNode = "0";
				fidlist = ("," + findChildNode(forum.getFid())).replace(",0,", "");
				for (String fidstr : fidlist.split(",")) {
					//					forumDAO.createQuery("update Forums set displayorder=? where fid=?", displayorder,
					//							Utils.null2Int(fidstr)).executeUpdate();
					Forums forums = forumDAO.get(Utils.null2Int(fidstr));
					forums.setDisplayorder(displayorder);
					forumManager.updateForum(forums);
					System.out.println("更新板块:" + forums.getFid() + "显示顺序:" + forums.getDisplayorder());
					displayorder++;
				}

			}
		}
	}

	/**
	 * 移动论坛版块
	 * @param currentfid 当前论坛版块id
	 * @param targetfid 目标论坛版块id
	 * @param isaschildnode 是否作为子论坛移动
	 * @return
	 */
	@Transactional(readOnly = false)
	public void moveForumsPos(int currentfid, int targetfid, boolean isaschildnode) {
		if (logger.isDebugEnabled()) {
			logger.debug("移动板块,当前板块:{},目标板块:{},是否作为子论坛:" + isaschildnode, currentfid, targetfid);
		}
		//取得当前论坛版块的信息
		Forums currentForum = forumDAO.get(currentfid);

		//取得目标论坛版块的信息
		Forums targetForum = forumDAO.get(targetfid);

		//当前论坛版块带子版块时
		if (forumDAO.find("select fid from Forums where forums.fid=?", currentfid).size() > 0) {
			System.out.println("当前论坛版块带子版块");
			if (isaschildnode) { //作为论坛子版块插入
				//让位于当前论坛版块(分类)显示顺序之后的论坛版块全部加1(为新加入的论坛版块让位结果)
				forumDAO.createQuery("update Forums set displayorder=displayorder+1 where displayorder>=?",
						(targetForum.getDisplayorder() + 1)).executeUpdate();

				//更新当前论坛版块的相关信息
				currentForum.setForums(targetForum);
				currentForum.setDisplayorder(targetForum.getDisplayorder() + 1);
				//				forumDAO.createQuery("update Forums set forums.fid=?,displayorder=? where fid=?", targetForum.getFid(),
				//						targetForum.getDisplayorder() + 1, currentfid).executeUpdate();
			} else { //作为同级论坛版块,在目标论坛版块之前插入
				//让位于包括当前论坛版块显示顺序之后的论坛版块全部加1(为新加入的论坛版块让位结果)
				forumDAO.createQuery("update Forums set displayorder=displayorder+1 where displayorder>=? or fid=?",
						targetForum.getDisplayorder(), targetForum.getFid()).executeUpdate();
				//更新当前论坛版块的相关信息
				currentForum.setForums(targetForum.getForums());
				currentForum.setDisplayorder(targetForum.getDisplayorder());
				//				forumDAO.createQuery("update Forums set forums.fid=?,displayorder=? where fid=?",
				//						targetForum.getForums().getFid(), targetForum.getDisplayorder(), currentfid).executeUpdate();

			}

			//更新由于上述操作所影响的版块数和帖子数
			if (currentForum.getTopics_1() != 0 && currentForum.getTopics_1() > 0
					&& (currentForum.getPosts() != 0 && currentForum.getPosts() > 0)) {
				if (!currentForum.getParentidlist().trim().equals("")) {
					forumDAO.createQuery(
							"update Forums set topics_1=topics_1-" + currentForum.getTopics_1() + ",posts=posts-"
									+ currentForum.getPosts() + " where fid in("
									+ currentForum.getParentidlist().trim() + ")").executeUpdate();
				}
				if (!targetForum.getParentidlist().trim().equals("")) {
					forumDAO.createQuery(
							"update Forums set topics_1=topics_1+" + currentForum.getTopics_1() + ",posts=posts+"
									+ currentForum.getPosts() + " where fid in(" + targetForum.getParentidlist().trim()
									+ ")").executeUpdate();
				}
			}
		} else { //当前论坛版块不带子版
			System.out.println("当前论坛版块不带子版");
			//设置旧的父一级的子论坛数
			forumDAO.createQuery("update Forums set subforumcount=subforumcount-1 where fid=?",
					currentForum.getForums().getFid()).executeUpdate();

			//让位于当前节点显示顺序之后的节点全部减1 [起到删除节点的效果]
			if (isaschildnode) { //作为子论坛版块插入
				//更新相应的被影响的版块数和帖子数				
				if ((currentForum.getTopics_1() != 0) && (currentForum.getTopics_1() > 0)
						&& (currentForum.getPosts() != 0) && (currentForum.getPosts() > 0)) {
					forumDAO.createQuery(
							"update Forums set topics_1=topics_1-" + currentForum.getTopics_1() + ",posts=posts-"
									+ currentForum.getPosts() + " where fid in("
									+ currentForum.getParentidlist().trim() + ")").executeUpdate();
					if (!targetForum.getParentidlist().trim().equals("0")) {
						forumDAO.createQuery(
								"update Forums set topics_1=topics_1+" + currentForum.getTopics_1() + ",posts=posts+"
										+ currentForum.getPosts() + " where fid in("
										+ targetForum.getParentidlist().trim() + "," + targetfid + ")").executeUpdate();
					}
				}

				//让位于当前论坛版块显示顺序之后的论坛版块全部加1(为新加入的论坛版块让位结果)
				forumDAO.createQuery("update Forums set displayorder=displayorder+1 where displayorder>=?",
						targetForum.getDisplayorder() + 1).executeUpdate();

				//设置新的父一级的子论坛数
				targetForum.setSubforumcount(targetForum.getSubforumcount() + 1);
				//				forumDAO.createQuery("update Forums set subforumcount=subforumcount+1 where fid=?", targetfid)
				//						.executeUpdate();

				String parentidlist = null;
				if (targetForum.getParentidlist().trim().equals("0")) {
					parentidlist = targetfid + "";
				} else {
					parentidlist = targetForum.getParentidlist().trim() + "," + targetfid;
				}

				//更新当前论坛版块的相关信息
				currentForum.setForums(targetForum);
				currentForum.setLayer(targetForum.getLayer() + 1);
				currentForum.setPathlist(targetForum.getPathlist().trim() + "<a href=\"showforum.action?forumid="
						+ currentfid + "\">" + currentForum.getName().trim().replace("'", "''") + "</a>");
				currentForum.setParentidlist(parentidlist);
				currentForum.setDisplayorder(targetForum.getDisplayorder() + 1);
				System.out.println("作为子论坛版块插入:" + currentForum.getLayer());
				//				forumDAO.createQuery(
				//						"update Forums set forums.fid=?,layer=?,pathlist=?,parentidlist=?,displayorder=? where fid=?",
				//						targetForum.getFid(),
				//						targetForum.getLayer() + 1,
				//						targetForum.getPathlist().trim() + "<a href=\"showforum.action?forumid=" + currentfid + "\">"
				//								+ currentForum.getName().trim().replace("'", "''") + "</a>", parentidlist,
				//						targetForum.getDisplayorder() + 1, currentfid).executeUpdate();

			} else { //作为同级论坛版块,在目标论坛版块之前插入
				//更新相应的被影响的版块数和帖子数
				if ((currentForum.getTopics_1() != 0) && (currentForum.getTopics_1() > 0)
						&& (currentForum.getPosts() != 0) && (currentForum.getPosts() > 0)) {
					forumDAO.createQuery(
							"update Forums set topics_1=topics_1-" + currentForum.getTopics_1() + ",posts=posts-"
									+ currentForum.getPosts() + " where fid in("
									+ currentForum.getParentidlist().trim() + ")").executeUpdate();
					forumDAO.createQuery(
							"update Forums set topics_1=topics_1+" + currentForum.getTopics_1() + ",posts=posts+"
									+ currentForum.getPosts() + " where fid in(" + targetForum.getParentidlist().trim()
									+ ")").executeUpdate();

				}

				//让位于包括当前论坛版块显示顺序之后的论坛版块全部加1(为新加入的论坛版块让位结果)
				forumDAO.createQuery("update Forums set displayorder=displayorder+1 where displayorder>=? or fid=?",
						targetForum.getDisplayorder() + 1, targetForum.getFid()).executeUpdate();

				//设置新的父一级的子论坛数
				forumDAO.createQuery("update Forums set subforumcount=subforumcount+1 where fid=?",
						targetForum.getForums().getFid()).executeUpdate();
				String parentpathlist = Utils.null2String(forumDAO.findUnique(
						"select pathlist from Forums where fid=?", targetForum.getForums().getFid()));

				//更新当前论坛版块的相关信息
				currentForum.setForums(targetForum.getForums());
				currentForum.setLayer(targetForum.getLayer());
				currentForum.setPathlist(parentpathlist + "<a href=\"showforum.action?forumid=" + currentfid + "\">"
						+ currentForum.getName().trim() + "</a>");
				currentForum.setParentidlist(targetForum.getParentidlist().trim());
				currentForum.setDisplayorder(targetForum.getDisplayorder());
				//				forumDAO.createQuery(
				//						"update Forums set forums.fid=?,layer=?,pathlist=?,parentidlist=?,displayorder=? where fid=?",
				//						targetForum.getForums().getFid(),
				//						targetForum.getLayer(),
				//						parentpathlist + "<a href=\"showforum.action?forumid=" + currentfid + "\">"
				//								+ currentForum.getName().trim() + "</a>", targetForum.getParentidlist().trim(),
				//						targetForum.getDisplayorder(), currentfid).executeUpdate();
			}
		}
		forumManager.updateForum(currentForum);
		forumManager.updateForum(targetForum);
	}

	/**
	 * 移动论坛版块
	 * @param currentfid 当前论坛版块id
	 * @param targetfid 目标论坛版块id
	 * @param isaschildnode 是否作为子论坛移动
	 * @return
	 */
	public boolean movingForumsPos(int currentfid, int targetfid, boolean isaschildnode) {
		moveForumsPos(currentfid, targetfid, isaschildnode);
		setForumslayer();
		setForumsSubForumCountAndDispalyorder();
		setForumsPathList();

		LForumCache.getInstance().removeCache("ForumListBoxOptions");
		LForumCache.getInstance().removeCache("ForumList");
		return true;
	}

	/**
	 * 设置版块列表中论坛路径(pathlist)字段
	 */
	@Transactional(readOnly = false)
	public void setForumsPathList() {
		List<Forums> forumList = forumManager.getForumList();
		for (Forums forum : forumList) {
			String pathlist = "";

			if (forum.getParentidlist().trim().equals("0")) {

				pathlist = "<a href=\"showforum.action?forumid=" + forum.getFid() + "\">" + forum.getName().trim()
						+ "</a>";
			} else {
				for (String parentid : forum.getParentidlist().trim().split(",")) {
					if (!parentid.trim().equals("")) {
						Forums tmpForums = forumDAO.get(Utils.null2Int(parentid));
						if (tmpForums != null) {

							pathlist += "<a href=\"showforum.action?forumid=" + tmpForums.getFid() + "\">"
									+ tmpForums.getName().trim() + "</a>";
						}
					}
				}

				pathlist += "<a href=\"showforum.action?forumid=" + forum.getFid() + "\">" + forum.getName().trim()
						+ "</a>";
			}

			forum.setPathlist(pathlist);
			forumManager.updateForum(forum);
			if (logger.isDebugEnabled()) {
				logger.debug("设置论坛 {} 路径为 {}", forum.getFid(), forum.getPathlist());
			}
		}
	}

	/**
	 * 获得用于树形的板块列表
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<Object[]> getForumTree() {
		return forumDAO.find("select fid,name from Forums");
	}

	/**
	 * 在当前节点之后加入同级论坛时的displayorder字段值
	 * @param minDisplayOrder
	 */
	public void updateForumsDisplayOrder(int minDisplayOrder) {
		forumDAO.createQuery("update Forums set displayorder=displayorder+1 where displayorder>?", minDisplayOrder);
	}

	/**
	 * 向版块列表中插入新的版块信息
	 * @param foruminfo
	 * @return
	 */
	public String insertForumsInf(Forums foruminfo) {
		forumDAO.save(foruminfo);

		setForumsPathList();
		LForumCache.getInstance().removeCache("ForumListBoxOptions");
		LForumCache.getInstance().removeCache("ForumList");
		LForumCache.getInstance().removeCache("HotForumList");
		LForumCache.getInstance().removeCache("ForumHotTopicList");
		LForumCache.getInstance().removeCache("ForumNewTopicList");
		if (logger.isDebugEnabled()) {
			logger.debug("添加新的论坛板块 {} 成功", foruminfo.getFid());
		}
		return setForumsModerators(foruminfo.getFid(), foruminfo.getForumfields().getModerators(), foruminfo
				.getInheritedmod());

	}

	/**
	 * 设置指定论坛版块版主
	 * @param fid 指定的论坛版块id
	 * @param moderators 相关要设置的版主名称(注:用","号分割)
	 * @param inheritedmod 是否使用继承选项 1为使用  0为不使用
	 * @return
	 */
	public String setForumsModerators(int fid, String moderators, int inheritedmod) {
		deleteModeratorByFid(fid);

		//使用继承机制时
		if (inheritedmod == 1) {
			int parentid = fid;
			String parendidlist = "-1";
			while (true) {
				parentid = Utils.null2Int(forumDAO.findUnique(
						"select forums.fid from Forums where inheritedmod=1 and fid=?", fid));
				if (parentid == -1) {
					break;
				}
				if (parentid == 0) {
					break;
				}

				parendidlist = parendidlist + "," + parentid;

			}

			int count = 1;
			for (Users user : getUidModeratorByFid(parendidlist)) {
				addModerator(user, fid, count, 1);
				count++;
			}
		}

		insertForumsModerators(fid, moderators, 1, 0);

		return updateUserInfoWithModerator(moderators);
	}

	/**
	 * 更新当前已设置为指定版块版主的相关用户信息
	 * @param moderators 相关要设置的版主名称(注:用","号分割)
	 * @return 返回不存在用户的字符串
	 */
	public String updateUserInfoWithModerator(String moderators) {
		moderators = moderators == null ? "" : moderators;
		String usernamenoexsit = "";
		Object obj = new Object();
		for (String moderator : moderators.split(",")) {
			if (!moderator.equals("")) {
				//当用户名是系统保留的用户名,请您输入其它的用户名
				if (GlobalsKeys.SYSTEM_USERNAME.equals(moderator)) {
					continue;
				}

				obj = getModeratorInfo(moderator);
				if (obj != null) {
					Object[] objects = (Object[]) obj;
					int groupid = Utils.null2Int(objects[1]);
					if ((groupid <= 3) && (groupid > 0))
						continue; //当为管理员,超级版主,版主时
					else {
						int radminid = Utils.null2Int(forumDAO.findUnique(
								"select admingroups.admingid from Usergroups where groupid=?", groupid));
						if (radminid <= 0)
							setModerator(moderator);
						else
							continue;
					}
				} else {
					usernamenoexsit = usernamenoexsit + moderator + ",";
				}
			}

		}

		AdminCacheManager.reSetModeratorList();

		return usernamenoexsit;
	}

	public void setModerator(String moderator) {
		forumDAO.createQuery("update Users set admingroups.admingid=3,usergroups.groupid=3 where username=?",
				moderator.trim()).executeUpdate();
		forumDAO.createQuery("update Online set admingroups.admingid=3,usergroups.groupid=3 where username=?",
				moderator.trim()).executeUpdate();
	}

	/**
	 * 获取版主信息
	 * @param moderator 版主名称
	 * @return
	 */
	public Object getModeratorInfo(String moderator) {
		return moderatorDAO
				.createQuery(
						"select uid,usergroups.groupid from Users where usergroups.groupid<>7 and usergroups.groupid<>8 and username=?",
						moderator).setMaxResults(1).uniqueResult();
	}

	/**
	 * 向版主列表中插入相关的版主信息
	 * @param fid 向版主列表中插入相关的版主信息
	 * @param moderators 相关要设置的版主名称(注:用","号分割)
	 * @param displayorder 显示顺序
	 * @param inherited 是否使用继承机制
	 */
	@Transactional(readOnly = false)
	public void insertForumsModerators(int fid, String moderators, int displayorder, int inherited) {
		moderators = moderators == null ? "" : moderators;
		int count = displayorder;
		Forums forums = forumDAO.get(fid);
		//数据库中存在的用户
		String usernamelist = "";
		//清除已有论坛的版主设置
		for (String username : moderators.split(",")) {
			if (!username.trim().equals("")) {
				Object object = forumDAO.createQuery(
						"select uid from Users where usergroups.groupid<>7 and usergroups.groupid<>8 and username=?",
						username).setMaxResults(1).uniqueResult();
				//先取出当前节点的信息
				if (object != null) {
					Moderators moderator = new Moderators();
					moderator.setDisplayorder(count);
					moderator.setForums(forums);
					moderator.setInherited(inherited);
					moderator.setUsers(new Users(Utils.null2Int(object)));
					moderatorDAO.save(moderator);
					usernamelist = usernamelist + username.trim() + ",";
					count++;
				}
			}
		}

		if (!usernamelist.equals("")) {
			forums.getForumfields().setModerators(moderators);
		} else {
			forums.getForumfields().setModerators("");
		}
		forumDAO.save(forums);
		AdminCacheManager.reSetModeratorList();
	}

	/**
	 * 添加论坛版主
	 * @param users 用户
	 * @param fid 板块ID
	 * @param displayorder 显示顺序
	 * @param inherited 是否使用继承选项  1为使用  0为不使用
	 */
	public void addModerator(Users users, int fid, int displayorder, int inherited) {
		Forums forums = new Forums();
		forums.setFid(fid);

		Moderators moderators = new Moderators();
		moderators.setDisplayorder(displayorder);
		moderators.setForums(forums);
		moderators.setInherited(inherited);
		moderators.setUsers(users);

		moderatorDAO.save(moderators);

		if (logger.isDebugEnabled()) {
			logger.debug("添加论坛 {} 版主 {} 成功", fid, moderators.getId());
		}
	}

	/**
	 * 获取指定板块版主列表
	 * @param fidlist
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<Users> getUidModeratorByFid(String fidlist) {
		if (logger.isDebugEnabled()) {
			logger.debug("获取指定板块{}版主列表", fidlist);
		}
		return forumDAO.find("select distinct users from Moderators where forums.fid in(" + fidlist + ")");
	}

	/**
	 * 清除已有论坛的版主设置
	 * 
	 * @param fid
	 */
	public void deleteModeratorByFid(int fid) {
		forumDAO.createQuery("delete from Moderators where forums.fid=?", fid).executeUpdate();
	}

	/**
	 * 检测指定板块ID下是否有子板块
	 * @param fid 板块ID
	 * @return 是否
	 */
	public boolean isExistSubForum(int fid) {
		int count = Utils.null2Int(forumDAO.findUnique("select count(fid) from Forums where forums.fid=?", fid), 0);
		return count > 0 ? true : false;
	}

	/**
	 * 删除指定fid的论坛版块
	 * @param fid 要删除的论坛版块的fid
	 * @return
	 */
	@Transactional(readOnly = false)
	public boolean deleteForumsByFid(int fid) {
		if (isExistSubForum(fid)) {
			return false;
		}
		//先取出当前节点的信息
		Forums forums = forumDAO.get(fid);

		//调整在当前节点排序位置之后的节点,做减1操作
		forumDAO.createQuery("update Forums set displayorder=displayorder-1 where displayorder>?",
				forums.getDisplayorder()).executeUpdate();

		//修改父结点中的子论坛个数
		forumDAO
				.createQuery("update Forums set subforumcount=subforumcount-1 where fid=?", forums.getForums().getFid())
				.executeUpdate();

		//删除相关投票的信息
		forumDAO.createQuery("delete from Polls where topics.tid in(select tid from Topics where forums.fid=?)", fid)
				.executeUpdate();

		//删除帖子附件表中的信息
		forumDAO
				.createQuery(
						"delete from Attachments where topics.tid in(select tid from Topics where forums.fid=? or postid.pid in(select pid from Posts where forums.fid=?))",
						fid, fid).executeUpdate();

		//删除相关帖子
		forumDAO.createQuery("delete from Posts where forums.fid=?", fid).executeUpdate();

		//删除相关主题
		forumDAO.createQuery("delete from Topics where forums.fid=?", fid).executeUpdate();

		//删除当前节点
		forumDAO.delete(forums);

		//删除版主列表中的相关信息
		forumDAO.createQuery("delete from Moderators where forums.fid=?", fid).executeUpdate();

		LForumCache.getInstance().removeCache("ForumListBoxOptions");
		LForumCache.getInstance().removeCache("ForumList");

		if (logger.isDebugEnabled()) {
			logger.debug("删除论坛板块 {} 成功", fid);
		}
		return true;
	}

	/**
	 * 检测指定论坛是否为顶级论坛
	 * @param fid 
	 * @return
	 */
	public int getTopForum(int fid) {
		return Utils.null2Int(forumDAO.createQuery("select fid from Forums where forums.fid=0 and fid=?", fid), -1);
	}

	/**
	 * 合并版块
	 * @param sourcefid 合并版块
	 * @param targetfid 目标论坛版块
	 * @return
	 */
	@Transactional(readOnly = false)
	public boolean combinationForums(int sourcefid, int targetfid) {
		if (isExistSubForum(sourcefid)) {
			return false;
		} else {
			childNode = "0";
			String fidlist = ("," + findChildNode(targetfid)).replace(",0,", "");

			//更新帖子与主题的信息
			forumDAO.createQuery("update Topics set forums.fid=? where forums.fid=?", targetfid, sourcefid)
					.executeUpdate();

			//要更新目标论坛的主题数
			int totaltopics = Utils.null2Int(forumDAO.findUnique("select count(tid) from Topics where forums.fid in ("
					+ fidlist + ")"), 0);
			int totalposts = 0;

			forumDAO.createQuery("update Posts set forums.fid=? where forums.fid=?", targetfid, sourcefid)
					.executeUpdate();

			//要更新目标论坛的帖子数
			totalposts = totalposts
					+ Utils.null2Int(forumDAO.findUnique("select count(pid) from Posts where forums.fid in (" + fidlist
							+ ")"), 0);

			// 获取论坛信息
			Forums targetForum = forumDAO.get(targetfid);
			Forums sourceForum = forumDAO.get(sourcefid);

			targetForum.setTopics_1(totaltopics);
			targetForum.setPosts(totalposts);

			//调整在当前节点排序位置之后的节点,做减1操作
			forumDAO.createQuery("update Forums set displayorder=displayorder-1 where displayorder>?",
					sourceForum.getDisplayorder()).executeUpdate();

			//修改父结点中的子论坛个数
			forumDAO.createQuery("update Forums set subforumcount=subforumcount-1 where fid=?",
					sourceForum.getForums().getFid()).executeUpdate();

			//删除源论坛版块
			forumDAO.delete(sourceForum);
			forumManager.updateForum(targetForum);

			LForumCache.getInstance().removeCache("ForumListBoxOptions");
			LForumCache.getInstance().removeCache("ForumList");
			if (logger.isDebugEnabled()) {
				logger.debug("合并论坛 {} 到目标论坛 {}", sourcefid, targetfid);
			}
			return true;
		}
	}

	/**
	 * 获取当前UID是否为非指定板块的版主
	 * @param currentfid 板块
	 * @param uid UID
	 * @return 不是则返回-1
	 */
	public int getUidInModeratorsByUid(int currentfid, int uid) {
		return Utils.null2Int(forumDAO.createQuery(
				"select users.uid  from Moderators where forums.fid<>? and users.uid=?", currentfid, uid)
				.setMaxResults(1).uniqueResult());
	}

	/**
	 * 对比指定的论坛版块的新老信息,将作出相应的调整
	 * @param oldmoderators 老版主名称(注:用","号分割)
	 * @param newmoderators 新版主名称(注:用","号分割)
	 * @param currentfid 当前论坛版块的fid
	 */
	public void compareOldAndNewModerator(String oldmoderators, String newmoderators, int currentfid) {
		if (Utils.null2String(oldmoderators).equals("")) {
			return;
		}

		//在新的版主名单中查找老的版主是否存在
		for (String oldmoderator : oldmoderators.split(",")) {
			if ((!oldmoderator.equals("")) && ("," + newmoderators + ",").indexOf("," + oldmoderator + ",") < 0) //当不存在,则表示当前老的版主已被删除,则执行删除当前老版主
			{
				Object[] objects = userManager.getUidAdminIdByUsername(oldmoderator);
				if (objects != null) //当前用户存在
				{
					int uid = Utils.null2Int(objects[0]);
					int radminid = Utils.null2Int(objects[1]);

					//在其他版块未曾设置为版主 ,则不作任何处理
					if ((getUidInModeratorsByUid(currentfid, uid) != -1) && (radminid != 1)) {
						Users userinfo = userManager.getUserInfo(uid);

						forumDAO.createQuery("update Online] SET usergroups.groupid=?  where users.uid=?", userinfo
								.getUsergroups().getGroupid(), uid);
						Admingroups admingroups = new Admingroups();
						admingroups.setAdmingid(0);
						userinfo.setAdmingroups(admingroups);
						userManager.updateUserInfo(userinfo);
					}
				}
			}
		}
	}

	/**
	 * 保存论坛版块(分类)的相关信息
	 * @param forums
	 * @return
	 */
	public String saveForumsInf(Forums forums) {
		forumManager.updateForum(forums);
		setForumsPathList();

		LForumCache.getInstance().removeCache("ForumListBoxOptions");
		LForumCache.getInstance().removeCache("ForumList");
		LForumCache.getInstance().removeCache("TopicTypesOption" + forums.getFid());
		LForumCache.getInstance().removeCache("TopicTypesLink" + forums.getFid());
		LForumCache.getInstance().removeCache("HotForumList");
		LForumCache.getInstance().removeCache("ForumHotTopicList");
		LForumCache.getInstance().removeCache("ForumNewTopicList");
		if (logger.isDebugEnabled()) {
			logger.debug("修改论坛板块 {} 成功", forums.getFid());
		}
		return setForumsModerators(forums.getFid(), forums.getForumfields().getModerators(), forums.getInheritedmod());
	}
}
image 

posted @ 2009-07-09 16:21 cxccbv 阅读(154) 评论(0) 编辑

LForum与Ehcache(二)CachesManager--论坛html数据缓存

package com.javaeye.lonlysky.lforum.service;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Property;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springside.modules.orm.hibernate.SimpleHibernateTemplate;

import com.javaeye.lonlysky.lforum.cache.LForumCache;
import com.javaeye.lonlysky.lforum.comm.utils.Utils;
import com.javaeye.lonlysky.lforum.config.impl.ConfigLoader;
import com.javaeye.lonlysky.lforum.entity.forum.Bbcodes;
import com.javaeye.lonlysky.lforum.entity.forum.Forumlinks;
import com.javaeye.lonlysky.lforum.entity.forum.Forums;
import com.javaeye.lonlysky.lforum.entity.forum.Medals;
import com.javaeye.lonlysky.lforum.entity.forum.Onlinelist;
import com.javaeye.lonlysky.lforum.entity.forum.Smilies;
import com.javaeye.lonlysky.lforum.entity.forum.Templates;
import com.javaeye.lonlysky.lforum.entity.forum.Topicidentify;

/**
 * 缓存论坛HTML数据
 * 
 * @author 黄磊
 *
 */
@Service
public class CachesManager {

	private static final Object synObject = new Object();
	private SimpleHibernateTemplate<Forumlinks, Integer> forumlinkDAO;
	private SimpleHibernateTemplate<Topicidentify, Integer> topicidentifyDAO;
	private SimpleHibernateTemplate<Medals, Integer> medalDAO;

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		forumlinkDAO = new SimpleHibernateTemplate<Forumlinks, Integer>(sessionFactory, Forumlinks.class);
		topicidentifyDAO = new SimpleHibernateTemplate<Topicidentify, Integer>(sessionFactory, Topicidentify.class);
		medalDAO = new SimpleHibernateTemplate<Medals, Integer>(sessionFactory, Medals.class);
	}

	@Autowired
	private TemplateManager templateManager;

	@Autowired
	private OnlineUserManager onlineUserManager;

	@Autowired
	private ForumManager forumManager;

	@Autowired
	private SmilieManager smilieManager;

	@Autowired
	private EditorManager editorManager;

	/**
	 * 获得表情符的json数据
	 * 
	 * @return 表情符的json数据
	 */
	public String getSmiliesCache() {
		String smilesString = LForumCache.getInstance().getCache("SmiliesList", String.class);
		if (smilesString == null) {

			StringBuilder builder = new StringBuilder();
			List<Smilies> smileList = smilieManager.getSmiliesList();
			for (Smilies smilies : smileList) {
				// 如果是表情分类
				if (smilies.getType() == 0) {
					builder.append("'" + smilies.getCode().trim().replace("'", "\\'") + "': [\r\n");
					boolean flag = false;
					for (Smilies smilies2 : smileList) {
						// 如果属于以上表情分类
						if (smilies2.getType().equals(smilies.getId())) {
							builder.append("{'code' : '");
							builder.append(smilies2.getCode().trim().replace("'", "\\'"));
							builder.append("', 'url' : '");
							builder.append(smilies2.getUrl().trim().replace("'", "\\'"));
							builder.append("'},\r\n");
							flag = true;
						}
					}//end for
					if (builder.length() > 0 && flag) {
						builder = builder.delete(builder.length() - 3, builder.length());
						//builder = builder.deleteCharAt(builder.length() - 3);
					}
					builder.append("\r\n],\r\n");
				}//end if		

			}//end for
			builder = builder.delete(builder.length() - 3, builder.length());
			smilesString = builder.toString();
			LForumCache.getInstance().addCache("SmiliesList", smilesString);
		}
		return smilesString;
	}

	/**
	 * 获得表情分类列表
	 * 
	 * @return 表情分类列表
	 */
	public List<Smilies> getSmilieTypesCache() {
		List<Smilies> smilesList = LForumCache.getInstance().getListCache("SmilieTypes", Smilies.class);
		if (smilesList == null) {
			smilesList = smilieManager.getSmilieTypes();
			LForumCache.getInstance().addCache("SmilieTypes", smilesList);
		}
		return smilesList;
	}

	/**
	 * 获取第一页的表情
	 * 
	 * @return 获取第一页的表情
	 */
	public String getSmiliesFirstPageCache() {
		String smiliesFirstPage = LForumCache.getInstance().getCache("SmiliesFirstPage", String.class);
		if (smiliesFirstPage == null) {
			StringBuilder builder = new StringBuilder();
			List<Smilies> smileList = smilieManager.getSmiliesList();
			for (int i = 0; i < smileList.size(); i++) {
				// 如果是表情分类
				if (smileList.get(i).getType() == 0) {
					builder.append("'" + smileList.get(i).getCode().trim().replace("'", "\\'") + "': [\r\n");
					boolean flag = false;
					int smiliescount = 0;
					for (int j = 0; j < smileList.size(); j++) {
						// 如果属于以上表情分类
						if (smileList.get(j).getType().equals(smileList.get(i).getId()) && smiliescount < 16) {
							builder.append("{'code' : '");
							builder.append(smileList.get(j).getCode().trim().replace("'", "\\'"));
							builder.append("', 'url' : '");
							builder.append(smileList.get(j).getUrl().trim().replace("'", "\\'"));
							builder.append("'},\r\n");
							flag = true;
							smiliescount++;
						}
					}//end for
					if (builder.length() > 0 && flag) {
						builder = builder.delete(builder.length() - 3, builder.length());
					}
					builder.append("\r\n],\r\n");
				}//end if		

			}//end for
			builder = builder.delete(builder.length() - 3, builder.length());
			smiliesFirstPage = builder.toString();
			LForumCache.getInstance().addCache("SmiliesFirstPage", smiliesFirstPage);
		}
		return smiliesFirstPage;
	}

	/**
	 * 返回模板列表的下拉框html
	 * 
	 * @return 下拉框html
	 */
	public String getTemplateListBoxOptionsCache() {
		String templateListBoxOptions = LForumCache.getInstance().getCache("TemplateListBoxOptions", String.class);
		if (templateListBoxOptions == null) {
			synchronized (synObject) {
				StringBuilder sb = new StringBuilder();
				List<Templates> templateList = templateManager.getValidTemplateList();
				for (Templates templates : templateList) {
					sb.append("<li class=\"current\">");
					sb.append("<a href=\"#\" onclick=\"window.location.href='showtemplate.action?templateid="
							+ templates.getTemplateid() + "'\">");
					sb.append(templates.getName().trim());
					sb.append("</a>");
					sb.append("</li>");
				}
				templateListBoxOptions = sb.toString();
				LForumCache.getInstance().addCache("TemplateListBoxOptions", templateListBoxOptions);
			}
		}
		return templateListBoxOptions;
	}

	/**
	 * 获得在线用户列表图例
	 * @return 在线用户列表图例
	 */
	public String getOnlineGroupIconList() {
		String onlineGroupIconList = LForumCache.getInstance().getCache("OnlineGroupIconList", String.class);
		if (onlineGroupIconList == null) {
			List<Onlinelist> iconList = onlineUserManager.getOnlineGroupIconList();
			StringBuilder sb = new StringBuilder();
			for (Onlinelist onlinelist : iconList) {
				sb.append("<img src=\"images/groupicons/");
				sb.append(onlinelist.getImg());
				sb.append("\" /> ");
				sb.append(onlinelist.getTitle());
				sb.append(" &nbsp; &nbsp; &nbsp; ");
			}
			onlineGroupIconList = sb.toString();
			LForumCache.getInstance().addCache("OnlineGroupIconList", onlineGroupIconList);
		}
		return onlineGroupIconList;
	}

	/**
	 * 获得友情链接列表
	 * @return 友情链接列表
	 */
	@SuppressWarnings("unchecked")
	public List<Forumlinks> getForumLinkList() {
		List<Forumlinks> forumlinkList = LForumCache.getInstance().getListCache("ForumLinkList", Forumlinks.class);
		if (forumlinkList == null) {
			forumlinkList = forumlinkDAO.createCriteria(Property.forName("displayorder").gt(0)).add(
					Property.forName("logo").ne("")).addOrder(Order.asc("displayorder")).list();
			LForumCache.getInstance().addCache("ForumLinkList", forumlinkList);
		}
		return forumlinkList;
	}

	/**
	 * 前台版块列表弹出菜单
	 * @param usergroupid 用户组id
	 * @param userid 当前用户id
	 * @param extname 扩展名称
	 * @return 版块列表弹出菜单
	 */
	public String getForumListMenuDiv(int usergroupid, int userid, String extname) {
		String forumListMenuDiv = LForumCache.getInstance().getCache("ForumListMenuDiv", String.class);
		if (forumListMenuDiv == null) {
			StringBuilder sb = new StringBuilder();
			List<Forums> forumList = forumManager.getForumList();
			if (forumList.size() > 0) {
				sb
						.append("<div class=\"popupmenu_popup\" id=\"forumlist_menu\" style=\"overflow-y: auto; display:none\">");
				for (Forums forums : forumList) {
					if (forums.getLayer() >= 0 && forums.getLayer() <= 1 && forums.getStatus() == 1) {
						// 判断是否为私有论坛
						if (!forums.getForumfields().getViewperm().trim().equals("")
								&& !Utils.inArray(usergroupid + "", forums.getForumfields().getViewperm())) {
							// 暂无处理
						} else {
							if (forums.getLayer() == 0) { // 如果是论坛分类
								sb.append("<dl>");
								sb.append("<dt>");
								sb.append("<a href=\"showforum.action?forumid=");
								sb.append(forums.getFid());
								sb.append("\">");
								sb.append(forums.getName());
								sb.append("</a></dt>");
								sb.append("<dd><ul>");
								for (Forums forum : forumList) {
									if (Utils.null2Int(forum.getParentidlist().split(",")[0].trim()) == forums.getFid()
											&& forum.getLayer() == 1) {
										// 判断是否为第一级板块
										sb.append("<li><a href=\"showforum.action?forumid=");
										sb.append(forum.getFid());
										sb.append("\">");
										sb.append(forum.getName());
										sb.append("</a></li>");
									}
								}//end for
								sb.append("</ul></dd>");
								sb.append("</dl>");
							}//end if

						}//end if
					}
				}//end for
			}
			sb.append("</div>");
			forumListMenuDiv = sb.toString().replace("<dd><ul></ul></dd>", "");
			LForumCache.getInstance().addCache("ForumListMenuDiv", forumListMenuDiv);
		}
		return forumListMenuDiv;
	}

	/**
	 * 获得主题类型数组
	 * 
	 * @return 主题类型数组
	 */
	@SuppressWarnings("unchecked")
	public Map<Integer, Object> getTopicTypeArray() {
		Map<Integer, Object> topicTypeArray = (Map<Integer, Object>) LForumCache.getInstance().getCache(
				"TopicTypeArray");
		if (topicTypeArray == null) {
			topicTypeArray = new HashMap<Integer, Object>();
			List<Object[]> objList = forumlinkDAO.find("select typeid,name from Topictypes order by displayorder");
			if (objList.size() > 0) {
				for (Object[] objects : objList) {
					if (Utils.null2String(objects[0]) != "" && Utils.null2String(objects[1]) != "") {
						topicTypeArray.put(Utils.null2Int(objects[0]), objects[1]);
					}
				}
			}
			LForumCache.getInstance().addCache("TopicTypeArray", topicTypeArray);
		}
		return topicTypeArray;
	}

	/**
	 * 获得版块下拉列表
	 * @return 列表内容的html
	 */
	@SuppressWarnings("unchecked")
	public String getForumListBoxOptionsCache() {
		String forumListBoxOptions = LForumCache.getInstance().getCache("ForumListBoxOptions", String.class);
		if (forumListBoxOptions == null) {
			StringBuilder sb = new StringBuilder();
			List<Object[]> objList = forumlinkDAO
					.find("select name,fid,layer from Forums where forums.fid not in (select fid from Forums where status<1 and layer=0) and status>0 and displayorder>=0 order by displayorder");
			for (Object[] objects : objList) {
				sb.append("<option value=\"");
				sb.append(objects[1]);
				sb.append("\">");
				sb.append(Utils.getSpacesString(Utils.null2Int(objects[2], 0)));
				sb.append(objects[0].toString().trim());
				sb.append("</option>");
			}
			forumListBoxOptions = sb.toString();
			LForumCache.getInstance().addCache("ForumListBoxOptions", forumListBoxOptions);
		}
		return forumListBoxOptions;
	}

	/**
	 * 获得编辑器自定义按钮信息的javascript数组
	 * @return 表情符的javascript数组
	 */
	public String getCustomEditButtonList() {
		String str = LForumCache.getInstance().getCache("CustomEditButtonList", String.class);
		if (str != null) {
			return str;
		}
		StringBuilder sb = new StringBuilder();
		List<Bbcodes> bbcodeList = editorManager.getCustomEditButtonList();

		for (Bbcodes bbcodes : bbcodeList) {
			//说明:[标签名,对应图标文件名,[参数1描述,参数2描述,...],[参数1默认值,参数2默认值,...]]
			//实例["fly","swf.gif",["请输入flash网址","请输入flash宽度","请输入flash高度"],["http://","200","200"],3]
			sb.append(",'" + Utils.replaceStrToScript(bbcodes.getTag()) + "':['");
			sb.append(Utils.replaceStrToScript(bbcodes.getTag()));
			sb.append("','");
			sb.append(Utils.replaceStrToScript(bbcodes.getIcon()));
			sb.append("','");
			sb.append(Utils.replaceStrToScript(bbcodes.getExplanation()));
			sb.append("',['");
			sb.append(Utils.replaceStrToScript(bbcodes.getParamsdescript()).replace(",", "','"));
			sb.append("'],['");
			sb.append(Utils.replaceStrToScript(bbcodes.getParamsdefvalue()).replace(",", "','"));
			sb.append("'],");
			sb.append(Utils.replaceStrToScript(bbcodes.getParams().toString()));
			sb.append("]");
		}
		if (sb.length() > 0) {
			sb = sb.delete(0, 1);
		}
		str = sb.toString().replace("\r\n", "");
		LForumCache.getInstance().addCache("CustomEditButtonList", str);
		return str;
	}

	/**
	 * 返回脏字过滤列表
	 * @return 返回脏字过滤列表数组
	 */
	@SuppressWarnings("unchecked")
	public String[][] getBanWordList() {
		String[][] str = (String[][]) LForumCache.getInstance().getCache("BanWordList");
		if (str != null) {
			return str;
		}
		List list = forumlinkDAO.find("select find,replacement from Words");
		if (list == null) {
			return str;
		}
		List<Object[]> words = list;
		str = new String[words.size()][2];
		String temp = "";
		Pattern pattern = Pattern.compile("\\{(\\d+)\\}");
		for (int i = 0; i < words.size(); i++) {
			temp = Utils.null2String(words.get(i)[0]);
			Matcher matcher = pattern.matcher(temp);
			for (int j = 0; j < matcher.groupCount(); j++) {
				temp = temp.replace(matcher.group(j), matcher.group(j).replace("{", ".{0,"));
			}
			str[i][0] = temp.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\'").replace("[", "\\[")
					.replace("]", "\\]");
			str[i][1] = Utils.null2String(words.get(i)[1]);
		}
		LForumCache.getInstance().addCache("BanWordList", str);
		return str;
	}

	/**
	 * 替换原始字符串中的脏字词语
	 * @param text 原始字符串
	 * @return 替换后的结果
	 */
	public String banWordFilter(String text) {
		StringBuilder sb = new StringBuilder(text);
		String[][] str = getBanWordList();
		int count = str.length / 2;
		for (int i = 0; i < count; i++) {
			if (!str[i][1].equals("{BANNED}") && !str[i][1].equals("{MOD}")) {
				sb = new StringBuilder().append(sb.toString().replace(str[i][0], str[i][1]));
			}
		}
		return sb.toString();
	}

	/**
	 * 判断字符串是否包含脏字词语
	 * @param text 原始字符串
	 * @return 如果包含则返回true, 否则返回false
	 */
	public boolean hasBannedWord(String text) {
		String[][] str = getBanWordList();
		int count = str.length / 2;
		Pattern r_word = null;
		for (int i = 0; i < count; i++) {
			if (str[i][1].equals("{BANNED}")) {

				r_word = Pattern.compile(str[i][0]);
				Matcher matcher = r_word.matcher(text);
				while (matcher.find()) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 指定的字符串中是否含有需要审核词汇
	 */
	public boolean hasAuditWord(String text) {
		String[][] str = getBanWordList();
		int count = str.length / 2;
		Pattern r_word = null;
		for (int i = 0; i < count; i++) {
			if (str[i][1].equals("{MOD}")) {

				r_word = Pattern.compile(str[i][0]);
				Matcher matcher = r_word.matcher(text);
				while (matcher.find()) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 获取主题鉴定项
	 * @param identifyid 主题签定id
	 * @return 主题鉴定信息
	 */
	public Topicidentify getTopicIdentify(int identifyid) {
		for (Topicidentify ti : getTopicIdentifyList()) {
			if (ti.getIdentifyid() == identifyid) {
				return ti;
			}
		}
		return new Topicidentify();
	}

	/**
	 * 获取主题签定集合项
	 * @return 主题签定集合项
	 */
	public List<Topicidentify> getTopicIdentifyList() {
		List<Topicidentify> topicidentifyList = LForumCache.getInstance().getListCache("TopicIdentifyList",
				Topicidentify.class);
		if (topicidentifyList != null) {
			return topicidentifyList;
		}
		topicidentifyList = new ArrayList<Topicidentify>();
		topicidentifyList = topicidentifyDAO.findAll();
		StringBuilder jsArray = new StringBuilder("<script type='text/javascript'>var topicidentify = { ");

		for (Topicidentify topicidentify : topicidentifyList) {
			jsArray.append("'" + topicidentify.getIdentifyid() + "':'" + topicidentify.getFilename() + "',");
		}

		jsArray.deleteCharAt(jsArray.length() - 1);
		jsArray.append("};</script>");
		LForumCache.getInstance().addCache("TopicIdentifyList", topicidentifyList);
		LForumCache.getInstance().addCache("TopicIndentifysJsArray", jsArray.toString());
		return topicidentifyList;
	}

	/**
	 * 获得勋章列表
	 * @return 勋章列表
	 */
	@SuppressWarnings("unchecked")
	public Map<String, String> getMedalsList() {
		Map<String, String> medalsMap = (Map<String, String>) LForumCache.getInstance().getCache("MedalsList");
		if (medalsMap == null) {
			medalsMap = new HashMap<String, String>();
			List<Medals> medalList = medalDAO.findAll();
			for (Medals medals : medalList) {
				if (medals.getAvailable() == 1) {
					if (!medals.getImage().trim().equals("")) {
						medals.setImage("<img border=\"0\" src=\"images/medals/" + medals.getImage() + "\" alt=\""
								+ medals.getName() + "\" title=\"" + medals.getName() + "\" class=\"medals\" />");
					} else {
						medals.setImage("");
					}
				} else {
					medals.setImage("");
				}
				medalsMap.put(medals.getName(), medals.getImage());
			}
			LForumCache.getInstance().addCache("MedalsList", medalsMap);
		}
		return medalsMap;
	}

	/**
	 * 获取指定id的勋章列表html
	 * @param mdealList 勋章id
	 * @return 勋章列表html
	 */
	public String getMedalsList(String mdealList) {
		Map<String, String> medalsMap = getMedalsList();
		String[] list = mdealList.split(",");
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < list.length; i++) {
			sb.append(medalsMap.get(list[i]));
		}
		return sb.toString();
	}

	/**
	 * 获取自带头像列表
	 * @return 获取自带头像列表
	 */
	public List<String> getAvatarList() {
		List<String> avatarList = LForumCache.getInstance().getListCache("CommonAvatarList", String.class);
		if (avatarList == null) {
			avatarList = new ArrayList<String>();
			File dirinfo = new File(ConfigLoader.getConfig().getWebpath() + "avatars/common/");
			String extname = "";
			for (File file : dirinfo.listFiles()) {
				if (file != null) {
					extname = file.getName().substring(file.getName().lastIndexOf(".")).toLowerCase();
					if (extname.equals(".jpg") || extname.equals(".gif") || extname.equals(".png")) {
						avatarList.add("avatars/common/" + file.getName());
					}
				}
			}
			LForumCache.getInstance().addCache("CommonAvatarList", avatarList);
		}
		return avatarList;
	}

	/**
	 * 获取主题鉴定图片地址缓存数组
	 * @return 主题鉴定图片地址缓存数组
	 */
	public String getTopicIdentifyFileNameJsArray() {
		String jsArray = LForumCache.getInstance().getCache("TopicIndentifysJsArray", String.class);
		if (jsArray == null) {
			getTopicIdentifyList();
			jsArray = LForumCache.getInstance().getCache("TopicIndentifysJsArray", String.class);
		}
		return jsArray;
	}
}

posted @ 2009-07-09 16:01 cxccbv 阅读(199) 评论(0) 编辑

LForum与Ehcache(一)LForumCache

LForumCache

package com.javaeye.lonlysky.lforum.cache;

import java.net.URL;
import java.util.List;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * LForum缓存管理
 * 
 * @author 黄磊
 *
 */
public class LForumCache {

	private static final Logger logger = LoggerFactory.getLogger(LForumCache.class);
	private static Object lockObject = new Object();
	private static LForumCache instance = null;

	public static final String CACHE_NAME = "LForumCaches";

	private LForumCache() {
	}

	/**
	 * 获取换此类实例
	 * 
	 * @return LForumCache实例
	 */
	public static LForumCache getInstance() {
		if (instance == null) {
			synchronized (lockObject) {
				logger.info("创建新的缓存类实例");
				instance = new LForumCache();
			}
		}
		return instance;
	}

	/**
	 * 获取缓存管理类
	 * 
	 * @return 缓存管理类
	 */
	public CacheManager getCacheManager() {
		URL url = getClass().getResource("/ehcache-hibernate.xml");
		return CacheManager.create(url);
	}

	/**
	 * 获取论坛缓存
	 * 
	 * @return 缓存
	 */
	public Cache getCache() {
		return getCacheManager().getCache(CACHE_NAME);
	}

	/**
	 * 添加缓存对象
	 * 
	 * @param key 缓存KEY
	 * @param cacheObj 缓存对象
	 */
	public void addCache(String key, Object cacheObj) {
		getCache().put(new Element(key, cacheObj));
		if (logger.isDebugEnabled()) {
			logger.debug("添加KEY为{}的缓存对象,当前缓存{}个", key, getCache().getSize());
		}
	}

	/**
	 * 获取缓存对象
	 * 
	 * @param key 缓存KEY
	 * @return 缓存对象
	 */
	public Object getCache(String key) {
		Element element = getCache().get(key);
		if (element == null) {
			return null;
		}
		if (logger.isDebugEnabled()) {
			logger.debug("获取KEY为{}的缓存对象", key);
		}
		return (Object) element.getValue();

	}

	/**
	 * 获取指定类型缓存对象
	 * 
	 * @param <T> 指定类型
	 * @param key  缓存KEY
	 * @param classz 类型Class
	 * @return 指定类型缓存对象
	 */
	@SuppressWarnings("unchecked")
	public <T> T getCache(String key, Class<T> classz) {
		if (getCache(key) == null) {
			return null;
		}
		if (logger.isDebugEnabled()) {
			logger.debug("获取KEY为{}且类型为{}的缓存对象", key, classz.getName());
		}
		return (T) getCache(key);
	}

	/**
	 * 获取指定类型LIST缓存
	 * 
	 * @param <T>  指定类型
	 * @param key 缓存KEY
	 * @param classz 类型Class
	 * @return 指定类型LIST缓存
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> getListCache(String key, Class<T> classz) {
		if (getCache(key) == null) {
			return null;
		}
		if (logger.isDebugEnabled()) {
			logger.debug("获取KEY为{}且类型为{}的LIST缓存", key, classz.getName());
		}
		return (List<T>) getCache(key);
	}

	/**
	 * 移除指定KEY缓存对象
	 * 
	 * @param key 缓存KEY
	 */
	public void removeCache(String key) {
		if (getCache(key) != null) {
			getCache().remove(key);
			if (logger.isDebugEnabled()) {
				logger.debug("移除取KEY为{}的缓存对象", key);
			}
		}
	}
}

在各个manager操作中使用LForumCache缓存

package com.javaeye.lonlysky.lforum.service;

import java.util.List;

import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springside.modules.orm.hibernate.SimpleHibernateTemplate;

import com.javaeye.lonlysky.lforum.cache.LForumCache;
import com.javaeye.lonlysky.lforum.entity.forum.Announcements;

/**
 * 论坛公告操作类
 * 
 * @author 黄磊
 *
 */
@Service
@Transactional
public class AnnouncementManager {

	private final static Logger logger = LoggerFactory.getLogger(AnnouncementManager.class);
	private SimpleHibernateTemplate<Announcements, Integer> announcementDAO;

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		announcementDAO = new SimpleHibernateTemplate<Announcements, Integer>(sessionFactory, Announcements.class);
	}

	/**
	 * 获得全部指定时间段内的公告列表
	 * @param starttime 开始时间
	 * @param endtime 结束时间
	 * @return 公告列表
	 */
	@SuppressWarnings("unchecked")
	public List<Announcements> getAnnouncementList(String starttime, String endtime) {
		List<Announcements> announcemenList = LForumCache.getInstance().getListCache("AnnouncementList",
				Announcements.class);
		if (announcemenList == null) {
			announcemenList = announcementDAO.find(
					"from Announcements where starttime<=? and endtime>=? order by displayorder,id desc", starttime,
					endtime);
			if (logger.isDebugEnabled()) {
				logger.debug("获得时间段" + starttime + " - " + endtime + "的公告列表");
			}
			LForumCache.getInstance().addCache("AnnouncementList", announcemenList);
		}
		return announcemenList;
	}

	/**
	 * 获得全部指定时间段内的前n条公告列表
	 * @param starttime 开始时间
	 * @param endtime 结束时间
	 * @param maxcount 最大记录数,小于0返回全部
	 * @return 公告列表
	 */
	@SuppressWarnings("unchecked")
	public List<Announcements> getSimplifiedAnnouncementList(String starttime, String endtime, int maxcount) {
		List<Announcements> announcemenList = LForumCache.getInstance().getListCache("SimplifiedAnnouncementList",
				Announcements.class);
		if (announcemenList == null) {
			announcemenList = announcementDAO.createQuery(
					"from Announcements where starttime<=? and endtime>=? order by displayorder,id desc", starttime,
					endtime).setMaxResults(maxcount).list();
			LForumCache.getInstance().addCache("SimplifiedAnnouncementList", announcemenList);
		}
		return announcemenList;
	}
}

posted @ 2009-07-09 16:00 cxccbv 阅读(180) 评论(0) 编辑

Ehcache源码研究(五)CacheFilter

/**
 *  Copyright 2003-2009 Luck Consulting Pty Ltd
 *
 *  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.
 */

package net.sf.ehcache.constructs.web.filter;

import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.constructs.blocking.BlockingCache;
import net.sf.ehcache.constructs.blocking.LockTimeoutException;
import net.sf.ehcache.constructs.web.AlreadyCommittedException;
import net.sf.ehcache.constructs.web.AlreadyGzippedException;
import net.sf.ehcache.constructs.web.GenericResponseWrapper;
import net.sf.ehcache.constructs.web.PageInfo;
import net.sf.ehcache.constructs.web.ResponseHeadersNotModifiableException;
import net.sf.ehcache.constructs.web.ResponseUtil;
import net.sf.ehcache.constructs.web.SerializableCookie;



import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.zip.DataFormatException;

/**
 * An abstract CachingFilter.
 * <p/>
 * This class should be sub-classed for each page to be cached.
 * <p/>
 * The filters must be declared in the web.xml deployment descriptor. Then a mapping from a web resource,
 * such as a JSP Page, FreeMarker page, Velocity page, Servlet or static resouce needs to be defined. Finally, a succession of mappings can be used
 * to create a filter chain. See SRV.6 of the Servlet 2.3 specification for more details.
 * <p/>
 * Care should be taken not to define a filter chain such that the same {@link CachingFilter} class is reentered.
 * The {@link CachingFilter} uses the {@link net.sf.ehcache.constructs.blocking.BlockingCache}. It blocks until the thread which
 * did a get which results in a null does a put. If reentry happens a second get happens before the first put. The second
 * get could wait indefinitely. This situation is monitored and if it happens, an IllegalStateException will be thrown.
 * <p/>
 * The following init-params are supported:
 * <ol>
 * <li>cacheName - the name in ehcache.xml used by the filter. 
 * <li>blockingTimeoutMillis - the time, in milliseconds, to wait for the filter chain to return with a response on a cache
 * miss. This is useful to fail fast in the event of an infrastructure failure.
 * </ol>
 * @author @author Greg Luck
 */
public abstract class CachingFilter extends Filter {

    private static final Logger LOG = Logger.getLogger(CachingFilter.class.getName());
    private static final String BLOCKING_TIMEOUT_MILLIS = "blockingTimeoutMillis";
    private static final String CACHE_NAME = "cacheName";

    /**
     * The cache name can be set through init parameters. If it is set it is stored here.
     */
    protected String cacheName;


    /**
     * The cache holding the web pages. Ensure that all threads for a given cache name are using the same instance of this.
     */
    protected BlockingCache blockingCache;


    /**
     * Initialises blockingCache to use. The BlockingCache created by this method does not have a lock timeout set.
     * <p/>
     * A timeout can be appled using <code>blockingCache.setTimeoutMillis(int timeout)</code> and takes effect immediately
     * for all new requests
     *
     * @throws CacheException The most likely cause is that a cache has not been
     *                        configured in ehcache's configuration file ehcache.xml for the filter name
     * @param filterConfig this filter's configuration.
     */
    public void doInit(FilterConfig filterConfig) throws CacheException {
        synchronized (this.getClass()) {
            if (blockingCache == null) {
                setCacheNameIfAnyConfigured(filterConfig);
                final String localCacheName = getCacheName();
                Ehcache cache = getCacheManager().getEhcache(localCacheName);
                if (!(cache instanceof BlockingCache)) {
                    //decorate and substitute
                    BlockingCache newBlockingCache = new BlockingCache(cache);
                    getCacheManager().replaceCacheWithDecoratedCache(cache, newBlockingCache);
                }
                blockingCache = (BlockingCache) getCacheManager().getEhcache(localCacheName);
                Integer blockingTimeoutMillis = parseBlockingCacheTimeoutMillis(filterConfig);
                if (blockingTimeoutMillis != null && blockingTimeoutMillis > 0) {
                    blockingCache.setTimeoutMillis(blockingTimeoutMillis);
                }
            }
        }
    }

    /**
     * Reads the filterConfig for the parameter "blockingTimeoutMillis", and if found,
     * set the blocking timeout. If there is a parsing exception, no timeout is set.
     */
    Integer parseBlockingCacheTimeoutMillis(FilterConfig filterConfig) {

        String timeout = filterConfig.getInitParameter(BLOCKING_TIMEOUT_MILLIS);
        try {
            return Integer.parseInt(timeout);
        } catch (NumberFormatException e) {
            return null;
        }

    }

    /**
     * Sets the cacheName from the filterConfig
     */
    protected void setCacheNameIfAnyConfigured(FilterConfig filterConfig) {
        this.cacheName = filterConfig.getInitParameter(CACHE_NAME);

    }


    /**
     * Destroys the filter.
     */
    protected void doDestroy() {
        //noop
    }

    /**
     * Performs the filtering for a request. This method caches
     * based responses keyed by {@link #calculateKey(javax.servlet.http.HttpServletRequest)}
     * <p/>
     * By default this method will queue requests requesting the page response for a given key
     * until the first thread in the queue has completed. The request which occurs when the page
     * expires incurs the cost of waiting for the downstream processing to return the respone.
     * <p/>
     * The maximum time to wait can be configured by setting <code>setTimeoutMillis</code> on the
     * underlying <code>BlockingCache</code>.
     *
     * @param request
     * @param response
     * @param chain
     * @throws AlreadyGzippedException     if a double gzip is attempted
     * @throws AlreadyCommittedException   if the response was committed on the way in or the on the way back
     * @throws FilterNonReentrantException if an attempt is made to reenter this filter in the same request.
     * @throws LockTimeoutException        if this request is waiting on another that is populating the cache entry
     *                                     and timeouts while waiting. Only occurs if the BlockingCache has a timeout set.
     * @throws Exception                   for all other exceptions. They will be caught and logged in
     *                                     {@link Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)}
     */
    protected void doFilter(final HttpServletRequest request, final HttpServletResponse response,
                            final FilterChain chain)
            throws AlreadyGzippedException,
            AlreadyCommittedException,
            FilterNonReentrantException,
            LockTimeoutException,
            Exception {
        if (response.isCommitted()) {
            throw new AlreadyCommittedException("Response already committed before doing buildPage.");
        }
        logRequestHeaders(request);
        PageInfo pageInfo = buildPageInfo(request, response, chain);

        //return on error or redirect code
        int statusCode = pageInfo.getStatusCode();
        if (statusCode != HttpServletResponse.SC_OK) {
            return;
        }

        if (response.isCommitted()) {
            throw new AlreadyCommittedException("Response already committed after doing buildPage"
                    + "but before writing response from PageInfo.");
        }
        writeResponse(request, response, pageInfo);
    }


    /**
     * Build page info either using the cache or building the page directly.
     * <p/>
     * Some requests are for page fragments which should never be gzipped, or for
     * other pages which are not gzipped.
     */
    protected PageInfo buildPageInfo(final HttpServletRequest request, final HttpServletResponse response,
                                     final FilterChain chain) throws Exception {
        // Look up the cached page
        final String key = calculateKey(request);
        PageInfo pageInfo = null;
        String originalThreadName = Thread.currentThread().getName();
        try {
            checkNoReentry(request);
            Element element = blockingCache.get(key);
            if (element == null || element.getObjectValue() == null) {
                try {
                    // Page is not cached - build the response, cache it, and send to client
                    pageInfo = buildPage(request, response, chain);
                    if (pageInfo.isOk()) {
                        if (LOG.isLoggable(Level.FINEST)) {
                            LOG.finest("PageInfo ok. Adding to cache " + blockingCache.getName() + " with key " + key);
                        }
                        blockingCache.put(new Element(key, pageInfo));
                    } else {
                        if (LOG.isLoggable(Level.FINE)) {
                            LOG.fine("PageInfo was not ok(200). Putting null into cache " + blockingCache.getName()
                                    + " with key " + key);
                        }
                        blockingCache.put(new Element(key, null));
                    }
                } catch (final Throwable throwable) {
                    // Must unlock the cache if the above fails. Will be logged at Filter
                    blockingCache.put(new Element(key, null));
                    throw new Exception(throwable);
                }
            } else {
                pageInfo = (PageInfo) element.getObjectValue();
            }
        } catch (LockTimeoutException e) {
            //do not release the lock, because you never acquired it
            throw e;
        } finally {
            Thread.currentThread().setName(originalThreadName);
        }
        return pageInfo;
    }


    /**
     * Builds the PageInfo object by passing the request along the filter chain
     *
     * @param request
     * @param response
     * @param chain
     * @return a Serializable value object for the page or page fragment
     * @throws AlreadyGzippedException if an attempt is made to double gzip the body
     * @throws Exception
     */
    protected PageInfo buildPage(final HttpServletRequest request, final HttpServletResponse response,
                                 final FilterChain chain) throws AlreadyGzippedException, Exception {

        // Invoke the next entity in the chain
        final ByteArrayOutputStream outstr = new ByteArrayOutputStream();
        final GenericResponseWrapper wrapper = new GenericResponseWrapper(response, outstr);
        chain.doFilter(request, wrapper);
        wrapper.flush();

        long timeToLiveSeconds = blockingCache.getCacheConfiguration().getTimeToLiveSeconds();

        // Return the page info
        return new PageInfo(wrapper.getStatus(), wrapper.getContentType(), wrapper.getHeaders(), wrapper.getCookies(),
                outstr.toByteArray(), true, timeToLiveSeconds);
    }

    /**
     * Writes the response from a PageInfo object.
     * <p/>
     * Headers are set last so that there is an opportunity to override 
     *
     * @param request
     * @param response
     * @param pageInfo
     * @throws IOException
     * @throws DataFormatException
     * @throws ResponseHeadersNotModifiableException
     *
     */
    protected void writeResponse(final HttpServletRequest request, final HttpServletResponse response, final PageInfo pageInfo)
            throws IOException, DataFormatException, ResponseHeadersNotModifiableException {
        boolean requestAcceptsGzipEncoding = acceptsGzipEncoding(request);

        setStatus(response, pageInfo);
        setContentType(response, pageInfo);
        setCookies(pageInfo, response);
        //do headers last so that users can override with their own header sets
        setHeaders(pageInfo, requestAcceptsGzipEncoding, response);
        writeContent(request, response, pageInfo);
    }

    /**
     * Set the content type.
     *
     * @param response
     * @param pageInfo
     */
    protected void setContentType(final HttpServletResponse response, final PageInfo pageInfo) {
        String contentType = pageInfo.getContentType();
        if (contentType != null && contentType.length() >  0) {
            response.setContentType(contentType);
        }
    }

    /**
     * Set the serializableCookies
     *
     * @param pageInfo
     * @param response
     */
    protected void setCookies(final PageInfo pageInfo, final HttpServletResponse response) {

        final Collection cookies = pageInfo.getSerializableCookies();
        for (Iterator iterator = cookies.iterator(); iterator.hasNext();) {
            final Cookie cookie = ((SerializableCookie) iterator.next()).toCookie();
            response.addCookie(cookie);
        }
    }

    /**
     * Status code
     *
     * @param response
     * @param pageInfo
     */
    protected void setStatus(final HttpServletResponse response, final PageInfo pageInfo) {
        response.setStatus(pageInfo.getStatusCode());
    }


    /**
     * Set the headers in the response object, excluding the Gzip header
     *
     * @param pageInfo
     * @param requestAcceptsGzipEncoding
     * @param response
     */
    protected void setHeaders(final PageInfo pageInfo,
                              boolean requestAcceptsGzipEncoding,
                              final HttpServletResponse response) {

        final Collection headers = pageInfo.getResponseHeaders();
        final int header = 0;
        final int value = 1;

        for (Iterator iterator = headers.iterator(); iterator.hasNext();) {
            final String[] headerPair = (String[]) iterator.next();
            response.setHeader(headerPair[header], headerPair[value]);
        }
    }


    /**
     * A meaningful name representative of the JSP page being cached.
     * <p/>
     * The <code>cacheName</code> field is be set by the <code>doInit</code> method.
     * Override to control the name used. The significance is that the name is used to find a cache
     * configuration in <code>ehcache.xml</code>
     *
     * @return the name of the cache to use for this filter.
     */
    protected String getCacheName() {
        return cacheName;
    }


    /**
     * Gets the CacheManager for this CachingFilter. It is therefore up to subclasses what CacheManager to use.
     * <p/>
     * This method was introduced in ehcache 1.2.1. Older versions used a singleton CacheManager instance created with
     * the default factory method.
     *
     * @return the CacheManager to be used
     * @since 1.2.1
     */
    protected abstract CacheManager getCacheManager();


    /**
     * CachingFilter works off a key.
     * <p/>
     * The key should be unique. Factors to consider in generating a key are:
     * <ul>
     * <li>The various hostnames that a request could come through
     * <li>Whether additional parameters used for referral tracking e.g. google should be excluded
     * to maximise cache hits
     * <li>Additional parameters can be added to any page. The page will still work but will miss the
     * cache. Consider coding defensively around this issue.
     * </ul>
     * <p/>
     * Implementers should differentiate between GET and HEAD requests otherwise blank pages
     * can result. See SimplePageCachingFilter for an example implementation.
     *
     * @param httpRequest
     * @return the key, generally the URL plus request parameters
     */
    protected abstract String calculateKey(final HttpServletRequest httpRequest);


    /**
     * Writes the response content.
     * This will be gzipped or non gzipped depending on whether the User Agent accepts
     * GZIP encoding.
     * <p/>
     * If the body is written gzipped a gzip header is added.
     *
     * @param response
     * @param pageInfo
     * @throws IOException
     */
    protected void writeContent(final HttpServletRequest request,
                                final HttpServletResponse response, final PageInfo pageInfo)
            throws IOException, ResponseHeadersNotModifiableException {
        byte[] body;
        if (acceptsGzipEncoding(request)) {
            ResponseUtil.addGzipHeader(response);
            body = pageInfo.getGzippedBody();
            if (ResponseUtil.shouldGzippedBodyBeZero(body, request)) {
                body = new byte[0];
            }
        } else {
            body = pageInfo.getUngzippedBody();
        }

        boolean shouldBodyBeZero = ResponseUtil.shouldBodyBeZero(request, pageInfo.getStatusCode());
        if (shouldBodyBeZero) {
            body = new byte[0];
        }

        response.setContentLength(body.length);
        OutputStream out = new BufferedOutputStream(response.getOutputStream());
        out.write(body);
        out.flush();
    }

    /**
     * Check that this caching filter is not being reentered by the same recursively.
     * Recursive calls will block indefinitely because the first request has not yet
     * unblocked the cache.
     * <p/>
     * This condition usually indicates an error in filter chaining or RequestDispatcher
     * dispatching.
     *
     * @param httpRequest
     * @throws FilterNonReentrantException if reentry is detected
     */
    protected void checkNoReentry(final HttpServletRequest httpRequest) throws FilterNonReentrantException {
        Thread thread = Thread.currentThread();
        String threadName = thread.getName();
        String filterName = getClass().getName();
        if (thread.getName().indexOf(" been through " + filterName) != -1) {
            throw new FilterNonReentrantException("The request thread is attempting to reenter"
                    + " filter "
                    + filterName
                    + ". URL: "
                    + httpRequest.getRequestURL());
        }
        //Instrument thread name
        thread.setName(thread.getName() + " been through " + filterName);
        String newThreadName = thread.getName();
        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("Thread name changed from " + threadName
                    + " to " + newThreadName);
        }
    }
}

posted @ 2009-07-09 12:00 cxccbv 阅读(622) 评论(0) 编辑

Ehcache源码研究(四)Filter

Ehcache-1.6.0源码\web\src\main\java\net\sf\ehcache\constructs\web\

/**
 *  Copyright 2003-2009 Luck Consulting Pty Ltd
 *
 *  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.
 */

package net.sf.ehcache.constructs.web.filter;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A generic {@link javax.servlet.Filter} with most of what we need done.
 * <p/>
 * Participates in the Template Method pattern with {@link javax.servlet.Filter}.
 *
 * @author <a href="mailto:gluck@thoughtworks.com">Greg Luck</a>
 * @version $Id: Filter.java 746 2008-08-18 08:08:02Z gregluck $
 */
public abstract class Filter implements javax.servlet.Filter {
    /**
     * If a request attribute NO_FILTER is set, then filtering will be skipped
     */
    public static final String NO_FILTER = "NO_FILTER";
    private static final Logger LOG = Logger.getLogger(Filter.class.getName());

    /**
     * The filter configuration.
     */
    protected FilterConfig filterConfig;

    /**
     * The exceptions to log differently, as a comma separated list
     */
    protected String exceptionsToLogDifferently;


    /**
     * A the level of the exceptions which will be logged differently.
     * <p/>
     * This should match the logging method name in Java logging e.g. fine
     */
    protected Level exceptionsToLogDifferentlyLevel;


    /**
     * Most {@link Throwable}s in Web applications propagate to the user. Usually they are logged where they first
     * happened. Printing the stack trace once a {@link Throwable} as propagated to the servlet is sometimes
     * just clutters the log.
     * <p/>
     * This field corresponds to an init-param of the same name. If set to true stack traces will be suppressed.
     */
    protected boolean suppressStackTraces;


    /**
     * Performs the filtering.  This method calls template method
     * {@link #doFilter(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,javax.servlet.FilterChain) } which does the filtering.
     * This method takes care of error reporting and handling.
     * Errors are reported at warn level because http tends to produce lots of errors.
     *
     * @throws IOException if an IOException occurs during this method it will be rethrown and will not be wrapped
     */
    public final void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
            throws ServletException, IOException {
        final HttpServletRequest httpRequest = (HttpServletRequest) request;
        final HttpServletResponse httpResponse = (HttpServletResponse) response;
        try {
            //NO_FILTER set for RequestDispatcher forwards to avoid double gzipping
            if (filterNotDisabled(httpRequest)) {
                doFilter(httpRequest, httpResponse, chain);
            } else {
                chain.doFilter(request, response);
            }
        } catch (final Throwable throwable) {
            logThrowable(throwable, httpRequest);
        }
    }

    /**
     * Filters can be disabled programmatically by adding a {@link #NO_FILTER} parameter to the request.
     * This parameter is normally added to make RequestDispatcher include and forwards work.
     *
     * @param httpRequest the request
     * @return true if NO_FILTER is not set.
     */
    protected boolean filterNotDisabled(final HttpServletRequest httpRequest) {
        return httpRequest.getAttribute(NO_FILTER) == null;
    }

    /**
     * This method should throw IOExceptions, not wrap them.
     */
    private void logThrowable(final Throwable throwable, final HttpServletRequest httpRequest)
            throws ServletException, IOException {
        StringBuffer messageBuffer = new StringBuffer("Throwable thrown during doFilter on request with URI: ")
                .append(httpRequest.getRequestURI())
                .append(" and Query: ")
                .append(httpRequest.getQueryString());
        String message = messageBuffer.toString();
        boolean matchFound = matches(throwable);
        if (matchFound) {
            try {
                if (suppressStackTraces) {
                    LOG.log(exceptionsToLogDifferentlyLevel, throwable.getMessage());
                } else {
                    LOG.log(exceptionsToLogDifferentlyLevel, throwable.getMessage(), throwable);
                }
            } catch (Exception e) {
                LOG.log(Level.SEVERE, "Could not invoke Log method for " + exceptionsToLogDifferentlyLevel, e);
            }
            if (throwable instanceof IOException) {
                throw (IOException) throwable;
            } else {
                throw new ServletException(message, throwable);
            }
        } else {

            if (suppressStackTraces) {
                LOG.log(Level.WARNING, messageBuffer.append(throwable.getMessage()).append("\nTop StackTraceElement: ")
                        .append(throwable.getStackTrace()[0].toString()).toString());
            } else {
                LOG.log(Level.WARNING, messageBuffer.append(throwable.getMessage()).toString(), throwable);
            }
            if (throwable instanceof IOException) {
                throw (IOException) throwable;
            } else {
                throw new ServletException(throwable);
            }
        }
    }

    /**
     * Checks whether a throwable, its root cause if it is a {@link ServletException}, or its cause, if it is a
     * Chained Exception matches an entry in the exceptionsToLogDifferently list
     *
     * @param throwable
     * @return true if the class name of any of the throwables is found in the exceptions to log differently
     */
    private boolean matches(Throwable throwable) {
        if (exceptionsToLogDifferently == null) {
            return false;
        }
        if (exceptionsToLogDifferently.indexOf(throwable.getClass().getName()) != -1) {
            return true;
        }
        if (throwable instanceof ServletException) {
            Throwable rootCause = (((ServletException) throwable).getRootCause());
            if (exceptionsToLogDifferently.indexOf(rootCause.getClass().getName()) != -1) {
                return true;
            }
        }
        if (throwable.getCause() != null) {
            Throwable cause = throwable.getCause();
            if (exceptionsToLogDifferently.indexOf(cause.getClass().getName()) != -1) {
                return true;
            }
        }
        return false;
    }

    /**
     * Initialises the filter.
     * <p/>
     * Calls template method {@link #doInit(javax.servlet.FilterConfig)} to perform any filter specific initialisation.
     */
    public final void init(final FilterConfig filterConfig) throws ServletException {
        try {

            this.filterConfig = filterConfig;
            processInitParams(filterConfig);

            // Attempt to initialise this filter
            doInit(filterConfig);
        } catch (final Exception e) {
            LOG.log(Level.SEVERE, "Could not initialise servlet filter.", e);
            throw new ServletException("Could not initialise servlet filter.", e);
        }
    }

    /**
     * Processes initialisation parameters. These are configured in web.xml in accordance with the
     * Servlet specification using the following syntax:
     * <pre>
     * <filter>
     *      ...
     *      <init-param>
     *          <param-name>blah</param-name>
     *          <param-value>blahvalue</param-value>
     *      </init-param>
     *      ...
     * </filter>
     * </pre>
     * @throws ServletException
     */
    protected void processInitParams(final FilterConfig config) throws ServletException {
        String exceptions = config.getInitParameter("exceptionsToLogDifferently");
        String level = config.getInitParameter("exceptionsToLogDifferentlyLevel");
        String suppressStackTracesString = config.getInitParameter("suppressStackTraces");
        suppressStackTraces = Boolean.valueOf(suppressStackTracesString).booleanValue();
        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("Suppression of stack traces enabled for " + this.getClass().getName());
        }

        if (exceptions != null) {
            validateMandatoryParameters(exceptions, level);
            setLevel(level);
            exceptionsToLogDifferently = exceptions;
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("Different logging levels configured for " + this.getClass().getName());
            }
        }
    }

    private void validateMandatoryParameters(String exceptions, String level) throws ServletException {
        if ((exceptions != null && level == null) || (level != null && exceptions == null)) {
            throw new ServletException("Invalid init-params. Both exceptionsToLogDifferently"
                    + " and exceptionsToLogDifferentlyLevelvalue should be specified if one is"
                    + " specified.");
        }
    }

    private void setLevel(String level) throws ServletException {
        //Check correct level set
        if (level.equals("finest")) {
            exceptionsToLogDifferentlyLevel = Level.FINEST;
        } else if (level.equals("fine")) {
            exceptionsToLogDifferentlyLevel = Level.FINE;
        } else if (level.equals("info")) {
            exceptionsToLogDifferentlyLevel = Level.INFO;
        } else if (level.equals("warning")) {
            exceptionsToLogDifferentlyLevel = Level.WARNING;
        } else if (level.equals("severe")) {
            exceptionsToLogDifferentlyLevel = Level.SEVERE;
        } else {
            throw new ServletException("Invalid init-params value for \"exceptionsToLogDifferentlyLevel\"."
                    + "Must be one of finest, fine, info, warning or severe.");
        }
    }

    /**
     * Destroys the filter. Calls template method {@link #doDestroy()}  to perform any filter specific
     * destruction tasks.
     */
    public final void destroy() {
        this.filterConfig = null;
        doDestroy();
    }

    /**
     * Checks if request accepts the named encoding.
     */
    protected boolean acceptsEncoding(final HttpServletRequest request, final String name) {
        final boolean accepts = headerContains(request, "Accept-Encoding", name);
        return accepts;
    }

    /**
     * Checks if request contains the header value.
     */
    private boolean headerContains(final HttpServletRequest request, final String header, final String value) {

        logRequestHeaders(request);

        final Enumeration accepted = request.getHeaders(header);
        while (accepted.hasMoreElements()) {
            final String headerValue = (String) accepted.nextElement();
            if (headerValue.indexOf(value) != -1) {
                return true;
            }
        }
        return false;
    }

    /**
     * Logs the request headers, if debug is enabled.
     *
     * @param request
     */
    protected void logRequestHeaders(final HttpServletRequest request) {
        if (LOG.isLoggable(Level.FINE)) {
            Map headers = new HashMap();
            Enumeration enumeration = request.getHeaderNames();
            StringBuffer logLine = new StringBuffer();
            logLine.append("Request Headers");
            while (enumeration.hasMoreElements()) {
                String name = (String) enumeration.nextElement();
                String headerValue = request.getHeader(name);
                headers.put(name, headerValue);
                logLine.append(": ").append(name).append(" -> ").append(headerValue);
            }
            LOG.fine(logLine.toString());
        }
    }


    /**
     * A template method that performs any Filter specific destruction tasks.
     * Called from {@link #destroy()}
     */
    protected abstract void doDestroy();


    /**
     * A template method that performs the filtering for a request.
     * Called from {@link #doFilter(ServletRequest,ServletResponse,FilterChain)}.
     */
    protected abstract void doFilter(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse,
                                     final FilterChain chain) throws Throwable;

    /**
     * A template method that performs any Filter specific initialisation tasks.
     * Called from {@link #init(FilterConfig)}.
     * @param filterConfig
     */
    protected abstract void doInit(FilterConfig filterConfig) throws Exception;

    /**
     * Returns the filter config.
     */
    public FilterConfig getFilterConfig() {
        return filterConfig;
    }

    /**
     * Determine whether the user agent accepts GZIP encoding. This feature is part of HTTP1.1.
     * If a browser accepts GZIP encoding it will advertise this by including in its HTTP header:
     * <p/>
     * <code>
     * Accept-Encoding: gzip
     * </code>
     * <p/>
     * Requests which do not accept GZIP encoding fall into the following categories:
     * <ul>
     * <li>Old browsers, notably IE 5 on Macintosh.
     * <li>Search robots such as yahoo. While there are quite a few bots, they only hit individual
     * pages once or twice a day. Note that Googlebot as of August 2004 now accepts GZIP.
     * <li>Internet Explorer through a proxy. By default HTTP1.1 is enabled but disabled when going
     * through a proxy. 90% of non gzip requests are caused by this.
     * <li>Site monitoring tools
     * </ul>
     * As of September 2004, about 34% of requests coming from the Internet did not accept GZIP encoding.
     *
     * @param request
     * @return true, if the User Agent request accepts GZIP encoding
     */
    protected boolean acceptsGzipEncoding(HttpServletRequest request) {
        return acceptsEncoding(request, "gzip");
    }

}

posted @ 2009-07-09 11:59 cxccbv 阅读(634) 评论(0) 编辑

Ehcache源码研究(三)Element

/**
 *  Copyright 2003-2008 Luck Consulting Pty Ltd
 *
 *  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.
 */


package net.sf.ehcache;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A Cache Element, consisting of a key, value and attributes.
 * <p/>
 * From ehcache-1.2, Elements can have keys and values that are Serializable or Objects. To preserve backward
 * compatibility, special accessor methods for Object keys and values are provided: {@link #getObjectKey()} and
 * {@link #getObjectValue()}. If placing Objects in ehcache, developers must use the new getObject... methods to
 * avoid CacheExceptions. The get... methods are reserved for Serializable keys and values.
 *
 * @author Greg Luck
 * @version $Id: Element.java 978 2009-06-16 23:29:59Z gregluck $
 */
public class Element implements Serializable, Cloneable {
    /**
     * serial version
     * Updated for version 1.2 and again for 1.2.1
     */
    private static final long serialVersionUID = 3343087714201120157L;

    private static final Logger LOG = Logger.getLogger(Element.class.getName());

    private static final long ONE_SECOND = 1000L;

    /**
     * the cache key.
     */
    private final Object key;

    /**
     * the value.
     */
    private Object value;

    /**
     * version of the element. System.currentTimeMillis() is used to compute version for updated elements. That
     * way, the actual version of the updated element does not need to be checked.
     */
    private long version;

    /**
     * The creation time.
     */
    private long creationTime;

    /**
     * The last access time.
     */
    private long lastAccessTime;

    /**
     * The next to last access time. Used by the expiry mechanism
     */
    private long nextToLastAccessTime;

    /**
     * The number of times the element was hit.
     */
    private long hitCount;

    /**
     * The amount of time for the element to live, in seconds. 0 indicates unlimited.
     */
    private int timeToLive;

    /**
     * The amount of time for the element to idle, in seconds. 0 indicates unlimited.
     */
    private int timeToIdle;

    /**
     * If there is an Element in the Cache and it is replaced with a new Element for the same key,
     * then both the version number and lastUpdateTime should be updated to reflect that. The creation time
     * will be the creation time of the new Element, not the original one, so that TTL concepts still work.
     */
    private long lastUpdateTime;

    /**
     * Whether the element is eternal, i.e. never expires.
     */
    private boolean eternal;

    /**
     * Whether any combination of eternal, TTL or TTI has been set.
     */
    private boolean lifespanSet;

    /**
     * A full constructor.
     * <p/>
     * Creation time is set to the current time. Last Access Time and Previous To Last Access Time
     * are not set.
     *
     * @since .4
     */
    public Element(Serializable key, Serializable value, long version) {
        this((Object) key, (Object) value, version);

    }

    /**
     * A full constructor.
     * <p/>
     * Creation time is set to the current time. Last Access Time and Previous To Last Access Time
     * are not set.
     *
     * @since 1.2
     */
    public Element(Object key, Object value, long version) {
        this.key = key;
        this.value = value;
        this.version = version;
        creationTime = System.currentTimeMillis();
        hitCount = 0;
    }

    /**
     * Constructor.
     *
     * @since 1.3
     */
    public Element(Object key, Object value, long version,
                   long creationTime, long lastAccessTime,
                   long nextToLastAccessTime, long lastUpdateTime,
                   long hitCount) {
        this.key = key;
        this.value = value;
        this.version = version;
        this.creationTime = creationTime;
        this.lastAccessTime = lastAccessTime;
        this.nextToLastAccessTime = nextToLastAccessTime;
        this.lastUpdateTime = lastUpdateTime;
        this.hitCount = hitCount;
    }


    /**
     * Constructor used by ehcache-server
     *
     * @param key               any non null value
     * @param value             any value, including nulls
     * @param eternal           specify as non-null to override cache configuration
     * @param timeToIdleSeconds specify as non-null to override cache configuration
     * @param timeToLiveSeconds specify as non-null to override cache configuration
     */
    public Element(Object key, Object value,
                   Boolean eternal, Integer timeToIdleSeconds, Integer timeToLiveSeconds) {
        this.key = key;
        this.value = value;
        if (eternal != null) {
            setEternal(eternal.booleanValue());
        }
        if (timeToIdleSeconds != null) {
            setTimeToIdle(timeToIdleSeconds.intValue());
        }
        if (timeToLiveSeconds != null) {
            setTimeToLive(timeToLiveSeconds.intValue());
        }
        creationTime = System.currentTimeMillis();
    }

    /**
     * Constructor.
     *
     * @param key
     * @param value
     */
    public Element(Serializable key, Serializable value) {
        this((Object) key, (Object) value, 1L);
    }

    /**
     * Constructor.
     *
     * @param key
     * @param value
     * @since 1.2
     */
    public Element(Object key, Object value) {
        this(key, value, 1L);
    }


    /**
     * Gets the key attribute of the Element object.
     *
     * @return The key value. If the key is not Serializable, null is returned and an info log message emitted
     * @see #getObjectKey()
     */
    public final Serializable getKey() {
        Serializable keyAsSerializable;
        try {
            keyAsSerializable = (Serializable) key;
        } catch (Exception e) {
            throw new CacheException("The key " + key + " is not Serializable. Consider using Element#getObjectKey()");
        }
        return keyAsSerializable;
    }

    /**
     * Gets the key attribute of the Element object.
     * <p/>
     * This method is provided for those wishing to use ehcache as a memory only cache
     * and enables retrieval of non-Serializable values from elements.
     *
     * @return The key as an Object. i.e no restriction is placed on it
     * @see #getKey()
     */
    public final Object getObjectKey() {
        return key;
    }

    /**
     * Gets the value attribute of the Element object.
     *
     * @return The value which must be Serializable. If not use {@link #getObjectValue}. If the value is not Serializable, null is returned and an info log message emitted
     * @see #getObjectValue()
     */
    public final Serializable getValue() {
        Serializable valueAsSerializable;
        try {
            valueAsSerializable = (Serializable) value;
        } catch (Exception e) {
            throw new CacheException("The value " + value + " for key " + key +
                    " is not Serializable. Consider using Element#getObjectKey()");
        }
        return valueAsSerializable;
    }

    /**
     * Gets the value attribute of the Element object as an Object.
     * <p/>
     * This method is provided for those wishing to use ehcache as a memory only cache
     * and enables retrieval of non-Serializable values from elements.
     *
     * @return The value as an Object.  i.e no restriction is placed on it
     * @see #getValue()
     * @since 1.2
     */
    public final Object getObjectValue() {
        return value;
    }

    /**
     * Equals comparison with another element, based on the key.
     */
    public final boolean equals(Object object) {
        if (object == null || !(object instanceof Element)) {
            return false;
        }

        Element element = (Element) object;
        if (key == null || element.getObjectKey() == null) {
            return false;
        }

        return key.equals(element.getObjectKey());
    }

    /**
     * Sets time to Live
     *
     * @param timeToLiveSeconds the number of seconds to live
     */
    public void setTimeToLive(int timeToLiveSeconds) {
        this.timeToLive = timeToLiveSeconds;
        lifespanSet = true;
    }

    /**
     * Sets time to idle
     *
     * @param timeToIdleSeconds the number of seconds to idle
     */
    public void setTimeToIdle(int timeToIdleSeconds) {
        this.timeToIdle = timeToIdleSeconds;
        lifespanSet = true;
    }

    /**
     * Gets the hashcode, based on the key.
     */
    public final int hashCode() {
        return key.hashCode();
    }

    /**
     * Sets the version attribute of the ElementAttributes object.
     *
     * @param version The new version value
     */
    public final void setVersion(long version) {
        this.version = version;
    }

    /**
     * Gets the creationTime attribute of the ElementAttributes object.
     *
     * @return The creationTime value
     */
    public final long getCreationTime() {
        return creationTime;
    }

    /**
     * Calculates the latest of creation and update time
     * @return if never updated, creation time is returned, otherwise updated time
     */
    public final long getLatestOfCreationAndUpdateTime() {
        if (lastUpdateTime == 0) {
            return creationTime;
        } else {
            return lastUpdateTime;
        }
    }

    /**
     * Sets the creationTime attribute of the ElementAttributes object.
     */
    public final void setCreateTime() {
        creationTime = System.currentTimeMillis();
    }

    /**
     * Gets the version attribute of the ElementAttributes object.
     *
     * @return The version value
     */
    public final long getVersion() {
        return version;
    }

    /**
     * Gets the last access time.
     * Access means a get. So a newly created {@link Element}
     * will have a last access time equal to its create time.
     */
    public final long getLastAccessTime() {
        return lastAccessTime;
    }

    /**
     * Gets the next to last access time.
     *
     * @see #getLastAccessTime()
     */
    public final long getNextToLastAccessTime() {
        return nextToLastAccessTime;
    }

    /**
     * Gets the hit count on this element.
     */
    public final long getHitCount() {
        return hitCount;
    }

    /**
     * Resets the hit count to 0 and the last access time to 0.
     */
    public final void resetAccessStatistics() {
        lastAccessTime = 0;
        nextToLastAccessTime = 0;
        hitCount = 0;
    }

    /**
     * Sets the last access time to now.
     */
    public final void updateAccessStatistics() {
        nextToLastAccessTime = lastAccessTime;
        lastAccessTime = System.currentTimeMillis();
        hitCount++;
    }

    /**
     * Sets the last access time to now.
     */
    public final void updateUpdateStatistics() {
        lastUpdateTime = System.currentTimeMillis();
        version = lastUpdateTime;
    }


    /**
     * Returns a {@link String} representation of the {@link Element}.
     */
    public final String toString() {
        StringBuffer sb = new StringBuffer();

        sb.append("[ key = ").append(key)
                .append(", value=").append(value)
                .append(", version=").append(version)
                .append(", hitCount=").append(hitCount)
                .append(", CreationTime = ").append(this.getCreationTime())
                .append(", LastAccessTime = ").append(this.getLastAccessTime())
                .append(" ]");

        return sb.toString();
    }

    /**
     * Clones an Element. A completely new object is created, with no common references with the
     * existing one.
     * <p/>
     * This method will not work unless the Object is Serializable
     * <p/>
     * Warning: This can be very slow on large object graphs. If you use this method
     * you should write a performance test to verify suitability.
     *
     * @return a new {@link Element}, with exactly the same field values as the one it was cloned from.
     * @throws CloneNotSupportedException
     */
    public final Object clone() throws CloneNotSupportedException {
        //Not used. Just to get code inspectors to shut up
        super.clone();

        Element element = new Element(deepCopy(key), deepCopy(value), version);
        element.creationTime = creationTime;
        element.lastAccessTime = lastAccessTime;
        element.nextToLastAccessTime = nextToLastAccessTime;
        element.hitCount = hitCount;
        return element;
    }

    private Object deepCopy(Object oldValue) {
        Serializable newValue = null;
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            oos = new ObjectOutputStream(bout);
            oos.writeObject(oldValue);
            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
            ois = new ObjectInputStream(bin);
            newValue = (Serializable) ois.readObject();
        } catch (IOException e) {
            LOG.log(Level.SEVERE, "Error cloning Element with key " + key
                    + " during serialization and deserialization of value");
        } catch (ClassNotFoundException e) {
            LOG.log(Level.SEVERE, "Error cloning Element with key " + key
                    + " during serialization and deserialization of value");
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
                if (ois != null) {
                    ois.close();
                }
            } catch (Exception e) {
                LOG.log(Level.SEVERE, "Error closing Stream");
            }
        }
        return newValue;
    }

    /**
     * The size of this object in serialized form. This is not the same
     * thing as the memory size, which is JVM dependent. Relative values should be meaningful,
     * however.
     * <p/>
     * Warning: This method can be <b>very slow</b> for values which contain large object graphs.
     * <p/>
     * If the key or value of the Element is not Serializable, an error will be logged and 0 will be returned.
     *
     * @return The serialized size in bytes
     */
    public final long getSerializedSize() {

        if (!isSerializable()) {
            return 0;
        }
        long size = 0;
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(bout);
            oos.writeObject(this);
            size = bout.size();
            return size;
        } catch (IOException e) {
            LOG.log(Level.FINE, "Error measuring element size for element with key " + key + ". Cause was: " + e.getMessage());
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
            } catch (Exception e) {
                LOG.log(Level.SEVERE, "Error closing ObjectOutputStream");
            }
        }

        return size;
    }

    /**
     * Whether the element may be Serialized.
     * <p/>
     * While Element implements Serializable, it is possible to create non Serializable elements
     * for use in MemoryStores. This method checks that an instance of Element really is Serializable
     * and will not throw a NonSerializableException if Serialized.
     * <p/>
     * This method was tweaked in 1.6 as it has been shown that Serializable classes can be serializaed as can
     * null, regardless of what class it is a null of. ObjectOutputStream.write(null) works and ObjectInputStream.read()
     * will read null back.
     *
     * @return true if the element is Serializable
     * @since 1.2
     */
    public final boolean isSerializable() {
        return isKeySerializable() && (value instanceof Serializable || value == null);
    }

    /**
     * Whether the element's key may be Serialized.
     * <p/>
     * While Element implements Serializable, it is possible to create non Serializable elements and/or
     * non Serializable keys for use in MemoryStores.
     * <p/>
     * This method checks that an instance of an Element's key really is Serializable
     * and will not throw a NonSerializableException if Serialized.
     *
     * @return true if the element's key is Serializable
     * @since 1.2
     */
    public final boolean isKeySerializable() {
        return key instanceof Serializable || key == null;
    }

    /**
     * If there is an Element in the Cache and it is replaced with a new Element for the same key,
     * then both the version number and lastUpdateTime should be updated to reflect that. The creation time
     * will be the creation time of the new Element, not the original one, so that TTL concepts still work.
     *
     * @return the time when the last update occured. If this is the original Element, the time will be null
     */
    public long getLastUpdateTime() {
        return lastUpdateTime;
    }

    /**
     * An element is expired if the expiration time as given by {@link #getExpirationTime()} is in the past.
     *
     * @return true if the Element is expired, otherwise false. If no lifespan has been set for the Element it is
     *         considered not able to expire.
     * @see #getExpirationTime()
     */
    public boolean isExpired() {
        if (!lifespanSet) {
            return false;
        }

        long now = System.currentTimeMillis();
        long expirationTime = getExpirationTime();

        return now > expirationTime;
    }

    /**
     * Returns the expiration time based on time to live. If this element also has a time to idle setting, the expiry
     * time will vary depending on whether the element is accessed.
     *
     * @return the time to expiration
     */
    public long getExpirationTime() {

        if (!lifespanSet || eternal || (timeToLive == 0 && timeToIdle == 0)) {
            return Long.MAX_VALUE;
        }

        long expirationTime = 0;
        long ttlExpiry = creationTime + timeToLive * ONE_SECOND;

        long mostRecentTime = Math.max(creationTime, nextToLastAccessTime);
        long ttiExpiry = mostRecentTime + timeToIdle * ONE_SECOND;

        if (timeToLive != 0 && (timeToIdle == 0 || lastAccessTime == 0)) {
            expirationTime = ttlExpiry;
        } else if (timeToLive == 0) {
            expirationTime = ttiExpiry;
        } else {
            expirationTime = Math.min(ttlExpiry, ttiExpiry);
        }
        return expirationTime;
    }

    /**
     * @return true if the element is eternal
     */
    public boolean isEternal() {
        return eternal;
    }

    /**
     * Sets whether the element is eternal.
     *
     * @param eternal
     */
    public void setEternal(boolean eternal) {
        this.eternal = eternal;
        lifespanSet = true;
    }

    /**
     * Whether any combination of eternal, TTL or TTI has been set.
     *
     * @return true if set.
     */
    public boolean isLifespanSet() {
        return lifespanSet;
    }

    /**
     * @return the time to live, in seconds
     */
    public int getTimeToLive() {
        return timeToLive;
    }

    /**
     * @return the time to idle, in seconds
     */
    public int getTimeToIdle() {
        return timeToIdle;
    }
}



posted @ 2009-07-09 11:47 cxccbv 阅读(480) 评论(0) 编辑

Ehcache源码研究(二)CacheManager

posted @ 2009-07-09 11:45 cxccbv 阅读(1233) 评论(2) 编辑

Ehcache源码研究(一)Ehcache接口

/**
 *  Copyright 2003-2008 Luck Consulting Pty Ltd
 *
 *  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.
 */

package net.sf.ehcache;

import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.event.RegisteredEventListeners;
import net.sf.ehcache.exceptionhandler.CacheExceptionHandler;
import net.sf.ehcache.extension.CacheExtension;
import net.sf.ehcache.loader.CacheLoader;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * An interface for Ehcache.
 * <p/>
 * Ehcache is the central interface. Caches have {@link Element}s and are managed
 * by the {@link CacheManager}. The Cache performs logical actions. It delegates physical
 * implementations to its {@link net.sf.ehcache.store.Store}s.
 * <p/>
 * A reference to an EhCache can be obtained through the {@link CacheManager}. An Ehcache thus obtained
 * is guaranteed to have status {@link Status#STATUS_ALIVE}. This status is checked for any method which
 * throws {@link IllegalStateException} and the same thrown if it is not alive. This would normally
 * happen if a call is made after {@link CacheManager#shutdown} is invoked.
 * <p/>
 * Statistics on cache usage are collected and made available through public methods.
 *
 * @author Greg Luck
 * @version $Id: Ehcache.java 885 2009-01-29 07:38:23Z gregluck $
 */
public interface Ehcache extends Cloneable {
    /**
     * Put an element in the cache.
     * <p/>
     * Resets the access statistics on the element, which would be the case if it has previously been
     * gotten from a cache, and is now being put back.
     * <p/>
     * Also notifies the CacheEventListener that:
     * <ul>
     * <li>the element was put, but only if the Element was actually put.
     * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
     * if it was requested
     * </ul>
     *
     * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
     * @throws IllegalStateException    if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @throws IllegalArgumentException if the element is null
     * @throws CacheException
     */
    void put(Element element) throws IllegalArgumentException, IllegalStateException,
            CacheException;

    /**
     * Put an element in the cache.
     * <p/>
     * Resets the access statistics on the element, which would be the case if it has previously been
     * gotten from a cache, and is now being put back.
     * <p/>
     * Also notifies the CacheEventListener that:
     * <ul>
     * <li>the element was put, but only if the Element was actually put.
     * <li>if the element exists in the cache, that an update has occurred, even if the element would be expired
     * if it was requested
     * </ul>
     *
     * @param element                     An object. If Serializable it can fully participate in replication and the DiskStore.
     * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
     *                                    further notification to doNotNotifyCacheReplicators cache peers
     * @throws IllegalStateException    if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @throws IllegalArgumentException if the element is null
     */
    void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException,
            IllegalStateException,
            CacheException;

    /**
     * Put an element in the cache, without updating statistics, or updating listeners. This is meant to be used
     * in conjunction with {@link #getQuiet}
     *
     * @param element An object. If Serializable it can fully participate in replication and the DiskStore.
     * @throws IllegalStateException    if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @throws IllegalArgumentException if the element is null
     */
    void putQuiet(Element element) throws IllegalArgumentException, IllegalStateException,
            CacheException;

    /**
     * Gets an element from the cache. Updates Element Statistics
     * <p/>
     * Note that the Element's lastAccessTime is always the time of this get.
     * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
     *
     * @param key a serializable value
     * @return the element, or null, if it does not exist.
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @see #isExpired
     */
    Element get(Serializable key) throws IllegalStateException, CacheException;

    /**
     * Gets an element from the cache. Updates Element Statistics
     * <p/>
     * Note that the Element's lastAccessTime is always the time of this get.
     * Use {@link #getQuiet(Object)} to peak into the Element to see its last access time with get
     *
     * @param key an Object value
     * @return the element, or null, if it does not exist.
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @see #isExpired
     * @since 1.2
     */
    Element get(Object key) throws IllegalStateException, CacheException;

    /**
     * Gets an element from the cache, without updating Element statistics. Cache statistics are
     * still updated.
     * <p/>
     *
     * @param key a serializable value
     * @return the element, or null, if it does not exist.
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @see #isExpired
     */
    Element getQuiet(Serializable key) throws IllegalStateException, CacheException;

    /**
     * Gets an element from the cache, without updating Element statistics. Cache statistics are
     * also not updated.
     * <p/>
     *
     * @param key a serializable value
     * @return the element, or null, if it does not exist.
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @see #isExpired
     * @since 1.2
     */
    Element getQuiet(Object key) throws IllegalStateException, CacheException;

    /**
     * Returns a list of all elements in the cache, whether or not they are expired.
     * <p/>
     * The returned keys are unique and can be considered a set.
     * <p/>
     * The List returned is not live. It is a copy.
     * <p/>
     * The time taken is O(n). On a single cpu 1.8Ghz P4, approximately 8ms is required
     * for each 1000 entries.
     *
     * @return a list of {@link Object} keys
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    List getKeys() throws IllegalStateException, CacheException;

    /**
     * Returns a list of all elements in the cache. Only keys of non-expired
     * elements are returned.
     * <p/>
     * The returned keys are unique and can be considered a set.
     * <p/>
     * The List returned is not live. It is a copy.
     * <p/>
     * The time taken is O(n), where n is the number of elements in the cache. On
     * a 1.8Ghz P4, the time taken is approximately 200ms per 1000 entries. This method
     * is not synchronized, because it relies on a non-live list returned from {@link #getKeys()}
     * , which is synchronised, and which takes 8ms per 1000 entries. This way
     * cache liveness is preserved, even if this method is very slow to return.
     * <p/>
     * Consider whether your usage requires checking for expired keys. Because
     * this method takes so long, depending on cache settings, the list could be
     * quite out of date by the time you get it.
     *
     * @return a list of {@link Object} keys
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    List getKeysWithExpiryCheck() throws IllegalStateException, CacheException;

    /**
     * Returns a list of all elements in the cache, whether or not they are expired.
     * <p/>
     * The returned keys are not unique and may contain duplicates. If the cache is only
     * using the memory store, the list will be unique. If the disk store is being used
     * as well, it will likely contain duplicates, because of the internal store design.
     * <p/>
     * The List returned is not live. It is a copy.
     * <p/>
     * The time taken is O(log n). On a single cpu 1.8Ghz P4, approximately 6ms is required
     * for 1000 entries and 36 for 50000.
     * <p/>
     * This is the fastest getKeys method
     *
     * @return a list of {@link Object} keys
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    List getKeysNoDuplicateCheck() throws IllegalStateException;

    /**
     * Removes an {@link net.sf.ehcache.Element} from the Cache. This also removes it from any
     * stores it may be in.
     * <p/>
     * Also notifies the CacheEventListener after the element was removed.
     *
     * @param key
     * @return true if the element was removed, false if it was not found in the cache
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    boolean remove(Serializable key) throws IllegalStateException;

    /**
     * Removes an {@link net.sf.ehcache.Element} from the Cache. This also removes it from any
     * stores it may be in.
     * <p/>
     * Also notifies the CacheEventListener after the element was removed, but only if an Element
     * with the key actually existed.
     *
     * @param key
     * @return true if the element was removed, false if it was not found in the cache
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @since 1.2
     */
    boolean remove(Object key) throws IllegalStateException;

    /**
     * Removes an {@link net.sf.ehcache.Element} from the Cache. This also removes it from any
     * stores it may be in.
     * <p/>
     * Also notifies the CacheEventListener after the element was removed, but only if an Element
     * with the key actually existed.
     *
     * @param key
     * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
     *                                    further notification to doNotNotifyCacheReplicators cache peers
     * @return true if the element was removed, false if it was not found in the cache
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    boolean remove(Serializable key, boolean doNotNotifyCacheReplicators) throws IllegalStateException;

    /**
     * Removes an {@link net.sf.ehcache.Element} from the Cache. This also removes it from any
     * stores it may be in.
     * <p/>
     * Also notifies the CacheEventListener after the element was removed, but only if an Element
     * with the key actually existed.
     *
     * @param key
     * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer, in which case this put should not initiate a
     *                                    further notification to doNotNotifyCacheReplicators cache peers
     * @return true if the element was removed, false if it was not found in the cache
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    boolean remove(Object key, boolean doNotNotifyCacheReplicators) throws IllegalStateException;

    /**
     * Removes an {@link net.sf.ehcache.Element} from the Cache, without notifying listeners. This also removes it from any
     * stores it may be in.
     * <p/>
     *
     * @param key
     * @return true if the element was removed, false if it was not found in the cache
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    boolean removeQuiet(Serializable key) throws IllegalStateException;

    /**
     * Removes an {@link net.sf.ehcache.Element} from the Cache, without notifying listeners. This also removes it from any
     * stores it may be in.
     * <p/>
     *
     * @param key
     * @return true if the element was removed, false if it was not found in the cache
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @since 1.2
     */
    boolean removeQuiet(Object key) throws IllegalStateException;

    /**
     * Removes all cached items.
     *
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    void removeAll() throws IllegalStateException, CacheException;

    /**
     * Removes all cached items.
     *
     * @param doNotNotifyCacheReplicators whether the put is coming from a doNotNotifyCacheReplicators cache peer,
     *                                    in which case this put should not initiate a further notification to doNotNotifyCacheReplicators cache peers
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    void removeAll(boolean doNotNotifyCacheReplicators) throws IllegalStateException, CacheException;

    /**
     * Flushes all cache items from memory to the disk store, and from the DiskStore to disk.
     *
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    void flush() throws IllegalStateException, CacheException;

    /**
     * Gets the size of the cache. This is a subtle concept. See below.
     * <p/>
     * The size is the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.MemoryStore} plus
     * the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.DiskStore}.
     * <p/>
     * This number is the actual number of elements, including expired elements that have
     * not been removed.
     * <p/>
     * Expired elements are removed from the the memory store when
     * getting an expired element, or when attempting to spool an expired element to
     * disk.
     * <p/>
     * Expired elements are removed from the disk store when getting an expired element,
     * or when the expiry thread runs, which is once every five minutes.
     * <p/>
     * To get an exact size, which would exclude expired elements, use {@link #getKeysWithExpiryCheck()}.size(),
     * although see that method for the approximate time that would take.
     * <p/>
     * To get a very fast result, use {@link #getKeysNoDuplicateCheck()}.size(). If the disk store
     * is being used, there will be some duplicates.
     *
     * @return The size value
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    int getSize() throws IllegalStateException, CacheException;

    /**
     * Gets the size of the memory store for this cache
     * <p/>
     * Warning: This method can be very expensive to run. Allow approximately 1 second
     * per 1MB of entries. Running this method could create liveness problems
     * because the object lock is held for a long period
     * <p/>
     *
     * @return the approximate size of the memory store in bytes
     * @throws IllegalStateException
     */
    long calculateInMemorySize() throws IllegalStateException, CacheException;

    /**
     * Returns the number of elements in the memory store.
     *
     * @return the number of elements in the memory store
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    long getMemoryStoreSize() throws IllegalStateException;

    /**
     * Returns the number of elements in the disk store.
     *
     * @return the number of elements in the disk store.
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     */
    int getDiskStoreSize() throws IllegalStateException;

    /**
     * Gets the status attribute of the Cache.
     *
     * @return The status value from the Status enum class
     */
    Status getStatus();

    /**
     * Gets the cache name.
     */
    String getName();

    /**
     * Sets the cache name which will name.
     *
     * @param name the name of the cache. Should not be null.
     */
    void setName(String name);

    /**
     * Returns a {@link String} representation of {@link net.sf.ehcache.Cache}.
     */
    String toString();

    /**
     * Checks whether this cache element has expired.
     * <p/>
     * The element is expired if:
     * <ol>
     * <li> the idle time is non-zero and has elapsed, unless the cache is eternal; or
     * <li> the time to live is non-zero and has elapsed, unless the cache is eternal; or
     * <li> the value of the element is null.
     * </ol>
     *
     * @param element the element to check
     * @return true if it has expired
     * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
     * @throws NullPointerException  if the element is null
     */
    boolean isExpired(Element element) throws IllegalStateException, NullPointerException;

    /**
     * Clones a cache. This is only legal if the cache has not been
     * initialized. At that point only primitives have been set and no
     * {@link net.sf.ehcache.store.MemoryStore} or {@link net.sf.ehcache.store.DiskStore} has been created.
     * <p/>
     * A new, empty, RegisteredEventListeners is created on clone.
     * <p/>
     *
     * @return an object of type {@link net.sf.ehcache.Cache}
     * @throws CloneNotSupportedException
     */
    Object clone() throws CloneNotSupportedException;


    /**
     * Use this to access the service in order to register and unregister listeners
     *
     * @return the RegisteredEventListeners instance for this cache.
     */
    RegisteredEventListeners getCacheEventNotificationService();

    /**
     * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
     *
     * @return true if an element matching the key is found in memory
     */
    boolean isElementInMemory(Serializable key);

    /**
     * Whether an Element is stored in the cache in Memory, indicating a very low cost of retrieval.
     *
     * @return true if an element matching the key is found in memory
     * @since 1.2
     */
    boolean isElementInMemory(Object key);

    /**
     * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
     *
     * @return true if an element matching the key is found in the diskStore
     */
    boolean isElementOnDisk(Serializable key);

    /**
     * Whether an Element is stored in the cache on Disk, indicating a higher cost of retrieval.
     *
     * @return true if an element matching the key is found in the diskStore
     * @since 1.2
     */
    boolean isElementOnDisk(Object key);

    /**
     * The GUID for this cache instance can be used to determine whether two cache instance references
     * are pointing to the same cache.
     *
     * @return the globally unique identifier for this cache instance. This is guaranteed to be unique.
     * @since 1.2
     */
    String getGuid();

    /**
     * Gets the CacheManager managing this cache. For a newly created cache this will be null until
     * it has been added to a CacheManager.
     *
     * @return the manager or null if there is none
     */
    CacheManager getCacheManager();

    /**
     * Resets statistics counters back to 0.
     */
    void clearStatistics();

    /**
     * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
     *
     * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
     */
    public int getStatisticsAccuracy();


    /**
     * Sets the statistics accuracy.
     *
     * @param statisticsAccuracy one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
     */
    public void setStatisticsAccuracy(int statisticsAccuracy);


    /**
     * Causes all elements stored in the Cache to be synchronously checked for expiry, and if expired, evicted.
     */
    void evictExpiredElements();

    /**
     * An inexpensive check to see if the key exists in the cache.
     *
     * @param key the key to check for
     * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
     */
    boolean isKeyInCache(Object key);

    /**
     * An extremely expensive check to see if the value exists in the cache.
     *
     * @param value to check for
     * @return true if an Element matching the key is found in the cache. No assertions are made about the state of the Element.
     */
    boolean isValueInCache(Object value);

    /**
     * Gets an immutable Statistics object representing the Cache statistics at the time. How the statistics are calculated
     * depends on the statistics accuracy setting. The only aspect of statistics sensitive to the accuracy setting is
     * object size. How that is calculated is discussed below.
     * <h3>Best Effort Size</h3>
     * This result is returned when the statistics accuracy setting is {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
     * <p/>
     * The size is the number of {@link Element}s in the {@link net.sf.ehcache.store.MemoryStore} plus
     * the number of {@link Element}s in the {@link net.sf.ehcache.store.DiskStore}.
     * <p/>
     * This number is the actual number of elements, including expired elements that have
     * not been removed. Any duplicates between stores are accounted for.
     * <p/>
     * Expired elements are removed from the the memory store when
     * getting an expired element, or when attempting to spool an expired element to
     * disk.
     * <p/>
     * Expired elements are removed from the disk store when getting an expired element,
     * or when the expiry thread runs, which is once every five minutes.
     * <p/>
     * <h3>Guaranteed Accuracy Size</h3>
     * This result is returned when the statistics accuracy setting is {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}.
     * <p/>
     * This method accounts for elements which might be expired or duplicated between stores. It take approximately
     * 200ms per 1000 elements to execute.
     * <h3>Fast but non-accurate Size</h3>
     * This result is returned when the statistics accuracy setting is {@link Statistics#STATISTICS_ACCURACY_NONE}.
     * <p/>
     * The number given may contain expired elements. In addition if the DiskStore is used it may contain some double
     * counting of elements. It takes 6ms for 1000 elements to execute. Time to execute is O(log n). 50,000 elements take
     * 36ms.
     *
     * @return the number of elements in the ehcache, with a varying degree of accuracy, depending on accuracy setting.
     * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
     */
    Statistics getStatistics() throws IllegalStateException;


    /**
     * Sets the CacheManager
     *
     * @param cacheManager the CacheManager for this cache to use.
     */
    void setCacheManager(CacheManager cacheManager);


    /**
     * Accessor for the BootstrapCacheLoader associated with this cache. For testing purposes.
     *
     * @return the BootstrapCacheLoader to use
     */
    BootstrapCacheLoader getBootstrapCacheLoader();

    /**
     * Sets the bootstrap cache loader.
     *
     * @param bootstrapCacheLoader the loader to be used
     * @throws CacheException if this method is called after the cache is initialized
     */
    void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) throws CacheException;


    /**
     * DiskStore paths can conflict between CacheManager instances. This method allows the path to be changed.
     *
     * @param diskStorePath the new path to be used.
     * @throws CacheException if this method is called after the cache is initialized
     */
    void setDiskStorePath(String diskStorePath) throws CacheException;

    /**
     * Newly created caches do not have a {@link net.sf.ehcache.store.MemoryStore} or a {@link net.sf.ehcache.store.DiskStore}.
     * <p/>
     * This method creates those and makes the cache ready to accept elements
     */
    void initialise();

    /**
     * Bootstrap command. This must be called after the Cache is intialised, during
     * CacheManager initialisation. If loads are synchronous, they will complete before the CacheManager
     * initialise completes, otherwise they will happen in the background.
     */
    void bootstrap();

    /**
     * Flushes all cache items from memory to auxilliary caches and close the auxilliary caches.
     * <p/>
     * Should be invoked only by CacheManager.
     *
     * @throws IllegalStateException if the cache is not {@link Status#STATUS_ALIVE}
     */
    public void dispose() throws IllegalStateException;

    /**
     * Gets the cache configuration this cache was created with.
     * <p/>
     * Things like listeners that are added dynamically are excluded.
     */
    CacheConfiguration getCacheConfiguration();

    /**
     * Register a {@link CacheExtension} with the cache. It will then be tied into the cache lifecycle.
     * <p/>
     * If the CacheExtension is not initialised, initialise it.
     */
    public void registerCacheExtension(CacheExtension cacheExtension);

    /**
     * Unregister a {@link CacheExtension} with the cache. It will then be detached from the cache lifecycle.
     */
    public void unregisterCacheExtension(CacheExtension cacheExtension);


    /**
     *
     * @return the cache extensions as a live list
     */
    public List<CacheExtension> getRegisteredCacheExtensions();

    /**
     * The average get time in ms.
     */
    public float getAverageGetTime();

    /**
     * Sets an ExceptionHandler on the Cache. If one is already set, it is overwritten.
     */
    public void setCacheExceptionHandler(CacheExceptionHandler cacheExceptionHandler);

    /**
     * Sets an ExceptionHandler on the Cache. If one is already set, it is overwritten.
     */
    public CacheExceptionHandler getCacheExceptionHandler();

    /**
     * Register a {@link CacheLoader} with the cache. It will then be tied into the cache lifecycle.
     * <p/>
     * If the CacheLoader is not initialised, initialise it.
     *
     * @param cacheLoader A Cache Loader to register
     */
    public void registerCacheLoader(CacheLoader cacheLoader);

    /**
     * Unregister a {@link CacheLoader} with the cache. It will then be detached from the cache lifecycle.
     *
     * @param cacheLoader A Cache Loader to unregister
     */
    public void unregisterCacheLoader(CacheLoader cacheLoader);


    /**
     *
     * @return the cache loaders as a live list
     */
    public List<CacheLoader> getRegisteredCacheLoaders();

    /**
     * This method will return, from the cache, the object associated with
     * the argument "key".
     * <p/>
     * If the object is not in the cache, the associated
     * cache loader will be called. That is either the CacheLoader passed in, or if null, the one associated with the cache.
     * If both are null, no load is performed and null is returned.
     * <p/>
     * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
     * are synchronized.
     *
     * @param key key whose associated value is to be returned.
     * @param loader the override loader to use. If null, the cache's default loader will be used
     * @param loaderArgument an argument to pass to the CacheLoader.
     * @return an element if it existed or could be loaded, otherwise null
     * @throws CacheException
     */
    public Element getWithLoader(Object key, CacheLoader loader, Object loaderArgument) throws CacheException;

    /**
     * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
     * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
     * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
     * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
     * the object. If no "arg" value is provided a null will be passed to the loadAll method. The storing of null values in the cache
     * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
     * the cache. In both cases a null is returned.
     * <p/>
     * <p/>
     * Note. If the getAll exceeds the maximum cache size, the returned map will necessarily be less than the number specified.
     * <p/>
     * Because this method may take a long time to complete, it is not synchronized. The underlying cache operations
     * are synchronized.
     * <p/>
     * The constructs package provides similar functionality using the
     * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
     * @param keys a collection of keys to be returned/loaded
     * @param loaderArgument an argument to pass to the CacheLoader.
     * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
     * @throws CacheException
     */
    public Map getAllWithLoader(Collection keys, Object loaderArgument) throws CacheException;


    /**
     * The load method provides a means to "pre load" the cache. This method will, asynchronously, load the specified
     * object into the cache using the associated cacheloader. If the object already exists in the cache, no action is
     * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
     * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
     * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
     * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
     * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
     * the object in the cache. In both cases a null is returned.
     * <p/>
     * The Ehcache native API provides similar functionality to loaders using the
     * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
     *
     * @param key key whose associated value to be loaded using the associated cacheloader if this cache doesn't contain it.
     * @throws CacheException
     */
    public void load(final Object key) throws CacheException;


    /**
     * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
     * the specified objects into the cache using the associated cache loader. If the an object already exists in the
     * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
     * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
     * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
     * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
     * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
     * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
     * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
     * If no "arg" value is provided a null will be passed to the loadAll method.
     * <p/>
     * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
     * cacheloader if this cache doesn't contain them.
     * <p/>
     * The Ehcache native API provides similar functionality to loaders using the
     * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
     */
    public void loadAll(final Collection keys, final Object argument) throws CacheException;



    /**
     * Whether this cache is disabled. "Disabled" means:
     * <ol>
     * <li>bootstrap is disabled
     * <li>puts are discarded
     * <li>putQuites are discarded
     * </ol>
     * In all other respects the cache continues as it is.
     * <p/>
     * You can disable and enable a cache programmatically through the {@link #setDisabled(boolean)} method.
     * <p/>
     * @return true if the cache is disabled.
     */
    public boolean isDisabled();

    /**
     * Disables or enables this cache. This call overrides the previous value of disabled.
     * <p/>
     * @param disabled true if you wish to disable, false to enable
     * @see #isDisabled()
     */
    public void setDisabled(boolean disabled);


}

posted @ 2009-07-09 11:38 cxccbv 阅读(1206) 评论(3) 编辑

Ehcache学习(八)实例二

Ehcache使用实例(二)

Cache使用

l 得到一个Cache引用

获得一个sampleCache1的引用,从官方下载ehcache.xml,在ehcache.xml中已经有配置好的缓存,大家直接使用就可以,或是做测试,如果说真正使用的时候,最后自己手动配置一个比较好。

Cache cache = manager.getCache(“sampleCache1”);

l 使用Cache

Put一个Element到cache中

Cache cache = manager.getCache(“sampleCache1”);

Element element = new Element(“key1”,”value1”);

cache.put(element);

update一个element时,只要在构造element时将相同的key传入,在调用cache.put(element),这是Ehcache会根据key到缓存中找到对应的element并做更新。

Cache cache = manager.getCache(“sampleCache1”);

Cache.put(new Element(“key1”, “value1”));

//更新element

Cache.put(new Element(“key1”, “value2”));

根据key取得对应element的序列化value值

Cache cache = manager.getCache(“sampleCache1”);

Element element = cache.get(“key1”);

Serializable value = element.getValue();

根据key取得对应element的非序列化value值

Cache cache = manager.getcache(“samplecache1”);

Element element = cache.get(“key1”);

Ojbect value = element.getObjectValue();

http://blog.csdn.net/mgoann/archive/2009/04/22/4099190.aspx

posted @ 2009-07-09 11:12 cxccbv 阅读(620) 评论(0) 编辑

Ehcache学习(七)实例一

Ehcache使用用例(一)

Singleton创建方式

Ehcache1.2版本之后,都可以使用singleton(工厂创建方法)去创建一个singleton的CacheManager实例。

CacheManager.create();

String[] cacheNames = CacheManager.getInstance().getCacheNames();

使用默认配置创建CacheManager

CacheManager manager = new CacheManager();

String[] cacheNames = manager.getCacheNames();

使用配置文件创建指定的CacheManager

CacheManager manager1 = new CacheManager(“src/config/ehcache1.xml”);

CacheManager manager2 = new CacheManager(“src/config/ehcache2.xml”);

String[] cacheNamesForManager1 = manager1.getCacheNames();

String[] cacheNamesForManager2 = manager2.getCacheNames();

配置文件加载方式

· CacheManager manager = new CacheManager();会在classpath路径下找ehcache.xml配置文件。

· CacheManager manager = new CacheManager(“src/config.ehcache.xml”); 也可以根据相对文件路径来加载配置文件.

· 通过URL加载

URL url = getClass().getResource(“/anotherconfigurationname.xml”);

CacheManager manager = new CacheManager(url);

· 通过流加载

InputSream fis = new FileInputStream(new File(“src/config/ehcache.xml”).getAbsolutePath());

Try {

CacheManager manager = new CacheManager(fis);

} finally {

Fis.close();

}

编码实现添加和缓存

Ehcache中不仅可以用配置文件来配置缓存,而在代码中也可以实现同样的功能。

CacheManager singletonManager = CacheManager.create();

Cache memoryOnlyCache = new Cache(“testCache”, 50000, false, false, 8, 2);

Cache test = singletonManager.getCache(“testCache”);

删除只需要调用singletonManager.removeCache(“testCache”);

Shotdown CacheManager

在使用完Ehcache后,必须要shutdown缓存。Ehcache中有自己的关闭机制,不过最好在你的代码中显示调用CacheManager.getInstance().shutdown();

http://blog.csdn.net/mgoann/archive/2009/04/20/4095155.aspx

posted @ 2009-07-09 11:10 cxccbv 阅读(652) 评论(0) 编辑

Ehcache学习(六)缓存机制

Ehcache Storage Options  http://blog.csdn.net/mgoann/archive/2009/04/20/4094768.aspx

简介

Ehcache俩中缓存机制:

· MemoryStore(内存存储)

· DiskStore(磁盘存储)

MemoryStore

MemoryStore总是可用的,但不可直接操作,当中存储着所有的Cache。

· 合适的Element类型

所有的Element都可以放在MemoryStore中。

· 安全性:使用多个线程并行检查内存泄露。

· JDK:使用了JDK1.5的LinkedHashMap来存放Elemen。t

· 快速的:是最快的缓存机制,因为它是存放在内存中。

· 失效策略(Memory)

Cache可以配置最大缓存Element的个数,以及失效时间。如果在添加Elemtent时,缓存中的Element个数达到了最大缓存数并且overflowToDisk配置的属性为true,Ehcache会更具配置项MemoryStoreEvictionPolicy的失效策略将Element输出到磁盘。如果overflowToDisk为fasle,Ehcache将删除内存中Element。Ehcache支持三种失效策略:LRU、LFU、FIFO。

值得注意的是缓存中失效的Element并不会别马上清理掉,所以想得到内存的真实大小应该调用方法calculateInMemorySize()方法。

DiskStore

DiskStore可缓存到外部设备上(硬盘)。

· DiskStores are Optional

Ehcache从1.5版本开始支持DiskStore。如果你需要多个DiskStore的话,最好给他们配置不同的文件路径。

· 关闭磁盘缓存:只要注释掉ehcache.xml配置文件中的磁盘缓存配置项即可。而ehcache-failsafe.xml的磁盘缓存配置不会影响到你自己的cache。

· 合适的Element类型

这里要注意要想使用磁盘缓存,缓存的Element必须实现序列化接口。否则会抛出NotSerializableException异常。

· 存储:Ehcache会将每个缓存配置的文件路径下创建一个cache_name.data文件,如果使用的磁盘持久化技术,还会生成一个cache name.index文件。

· 失效

Ehcache有一个后天线程专门做Ellment失效监测以及清除工作。设置线程运行间隔时间,可通过设置diskExpiryThreadIntervalSeconds属性来完成,此值不宜设置过低,否则会导致清理线程占用大量CPU资源。默认值是120秒。

· 持久化

持久化可在Element的diskPersistent配置项中配置,如果配置为“false”或是“omitted”在CacheManager shutdown或是startup后,用来缓存Element的文件将被清除掉。如果设置为“true”,data和index文件会被保存下来,对于新创建的CacheManager Element也是可用的。

使用时必须显示调用cache. Flush()才会将数据缓存到磁盘中。

磁盘缓存步骤:从MemoryStore中把没有失效的Element刷新到DiskStore,Element被写入到data文件,Element将被序列化到index文件。

Ehcache缓存回收策略

简介

缓存回收就是当缓存满了的时候,Ehcache会根据指定的策略来清理缓存。这里的缓存回收策略有别与Ehcache中的失效清理策略。失效清理策略是对失效的Element进行批量清理时所采用的策略,最终所有失效的Element都将被清理,只不过是清理的先后顺序不同罢了。Ehcache中缓存的最大Element数是由maxElementsInMemory来指定的。当缓存中的Element个数达到maxElementsInMemory指定的值时,Ehcache会根据具体的策略来清理缓存,默认的策略是LRU(最近最少使用)。

磁盘缓存大小默认是没有限制的,不过可通过maxElementsOnDisk来指定。当磁盘缓存达到maxElementsOnDisk指定的值时,Ehcache会清理磁盘中的缓存使用默认策略是LFU(使用频率最低)。

MemoryStore回收策略

MemoryStore支持三种策略:LRU、LFU、FIFO。

posted @ 2009-07-09 11:02 cxccbv 阅读(692) 评论(0) 编辑

Ehcache学习(五)缓存配置

Ehcache缓存配置 http://blog.csdn.net/mgoann/archive/2009/04/17/4087714.aspx

简介

Cache的配置很灵活,官方提供的Cache配置方式有好几种。你可以通过声明配置、在xml中配置、在程序里配置或者调用构造方法时传入不同的参数。

你可以将Cache的配置从代码中剥离出来,也可以在使用运行时配置,所谓的运行时配置无非也就是在代码中配置。以下是运行时配置的好处:

· 在同一个地方配置所有的Cache,这样很容易管理Cache的内存和磁盘消耗。

· 发布时可更改Cache配置。

· 可再安装阶段就检查出配置错误信息,而避免了运行时错误。

本文将会对ehcache.xml配置文件进行详细的阐述。在配置的时可以拷贝一个现有的ehcache.xml,如果没有请点击这里去下载。

ehcache-failsafe.xml

如果你调用了CacheManager默认构造方法去创建CacheManager的实例,此方法会到classpath中找ehcache.xml文件,否则它会到类路径下找ehcache-failsafe.xml文件。而ehcache-failsafe.xml被包含在jar包中,所有它肯定能找的到。

ehcache-failsafe.xml提供了一个非常简单的默认配置,这样可以使用户在没有创建ehcache.xml的情况下使用Ehcache。

不过这样做Ehcache会提醒用户创建一个正确的Ehcache配置。

ehcache.xml片段:

<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
</ehcache>

ehcache.xml和其他配置文件

在Ehcache-1.6之前的版本,只支持ASCII编码的ehcache.xml配置文件。在Ehcach-1.6之后版本中,支持UTF8编码的ehcache.xml配置文件。因为向后兼容,所有采用ASCII编码的配置文件完全没有必要转换为UTF8。

一个CacheManager必须要有一个XML配置。由于磁盘路径或是监听端口,多个CacheManager使用同一个配置文件时会出现错误。

下面是ehcache.xml具体实例以及配置指南

<ehcache xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

· CacheManager配置

DmulticastGroupPort=4446,这样可以配置监听端口。

· DiskStore配置

如果你使用的DiskStore(磁盘缓存),你必须要配置DiskStore配置项。如果不配置,Ehcache将会使用java.io.tmpdir。

diskStroe的“path”属性是用来配置磁盘缓存使用的物理路径的,Ehcache磁盘缓存使用的文件后缀名是.data和.index。

<disStore path=”java.io.tmpdir”/>

· CacheManagerEventListener配置

我们通过CacheManagerEventListenerFactory可以实例化一个CacheManagerPeerProvider,当我们从CacheManager中added和removed Cache时,将通知CacheManagerPeerProvider,这样一来,我们就可以很方面的对CacheManager中的Cache做一些统计。

注册到CacheManager的事件监听类名有: adding a Cache和removing a Cache

<cacheManagerEventListenerFacotory class=”” properties=””/>

· CacheManagerPeerProvider配置

在集群中CacheManager配置CacheManagerPeerProviderFactory创建CacheManagerPeerProvider。具体的实例如下:

<cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.

RMICacheManagerPeerProviderFactory"

properties="peerDiscovery=manual, rmiUrls=//server1:40000/sampleCache1|//server2:40000/sampleCache1| //server1:40000/sampleCache2|//server2:40000/sampleCache2"

propertySeparator="," />

· CacheManagerPeerListener配置

CacheManagerPeerListener配置是用来监听集群中缓存消息的分发的。

<cacheManagerPeerListenerFactory

class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"

properties="hostName=fully_qualified_hostname_or_ip,

port=40001,

socketTimeoutMillis=120000"

propertySeparator="," />

· Cache配置

· name:Cache的唯一标识

· maxElementsInMemory:内存中最大缓存对象数。

· maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大。

· eternal:Element是否永久有效,一但设置了,timeout将不起作用。

· overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中。

· timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。

· timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大。

· diskPersistent:是否缓存虚拟机重启期数据。(这个虚拟机是指什么虚拟机一直没看明白是什么,有高人还希望能指点一二)。

· diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。

· diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。

· memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。这里比较遗憾,Ehcache并没有提供一个用户定制策略的接口,仅仅支持三种指定策略,感觉做的不够理想。

· Cache Exception Handling配置

<cacheExceptionHandlerFactory class="com.example.ExampleExceptionHandlerFactory" properties="logLevel=FINE"/>

总结

这里只对通用缓存的配置做了详细的阐述,至于RMI缓存和集群缓存可以参考这里

下面给出几个配置示例:

· Ehcache默认Cache配置

<defaultCache

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

overflowToDisk="true"

diskSpoolBufferSizeMB="30"

maxElementsOnDisk="10000000"

diskPersistent="false"

diskExpiryThreadIntervalSeconds="120"

memoryStoreEvictionPolicy="LRU"

/>

· SampleCache1配置

简单配置,在ehcache.xml文件中有此配置,在使用Ehcache前最好将其删除掉,自己配置。

缓存名sampleCache1,内存中最多可缓存10000个Element,其中的element会在闲置

posted @ 2009-07-09 11:01 cxccbv 阅读(1135) 评论(0) 编辑

Ehcache学习(四)缓存模式

简介 http://blog.csdn.net/mgoann/archive/2009/04/17/4086641.aspx

缓存有多种不同的缓存模式。以下是Ehcache支持的缓存模式:

· 直接操作(direct manipulation)

· 推送模式(pull-through)

· 自填充(self-populating)

直接操作(direct manipulation

你可以通过方法cache.put(Elemtn element)来存储对象,通过方法cache.get(Ojbect key)来取得对象。

自填充(self-populating

只需要使用cache.get(Ojbect key)来取得对象。Cache自动去构建实体。

posted @ 2009-07-09 10:59 cxccbv 阅读(399) 评论(0) 编辑

Ehcache学习(三)关键类

http://blog.csdn.net/mgoann/archive/2009/04/17/4086534.aspx

Ehcache关键类

简介

Ehcache中的CacheManager是用来管理Cache的。而Cache中包含元素Element,而Element实质上就是一个键值对。Cache在物理方面的实现有内存实现和磁盘实现。

CacheManager

CacheManager包含Cache,而Cache反过来构成了CacheManager的要素。

·   CacheManager创建模式:CacheManger创建模式包含singleton和instance俩种。

·       Simgleton Mode

从字面可理解为单例模式,这种模式只允许创建一个CacheManger实例。

·       Instance Mode

那这种模式从字面上理解也应该是实例化模式了。从Ehcache-1.2以来,CacheManager有多种静态创建方法。这样我们就可以同时创建多个复杂的不同配置的CacheManager。

如果只是用内存来存储Cache,那么没有什么需要你特别考虑的。如果是用磁盘来缓存Cache的话,你就必须为CacheManager指定的磁盘路径。当一个新的CacheManager被创建时,必须要确保磁盘路径没有被别的CacheManager使用。如果重复使用磁盘路径,这个时候就会抛出CacheException。如果CacheManger配置成集群模式的,还要注意端口的指定。

·       Singletion和Instance混用

如果一个应用程序通过构造方法创建了一个CacheManager的实例,也调用了静态创建方法,这个时候会,没调用一次静态创建方法就会返回一个CacheManager的Singleton实例,所有的这些实例将会共存。

Ehcache

Ehcache是一个接口,所有的Cache都实现了Ehcache。每个Cache都有自己的名字和特定的属性以及包含着Element元素。

Ehcache中的Cache代表一块特定的缓存区域或是缓存系统

Cache的Element元素可以存储到MemoryStore(内存)中,也可以写到DiskStore(磁盘)中。

Element

每一个Element对应一个缓存的原子实体。它有key、value以及访问记录属性。Element可以被put进Cache也可以remove出Cache。通过配置Cache可定制Element的失效以及移除策略。

在Ehcache-1.2 API中要求存储对象要可序列化。没有序列化的对象不可以存储到DiskStore(磁盘)中,也不能被拷贝。

在Element类中注意到有俩个方法getOjbectValue和getKeyValue,这个方法都是从缓存中取得Element元素的方法,唯一区别就是一个是用来取得序列化对象,另外一个是不可序列化对象。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mgoann/archive/2009/04/17/4086534.aspx

posted @ 2009-07-09 10:51 cxccbv 阅读(451) 评论(0) 编辑

Ehcache学习(二)入门指南

入门指南http://blog.csdn.net/mgoann/archive/2009/04/16/4083962.aspx

Ehcache Getting Started

简介

Ehcache可以直接使用。也可以和Hibernate对象/关系框架结合使用。还可以做Servlet缓存。

通用缓存

· 确保JDK版本支持你现有的Ehcache版本,Ehcache支持JDK1.4和1.5以及1.6版本。

· Ehcache jar包加入你的classpath环境变量中。

· Ehcache依赖的类库加入到你的classpath环境变量中,不同版本的Ehcache依赖类库不尽相同,请注意的Ehcache版本以及相关依赖类库,这里不做累述,可以参考

http://ehcache.sourceforge.net/documentation/dependencies.html

· 配置ehcache.xml配置文件,并加入到classpath中。

· 配置logging到合适的级别。

Hibernate

· 参考通用缓存的步骤。

· 在ehcache.xml中创建缓存。

Java EE Servlet缓存

· 参考通用缓存步骤。

· 在ehcache.xml中为你的web页面配置缓存。

· 如果要缓存全部页面,可以使用Ehcache提供的SimplePageCachingFilter或是自己写一个子类继承CachingFilter。

· 若要缓存某个具体的JSP页面(包括由RequestDispatcher返回的页面),你可以使用Ehcache提供的SimplePageFragmentCachingFilter或是写个子类去继承PageFragmentCachingFilter。

· 配置web.xml。这里就相对简单一些,只是将你上俩个步骤使用的Filter配置到web.xml中,以便请求再此到来时能够访问缓存中的页面,从而达到提供页面的相应速度。

RESTfulSOAP缓存

· 从http://sourceforge.net/project/showfiles.php?group_id=93232下载缓存服务端。

· 使用cd命令切换到bin目录下。

· 键入startup.sh启动服务。

默认情况下使用的是8080端口,RESTful和SOAP web服务器也都使用这个端口,请注意端口占用问题。

· 这个时候就可以使用Cache Server了,你可以使用Java后者任何其他语言。具体示例请参考

http://ehcache.sourceforge.net/documentation/cache_server.html

Jcache style caching

Ehcache在net.sh.ehcache.jcache这个包下,有Ehcache早期对Jcache的一个粗略实现。

SpringCocoonAcegi和其他框架

一般,和这些框架结合使用Ehcache时,没有太多特殊的地方。你只需要注意以下几点:

· 注意这些框架中使用的是什么缓存。

· 创建ehcache.xml,配置缓存再放入你的classpath路径下。

posted @ 2009-07-09 10:41 cxccbv 阅读(490) 评论(0) 编辑

Ehcache学习(一)简介

Ehacahe简介http://blog.csdn.net/mgoann/archive/2009/04/16/4083179.aspx

Ehcache

简介

Ehcache是一种广泛使用java分布式缓存的通用缓存,J2EE和轻量级容器。

它具有内存和磁盘缓存,副本的copy和失效,监听,缓存装载,扩展缓存,缓存异常处理,gzip缓存servlet过滤器,RESTful&SOAP API等特性。

Ehcache下提供Apache开源许可证并在积极的开发、维护和支持。

特性

· 高效且轻量级

· 快速

· 简单

· 低消耗

· 依赖性小

· 扩展性

· 提供内存和磁盘缓存,可扩展到数亿bytes

· 可扩展数百种缓存

· 适合高负载多核CPU

· 可以为每个虚拟机创建多个复杂的CacheManager

· 灵活性

· 支持对象或序列化缓存

· 支持缓存或元素的失效

· 提供LRU、LFU和FIFO缓存策略

· 支持内存缓存和磁盘缓存

· 分布式缓存机制

· 基于标准

· 完全实现JSR107 JCACHE API

· 扩展性

· 插件式

· 插件式查询、克隆和监测

· 插件式缓存扩展

· 插件式缓存异常处理

· 持久化应用

· 持久化磁盘存储JVM重启期数据。

· 可控磁盘缓存

· 支持监测

· CacheManager监测

· 缓存时间监测

· JMX支持

· 分布式

· 支持RMI、Jgroups、JMS或Terracotta深层拷贝

· 预查询

· 可靠分发机制

· 同步异步拷贝

· 复制或是无效拷贝

· 事务拷贝

· 扩展性

· 缓存服务

· RESTful缓存服务

· SOAP缓存服务

· Java EE高速缓存

· 高速缓存拦截机制避免并发操作的重复处理

· 为耗时操作提供自我缓存

· Java EE Gzip Servlet过滤

· 指令集缓存

· 兼容Hibernate

· 高性能

· 高覆盖率测试

· 自动化负载、边界、性能系统测试

· 产品级测试

· 完整的文档

· Popular框架组织信赖

· 开放源码证书

· Apache2.0许可证

posted @ 2009-07-09 10:37 cxccbv 阅读(488) 评论(0) 编辑

导航

统计

公告