Sun GlassFish Enterprise Server v3 Add-On Component Development Guide

Chapter 6 Adding Configuration Data for a Component

The configuration data of a component determines the characteristics and runtime behavior of a component. Enterprise Server provides interfaces to enable an add-on component to store its configuration data in the same way as other Enterprise Server components. These interfaces are similar to interfaces that are defined in JavaTM Specification Request (JSR) 222: Java Architecture for XML Binding (JAXB) 2.0. By using these interfaces to store configuration data, you ensure that the add-on component is fully integrated with Enterprise Server. As a result, administrators can configure an add-on component in the same way as they can configure other Enterprise Server components.

The following topics are addressed here:

How Enterprise Server Stores Configuration Data

Enterprise Server stores the configuration data for a domain in a single configuration file that is named domain.xml. This file is an extensible markup language (XML) instance that contains a hierarchy of elements to represent a domain's configuration. The content model of this XML instance is not defined in a document type definition (DTD) or an XML schema. Instead, the content model is derived from Java language interfaces with appropriate annotations. You use these annotations to add configuration data for a component as explained in the sections that follow.

For detailed information about the domain.xml file, see Sun GlassFish Enterprise Server v3 Domain File Format Reference.

Defining an Element

An element represents an item of configuration data. For example, to represent the configuration data for a network listener, Enterprise Server defines the network-listener element.

Define an element for each item of configuration data that you are adding.

ProcedureTo Define an Element

  1. Define a Java language interface to represent the element.

    Define one interface for each element. Do not represent multiple elements in a single interface.

    The name that you give to the interface determines name of the element as follows:

    • A change from lowercase to uppercase in the interface name is transformed to the hyphen (-) separator character.

    • The element name is all lowercase.

    For example, to define an interface to represent the wombat-container-config element, give the name WombatContainerConfig to the interface.

  2. Specify the parent of the element.

    To specify the parent, extend the interface that identifies the parent as shown in the following table.

    Parent Element 

    Interface to Extend 

    config

    org.glassfish.api.admin.config.Container

    applications

    org.glassfish.api.admin.config.ApplicationName

    Another element that you are defining 

    org.jvnet.hk2.config.ConfigBeanProxy

  3. Annotate the declaration of the interface with the org.jvnet.hk2.config.Configured annotation.


Example 6–1 Declaration of an Interface That Defines an Element

This example shows the declaration of the WombatContainerConfig interface that represents the wombat-container-config element. The parent of this element is the config element.

...
import org.jvnet.hk2.config.Configured;
...
import org.glassfish.api.admin.config.Container;
...
@Configured
public interface WombatContainerConfig extends Container {
...
} 

How Interfaces That Are Annotated With @Configured Are Implemented

You are not required to implement any interfaces that you annotate with the @Configured annotation. Enterprise Server implements these interfaces by using the Dom class. Enterprise Server creates a Java Platform, Standard Edition (Java SE) proxy for each Dom object to implement the interface.

Defining an Attribute of an Element

The attributes of an element describe the characteristics of the element. For example, the port attribute of the network-listener element identifies the port number on which the listener listens. For a description of all the attributes of the network-listener element, see network-listener in Sun GlassFish Enterprise Server v3 Domain File Format Reference.

Representing an Attribute of an Element

Represent each attribute of an element as the property of a pair of JavaBeansTM specification getter and setter methods of the interface that defines the element. The component for which the configuration data is being defined can then access the attribute through the getter method. The setter method enables the attribute to be updated.

Specifying the Data Type of an Attribute

The data type of an attribute is the return type of the getter method that is associated with the attribute. To enable the attribute take properties in the form ${property-name} as values, specify the data type as String.

Identifying an Attribute of an Element

To identify an attribute of an element, annotate the declaration of the getter method that is associated with the attribute with the org.jvnet.hk2.config.Attribute annotation.

To specify the properties of the attribute, use the elements of the @Attribute annotation as explained in the sections that follow.

Specifying the Name of an Attribute

To specify the name of an attribute, set the value element of the @Attribute annotation to a string that specifies the name. If you do not set this element, the name is derived from the name of the property as follows:

For example, if the getter method getNumberOfInstances is defined for the property NumberOfInstances to represent an attribute, the name of the attribute is number-of-instances.

Specifying the Default Value of an Attribute

The default value of an attribute is the value that is applied if the attribute is omitted when the element is written to the domain configuration file.

To specify the default value of an attribute, set the defaultValue element of the @Attribute annotation to a string that contains the default value. If you do not set this element, the parameter has no default value.

Specifying Whether an Attribute Is Required or Optional

Whether an attribute is required or optional determines how Enterprise Server responds if the parameter is omitted when the element is written to the domain configuration file:

To specify whether an attribute is required or optional, set the required element of the @Attribute annotation as follows:

Example of Defining an Attribute of an Element


Example 6–2 Defining an Attribute of an Element

This example defines the attribute number-of-instances. To enable the attribute take properties in the form ${property-name} as values, the data type of this attribute is String.

import org.jvnet.hk2.config.Attribute;
...
    @Attribute
    public String getNumberOfInstances();
    public void setNumberOfInstances(String instances) throws PropertyVetoException;
...

Defining a Subelement

A subelement represents a containment or ownership relationship. For example, Enterprise Server defines the network-listeners element to contain the configuration data for individual network listeners. The configuration data for an individual network listener is represented by the network-listener element, which is a subelement of network-listeners element.

ProcedureTo Define a Subelement

  1. Define an interface to represent the subelement.

    For more information, see Defining an Element.

    The interface that represents the subelement must extend the org.jvnet.hk2.config.ConfigBeanProxy interface.

  2. In the interface that defines the parent element, identify the subelement to its parent element.

    1. Represent the subelement as the property of a JavaBeans specification getter or setter method.

    2. Annotate the declaration of the getter or setter method that is associated with the subelement with the org.jvnet.hk2.config.Element annotation.


Example 6–3 Declaring an Interface to Represent a Subelement

This example shows the declaration of the WombatElement interface to represent the wombat-element element.

...
import org.jvnet.hk2.config.ConfigBeanProxy;
import org.jvnet.hk2.config.Configured;
...
@Configured
public interface WombatElement extends ConfigBeanProxy {
...
}
...


Example 6–4 Identifying a Subelement to its Parent Element

This example identifies the wombat-element element as a subelement.

...
import org.jvnet.hk2.config.Element;
...
import java.beans.PropertyVetoException;
...
@Element
    public WombatElement getElement();
    public void setElement(WombatElement element) throws PropertyVetoException;
...

Validating Configuration Data

Validating configuration data ensures that attribute values that are being set or updated do not violate any constraints that you impose on the data. For example, you might require that an attribute that represents a name is not null, or an integer that represents a port number is within the range of available port numbers. Any attempt to set or update an attribute value that fails validation fails. Any validations that you specify for an attribute are performed when the attribute is initialized and every time the attribute is changed.

To standardize the validation of configuration data, Enterprise Server uses JSR 303: Bean Validation for validating configuration data. JSR 303 defines a metadata model and API for the validation of JavaBeans components.

To validate an attribute of an element, annotate the attribute's getter method with the annotation in the javax.validation.constraints package that performs the validation that you require. The following table lists commonly used annotations for validating Enterprise Server configuration data. For the complete list of annotations, see the javax.validation.constraints package summary.

Table 6–1 Commonly Used Annotations for Validating Enterprise Server Configuration Data

Validation 

Annotation 

Not null 

javax.validation.constraints.NotNull

Null 

javax.validation.constraints.Null

Minimum value 

javax.validation.constraints.Min

Set the value element of this annotation to the minimum allowed value.

Maximum value 

javax.validation.constraints.Max

Set the value element of this annotation to the maximum allowed value.

Regular expression matching

javax.validation.constraints.Pattern

Set the regexp element of this annotation to the regular expression that is to be matched.


Example 6–5 Specifying a Range of Valid Values for an Integer

This example specifies that the attribute rotation-interval-in-minutes must be a positive integer.

...
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
...
@Min(value=1) 
@Max(value=Integer.MAX_VALUE)
String getRotationIntervalInMinutes();
...


Example 6–6 Specifying Regular Expression Matching

This example specifies that the attribute classname must contain only non-whitespace characters.

import javax.validation.constraints.Pattern;
...
@Pattern(regexp="^[\\S]*$")
String getClassname();
...

Initializing a Component's Configuration Data

To ensure that a component's configuration data is added to the domain.xml file when the component is first instantiated, you must initialize the component's configuration data.

Initializing a component's configuration data involves the following tasks:

ProcedureTo Define a Component's Initial Configuration Data

  1. Create a plain-text file that contains an XML fragment to represent the configuration data.

    • Ensure that each XML element accurately represents the interface that is defined for the element.

    • Ensure that any subelements that you are initializing are correctly nested.

    • Set attributes of the elements to their required initial values.

  2. When you package the component, include the file that contains the XML fragment in the component's JAR file.


Example 6–7 XML Data Fragment

This example shows the XML data fragment for adding the wombat-container-config element to the domain.xml file. The wombat-container-config element contains the subelement wombat-element. The attributes of wombat-element are initialized as follows:

<wombat-container-config>
    <wombat-element foo="something" bar="anything"/>
</wombat-container-config>

ProcedureTo Write a Component's Initial Configuration Data to the domain.xml File

Add code to write the component's initial configuration data in the class that represents your add-on component. If your add-on component is a container, add this code to the sniffer class. For more information about adding a container, see Chapter 7, Adding Container Capabilities.

  1. Set an optional dependency on an instance of the class that represents the XML element that you are adding.

    1. Initialize the instance variable to null.

      If the element is not present in the domain.xml file when the add-on component is initialized, the instance variable remains null.

    2. Annotate the declaration of the instance variable with the org.jvnet.hk2.annotations.Inject annotation.

    3. Set the optional element of the @Inject annotation to true.

  2. Set a dependency on an instance of the following classes:

    • org.glassfish.api.admin.config.ConfigParser

      The ConfigParser class provides methods to parse an XML fragment and to write the fragment to the correct location in the domain.xml file.

    • org.jvnet.hk2.component.Habitat

  3. Invoke the parseContainerConfig method of the ConfigParser object only if the instance is null.

    If your add-on component is a container, invoke this method within the implementation of the setup method the sniffer class. When the container is first instantiated, Enterprise Server invokes the setup method.

    The test that the instance is null is required to ensure that the configuration data is added only if the data is not already present in the domain.xml file.

    In the invocation of the parseContainerConfig method, pass the following items as parameters:

    • The Habitat object on which you set a dependency

    • The URL to the file that contains the XML fragment that represents the configuration data


Example 6–8 Writing a Component's Initial Configuration Data to the domain.xml File

This example writes the XML fragment in the file init.xml to the domain.xml file. The fragment is written only if the domain.xml file does not contain the wombat-container-config-element.

The wombat-container-config element is represented by the WombatContainerConfig interface. An optional dependency is set on an instance of a class that implements WombatContainerConfig.

...
import org.glassfish.api.admin.config.ConfigParser;
import org.glassfish.examples.extension.config.WombatContainerConfig;
...
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.component.Habitat;
import com.sun.enterprise.module.Module;

import java.util.logging.Logger;
...
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.net.URL;
...
    @Inject(optional=true)
    WombatContainerConfig config=null;
...
@Inject
    ConfigParser configParser;

    @Inject
    Habitat habitat;

    public Module[] setup(String containerHome, Logger logger) throws IOException {
        if (config==null) {
            URL url = this.getClass().getClassLoader().getResource("init.xml");
            if (url!=null) {
               configParser.parseContainerConfig(habitat, url, 
                   WombatContainerConfig.class);
            }
        }
        return null;
    }
...


Example 6–9 domain.xml File After Initialization

This example shows the domain.xml file after the setup method was invoked to add the wombat-container-config element under the config element.

<domain...>
...
   <configs>
    <config name="server-config">
      <wombat-container-config number-of-instances="5">
        <wombat-element foo="something" bar="anything" />
      </wombat-container-config>
      <http-service>
...
</domain>  

Creating a Transaction to Update Configuration Data

Creating a transaction to update configuration data enables the data to be updated without the need to specify a dotted name in the set(1) subcommand. You can make the transaction available to system administrators in the following ways:

ProcedureTo Create a Transaction to Update Configuration Data

Any transaction that you create to modify configuration data must use a configuration change transaction to ensure that the change is atomic, consistent, isolated, and durable (ACID).

  1. Set a dependency on the configuration object to update.

  2. Define a method to invoke to perform the transaction.

    1. Use the generic SimpleConfigCode interface to define the method that is to be invoked on a single configuration object, namely: SingleConfigCode<T extends ConfigBeanProxy>().

    2. In the body of this method, implement the run method of the SingleConfigCode<T extends ConfigBeanProxy> interface.

    3. In the body of the run method, invoke the setter methods that are defined for the attributes that you are setting.

      These setter methods are defined in the interface that represents the element whose elements you are setting.

  3. Invoke the static method org.jvnet.hk2.config.ConfigSupport.ConfigSupport.apply.

    In the invocation, pass the following information as parameters to the method:

    • The code of the method that you defined in Step 2

    • The configuration object to update, on which you set the dependency in Step 1


Example 6–10 Creating a Transaction to Update Configuration Data

This example shows code in the execute method of an asadmin subcommand for updating the number-of-instances element of wombat-container-config element.

...
import org.glassfish.api.Param;
...
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.config.Transactions;
import org.jvnet.hk2.config.ConfigSupport;
import org.jvnet.hk2.config.SingleConfigCode;
import org.jvnet.hk2.config.TransactionFailure;
...
    @Param
    String instances;

    @Inject
    WombatContainerConfig config;

    public void execute(AdminCommandContext adminCommandContext) {
        try {
            ConfigSupport.apply(new SingleConfigCode<WombatContainerConfig>() {
                public Object run(WombatContainerConfig wombatContainerConfig) 
			            throws PropertyVetoException, TransactionFailure {
                    wombatContainerConfig.setNumberOfInstances(instances);
                    return null;
                }
            }, config);
        } catch(TransactionFailure e) {            
        }
    }
...

Dotted Names of Configuration Attributes

The Enterprise Server administrative commands get(1), list(1), and set(1) locate a configuration attribute through the dotted name of the attribute. The dotted name of an attribute of a configuration element is as follows:

configs.config.server-config.element-name[.subelement-name...].attribute-name
element-name

The name of an element that contains a subelement or the attribute.

subelement-name

The name of a subelement, if any.

attribute-name

The name of the attribute.

For example, the dotted name of the foo attribute of the wombat-element element is as follows:

configs.config.server-config.wombat-container-config.wombat-element.foo

Examples of Adding Configuration Data for a Component

This example shows the interfaces that define the configuration data for the Greeter Container component. The data is comprised of the following elements:

This example also shows an XML data fragment for initializing an element. See Example 6–13.

Code for the Greeter Container component is shown in Example of Adding Container Capabilities.

Code for an asadmin subcommand that updates the configuration data in this example is shown in Example 4–7.


Example 6–11 Parent Element Definition

This example shows the definition of the greeter-container-config element. The attributes of the greeter-container-config element are as follows:

The greeter-element element is identified as a subelement of the greeter-container-config element. The definition of the greeter-element element is shown in Example 6–12.

package org.glassfish.examples.extension.greeter.config;

import org.jvnet.hk2.config.Configured;
import org.jvnet.hk2.config.Attribute;
import org.jvnet.hk2.config.Element;
import org.glassfish.api.admin.config.Container;

import javax.validation.constraints.Pattern;
import javax.validation.constraints.Min;
import javax.validation.constraints.Max;

import java.beans.PropertyVetoException;

@Configured
public interface GreeterContainerConfig extends Container {

    @Attribute
    @Min(value=1)
    @Max (value=10)
    public String getNumberOfInstances();
    public void setNumberOfInstances(String instances) throws PropertyVetoException;

    @Attribute
    @Pattern(regexp = "^[\\S]*$")
    public String getLanguage();
    public void setLanguage(String language) throws PropertyVetoException;

    @Attribute
    @Pattern(regexp = "^[\\S]*$")
    public String getStyle();
    public void setStyle(String style) throws PropertyVetoException;

    @Element
    public GreeterElement getElement();
    public void setElement(GreeterElement element) throws PropertyVetoException;


}


Example 6–12 Subelement Definition

This example shows the definition of the greeter-element element, which is identified as a subelement of the greeter-container-config element in Example 6–11. The only attribute of the greeter-element element is greeter-port, which must be in the range 1030–1050.

package org.glassfish.examples.extension.greeter.config;

import org.jvnet.hk2.config.ConfigBeanProxy;
import org.jvnet.hk2.config.Configured;
import org.jvnet.hk2.config.Attribute;

import javax.validation.constraints.Min;
import javax.validation.constraints.Max;

import java.beans.PropertyVetoException;

@Configured
public interface GreeterElement extends ConfigBeanProxy {

    @Attribute
    @Min(value=1030)
    @Max (value=1050)
    public String getGreeterPort();
    public void setGreeterPort(String greeterport) throws PropertyVetoException;
    
}


Example 6–13 XML Data Fragment for Initializing the greeter-container-config Element

This example shows the XML data fragment for adding the greeter-container-config element to the domain.xml file. The greeter-container-config element contains the subelement greeter-element.

The attributes of greeter-container-config are initialized as follows:

The greeter-port attribute of the greeter-element element is set to 1040.

<greeter-container-config number-of-instances="5" language="norsk" style="formal">
    <greeter-element greeter-port="1040"/>
</greeter-container-config>

The definition of the greeter-container-config element is shown in Example 6–11. The definition of the greeter-element element is shown in Example 6–12.