JavaTest Harness Architect's Guide, JavaTest Harness 4.5 for the Java Platform E20663-03 |
|
Previous |
Next |
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:
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:
|
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 of harness execution is supported in both GUI and batch mode test execution. The work flow consists of the following:
The ServiceManager
and Service
instances are instantiated after the test suite is loaded inside harness.
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
.
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 |
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.
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:
Service
- Interface describing a service.
Service
objects should be instantiated by ServiceReader
. The particular implementation class should be specified so that the ServiceReader
can access it. A default implementation, AntService
(see Implementation of Service
Interface) is provided by the JavaTest harness. Such a service is the Ant target in provided ant xml
file. The benefit of such a service representation is that the service can easily be started without running the JavaTest harness.
ServiceReader
- Interface responsible for reading service definitions and parameters.
The implementation should be provided by the test suite. A default implementation, PropertyServiceReader
(see Implementations of ServiceReader
Interface), is provided by the JavaTest harness. This implementation reads Service
type definitions and start parameters of each particular instance, as well as maps test paths to service instances from a single property file.
ServiceConnector
and ServiceExecutor
- ServiceConnector
is responsible for the connection between harness representative (Service
interface) and ServiceExecutor
is responsible for responsible for running service in case of remote execution.
Each executor type is related to a respective service type, such as AntService
and AntServiceExecutor
(see Implementation of Service
Interface). Connector
doesn't depend on the Service
and ServiceExecutor
type. Connector
has a unified interface and any Connector
implementation should work with any Service
- ServiceExecutor
pair. The JavaTest harness only provides a pseudo local connector that redirects requests to a ServiceExecutor
working in the same VM. The following are available types of ServiceExecutor
:
ServiceReader
InterfaceTo 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:
PropertyServiceReader
is the default implementation. It looks for information in a property file (see PropertyServiceReader File Format).
XMLServiceReader
looks for information in an XML file (see XMLServiceReader File Format).
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.
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
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.
Service
InterfaceThe 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.
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.
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
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.
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
.
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.
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.
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.
Service
, Connector
, and ServiceExecutor
InterfacesThe 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.
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:
Takes the test suite path as input.
Instantiates all found Services
, ServiceExecutors,
and local Connectors.
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.