Java Platform Debugger Architecture Service Provider Interfaces

Java Platform Debugger Architecture Service Provider Interfaces

The service provider interfaces in the Java Debug Interface (JDI) enable the Connector and TransportService implementations to be developed and deployed. The TransportService class represents the underlying transport service used by a Connector and is used to establish connections and transport Java Debug Wire Protocol (JDWP) packets between a debugger and a target VM.

In addition to service provider interfaces in JDI, the JDK also includes a transport library interface called the Java Debug Wire Protocol Transport Interface (jdwpTransport). This is a native (C/C++) interface to allow the development and deployment of transport libraries. A transport library is loaded by the debuggee-side JDWP agent and is used to establish a connection to the debugger and to transport JDWP packets between the debugger and the VM.

This page describes a number of scenarios where the new interfaces may be used. It also provides an overview of the tasks involved in developing and deploying new Connectors and transport implementations.


Example Scenarios

The service provider interfaces and the native transport interface are expected to be used by the following classes of users:

Given the above class of user the following describes a number of scenarios where the new interfaces might be used.


Developing a Connector

Developing a Connector involves creating a concrete implementation of a LaunchingConnector, AttachingConnector, or ListeningConnector.

Every Connector implementation must have a public no-arg constructor in addition to implementing all the Connector methods. The constructor will be called by the VirtualMachineManager during initialization. The constructor may be a no-op or it may perform initialization tasks such as loading a transport service. The constructor does not throw any checked exceptions so any problems during initialization should be thrown as an Error or other unchecked exceptions.

Connectors are not required to use a TransportService. Some Connectors may connect to the target VM using a mechanism other than a transport (in the Example Scenarios section we list the examples of AttachingConnectors that attach to crash dumps or hung processes). For Connectors that use a TransportService implementation the Connector can either reference the TransportService implementation directly or it can load the implementation at runtime. Connectors that wish to make use of Oracle-provided transports should load the transport service using code such as following:

try {
    Class<?> c = Class.forName("com.sun.tools.jdi.SocketTransportService");
    ts = (TransportService)c.newInstance();
} catch (Exception x) {
    throw new Error(x);
}

Java SE implementations are not required to include Oracle's socket or shared memory transport service implementations so in the above example an Error will be thrown if the transport service does not exist.

Assuming that the type of Connector is known then the following items should be noted when developing the implementation:

    VirtualMachine vm = Bootstrap.virtualMachineManager().createVirtualMachine(conn);
\
\
The `VirtualMachineManager` also involves another form of the
`createVirtualMachine` method for use by `LaunchingConnector` instances.
The other form allows the `LaunchingConnector` to specify the
`java.lang.Process` that represents the debuggee. See the specification for
`com.sun.jdi.VirtualMachineManager` for further details.

Deploying a Connector

To deploy a Connector requires packaging the classes for the Connector implementation into a jar file along with a service configuration file that lists the fully-qualified class name of the Connector. The jar file is then deployed in a location that is visible to the system class loader.

The service configuration file that must be included in the jar file is named META-INF/services/com.sun.jdi.connect.Connector. The file simply lists the fully-qualified class names of the Connector included in the jar file. Multiple Connector implementations may be included in the same jar file and in that case the class name for each Connector is listed on a separate line.

Suppose you wish to deploy a launching connector named SimpleLaunchingConnector. In order to deploy this you create a file META-INF/services/com.sun.jdi.connect.Connector similar to the following:

# A very simple launching connector
SimpleLaunchingConnector

This service configuration file is then packaged into a jar file along with the classes that comprise the implementation of the Connector:

jar cf SimpleLaunchingConnector.jar \
    META-INF/services/com.sun.jdi.connect.Connector \
    SimpleLaunchingConnector.class

The jar file is then copied into a location that is visible to the system class loader.

Once deployed the Connector will be located by the debugger when the debugger is restarted. That is, SimpleLaunchingConnector will be included in the list of Connectors returned by VirtualMachineManager's allConnectors() method. In addition, as this is a launching connector, it will also appear on the list of launching connectors returned by the launchingConnectors() method.


Developing a TransportService

Developing a transport service requires developing two components :

The development of a transport service requires a high degree of familiarity with the transport and the underlying communication protocol. A transport service essentially binds JDWP to an underlying communication protocol. The service that it provides is reliable and JDWP packets are exchanged between the debugger and debuggee without duplication or data loss. Given that packets must be exchanged in a reliable manner might mean that additional protocol support be included in the transport service over and beyond that provided by the underlying communication protocol. For example, if debugging over a raw and unreliable serial connection is required, then the transport service implementer may be required to build in error detection and recovery in the implementation to ensure that JDWP packets can be reliably transported between the debugger and the debuggee.

Assuming the details of the transport and underlying communication protocol are understood then the next step is to consider the following:

    /dev/ttya;9600,1

Once the above have been resolved then creating a TransportService involves extending com.sun.jdi.connect.spi.TransportService and providing an implementation. The attach and accept methods return an instance of com.sun.jdi.connect.spi.Connection so an implementation of Connection is required so that the debugger can exchange JDWP packets with the debuggee.

To develop a native transport library requires implementing each of the functions listed in the jdwpTransport specification. The function prototypes and definitions are defined in ${java_home}/include/jdwpTransport.h.

The transport library implementer compiles and links the function implementations into a dynamic library (or equivalent). The library exports a function jdwpTransport_OnLoad function which the JDWP agent will call when the transport library is loaded. Some embedded environments don't support dynamic linking and in such environments the transport library may need to be statically linked. In that case there isn't any library loading but the jdwpTransport_OnLoad function will still be called to initialize the transport library and obtain the environment pointer.


Deploying a TransportService

A TransportService is deployed in a similar manner to a Connector. To deploy a TransportService requires packaging the classes for the TransportService implementation into a jar file along with a service configuration file that lists the fully-qualified class name of the TransportService. The jar file is then deployed in a location that is visible to the system class loader.

The service configuration file that must be included in the jar file is named META-INF/services/com.sun.jdi.connect.spi.TransportService. As per Connector deployment the configuration file may list the classes names of multiple transport service implementations in the event that the jar file includes multiple implementations.

In the case of a transport service com.sun.SerialTransportService then the service configuration file will be similar to the following :

# Serial line transport
com.foo.SerialTransportService

This service configuration file is then packaged into a jar file along with the classes that comprise the implementation. For this example we will assume that the implementation involves a number of classes :

jar cf SerialTransportService.jar \
    META-INF/services/com.sun.jdi.connect.spi.TransportService \
    com/foo/SerialTransportService.class \
    com/foo/SerialConnection.class \
    com/foo/SerialCapabilities.clas \
    com/foo/SerialIO.class \
    com/foo/SerialProtocol.class

As per the deployment of Connectors, the jar file is then copied into a location that is visible to the system class loader.

A TransportService may have native methods or rely on other APIs that require a native library. In that case the native library must be location that allows it be loaded using System.loadLibrary.

A native transport library is loaded by the JDWP agent. In must be located on the normal runtime library search path for the operating systems. For example, on Linux systems it must be on search path specified by the LD_LIBRARY_PATH environment variable.