Java Dynamic Management Kit 5.0 Tutorial

Defining Relations

A relation is composed of named roles, each of which defines the cardinality and class of MBeans that will be put into association with the other roles. A set of one or more roles defines a relation type. The relation type is a template for all relation instances that associate MBeans representing its roles. We use the term relation to mean a specific instance of a relation that associates existing MBeans according to the roles in its defining relation type.

For example, we can say that Books and Owner are roles. Books represents any number of owned books of a given MBean class, and Owner is a single book owner of another MBean class. We might define a relation type containing these two roles and call it Personal Library. It represents the concept of book ownership.

The following diagram represents this sample relation, as compared to the unified modeling language (UML) modeling of its corresponding association.

Figure 15–1 Comparison of the Relation Models

Diagram comparing the JMX and UML relation models

There is a slight difference between the two models. The UML association implies that each one of the Books can only have one owner. Our relation type only models a set of roles, guaranteeing that a relation instance has one Owner MBean and any number of MBeans in the role of Books.


Note –

The relation service does not do inter-relation consistency checks; if they are needed they are the responsibility of the designer. In our example, the designer needs to ensure that the same book MBean does not participate in two different Personal Library relations, while allowing it for an owner MBean.


In the rest of this topic, we will see how to handle the roles, relation types and relation instances through the relation service. We will use the names and relations presented in the programming example. First of all, we instantiate the relation service as we would any other MBean and register it in our agent's MBean server.


Example 15–1 Relation Service MBean

// Create the RelationService MBean
String relServClassName = 
    "javax.management.relation.RelationService";
ObjectName relServObjName = new ObjectName(
    "DefaultDomain:type=javax.management.relation.RelationService1");

// We use a constructor which takes a boolean parameter
// to set the ImmediatePurge mode for relation consistency
Object[] params = new Object[1];
params[0] = new Boolean(true);
String[] signature = new String[1];
signature[0] = "boolean";

server.createMBean(theRelServClassName, theRelServObjName,
                   params, signature);

The relation service exposes an attribute called Active that is false until its MBean is registered with the MBean server. All of the operations that handle relation or role MBeans, either directly or indirectly, will throw an exception when the service is not active.


Defining Role Information

Before we can create relation instances, we need a relation type, and before we can define a relation type, we need to represent the information about its roles. This information includes:

The name can be any string that is manipulated by the Java String class. It will be used to retrieve the corresponding MBeans when accessing a relation instance. The multiplicity is limited to the range between the minimum and maximum number of MBeans required for this role to fulfilled. The read-write permissions apply to all of the MBeans in the role, since the value of a role is read or written as a complete list.

The role information is represented by the RoleInfo class. In our example, we define two different roles:


Example 15–2 Instantiating RoleInfo Objects

// Define two roles in an array
// - container: SimpleStandard class/read-write access/multiplicity: 1..1
// - contained: SimpleStandard class/read-write access/multiplicity: 0..n

RoleInfo[] roleInfoArray = new RoleInfo[2];
String role1Name = "container";
roleInfoArray[0] =
    new RoleInfo( role1Name, "SimpleStandard",
                  true, true,
                  1, 1,
                  null);

String role2Name = "contained";
roleInfoArray[1] =
    new RoleInfo( role2Name, "SimpleStandard",
                  true, true,
                  0, -1,
                  null);

We build an array of RoleInfo objects that is intended to define a relation type, so it needs to define a valid set of roles. All role names must be unique within the array, and none of the array's elements can be null. Also, the minimum and maximum cardinalities must define a range of at least one integer.

Defining Relation Types

We define a relation type in the relation service by associating a name for the relation type with a non-empty array of RoleInfo objects. These are the parameters to the service's createRelationType method that we call through the MBean server.


Example 15–3 Defining a Relation Type

try {
    String relTypeName = "myRelationType";

    Object[] params = new Object[2];
    params[0] = relTypeName;
    params[1] = roleInfoArray;
    String[] signature = new String[2];
    signature[0] = "java.lang.String";
    // get the string representing the "RoleInfo[]" object
    signature[1] = (roleInfoArray.getClass()).getName();

    server.invoke( relServObjName, "createRelationType",
                   params, signature);

} catch (Exception e) {
    echo("\tCould not create the relation type " + relTypeName);
    printException(e);
}

The relation type name given by the calling process must be unique among all relation type names already created in the relation service. Relations will refer to this name to define their type, and the service will verify that the roles of the relation match the role information in this type.

The relation service provides methods for managing the list of relation types it stores. The removeRelationType removes the type's definition from the relation service and also removes all relation instances of this type. This mechanism is further covered in Maintaining Consistency.

Other methods give the list of all currently defined relation types or the role information associated with a given type. Role information is always obtained through the name of the relation type where it is defined. Here we show the subroutine that our example uses to print out all role and type information.


Example 15–4 Retrieving Relation Types and Role Information

try {
    echo("\n-> Retrieve all relation types");
    Object[] params1 = new Object[0];
    String[] signature1 = new String[0];
    ArrayList relTypeNameList = (ArrayList)
    (server.invoke( relServObjName, "getAllRelationTypeNames",
                    params1, signature1));

    for (Iterator relTypeNameIter = relTypeNameList.iterator();
         relTypeNameIter.hasNext(); ) {
        String currRelTypeName = (String)(relTypeNameIter.next());
        echo("\n-> Print role info for relation type " +
             currRelTypeName);
        Object[] params2 = new Object[1];
        params2[0] = currRelTypeName;
        String[] signature2 = new String[1];
        signature2[0] = "java.lang.String";
        ArrayList roleInfoList = (ArrayList)
            (server.invoke( relServObjName,"getRoleInfos",
                            params2, signature2));
        printList(roleInfoList);
    }
} catch (Exception e) {
    echo("\tCould not browse the relation types");
    printException(e);
}

Creating Relations

Now that we have defined a relation type, we can use it as a template for creating a relation. A relation is a set of roles that fulfills all of the role information of the relation type. The Role object contains a role name and value which is the list of MBeans that fulfills the role. The RoleList object contains the set of roles used when setting a relation or getting its role values.

In order to create a relation we must provide a set of roles whose values will initialize the relation correctly. In our example we use an existing SimpleStandard MBean in each role that we have defined. Their object names are added to the value list for each role. Then each Role object is added to the role list.


Example 15–5 Initializing Role Objects and Creating a Relation

[...] // define object names and create SimpleStandard MBeans

// Instantiate the roles using the object names of the MBeans
ArrayList role1Value = new ArrayList();
role1Value.add(mbeanObjectName1);
Role role1 = new Role(role1Name, role1Value);

ArrayList role2Value = new ArrayList();
role2Value.add(mbeanObjectName2);
Role role2 = new Role(role2Name, role2Value);

RoleList roleList1 = new RoleList();
roleList1.add(role1);
roleList1.add(role2);

String relId1 = relTypeName + "_instance";

try {
    Object[] params = new Object[3];
    params[0] = relId1;
    params[1] = relTypeName;
    params[2] = roleList1;
    String[] signature = new String[3];
    signature[0] = "java.lang.String";
    signature[1] = "java.lang.String";
    signature[2] = "javax.management.relation.RoleList";
    server.invoke(theRelServObjName, "createRelation",
                  params, signature);
} catch(Exception e) {
    echo("\tCould not create the relation " + RelId1);
    printException(e);
}

The createRelation method will raise an exception if the provided roles do not fulfill the specified relation type. You can omit Role objects for roles that allow a cardinality of 0; their values will be initialized with an empty list of object names. The relation service will check all provided object names to ensure they are registered with the MBean server. Also, the relation identifier name is a string that must be unique among all relations in the service.

The corresponding removeRelation method is also exposed for management operations. It is also called internally to keep all relations coherent, as described in Maintaining Consistency. In both cases, removing a relation means that you can no longer access it through the relation service, and the isRelation operation will return false when given its relation identifier. Also, its participating MBeans will no longer be associated through the roles in which they belonged. The MBeans continue to exist unaltered otherwise and can continue to participate in other relations.