Skip navigation

POJO Quick Start

Article Rating?


Table of Contents


Overview

This section covers general information, including the Terracotta architecture.

Installation

Unix/Linux
Download the Unix/Linux install kit for Terracotta DSO and unzip and untar it. A typical location to use for a production deployment is the /usr/local directory. For testing purposes, your home directory is appropriate.

For information on the sample applications included with Terracotta DSO, view the samples.html file in the samples/pojo directory.

Microsoft Windows
Download the Windows executable (.exe) install file for Terracotta DSO and run it. The installer guides you through the process of installing the product.

If the same version of the product has been previously installed in the same directory, the installer will backup the existing installation and perform a new install. The installer will notify the user about the backup and display the location of the new backup directory.

The installation creates an uninstaller for removing the product. The uninstaller does not delete files modified or created after the installation. These files will need to be removed manually.

Running Servers and Clients

This section covers starting the Terracotta Server and the DSO client nodes.

Starting the Terracotta Server

The start-tc-server utility script runs the Terracotta Server. It takes a single, optional argument specifying a configuration file.

Unix/Linux:

${TC_HOME}/bin/start-tc-server.sh -f <tc-config-path>

Windows:

%TC_HOME%\bin\start-tc-server.bat -f <tc-config-path>

If <tc-config-path> is not specified, a configuration file named tc-config.xml located in the working directory will be used.

If using the start-tc-server script is not appropriate for your environment, the Terracotta Server can also be started directly as a Java application.

Unix/Linux:

${JAVA_HOME}/bin/java -classpath ${TC_HOME}/lib/tc.jar -Dtc.install-root=${TC_HOME} com.tc.server.TCServerMain -f <tc-config-path>

On Windows:

"%JAVA_HOME%\bin\java" -classpath "%TC_HOME%\lib\tc.jar" -Dtc.install-root="%TC_HOME%" com.tc.server.TCServerMain -f <tc-config-path>

Starting the DSO Client Nodes

The dso-java utility script runs a Terracotta DSO client. It takes a fully qualified startup class name, containing a static main method.

Unix/Linux:

${TC_HOME}/bin/dso-java.sh Dtc.config=<tc-config-path> <startup-classname>

Windows:

${TC_HOME}\bin\dso-java.bat -Dtc.config=<tc-config-path> <startup-classname>

If using the dso-java script is not appropriate for your environment, DSO client nodes can also be started directly as Java applications.

Unix/Linux:

${JAVA_HOME}/bin/java -Dtc.install-root=${TC_HOME} -Xbootclasspath/p:<dso-boot-jar-path> -Dtc.config=<tc-config-path> <startup-classname>

Windows:

"%JAVA_HOME%\bin\java" -Dtc.install-root=${TC_HOME} -Xbootclasspath/p:<dso-boot-jar-path> -Dtc.config=<tc-config-path> <startup-classname>

The <dso-boot-jar-path> parameter value specifies the location of the virtual machine-specific DSO boot JAR. Terracotta ships pre-built, platform-specific boot JARs for Java versions 1.4 and 1.5. For more information on creating custom DSO boot JARs, refer to the "Boot JAR" topic in the section titled "Basic DSO Concepts and Configuration."
For more information on configuration view [Configuration Guide and Reference]

Tutorials

Step through the Terracotta DSO tutorials to gain hands-on experience using Terracotta to cluster applications.

Hello World Tutorial

DSO is the transparent Distributed Shared Objects component of Terracotta. It is an API-free way to convert a multi-threaded application to a multi-JVM (clustered) application.

DSO Features

DSO include the following features:

Transparency:
DSO uses no API. There are no Terracotta classes to import. Just add a little bit of configuration and some start-up parameters - and you're off and running.

Memory Management:
Shared object trees using DSO can be arbitrarily large, and shared objects are transparently moved between the client VM, Terracotta server VM, and disk. This memory management capability is sometimes called "virtual heaps" since the capability allows the JVM to use a heap that is restricted in size only by the sum of available RAM and disk space on the Terracotta Server. For instance, heaps on 32-bit hardware can exceed 2 GB.

State Sharing:
With DSO, a "root" is defined as an object (often a collection, and very likely referencing other objects) that is a good candidate for sharing across JVMs in order to enhance reliability, performance, or scalability.

Objects reachable by user-defined roots are transparently shared between clustered JVMs on an "as needed" basis. Changes are captured as they occur, at a fine-grained level. When exiting Java synchronization, only those changes are shared with the Terracotta Server and any Terracotta Clients that currently need to know about the changes. Nodes not needing to receive those changes will not receive them. This method of state sharing is very efficient as it eliminates redundant network traffic and unnecessary client-side processing. In particular, Terracotta will not transmit redundant object data across the network as many mechanisms (like serialization or RMI) will.

Process Coordination:
Terracotta extends Java's thread coordination and memory model to the entire cluster. Synchronizing on a shared object can be turned into a variety of cluster-wide locks. Calling Object.wait() and Object.notify() on a shared object can also be extended to the entire cluster.

Drop-in Clustered WebLogic or Tomcat Session Replication:
Terracotta applies the tools described above to provide drop-in clustering for WebLogic or Tomcat sessions. Session objects are dynamically loaded into WebLogic or Tomcat instances on an "as needed" basis and only fine-grained changes are recorded - not complete object trees. Those changes are then forwarded to only the WebLogic or Tomcat instances that recently used the changed Session object.

The next section covers the basic tools needed to apply DSO to a simple application.

First Things First

Using DSO requires the developer to do three things:

  • Select one or more fields to be the root of graphs of shared objects
  • Protect these shared objects using standard Java synchronization, just as if ordinary threads in a single JVM were sharing them
  • Include the list of packages or classes of potential objects to be shared

All objects reachable from the roots are accessible from all JVMs that reference those roots. Shared objects are dynamically moved in and out of JVMs behind the scenes, as needed, without requiring development-time work. Furthermore, not all shared objects are moved into all JVMs they are moved only where they are needed.

Think about DSO as taking a multi-threaded application and extending it to be a multi-virtual machine or distributed application without code changes.

A review of the "Hello, World" style example illustrates this point:

HelloWorld.java
package tutorial;

import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

public class HelloWorld {
   private List hellos = new ArrayList();

   public void sayHello() {
      synchronized(hellos) {
         hellos.add("Hello, World " + new Date());
         for(Iterator it = hellos.iterator();it.hasNext();) {
            System.out.println(it.next());
         }
      }
   }

   public static void main(String[] args) {
      new HelloWorld().sayHello();
   }

}

For automatic locking based on Java synchronization, lock on shared objects. In this example the root hellos are locked.

All you need now is a little bit of configuration and you can test our application. Create a file called tc-config.xml with the following:

<?xml version="1.0" encoding="UTF-8"?>
<tc:tc-config xmlns:tc="http://www.terracotta.org/config">
  <application>
    <dso>
      <roots>
        <root>
          <field-name>tutorial.HelloWorld.hellos</field-name>
        </root>
      </roots>
      <locks>
        <autolock>
          <method-expression>* tutorial.HelloWorld*.*(..)</method-expression>
          <lock-level>write</lock-level>
        </autolock>
      </locks>
      <instrumented-classes>
        <include><class-expression>tutorial..*</class-expression></include>
      </instrumented-classes>
    </dso>
  </application>
</tc:tc-config>

This configuration demonstrates the three things discussed above:

  • It defines which variables represent roots. A root is defined by a fully-qualified variable name. A root can also have a name, but the default is to have the name be the same as the fully-qualified field name.
  • The locks were defined. The tag <method-expression> defines where to apply the lock and the <locklevel> tag defines the lock's level.

The syntax used to define locks is based on Join Point expressions in AspectWerkz

A write lock works just like Java synchronization but functions across an entire cluster. Here an autolock is used that relies on the Java synchronization within the methods referenced by the expression and the identity of the shared object locked on.

If the locked object is not shared, then the lock is not distributed. Share those objects that require distributed locks.

The <instrumented-classes> section defines which set of classes can have their instances shared. Most Java classes do not need to be added to be included.

Perform this procedure to run the application without DSO by simply using standard Java:

  1. Compile HelloWorld to create the class file:

    mkdir classes
    javac -d classes HelloWorld.java

  2. Now try running it a few times.

    java -cp classes tutorial.HelloWorld

    Each run should look something like this:

    my-computer > Hello, World Sat Jun 09 14:17:31 PDT 2005

    It may look a little different depending on the OS/Shell, but the output should always be just the one line.

  3. Drop in DSO by following these steps.
    Start the Terracotta Server.
    Unix:

    $TC_HOME/samples/start-demo-server.sh

    Windows:

    %TC_HOME%\samples\start-demo-server.bat

    Substitute the location of the Terracotta installation for <TC_HOME>. The output should be similar to:

    > 2006-04-04 09:27:46,782 INFO - Terracotta Server has started up
    > successfully, and is now ready for work.
    
  4. Run the application using the dso-java script:
    Unix:

    $TC_HOME/bin/dso-java.sh -cp classes -Dtc.config=tc-config.xml tutorial.HelloWorld

    The output looks something like:

    2006-02-07 15:09:51,525 INFO - Terracotta, version 2.2 as of 20060207-145108.
    2006-02-07 15:09:53,037 INFO - Log file is now:
    '/home/terra/client-logs-myhost/terracotta-client.log'.
    2006-02-07 15:09:53,059 INFO - Configuration loaded from server at 'localhost:9510'.
    Hello, World Sat Jun 09 14:17:31 PDT 2005
    

    Windows:

    %TC_HOME%\bin\dso-java.bat -cp classes -Dtc.config=tc-config.xml tutorial.HelloWorld

  5. Try running it several times.
    After you run it once there should be two lines of "Hello, World" output. After several tries a new line should added after each run.

    New lines are added after each time because the array list is being persisted to the Terracotta Server, so the application runs each time with the same root.

Congratulations! You have just written and run your first DSO clustered application.

Adding More Complexity

Now let's have a little more fun.

Add some more objects and sub-objects to the root and see those get shared, too:

HelloWorld.java
package tutorial;

import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

public class HelloWorld {
   private List hellos = new ArrayList();

   public void sayHello(){
      synchronized(hellos){
         ArrayList al = new ArrayList();
         al.add(new Date());
         al.add(new ArrayList());
         al.add("Nested Hello World");
         hellos.add(al);

         hellos.add("Hello, World" + new Date());
         for(Iterator it = hellos.iterator();it.hasNext();) {
            System.out.println(it.next());
         }
      }
   }

   public static void main(String[] args) {
      new HelloWorld().sayHello();
   }

}

After running the above a few times, the output should look like the following:

2006-02-07 15:09:51,525 INFO - Terracotta, version
2.0.0 as of 20060207-145108.
2006-02-07 15:09:53,037 INFO - Log file is now:
'/home/terra/client-logs-myhost/terracotta-client.log'.
2006-02-07 15:09:53,059 INFO - Configuration successfully
found and loaded from server at 'localhost:9510'.
 [Mon Jul 25 11:59:33 PDT 2005, [], Nested Hello World]
Hello, World:Mon Jul 25 11:59:33 PDT 2005
[Mon Jul 25 11:59:38 PDT 2005, [], Nested Hello World]
Hello, World:Mon Jul 25 11:59:38 PDT 2005
[Mon Jul 25 11:59:43 PDT 2005, [], Nested Hello World]
Hello, World:Mon Jul 25 11:59:43 PDT 2005

How DSO Works with Applications

The most important tools that exist for debugging and understanding your application are logging and the Terracotta Console. When you started your application the location and name of your log file was output to the screen. Open that log file and take a look.

At the top of each session is information about the Java environment, followed by the full con-figuration file. Lastly, is the information DSO thinks is defined in your configuration. This is all helpful but there is so much more that can be seen. To get a better picture of how classes are instrumented at load time, turn on instrumentation logging in the tc-config.xml file.

Add the following section at the top level of the configuration file, just inside the <tc-config> element:

<clients>
  <dso>
    <debugging>
      <instrumentation-logging>
        <class>true</class>
        <hierarchy>true</hierarchy>
        <locks>true</locks>
        <transient-root>true</transient-root>
        <roots>true</roots>
        <distributed-methods>true</distributed-methods>
      </instrumentation-logging>
    </debugging>
  </dso>
</clients>

To see what is happening at runtime turn on runtime logging in your tc-config.xml file, inside the <debugging> element you created above:

<runtime-logging>
  <lock-debug>true</lock-debug>  
  <field-change-debug>true</field-change-debug>  
  <wait-notify-debug>true</wait-notify-debug>
  <distributed-method-debug>true</distributed-method-debug>
  <new-object-debug>true</new-object-debug>
</runtime-logging>

As you can see, there are a large number of debugging options available.

All output is sent to your terracotta-client.log file. These options can have a large negative impact on performance and are recommended for development use only.

To get a clear picture of what is happening at runtime without a performance hit, use the tc-admin tool.

Unix:

$TC_HOME/bin/admin.sh

Windows:

%TC_HOME%\bin\admin.bat

After it starts, connect to your server by pushing the connect button on the right hand side. When the server is connected a screen similar to the following displays:

This tool displays the shared objects, roots, locks, status messages, and various data flow statistics. You can also detect distributed deadlocks. This tool is useful for helping a developer under-stand the performance of the application.

Conclusion

In this DSO tutorial you have learned how to define a root, a lock, and how to gain visibility into the operations of an application running with DSO. In subsequent tutorials you will dig deeper into more advanced concepts such as distributed wait and notify, distributed method calls, and memory management.

Slider Tutorial

The goal of this tutorial is to create a simple Distributed Shared Objects (DSO) application with clear step-by-step instructions exploring the main concepts of Terracotta DSO along the way.

During this tutorial you perform the following tasks:

  • Create a simple Swing GUI application that displays a window containing a slider.
  • Convert the project to use DSO.
  • Configure the application so the slider value is transparently shared among multiple instances of the application.
  • Create a second, similar application that displays the shared slider value in a text label

When running multiple instances of the application, dragging the slider in one window automatically updates the values of the sliders in each of the other windows.

Terracotta recommends that the JDK be installed with source code available to Eclipse.

Building a DSO Application

The goal of this tutorial is to create a simple DSO Application with clear step-by-step instructions, exploring the main concepts of Terracotta DSO along the way. You start out by creating a standard Eclipse application, then convert the application over to DSO.

This tutorial is based on the Terracotta DSO Eclipse Plug-in. Consult http://www.terracotta.org/eclipse for instructions on installing the Eclipse plugin.

The Problem

The application you will create is a simple Swing GUI application that displays a window containing a slider. You will then convert the project to use DSO and configure the application such that the slider value will be transparently shared among multiple instances of the application. When running multiple instances of the application, dragging the slider in one window will automatically update the values of the sliders in each of the other windows. Finally, you will create a second, similar application that will display the shared slider value in a text label.

Getting Started

To get started with your DSO application, perform the following:

  1. Create a standard Java project by clicking File → New → Project... → Java project. The New Java Project screen will display.

New Java Project

  1. Name the project DSO Tutorial and accept the default location for the project within your workspace.
  2. Create a new class by clicking File → New → Class.

New Java Class

  1. Specify the Package name as tutorial and the class Name as Slider. The new class appears in the Java Code Editor.
  2. Enter the following code for the Slider.java module:
package tutorial;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.JFrame;
import javax.swing.JSlider;

public class Slider {
  private DefaultBoundedRangeModel rangeModel;

  public static void main(String[] args) {
    new Slider();
   }

  public Slider() {
    JFrame frame = new JFrame("Slider Test");
    rangeModel = new DefaultBoundedRangeModel();
    frame.getContentPane().add(new JSlider(rangeModel));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
   }
}

Java Code Editor

The Slider Module

The Slider constructor creates a window (JFrame), sets the window such that closing it terminates the process, adds a slider to the window, packs the window to its preferred size, and makes the window visible. The static main() method is required to run the class as an application.

You might notice that we explicitly declared a DefaultBoundedRangeModel instance variable. Normally you would not need to create a model for a slider but for the purposes of this tutorial we have because it is that model that we plan on sharing with DSO.

Running the Slider Application

Steps:

  1. If you do not have the Project → Build Automatically option enabled, compile the Slider class click Project → Build Project.
  2. Right-click the Slider module and choose Run As → Java Application. The window containing the slider should appear in the upper left corner of your display.

Slider Module

If you run another instance of the Slider class using the same procedure you will have two separately executing copies of the application. Our goal in the next section is to make it such that dragging one of the two sliders causes both to display the same value. You will accomplish this goal without changing the existing code at all but rather by converting the Tutorial project into a DSO project.

Adding the Terracotta Nature

To convert the standard Java project to a DSO project, perform the following steps:

  1. Right-click the DSO Tutorial project node in the Package Explorer view and select Terracotta → Add Terracotta nature.
  2. Specify the location within the project where you want to store the configuration file tc-config.xml. By default the file is created in your root project folder.

Terracotta Project Setup

  1. Click Finish to complete the conversion of your project.

A folder named terracotta is created in the root of your project, which contains files (such as log files) created by the DSO runtime. The tc-config.xml file specifies a default server configuration named localhost, which writes its log files in the terracotta/server-logs folder. Client logs are stored in terracotta/client-logs.

Running the Server

At this point you can run the Terracotta Server in one of several ways. You'll notice a new drop-down menu control has been added to the workspace toolbar. That drop-down menu contains all the server configurations that have been added to tc-config.xml. There should be a single entry labeled localhost which you should select.

Toolbar Drop-down Menu

The Terracotta server will be run using the information specified for localhost in the server section of tc-config.xml. You can stop the server by invoking the Stop Terracotta Server project context menu. If you attempt to start a new server when one is already running, the running server will first be stopped and then a new server will start.

Sharing the Slider Module

To accomplish the goal of sharing the slider model among multiple running instances of your application, you need to perform the following configuration steps:

  • Declare the Slider instance variable rangeModel be a DSO Shared Root
  • Declare Named Locks on the mutator methods of DefaultBoundedRangeModel
  • Declare any Distributed Methods defined by DefaultBoundedRangeModel
  • Declare any Transient Fields belonging to DefaultBoundedRangeModel

After you complete the configuration, you will be able to run your newly configured DSO application.

Declaring the Shared Root

To declare the shared slider model, select the rangeModel instance variable in the source editor and right-click to select Terracotta → Field rangeModel → Shared root.

Optionally, you can also specify the shared root by navigating to the field in the Package Explorer view, using the context menu on the rangeModel field.

Terracotta Field Shared Root Context Menu

The DSO configuration editor, which you access by using the Open with...Terracotta Config context-menu on the DSO configuration file node, provides graphical editors for carrying out all DSO configuration activities. For example, the Roots panel of the DSO config tab lets you see and manipulate the complete set of roots specified in the configuration. You click the Add button and then select rangeModel from the Field Navigator selection box.

Roots Tab with Field Navigator Selection Box

As a side-effect of declaring rangeModel as a shared root, the declaring type Slider and the rangeModel type, javax.swing.DefaultBoundedRangeModel, are declared as being instrumented classes. Additionally, because DefaultBoundedRangeModel is a system type and not a class that is pre-instrumented by Terracotta, it is also declared as an additional bootjar class. When it comes time to run the Slider application the DSO plug-in ensures that a custom bootjar is created based on the configuration file you are creating. You can manually create a custom bootjar from your configuration at any time by clicking Terracotta → Build boot JAR....

Declare Locks for the Root Mutators

Since DefaultBoundedRangeModel was not written for multi-threaded operation, You will need to declare DSO Named Locks on any methods that change any part of the model. These methods are known as the model mutators. In this case you need to know about how DefaultBoundedRangeModel works. By inspecting the code it would seem that setValue() is the method that actually sets the slider value:

public void setValue(int n) {
   int newValue = Math.max(n, min);
   if (newValue + extent > max) {
      newValue = max - extent;
   }
   setRangeProperties(newValue, extent, min, max, isAdjusting);
}

Notice that setValue() delegates to setRangeProperties() to set the slider values. You need to create a Named Lock on setRangeProperties().

Follow these steps to declare the named locks:

  1. Select the method name and press F3 to reveal the code.
  2. Use the Terracotta → Method → Name Locked context menu to add the lock to the DSO configuration.

Optionally, you can use the Instrumented classes panel of the DSO Config tab of the Configuration Editor to add the new Name Lock and select the method using the Method Navigator. By default, the new lock is a WRITE lock.

Terracotta Context Menu

Declaring Distributed Methods

All Swing models implement a listener interface to inform other objects of changes to the model state. Objects add themselves as listeners of the model and respond to callbacks issued by the model upon change. In this case the range model declares a list of ChangeListeners that it notifies of changes by its fireStateChanged() method. You must make this method distributed, meaning that it invokes on each of the Slider models in each DSO application.

Follow these steps to declare the distributed methods:

  1. Select the name of the fireStateChanged() method.
  2. Use the Terracotta → Methods → Distributed method context menu to add this distributed method to your DSO configuration Optionally, you can use the DSO Configuration Editor to add the method.

Declaring Transient Fields

The list of listeners that the range model notifies upon change is held in an instance variable named listenerList:

/* The listeners waiting for model changes. */
protected EventListenerList listenerList = new EventListenerList();

Add this variable as a Transient Field in the configuration so that DSO knows not to try to share this field across applications. This list contains references to Swing views that are display-specific and should not be shared by DSO. If this instance variable had been declared to be a Java transient, you could have simply enabled the honor-transient attribute of the DefaultBoundedRangeModel's DSO instrumentation specification. As it turns out, you still need to enable its honor-transient attribute because the model lazily maintains a ChangeEvent that it uses to pass to the listeners held in listenerList, and that ChangeEvent is declared as a Java transient. Lazily means that the model always checks to see if the event is null before using it to announce a change, and if null re-initializes. It functions like this to support Java object serialization.

Steps:

  1. Select its name in the declaration.
  2. Select Terracotta → Fields → Transient from the context menu
  3. Add this field as a transient

Now that you have ensured that DSO will not attempt to share either the listenerList or changeEvent, you need a way to ensure the listenerList is available to shared instances of the model when it is loaded into a DSO client from a DSO server.

It is important to understand that the first time the Slider application is run in the context of DSO, the model is created through the normal invocation of its default constructor: rangeModel = new DefaultBoundedRangeModel();

This code results in the DSO root you previously declared, rangeModel, being transmitted to the DSO server. All subsequent changes made to the state of the model are also sent to the server for possible re-transmission to any other connected clients. When the second Slider application is executed, DSO queries the server to see if there is a shared instance already defined, finds that there is, and returns that instance to be assigned to the root field. To clarify, even in the first application DSO looked for a pre-existing instance of the root in the server - it just did not find one - so the normal construction took place.

The shared object life cycle is important in understanding why and how you can make sure a shared object that contains transient state can see that state as if it were not being transparently managed by DSO.

Recreating Transient State

There are three different methods to specify to DSO how to cause the model's listenerList to be re-initialized when the model is loaded from the DSO server:

  • Lazy Initialization
  • Specifying an Initialization Method
  • Providing BeanShell Script

Lazy Initialization

Lazy Initialization is the method described in the "Declaring Transient Fields" topic, which is used by the DefaultBoundedRangeModel to ensure that its changeEvent is always non-null prior to its use in change notification:

protected void fireStateChanged() {
   Object[] listeners = listenerList.getListenerList();
   for (int i = listeners.length - 2; i >= 0; i -=2 ) {
      if (listeners[i] == ChangeListener.class) {
         if (changeEvent == null) {
            changeEvent = new ChangeEvent(this);
         }
      ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
      }
   }
}

The model simply checks for a null changeEvent and recreates it if needed.

Specifying an Initialization Method

Specifying an Initialization Method involves adding a method to your DSO-instrumented class whose purpose is to re-initialize any transient data,and specifying that method by name in the class's instrumentation specification using the on-load call element:

<include>
  <class-expression>com.my.Class</class-expression>
  <on-load>
    <method>initTransients</method>
  </on-load>
</include>

The specified method must have public access, return void and have no arguments.

In order to use this technique for the Slider application, you would need to subclass, or extend, DefaultBoundedRangeModel to add the new initialization method.

Providing BeanShell Script

Providing BeanShell script entails using the on-load execute element to write BeanShell script that initializes a class's transient data. In the case of the DefaultBoundedRangeModel class, you can use the Terracotta Config editor to specify BeanShell script to initialize the listenerList.

BeanShell Script for initialization:

<include>
  <class-expression>javax.swing.DefaultBoundedRangeModel</class-expression>
  <on-load>
    <execute>self.listenerList = new EventListenerList();</execute>
  </on-load>
</include>

This is a powerful technique that does not require you to subclass the model.

You can accomplish all of these tasks by using the Instrumented classes panel of the DSO Configuration Editor.

Validating the Configuration

Until now you used context menus to perform the configuration tasks. Now let's take a more detailed look at the complete configuration picture.

  1. To view the DSO Configuration Editor, use the Open with → Terracotta Config context menu on tc-config.xml.

Terracotta Config Context Menu

The Configuration Editor displays, showing the raw XML as well as various panels displaying the information in a structured way.

  1. Click the DSO config tab, which contains more panels broken down bycontent area.

Terracotta Config Screen

  1. Click the Roots panel to validate that the shared root tutorial.Slider.rangeModel is listed.
  2. Click the Locks panel and check that there is a Named write-lock on javax.swing.DefaultBoundedRangeModel.setRangeProperties().
  3. Click the Transient Fields panel and check that javax.swing.DefaultBoundedRangeModel.listenerList is listed.
  4. Click the Instrumented Classes panel to be sure both tutorial.Slider and javax.swing.DefaultBoundedRangeModel are listed, and that DefaultBoundedRangeModel has Honor transients enabled and the proper BeanShell script to be executed On load.
  5. Click the Distributed Methods panel and check that javax.swing.DefaultBoundedRangeModel.fireStateChanged() is listed.
  6. Click the Boot Classes panel and check that javax.swing.DefaultBoundedRangeModel is listed.

Running the Slider Application

Now that you have confirmed that the configuration settings are in place, run the DSO application. If you stopped the server that you ran at the beginning of this tutorial, be sure to restart it now. To execute the Slider class, perform these steps:

  1. Use the Run As → Terracotta DSO Application context menu in the Java source editor for Slider.java. Output displays in the Console View indicating that the DSO runtime environment is activated and the Slider Test window appears in the upper left corner of your display.

Java Source Editor

  1. Repeat this procedure to run a second instance of the Slider DSO application.

The second Slider Test window appears directly overtop of the first window.

Slider Test Windows

  1. Move the second Slider Test window so that both windows can be viewed at once.
  2. Drag one of the sliders and observer how the second slider updates automatically to the same value.

Sharing a Root Across Different Applications

You created a single DSO application that demonstrated that you can transparently share a field among multiple running copies of that application. The next step is to create a different application class that also shares the same range model but displays it in a different way.

  1. From the File menu, select New → Class to create a class named Label in package tutorial.
    Copy the following code into the new Label class:
package tutorial;

import javax.swing.DefaultBoundedRangeModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Label {
   private DefaultBoundedRangeModel rangeModel;
   private JLabel label;

   public static void main(String[] args) {
      new Label();
   }

   public Label() {
      JFrame frame = new JFrame("Label Test");
      rangeModel = new DefaultBoundedRangeModel();
      rangeModel.addChangeListener(new ChangeListener() {
         public void stateChanged(ChangeEvent e) {
            label.setText(rangeModel.getValue()+"");
         }
      });
      label = new JLabel(rangeModel.getValue()+"");
      frame.getContentPane().add(label);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setVisible(true);
   }
}

This class is very similar to the original Slider class except that it uses a label to display the slider value. To configure this class as a DSO application, follow the remaining steps:

  1. Make the rangeModel a shared root and check that the Label class is declared as instrumented.
  2. Give this root the same name as the original root declared for the Slider application.

If not set, a root's name is the same as its field-name. That is, tutorial.Slider.rangeModel is the field-name and, if not otherwise specified, it is also the root-name. You need to give the Slider.rangeModel root a name of SliderRoot and also assign that name to the Label.rangeModel root. By giving these two roots the same name, they are treated as the same shared object by DSO.

  1. Restart the server applications to pick up the new changes.

Running the Label Application

Steps:

  1. Run the Label DSO application in the same way you previously ran the Slider DSO application.
  2. Change one of the sliders to view the label update to the slider's new value.

Label Test Window

Conclusions

In this tutorial you accomplished the following:

  • Created a simple Swing application, which you then converted to a DSOapplication.
  • Configured the application to transparently share a simple data model among multiple instances of the same application.
  • Shared the data model - a system class, loaded from the Java boot classpath, which required some special treatment to integrate with the DSO runtime - with a completely different application by simply declaring the same shared object name.

Hopefully this introduction to Terracotta DSO made clear the potential power provided by a non-API, declarative approach to transparent data sharing.

Basic DSO Concepts and Configuration

This section covers fundamentals of clustering and strategies and operations.

Fundamentals of Clustering

This section covers the fundamentals of clustering, including object sharing, locks, distributed methods, transient fields, instrumentation, and the boot JAR.

Object Sharing

DSO provides the services required to build reliable and efficient clustered applications. At the highest level, DSO allows your application to transparently share objects across the virtual machine boundary. Once an object has been shared, it can be accessed by any virtual machine participating in your application cluster. To run your application with DSO, you must determine which of your objects should be managed by DSO, and arrange for them to be shared.

Determining Objects to Share

The exact objects that should be shared will be a characteristic of your particular application. In general, any object state that needs to be accessed, mutated, or destroyed by more than one virtual machine is a candidate for sharing. You will want to manage objects that represent pure state information and avoid replicating objects that refer to resources that are specific to any virtual machine (files, sockets, database connections, etc.). For example, business objects such as customer records might make good DSO shared objects.

Consider a web application that manages a product catalog. More than likely, the product catalog will modeled as a tree of java objects (with class names like Product, Category, Promotion, etc.). When the application is confined to a single virtual machine, it is virtually effortless to maintain consistency since you only have one copy of the catalog objects. To change the price or the inventory level of a product, you simply update the relevant fields in your Product object. If this web application needs to run on multiple virtual machines (for scalability and/or fault tolerance reasons), you suddenly have the problem of maintaining the consistency of product catalog within the cluster. With the product catalog objects shared in DSO, you can continue to program in a simple and natural manner, your objects will have consistent state in every virtual machine in your cluster.

Unclusterable Objects

Certain types of objects should not or cannot be part of a shared object graph. These types of objects are intimately bound to a particular host and generally cannot be moved across the network. For example, an instance of java.lang.Thread is tightly bound to the CPU on which it is meant to execute. Other types of objects that are not candidates for transparent distribution are any AWT (Abstract Window Toolkit) or Swing component, as they are directly associated with a graphical element implemented as a native component.

Shared Roots

A shared root is a class data member that is transparently mirrored across all like-configured DSO applications running against the same DSO server. The term root refers to the fact that the field may represent the top of an arbitrarily large object graph. For instance, a java.util.ArrayList is a collection of java.lang.Objects, each of which may also be composed of further sub-objects. A root can also be a simple primitive such as an int or boolean.

Shared Root Initialization (and re-initialization)

DSO Shared roots are only ever truly initialized once within the cluster. A common use case is to use a java collection (such as a HashMap) as a shared root. The application code must instantiate the instance of HashMap that is then assigned to root. The very first non-null reference that is assigned to any particular root is value that is retained forever. Attempts to assign new references (including the value null) to the root are silently ignored. The main reason for this semantic is that your code does not need to change to work in the context of a cluster. Each node can start and attempt to initialize the root. If the root assignment in each node was obeyed, they would eradicate the shared data structure. Your application is free to mutate the shared HashMap by calling clear(), put(), etc., but the top level reference can itself be replaced.

The above semantics only applies to roots of reference types. Roots that are of primitive types (int, boolean, primitive wrappers like Long, Integer, etc.) can be freely initialized and mutated.

Distributed Locks

A distributed lock is a form of a distributed semaphore, protecting areas of code that modify members of a shared root graph from simultaneous access and providing the hook DSO uses to synchronize data values across to the server for subsequent broadcast to other connected clients. In the distributed programming paradigm, a lock is analogous to a distributed synchronized method.

There are two categories of locks, named and automatic.

Named Locks

A named locks is meant to be used with code not specifically written for multi-thread safety. A method can be associated with a particular name, meaning that all similarly named methods use the same lock. In other words, if two methods share the same lock name and one method invokes the other, only a single actual lock will be taken out for the duration of the outer invocation.

Automatic Locks

An auto lock is meant for the case were the code is written with multiple threads in mind, using the synchronized keyword to demarcate protected areas. A synchronized block or method denotes a section of control flow that will be serialized with respect to thread access. Only a single thread can enter the block at a time.

Distributed Methods

Any method of an object contained in a shared object graph can be distributed, meaning that an invocation of that method in one virtual-machine will trigger the same method invocation on all the mirrored instances in other virtual-machines. This is a useful mechanism for implementing a distributed listener model. The Shared Graphics Editor sample application makes use of a distributed method to inform each view that the set of drawing objects has changed, notifying the view to update itself. It is important to note that distributed methods work in the context of a shared instance that defines that method. It is not sufficient that the instance's class be instrumented, it must also be contained in a shared object graph as defined by the set of shared roots declared by your DSO application configuration.

Transient Fields

Certain types of data members of an instrumented class should never be part of a shared object graph. For instance, a thread or a user interface object is intimately tied to the hosting virtual machine. Yet, members such as these may be contained in a class that you would like to be part of a shared object graph. To indicate to DSO that these data members be excluded from distributed mirroring, list them as transient fields in the configuration file. The keyword transient in Java indicates a field is not part of the object's persistent state and the field should be ignored when the object is serialized. DSO can be configured to automatically honor the keyword transient so that such fields are also treated as transient in DSO and not shared across JVMs.

Volatile Fields

Terracotta does not honor the volatile keyword. To achieve correct functionality you will have to use synchronized getters and setters.

Instrumented Classes

Any class that contains a shared root, distributed lock, or distributed method needs to be adapted in such a way that DSO can intervene to accomplish data distribution to ensure the integrity of the data. Class adaptation involves creating a new instrumented version of a class that adds additional information and code in support of DSO features.

The set of classes that are to be instrumented are specified through a pattern language that is based on the AspectWorkz Aspect-Oriented Programming (AOP) framework. A specific name can be used as well as shorthand wild-cards. The table below describes valid instrumentation include patterns:

Valid Instrumentation Include Patterns

Pattern Example Meaning
com.project.Main Specifies only class Main in com.project
com.project.* Specifies all classes in com.project
com.project.*Main Specifies all classes in com.project ending with Main
com..* Specifies all classes under com

Specifying a shared root or distributed lock that is not contained in an instrumented class results in a runtime exception when accessed. To exclude from transparent distribution particular data members of an instrumented class, list those members as Transient Fields. Instrumented system classes, such as java.lang.String, must reside in the DSO boot JAR.

Boot JAR

Java system classes are loaded from the special java.boot.class.path system property. System classes are those classes originating from the core system libraries defining the java package hierarchy, as well as others. These bootstrap classes are treated specially by the Java virtual machine in that they are given precedence over, and a higher security clearance than, classes loaded via the standard java.class.path system property. The -Xbootclasspath argument to the java command lets you add bootstrap classes and resources.

Java system classes that have been pre-instrumented for use with DSO must be made available to the Java virtual-machine as bootstrap classes, such that they can replace their original forms. Terracotta provides pre-instrumented versions of many of the most useful system classes, such as the collection classes in the java.util package. When you run a DSO application, it is required that the DSO boot JAR be prepended to the bootstrap class path.

Circumstances may arise where a shared object graph you have declared includes a system class that is not one of those pre-instrumented by Terracotta. In that case, you need to declare that class in your DSO configuration and ensure that a new, custom boot JAR is created, containing an instrumented version of that class. Further, you need to be sure to use that custom boot JAR when running your DSO application.

Strategies and Operations

This section covers strategies and operations, including clustering for web containers, failover, shutdown, memory management, and log files.

Clustering for Web Containers

In addition to generic object sharing, Terracotta DSO can be used within a web application server environment for HTTP session replication and to serve as alternative implementation for session management for your web applications. Terracotta DSO session management automatically and dynamically distributes the data for each user's session to all your application servers, enabling system scale-out without sacrificing performance or fault tolerance.

Startup Modifications

The process for enabling DSO within a web application server environment is the same as for generic DSO use. If you are using the stock startup scripts provided by your application server, the recommended approach is to construct a new startup script that uses environment variables to pass the Terracotta startup parameters (as opposed to directly modifying the vendor supplied scripts).

Apache Tomcat:
The variable JAVA_OPTS can be used to pass the required DSO startup parameters to the standard catalina.sh or catalina.bat script.

BEA WebLogic:
The variable JAVA_OPTIONS can be used to pass the required DSO startup parameters to the standard startWebLogic.sh or startWebLogic.cmd script. The same applies to the startManagedWebLogic.sh and startManagedWebLogic.cmd scripts. In both cases, the parameters described in "Starting the DSO Client Nodes" section of the "Running Servers and Clients" chapter are what you want to pass the application server startup script.

Configuration

In general, the configuration of Terracotta DSO for use within a web container is not different from generic DSO use. Objects to be shared still need to be included for instrumentation, roots need to be defined, lock expression added as appropriate, etc.

To enable Terracotta DSO sessions for your web application, there are two pieces of required configuration:

  1. Include classes that will join the object graph of your sessions.
  2. Include your application in the <web-applications> section of the Terracotta configuration file. The proper value for the <web-application> element is the context name of your application.
Determine if Application Server is DSO Enabled

The first output to the console (standard output or standard error) should include a line similar to:

2006-09-11 13:18:40,070 INFO - Terracotta, version 2.1.0 as of 20060911-110917.

If this line is not present in the console output of your application server, more than likely the value for DSO boot jar is incorrect. Check the -Xbootclasspath parameter passed and verify that the jar file exists at the path specified. It is possible that your environment is redirecting console output to a file, in which you will need to look there as opposed to on the screen.

You can also look in the Terracotta Administrator Console to see if any clients are connected. The appearance and disappearance of a connected client when you start/stop your application server is indication that DSO is enabled or not.

Determine if DSO Sessions are Enabled

Base DSO functionality needs to be enabled before DSO sessions will work.

Once base DSO functionality is enabled, you can verify DSO sessions in the Terracotta Administrator Console. In the Console, traverse to the DSO roots section. For each web application that uses DSO sessions, there will be a root present. For example if a web application named "Cart" was configured to use DSO sessions and deployed within a Tomcat server, there would be a DSO root named tc:tomcat_session_Cart.

If this root is not present, make sure that your web application is properly specified in the Terracotta configuration file. For the case of the default context under tomcat, specify the a value of ROOT for the <web-application>.

Server Failover

Failover setup for DSO servers enables a standby DSO server to take over clustering operations in the event the active DSO server fails. Each DSO server needs to be in persistence mode so changes to shared data are saved to disk. The DSO servers are started prior to starting the client applications, and the first DSO server to begin running handles all clustering operations. The other DSO servers are in standby ready to take over as the new active DSO server should the current active DSO server fail.

The persistence mode needed for failover requires an NFS shared file system with normal file locking semantics enabled. All DSO server hosts must mount this file system. Add multiple <server> elements to the Terracotta configuration file where each element defines a single DSO server. Specify the <data> element in each server's configuration so that each DSO server points to the same shared directory on the NFS server.

Use Terracotta Administrator Console to see which DSO server is active and which ones are in standby. To cleanly shutdown a DSO server in persistence mode on Unix/Linux, use <CTRL-C> to send the KILL signal, and on Microsoft Windows use the End Process feature in Task Manager.

If there is only one DSO server and it fails, the connected client applications will block and attempt to reconnect when the DSO server is restarted.

Shutting Down the Server

Use the stop-tc-server.sh or stop-tc-server.bat script in the dso/bin directory to terminate the Terracotta Server unless the server is running in persistent mode. If the Terracotta Server is running in persistent mode, terminate the process with prejudice. On Unix/Linux use kill -9 or <CTRL-C>, and on Windows use the End Process in Task Manager or <CTRL-C>.

Shared Object Memory Management

The shared object memory management feature of DSO is like the virtual memory subsystem of an operating system. It maintains a cache of the most-used objects in memory and flushes unused objects out of the cache as necessary. If an object is not available in memory when accessed, it will automatically fault that object into memory.

In the client, objects are flushed and faulted to and from the Terracotta Server. As objects fall out of the client cache, they are removed from the client virtual machine's heap. Conversely, if your application follows a reference to an object that isn't currently in heap, it will be retrieved from the Terracotta Server and automatically materialized back in the heap.

For example, if your application parses a very large XML file into a DOM object and makes that DOM object shared, only a subset of the DOM object will be kept in cache. The DSO memory management feature will allow you to manipulate a much larger DOM object than will fit in heap. However, if your application frequently or rapidly walks the entire DOM tree, objects will cycle through the cache and be flushed and faulted rapidly, causing the memory management system to thrash.

In the server, objects are cached in memory. As they fall out of the server cache, they are flushed to persistent storage on disk. In permanent-store mode, the server will flush object changes to disk ask they arrive. In temporary-swap-only mode, the server will flush object changes to disk as they fall out of cache.

Sample Applications

Terracotta DSO sample applications are included in the dso/samples directory. These samples illustrate how to cluster with Terracotta and show by example ways you can cluster your own applications.

Shared JTable

The Shared JTable sample application shows how to use Terracotta DSO to cluster a Java Swing application and use Terracotta Distributed Method Invocation (DMI) to propagate model data changes across the cluster.

Java Swing supports building Java applications with a Model-View-Controller architecture where the Model is the underlying data and is independent of its View (the user interface) and its Controller (the business logic). For this application, the Model is a DefaultTableModel object for JTable, and it is marked as a DSO root (a shared object) so all Views see the same data. Terracotta Distributed Method Invocation (DMI) causes JTable change events to be fired across all JVMs whenever the data is changed.

Terracotta's ability to pre-instrument many of the base classes means the configuration file for this application is as simple as the code.

For instructions on running the Shared JTable sample application, consult the readme.html file in the dso/samples/jtable directory.

Shared Graphics Editor

The Shared Graphics Editor sample application shows how to use Terracotta DSO to transform a single user drawing tool into a multi-person drawing tool where all users share a common drawing canvas.

The Shared Graphics Editor features field change communication and its ability to speed up object clustering. In this application you can render an image to the canvas, resize it, and then see the resizing on multiple canvasses at once. This responsiveness is feasible because only the fields that have changed in an object are communicated across the network. In this case, only changes to the width and height of the image are transmitted across the network.

This application is based upon a data model that is shared across a cluster. The DModel class is responsible for containing information on all the objects on the distributed canvas. Both read and write access to this data model are synchronized and that synchronization is defined in tc-config.xml as being cluster wide, ensuring cluster-level data model integrity.

Each of the images that can be rendered on the canvas implements the DObject interface that specifies certain attributes a renderable object must have and a common method for drawing that object. Whenever an object is added to the canvas or is resized, the changes are communicated to every node. Each node has added a listener to the DModel, so it is alerted of any changes and triggers a re-render of the DModel. It is important to note that the listeners to the DModel have been declared "transient". This means that the listeners are not shared across the cluster. This is not the default behavior, which can be overridden with <honor-transient>true</honor-transient> in the <include> section of the <instrumented-classes>.

This is done by changing:

<instrumented-classes>
 <include>
 <class-expression>demo.sharedEditor..*</class-expression>
 </include>
</instrumented-classes>

to:

<instrumented-classes>
 <include>
 <class-expression>demo.sharedEditor..*</class-expression>
 <honor-transient>true</honor-transient>
 </include>
</instrumented-classes>

If the listeners are not declared as transient, then the following steps take place:

  1. The DModel is updated.
  2. The Distributed Method Invocation fireChanged() is called on every node in the network.
  3. Each node iterates through all the listeners to the data model (one for each node in the network) and tells it to re-render the data model. Consequently, if there are four nodes each alerting all four nodes, there will be alerts sent across the network. You only need each node alerted once and this is achieved by omitting the listeners from the cluster so each data model only alerts its local listener.

Field-level object replication is important for sharing large objects. For example, resizing an image in Shared Graphics Editor does not cause the entire image to be sent to each node. Select a picture from your file system (using the Change... button after selecting Image) and drag it around the screen. Notice how it is resized in real-time on every node. This would not be possible if the whole image was being transmitted to every node 24 times a second. However, Terracotta only transmits the fields of the object that are changing, which in this case are the size attributes. This happens without any intervention or planning by the developer, enabling Terracotta DSO to efficiently share large objects across the network.

You can view the code for Shared Graphics Editor at: dso/samples/sharededitor/src/

For instructions on running the Shared Graphics Editor sample application, consult the readme.html file in the dso/samples/sharededitor directory.

Shared Work Queue

The Shared Work Queue sample application shows how to use Terracotta DSO in a web environment to create a list of computation tasks that is worked on by all the nodes in the cluster.

This sample application shows a servlet front end to a shared calculator. A work queue is established, and multiple DSO clients take work off the queue (computations to perform), and provide results on a shared results queue. More "workers" can be added to increase throughput. This application demonstrates how Terracotta enables a stand alone threaded application to become a distributed application with no code overhead.

Running this application on a given port is equivalent to starting a web server containing one simple application on that port. Simulate multiple web servers running in a cluster by running two instances of this application, each on a different port.

On one of the web pages, enter numbers in the "Length of Work" and "Units of Work" fields. These numbers signify how long a unit of work takes to process, and how many units require processing. Press the Execute button and then refresh both web pages. Looking at the results at the bottom of the page shows that the work assigned to the application is automatically split across the two running servers. Adding more servers to the cluster also automatically spreads the processing load across the new servers.

The SharedQueueExampleHttpServer class acts as a simple web application belonging to an embedded web server.

The clustered processing of the work queue is achieved by defining the queue as a shared object. This is also the case for the results queue so each node has access to and displays all the work that has been processed across the cluster. Synchronized access ensures that two nodes never process the same work unit and distributed wait/notify ensures that each node waits while the work queue is empty but starts processing as soon as there is work to process again.

For instructions on running the Shared Work Queue sample application, consult the readme.html file in the samples/pojo/sharedqueue directory.

Adaptavist Theme Builder Powered by Atlassian Confluence