Call us: +1-415-738-4000

Working With the Terracotta Toolkit

The Terracotta Toolkit provides access to a number of useful classes, or tools, such as distributed collections. To access the tools in the Toolkit, your application must also first initialize the Terracotta Toolkit.

Initializing the Toolkit

Initializing the Terracotta Toolkit always begins with starting a Terracotta client:

...
// These classes must be imported:
import org.terracotta.api.ClusteringToolkit;
import org.terracotta.api.TerracottaClient;
...
// Start the client.
TerracottaClient client = new TerracottaClient("localhost:9510"); 

// Make the Toolkit available to your application.
ClusteringToolkit toolkit = client.getToolkit(); 
...

Or more compactly:

...
import org.terracotta.api.ClusteringToolkit;
import org.terracotta.api.TerracottaClient;
...
ClusteringToolkit toolkit = new TerracottaClient("localhost:9510").getToolkit();
...

When a Terracotta client is started, it must load a Terracotta configuration. Programmatically, the TerracottaClient constructor takes as argument the source for the client's configuration. In the example above, the configuration source is a Terracotta server running on the local host, with DSO port set to 9510. In general, a filename, an URL, or a resolvable hostname or IP address and DSO port number can be used. The specified server instance must be running and accessible before the code that starts the client executes.

Using Toolkit Tools

The data structures and other tools provided by the Toolkit are automatically distributed (clustered) when your application is run in a Terracotta cluster. Since the Toolkit is obtained from an instantiated client, all Toolkit tools must be used clustered. Unclustered use is not supported in this version.

Toolkit Data Structures and Serialization

Only the following types can be put into Toolkit data structures:

  • Java primitives (int, char, byte, etc.)
  • Wrapper classes for Java primitives (Integer, Character, Byte, etc.)
  • BigInteger and BigDecimal (from java.math.)
  • String, Class, and StackTraceElement (from java.lang)
  • java.util.Currency
  • All Enum types
  • Arrays of any of the types listed above

To add other types to a Toolkit data structure, serialize it and then put the resulting byte array into the data structure.

Maps

Clustered collections are found in the package org.terracotta.collections. This package includes a clustered Map, BlockingQueue, Set, and List. You can access these clustered collections directly through the Terracotta Toolkit.

For example, the following gets a reference to a clustered map:

...
import org.terracotta.api.ClusteringToolkit;
import org.terracotta.api.TerracottaClient;
import org.terracotta.collections.ClusteredMap;
...
ClusteringToolkit toolkit = new TerracottaClient("localhost:9510").getToolkit();
...
ClusteredMap<int, Object> myClusteredMap = toolkit.getMap("myMap");

The returned map is a fully concurrent implementation of the ClusteredMap interface, which means that locking is provided.

TIP: Does a Collection Provide Locking?
If a class's name includes concurrent, it provides locking. For example, the List implementation, TerracottaList, does not provide locking.

Queues

To obtain a clustered BlockingQueue, use the following:

BlockingQueue<byte[]> queue = clusterToolkit.getBlockingQueue(String MY_QUEUE);

where the String MY_QUEUE holds the name of the queue. This BlockingQueue has unlimited capacity.

To obtain a clustered BlockingQueue with a limited capacity, use the following:

BlockingQueue<byte[]> queue = clusterToolkit.getBlockingQueue(MY_QUEUE, MAX_ITEMS);

where the int MAX_ITEMS is the maximum capacity of the queue.

Producers in the Terracotta cluster can add to the clustered queue with add(), while consumers can take from the queue with take(). The clustered queue's data is automatically shared and updated across the Terracotta cluster so that all nodes have the same view.

Cluster Information

The Terracotta Toolkit allows you to access cluster information for monitoring the nodes in the cluster, as well as obtaining information about those nodes.

For example, you can set up a cluster listener to receive events about the status of client nodes:

import org.terracotta.api.ClusteringToolkit;
import org.terracotta.api.TerracottaClient;
import org.terracotta.cluster;
...

// Start a client and access cluster events and meta data 
// such as topology for the cluster that the client belongs to:
ClusteringToolkit toolkit = new TerracottaClient("localhost:9510")
.getToolkit();
ClusterInfo clusterInfo = toolkit.getClusterInfo();

// Register a cluster listener and implement methods for events:
clusterInfo.addClusterListener(new ClusterListener()
  {
     // Implement methods for nodeJoined, nodeLeft, etc. here:
     public void nodeJoined(ClusterEvent event) {
       // Do something when event is received.
     }
     public void nodeLeft(ClusterEvent event) {
       // Do something when event is received.
     }
     public void operationsEnabled(ClusterEvent event) {
       // Do something when event is received.
     }
     public void operationsDisabled(ClusterEvent event) {
       // Do something when event is received.
     }
})

You can write your own listener classes that implements the event methods, and add or remove your own listeners:

clusterInfo.addClusterListener(new MyClusterListener());

// To remove a listener:
clusterInfo.removeClusterListener(myClusterListener);

Locks

Clustered locks allow you to perform safe operations on clustered data. The following types of locks are available:

  • READ – This is a read lock that blocks writes.
  • WRITE – This is a write lock that blocks reads and writes. To improve performance, this lock flushes changes to the Terracotta Server Array asynchronously.
  • SYNCHRONOUS-WRITE – A write lock that blocks until the Terracotta Server Array acknowledges commitment of the changes that the lock has flushed to it. Maximizes safety at the cost of performance.
  • CONCURRENT – A lock that makes no guarantees that any of the changes flushed to the Terracotta Server Array have been committed. This lock is high-risk and used only where data integrity is unimportant.

To obtain a clustered lock, use ClusteringToolkit.createLock(Object monitor, LockType type). For example, to obtain a write lock:

import org.terracotta.api.ClusteringToolkit;
import org.terracotta.api.TerracottaClient;
import org.terracotta.locking;
import org.terracotta.locking.strategy;
...

// Start a client.
ClusteringToolkit toolkit = new TerracottaClient("localhost:9510")
.getToolkit();

// Obtain a clustered lock. The monitor object must be clustered and cannot be null.
Lock myLock = toolkit.createLock(myMonitorObject, WRITE);
myLock.lock();
try {
    // some operation under the lock
} finally {
    myLock.unlock();
}

To obtain a clustered read-write lock:

TerracottaClient client = new TerracottaClient("myServer:9510");

// If the identified lock exists, it is returned instead of created.
Lock rwlock = client.getToolkit().getReadWriteLock("my-lock-identifier").writeLock();

rwlock.lock();
try {
    // some operation under the lock
} finally {
    rwlock.unlock();
}

If you are using Enterprise Ehcache, you can use explicit locking methods on specific keys. See 2.2.5 Explicit Locking for more information.

Clustered Barriers

Coordinating independent nodes is useful in many aspects of development, from running more accurate performance and capacity tests to more effective management of workers across a cluster. A clustered barrier is a simple and effective way of coordinating client nodes.

To get a clustered barrier:

import org.terracotta.api.ClusteringToolkit;
import org.terracotta.api.TerracottaClient;
import org.terracotta.coordination;
...

// Start a client.
ClusteringToolkit toolkit = new TerracottaClient("localhost:9510")
.getToolkit();

// Get a clustered barrier.
// Note that getBarrier() as implemented in Terracotta Toolkit returns a CyclicBarrier.
Barrier clusteredBarrier = toolkit.getBarrier(String barrierName, int numberOfParties);

Utilities

Utilities such as a clustered AtomicLong help track counts across a cluster. You can get (or create) a ClusteredAtomicLong using toolkit.getAtomicLong(String name).

Another utility, ClusteredTextBucket, shares string outputs from all nodes. Printed output from each local node is available on every other node via this bucket. You can get (or create) ClusteredTextBucket using toolkit.getTextBucket(String name).

Destroying Clustered Terracotta Toolkit Objects

You can use the unregister methods available in the interface org.terracotta.api.ClusteringToolkitExtension to destroy clustered Toolkit objects. The following example shows how to unregister a clustered Map:

TerracottaClient client = new TerracottaClient("localhost:9510");
ClusteringToolkit toolkit = client.getToolkit();

Map map = toolkit.getMap("myMap");

 // The instance must be cast to ClusteringToolkitExtension.
 ClusteringToolkitExtension toolkitExtension = (ClusteringToolkitExtension) toolkit; 

 // The Terracotta cluster no longer has knowledge of myMap.
 toolkitExtension.unregisterMap("myMap");

Do not attempt to continue using clustered Toolkit objects that have been unregistered. Doing so can lead to unpredictable results.