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.
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.
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.
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:
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).
The name that has been assigned to the data type
represented by this OpenType
instance. Must be
unique across all other open MBean types.
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:
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.
There are three other basic types that are currently part of the JMX specification:
ArrayType
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.
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
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:
The name
assigned to the
new complex type. Continuing with the C/C++ analogy, this is similar
to the name of the struct
.
A human-readable text description of the type described
by this CompositeType
, suitable for display on a
management console.
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.
A String
array
of human-readable descriptions that
correspond to the itemNames array.
An array of OpenType
objects
that describe the basic type of each item that comprises the data
structure described by this CompositeType
object.
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.
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:
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:
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.
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.
Returns true
if any value in the
CompositeData
object contains the specified value.
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).
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
.
Returns a
string representation of this CompositeData
object. The format of the representation depends on the
implementation.
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 |
---|---|
|
“Fictitious Life Building” |
|
3 |
|
45 |
|
|
|
1 |
|
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) { // . . . } }
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
String
s 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
.
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):
Name
NumberOfFloors
Height
UndergroundParking
NumberOfElevators
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 |
|
1 |
10000 |
Building B |
7 |
90 |
|
3 |
70000 |
Building C |
42 |
478 |
|
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 Object
s 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
String
s.
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.
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.