This document is focused on the use of the JBoss Cache clustered transactional caching library as a tool for caching data in a Hibernate-based application.
Working with object-oriented software and a relational database can be cumbersome and time consuming in today's enterprise environments. Hibernate is an object/relational mapping tool for Java environments. The term object/relational mapping (ORM) refers to the technique of mapping a data representation from an object model to a relational data model with a SQL-based schema.
Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities and can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC.
In any application that works with a relational database, caching data
retrieved from the database can potentially improve application performance.
Hibernate provides a number of facilities for caching data on the
database client side, i.e. in the Java process in which Hibernate is
running. The primary facility is the Hibernate Session
itself, which maintains a transaction-scoped cache of persistent data.
If you wish to cache data beyond the scope of a transaction, it is
possible to configure a cluster or JVM-level (technically a
SessionFactory
-level) cache on a class-by-class,
collection-by-collection and query-by-query basis. This type of cache
is referred to as a Second Level Cache.
Hibernate provides a pluggable architecture for implementing its Second Level Cache, allowing it to integrate with a number of third-party caching libraries. This document is focused on the use of the JBoss Cache clustered transactional caching library as an implementation of the Second Level Cache. It specifically focuses on JBoss Cache 3.
If you are new to Hibernate and Object/Relational Mapping or even Java, please follow these steps:
Read the Hibernate Reference Documentation, particularly the Introduction and Architecture sections.
Have a look at the eg/
directory in the Hibernate
distribution, it contains a simple standalone application. Copy your
JDBC driver to the lib/
directory and edit
etc/hibernate.properties
, specifying correct values for
your database. From a command prompt in the distribution directory,
type ant eg
(using Ant), or under Windows, type
build eg
.
Use the Hibernate Reference Documentation as your primary source of information. Consider reading Java Persistence with Hibernate (http://www.manning.com/bauer2) if you need more help with application design or if you prefer a step-by-step tutorial. Also visit http://caveatemptor.hibernate.org and download the example application for Java Persistence with Hibernate.
FAQs are answered on the Hibernate website.
Third party demos, examples, and tutorials are linked on the Hibernate website.
The Community Area on the Hibernate website is a good resource for design patterns and various integration solutions (Tomcat, JBoss AS, Struts, EJB, etc.).
If you are new to the Hibernate Second Level Cache or to JBoss Cache, please follow these steps:
Read the Hibernate Reference Documentation, particularly the Second Level Cache and Configuration sections.
Read the JBoss Cache User Guide, Core Edition.
Use this guide as your primary source of information on the usage of JBoss Cache 3 as a Hibernate Second Level Cache.
If you have questions, use the user forum linked on the Hibernate website. The user forum on the JBoss Cache website is also useful. We also provide a JIRA issue tracking system for bug reports and feature requests. If you are interested in the development of Hibernate, join the developer mailing list. If you are interested in translating this documentation into your language, contact us on the developer mailing list.
Commercial development support, production support, and training for Hibernate is available through Red Hat, Inc. (see http://www.hibernate.org/SupportTraining/). Hibernate is a Professional Open Source project and a critical component of the JBoss Enterprise Application Platform.
A JBoss Cache configuration file, and usually a JGroups configuration
file[1], need to be on the classpath. The
hibernate-jbosscache.jar
includes standard
configuration files in the org.hibernate.cache.jbc.builder
package. The jbc-configs.xml
file is for JBoss
Cache and the jgroups-stacks.xml
file is for JGroups.
See Section 3.2, “Configuring JBoss Cache” and Section 3.3, “JGroups Configuration”
for more details on these files. Users can create their own versions
and tell the SessionFactory
to use them; see
Section 3.1, “Configuring the Hibernate Session Factory” for details.
The key steps in using JBoss Cache as a second level cache are to:
hibernate.cache.region.factory_class= org.hibernate.cache.jbc.MultiplexedJBossCacheRegionFactory
There are a number of values that can be provided for the
hibernate.cache.region.factory_class
property, depending on how you want the JBoss Cache integration
to work. Based on what factory class you specify, there are
additional detail configuration properties you can add to further
control that factory. However, simply specifying the
MultiplexedJBossCacheRegionFactory
shown
above provides a reasonable set of default values useful for
many applications. See Section 3.1.2, “Specifying the RegionFactory Implementation”
for more details.
Do not set the legacy hibernate.cache.provider_class
property when using JBoss Cache 3. That is a legacy property
from before Hibernate 3.3's redesign of second level caching
internals. It will not work with JBoss Cache 3.
Tell Hibernate you want to enable caching of entities and collections. No need to set this property if you don't:
hibernate.cache.use_second_level_cache=true
Tell Hibernate you want to enable caching of query results. No need to set this property if you don't:
hibernate.cache.use_query_cache=true
If you have enabled caching of query results, tell Hibernate if you want to suppress costly replication of those results around the cluster. No need to set this property if you want query results replicated:
hibernate.cache.jbc.query.localonly=true
See Chapter 3, Configuration for full details on configuration.
This chapter focuses on some of the core concepts underlying how the JBoss Cache-based implementation of the Hibernate Second Level Cache works. There's a fair amount of detail, which certainly doesn't all need to be mastered to use JBoss Cache with Hibernate. But, an understanding of some of the basic concepts here will help a user understand what some of the typical configurations discussed in the next chapter are all about.
If you want to skip the details for now, feel free to jump ahead to Section 2.3.4, “Bringing It All Together”
The semantics of query caching are significantly different
from those of entity caching. A database row that reflects an
entity's state can be locked, with cache updates applied with that
lock in place. The semantics of entity caching take advantage of
this fact to help ensure cache consistency across the cluster.
There is no clear database analogue to a query result set that can
be efficiently locked to ensure consistency in the cache. As a result,
the fail-fast semantics used with the entity caching put
operation are not available; instead query caching has semantics
akin to an entity insert, including costly synchronous cluster
updates and the JBoss Cache two phase commit protocol. Furthermore,
Hibernate must agressively invalidate query results from the cache
any time any instance of one of the entity classes involved in the
query's WHERE clause changes. All such query results are invalidated,
even if the change made to the entity instance would not have affected
the query result. It is not performant for Hibernate to try to
determine if the entity change would have affected the query result,
so the safe choice is to invalidate the query. See
Section 2.1.4, “Timestamps” for more on query
invalidation.
The effect of all this is that query caching is less likely to provide a performance boost than entity/collection caching. Use it with care and benchmark your application with it enabled and disabled. Be careful about replicating query results; caching them locally only on the node that executed the query will be more performant unless the query is quite expensive, is very likely to be repeated on other nodes, and is unlikely to be invalidated out of the cache.[2].
The JBoss Cache-based implementation of query caching adds a couple of interesting semantics, both designed to ensure that query cache operations don't block transactions from proceeding:
The insertion of a query result into the cache is very much like the insertion of a new entity. The difference is it is possible for two transactions, possibly on different nodes, to try to insert the same query at the same time. (If this happened with entities, the database would throw an exception with a primary key violation before any caching work could start). This could lead to long delays as the transactions compete for cache locks. To prevent such delays, the cache integration layer will set a very short (a few ms) lock timeout before attempting to cache a query result. If there is any sort of locking conflict, it will be detected quickly, and the attempt to cache the result will be quietly abandonded.
A read of a query result does not result in any long-lasting read lock in the cache. Thus, the fact that an uncommitted transaction had read a query result does not prevent concurrent transactions from subsequently invalidating that result and caching a new result set. However, an insertion of a query result into the cache will result in an exclusive write lock that lasts until the transaction that did the insert commits; this lock will prevent other transactions from reading the result. Since the point of query caching is to improve performance, blocking on a cache read for an extended period seems suboptimal. So, the cache integration code will set a very low lock acquisition timeout before attempting the read; if there is a lock conflict, the read will silently fail, resulting in a cache miss and a re-execution of the query against the database.
JBoss Cache is a very flexible tool and includes a great number of configuration options. See the JBoss Cache User Guide for an in depth discussion of these options. Here we focus on the main concepts that are most important to the Second Level Cache use case. This discussion will focus on concepts; see Section 3.2, “Configuring JBoss Cache” for details on the actual configurations involved.
Eviction refers to the process by which old, relatively unused, or excessively voluminous data can be dropped from the cache, allowing the cache to remain within a memory budget. Generally, applications that use the Second Level Cache should configure eviction. See Chapter 4, Cache Eviction for details.
There were three key changes that make this improvement possible:
See Chapter 3, Configuration for more on how to do this.
The task of a Hibernate Second Level Cache user is to:
Tell Hibernate where to find that set of configurations
on the classpath. See Section 3.1, “Configuring the Hibernate Session Factory” for
details on how to do this. This is not necessary if the
default set included in hibernate-jbosscache.jar
is used.
In the JBoss Cache configurations you are using specify
the name of the channel you want to use. This should be
one of the named configurations in the JGroups XML file.
The default set of JBoss Cache configurations found in the
hibernate-jbosscache.jar
already have
appropriate default choices. See Section 3.2.3.4, “JGroups Channel Configuration”
for details on how to set this if you don't wish to use the
defaults.
See Section 3.3, “JGroups Configuration” for more on JGroups.
Here's a decision tree you can follow:
If you are using query caching, from the above decision tree you've either got your timestamps sharing a cache with other data types, or they are by themselves. Either way, the cache being used for timestamps must have initial state transfer enabled. Now, if the timestamps are sharing a cache with entities, collections or queries, decide whether you want initial state transfer for that other data. See Section 2.2.5, “Initial State Transfer” for the implications of this. If you don't want initial state transfer for the other data, you'll need to have a separate cache for the timestamps.
Finally, if your queries are sharing a cache configured
for replication, decide if you want the cached query results
to replicate. (The timestamps cache must
replicate.) If not, you'll want to set the
hibernate.cache.region.jbc2.query.localonly=true
option when you configure your SessionFactory
Once you've made these decisions, you know whether you need just one underlying JBoss Cache instance, or more than one. Next we'll see how to actually configure the setup you've selected.
[2] See the discussion of the
hibernate.cache.jbc.query.localonly
property in Section 3.1, “Configuring the Hibernate Session Factory”
There are three main areas of configuration involved in using JBoss Cache
3 for your Hibernate Second Level Cache: configuring the Hibernate
SessionFactory
, configuring the underlying JBoss Cache
instance(s), and configuring the JGroups ChannelFactory
.
If you use the standard JBoss Cache and JGroups configurations that ship
with the hibernate-jbosscache2.jar
, then all you
need to worry about is the SessionFactory
configuration.
There are five basic steps to configuring the
SessionFactory
:
Make sure your Hibernate is configured to use JTA transactions. See Section 1.2.2, “JTA Transactional Support” for details.
Tell Hibernate you whether to enable caching of entities and collections. No need to set this property if you don't:
hibernate.cache.use_second_level_cache=true
Tell Hibernate you want to enable caching of query results. No need to set this property if you don't:
hibernate.cache.use_query_cache=true
If you have enabled caching of query results, tell Hibernate if you want to suppress costly replication of those results around the cluster. No need to set this property if you want query results replicated:
hibernate.cache.jbc.query.localonly=true
Finally, you need to tell Hibernate what RegionFactory
implementation to use to manage your caches. You do this by
setting the hibernate.cache.region.factory_class
configuration option.
hibernate.cache.region.factory_class= org.hibernate.cache.jbc.MultiplexedJBossCacheRegionFactory
To determine the correct factory class, you must decide whether you need just one underlying JBoss Cache instance to support the different types of caching you will be doing, or whether you need more than one. See Chapter 2, Core Concepts and particularly Section 2.3.4, “Bringing It All Together” for more on how to make that decision. Once you know the answer, see Section 3.1.2, “Specifying the RegionFactory Implementation” to find the factory class that best meets your needs.
Once you've specified your factory class, there may be other factory-class-specific configurations you may want to set. The available options are explained below.
The SharedJBossCacheRegionFactory
supports a
number of additional configuration options:
Note that the default treecache.xml
file does
not exist; it is up to the user to provide it.
The JndiSharedJBossCacheRegionFactory
supports an
additional configuration option:
The MultiplexedJBossCacheRegionFactory
supports a
number of additional configuration options:
Many of the default values name JBoss Cache configurations in the
standard jbc-configs.xml
file found in the
hibernate-jbosscache.jar
. See
Section 3.2.4, “Standard JBoss Cache Configurations” for details on those
configurations. If you want to set
hibernate.cache.jbc.configs
and use your
own JBoss Cache configuration file, you can still take advantage of
these default names; just name the configurations in your file
to match.
This is all looks a bit complex, so let's show what happens if you just configure the defaults, with query caching enabled:
hibernate.cache.use_second_level_cache=true hibernate.cache.use_query_cache=true hibernate.cache.region.factory_class= org.hibernate.cache.jbc.MultiplexedJBossCacheRegionFactory
You would end up using two JBoss Cache instances:
One, for the entities, collection and queries, would use the
optimistic-entity
configuration. This cache
would use optimistic locking, synchronous invalidation and would
disable initial state transfer.
The second, for timestamps, would use the
timestamps-cache
configuration. This cache
would use pessimistic locking, asynchronous replication and would
enable initial state transfer.
See Section 3.2.4, “Standard JBoss Cache Configurations” for more on these standard cache configurations.
If you hadn't set hibernate.cache.use_query_cache=true
you'd just have the single optimistic-entity
cache,
shared by the entities and collections.
The JndiMultiplexedJBossCacheRegionFactory
supports the following additional configuration options:
See Section 3.1.5, “The MultiplexedJBossCacheRegionFactory” for a discussion of
how the various hibernate.cache.jbc.cfg
options combine to determine what JBoss Cache instances are used.
The JndiMultiplexedJBossCacheRegionFactory
requires
that the JBoss Cache CacheManager
is already
bound in JNDI; it will not create and bind one if it isn't. It is
up to the the user to ensure the cache is CacheManager
and bound in JNDI before the Hibernate SessionFactory
is created.
If the resource path to your file is treecache.xml
,
the SharedJBossCacheRegionFactory
will find it
by default; otherwise you will need to use a configuration option
to tell it where it is. See Section 3.1.3, “The SharedJBossCacheRegionFactory”
for instruction on how to do that.
The JBoss Cache CacheMode attribute encapsulates whether the cache uses replication, invalidation or local mode, as well as whether messages should be synchronous or asynchronous. See Section 2.2.1, “Replication vs. Invalidation vs. Local Mode” and Section 2.2.2, “Synchronous vs. Asynchronous” for a discussion of these concepts.
The CacheMode is configured as follows:
<!-- Legal modes are LOCAL REPL_ASYNC REPL_SYNC INVALIDATION_ASYNC INVALIDATION_SYNC --> <attribute name="CacheMode">INVALIDATION_SYNC</attribute>
The JBoss Cache NodeLockingScheme attribute configures whether optimistic locking or pessimistic locking should be used. See Section 2.2.3, “Locking Scheme” for a discussion of locking.
The NodeLockingScheme is configured as follows:
<!-- Node locking scheme: MVCC PESSIMISTIC (default) --> <attribute name="NodeLockingScheme">MVCC</attribute>
The JBoss Cache IsolationLevel attribute configures whether READ_COMMITTED or REPEATABLE_READ semantics are needed if pessimistic locking is used. It has no effect if optimistic locking is used. See Section 2.2.4, “Isolation Level” for a discussion of isolation levels.
The IsolationLevel is configured as follows:
<!-- Isolation level: READ_COMMITTED REPEATABLE_READ --> <attribute name="IsolationLevel">READ_COMMITTED</attribute>
<attribute name="MultiplexerStack">udp</attribute>
<!-- JGroups protocol stack properties. --> <attribute name="ClusterConfig"> <config> <UDP mcast_addr="228.10.10.10" mcast_port="45588" tos="8" ... many more details <pbcast.STATE_TRANSFER/> <pbcast.FLUSH timeout="0"/> </config> </attribute>
See Section 3.3, “JGroups Configuration” for more on JGroups configuration.
See Section 2.2.5, “Initial State Transfer” for a discussion of the concept of initial state transfer.
Initial State Transfer is configured as follows:
<!-- Whether or not to fetch state on joining a cluster. --> <attribute name="FetchInMemoryState">false</attribute>
A few notes on the above table:
Valid For and Optimal For describe the suitability of the configuration for the four types of data -- Entities, Collections, Queries and Timestamps.
State Transfer indicates whether an initial state transfer will be performed when a cache region is activated.
Each of the configurations uses the udp
JGroups
protocol stack configuration (except local-query
,
which doesn't use JGroups at all). Since they all use the same
stack, if more than one of these caches is created they will share
their JGroups resources. See Section 3.3.2, “Standard JGroups Configurations”
for a description of the standard stacks.
The configurations that use MVCC or PESSIMISTIC locking use isolation level
READ_COMMITTED. There are also four other standard configurations
not shown in the table that use REPEATABLE_READ:
mvcc-entity-repeatable
,
mvcc-shared-repeatable
,
pessimistic-entity-repeatable
and
pessimistic-shared-repeatable
. In all other
respects those configurations are the same as the similarly named
non- "-repeatable" configurations shown in the table.
These standard configurations are a good choice for many applications. The primary reason users may want to use their own configurations is to support more complex eviction setups. See Chapter 4, Cache Eviction for more on the kinds of things you can do with eviction.
maxNodes
- This is the maximum number of nodes allowed in this region.
0 denotes no limit. If the region has more nodes than this,
the least recently used nodes will be evicted until the number
of nodes equals this limit.
timeToLiveSeconds
- The amount of time a node is not written to or read (in seconds)
before the node should be evicted. 0 denotes no limit. Nodes that
exceed this limit will be evicted whether or not a
maxNodes
limit has been breached.
maxAgeSeconds
- Lifespan of a node (in seconds) regardless of idle time before
the node is swept away. 0 denotes no limit. Nodes that
exceed this limit will be evicted whether or not a
maxNodes
or timeToLiveSeconds
limit has been breached.
minTimeToLiveSeconds
- the minimum amount of time a node must be allowed to live after
being accessed before it is allowed to be considered for eviction.
0 denotes that this feature is disabled, which is the default value.
Should be set to a value less than timeToLiveSeconds
.
It is recommended that this be set to a value slightly greater
than the maximum amount of time a transaction that affects the
region should take to complete. Configuring this is particularly
important when optimistic locking is used in conjunction with
invalidation.
All FQNs in a second level cache include two elements:
A Region Name, which is either
any value assigned to a <cache>
element's
region
attribute in a class or collection mapping.
See Section 4.2.2, “Entities” for
an example.
Any value assigned to a Hibernate Query
object's cacheRegion
property. See
Section 4.2.4, “Queries” for an
example.
The escaped class name of the type
being cached. An escaped class name
is simply a fully-qualified class name with any
.
replaced with a /
-- for example org/example/Foo
.
The FQN for the cache region where entities of a particular class are stored is derived as follows:
/
+ Region Prefix + /
+ Region Name + /ENTITY
<class name="org.example.Foo"> <cache usage="transactional" region="foo_region"/> .... </class>
The FQN of the region where Foo
entities
would be cached is /appA/foo_region/ENTITY
.
<class name="org.example.Bar"> <cache usage="transactional"/> .... </class>
the FQN of the region where Bar
entities
would be cached is /appA/org/example/Bar/ENTITY
.
The FQN for the cache region where entities of a particular class is stored is derived as follows:
/
+ Region Prefix + /
+ Region Name + /COLL
So, let's say our example Foo
entity
included a collection field bars
that
we wanted to cache:
<class name="org.example.Foo"> <cache usage="transactional"/> .... <set name="bars"> <cache usage="transactional" region="foo_region"/> <key column="FOO_ID"/> <one-to-many class="org.example.Bar"/> </set> </class>
The FQN of the region where the collection would be cached
would be
/appA/foo_region/COLL
.
/
+ Region Prefix + /
+ Region Name + /QUERY
Say we had the following query (again with a region prefix set to "appA"):
List blogs = sess.createQuery("from Blog blog " + "where blog.blogger = :blogger") .setEntity("blogger", blogger) .setMaxResults(15) .setCacheable(true) .setCacheRegion("frontpages") .list();
The FQN of the region where this query's results would be cached
would be /appA/frontpages/QUERY
.
Let's see a possible eviction configuration for this scenario:
<attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Default region to pick up anything we miss in the more specific regions below. --> <region name="/_default_"> <attribute name="maxNodes">500</attribute> <attribute name="timeToLiveSeconds">300</attribute> <attribute name="minTimeToLiveSeconds">120</attribute> </region> <!-- Don't ever evict modification timestamps --> <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/> <!-- Reference data --> <region name="/appA/reference"> <!-- Keep all reference data if it's being used --> <attribute name="maxNodes">0</attribute> <!-- Keep it around a long time (4 hours) --> <attribute name="timeToLiveSeconds">14400</attribute> <attribute name="minTimeToLiveSeconds">120</attribute> </region> <!-- Be more aggressive about queries on reference data --> <region name="/appA/reference/QUERY"> <attribute name="maxNodes">200</attribute> <attribute name="timeToLiveSeconds">1000</attribute> <attribute name="minTimeToLiveSeconds">120</attribute> </region> <!-- Lots of entity instances from this package, but different users are unlikely to share them. So, we can cache a lot, but evict unused ones pretty quickly. --> <region name="/appA/org/example/hibernate"> <attribute name="maxNodes">50000</attribute> <attribute name="timeToLiveSeconds">1200</attribute> <attribute name="minTimeToLiveSeconds">120</attribute> </region> <!-- Clean up misc queries very promptly --> <region name="/appA/org/hibernate/cache/StandardQueryCache"> <attribute name="maxNodes">200</attribute> <attribute name="timeToLiveSeconds">240</attribute> <attribute name="minTimeToLiveSeconds">120</attribute> </region> </config> </attribute>
We've now gone through all the main concepts and the configuration details; now we'll look a bit under the covers to understand a bit more about the architectural design of the Hibernate/JBoss Cache integration. Readers can skip this chapter if they aren't interested in a look under the covers.
void start(Settings settings, Properties properties) throws CacheException; void stop(); boolean isMinimalPutsEnabledByDefault(); long nextTimestamp(); EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException; CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription cdd) throws CacheException; QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException; TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException;
The various build***Region
methods are invoked as
Hibernate detects it needs to cache different data. Hibernate can
invoke these methods multiple times, with different
regionName
values; each call results in the
establishment of a separate area in the underlying JBoss Cache
instance(s). For example, if an application includes an
entity class org.example.Order
and another entity
class org.example.LineItem
, you would see two
calls to buildEntityRegion
, one for the
Order
class and one for the
LineItem
class. (Note that it is possible, and
recommended, to configure one or more shared regions for entities,
collections and queries. See Section 4.2, “Organization of Data in the Cache”
for some examples.)
For each call to a build***Region
method, the
region factory returns a region object implementing the
EntityRegion
, CollectionRegion
,
QueryResultsRegion
or TimestampsRegion
interface. Each interface specifies the needed semantics for
caching the relevant type of data. Thereafter, the Hibernate core
invokes on that region object to manage reading and writing data
in the cache.
Next, we'll look at the architecture of how the JBoss Cache integration implements these interfaces, first in the case where a single JBoss Cache instance is used, next in the case where multiple instances are desired.
The situation when multiple JBoss Cache instances are used is very similar to the single cache case:
MultiplexedCacheInstanceManager
analyzes
Hibernate's configuration to determine the name of the desired
cache configuration for entities, collections, queries and
timestamps. See Section 3.1.5, “The MultiplexedJBossCacheRegionFactory” for
details. It then asks its CacheManager
to
provide each needed cache. In the diagram, two different caches are needed:
One, using the "optimistic-entity" configuration, that is used for entities, collections and queries
Another, with the "timestamps-cache" configuration, that is used for timestamps.
Both the "optimistic-entity" configuration and the "timestamps-cache"
configuration specify the use of the "udp" JGroups channel
configuration, so the CacheManager
's
ChannelFactory
will ensure that they share
the underlying JGroups resources.
The way the region factory creates regions is exactly the same as in the single JBoss Cache case; the only difference is the region factory's internal reference to its timestamps cache now points to a different cache object from the entity, collection and query cache references.
Copyright © 2009 Red Hat, Inc.