JBoss.orgCommunity Documentation
         A
         Cache
         consists of a collection of
         Node
         instances, organised in a tree
         structure. Each
         Node
         contains a
         Map
         which holds the data
         objects to be cached. It is important to note that the structure is a mathematical tree, and not a graph; each
         Node
         has one and only one parent, and the root node is denoted by the constant fully qualitied name,
         Fqn.ROOT
         .
      
The reason for organising nodes as such is to improve concurrent access to data and make replication and persistence more fine-grained.
         In the diagram above, each box represents a JVM. You see 2 caches in separate JVMs, replicating data to each
         other.
         These VMs can be located on the same physical machine, or on 2 different machines connected by a network link.
         The
         underlying group communication between networked nodes is done using
         JGroups
         .
      
Any modifications (see API chapter ) in one cache instance will be replicated to the other cache. Naturally, you can have more than 2 caches in a cluster. Depending on the transactional settings, this replication will occur either after each modification or at the end of a transaction, at commit time. When a new cache is created, it can optionally acquire the contents from one of the existing caches on startup.
         In addition to
         Cache
         and
         Node
         interfaces, JBoss Cache exposes more powerful
         CacheSPI
         and
         NodeSPI
         interfaces, which offer more control over the internals
         of JBoss Cache. These interfaces are not intended for general use, but are designed for people who wish to
         extend and enhance JBoss Cache, or write custom
         Interceptor
         or
         CacheLoader
         instances.
      
         The
         CacheSPI
         interface cannot be created, but is injected into
         Interceptor
         and
         CacheLoader
         implementations by the
         setCache(CacheSPI cache)
         methods on these interfaces.
         CacheSPI
         extends
         Cache
         so all the functionality of the basic API is made available.
      
         Similarly, a
         NodeSPI
         interface cannot be created. Instead, one is obtained by performing operations on
         CacheSPI
         ,
         obtained as above. For example,
         Cache.getRoot() : Node
         is overridden as
         CacheSPI.getRoot() : NodeSPI
         .
      
         It is important to note that directly casting a
         Cache
         or
         Node
         to it's SPI
         counterpart is not recommended and is bad practice, since the inheritace of interfaces it is not a contract
         that is guaranteed to be upheld moving forward. The exposed public APIs, on the other hand, is guaranteed to
         be upheld.
      
Since the cache is essentially a collection of nodes, aspects such as clustering, persistence, eviction, etc. need to be applied to these nodes when operations are invoked on the cache as a whole or on individual nodes. To achieve this in a clean, modular and extensible manner, an interceptor chain is used. The chain is built up of a series of interceptors, each one adding an aspect or particular functionality. The chain is built when the cache is created, based on the configuration used.
         It is important to note that the
         NodeSPI
         offers some methods (such as the
         xxxDirect()
         method
         family) that operate on a node directly without passing through the interceptor stack. Plugin authors should
         note
         that using such methods will affect the aspects of the cache that may need to be applied, such as locking,
         replication, etc. Basically, don't use such methods unless you
         really
         know what you're doing!
      
            An
            Interceptor
            is an abstract class, several of which comprise an interceptor chain. It
            exposes an
            invoke()
            method, which must be overridden by implementing classes to add behaviour
            to a call before passing the call down the chain by calling
            super.invoke()
            .
         
JBoss Cache ships with several interceptors, representing different configuration options, some of which are:
TxInterceptor
                  - looks for ongoing transactions and registers with transaction managers to participate in
                  synchronization events
               ReplicationInterceptor
                  - replicates state across a cluster using a JGroups channel
               CacheLoaderInterceptor
                  - loads data from a persistent store if the data requested is not available in memory
               
            The interceptor chain configured for your cache instance can be obtained and inspected by calling
            CacheSPI.getInterceptorChain()
            ,
            which returns an ordered
            List
            of interceptors.
         
               Custom interceptors to add specific aspects or features can be written by extending
               Interceptor
               and overriding
               invoke()
               . The custom interceptor will need to be added to the interceptor chain by using the
               CacheSPI.addInterceptor()
               method.
            
Adding custom interceptors via XML is not supported at this time.
            org.jboss.cache.marshall.MethodCall
            is a class that encapsulates a
            java.lang.reflection.Method
            and an
            Object[]
            representing the method's arguments. It is an extension of the
            org.jgroups.blocks.MethodCall
            class, that adds a mechanism for identifying known methods using magic numbers and method ids,
            which makes marshalling and unmarshalling more efficient and performant.
         
            This is central to the
            Interceptor
            architecture, and is the only parameter passed in to
            Interceptor.invoke()
            .
         
            InvocationContext
            holds intermediate state for the duration of a single invocation, and is set up and
            destroyed by the
            InvocationContextInterceptor
            which sits at the start of the chain.
         
            InvocationContext
            , as its name implies, holds contextual information associated with a single cache
            method invocation. Contextual information includes associated
            javax.transaction.Transaction
            or
            org.jboss.cache.transaction.GlobalTransaction
            ,
            method invocation origin (
            InvocationContext.isOriginLocal()
            ) as well as
            
               Option
               overrides
            
            .
         
            The
            InvocationContext
            can be obtained by calling
            Cache.getInvocationContext()
            .
         
         Some aspects and functionality is shared by more than a single interceptor. Some of these have been
         encapsulated
         into managers, for use by various interceptors, and are made available by the
         CacheSPI
         interface.
      
This class is responsible for calls made via the JGroups channel for all RPC calls to remote caches, and encapsulates the JGroups channel used.
This class manages buddy groups and invokes group organisation remote calls to organise a cluster of caches into smaller sub-groups.
         Early versions of JBoss Cache simply wrote cached data to the network by writing to an
         ObjectOutputStream
         during replication. Over various releases in the JBoss Cache 1.x.x series this approach was gradually
         deprecated
         in favour of a more mature marshalling framework. In the JBoss Cache 2.x.x series, this is the only officially
         supported and recommended mechanism for writing objects to datastreams.
      
            The
            Marshaller
            interface extends
            RpcDispatcher.Marshaller
            from JGroups.
            This interface has two main implementations - a delegating
            VersionAwareMarshaller
            and a
            concrete
            CacheMarshaller200
            .
         
            The marshaller can be obtained by calling
            CacheSPI.getMarshaller()
            , and defaults to the
            VersionAwareMarshaller
            .
            Users may also write their own marshallers by implementing the
            Marshaller
            interface and
            adding it to their configuration, by using the
            MarshallerClass
            configuration attribute.
         
            As the name suggests, this marshaller adds a version
            short
            to the start of any stream when
            writing, enabling similar
            VersionAwareMarshaller
            instances to read the version short and
            know which specific marshaller implementation to delegate the call to.
            For example,
            CacheMarshaller200
            , is the marshaller for JBoss Cache 2.0.x.
            JBoss Cache 2.1.x, say, may ship with
            CacheMarshaller210
            with an improved wire protocol.
            Using a
            VersionAwareMarshaller
            helps achieve wire protocol compatibility between minor
            releases but still affords us the flexibility to tweak and improve the wire protocol between minor or micro
            releases.
         
            This marshaller treats well-known objects that need marshalling - such as
            MethodCall
            ,
            Fqn
            ,
            DataVersion
            , and even some JDK objects such as
            String
            ,
            List
            ,
            Boolean
            and others as types that do not need complete class definitions.
            Instead, each of these well-known types are represented by a
            short
            , which is a lot more efficient.
         
In addition, reference counting is done to reduce duplication of writing certain objects multiple times, to help keep the streams small and efficient.
            Also, if
            UseRegionBasedMarshalling
            is enabled (disabled by default) the marshaller adds region
            information to the stream before writing any data. This region information is in the form of a
            String
            representation of an
            Fqn
            . When unmarshalling, the
            RegionManager
            can be used to
            find the relevant
            Region
            , and use a region-specific
            ClassLoader
            to unmarshall
            the stream. This is specifically useful when used to cluster state for application servers, where each
            deployment has
            it's own
            ClassLoader
            . See the section below on
            regions
            for more information.
         
         When used to cluster state of application servers, applications deployed in the application tend to put
         instances
         of objects specific to their application in the cache (or in an
         HttpSession
         object) which
         would require replication. It is common for application servers to assign separate
         ClassLoader
         instances to each application deployed, but have JBoss Cache libraries referenced by the application server's
         ClassLoader
         .
      
To enable us to successfully marshall and unmarshall objects from such class loaders, we use a concept called regions. A region is a portion of the cache which share a common class loader (a region also has other uses - see eviction policies ).
         A region is created by using the
         Cache.getRegion(Fqn fqn, boolean createIfNotExists)
         method,
         and returns an implementation of the
         Region
         interface. Once a region is obtained, a
         class loader for the region can be set or unset, and the region can be activated/deactivated. By default,
         regions
         are active unless the
         InactiveOnStartup
         configuration attribute is set to
         true
         .