Skip Headers
JavaTest Harness Architect's Guide,
JavaTest Harness 4.5 for the Java Platform
E20663-03
  Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
 
Next
Next
 

11 Service Management

This chapter describes the ServiceManager (com.sun.javatest.services) component provided by the JavaTest harness and how test suite architects can use it to manage services that a test suite might require for test execution.

This chapter contains the following sections:

Description

A service is any unique component related to a test suite that is required for test execution and must be started in separate process or thread (such as RMI daemon or ORB service) before the test suite is run. Some TCKs might require many services. The ServiceManager component enables users to configure the services required to run tests.

Each test suite optionally specifies and describes a set of services that it requires to run tests. During the test suite load process, the TestSuite instantiates objects that implement the Service interface and uploads those objects to the ServiceManager component.

The ServiceManager component manages all aspects of the service life cycle. From information given by the test suite and the JavaTest harness, it determines which services are required and when they should be started. From test execution events, it also determines which services should be stopped.

The ServiceManager is available either when running the JavaTest harness or as a separate service without running the JavaTest harness.

Table 11-1 Service Manager Features 

Features Description

Automatically start and stop services

Can start and stop services automatically.

Mapping services on tests or test suites

Mapping services on individual tests or a set of tests, not on the whole test suite, enables the ServiceManager to start/stop services for group of tests.

For example, if a user is not running CORBA tests, the Service Manager will not try to start CORBA services. Consequently the user should not have to configure CORBA services.

Manually start a service

In case of remote test execution, users need the ability to determine (manually or automatically) if a service should be started or not.

Services are thread safe

Services work safely in case of parallel test execution.

Process management - when to start services

Provides an ability to start services in two modes:

  1. As needed - Start service only when the first test or group of tests that needs the service is about to run.

  2. Up front - Start all needed services up front so that any errors starting services can be detected before the actual test run.

Process management - when to stop services

Needed services stay up, once started, until the end of the run or until it can be shown they are no longer be required. Test execution finish, time out, and end of test run are points to stop related services.

Performance

The test suite does not run noticeably slower with this mechanism enabled.

Usability - configuration file

The user only provides or edits a single configuration file for all the services, not a file for each service. The file is optional. If the user doesn't provide a file, the test suite should assume that any required services will be started manually.

Services dependencies

Dependencies between different services are supported.

For example, one service must be started or stopped before other services.


Services-Related Work Flow

Services-related work flow of harness execution is supported in both GUI and batch mode test execution. The work flow consists of the following:

  1. The ServiceManager and Service instances are instantiated after the test suite is loaded inside harness.

  2. When the JavaTest harness object (Harness) starts a test run, information that the Harness has about the tests and groups of tests that should be run is provided to the ServiceManager.

  3. The ServiceManager filters out services that are unnecessary for the test run based on information from the Harness and information from deployed services regarding which test paths for which the service should be started.

    The services-related workflow is performed before starting a test run in the main Harness execution thread. During this process one of the following actions is taken based on the information that the harness has about the tests and groups of tests that should be run:

    • Start services as needed.

      After being notified by the Harness that a test has started, the ServiceManager determines if a particular service should be started for running the next test. This enables "lazy" service start up, which may be useful for performance reasons.

    • Start all required services now.

      Before running any tests, the ServiceManager starts all required services.

    • Start services manually.

      Service management is turned off for the test run. The Harness assumes that the user will manually start all required services.


      Note:

      When running in GUI mode, the ServiceManager functionality is enabled after the user presses the Run button which blocks the Harness execution thread until it determines how services will be started. In batch mode, the ServiceManager is functionality is enabled by using an option in the command line.


  4. The ServiceManager stops services either as it determines that a service is not required (all tests that require this service are completed) or at the end of test run.

    Stopping services after the test run finished is preferred.

Implementation

Because the ServiceManager component must serve different requirements, its service support design is as flexible as possible.To achieve this flexibility, the design uses abstract concepts of service, service description, service execution and service parameters. Some functionality, such as remote service management when services are instantiated and run on a remote host, has not been implemented. However the capability to implement the functionality is supported in the architecture. Additional functionality could be implemented by the test suite and set through the API, as is currently done in the JavaTest harness for other components.


Note:

Services support is optional, so that test suites, which do not need services support, are not required to implement additional components.


The JavaTest harness provides useful default implementations of the following interfaces:

Implementations of ServiceReader Interface

To make the process of acquiring information about services and instantiating components more flexible, a test suite should provide a special component that implements the ServiceReader interface. The ServiceReader interface reads information regarding service descriptions and tests-to-services mappings during test suite load, instantiates Service objects, and pushes the collected information into them.

The JavaTest harness provides two implementations of the ServiceReader interface:

The getServiceReader() method of the TestSuite class defines which implementation to use.

A test suite that uses PropertyServiceReader can be run by the test harness and the lite harness. A test suite that uses XMLServiceReader cannot be run by the lite harness because the lite harness does not support XML.

PropertyServiceReader File Format

The property file format is described below, an example follows.

service.id.class=...
service.id.description=...
service.id.arg.argname=...
property.general-property-name=...
testpath.tpid.path=...
testpath.tpid.ids=[space-separated list of service IDs] 

Example property file:

property.port=8080
property.testuite=path_to_testsuite

service.rmid_1.class=com.foo.bar.RMIDService
service.rmid_1.description=This is first variant of service to start rmid daemon
service.rmid_1.arg.arg1=5000
service.rmid_1.arg.arg2=path_to_testsuite

service.rmid_2.class=com.foo.bar.RMIDService
service.rmid_2.description=This is second variant of service to start rmid daemon 

service.msg_service.class=com.foo.bar2.MessagingService
service.msg_service.description=messaging service
service.msg_service.arg.timeout=1000

testpath.1.path=api/java_rmi
testpath.1.ids=ant rmid_1

testpath.2.path=api/foo_bar
testpath.2.ids=rmid_2 msg_service

XMLServiceReader File Format

The following sample provides a description of the contents and format of an XML service description file.

<Services>
<property file="local.properties"/>
<property name="port" value="8080"/>
<property name="testsuite" value="${root}/tests"/>
...................................
<service id="rmid_1" class="com.foo.bar.RMIDService" description="This is first variant of service to start rmid daemon">
<arg name="arg1" value="5000"/>
<arg name="arg2" value="${testsuite}"/>
</service>
<service id="rmid_2" class="com.foo.bar.RMIDService" description="This is second variant of service to start rmid daemon">
</service>
<service id="msg_service" class="com.foo.bar2.MessagingService" description="messaging service">
<arg name="timeout" value="1000"/>
</service> 
..................................
<testpath path="api/java_rmi">
<service refid="ant"/>
<service refid="rmid_1">
</testpath>
<testpath path="api/foo_bar">
<service refid="rmid_2"/>
<service refid="msg_service"/>
</testpath>
.....................................
</Services> 

The format of the XMLServicesReader file consists of three sections:

  • Properties - The first section specifies options and shared values that are used later in the service definition process.

    You can load property values from a file or specify them separately. Properties that do not have explicitly-set values are called parameters. Parameter values are resolved later by Service or ParamResolver classes. In the code example, root and testsuite properties are parameters. The Service interface should provide operations to get and set parameters and arguments. Service properties are described in Service Properties.

  • Services - The second section of the file describes the services.

    Services are described by using a tag. Each service specification tag contains a unique string ID that enables the user to refer to this service in the test map section, a Service interface implementation class, and description text.

  • Test Map - The third section of the file provides a map from test paths to services.

    Based on this information, ServiceManager determines, which services should be started/stopped and when. It consists of a regular expression with path-to-tests pattern and tags with references to services. One test path can refer to many services. Different test paths can refer to the same services. In case both such test paths are selected for test run, only one instance of a service will be started.

Implementation of Service Interface

The JavaTest harness provides a default implementation (AntService) of the Service interface that not only provides a description but also provides a definition and execution. The default implementation uses Ant. Each service is presented as an Ant target in a valid Ant file. In the service description XML file, a special service class (com.sun.javatest.services.AntService) describes the Ant-based service as follows:

<service id="any_uniq_id _you_want" 
class="com.sun.javatest.services.AntService"><arg name="ant.bin" value="~/apache-ant/bin/ant"/>
<arg name="ant.targets" value="rmid-target run-tests"/>
<arg name="ant.workdir" value="directory_to_start_ant_from"/>
<arg name="ant.env.JAVA_HOME" value="path to JDK"/>
<arg name="option1" value="-buildfile ${lib}/build.xml"/>
<arg name="option2" value="-verbose"/>
</service>
  • ant.bin - Specifies path to ANT execution script.

  • ant.targets - Specifies targets to execute

  • ant.workdir - Specifies the directory from which to start ant.

This is set to java.lang.ProcessBuilder using its directory() method.

  • ant.env.JAVA_HOME - The environment entry with which the process starts.

    Set to ProcessBuilder through its environment() method.

  • "option1" and "option2" - All other arguments inside the AntService are interpreted as ant start options.

    No special naming conventions are needed for them.

  • Ant-based services are the only service implementations provided by JavaTest harness. JavaTest harness provides AntService, which implements the Service interface, and AntServiceExecutor, which implements the ServiceExecutor interface.

Service Properties

All possible parameters, commands, and arguments, could be described as string key-value pairs. Such pairs should be passed to the Service object by the ServiceReader, who creates the Service objects. Each particular Service type may interpret them and pass through a connector to the ServiceExecutor.

However, not all key-value pairs or values may be known when the test suite is created. For example, some host names and port numbers may be known only when answering an Interview question before a test run. To resolve this issue, values in service descriptions are parametrized. That means, that some key-value pairs will have variable values with references to other keys, and are resolved when additional non-variable sets of properties are passed from somewhere outside (such as after interview completion).

The ServiceManager has a public API used to access to each Service and its ServiceProperties object. The non-variable set of properties may be passed at any time. However, a more convenient approach is to resolve variable properties using values obtained from the interview. These values are also key-value pairs and are stored in a special object, TestEnvironment. Harness passes this object to resolve service properties before every test run. Consequently, refer to interview question's tags to resolve variable values by interview.

A special ServiceProperties class performs this behavior. Its instance is the main part of each Service object. ServiceReader should instantiate a ServiceProperties object, fill it with information available from the service description file and pass it to the corresponding Service object. Should the test suite use the standard XMLServiceReader, the test suite developer shouldn't care about this.

Each key-value pair from the ServiceReader must use the format:

string=[string | ${string}]*

If a key has no value, it becomes a variable.

${string} represents a reference to another key. If its value has at least one reference inside it, it also becomes variable.

Example:

key1=
key2=value2
key3=value31_${key1}_value32_${key2}_value33

Later if we pass key1=value1, the expression is resolved as:

key1=value1
key2=value2
key3=value31_value1_value32_value2_value33

As described in Implementations of ServiceReader Interface, some properties are common to several objects and some are individually specified for each Service object. That is the reason why there are two namespaces for property names. One namespace is a common namespace, and the other is an individual namespace. The common namespace is shared between all Services. Consequently, a property, specified inside each particular Service, may refer to common properties. If a name of a property specified for an individual Service is contained in a common namespace, the individual property overwrites the common property.

Individual namespaces are not shared between Service objects. A property from one individual namespace cannot refer to a property from another individual namespace. If a property attempts to do this, the reference is interpreted as unknown.

When it prepare a command, a Service objects asks its ServiceProperties object to resolve its arguments. The ServiceProperties object returns a Map containing only the resolved individual properties (not the common properties). Resolved common properties may be achieved, using another method. Such division enables the Service class implementation to be simplified. It treats its individual arguments by using its own name conventions. Common properties are used to resolve individual properties or for other goals.

Service Management Architecture

The architecture of the Service Management feature of the JavaTest harness consists of five components:

Figure 11-1 illustrates the relationship between these components.

Figure 11-1 Service Management Architecture

Description of Figure 11-1 follows
Description of "Figure 11-1 Service Management Architecture"

  1. ServiceManager - Instantiated for each test suite instance.

    The same test suite, opened two times in different tabs, is interpreted as two different test suites and will have different ServiceManager objects. ServiceManager objects accomplish the following functions:

    • Are notified of all Harness events.

    • Manage a set of services.

    • Provide methods to achieve service's state, info and data to the JavaTest harness and test suite.

    • Provide methods for external configuration by JavaTest and test suite.

    • Start and stop services

    • Count which services and when should be started and stopped.

  2. Service - Root service interface.

    Service interface has two main goals:

    • Contains service information, execution parameters, and test mapping.

    • Provides start, stop, other operating methods, are invoked by ServiceManager, and are delegated through Connector to ServiceExecutor.

  3. ServiceExecutor - Root interface for the service executor.

    Method invocations from Service go through Connector and are executed by ServiceExecutor. The Service and ServiceExecutor implementation types have a 1:n relationship. Consequently, each Service implementation can have different ServiceExecutors for different situations (such as local and remote execution). ServiceExecutor implementations can execute a service as separate process, as a thread, or in any other required manner. Service and ServiceExecutor types must be coordinated to perform message exchange correctly.

  4. Connector - Interface that determines common connection methods between Service and ServiceExecutor, such as sendMessage or getOutputStream methods.

    Particular implementation should not be related with concrete Service and ServiceExecutor realizations. Connector is harness-side component, and we have no any interface for agent side part, because on those side such component (and it's incoming events) manages ServiceExecutor. Agent-side component is not under any management, so there is no need for it to have API.

  5. ParamResolver - Component, related with ServiceExecutor. Connector, that sends commands to a service and provides the parameters for this command execution.

    The parameters are decoded by ParamResolver and passed to ServiceExecutor. For example, if a connector sends "$host_name" param, it should be resolved by the ParamResolver. Implementations of ParamResolver should be interoperable with ServiceExecutor. How and what to resolve depends on the implementations of both components.

Service execution is divided into 3 components (Service, Connector, ServiceExecutor), because it must be able to implement remote services start-up and execution by any test suite. It is not possible to implement this feature directly in the JavaTest harness and its agent, as requirements from different customers vary.

Mechanism to Instantiate Service, Connector, and ServiceExecutor Interfaces

The Connector and ServiceExecutor may differ because configuration settings (such as local or remote execution) and the specific implementors are known only at the beginning of a test run. The ServiceManager should have a public API to enable any object (such as a test suite) to specify non-default Connectors for each service.

The Service interface has a method that returns an instance of the default ServiceExecutor, which will be used should service be run in the same VM as the JavaTest harness. This executor interoperates with the pseudo LocalConnector, which directly delegates requests to this executor by invoking its methods. If a test suite wants to execute a service in another way, before starting test run, it should set another Connector for this Service (through the ServiceManager by using the Service's ID). This Connector may be directly associated with a ServiceExecutor (as LocalConnector does it), or it can be a network connector, and send messages to a remote agent.

Separate Services Start Up

To simplify service start-up (in case there is no remote environment or you don't want to use the JavaTest harness to run the Service management feature), a separate entry point is available inside the JavaTest harness, such as ServicesMain, that performs the following operations:

  1. Takes the test suite path as input.

  2. Instantiates all found Services, ServiceExecutors, and local Connectors.

  3. Invokes the Service.start methods.

Services are unmanageable in this case and must be stopped by shutdown hook. Figure 11-2 illustrates the sequence of performing a separate services start-up.

Figure 11-2 Separate Service Start-Up

Description of Figure 11-2 follows
Description of "Figure 11-2 Separate Service Start-Up"