JBoss Community Archive (Read Only)

Infinispan 6.0

Tree API Module

Introduction

Infinispan's tree API module offers clients the possibility of storing data using a tree-structure like API. This API is similar to the one provided by JBoss Cache, hence the tree module is perfect for those users wanting to migrate their applications from JBoss Cache to Infinispan, who want to limit changes their codebase as part of the migration. Besides, it's important to understand that Infinispan provides this tree API much more efficiently than JBoss Cache did, so if you're a user of the tree API in JBoss Cache, you should consider migrating to Infinispan.

What is Tree API about?

The aim of this API is to store information in a hierarchical way. The hierarchy is defined using paths represented as Fqn or fully qualified names, for example: /this/is/a/fqn/path or /another/path . In the hierarchy, there's an special path called root which represents the starting point of all paths and it's represented as: /

Each FQN path is represented as a node where users can store data using a key/value pair style API (i.e. a Map). For example, in /persons/john , you could store information belonging to John, for example: surname=Smith, birthdate=05/02/1980...etc.

Please remember that users should not use root as a place to store data. Instead, users should define their own paths and store data there. The following sections will delve into the practical aspects of this API.

Using Tree API

Dependencies

For your application to use the tree API, you need to import infinispan-tree.jar which can be located in the Infinispan binary distributions, or you can simply add a dependency to this module in your pom.xml:

<dependencies>
  ...
  <dependency>
    <groupId>org.infinispan</groupId>
    <artifactId>infinispan-tree</artifactId>
    <version>$put-infinispan-version-here</version>
  </dependency>
  ...
</dependencies>

Creating a Tree Cache

The first step to use the tree API is to actually create a tree cache. To do so, you need to create an Infinispan Cache as you'd normally do, and using the TreeCacheFactory, create an instance of TreeCache. A very important note to remember here is that the Cache instance passed to the factory must be configured with invocation batching. For example:

import org.infinispan.config.Configuration;
import org.infinispan.tree.TreeCacheFactory;
import org.infinispan.tree.TreeCache;
...
Configuration config = new Configuration();
config.setInvocationBatchingEnabled(true);
Cache cache = new DefaultCacheManager(config).getCache();
TreeCache treeCache = TreeCacheFactory.createTreeCache(cache);

Manipulating data in a Tree Cache

The Tree API effectively provides two ways to interact with the data:

  1. Via TreeCache convenience methods: These methods are located within the TreeCache interface and enable users to store, retrieve, move, remove...etc data with a single call that takes the Fqn, in String or Fqn format, and the data involved in the call. For example:

    treeCache.put("/persons/john", "surname", "Smith");

    Or:

    import org.infinispan.tree.Fqn;
    ...
    Fqn johnFqn = Fqn.fromString("persons/john");
    Calendar calendar = Calendar.getInstance();
    calendar.set(1980, 5, 2);
    treeCache.put(johnFqn, "birthdate", calendar.getTime()));

  2. Via Node API: It allows finer control over the individual nodes that form the FQN, allowing manipulation of nodes relative to a particular node. For example:

    import org.infinispan.tree.Node;
    ...
    TreeCache treeCache = ...
    Fqn johnFqn = Fqn.fromElements("persons", "john"); 
    Node<String, Object> john = treeCache.getRoot().addChild(johnFqn);
    john.put("surname", "Smith");

    Or:

    Node persons = treeCache.getRoot().addChild(Fqn.fromString("persons"));
    Node<String, Object> john = persons.addChild(Fqn.fromString("john"));
    john.put("surname", "Smith");

    Or even:

    Fqn personsFqn = Fqn.fromString("persons");
    Fqn johnFqn = Fqn.fromRelative(personsFqn, Fqn.fromString("john"));
    Node<String, Object> john = treeCache.getRoot().addChild(johnFqn);
    john.put("surname", "Smith");

    A node also provides the ability to access its parent or children. For example:

    Node<String, Object> john = ...
    Node persons = john.getParent();

    Or:

    Set<Node<String, Object>> personsChildren = persons.getChildren();

Common Operations

In the previous section, some of the most used operations, such as addition and retrieval, have been shown. However, there are other important operations that are worth mentioning, such as remove:

You can for example remove an entire node, i.e. /persons/john , using:

treeCache.removeNode("/persons/john");

Or remove a child node, i.e. persons that a child of root, via:

treeCache.getRoot().removeChild(Fqn.fromString("persons"));

You can also remove a particular key/value pair in a node:

Node john = treeCache.getRoot().getChild(Fqn.fromElements("persons", "john"));
john.remove("surname");

Or you can remove all data in a node with:

Node john = treeCache.getRoot().getChild(Fqn.fromElements("persons", "john"));
john.clearData();

Another important operation supported by Tree API is the ability to move nodes around in the tree. Imagine we have a node called "john" which is located under root node. The following example is going to show how to we can move "john" node to be under "persons" node:

Current tree structure:

   /persons
   /john

Moving trees from one FQN to another:

Node john = treeCache.getRoot().addChild(Fqn.fromString("john"));
Node persons = treeCache.getRoot().getChild(Fqn.fromString("persons"));
treeCache.move(john.getFqn(), persons.getFqn());

Final tree structure:

   /persons/john

Locking In Tree API

Understanding when and how locks are acquired when manipulating the tree structure is important in order to maximise the performance of any client application interacting against the tree, while at the same time maintaining consistency.

Locking on the tree API happens on a per node basis. So, if you're putting or updating a key/value under a particular node, a write lock is acquired for that node. In such case, no write locks are acquired for parent node of the node being modified, and no locks are acquired for children nodes.

If you're adding or removing a node, the parent is not locked for writing. In JBoss Cache, this behaviour was configurable with the default being that parent was not locked for insertion or removal.

Finally, when a node is moved, the node that's been moved and any of its children are locked, but also the target node and the new location of the moved node and its children. To understand this better, let's look at an example:

Imagine you have a hierarchy like this and we want to move c/ to be underneath b/:

        /
      --|--
     /     \
     a     c
     |     |
     b     e
     |
     d

The end result would be something like this:

        /
        |          
        a     
        |     
        b     
      --|--
     /     \
     d     c
           |
           e

To make this move, locks would have been acquired on:

  • /a/b - because it's the parent underneath which the data will be put

  • /c and /c/e - because they're the nodes that are being moved

  • /a/b/c and /a/b/c/e - because that's new target location for the nodes being moved

Listeners for tree cache events

The current Infinispan listeners have been designed with key/value store notifications in mind, and hence they do not map to tree cache events correctly. Tree cache specific listeners that map directly to tree cache events (i.e. adding a child...etc) are desirable but these are not yet available. If you're interested in this type of listeners, please follow this issue to find out about any progress in this area.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-11 09:38:32 UTC, last content change 2012-03-29 10:03:53 UTC.