
Documentation
For Version 1.1
Table of Contents
1 Installation
Java
Requirements
Ehcache supports
JDK1.2, 1.3, 1.4 and 5 at runtime. When compiling from source, the
build
process requires JDK 1.4 or 5.
Ehcache does not work with JDK1.1.
Dependencies
For JDK1.4 and JDK5, ehcache requires commons-logging
from Apache's Jakarta project.
For JDK1.2 and JDK
1.3, ehcache also requires Apache xerces
(xml-apis.jar and xercesImpl.jar), version 2.5 and Apache commons-collections.
The dependencies are also Hibernate dependencies, so if you are
using ehcache as a Hibernate plugin, dependency requirements are met.
It has been reported
that IBM Websphere 5.1 running on IBM JDK1.4 also requires
commons-collection.jar even though it does not use it.
Step-by-step
Installation Instructions
For Use with Hibernate
There is nothing to do. Hibernate includes ehcache.
For Use as a
General Purpose Cache
This assumes you do not have Hibernate installed.
The steps are:
- Place
the ehcache-X.X.jar into your classpath.
- Ensure that any libraries required to satisfy dependencies
are also in the classpath.
- As an optional step, configure an appropriate logging
level.
2 CacheConcepts
ehcache consists
of a CacheManager
, which manages caches. Caches contain
elements,
which are essentially name value pairs. Caches are physically
implemented either in-memory, or on disk.
CacheManager
The Cache
Manager is made up of Caches which contain elements.
Creation of, access to and removal of caches is controlled by the
CacheManager
. You may only have one CacheManager
per class loader, as
it expects to be a singleton. If an application has more than one,
ensure a separate configuration file is used for each. Otherwise disk
caches will conflict.
Cache
A cache has a
name and attributes. Each cache contains elements.
The JCACHE JSR also has groups, which is an association between
objects
in the same cache. They may be invalidated together or have the same
element attributes. While groups may be useful, they are not used in
Hibernate and has not been implemented in ehcache.
Cache elements are stored in the MemoryStore
. Optionally
they also
overflow
to a DiskStore
.
Element
An element is an
atomic entry in a cache. It has a key, a value and a record of
accesses. Elements are put into and removed from caches. They can also
expire and be removed by the Cache, depending on the Cache settings.
3 Storage Options
Ehcache has two
physical stores:
- a MemoryStore and
- a DiskStore
Memory
Store
The MemoryStore
is
always enabled. It is not directly manipulated, but is a component of
every cache.
It has the following characteristics:
Safety
Thread safe for
use by multiple concurrent threads.
Tested for memory leaks. See MemoryCacheTest#testMemoryLeak. This test
passes for ehcache but exploits a number of memory leaks in JCS. JCS
will give an OutOfMemory error with a default 64M in 10 seconds.
Backed By JDK
LinkedHashMap
The MemoryStore
for JDK1.4 and JDK 5 it is backed by an extended LinkedHashMap.
This provides a combined linked list and a hash map, and is ideally
suited for caching. Using this standard Java class simplifies the
implementation of the memory cache. It directly supports
obtaining the least recently used element.
For JDK1.2 and JDK1.3, the LRUMap from Apache Commons is used. It
provides similar features to LinkedHashMap.
The implementation is determined dynamically at runtime. LinkedHashMap
is preferred if found in the classpath.
Fast
ehcache is
faster than JCS. Here are some benchmarks from MemoryStoreTest.
Benchmark
|
ehcache
|
JCS |
Difference |
insert 5 million typical
CacheElements. Then get each of them by key.
|
22.704s
|
42.176s
|
79% faster
|
insert 5,000,000 typical
CacheElements and get each of them by key. Then remove them one at a
time by key
|
14.999s
|
44.536s
|
293% faster
|
Memory Use,
Spooling and Expiry Strategy
All caches
specify their maximum in-memory size, in terms of the number of
elements,
at configuration time.
When an element is added to a cache and it goes beyond its maximum
memory size, the eldest
element is either
deleted, if overflowToDisk is false, or evaluated for spooling to disk,
if overflowToDisk is true. In the latter case, a check for expiry is
carried out. If it is expired it is deleted; if not it
is spooled.
The eldest element, is the
Least Recently Used (LRU). The last used timestamp is updated when an
element is put
into the cache or an element is retrieved from the cache with a get
call. There are also putQuiet
and getQuiet
methods which do not update the last used timestamp.
When there is a get
or a getQuiet
on an element, it is
checked for expiry. If
expired, it is removed and null is returned.
With this strategy there will usually be some expired elements in the
cache. Memory sizing of an application must always take into
account the maximum size of each cache. There is a convenience method
which can provide an estimate of the size in bytes of the MemoryStore
.
See calculateInMemorySize().
It returns the serialized size of the cache. Do not use this method in
production. It is very slow. It is only meant to provide a rough
estimate.
The alternative would have been to have an expiry thread. This is a
trade-off between lower memory use and short locking periods and cpu
utilisation. The design is in favour of the latter. For those concerned
with memory use, simply reduce the maxElementsInMemory
.
DiskStore
The disk cache
provides a disk spooling facility.
It has the following characteristics:
Storage Files
The disk cache
creates one file per
cache called "cache name.data".
if the DiskStore
is configured to be persistent, a "cache
name.index" file is also created.
Files are created in the directory specified by the diskStore
configuration element. The default configuration is "java.io.tmpdir",
which causes files to be created in the system's temporary
directory. Following is a list of Java system properties which
are supported as values for diskStore:
- user.home
- User's home directory
- user.dir -
User's current working
directory
- java.io.tmpdir
- Default temp file
path
Apart from
these, any directory can
be specified using syntax appropriate to the operating system. e.g. for
Unix "/home/application/cache".
Expiry
Strategy
One thread per
cache is used to
remove expired
elements. The optional attribute diskExpiryThreadIntervalSeconds
sets the interval between runs of the expiry thread. Warning: setting this to a low value
is not recommended. It can cause excessive DiskStore
locking and high cpu utilisation. The default value is 120 seconds.
Serializable Objects
Only
Only
Serializable
objects can be stored in a DiskStore
. A NotSerializableException
will be thrown if the object is not serializable.
Safety
DiskStore
s are thread safe.
Persistence
DiskStore
persistence is controlled
by the diskPersistent configuration
element. If false or omitted, DiskStore
s will not persist
between
CacheManager
restarts. The data file for each cache will
be deleted,
if it
exists, both on shutdown and startup. No data from a previous instance
CacheManager
is available.
If diskPersistent is true,
the data file, and an index file, are saved. Cache Elements are
available to a new CacheManager
. This CacheManager
may be in the same
VM instance, or a new one.
The data file is updated continuously during operation of the Disk
Store. New elements are spooled to disk, and deleted when expired. The
index file is only written when dispose is called on the DiskStore
.
This happens when the CacheManager is shut down, a Cache is disposed,
or the VM is being shut down. It is recommended that the
CacheManager shutdown()
method be used. See Virtual Machine Shutdown
Considerations for guidance on how to safely shut the Virtual
Machine down.
When a DiskStore
is persisted, the following steps take
place:
- Any non-expired Elements of the
MemoryStore
are
flushed to the
DiskStore
- Elements
awaiting spooling are
spooled to the data file
- The free
list and element list are
serialized to the index file
On startup the
following steps take
place:
- An attempt is made to read the index file. If it does not exist
or cannot be read successfully, due to disk corruption, upgrade of
ehcache, change in JDK version etc, then the data file is deleted and
the
DiskStore
starts with no Elements in it.
- If the index file is read successfully, the free list and element
list are loaded into memory. Once this is done, the index file contents
are removed. This way, if there is a dirty shutdown, when restarted,
ehcache will delete the dirt index and data files.
- The
DiskStore
starts. All data is available.
- The expiry thread starts. It will delete Elements which have
expired.
These actions favour safety over persistence. Ehcache is a cache, not a
database. If a file gets dirty, all data is deleted. Once started there
is further checking for corruption. When a get is done, if the Element
cannot be successfully derserialized, it is deleted, and null is
returned. These measures prevent corrupt and inconsistent data being
returned.
Fragmentation
Expiring an
element
frees its space on the file. This space is available for reuse by new
elements. The element is also removed from the in-memory index of
elements.
Speed
Spool requests are placed in-memory and then
asynchronously written to disk. There is one thread per cache. An
in-memory index of elements on disk is maintained to quickly resolve
whether a key exists on disk, and if so to seek it and read it.
Serialization
Writes to and from the disk use ObjectInputStream
and the Java serialization mechanism. This is not required for the
Memory Cache. So the disk cache will never be as fast.
Serialization speed is affected by the size of the objects being
serialized and their type. It has been found in the ElementTest test
that:
- The serialization time for a Java object being a large Map of
String arrays was 126ms, where the a serialized size was 349,225 bytes.
- The serialization time for a byte[] was 7ms, where the serialized
size was 310,232 bytes
Byte arrays are 20 times faster to serialize. Make use of byte arrays
to increase DiskStore performance.
RAMFS
However disks
can be of
different speeds. The disk cache can be sped up by using a faster disk.
On some operating systems there are a plethora of file systems to
choose from. For example, the Disk Cache has been successfully used
with Linux' RAMFS file system. This file system simply consists of
memory. Linux presents it as a file system. The Disk Cache treats it
like a normal disk - it is just way faster. With this type of file
system, object serialization becomes the limiting factor to performance.
4
Performance Considerations
Ehcache comes with a MemoryStore
and a DiskStore
. The MemoryStore
is
approximately
an order of magnitude faster than the DiskStore
. The
reason is that
the DiskStore
incurs the following extra overhead:
- Serialization of the key and value
- Ejection from the
MemoryStore
using an LRU
algorithm
- Reading from disk
Note that writing to disk is not a synchronous performance overhead
because it is handled by a separate thread.
A Cache should alway have its maximumSize
attribute
set to 1 or higher. A Cache with a maximum
size of 1 has twice the performance of a disk only cache, i.e. one
where the maximumSize
is
set to 0. For this reason a warning will be issued if a Cache is
created with a 0 maximumSize
.
5
Distributed Caching
Ehcache is not a distributed cache. There
are no plans to make it a distributed cache. The key design goals of
ehcache are simplicity and provability. Adding a distribution mechanism
to ehcache conflicts with those goals. Moreover, a requirement for
distribution is far less common than for requirements such as memory
and disk storage.
Ehcache does however support distribution. Many of the open source
distributed caches use the JGroups project
for distribution framework. Or, if you are using J2EE it is
straightforward to expose the CacheManager
via an EJB session bean.
A CacheManager
can access Cache
s and
Elements. Elements are serializable
. and can be sent over
a network using RMI or another protocol.
6 Virtual Machine
Shutdown Considerations
The DiskStore can optionally be configured to persist between
CacheManager and Virtual Machine instances. See documentation on
the diskPersistent cache attribute
for information on how to do this.
When diskPersistent is turned on for a cache, a Virtual Machine
shutdown hook is added to enable the DiskStore to persist itself. When
the Virtual Machine shuts down, the the hook runs and, if the cache is
not already disposed, it calls dispose. Any elements in the MemoryStore
are spooled to the DiskStore. The DiskStore then flushes the spool, and
writes the index to disk.
The cache shutdown hooks will run when:
- a program exists normally. e.g. System.exit() is called, or the
last non-daemon thread exits
- the Virtual Machine is terminated. e.g. CTRL-C. This corresponds
to
kill -SIGTERM pid
or kill -15 pid
on
Unix
systems.
The cache shutdown hooks will not run when:
- the Virtual Machine aborts
- A SIGKILL signal is sent to the Virtual Machine process on Unix
systems. e.g.
kill -SIGKILL pid
or kill -9 pid
- A
TerminateProcess
call is
sent to the process on Windows systems.
If dispose was not called on the cache either by
CacheManager.shutdown() or the shutdown hook, then the DiskStore will
be corrupt when the application is next started. If this happens, it
will be detected and the DiskStore file will be automatically truncated
and a log warning level message is emitted. The cache will work normally,
except that it will have lost all data.
7 Logging
Ehcache uses
the Apache Commons Logging library. It acts as a thin bridge between
logging statements in the code and logging infrastructure detected in
the classpath. It will use in order of preference: log4j, JDK1.4
logging and then its own SimpleLog
. This enables
ehcache to use logging infrastructures compatible with Java versions
from JDK1.2 to JDK5. It does create a dependency on Apache Commons
Logging, however many projects, including Hibernate, share the same
dependency.
For normal production use, use the WARN
level in log4J
and the WARNING
level for JDK1.4 logging.
8 Obtaining the
Source Code
The source code is distributed in the root directory of the download.
It is called ehcache-x.x.zip. It is also available from SourceForge
online or through cvs.
Online
The source code can be viewed online via viewcvs.
CVS
You can check out the sourcecode anonymously using cvs as follows:
1. cvs
-d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ehcache login
2. cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ehcache co
ehcache |
9 Cache Configuration
Declarative versus
Programmatic Configuration
Declarative
configuration refers to the practice of declaring configuration at
design time. Programmatic configuration to the practice of configuring
at runtime.
In ehcache the
following must always be configured declaratively:
- The directory in which
DiskStore
s are to be
created. (diskStore
element)
- Default configuration for caches created without declarative or
programmatic configuration. (defaultCache element)
Individual caches can be configured either declaratively or
programmatically.
It is better practice to configure
caches declaratively because:
- It is easy if you have all of your configuration in one place.
Caches consume memory, and disk space. They need to be carefully tuned.
You can see the total effect in a configuration file. You could do this
code, but it would not as visible.
- It is possible to change configuration without doing a recompile.
- Common configuration errors can be checked for at start-up,
rather than causing a runtime error.
The programmatic facility is provided primarily so that new caches can
be dynamically created at runtime.
Declarative
Configuration
The ehcache schema:
ehcache.xsd
Ehcache looks for an ehcache.xml file. These must be configured in
accordance with the following schema:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="ehcache">
<xs:complexType>
<xs:sequence>
<xs:element
ref="diskStore"/>
<xs:element
ref="defaultCache"/>
<xs:element
maxOccurs="unbounded" ref="cache"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="diskStore">
<xs:complexType>
<xs:attribute name="path"
use="required" type="xs:NCName"/>
</xs:complexType>
</xs:element>
<xs:element name="defaultCache">
<xs:complexType>
<xs:attribute name="eternal"
use="required" type="xs:boolean"/>
<xs:attribute
name="maxElementsInMemory" use="required" type="xs:integer"/>
<xs:attribute name="overflowToDisk"
use="required" type="xs:boolean"/>
<xs:attribute
name="timeToIdleSeconds" use="optional" type="xs:integer"/>
<xs:attribute
name="timeToLiveSeconds" use="optional" type="xs:integer"/>
<xs:attribute name="diskPersistent"
use="optional" type="xs:integer"/>
<xs:attribute
name="diskExpiryThreadIntervalSeconds" use="optional"
type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="cache">
<xs:complexType>
<xs:attribute name="eternal"
use="required" type="xs:boolean"/>
<xs:attribute
name="maxElementsInMemory" use="required" type="xs:integer"/>
<xs:attribute name="name"
use="required" type="xs:NCName"/>
<xs:attribute name="overflowToDisk"
use="required" type="xs:boolean"/>
<xs:attribute
name="timeToIdleSeconds" use="optional" type="xs:integer"/>
<xs:attribute
name="timeToLiveSeconds" use="optional" type="xs:integer"/>
<xs:attribute name="diskPersistent"
use="optional" type="xs:integer"/>
<xs:attribute
name="diskExpiryThreadIntervalSeconds" use="optional"
type="xs:integer"/>
</xs:complexType>
</xs:element>
</xs:schema> |
The schema is also provided in the root directory of the ehcache
distribution.
diskStore
The diskStore element is required, regardless of whether or not it is
intended to use any DiskStores.
path
This is a required attribute.
It sets the path used by any DiskStore objects for creating data and
index files.
Legal values must be a relative or absolute path to a directory with
syntax legal for the operating system ehcache is running on. If
the directory does not exist an attempt is made to create it.
If the path value is a Java System Property (e.g. "user.home") it is
replaced by its runtime value in the Virtual Machine.
The following Java System Properties are understood:
user.home - User's home
directory
user.dir - User's current working
directory
java.io.tmpdir - Default temporary directory
|
defaultCache
The defaultCache element contains configuration used by ehcache to
create caches, where a cache element matching the cache name does not
exist. It is a template.
An example of it use is in Hibernate. When you specify that a Domain
Object is cacheable Hibernate creates a new cache using the fully
qualified class name of the Domain Object as the cache name. No
configuration information is provided. Ehcache automatically creates
the new caches using the defaultCache settings as a template.
While the defaultCache is a great convenience, it is preferable for
each Cache to be configured individually. For this reason a log warning
level message is issued each time a cache is created based on the
defaultCache values.
cache
The cache element contains configuration used by ehcache to create each
cache. When the CacheManager is created it parses ehcache.xml for cache
elements and creates a cache for each one with the settings provided.
defaultCache and cache
attributes
defaultCache and cache share the same attributes, except that an
additional name attribute is required for the cache element.
name
This is a required attribute for cache elements only. The defaultCache
does not have a name attribute.
Legal values are any legal String value. Names must be unique. Ehcache
will throw a CacheException if an attempt is made to create two caches
with the same name.
Hibernate users should use the fully qualified class name of the
DomainObject being cached. There are different rules for other
types of Hibernate cache. For more on Hibernate naming see
Using ehcache as a Hibernate Plugin.
eternal
This is a required attribute.
Whether or not the cache is eternal. An eternal cache does not expire
its elements.
Legal values are "true" or "false".
maxElementsInMemory
This is a required attribute.
Legal values are integers between 0 and Integer.MAX_VALUE.
It is the maximum number of elements to store in the MemoryStore. It is
strongly recommended for performance reasons that this value is at
least 1. If not a warning will be issued at Cache creation time.
overflowToDisk
This is a required attribute.
Legal values are "true" or "false".
Whether or not to use the DiskStore when the number of Elements has
exceeded the maxElementsInMemory of the MemoryStore.
Entries to be removed from the MemoryStore, when it overflows, are
determined using a least recently used algorithm ("LRU"). Used means
inserted or accessed. If false, the LRU Element is discarded. If true,
it is transferred to the DiskStore.
timeToIdleSeconds
This is an optional attribute.
Legal values are integers between 0 and Integer.MAX_VALUE.
It is the number of seconds that an Element should live since it was
last used. Used means inserted or accessed.
0 has a special meaning, which is not to check the Element for time to
idle, i.e. it will idle forever.
The default value is 0.
timeToLiveSeconds
This is an optional attribute.
Legal values are integers between 0 and Integer.MAX_VALUE.
It is the number of seconds that an Element should live since it was
created. Created means inserted into a cache using the Cache.put method.
0 has a special meaning, which is not to check the Element for time to
live, i.e. it will live forever.
The default value is 0.
diskPersistent
This is an optional attribute.
Legal values are "true" or "false".
Whether or not the DiskStore should be persisted between CacheManager
shutdowns and Virtual Machine restarts.
diskExpiryThreadIntervalSeconds
This is an optional attribute.
Legal values are integers between 0 and Integer.MAX_VALUE.
It is how long to the disk expiry thread should sleep between
successive runs. Setting this value too low could cause performance
problems. A setting of 0 is not recommended. It will cause 100% cpu
load.
The default value is 120 seconds.
Sample ehcache.xml
A sample
ehcache.xml configuration file is included in the root directory of the
ehcache
distribution. It is reproduced below.
The sample includes a diskStore element, a defaultCache element and
three cache elements.
<ehcache>
<!-- Sets the path to the directory where cache
.data files are created.
If the path is a Java
System Property it is replaced by
its value in the
running VM.
The following
properties are translated:
user.home - User's
home directory
user.dir - User's
current working directory
java.io.tmpdir -
Default temp file path -->
<diskStore path="java.io.tmpdir"/>
<!--Default Cache configuration. These will
applied to caches programmatically created through
the CacheManager.
The following attributes are
required:
maxInMemory
- Sets the maximum number of objects that will be created in memory
eternal
- Sets whether elements are eternal. If eternal, timeouts are
ignored and the
element is never expired.
overflowToDisk
- Sets whether elements can overflow to disk when the in-memory cache
has reached the maxInMemory limit.
The following attributes are
optional:
timeToIdleSeconds
- Sets the time to idle for an element before it expires.
i.e. The maximum amount of time between accesses before an element
expires
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that an Element can idle for
infinity.
The default value is 0.
timeToLiveSeconds
- Sets the time to live for an element before it expires.
i.e. The maximum time between creation time and when an element expires.
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that and Element can live for
infinity.
The default value is 0.
diskPersistent
- Whether the disk store persists between restarts of the Virtual
Machine.
The default value is false.
diskExpiryThreadIntervalSeconds- The number of seconds between runs of
the disk expiry thread. The default value
is 120 seconds.
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!--Predefined caches. Add your cache
configuration settings here.
If you do not have a
configuration for your cache a WARNING will be issued when the
CacheManager starts
The following attributes are
required:
name
- Sets the name of the cache. This is used to identify the cache.
It must be unique.
maxInMemory
- Sets the maximum number of objects that will be created in memory
eternal
- Sets whether elements are eternal. If eternal, timeouts are
ignored and the
element is never expired.
overflowToDisk
- Sets whether elements can overflow to disk when the in-memory cache
has reached the maxInMemory limit.
The following attributes are
optional:
timeToIdleSeconds
- Sets the time to idle for an element before it expires.
i.e. The maximum amount of time between accesses before an element
expires
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that an Element can idle for
infinity.
The default value is 0.
timeToLiveSeconds
- Sets the time to live for an element before it expires.
i.e. The maximum time between creation time and when an element expires.
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that and Element can live for
infinity.
The default value is 0.
diskPersistent
- Whether the disk store persists between restarts of the Virtual
Machine.
The default value is false.
diskExpiryThreadIntervalSeconds- The number of seconds between runs of
the disk expiry thread. The default value
is 120 seconds.
-->
<!-- Sample cache named sampleCache1
This cache contains a
maximum in memory of 10000 elements, and will expire
an element if it is idle for
more than 5 minutes and lives for more than
10 minutes.
If there are more than 10000
elements it will overflow to the
disk cache, which in this
configuration will go to wherever java.io.tmp is
defined on your system. On a
standard Linux system this will be /tmp"
-->
<cache name="sampleCache1"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
/>
<!-- Sample cache named sampleCache2
This cache has a maximum of
1000 elements in memory. There is no overflow to disk, so 1000
is also the maximum cache
size. Note that when a cache is eternal, timeToLive and timeToIdle
are not used and do not need
to be specified -->
<cache name="sampleCache2"
maxElementsInMemory="1000"
eternal="true"
overflowToDisk="false"
/>
<!-- Sample cache named sampleCache3. This cache
overflows to disk. The disk store is persistent
between cache and VM
restarts. The disk expiry thread interval is set to 10 minutes,
overriding
the default of 2
minutes. -->
<cache name="sampleCache3"
maxElementsInMemory="500"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="1"
/>
<!-- Place configuration for your caches
following -->
</ehcache>
|
Installing ehcache.xml
By default the CacheManager.create() method expects to find ehcache.xml
as a resource in the classpath. It uses
Configurator.getClass().getResource(/"ehcache.xml"). The ehcache.xml
file should be placed at the root of the classpath, not in a package.
If placed in a jar, it should be in the root level directory of the jar.
If ehcache cannot find ehcache.xml
it
will instead use the failsafe configuration.
The CacheManager can also obtain ehcache.xml in other ways. See Creating a CacheManager.
Failsafe Configuration
Ehcache comes
with a
failsafe configuration declared in the ehcache-failsafe.xml file, which
is provided in the ehcache.jar. It is only used when no ehcache.xml
configuration file is found in the classpath, and CacheManager is
created without any arguments. If it is used a log warning level
message will be issued.
Ehcache deliberately
avoids placing an ehcache.xml configuration file in the JAR archive so
as to avoid frustrating classpath precedence problems.
The failsafe configuration provides ehcache with the minimum to
function: specified diskStore and defaultCache elements. It sets the DiskStore
path attribute to the value given by the Java system property
"java.io.tmpdir"
(/tmp on Unix-like systems) and sets the default cache to reasonable
values. ehcache-failsafe.xml is reproduced here:
<ehcache>
<!-- Sets the path to the directory where cache
.data files are created.
If the path is a Java
System Property it is replaced by
its value in the
running VM.
The following
properties are translated:
user.home - User's
home directory
user.dir - User's
current working directory
java.io.tmpdir -
Default temp file path -->
<diskStore path="java.io.tmpdir"/>
<!--Default Cache configuration. These will
applied to caches programmatically created through
the CacheManager.
The following attributes are
required:
maxElementsInMemory
- Sets the maximum number of objects that will be created in memory
eternal
- Sets whether elements are eternal. If eternal, timeouts are
ignored and the
element is never expired.
overflowToDisk
- Sets whether elements can overflow to disk when the in-memory cache
has reached the maxInMemory limit.
The following attributes are
optional:
timeToIdleSeconds
- Sets the time to idle for an element before it expires.
i.e. The maximum amount of time between accesses before an element
expires
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that an Element can idle for
infinity.
The default value is 0.
timeToLiveSeconds
- Sets the time to live for an element before it expires.
i.e. The maximum time between creation time and when an element expires.
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that and Element can live for
infinity.
The default value is 0.
diskPersistent
- Whether the disk store persists between restarts of the Virtual
Machine.
The default value is false.
diskExpiryThreadIntervalSeconds- The number of seconds between runs of
the disk expiry thread. The default value
is 120 seconds.
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
</ehcache>
|
Programmatic
Configuration
The
configuration for
a Cache can be specified programmatically in the Cache constructor:
public Cache(String name,
int maximumSize,
boolean overflowToDisk,
boolean eternal,
long timeToLiveSeconds,
long timeToIdleSeconds,
boolean diskPersistent,
long diskExpiryThreadIntervalSeconds) { {
...
}
|
Here is an example
which creates a cache called test.
//Create
a CacheManager using defaults
CacheManager manager = CacheManager.create();
//Create a Cache specifying its configuration.
Cache testCache = new Cache("test", 1000, true, false, 500, 200, false,
120);
manager.addCache(cache);
|
Once the cache is created, add it to the list of caches managed by the
CacheManager:
manager.addCache(testCache);
|
The cache is not usable until it has been added.
10 Code
Samples
Ehcache comes
with a comprehensive JUnit test suite. The test suite shows you how to
use all the features of ehcache.
Following are code samples for the most common tasks.
For comprehensive code samples see the test source code:
- In CVS online with viewcvs
- In the source distributed with the download
Using the
CacheManager
Create a
CacheManager using defaults
CacheManager
manager = CacheManager.create();
|
Create a
CacheManager specifying a configuration file.
CacheManager
manager = CacheManager.create("src/config/ehcache.xml");
|
Create a
CacheManager from a configuration resource in the classpath.
URL
url = getClass().getResource("/anothername.xml");
CacheManager manager = CacheManager.create(url);
|
Create a
CacheManager from a configuration in an InputStream.
InputStream fis = new FileInputStream(new
File("src/config/ehcache.xml").getAbsolutePath());
try {
manager = CacheManager.create(fis);
} finally {
fis.close();
}
|
Shutdown the
CacheManager
Using Caches
Obtain a Cache
called "sampleCache1" which has been preconfigured
in the configuration file
Cache
cache = manager.getCache("sampleCache1");
|
Create a new Cache
called "test" which does not exist, using
defaults
CacheManager
manager = CacheManager.create();
manager.addCache("test");
|
Create a new Cache
called "test", specifying its properties.
CacheManager
manager = CacheManager.create();
Cache cache = new Cache("test", 1, true, false, 5, 2);
manager.addCache(cache);
|
Put an element
into a cache
Element
element = new Element("key1", "value1"
cache.put(new Element(element);
|
Get an element
from a cache
Element
element = cache.get("key1");
|
11 Using ehcache as a
Hibernate plugin
Ehcache
easily integrates with the Hibernate
Object/Relational persistence and query service. Gavin King, the
maintainer of Hibernate, is also a committer to the ehcache project.
This ensures ehcache will remain a first class cache for Hibernate.
Since Hibernate 2.1, ehcache has been the default cache,
for Hibernate.
The net.sf.ehcache.hibernate package provides classes integrating
ehcache with Hibernate. Hibernate is an application of ehcache. Ehcache
is also widely used a general-purpose Java cache.
To use ehcache with Hibernate do the following:
- Ensure ehcache is enabled in the Hibernate configuration (it
should already be)
- Add the cache element to the Hibernate mapping file, either
manually, or via hibernatedoclet for each Domain Object you wish to
cache
- Add the cache element to the Hibernate mapping file, either
manually, or via hibernatedoclet for each Domain Object collection you
wish to cache
- Add the cache element to the Hibernate mapping file, either
manually, or via hibernatedoclet for each Hibernate query you wish to
cache
- Create a cache element in ehcache.xml
Each of these steps is illustrated using a fictional Country Domain
Object.
For more about cache configuration in Hibernate see the Hibernate
documentation. Parts of this chapter are drawn from Hibernate
documentation and source code comments. They are reproduced here for
convenience in using ehcache.
Ensuring ehcache is
enabled
To ensure ehcache is enabled, verify that the
hibernate.cache.provider_class property is set to
net.sf.ehcache.hibernate.Provider in the Hibernate configuration file;
typically hibernate.cfg.xml. By default it should already be.
<property
name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.Provider</property> |
The provider can also be set programmatically in Hiberante using
Configuration.setProperty("hibernate.cache.provider_class",
"net.sf.ehcache.hibernate.Provider").
Hibernate Mapping
Files
In Hibernate,
each domain object requires a mapping file.
For example
to enable cache entries for
the domain object com.somecompany.someproject.domain.Country there
would be a mapping file something like the following:
<hibernate-mapping>
<class
name="com.somecompany.someproject.domain.Country"
table="ut_Countries"
dynamic-update="false"
dynamic-insert="false"
>
...
</hibernate-mapping> |
To enable
caching, add the following element.
<cache
usage="read-write|nonstrict-read-write|read-only" /> |
e.g.
<cache
usage="read-write" /> |
read-write
Caches data that is sometimes updated while maintaining the semantics
of "read committed" isolation level. If the database is set to
"repeatable read", this concurrency strategy almost maintains the
semantics. Repeatable read isolation is compromised in the case
of concurrent writes.
This is an "asynchronous" concurrency strategy.
nonstrict-read-write
Caches data that is sometimes updated without ever locking the
cache. If concurrent access to an item is possible, this
concurrency strategy makes no guarantee that the item returned
from the cache is the latest version available in the database.
Configure your cache timeout accordingly!
This is an "asynchronous" concurrency strategy.
This policy is the fastest. It does not use synchronized methods
whereas read-write and read-only both do.
read-only
Caches data that is never updated.
Hibernate Doclet
Hibernate Doclet, part of the XDoclet
project, can be used to generate Hibernate mapping files from markup in
JavaDoc comments.
Following is an example of a Class level JavaDoc which configures a
read-write cache for the Country Domain Object:
/**
* A Country Domain Object
*
* @hibernate.class table="COUNTRY"
* @hibernate.cache usage="read-write"
*/
public class Country implements Serializable
{
...
}
|
The @hibernate.cache usage tag should be set to one
of read-write, nonstrict-read-write and read-only.
Configuration with
ehcache.xml
Because ehcache.xml has a defaultCache, caches will always be created
when required by Hibernate. However more control can be exerted by
specifying a configuration per cache, based on its name.
In particular, because Hibernate caches are populated from databases,
there is potential for them to get very large. This can be controlled
by capping their maxElementsInMemory and specifying whether to
overflowToDisk beyond that.
Hibernate uses a specific convention for the naming of caches of Domain
Objects, Collections, and Queries.
Domain Objects
Hibernate
creates caches named after the fully qualified name of Domain
Objects.
So, for example
to create a cache for
com.somecompany.someproject.domain.Country create a cache configuration
entry similar to the following in ehcache.xml.
<cache
name="com.somecompany.someproject.domain.Country"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/> |
Hibernate
CacheConcurrencyStrategy
read-write, nonstrict-read-write and read-only policies apply to Domain
Objects.
Collections
Hibernate creates collection caches named after the fully qualified
name of the Domain Object followed by "." followed by the collection
field name.
For example, a Country domain object has a set of
advancedSearchFacilities. The Hibernate doclet for the accessor
looks like:
/**
* Returns the advanced search facilities that
should appear for this country.
* @hibernate.set cascade="all" inverse="true"
* @hibernate.collection-key column="COUNTRY_ID"
* @hibernate.collection-one-to-many
class="com.wotif.jaguar.domain.AdvancedSearchFacility"
* @hibernate.cache usage="read-write"
*/
public Set getAdvancedSearchFacilities() {
return
advancedSearchFacilities;
}
|
You need an additional cache configured for the set. The ehcache.xml
configuration looks like:
<cache name="com.somecompany.someproject.domain.Country"
maxElementsInMemory="50"
eternal="false"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
<cache
name="com.somecompany.someproject.Country.advancedSearchFacilities"
maxElementsInMemory="450"
eternal="false"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
|
Hibernate
CacheConcurrencyStrategy
read-write, nonstrict-read-write and read-only policies apply to Domain
Object collections.
Queries
Hibernate allows the caching of query results. Two caches, one called
"net.sf.hibernate.cache.StandardQueryCache" in version 2.1.4 and higher
and "net.sf.hibernate.cache.QueryCache" in versions 2.1.0
- 2.1.3, and one called "net.sf.hibernate.cache.UpdateTimestampsCache"
are always used.
StandardQueryCache
This cache is used if you use a query cache without setting a
name. A typical ehcache.xml configuration is:
<cache
name="net.sf.hibernate.cache.StandardQueryCache"
maxElementsInMemory="5"
eternal="false"
timeToLiveSeconds="120"
overflowToDisk="true"/>
|
UpdateTimestampsCache
Tracks the timestamps of the most recent updates to particular tables.
It is important that the cache timeout of the underlying cache
implementation be set to a higher value than the timeouts of any of the
query caches. In fact, it is recommend that the the underlying cache
not be configured for expiry at all.
A typical ehcache.xml configuration is:
<cache
name="net.sf.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="true"/>
|
Named Query Caches
In addition, a QueryCache can be given a specific name in Hibernate
using Query.setCacheRegion(String name). The name of the cache in
ehcache.xml is then the name given in that method. The name can be
whatever you want, but by convention you should use "query." followed
by a descriptive name.
E.g.
<cache name="query.AdministrativeAreasPerCountry"
maxElementsInMemory="5"
eternal="false"
timeToLiveSeconds="86400"
overflowToDisk="true"/> |
Using Query Caches
For example, let's say we have a common query running against the
Country Domain.
Code to use a query cache follows:
public List getStreetTypes(final Country country) throws
HibernateException {
final Session session =
createSession();
try {
final Query query = session.createQuery(
"select st.id, st.name"
+ " from StreetType st "
+ " where st.country.id = :countryId "
+ " order by st.sortOrder desc, st.name");
query.setLong("countryId", country.getId().longValue());
query.setCacheable(true);
query.setCacheRegion("query.StreetTypes");
return query.list();
} finally {
session.close();
}
} |
The query.setCacheable(true);
line caches the query.
The query.setCacheRegion("query.StreetTypes");
line sets
the name of the Query Cache.
Hibernate
CacheConcurrencyStrategy
None of read-write, nonstrict-read-write and read-only policies apply
to Domain Objects. Cache policies are not configurable for query cache.
They act like a non-locking read only cache.
Hibernate Caching
Performance Tips
To get the most out of ehcache with Hibernate, Hibernate's use of it's
in-process cache is important to understand.
In-Process Cache
From Hibernate's point of view, Ehcache is an in-process cache. Cached
objects are accessible across different sessions. They are common to
the Java process.
Object Id
Hibernate identifies cached objects via an object id. This is normally
the primary key of a database row.
Session.load
Session.load will always try to use the cache.
Session.find and
Query.find
Session.find does not use the cache for the primary object. Hibernate
will try to use the cache for any associated objects. Session.find does
however cause the cache to be populated.
Query.find works in exactly the same way.
Use these where the chance of getting a cache hit is low.
Session.iterate and
Query.iterate
Session.iterate always uses the cache for the primary object and any
associated objects.
Query.iterate works in exactly the same way.
Use these where the chance of getting a cache hit is high.
