Skip Headers
Oracle GlassFish Server Add-On Component Development Guide
Release 3.1.2

Part Number E24927-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

4 Extending the asadmin Utility

The asadmin utility is a command-line tool for configuring and administering GlassFish Server. Extending the asadmin utility enables you to provide administrative interfaces for an add-on component that are consistent with the interfaces of other GlassFish Server components. A user can run asadmin subcommands either from a command prompt or from a script. For more information about the asadmin utility, see the asadmin(1M) man page.

The following topics are addressed here:

About the Administrative Command Infrastructure of GlassFish Server

To enable multiple containers to be independently packaged and loaded, the administrative command infrastructure of GlassFish Server provides the following features:

Adding an asadmin Subcommand

An asadmin subcommand identifies the operation or task that a user is to perform. Adding an asadmin subcommand enables the user to perform these tasks and operations through the asadmin utility.

The following topics are addressed here:

Representing an asadmin Subcommand as a Java Class

Each asadmin subcommand that you are adding must be represented as a Java class. To represent an asadmin subcommand as a Java class, write a Java class that implements the org.glassfish.api.admin.AdminCommand interface. Write one class for each subcommand that you are adding. Do not represent multiple asadmin subcommands in a single class.

Annotate the declaration of your implementations of the AdminCommand interface with the org.jvnet.hk2.annotations.Service annotation. The @Service annotation ensures that the following requirements for your implementations are met:

  • The implementations are eligible for resource injection and resource extraction.

  • The implementations are location independent, provided that the component that contains them is made known to the GlassFish Server runtime.

    For information about how to make a component known to the GlassFish Server runtime, see Integrating an Add-On Component With GlassFish Server.

Specifying the Name of an asadmin Subcommand

To specify the name of the subcommand, set the name element of the @Service annotation to the name.

Note:

Subcommand names are case-sensitive.

Subcommands that are supplied in GlassFish Server distributions typically create, delete, and list objects of a particular type. For consistency with the names of subcommands that are supplied in GlassFish Server distributions, follow these conventions when specifying the name of a subcommand:

  • For subcommands that create an object of a particular type, use the name create-object.

  • For subcommands that delete an object of a particular type, use the name delete-object.

  • For subcommands that list all objects of a particular type, use the name list-objects.

For example, GlassFish Server provides the following subcommands for creating, deleting, and listing HTTP listeners:

  • create-http-listener

  • delete-http-listener

  • list-http-listeners

You must also ensure that the name of your subcommand is unique. To obtain a complete list of the names of all asadmin subcommands that are installed, use the list-commands subcommand. For a complete list of asadmin subcommands that are supplied in GlassFish Server distributions, see the Oracle GlassFish Server Reference Manual.

Ensuring That an AdminCommand Implementation Is Stateless

To enable multiple clients to run a subcommand simultaneously, ensure that the implementation of the AdminCommand interface for the subcommand is stateless. To ensure that the implementation of the AdminCommand interface is stateless, annotate the declaration of your implementation with the org.jvnet.hk2.annotations.Scoped annotation. In the @Scoped annotation, set the scope as follows:

  • To instantiate the subcommand for each lookup, set the scope to PerLookup.class.

  • To instantiate the subcommand only once for each session, set the scope to Singleton.

Example of Adding an asadmin Subcommand

Example 4-1 Adding an asadmin Subcommand

This example shows the declaration of the class CreateMycontainer that represents an asadmin subcommand that is named create-mycontainer. The subcommand is instantiated for each lookup.

package com.example.mycontainer;

import org.glassfish.api.admin.AdminCommand;
...
import org.jvnet.hk2.annotations.Service;
...
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.component.PerLookup;

/**
 * Sample subcommand
 */
@Service(name="create-mycontainer")
@Scoped(PerLookup.class)
public Class CreateMycontainer implements AdminCommand {
…
}

Adding Parameters to an asadmin Subcommand

The parameters of an asadmin subcommand are the options and operands of the subcommand.

The following topics are addressed here:

Representing a Parameter of an asadmin Subcommand

Represent each parameter of a subcommand in your implementation as a field or as the property of a JavaBeans specification setter method. Use the property of a setter method for the following reasons:

  • To provide data encapsulation for the parameter

  • To add code for validating the parameter before the property is set

Identifying a Parameter of an asadmin Subcommand

Identifying a parameter of an asadmin subcommand enables GlassFish Server to perform the following operations at runtime on the parameter:

  • Validation. The GlassFish Server determines whether all required parameters are specified and returns an error if any required parameter is omitted.

  • Injection. Before the subcommand runs, the GlassFish Server injects each parameter into the required field or method before the subcommand is run.

  • Usage message generation. The GlassFish Server uses reflection to obtain the list of parameters for a subcommand and to generate the usage message from this list.

  • Localized string display. If the subcommand supports internationalization and if localized strings are available, the GlassFish Server can automatically obtain the localized strings for a subcommand and display them to the user.

To identify a parameter of a subcommand, annotate the declaration of the item that is associated with the parameter with the org.glassfish.api.Param annotation. This item is either the field or setter method that is associated with the parameter.

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

Specifying Whether a Parameter Is an Option or an Operand

Whether a parameter is an option or an operand determines how a user must specify the parameter when running the subcommand:

  • If the parameter is an option, the user must specify the option with the parameter name.

  • If the parameter is an operand, the user may omit the parameter name.

To specify whether a parameter is an option or an operand, set the primary element of the @Param annotation as follows:

  • If the parameter is an option, set the primary element to false. This value is the default.

  • If the parameter is an operand, set the primary element to true.

Specifying the Name of an Option

The name of an option is the name that a user must type on the command line to specify the option when running the subcommand.

The name of each option that you add in your implementation of an asadmin subcommand can have a long form and a short form. When running the subcommand, the user specifies the long form and the short form as follows:

  • The short form of an option name has a single dash (-) followed by a single character.

  • The long form of an option name has two dashes (--) followed by an option word.

For example, the short form and the long form of the name of the option for specifying terse output are as follows:

  • Short form: -m

  • Long form: --monitor

Note:

Option names are case-sensitive.

Specifying the Long Form of an Option Name

To specify the long form of an option name, set the name element of the @Param annotation to a string that specifies the name. If you do not set this element, the default name depends on how you represent the option.

  • If you represent the option as a field, the default name is the field name.

  • If you represent the option as the property of a JavaBeans specification setter method, the default name is the property name from the setter method name. For example, if the setter method setPassword is associated with an option, the property name and the option name are both password.

Specifying the Short Form of an Option Name

To specify the short form of an option name, set the shortName element of the @Param annotation to a single character that specifies the short form of the parameter. The user can specify this character instead of the full parameter name, for example -m instead of --monitor. If you do not set this element, the option has no short form.

Specifying the Acceptable Values of a Parameter

When a user runs the subcommand, the GlassFish Server validates option arguments and operands against the acceptable values that you specify in your implementation.

To specify the acceptable values of a parameter, set the acceptableValues element of the @Param annotation to a string that contains a comma-separated list of acceptable values. If you do not set this element, any string of characters is acceptable.

Specifying the Default Value of a Parameter

The default value of a parameter is the value that is applied if a user omits the parameter when running the subcommand.

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

Specifying Whether a Parameter Is Required or Optional

Whether a parameter is required or optional determines how a subcommand responds if a user omits the parameter when running the subcommand:

  • If the parameter is required, the subcommand returns an error.

  • If the parameter is optional, the subcommand runs successfully.

To specify whether a parameter is optional or required, set the optional element of the @Param annotation as follows:

  • If the parameter is required, set the optional element to false. This value is the default.

  • If the parameter is optional, set the optional element to true.

Example of Adding Parameters to an asadmin Subcommand

Example 4-2 Adding Parameters to an asadmin Subcommand

This example shows the code for adding parameters to an asadmin subcommand with the properties as shown in the table.

Name Represented As Acceptable Values Default Value Optional or Required Short Name Option or Operand

--originator

A field that is named originator

Any character string

None defined

Required

None

Option

--description

A field that is named mycontainerDescription

Any character string

None defined

Optional

None

Option

--enabled

A field that is named enabled

true or false

false

Optional

None

Option

--containername

A field that is named containername

Any character string

None defined

Required

None

Operand


...
import org.glassfish.api.Param;
...
{
…
    @Param
    String originator;
    
    @Param(name="description", optional=true)
    …
    String mycontainerDescription
    
    @Param (acceptableValues="true,false", defaultValue="false", optional=true)
    String enabled
    
    @Param(primary=true)
    String containername;
…
}

Making asadmin Subcommands Cluster-Aware

The GlassFish Server asadmin command framework provides support for making asadmin subcommands work properly in a clustered environment or with standalone server instances. A command that changes a configuration is first executed on the domain administration server (DAS) and then executed on each of the server instances affected by the change. Annotations provided by the framework determine the instances on which the command should be replicated and executed. Commands that do not change a configuration need not be executed on the DAS at all, but only on the necessary instances. The framework provides support for collecting the output from the instances and sending a report back to the user.

Subcommands in a multi-instance environment can accept a --target option to specify the cluster or instance on which the command acts. From within the command, the Target utility allows the command to determine information about where it is running. For some commands, it may be desirable to have a main command that runs on the DAS and supplemental preprocessing or postprocessing commands that run on the instances.

The following topics are addressed here:

Specifying Allowed Targets

When you define a --target option by using the @Param annotation in the org.glassfish.api package, possible targets are as follows:

  • domain — The entire domain

  • server — The domain administration server, or DAS

  • cluster — A homogeneous set of server instances that function as a unit

  • standalone instance — A server instance that isn't part of a cluster

  • clustered instance — A server instance that is part of a cluster

  • config — A configuration for a cluster or standalone server instance

These possible targets are represented by the following CommandTarget elements of the @TargetType annotation in the org.glassfish.config.support package:

  • CommandTarget.DOMAIN

  • CommandTarget.DAS

  • CommandTarget.CLUSTER

  • CommandTarget.STANDALONE_SERVER

  • CommandTarget.CLUSTERED_INSTANCE

  • CommandTarget.CONFIG

By default, the allowed targets are server (the DAS), standalone server instances, clusters, and configurations. Not specifying a @TargetType annotation is equivalent to specifying the following @TargetType annotation:

@TargetType(CommandTarget.DAS,CommandTarget.STANDALONE_SERVER,CommandTarget.CLUSTER,CommandTarget.CONFIG)

Subcommands that support other combinations of targets must specify @TargetType annotations. For example, the create-http-lb subcommand supports only standalone server instance and cluster targets. Its @TargetType annotation is as follows:

@TargetType(CommandTarget.STANDALONE_SERVER,CommandTarget.CLUSTER)

Most subcommands do not act on server instances that are part of a cluster. This ensures that all server instances in a cluster remain synchronized. Thus, the CommandTarget.CLUSTERED_INSTANCE element of the @TargetType annotation is rarely used.

An example exception is the enable subcommand. To perform a rolling upgrade of an application deployed to a cluster, you must be able to enable the new application (which automatically disables the old) on one clustered instance at a time. The @TargetType annotation for the enable subcommand is as follows, all on one line:

@TargetType(CommandTarget.DAS,CommandTarget.STANDALONE_INSTANCE,CommandTarget.CLUSTER,
CommandTarget.CLUSTERED_INSTANCE)

Note that the CommandTarget.CLUSTERED_INSTANCE element is specified.

The target name specified in the command line is injected into the subcommand implementation if the following annotation is present:

@Param(optional=true,defaultValue=SystemPropertyConstants.DEFAULT_SERVER_INSTANCE_NAME)
    String target;

The Target Utility

The Target utility is a service, present in the internal-api module, org.glassfish.internal.api package, which a command implementation can obtain by using the following annotation:

@Inject Target targetUtil;

You can use this utility to avoid writing boiler plate code for actions such as getting the list of server instances for a cluster or checking if a server instance is part of a cluster. For example, here is an example of using the utility to obtain the configuration for a target cluster or server instance:

Config c = targetUtil.getConfig(target);

The Target utility is packaged in the as-install/modules/internal-api.jar file. Its methods are documented with comments.

Specifying asadmin Subcommand Execution

By default, all asadmin subcommands are automatically replicated and run on the DAS and all GlassFish Server instances specified in the --target option. To run a subcommand only on the DAS, use the following @ExecuteOn annotation in the org.glassfish.api.admin package:

@ExecuteOn(RuntimeType.DAS)

The stop-domain subcommand and subcommands that list information are examples of subcommands that execute only on the DAS.

To run a subcommand only on applicable server instances, use the following @ExecuteOn annotation:

@ExecuteOn(RuntimeType.INSTANCE)

Not specifying an @ExecuteOn annotation is equivalent to specifying the following @ExecuteOn annotation:

@ExecuteOn(RuntimeType.DAS,RuntimeType.INSTANCE)

In addition to RuntimeType, you can specify the following additional elements with the @ExecuteOn annotation:

  • ifFailure — By default, if errors occur during execution of a subcommand on a server instance, command execution is considered to have failed and further execution is stopped. However, you can choose to ignore the failure or warn the user rather than stopping further command execution. Specify the ifFailure element and set it to FailurePolicy.Ignore or FailurePolicy.Warn. For example:

    @ExecuteOn(value={RuntimeType.DAS}, ifFailure=FailurePolicy.Warn)
    
  • ifOffline — By default, if a server instance is found to be offline during the command replication process, command execution is considered to have failed and further execution is stopped. However, you can choose to ignore the failure or warn the user rather than stopping further command execution. Specify the ifOffline element and set it to FailurePolicy.Ignore or FailurePolicy.Warn. For example:

    @ExecuteOn(value={RuntimeType.DAS}, ifOffline=FailurePolicy.Ignore)
    

Subcommand Preprocessing and Postprocessing

Some asadmin subcommands may require preprocessing or postprocessing. For example, after an application is deployed to the DAS, references are created in all applicable server instances, which synchronize with the DAS. As another example, Message Queue or load balancer settings may have to be reconfigured whenever a server instance is added to a cluster.

For such cases, the command replication framework provides the @Supplemental annotation (in the org.glassfish.api.admin package). An implementation must use the value element of the @Supplemental annotation to express the supplemented command. This value is the name of the command as defined by the supplemented command's @Service annotation (in the org.jvnet.hk2.annotations package).

For example, the deploy subcommand requires postprocessing. The deployment command implementation looks like this:

@Service(name="deploy")
@ExecuteOn(RuntimeType.DAS)
public DeployCommand implements AdminCommand {
//Do Actual Deployment
}

A supplemental command that is run after every successful deployment looks like this:

@Service(name="DeploymentSupplementalCommand")
@Supplemental("deploy")
@ExecuteOn(RuntimeType.INSTANCE)
public DeploymentSupplementalCommand implements AdminCommand {
//Do logic that happens after deployment has been done
}

As another example, a subcommand to create a local server instance might look like this:

@Service(name = "create-local-instance")
@Scoped(PerLookup.class)
public final class CreateLocalInstanceCommand implements AdminCommand {
//Do local instance creation
}

A supplemental command to change Message Queue or load balancer settings after local instance creation might look like this:

@Service(name="CreateLocalInstanceSupplementalCommand")
@Supplemental("create-local-instance")
public CreateLocalInstanceSupplementalCommand implements AdminCommand {
//Change MQ/LB properties here
}

A supplemental command implements AdminCommand, thus it can use the @Param annotation and expect the corresponding asadmin command parameters to be injected at runtime. The parameter values available for injection are the same ones provided for the original command with which the supplemental command is associated. For example, the DeploymentSupplementalCommand has access to the parameter values available to the DeployCommand invocation.

An asadmin subcommand can be supplemented with multiple supplemental commands. In this case, all supplemental commands are run after completion of the main command but without any guarantee of the order in which they run.

To specify that a supplemental command is run before the main command, set the on element of the @Supplemental annotation to Supplemental.Timing.Before. For example:

@Supplemental(value="mycommand", on=Supplemental.Timing.Before)

Supplemental commands can use the @ExecuteOn annotation as described in Specifying asadmin Subcommand Execution.

Running a Command from Another Command

An asadmin subcommand or supplemental command might need to run another subcommand. For example, a subcommand running on the DAS might need to run a different subcommand on one or more server instances. Such invocations might use the ClusterExecutor class (in the org.glassfish.api.admin package), which accepts a ParameterMap, to pass parameters and their values to the invoked command.

The ParameterMapExtractor utility is a service, present in the common-util module, org.glassfish.common.util.admin package, which creates a new ParameterMap populated using the parameters and values of another AdminCommand that has already been injected.

To list parameter names you want excluded from the ParameterMap, pass the following:

Set<String>

This is optional.

Adding Message Text Strings to an asadmin Subcommand

A message text string provides useful information to the user about an asadmin subcommand or a parameter.

To provide internationalization support for the text string of a subcommand or parameter, annotate the declaration of the subcommand or parameter with the org.glassfish.api.I18n annotation. The @I18n annotation identifies the resource from the resource bundle that is associated with your implementation.

To add message text strings to an asadmin subcommand, create a plain text file that is named LocalStrings.properties to contain the strings. Define each string on a separate line of the file as follows:

key=string
key

A key that maps the string to a subcommand or a parameter. The format to use for key depends on the target to which the key applies and whether the target is annotated with the @I18n annotation. See the following table.

Target Format

Subcommand or parameter with the @I18n annotation

subcommand-name.command.resource-name

Subcommand without the @I18n annotation

subcommand-name.command

Parameter without the @I18n annotation

subcommand-name.command.param-name

The replaceable parts of these formats are as follows:

subcommand-name

The name of the subcommand.

resource-name

The name of the resource that is specified in the@I18n annotation.

param-name

The name of the parameter.

string

A string without quotes that contains the text of the message.

Note:

To display the message strings to users, you must provide code in your implementation of the execute method to display the text. For more information about implementing the execute method, see Enabling an asadmin Subcommand to Run.

Example 4-3 Adding Message Strings to an asadmin Subcommand

This example shows the code for adding message strings to the create-mycontainer subcommand as follows:

  • The create-mycontainer subcommand is associated with the message Creates a custom container. No internationalization support is provided for this message.

  • The --originator parameter is associated with the message The originator of the container. No internationalization support is provided for this message.

  • The --description parameter is associated with the message that is contained in the resource mydesc, for which internationalization is provided. This resource contains the message text A description of the container.

  • The --enabled parameter is associated with the message Whether the container is enabled or disabled. No internationalization support is provided for this message.

  • The --containername parameter is associated with the message The container name. No internationalization support is provided for this message.

The addition of the parameters originator, description, enabled and containername to the subcommand is shown in Example 4-2.

package com.example.mycontainer;

import org.glassfish.api.admin.AdminCommand;
...
import org.glassfish.api.I18n;
import org.glassfish.api.Param;
import org.jvnet.hk2.annotations.Service;
...
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.component.PerLookup;

/**
 * Sample subcommand
 */
@Service(name="create-mycontainer")
@Scoped(PerLookup.class)
public Class CreateMycontainer implements AdminCommand {

    …

    @Param
    String originator;

    @Param(name="description", optional=true)
    @I18n("mydesc")
    String mycontainerDescription
   
    @Param (acceptableValues="true,false", defaultValue="false", optional=true)
    String enabled
    
    @Param(primary=true)
    String containername;
 …

}

The following message text strings are defined in the file LocalStrings.properties for use by the subcommand:

create-mycontainer.command=Creates a custom container
create-mycontainer.command.originator=The originator of the container 
create-mycontainer.command.mydesc=A description of the container
create-mycontainer.command.enabled=Whether the container is enabled or disabled
create-mycontainer.command.containername=The container name

Enabling an asadmin Subcommand to Run

To enable an asadmin subcommand to run, implement the execute method in your implementation of the AdminCommand interface. The declaration of the execute method in your implementation must be as follows.

public void execute(AdminCommandContext context);

Pass each parameter of the subcommand as a property to your implementation of the execute method. Set the key of the property to the parameter name and set the value of the property to the parameter's value.

In the body of the execute method, provide the code for performing the operation that the command was designed to perform. For examples, see Example 4-6 and Example 4-7.

Setting the Context of an asadmin Subcommand

The org.glassfish.api.admin.AdminCommandContext class provides the following services to an asadmin subcommand:

To set the context of an asadmin subcommand, pass an AdminCommandContext object to the execute method of your implementation.

Changing the Brand in the GlassFish Server CLI

The brand in the GlassFish Server command-line interface (CLI) consists of the product name and release information that are displayed in the following locations:

If you are incorporating GlassFish Server into a new product with an external vendor's own brand name, change the brand in the GlassFish Server CLI.

To change the brand in the GlassFish Server CLI, create an OSGi fragment bundle that contains a plain text file that is named src/main/resources/BrandingVersion.properties.

In the BrandingVersion.properties file, define the following keyword-value pairs:

product_name=product-name
abbrev_product_name=abbrev-product-name
major_version=major-version
minor_version=minor-version
build_id=build-id
version_prefix=version-prefix
version_suffix=version-suffix

Define each keyword-value pair on a separate line of the file. Each value is a text string without quotes.

The meaning of each keyword-value pair is as follows:

product_name=product-name

Specifies the full product name without any release information, for example, name="ProductNameFullPlain" content="Oracle GlassFish Server".

abbrev_product_name=abbrev-product-name

Specifies an abbreviated form of the product name without any release information, for example, name="ProductNamePlain" content="GlassFish Server".

major_version=major-version

Returns the product major version, for example, 3

minor_version=minor-version

Specifies the product minor version, for example, 0.

build_id=build-id

Specifies the build version, for example, build 17.

version_prefix=version-prefix

Specifies a prefix for the product version, for example, v.

version_suffix=version-suffix

Specifies a suffix for the product version, for example, Beta.

Example 4-4 BrandingVersion.properties File for Changing the Brand in the GlassFish Server CLI

This example shows the content of the BrandingVersion.properties for defining the product name and release information of Oracle GlassFish Server 3.0.1, build 17. The abbreviated product name is glassfish-server.

product_name=Oracle GlassFish Server
abbrev_product_name=glassfish-server
major_version=3
minor_version=0.1
build_id=build 17

Examples of Extending the asadmin Utility

Example 4-5 asadmin Subcommand With Empty execute Method

This example shows a class that represents the asadmin subcommand create-mycontainer.

The usage statement for this subcommand is as follows:

asadmin create-mycontainer --originator any-character-string
[--description any-character-string]
[--enabled {true|false}] any-character-string

This subcommand uses injection to specify that a running domain is required.

package com.example.mycontainer;

import org.glassfish.api.admin.AdminCommand;
import org.glassfish.api.admin.AdminCommandContext;
import org.glassfish.api.I18n;
import org.glassfish.api.Param;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.component.PerLookup;

/**
 * Sample subcommand
 */
@Service(name="create-mycontainer")
@Scoped(PerLookup.class)
public Class CreateMycontainer implements AdminCommand {

    @Inject
    Domain domain;

    @Param
    String originator;

    @Param(name="description", optional=true)
    @I18n("mydesc")
    String mycontainerDescription

    @Param (acceptableValues="true,false", defaultValue="false", optional=true)
    String enabled
    
    @Param(primary=true)
    String containername;

    /**
     * Executes the subcommand with the subcommand parameters passed as Properties 
     * where the keys are the paramter names and the values the parameter values
     * @param context information 
     */
    public void execute(AdminCommandContext context) {
        // domain and originator are not null
        // mycontainerDescription can be null.
    }
}

The following message text strings are defined in the file LocalStrings.properties for use by the subcommand:

create-mycontainer.command=Creates a custom container
create-mycontainer.command.originator=The originator of the container 
create-mycontainer.command.mydesc=A description of the container
create-mycontainer.command.enabled=Whether the container is enabled or disabled
create-mycontainer.command.containername=The container name

Example 4-6 asadmin Subcommand for Retrieving and Displaying Information

This example shows a class that represents the asadmin subcommand list-runtime-environment. The subcommand determines the operating system or runtime information for GlassFish Server.

The usage statement for this subcommand is as follows:

asadmin list-runtime-environment{runtime|os}

package com.example.env.cli;

import org.glassfish.api.admin.AdminCommand;
import org.glassfish.api.admin.AdminCommandContext;
import org.glassfish.api.ActionReport;
import org.glassfish.api.I18n;
import org.glassfish.api.ActionReport.ExitCode;
import org.glassfish.api.Param;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.component.PerLookup;

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;

/**
 * Demos asadmin CLI extension
 *
  */
@Service(name="list-runtime-environment")
@Scoped(PerLookup.class)
public class ListRuntimeEnvironmentCommand implements AdminCommand {
    
    // this value can be either runtime or os for our demo
    @Param(primary=true)
    String inParam;
    
    public void execute(AdminCommandContext context) {

        ActionReport report = context.getActionReport();
        report.setActionExitCode(ExitCode.SUCCESS);

        // If the inParam is 'os' then this subcommand returns operating system
        // info and if the inParam is 'runtime' then it returns runtime info.
        // Both of the above are based on mxbeans.

        if ("os".equals(inParam)) {
            OperatingSystemMXBean osmb = ManagementFactory.getOperatingSystemMXBean();
            report.setMessage("Your machine operating system name = " + osmb.getName());
        } else if ("runtime".equals(inParam)) {
            RuntimeMXBean rtmb = ManagementFactory.getRuntimeMXBean();
            report.setMessage("Your JVM name = " + rtmb.getVmName());
        } else {
            report.setActionExitCode(ExitCode.FAILURE);
            report.setMessage("operand should be either 'os' or 'runtime'");
        }

    }
}

Example 4-7 asadmin Subcommand for Updating Configuration Data

This example shows a class that represents the asadmin subcommand configure-greeter-container. The subcommand performs a transaction to update configuration data for a container component. For more information about such transactions, see Creating a Transaction to Update Configuration Data.

The usage statement for this subcommand is as follows:

asadmin configure-greeter-container --instances instances [--language language] [--style style]

The acceptable values and default value of each option of the subcommand are shown in the following table. The table also indicates whether each option is optional or required.

Option Acceptable Values Default value Optional or Required

--instances

An integer in the range 1-10

5

Required

--language

english, norsk, or francais

norsk

Optional

--style

formal, casual, or expansive

formal

Optional


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

Code that defines the configuration data for the container component is shown in Examples of Adding Configuration Data for a Component.

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

import org.glassfish.api.admin.AdminCommand;
import org.glassfish.api.admin.AdminCommandContext;
import org.glassfish.api.Param;
import org.jvnet.hk2.annotations.Service;
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;

import java.beans.PropertyVetoException;

@Service(name = "configure-greeter-container")
public class ConfigureGreeterContainerCommand implements AdminCommand {

    @Param(acceptableValues = "1,2,3,4,5,6,7,8,9,10", defaultValue = "5")
    String instances;
    @Param(acceptableValues = "english,norsk,francais", defaultValue = "norsk",
    optional = true)
    String language;
    @Param(acceptableValues = "formal,casual,expansive", defaultValue = "formal",
    optional = true)
    String style;
    @Inject
    GreeterContainerConfig config;

    public void execute(AdminCommandContext adminCommandContext) {
        try {
            ConfigSupport.apply(new SingleConfigCode<GreeterContainerConfig>() {

                public Object run(GreeterContainerConfig greeterContainerConfig)
                        throws PropertyVetoException, TransactionFailure {
                    greeterContainerConfig.setNumberOfInstances(instances);
                    greeterContainerConfig.setLanguage(language);
                    greeterContainerConfig.setStyle(style);
                    return null;
                }
            }, config);
        } catch (TransactionFailure e) {
        }

    }
}

Implementing Create, Delete, and List Commands Using Annotations

Many asadmin subcommands simply create, delete, or list objects in the configuration. Such code is repetitive to write and error prone. To simplify the writing of these asadmin commands, GlassFish Server supports annotations that can create, delete, and list configuration objects from a command invocation. Unless attributes or properties are set to non-default values or extra actions are required, no writing of code is needed.

The following topics are addressed here:

Command Patterns

Create command pattern. The most basic create commands are implemented in the following pattern:

  1. Retrieve the parent configuration object instance to which the child will be added. For example, the parent could be a Clusters object and the child a Cluster object.

  2. Start a transaction on the parent instance.

  3. Create the child configuration object instance.

  4. Set the attributes and properties of the newly created child instance.

  5. Add the child to the parent using one of the following accessor methods:

    void setChild(ChildType child)
    

    Used when there can be zero or one children of a single type associated with one parent instance.

    List<ChildType> getChildren()
    

    Used when there can be zero or more children of a single type associated with one parent instance.

    You cannot retrieve a set of children of the same type from the same parent using two different accessor methods.

  6. Commit the transaction.

A generic create command implementation can do most of these tasks if the following information is provided:

  • A way to resolve the identity of the parent instance.

  • The type of the child instance.

  • A mapping between command options and child attributes.

  • The accessor method for adding the child to the parent.

Delete command pattern. The most basic delete commands are implemented in the following pattern:

  1. Retrieve the configuration object instance to be deleted.

  2. Start a transaction on the parent instance.

  3. Delete the child by removing it from the list or calling setXXX(null).

  4. Commit the transaction.

A generic delete command implementation can do most of these tasks if the following information is provided:

  • A way to resolve the identity of the child instance.

  • The accessor method for deleting the child.

List command pattern. The most basic list commands simply retrieve all configuration object instances of a given type.

Resolvers

A resolver retrieves a configuration object instance of a particular type. For a create command, it retrieves the parent of the object to be created. For a delete command, it retrieves the object to be deleted. A resolver implements the CrudResolver interface:

package org.glassfish.config.support;

/**
 * A config resolver is responsible for finding the target object of a specified
 * type on which a creation command invocation will be processed.
 *
 * Implementation of these interfaces can be injected with the command invocation
 * parameters in order to determine which object should be returned
 */
@Contract
public interface CrudResolver {

    /**
     * Retrieves the existing configuration object a command invocation is 
     * intented to mutate.
     * @param context the command invocation context
     * @param type the type of the expected instance
     * @return the instance or null if not found 
     */
    <T extends ConfigBeanProxy> T resolve(AdminCommandContext context, Class<T> type);
}

Given an AdminCommandContext, plus injection with the asadmin command line parameters (or any other HK2 services if necessary), the resolver should be able to determine the particular configuration object on which to act.

The following resolvers are provided in the org.glassfish.config.support package:

  • TargetBasedResolver — Uses the --target option and the expected return type to retrieve the configuration object instance.

  • TargetAndNameBasedResolver — Uses the --target option to look up a Config object and a name to retrieve one of the Config object's children.

  • TypeAndNameResolver — Uses the requested type and asadmin command name operand to find the configuration object instance. This is useful for a configuration that uses the @Index annotation, which registers instances under names.

  • TypeResolver — Uses the requested type to find the configuration object instance. This is the default resolver.

The @Create Annotation

By placing the org.glassfish.config.support.Create annotation on a method, you provide the following information:

  • The value element of the @Create annotation is the name of the asadmin subcommand that creates the configuration object.

  • The method's class is the type of the parent.

  • The method's return type or parameter type is the type of the child.

  • The method is the accessor method that adds a child of the specified type to the parent.

The only additional information needed is the resolver to use.

The following example specifies a create-cluster subcommand:

@Configured
public interface Clusters extends ConfigBeanProxy, Injectable {

     /**
      * Return the list of clusters currently configured
      *
      * @return list of {@link Cluster }
      */
    @Element
    @Create(value="create-cluster")
    public List<Cluster> getCluster();
}

Because there is only one instance of the parent type, Clusters, in the configuration, this example uses the default resolver to retrieve it. Therefore, no resolver needs to be specified.

The @Delete Annotation

By placing the org.glassfish.config.support.Delete annotation on a method, you provide the following information:

  • The value element of the @Delete annotation is the name of the asadmin subcommand that deletes the configuration object.

  • The method's class is the type of the parent.

  • The method's return type or parameter type is the type of the child.

  • The method is the accessor method that deletes a child of the specified type from the parent.

The only additional information needed is the resolver to use.

The following example specifies a delete-cluster subcommand:

@Configured
public interface Clusters extends ConfigBeanProxy, Injectable {

     /**
      * Return the list of clusters currently configured
      *
      * @return list of {@link Cluster }
      */
    @Element
    @Delete(value="delete-cluster", resolver=TypeAndNameResolver.class)
    public List<Cluster> getCluster();
}

The TypeAndNameResolver uses the child type and the name operand passed through the command line to retrieve the specific cluster instance to be deleted.

The @Listing Annotation

By placing the org.glassfish.config.support.Listing annotation on a method, you provide the following information:

  • The value element of the @Listing annotation is the name of the asadmin subcommand that lists the configuration objects.

  • The method's class is the type of the parent.

  • The method's return type is the type of the children to be listed.

  • The method is always the following accessor method:

    List<ChildType> getChildren()
    

The default resolver retrieves all of the children of the specified type. Therefore, no resolver needs to be specified for a list command.

The following example specifies a list-clusters subcommand:

@Configured
public interface Clusters extends ConfigBeanProxy, Injectable {

     /**
      * Return the list of clusters currently configured
      *
      * @return list of {@link Cluster }
      */
    @Element
    @Listing(value="list-clusters")
    public List<Cluster> getCluster();
}

Create Command Decorators

Most create commands must do more than create a single configuration object instance with default attribute values. For example, most create commands allow the user to specify non-default attribute values through command options. For another example, the create-cluster subcommand creates children of the Cluster object and copies a referenced Config object. A creation decorator provides the code necessary to perform such additional operations.

The interface that a creation decorator must implement is as follows:

@Scoped(PerLookup.class)
public interface CreationDecorator<T extends ConfigBeanProxy> {

    /**
     * The element instance has been created and added to the parent, it can be
     * customized. This method is called within a 
     * {@link org.jvnet.hk2.config.Transaction}
     * and instance is therefore a writeable view on the configuration component.
     *
     * @param context administration command context
     * @param instance newly created configuration element
     * @throws TransactionFailure if the transaction should be rollbacked
     * @throws PropertyVetoException if one of the listener of <T> is throwing 
     * a veto exception
     */
    public void decorate(AdminCommandContext context, T instance) 
        throws TransactionFailure, PropertyVetoException;

    /**
     * Default implementation of a decorator that does nothing.
     */
    @Service
    public class NoDecoration implements CreationDecorator<ConfigBeanProxy> {
        @Override
        public void decorate(AdminCommandContext context, ConfigBeanProxy instance) 
            throws TransactionFailure, PropertyVetoException {
            // do nothing
        }
    }
}

The CreationDecorator interface is in the org.glassfish.config.support package.

A @Create annotation specifies a creation decorator using a decorator element. For example:

@Configured
public interface Clusters extends ConfigBeanProxy, Injectable {

     /**
      * Return the list of clusters currently configured
      *
      * @return list of {@link Cluster }
      */
    @Element
    @Create(value="create-cluster", decorator=Cluster.Decorator.class)
    public List<Cluster> getCluster();
}

The @Create annotation is on a method of the parent class. However, the referenced creation decorator class is associated with the child class. For example:

@Configured
public interface Cluster extends ConfigBeanProxy, ... {

    ...

    @Service
    @Scoped(PerLookup.class)    
    class Decorator implements CreationDecorator<Cluster> {

        @Param(name="config", optional=true)
        String configRef=null;

        @Inject
        Domain domain;

        @Override
        public void decorate(AdminCommandContext context, final Cluster instance) 
                throws TransactionFailure, PropertyVetoException {

        ...

        }
    }
}

The decorator class can optionally be an inner class of the child class. You can inject command options using the @Param annotation. You can also inject HK2 services or configuration instances.

Delete Command Decorators

Some delete commands must do more than delete a single configuration object instance. For example, the delete-cluster subcommand deletes the referenced Config object if no other Cluster or Instance objects reference it. A deletion decorator provides the code necessary to perform such additional operations.

The interface that a deletion decorator must implement is as follows:

/**
 * A decorator for acting upon a configuration element deletion.
 *
 * @param <T> the deleted element parent type
 * @param <U> the deleted element
 */
@Scoped(PerLookup.class)
public interface DeletionDecorator<T extends ConfigBeanProxy, 
    U extends ConfigBeanProxy> {

    /**
     * notification of a configuration element of type U deletion.
     * 
     * Note that this notification is called within the boundaries of the
     * configuration transaction, therefore the parent instance is a
     * writable copy and further changes to the parent can be made without
     * enrolling it inside a transaction.
     *
     * @param context the command context to lead to the element deletion
     * @param parent the parent instance the element was removed from
     * @param child the deleted instance
     */
    public void decorate(AdminCommandContext context, T parent, U child);
}

The DeletionDecorator interface is in the org.glassfish.config.support package.

A @Delete annotation specifies a deletion decorator using a decorator element. For example:

@Configured
public interface Clusters extends ConfigBeanProxy, Injectable {

     /**
      * Return the list of clusters currently configured
      *
      * @return list of {@link Cluster }
      */
    @Element
    @Delete(value="delete-cluster", resolver= TypeAndNameResolver.class, 
        decorator=Cluster.DeleteDecorator.class)
    public List<Cluster> getCluster();
}

The @Delete annotation is on a method of the parent class. However, the referenced deletion decorator class is associated with the child class. For example:

@Configured
public interface Cluster extends ConfigBeanProxy, ... {

    ..
    @Service
    @Scoped(PerLookup.class)
    class DeleteDecorator implements DeletionDecorator<Clusters, Cluster> {
        ....
    }
}

The decorator class can optionally be an inner class of the child class. You can inject command options using the @Param annotation. You can also inject HK2 services or configuration instances.

Specifying Command Execution

Commands specified with the @Create, @Delete, and @Listing annotations can use the @ExecuteOn annotation. The @ExecuteOn annotation specifies whether the command runs on the DAS, on server instances, or both (the default). For more information, see Specifying asadmin Subcommand Execution.

To add an @ExecuteOn annotation to a @Create or @Delete annotation, use the cluster element. For example:

@Create(value="create-instance", resolver=TypeResolver.class,
        decorator=Server.CreateDecorator.class,
        cluster=@org.glassfish.api.admin.ExecuteOn(value=RuntimeType.DAS))

Using Multiple Command Annotations

You can specify multiple command annotations on the same method. The following example combines create, delete, and list commands for clusters:

@Configured
public interface Clusters extends ConfigBeanProxy, Injectable {

     /**
      * Return the list of clusters currently configured
      *
      * @return list of {@link Cluster }
      */
    @Element
    @Create(value="create-cluster", decorator=Cluster.Decorator.class)
    @Delete(value="delete-cluster", resolver= TypeAndNameResolver.class, 
        decorator=Cluster.DeleteDecorator.class)
    @Listing(value="list-clusters")
    public List<Cluster> getCluster();
}

You can also specify multiple create or delete command annotations for the same configuration object type using the @Creates or @Deletes annotation (both in the org.glassfish.config.support package). For example:

@Element
@Creates(
   @Create(value="create-something", decorator=CreateSomething.Decorator)
   @Create(value="create-something-else", decorator=CreateSomethingElse.Decorator)
   List<Something> getSomethings();
)

These commands create configuration object instances of the same type. Differences in the decorators and resolvers can produce differences in the options each command takes. The @Param annotated attributes of the created type define a superset of options for both commands.