Chapter 5. Open MBeans

So far, we have looked at three ways to instrument resources to be manageable. Now we will look at a way to instrument resources so that they are the most open to management applications. In this chapter, we will discuss how to instrument resources whose attributes are more complex than the fundamental types and whose operations take complex parameters. The key to this more open means of instrumentation lies in the set of data types defined by the JMX specification called open MBeans. By using open MBeans, we can instrument application resources of any type and make them available to any agent or management application that does not have access to the bytecode for either the resource, attribute, or operation parameter. The agent or management application can even be a non-Java program!

We will first look at the various open MBean types. Those types include the fundamental types, such as int, long, and char, as well as new types, such as structural and tabular data. All of the open MBean types are classes that derive from a single open MBean type, OpenType.

Next, we will examine the various open MBean metadata classes that allow us to instrument our resources as open MBeans. At the time of this writing, open MBeans are newly finalized (they were not part of the JMX 1.0 specification), so some of the information in this chapter may be subject to change.

Open MBean Types

The open MBean types are at the heart of what makes open MBeans “open.” The JMX RI defines a base class, OpenType, from which all open MBean types are derived. This ensures consistency among all of the subsequent types. In this section, we will look at the basic types, most of which are fundamental and correspond to their JDK wrapper types. We will first look at OpenType, then we will take a look at SimpleType, an RI class that provides static methods to obtain references to the various fundamental open types. Then we will look briefly at the basic types that allow us to describe complex data.

This section also describes how to represent complex data using the JMX classes CompositeData and TabularData and discusses their type definition classes (CompositeType and TabularType, respectively). Finally, we will look at the JMX support classes that implement these complex type enablers.

Basic Types

All of the open MBean types are one of the basic types. There are two categories of basic types: those types that are fundamental and those that may be used to describe arbitrarily complex types. In this section, we will look at all of the open MBean basic types. We will start with the base class for all open MBean types, OpenType. Then we will look at SimpleType, a subclass of OpenType, which is used to obtain instances of the fundamental open MBean types. Finally, we will take a quick look at the open MBean types that are used to represent complex data, saving the bulk of the discussion for the next section, Section 5.1.2.

OpenType

This class is the base class for all open MBean types. OpenType is abstract, so it cannot be instantiated. However, the essential characteristics for all open MBean types are defined in the protected constructor for OpenType, to which subclasses must delegate:

Class name

The fully qualified name of the Java class that the OpenType instance represents (e.g., java.lang.Integer is the class name for integer data types).

Type name

The name that has been assigned to the data type represented by this OpenType instance. Must be unique across all other open MBean types.

Description

A human-readable description (suitable for display on a management console, for example) of the type represented by this OpenType instance.

These three attributes are read-only and are common to all open MBean types. The values for these attributes may not be null, all spaces, or an empty string, or the OpenType constructor will throw an IllegalArgumentException .

In addition to these attributes, OpenType provides a few helper methods that agent and management application developers may find useful when interrogating open MBeans:

isArray( )

Returns true if the open type represented by this instance of OpenType is an instance of ArrayType (see below)

isValue( )

Returns true if the class name of the specified Object argument is the same as this instance of OpenType

SimpleType

This class is a subclass of OpenType (as are all valid open MBean types) that is used to represent the following open MBean types:

  • java.lang.Void

  • java.lang.Boolean

  • java.lang.Character

  • java.lang.Byte

  • java.lang.Short

  • java.lang.Integer

  • java.lang.Long

  • java.lang.Float

  • java.lang.Double

  • java.lang.String

  • java.math.BigDecimal

  • java.math.BigInteger

  • javax.management.ObjectName

The constructor for SimpleType is explicitly declared with private visibility, so it cannot be instantiated. Publicly declared static fields are provided to obtain an instance of an open type corresponding to each of the above types. For example, to obtain an instance of a SimpleType object that represents the open type for java.lang.Long, use:

OpenType openType = SimpleType.LONG;

Notice that the field name is the unqualified name of the corresponding Java class in capital letters. Thus, the open type for java.lang.Integer is obtained through SimpleType.INTEGER, and so on.

Other basic types

There are three other basic types that are currently part of the JMX specification:

ArrayType

Describes n-dimensional arrays of open types.

CompositeType

Describes structural (i.e., inherently nonuniform) data of arbitrary complexity. A CompositeData object is logically an “instance” of CompositeType.

TabularType

Describes tables of CompositeData objects. The same CompositeType object describes every row of the table, so the table is homogeneous. A TabularData object is logically an “instance” of TabularType.

All of these classes are subclasses of OpenType. If one of the attributes on an open MBean is an array type, it can be completely described by an ArrayType instance and understood by a management application. CompositeType and TabularType are used to describe complex data. We will look at these types in detail in the next section.

Complex Data

As implemented in the JMX RI, the basic data types for representing complex data are implemented by a type class, an interface, and a support class. We saw the support-class idiom when we looked at model MBeans. In this section, we will see how the type/interface/support-class idiom works for open MBeans for the two basic open MBean types, CompositeType and TabularType. While the JMX specification addresses CompositeData and TabularData (which are interfaces) and their underlying support classes, it makes sense to talk first about the classes provided by the RI that describe them.

CompositeType

CompositeType is a concrete class provided by the JMX RI that allows us to describe complex data that is composed of other basic open MBean types (including other complex types). There is no limit to the complexity of a complex type in open MBeans. With CompositeType, we are able to describe attributes, parameters, and return value types that are objects of arbitrary complexity. You can think of a CompositeType as providing metadata about a complex type, similar to the way the source code for a struct in C/C++ describes a complex data structure.

CompositeType includes the following parameters:

typeName

The name assigned to the new complex type. Continuing with the C/C++ analogy, this is similar to the name of the struct.

description

A human-readable text description of the type described by this CompositeType, suitable for display on a management console.

itemNames

A String array of names for the members of the CompositeType. This is similar to the variable names of a C/C++ struct. When dealing with a CompositeData object, the item names are referred to as the keys for accessing the corresponding values in the CompositeData object.

itemDescriptions

A String array of human-readable descriptions that correspond to the itemNames array.

itemTypes

An array of OpenType objects that describe the basic type of each item that comprises the data structure described by this CompositeType object.

Tip

Note that the indexes of the itemNames, itemDescriptions, and itemTypes arrays must match each other exactly.

The CompositeType class provides a constructor that allows us to set the values of the above attributes; its constructor is defined as:

public CompositeType(String typeName,
                     String description,
                     String[] itemNames,
                     String[] itemDescriptions,
                     OpenType[] itemTypes)
  throws OpenDataException {
  // . . .
}

When creating a CompositeData object, we must first create a CompositeType object to describe it. Let’s look at a simple example that deviates somewhat from the sample application. Suppose we have to represent a simple (and not very object-oriented) data structure that looks like the following:

public class Building {
  public String name;
  public short numberOfFloors;
  public int height;
  public boolean undergroundParking;
  public short numberOfElevators;
  public long officeSpace;
}

We might then create a CompositeType object to represent all possible instances of Building, as shown in Example 5-1.

Example 5-1. Describing a complex data type using CompositeType

try {
    // first describe the attribute names
    String[] itemNames = {
      "Name",
      "NumberOfFloors",
      "Height",
      "UndergroundParking",
      "NumberOfElevators",
      "OfficeSpace"
    };
    // next describe the attribute descriptions
    String[] itemDescriptions = {
      "Name of the building",
      "The number of floors (stories) the building has",
      "The height of the building in feet",
      "Whether or not the building has underground parking",
      "The total number of elevators in the building",
      "The amount of office space in square feet"
    };
    // next describe the data type of each item
    OpenType[] itemTypes = {
      SimpleType.STRING,
      SimpleType.SHORT,
      SimpleType.INTEGER,
      SimpleType.BOOLEAN,
      SimpleType.SHORT,
      SimpleType.LONG
    };
  CompositeType buildingType = new CompositeType(
    "BuildingCompositeType",
    "CompositeType that represents a Building.",
    itemNames,
    itemDescriptions,
    itemTypes
  );
} catch (OpenDataException e) {
  // . . .
}

What we have done, in essence, is to create an entirely new data type. Because we used the open MBean class CompositeType to do it, the new type can be instrumented in an application and be managed by a JMX-compliant management application.

CompositeData

The CompositeData interface describes how to access the contents of a complex object. This interface is defined as:

public interface CompositeData {
    public CompositeType getCompositeType(  );
    public Object get(String key);
    public Object[] getAll(String[] keys);
    public boolean containsKey(String key);
    public boolean containsValue(Object value);
    public Collection values(  );
    public boolean equals(Object obj);
    public int hashCode(  );
    public String toString(  );
}

Whereas CompositeType describes one or more instances of CompositeData, the CompositeData class itself contains values. Two methods are provided to access the values inside a CompositeData object:

get( )

Retrieves the object (i.e., the value) of a complex data structure that has the specified key

getAll( )

Retrieves an array of objects of a complex data structure that have the specified keys

Recall from our discussion of CompositeType that each member of a complex structure—at a particular index—described by a CompositeType object is assigned a name, located in the item names attribute at that index. That name is the key for retrieval. In Example 5-1, we saw how to create a CompositeType object for a complex data type called Building. Suppose that we need to manipulate an instance of a CompositeData object that is based on BuildingCompositeType (the name given to the new type in Example 5-1) and that this instance is passed to some method we have created. If we needed to access the Name attribute, we would use the get( ) method:

public void someMethod(CompositeData buildingData) {
  String buildingName = (String)buildingData.get("Name");
  // . . .
}

By the same token, we could retrieve several attributes at once by using the getAll( ) method:

public void someMethod(CompositeData buildingData) {
  String[] attributeNames = {
    "Name",
    "Height",
    "OfficeSpace"
  };
  Object[] attributeValues = buildingData.getAll(attributeNames);
  String name = (String)attributeValues[0];
  int height = ((Integer)attributeValues[1]).intValue(  );
  long officeSpace = ((Long)attributeValues[2]).longValue(  );
  // . . .
}

The order of the values in the array is the same as the order in which the keys were specified. Notice that we must unwrap any fundamental types (after casting them out of the Object array) to get the Java primitive.

The get( ) and getAll( ) methods will probably be the ones you use most often to manipulate CompositeData objects. However, the following methods may also come in handy:

getCompositeType( )

Retrieves the CompositeType object that describes this CompositeData instance. This might be useful, for example, when you need to display descriptions of the item names.

containsKey( )

Returns a boolean indicating whether or not the specified key value is one of the item names of the CompositeType. For example, the following code from our Building example would return true:

public void someMethod(CompositeData buildingData) {
  if (buildingData.containsKey("Height"))
    doSomething(  );
}

and the doSomething( ) method would be invoked. This code would return false:

public void someMethod(CompositeData buildingData) {
  if (buildingData.containsKey("Width"))
    doSomething(  );
}

and doSomething( ) would not be executed, because Width is not a valid key.

containsValue( )

Returns true if any value in the CompositeData object contains the specified value.

values( )

Returns an object that implements the Collection interface and contains all of the values in the CompositeData object. The values are in the same order as their corresponding keys (i.e., in alphabetical, ascending key order).

equals( )

Tests the equality of this instance of a CompositeData object with another. For this method to return true, the two CompositeData objects must be of the same CompositeType, and all of their item values must be equal. If the specified object does not implement the CompositeData interface, or is a null reference, this method returns false.

hashCode( )

Returns a hashcode for this CompositeData object.

toString( )

Returns a string representation of this CompositeData object. The format of the representation depends on the implementation.

CompositeDataSupport

This class implements the CompositeData interface. If we need to create a CompositeData object, we use one of this class’s constructors:

public CompositeDataSupport(CompositeType compositeType, 
                            String[] itemNames, Object[] itemValues)
  throws OpenDataException {
  // . . .
}
public CompositeDataSupport(CompositeType compositeType, Map items)
  throws OpenDataException {
  // . . .
}

The first constructor takes a CompositeType object, a String array of item names, and an Object array of item values in the exact order of the item names. Each of the item names in itemNames must be the same as one of the item names in the CompositeType object, but the order does not have to be the same.

The RI checks the parameters to make sure that all of the following are true:

  • compositeType is not null.

  • itemNames and itemValues are not empty arrays or null, none of their elements are empty strings or null, and itemNames and itemValues are of the same length.

  • itemNames and itemValues are both of the size specified in compositeType (as its itemNames attribute).

  • Each of the elements in itemNames is one of the elements in the itemNames attribute of compositeType and is of the correct type for that item name.

If the first two conditions are not true, an IllegalArgumentException will be thrown. If either of the last two is not true, an OpenDataException will be thrown. Using our Building example, suppose we wanted to create an instance of CompositeDataSupport using the attribute values listed in Table 5-1.

Table 5-1. Attribute values for new CompositeDataSupport instance

Attribute name

Attribute value

Name

“Fictitious Life Building”

NumberOfFloors

3

Height

45

UndergroundParking

false

NumberOfElevators

1

OfficeSpace

10000

Using these values, we can create an instance of CompositeDataSupport (as shown in Example 5-2), using the buildingType CompositeType created in Example 5-1.

Example 5-2. Creating a CompositeData object using CompositeDataSupport

public CompositeData createCompositeDataObject(  ) {
  try {
    String[] itemNames = {
      "Name",
      "NumberOfFloors",
      "Height",
      "UndergroundParking",
      "NumberOfElevators",
      "OfficeSpace"
    };
    Object[] itemValues = {
      "Building A",
      new Short(3),
      new Integer(45),
      new Boolean(false),
      new Short(1),
      new Long(10000)
    };
    CompositeData buildingData = new CompositeDataSupport(
      buildingType, // See Example 5-1
      itemNames,
      itemValues
    );
    return buildingData;
  } catch (Exception e) {
    // . . .
  }
}

We can also create a CompositeData object through CompositeDataSupport’s second constructor, passing a Map object that contains the name/value pairs:

public CompositeData createCompositeDataObject(  ) {
  try {
    Map items = new HashMap(  );
    items.put("Name", "Building A");
    items.put("NumberOfFloors", new Short(3));
    items.put("Height", new Integer(45));
    items.put("UndergroundParking", new Boolean(false));
    items.put("NumberOfElevators", new Short(1));
    items.put("OfficeSpace", new Long(10000));
    CompositeData buildingData = new CompositeDataSupport(
      buildingType, // see Example 5-1
      items
    );
    return buildingData;
  } catch (Exception e) {
    // . . .
  }
}

TabularType

This open MBean type class represents tabular data, which consists of rows of CompositeType elements. As we’ve seen, while a CompositeType data structure can have an arbitrarily complex structure, each CompositeData object that follows the structure is only a single object. However, with TabularType, we can further compose CompositeData objects into a table view, from which rows can be added or deleted. Each row of the TabularData object that adheres to its corresponding TabularType object must be of the same CompositeType, so that the rows are homogeneous. Beyond that, there is no limit to the complexity of the structure of a TabularType object.

You can think of the elements (items) of the CompositeData objects that make up the rows in a TabularData object as columns in a database table. As with a database table, you can define a set of keys, each of which is composed of one or more of the items in the CompositeData object that makes up each row. You can then use these keys to access (i.e., update, add, or remove) rows of the table.

TabularType is a concrete class provided with the JMX RI. It provides a constructor with which to create instances, defined as:

public TabularType(String typeName, String description,
                   CompositeType rowType, String[] indexNames)
  throws OpenDataException {
// . . .
}

The typeName parameter allows us to give a name to the new TabularType object. description lets us provide a human-readable description of the type. Each row of a TabularData object described by the TabularType we are creating must be of the same CompositeType, given by the rowType parameter. Finally, the indexNames parameter is an array of Strings that specify the item names from the CompositeType; this array forms the key that lets us access the elements of a TabularData object described by the TabularType we are creating.

Suppose we want to create a new TabularType object that describes a table of Building objects (from our earlier example). Example 5-3 shows how we would do this.

Example 5-3. Creating a TabularType object

try {
  TabularType buildingTableType = new TabularType(
    "BuildingTabularType",
    "Tabular view of BuildingCompositeTypes",
    buildingType,
    new String[] {
      "Name",
      "Height"
    }
  );
} catch (Exception e) {
  // . . .
}

In this example, we create a new TabularType object where each row is of type BuildingCompositeType (see Example 5-1) and is keyed by the Name and Height items of BuildingCompsiteType.

TabularData

This is an interface that describes how to access data structured in tabular format. It must be implemented by a concrete class (most likely TabularDataSupport, which we will discuss later). TabularData provides a convenient way to manipulate objects that are in tabular format. In discussing TabularData in this section, we will assume that an instance of the concrete class that implements this interface has been created elsewhere. In the next section, we will see how to create TabularData objects by using TabularDataSupport. The TabularData interface is defined as:

public interface TabularData { 
  public TabularType getTabularType(  );
  public Object[] calculateIndex(CompositeData value);
  public String toString(  );
  // Map interface
  public void clear(  );
  public boolean containsKey(Object[] key);
  public boolean containsValue(CompositeData value);
  public boolean equals(Object obj);
  public CompositeData get(Object[] key);
  public int hashCode(  );
  public boolean isEmpty(  );
  public Set keySet(  );
  public void put(CompositeData value);
  public void putAll(CompositeData[] values);
  public CompositeData remove(Object[] key);
  public int size(  );
  public Collection values(  );
}

As we can see from the methods on the interface, TabularData is structured much like a Map object. With the exception of entrySet( ), TabularData implements methods with the same names as those on the JDK Map interface. However, the Map behavior required of TabularData necessitates different parameters from those of the corresponding Map interface method. For example, the containsKey( ) method of the Map interface takes a single Object parameter, whereas the TabularData version takes an Object array. For that reason, we will take a look at all of these methods in this section. In addition to the Map-like methods of TabularData, there are several other methods on this interface, and we’ll look at each one. If you’ve worked much with Java’s Map implementations (HashMap, TreeMap, etc.), much of this section will look very familiar.

We will use the following example to demonstrate how these methods work. In Example 5-3, we created a TabularType object to describe a table of BuildingCompositeType objects (see Example 5-1). This TabularType object has as its key the BuildingCompositeType items Name and Height. Recall that the elements (columns) of the underlying CompositeType that makes up each row have the following format (in the specified order):

  1. Name

  2. NumberOfFloors

  3. Height

  4. UndergroundParking

  5. NumberOfElevators

  6. OfficeSpace

Table 5-2 summarizes the rows in our hypothetical example.

Table 5-2. Rows of tabular data for our example

Name

NumberOfFloors

Height

UndergroundParking

NumberOfElevators

OfficeSpace

Building A

3

45

false

1

10000

Building B

7

90

false

3

70000

Building C

42

478

true

5

335000

getTabularType( ) returns the TabularType object that describes this TabularData object. In our example, this method returns the BuildingCompositeType object created in Example 5-3.

calculateIndex( ) takes a CompositeData object and returns an array of Objects that contains the data items of the CompositeData object that correspond to the key of the TabularType on which this TabularData object is based. In our example, this method would return array containing the Name (at element 0) and Height (at element 1) values for the CompositeData object parameter. In Example 5-2, we created a CompositeData object that looks like the first row in Table 5-1.

public void someMethod(TabularData buildingData) {
  try {
    CompositeData cd = createCompositeDataObject(  );  //from Example 5-2
    Object[] index = buildingData.calculateIndex(cd);
    // . . .
  } catch (Exception e) {
    // . . .
  }
}

The index array returned by this method contains two elements: at index 0 is a String containing "Building A", and at index 1 is an Integer containing the value 3. This method is handy if the caller is aware of the structure of the CompositeType of which each row is constituted but wants to retrieve key values of a particular row without necessarily having to know what the key structure looks like. If the CompositeData parameter is null, an IllegalArgumentException is thrown. If the CompositeData parameter does not conform to this TabularData object’s CompositeType structure, an InvalidOpenTypeException is thrown.

toString( ) returns a string representation of this TabularData object. The format of the representation is implementation-dependent.

Now let’s look at each of the Map-like methods of TabularData.

clear( ) removes all rows from this TabularData object.

containsKey( ) takes an Object array that contains values that correspond to the key type of the CompositeData object and returns true if there is a row containing that key. Using our example, suppose we pass an Object array that contains "Building A" (at element 0) and 3 (at element 1):

public someMethod(TabularData buildingData) {
  try {
    Object[] key = {
      "Building A",
      new Short(3)
    };
    if (buildingData.containsKey(key))
      doSomething(  ); // gets executed
    key = new Object[2];
    key[0] = "No Such Building";
    key[1] = new Short(3);
    if (buildingData.containsKey(key))
      doSomethingElse(  ); // does NOT get executed
  } catch (Exception e) {
    // . . .
  }
}

As we can see from Table 5-2, the first call to containsKey( ) in this example will return true, because the key specified corresponds to a row that exists in the TabularData object. The second call will return false, because no row contains a name of “No Such Building.” In addition, if the key does not conform to the structure of the key specified when the TabularType object was created, this method returns false. It does not throw an exception. This method is useful when the caller needs to know whether a particular row exists in the table but doesn’t necessarily have to retrieve and process that row.

containsValue( ) functions in exactly the same manner as containsKey( ), except it takes a CompositeData object as a parameter. If all fields of the row specified by the CompositeData object passed as a parameter match the contents of one row of this TabularData object exactly, this method returns true. If not, or if the CompositeData object has a different structure than the rows of this TabularData object, this method returns false. It does not throw an exception.

equals( ) takes an Object parameter and returns true if the specified Object is a TabularData object and each row is a match for at least one other row in this TabularData object. If the specified Object parameter is null, is not a TabularType object, or has at least one row that is different from this TabularData object, this method returns false. It does not throw an exception.

get( ) takes an Object array that contains a key to this TabularData object and returns the corresponding CompositeData object for the row that matches the specified key. If the specified key is null, a NullPointerException is thrown. If the specified key does not conform to this TabularData object’s TabularType definition, an InvalidKeyException is thrown.

hashcode( ) returns the hashcode for this TabularData object.

isEmpty( ) returns a boolean indicating whether or not this TabularData object contains any rows. If it contains at least one CompositeData object (i.e., a row), this method returns true. Otherwise, it returns false.

keySet( ) returns a Set that contains all of the keys in the underlying Map-like implementation. The returned Set can then be iterated over by calling the iterator( ) method of the Set. Each item in the Set is a List object that contains the objects that constitute the key, which in the current implementation are Strings.

public void someMethod(TabularData buildingData) {
  try {
    Set set = buildingData.keySet(  );
    Iterator iter = set.iterator(  );
    while (iter.hasNext(  )) {
      List key = (List)iter.next(  );
      String name = (String)key.get(0);
      int height = ((Integer)key.get(1)).intValue(  );
      // now do something with these values. . .
    }
  } catch (Exception e) {
    // . . .
  }
}

Because a List is used to maintain the keys internally, the order of the items follows exactly the order in which the keys were specified when the TabularType object was created for this TabularData object.

put( ) takes a CompositeData object and stores it in its internal Map-like implementation. No key needs to be specified (as is the case with the put( ) method of the Map interface, for example), because the key is calculated based on the index names specified in the TabularType object that describes this TabularData object. However, TabularData does not allow duplicate keys. In our example, the index names are the Name and Height fields of the BuildingCompositeType object. If the CompositeData object we want to store in this TabularType object looks like the second row from Table 5-2, the key is calculated to be "Building B" and 90. Consider the following example:

public void addCompositeDataObject(TabularData buildingData)
  try {
    String[] itemNames = {
      "Name",
      "NumberOfFloors",
      "Height",
      "UndergroundParking",
      "NumberOfElevators",
      "OfficeSpace"
    };
    Object[] itemValues = {
      "Building B",
      new Short(7),
      new Integer(90),
      new Boolean(false),
      new Short(3),
      new Long(70000)
    };
    CompositeData building = new CompositeDataSupport(
      buildingType, // See Example 5-1
      itemNames,
      itemValues
    );
    buildingData.put(building);
  } catch (Exception e) {
    // . . .
  }

The emphasized line shows the method call to add a CompositeData object. As in the previous examples, we have assumed that the TabularData object with which we’re working has already been created (we haven’t seen how to do this yet—if you suspect that we use TabularDataSupport to create the object, you’re correct; we’ll look at this in the next section). The following code shows how to retrieve the row we added in the previous example:

public void addCompositeDataObject(TabularData buildingData)
  try {
    // see above example. . .
    buildingData.put(building);
    // now retrieve the row just added:
    Object[] key = {
                        "Building B",
                        new Integer(90)
                      };
                      CompositeData newRow = buildingData.get(key);
    // . . .
  } catch (Exception e) {
    // . . .
  }
}

If the CompositeData object reference passed to this method is null, a NullPointerException is thrown. If the CompositeData object doesn’t conform to the CompositeType object that dictates the row structure of this TabularData object, an InvalidOpenTypeException is thrown. As we mentioned earlier, the key for the CompositeData row to be added is calculated internally, based on the contents of the CompositeData object and the key structure specified when the TabularType object for this TabularData object was created. If the key already exists in the TabularData object, a KeyAlreadyExistsException will be thrown. The key for each row must be unique.

putAll( ) functions in exactly the same manner as put( ), except that putAll( ) allows us to store more than one CompositeData object in a single method call. putAll( ) takes an array of CompositeData objects and stores them all inside the internal Map-like implementation. When an attempt is made to put each CompositeData element into the array that is passed to this method, that putAll( ) attempt is subject to the same conditions as a put( ) attempt. This means that the same exceptions can be thrown.

remove( ) takes an Object array containing the key of a CompositeData row to be removed. If the key does not exist, this method returns null; otherwise, it returns a reference to the CompositeData object that was removed. If the Object array reference is null, a NullPointerException will be thrown. If the reference is to an object that does not conform to the CompositeType that specifies the structure of rows in this TabularData object, an InvalidKeyException will be thrown.

size( ) returns the number of CompositeData elements, or rows, that are contained within this TabularData object.

values( ) returns a Collection that contains all of the CompositeData elements that are contained in the underlying Map-like implementation. The returned Collection can then be used to iterate through the CompositeData objects.

TabularDataSupport

This class implements the TabularData interface. If we need to create a TabularData object, we use one of this class’s two constructors:

public TabularDataSupport(TabularType tabularType) {
  // . . .
}
public TabularDataSupport(TabularType tabularType,
                          int initialCapacity,
                          float loadFactor) {
  // . . .
}

The first parameter to both constructors is a TabularType object that describes the key structure for the TabularData object to be created, as well as the structure of the CompositeData elements that make up each row in this TabularData object. As we’ve already seen, the underlying implementation TabularData is a Map (in fact, it’s a HashMap). The initialCapacity and loadFactor parameters of the second constructor allow us to set the initial size of the Map and the load factor, which must be a floating-point number between zero and one in other words, it’s a percentage). Once the number of entries in the Map exceeds this percentage, the capacity of the Map is increased.

Both constructors verify the validity of the tabularType parameter. If it is null, an IllegalArgumentException is thrown. When using the second constructor, an IllegalArgumentException is also thrown if the initialCapacity or the loadFactor is less than zero.

Unlike CompositeDataSupport, for which we must specify the constituents when we create an instance, TabularDataSupport can be created empty. Once we create an instance of TabularDataSupport, we can use the put( ) or putAll( ) method to add CompositeData rows.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset