40 Developing Custom Transport Providers

This chapter describes the basic steps involved in developing a custom transport provider. The Transport SDK provides a layer of abstraction between transport protocols and the Service Bus runtime system. This layer of abstraction makes it possible to develop and plug in new transport providers to Service Bus. The Transport SDK interfaces provide this bridge between transport protocols, such as HTTP, and the Service Bus runtime.

Tip:

Before beginning this chapter, be sure to review Learning About Custom Transport Providers..

This chapter includes the following sections:

40.1 Development Road Map

The process of designing and building a custom transport provider is complex. This section offers a recommended path to follow as you develop your transport provider. Development of a custom transport provider breaks down into three basic stages: Planning, developing, and packaging and deploying.

40.1.1 Planning

Perform the following planning steps before developing a custom transport provider.

  1. Decide if you really need to develop a custom transport provider. See Determining Whether to Develop a Custom Transport Provider
  2. Run and study the example socket transport provider. The source code for this provider is installed with Service Bus and is publicly available for you to examine and reuse. See Creating a Sample Socket Transport Provider.
  3. Review Learning About Custom Transport Providers. This chapter discusses the architecture of a transport provider and many aspects of transporter provider design, such as the security model and the threading model employed by transport providers.
  4. Review Before You Begin.

40.1.2 Developing

Custom transport development steps include creating required artifacts, such as XML schema files, configuration components, and user interfaces. Basic Development Steps describes steps you need to take to develop a transport provider. Before developing your custom transport, review Important Development Topics, which discusses several topics that you might need to refer to during the development cycle, such as message and error handling and transforming messages.

40.1.3 Packaging and Deploying

For detailed information on packaging and deploying a transport provider, see Packaging and Deploying a Custom Transport Provider.

40.2 Before You Begin

There are several design considerations to take into account before you begin to develop a custom transport provider, including the following:

40.3 Basic Development Steps

Below are the basic steps to follow when developing a custom transport provider:

Step1. Review the Transport Framework Components

Step 2. Create a Directory Structure for Your Transport Project

Step 3. Create an XML Schema File for Transport-Specific Artifacts

Step 4. Define Transport-Specific Artifacts

Step 5. Define the TransportProviderConfiguration XMLBean

Step 6. Implement the Transport Provider User Interface

Step 7. Implement the Runtime Interfaces

Step 8. Package and Deploy the Transport Provider

40.3.1 Step1. Review the Transport Framework Components

Figure 40-1 illustrates the components that you must implement and configure to create a custom transport provider. The transport manager controls and manages the registration of transport providers and handles communication with Service Bus. A transport provider manages the life cycle and runtime behavior of transport endpoints (resources where messages originate or are targeted). You use the Transport SDK to develop custom transport providers.

Figure 40-1 Transport Subsystem Overview

Description of Figure 40-1 follows
Description of "Figure 40-1 Transport Subsystem Overview"

The parts of the transport subsystem that you must implement and configure include the following:

40.3.2 Step 2. Create a Directory Structure for Your Transport Project

Before developing a new transport provider, take time to set up an appropriate directory structure for your project. The recommended approach is to copy the directory structure used for the sample socket transport provider. For a detailed description of this structure, see Sample Location and Directory Structure .

40.3.3 Step 3. Create an XML Schema File for Transport-Specific Artifacts

Create an XML schema (XSD) file for transport-specific definitions. You can base this file on the schema file developed for the sample socket transport provider: OSB_ORACLE_HOME/samples/servicebus/sample-transport/schemas/SocketTransport.xsd

Note:

The SocketTransport.xsd file imports the file TransportCommon.xsd. This file is the base schema definition file for service endpoint configurations. This file is located in OSB_ORACLE_HOME/lib/modules/oracle.servicebus.kernel-api.jar. You might want to review the contents of this file before continuing.

40.3.4 Step 4. Define Transport-Specific Artifacts

Define XML schemas for the following transport-specific artifacts in the XML schema file described in the previous section, Step 3. Create an XML Schema File for Transport-Specific Artifacts.

  • EndpointConfiguration

  • RequestMetaDataXML

  • ResponseMetaDataXML

Each of these schema definitions is converted into a corresponding Java file and compiled. Once you build the sample socket transport provider, you can find examples of these converted Java source files in OSB_ORACLE_HOME/samples/servicebus/sample-transport/build/classes/com/bea/alsb/transports/sock/impl. Only simple XML types are supported when defining metadata and headers specific to the transport provider. For example, complex types with nested elements are not supported. Furthermore, there can be at most one header with a given name.

40.3.4.1 EndPointConfiguration

EndPointConfiguration is the base type for endpoint configuration, and describes the complete set of parameters necessary for the deployment and operation of an inbound or outbound endpoint. This configuration consists of generic and provider-specific parts. For more information on the EndPointConfiguration schema definition, refer to the documentation elements in the TransportCommon.xsd file.

You need to specify a provider-specific endpoint configuration in the schema file. The following example shows an excerpt from the SocketTransport.xsd.

Example - Sample SocketEndPointConfiguration Definition

<xs:complexType name="SocketEndpointConfiguration">
    <xs:annotation>
      <xs:documentation>
        SocketTransport - specific configuration
      </xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:choice>
        <xs:element name="outbound-properties"
                    type="SocketOutboundPropertiesType"/>
        <xs:element name="inbound-properties"
                    type="SocketInboundPropertiesType"/>
      </xs:choice>
      <xs:element name="request-response" type="xs:boolean">
        <xs:annotation>
          <xs:documentation>
            Whether the message pattern is synchronous
            request-response or one-way.
          </xs:documentation>
        </xs:annotation>
      </xs:element>
...

40.3.4.2 RequestMetaDataXML

Each transport provider must store metadata (message headers) in a Plain Old Java Object (POJO) and pass that to the pipeline. Examples of information that might be transmitted in the metadata are the Content-Type header, security information, or locale information. A RequestMetaData POJO is a generic object that extends the RequestMetaData abstract class and describes the message metadata of the incoming or outgoing request. The transport provider must deliver the message metadata to the Service Bus runtime in a RequestMetaData POJO. For additional information, see Request and Response Metadata Handling.

RequestMetaDataXML is an XML representation of the same RequestMetaData POJO. This XML representation uses Apache XML bean technology. It is only needed by the Service Bus runtime when processing of the message involves any actions in the pipeline that need an XML representation of the metadata, such as setting the entire metadata to a specified XML fragment on the outbound request.

You must specify request metadata configuration in the schema file. The following example shows an excerpt from the SocketTransport.xsd.

Example - Sample SocketRequestMetaDataXML Definition

<xs:complexType name="SocketRequestMetaDataXML">
    <xs:annotation>
      <xs:documentation/>
    </xs:annotation>
    <xs:complexContent>
      <xs:extension base="ts:RequestMetaDataXML">
        <xs:sequence>
          <xs:element name="client-host"
                      type="xs:string" minOccurs="0">
            <xs:annotation>
              <xs:documentation>
                Client host name
              </xs:documentation>
            </xs:annotation>
          </xs:element>
          <xs:element name="client-port" type="xs:int" minOccurs="0">
            <xs:annotation>
              <xs:documentation>Client port</xs:documentation>
            </xs:annotation>
          </xs:element>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

40.3.4.3 RequestHeadersXML

RequestHeadersXML is the base type for a set of inbound or outbound request headers. You need to specify the RequestHeadersXML configuration in the schema file. The following example shows an excerpt from the SocketTransport.xsd.

Example - Sample SocketRequestHeadersXML Definition

<xs:complexType name="SocketRequestHeadersXML">
    <xs:annotation>
      <xs:documentation/>
    </xs:annotation>
    <xs:complexContent>
      <xs:extension base="ts:RequestHeadersXML">
        <xs:sequence>
          <xs:element name="message-count" type="xs:long" minOccurs="0">
            <xs:annotation>
              <xs:documentation>
                Number of messages passed till now.
              </xs:documentation>
            </xs:annotation>
          </xs:element>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

40.3.4.4 ResponseMetaDataXML

ResponseMetaDataXML is the base type for metadata for a response to an inbound or outbound message. You need to specify the ResponseMetaDataXML configuration in the schema file. The following example shows an excerpt from the SocketTransport.xsd.

Example - Sample SocketResponseMetaDataXML Definition

<xs:complexType name="SocketResponseMetaDataXML">
    <xs:complexContent>
      <xs:extension base="ts:ResponseMetaDataXML">
        <xs:sequence>
          <xs:element name="service-endpoint-host"
                      type="xs:string" minOccurs="0">
            <xs:annotation>
              <xs:documentation>
                Host name of the service endpoint connection.
              </xs:documentation>
            </xs:annotation>
          </xs:element>
          <xs:element name="service-endpoint-ip"
                      type="xs:string" minOccurs="0">
            <xs:annotation>
              <xs:documentation>
                IP address of the service endpoint connection.
              </xs:documentation>
            </xs:annotation>
          </xs:element>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

40.3.4.5 ResponseHeadersXML

ResponseHeadersXML is the base type for a set of response headers. You need to specify the ResponseHeadersXML configuration in the schema file. The following example shows an excerpt from the SocketTransport.xsd.

Example - Sample SocketResponseHeadersXML Definition

<xs:complexType name="SocketResponseHeadersXML">
    <xs:annotation>
      <xs:documentation/>
    </xs:annotation>
    <xs:complexContent>
      <xs:extension base="ts:ResponseHeadersXML"/>
    </xs:complexContent>
  </xs:complexType>

40.3.5 Step 5. Define the TransportProviderConfiguration XMLBean

To configure the TransportProviderConfiguration XML bean, edit the transport provider configuration file. This XML file is located in the resources directory in the sample-transport directory. Configure the file according to the following guidelines:

  • If proxy services can use your transport, set the inbound-direction-supported element to true.

  • If business services use your transport, set the outbound-direction-supported element to true.

  • If your transport is self-described, include an element self-described with the value set to true. A self-described transport is one whose services are responsible for describing their shape (schema or WSDL file) based on their endpoint configuration.

  • If you want to publish a tModel for your transport to a UDDI registry, include an element UDDI. See About Publishing Proxy Services to a UDDI Registry for more info.

    Tip:

    The schema for TransportProviderConfiguration is defined in TransportCommon.xsd, which is located in OSB_ORACLE_HOME/lib/servicebus-schemas.jar. Refer to the schema file for more information.

40.3.6 Step 6. Implement the Transport Provider User Interface

When you add a business or proxy service using the Oracle Service Bus Console, you select a transport provider in the service creation wizard. The wizard includes the transport providers that are provided with Service Bus and any custom transport providers that were developed with the Transport SDK. When you configure a business or proxy service, properties specific to the transport being used appear in the editor. These are all part of the user interface you need to develop for the custom transport provider.

This section discusses the Transport SDK API components that bind your custom transport provider to the Oracle Service Bus Console user interface. You must implement these APIs to connect your provider to the user interface.

Tip:

This section assumes that you are familiar with the service creation wizards and the service definition editors. See Creating a Socket Transport Sample Project, for a detailed, illustrated example.

Transport Configuration Processing Flow

  1. When users create a new service, they must select an appropriate transport provider on the service creation wizard. They then also select a service type, such as SOAP, WSDL-based, XML, or messaging. To validate the selection, the wizard calls the following method of the TransportUIBinding interface:
    public boolean isServiceTypeSupported(BindingTypeInfo binding)
    

    This method determines if the transport provider is suitable for the selected service type.

  2. Users then enter an endpoint URI. To validate this URI, the wizard calls the following method of the TransportUIBinding interface:
    public TransportUIError[] validateMainForm(TransportEditField[] fields)
    
  3. Once you create a service, the console displays the service definition editor, which includes a transport-specific configuration page. To render this page, the editor calls the following method of the TransportUIBinding interface:
    public TransportEditField[] getEditPage(EndPointConfiguration config, BindingTypeInfo binding) throws TransportException 
    

    The Transport SDK offers a set of TransportUIObjects that represent fields on the configuration page. For example, you can add text boxes, check boxes, and other types of UI elements. Use the TransportUIFactory to create them, and then use the same factory to specify additional properties and obtain TransportEditField objects that can be displayed on the service definition editors.

    Tip:

    You can associate events with most of the UI fields. An event acts like a callback mechanism for the TransportUIBinding class and lets you refresh, validate, and update the configuration page. When an event is triggered, the wizard calls the following method:

    updateEditPage(TransportEditField[] fields, String name) throws TransportException 
    
  4. When users complete the transport configuration, the editor calls the validation method:
    TransportUIError[] validateProviderSpecificForm(TransportEditField[] fields)
    
  5. After the service is saved, the transport manager calls the following method of the TransportProvider class:
    void validateEndPointConfiguration(TransportValidationContext context)
    

    If no error is reported, a new endpoint is created. The transport manager then calls the following method:

    TransportEndPoint createEndPoint(EndPointOperations.Create context) throws TransportException
    

    If this method returns successfully, the new service is listed and the underlying transport configuration is associated with an endpoint on the TransportProvider.

    Note:

    The endpoint configuration is saved in the Service Bus session and does not need to be persisted or recovered in case of a server restart by the transport provider.

  6. Once the session is activated, you must deploy the endpoint to start processing requests. To learn more about deploying an endpoint and processing requests, see When to Implement TransportWLSArtifactDeployer and Deploying to a Cluster..

    Tip:

    For the sample socket transport provider, you can find the implementations of these interfaces in the sample-transport/src directory.

40.3.7 Step 7. Implement the Runtime Interfaces

A new custom transport provider must implement certain runtime interfaces. For a summary of the Transport SDK interfaces and related classes, see Transport SDK Interfaces and Classes. For detailed information on interfaces and classes, see the Java API Reference for Oracle Service Bus.

Tip:

For the sample socket transport provider, you can find the implementations of these interfaces in the sample-transport/src directory.

You must implement the following interfaces when developing a custom transport provider:

  • TransportProvider

  • TransportWLSArtifactDeployer

  • TransportEndPoint

  • InboundTransportMessageContext

  • OutboundTransportMessageContext

  • Transformer

    Note:

    • Only implement the TransportWLSArtifactDeployer interface if the transport provider needs to deploy WebLogic Server-related artifacts, such as EAR, WAR, and JAR files, that go into a WebLogic Server change list at the time of endpoint creation. For more information, see When to Implement TransportWLSArtifactDeployer.

    • Only implement the Transformer interface if the transport provider needs to work with non-standard payload bindings, for example, anything other than Stream, DOM, SAX, or XMLBean. For more information, see Transforming Messages .

40.3.8 Step 8. Package and Deploy the Transport Provider

For information about this process, see Packaging and Deploying a Custom Transport Provider.

40.4 Important Development Topics

This section discusses several topics that you should consider when developing a custom transport provider, including message and error handling, message transformation, transport options, environment values, UDDI registries, and so on.

40.4.1 Handling Messages

The Transport SDK features a flexible representation of message payloads. All Transport SDK APIs dealing with payload use the Source interface to represent message content.

The Source-derived message types provided with the Transport SDK include:

  • StreamSource

  • ByteArraySource

  • StringSource

  • XmlObjectSource

  • DOMSource

  • MFLSource

  • SAAJSource

  • MimeSource

    Note:

    StreamSource is a single use source; that is, it implements the marker interface SingleUseSource. With the other Sources, you can get the input stream from the source multiple times. Each time the Source object gets the input stream from the beginning. With a SingleUseSource, you can only get the input stream once. Once the input is consumed, it is gone (for example, a stream from a network socket); however, Service Bus buffers the input from a SingleUseSource, essentially keeping a copy of all of its data.

    If you implement a Source class for your transport provider, you need to determine whether you can re-get the input stream from the beginning. If the nature of the input stream is that it can only be consumed once, your Source class should implement the marker interface SingleUseStream.

The Transport SDK provides a set of transformers to convert between source objects. You can implement new transformations, as needed, as long as they support transformations to and from a set of canonical representations. For more information, see Transforming Messages and Designing for Message Content .

40.4.1.1 Sending and Receiving Message Data

When implementing inbound endpoints to deliver the inbound message to the Service Bus runtime, you need to call TransportManager.receiveMessage(). The transport provider is free to expose the incoming message payload in either one of the standard source-derived objects, such as stream, DOM or SAX, or a custom one.

If Service Bus needs to send a response message back to the client that sent the request, it will call methods setResponseMetaData() and setResponsePayload() followed by close() on InboundTransportMessageContext to indicate that the response is ready to be sent back. When the Service Bus runtime calls the inbound transport message context close() method, this is done from a different thread than that on which the inbound request message was received. The transport provider should be aware of this because it may affect the semantics of transactions. Also, the transport provider cannot attempt to access the response payload or metadata until close() has been called.

40.4.1.2 Request and Response Metadata Handling

Each transport provider must store metadata and headers in a Plain Old Java Object (POJO) and pass that to the pipeline. There are some cases where Service Bus requires an XMLBean. In these cases, you need to implement a conversion from POJO to XMLBean using the API.

You must provide the following methods to convert from a POJO to XML:

RequestHeaders.toXML()

RequestMetaData<T>.toXML()

ResponseHeaders.toXML()

ResponseMetaData<T>.toXML()

For the reverse direction (XML to POJO) you need to implement:

TransportEndPoint.createRequestMetaData(RequestMetaDataXML)

InboundTransportMessageContext.createResponseMetaData(ResponseMetaDataXML)

40.4.1.3 Character Set Encoding

Each transport provider is responsible for specifying the character set encoding of the incoming message payload to Service Bus. For outgoing messages (outbound request and inbound response), the transport provider is responsible for telling Service Bus what character set encoding to use for the outgoing payload. The character-set encoding is specified in request and response metadata.

In virtually every case, the character-set encoding that the transport is responsible for inserting into the metadata is exactly the encoding that is statically specified in the service configuration. One of the few exceptions to this is HTTP transport, which inspects Content-Type for any "charset" parameters and overrides any encoding configured in the service. This is necessary in order to conform to HTTP specifications. Other transport protocols may need to handle similar issues.

Tip:

In general, the encoding for a service is fixed. If someone sends a UTF-16 encoded message to a proxy that is specified to be SHIFT_JIS, then that is generally considered to be an error. Transport providers should not need to inspect the message simply to determine encoding.

For outgoing messages, the transport provider tells Service Bus what encoding it requires for the outbound request, and Service Bus performs the conversion if necessary.

Transports should always rely on this encoding for outgoing messages and should not assume that it is the same as the encoding specified in the service configuration. If there is a discrepancy, the transport can choose to allow it, but others could consider it an error and throw an exception. Also the transport has the additional option of leaving the encoding element blank. That leaves the pipeline free to specify the encoding (for example, using pass-through).

40.4.1.4 Co-Located Calls

If a given transport provider supports proxy service endpoints, you can configure the request pipeline such that there is a routing step that routes to that provider's proxy service. Furthermore there could be a Publish or a Service Callout action that sends a message to a proxy service instead of a business service. This use case is referred to as co-located calls.

The transport provider needs to be aware of co-located calls, and handle them accordingly. Depending on the nature of the proxy service endpoint implementation, the transport provider may choose to optimize the invocation such that this call bypasses the entire transport communication stack and any inbound authentication and authorization, and instead is a direct call that effectively calls TransportManager.receiveMessage() immediately.

Tip:

Service Bus has implemented this optimization with the HTTP, File, Email and FTP transport providers. The JMS provider does not use this optimization due to the desire to separate the transactional semantics of send operation versus receive operations.

If you want to use this optimization in a custom transport provider, you need to extend the CoLocatedMessageContext class and call its send() method when TransportProvider.sendMessageAsync() is invoked.

40.4.1.5 Returning Outbound Responses to the Service Bus Runtime

When the Service Bus runtime sends a message to an outbound endpoint and there is a response message to be returned, the transport provider must return this response asynchronously. That means TransportSendListener.onReceiveResponse() or TransportSendListener.onError() methods need to be called from a different thread than the one on which TransportProvider.sendMessageAsync() was called.

If the transport provider has a built-in mechanism by which the response arrives asynchronously, such as responses to JMS requests or HTTP requests when the async response option is used, it happens naturally. However, if the transport provider has no built-in mechanism for retrieving responses asynchronously, it can execute the outbound request in a blocking fashion and then schedule a new worker thread using the TransportManagerHelper.schedule() method, in which the response is posted to the TransportSendListener.

40.4.2 Transforming Messages

When Service Bus needs to set either the request payload to an outbound message or the response payload to an inbound message, it asks the transport provider to do so through an object derived from the Source interface. The transport provider then needs to decide what representation the underlying transport layer requires and use the Transformer.transform() method to translate the Source object into the desired source.

Tip:

For more information on message transformation, see Designing for Message Content . For a list of built-in transformations, see Built-In Transformations and Source and Transformer Classes and Interfaces.

A custom transport provider can support new kinds of transformations. Suppose a transport provider needs to work with a DOM object in order to send the outbound message. When called with setRequestPayload(Source src), the transport provider needs to call the method:

TransportManagerHelper.getTransportManager().getTransformer().
transform(src, DOMSource.class, transformOptions)

The return value of the method gives a DOMSource, which can then be used to retrieve the DOM node.

Note:

If the transport provider requires a stream, there is a shortcut: each Source object supports transformation to stream natively.

You can add new transformations to a custom transport provider. For example, suppose you want to add a new kind of Source-derived class, called XYZSource. For performance reasons, transport providers are encouraged to provide conversions from XYZSource to one of the two canonical Source objects, XmlObjectSource and StreamSource when applicable. Without such transformation, generic transformers are used, which rely on the StreamSource representation of XYZSource. Of course, if XYZSource is a simple byte-based Source with no internal structure, then relying on the generic transformers is usually sufficient. Note that any custom transformer that is registered with TransportManager is assumed to be thread-safe and stateless.

To support attachments, the transport provider has the following three options:

  • The Source returned by TransportMessageContext must be an instance of MessageContextSource. A limitation of this option is that MessageContextSource requires that the content has already been partitioned into a core-message Source and an attachments Source.

  • The Source is an instance of MimeSource and the Headers objects contain a multipart Content-Type header.

  • The Content-Type is a pre-defined header for the transport provider with the specific value multipart/related. Both HTTP and email transports rely on this third option for supporting attachments.

40.4.3 Working with TransportOptions

A TransportOptions object is used to supply options for sending or receiving a message. A TransportOptions object is passed from the transport provider to the transport manager on inbound messages. On outbound messages, a TransportOptions object is passed from the Service Bus runtime to the transport manager, and finally to the transport provider.

40.4.3.1 Inbound Processing

The transport provider supplies the following parameters to TransportManager.receiveMessage():

  • QOS: Specifies exactly-once or best-effort quality of service. Exactly-once quality of service is specified when the incoming message is transactional.

  • Throw On Error: If this flag is set, an exception is thrown to the callee of method TransportManager.receiveMessage() when an error occurs during the Service Bus pipeline processing. The options for throwing the exception include: throw the exception back to the inbound message or create a response message from the error and notify the inbound message with the response message. Typically, you set Throw On Error to true when QOS is exactly-once (for transactional messages).

    For example, JMS/XA sets this flag to true to throw the exception in the same request thread, so it can mark the exception for rollback. HTTP sets the flag to false, because there is no retry mechanism. The error is converted to a status code and a response message is returned.

  • Any transport-specific opaque data: Opaque data can be any data that is set by the transport provider and passed through the pipeline to the outbound call. This technique optimizes performance when the same transport is used on inbound and outbound. The opaque data is passed directly through the pipeline from the inbound transport to the outbound transport. For example, the HTTP transport provider can pass the user name and password directly from the inbound to the outbound to efficiently support identity pass-through propagation.

40.4.3.2 Outbound Processing

For outbound processing, the Service Bus runtime supplies parameters to the transport manager, which uses some of the parameters internally and propagates some parameters to TransportProvider.sendMessageAsync(). These parameters include the following:

  • QOS: Specifies whether or not exactly-once quality of service can be achieved. For example, for HTTP, if quality of service is set to exactly once, the HTTP call is blocking. If it is set to best effort, it is a non-blocking HTTP call.

  • Mode: Specifies one-way or request response. For more information, see Transport Provider Modes.

  • URI, Retry Interval, and Count: The transport provider uses the URI to initialize the outbound transport connection. For example, the HTTP transport provider uses the URI when instantiating a new HttpURLConnection. The transport provider is not required to use retry interval and count.

  • OperationName: The transport provider can use OperationName if it needs to know what outbound web service is being used. The transport manager uses this parameter to keep track of monitoring statistics.

  • Any transport-specific opaque data: An example of transport-specific opaque data is the value of the Authorization header for HTTP.

40.4.3.3 Request Mode

The request mode is defined as an enumeration with two values: REQUEST_ONLY (also called one-way) and REQUEST_RESPONSE. These modes are interpreted as follows for requests and responses:

  • On outbound requests, the pipeline indicates the mode through TransportOptions and the transport provider must honor the mode.

  • On inbound requests, the pipeline knows the mode and closes the inbound request and does not send a response if it computes the mode REQUEST_ONLY.

  • If a response is sent by the pipeline, then there is a response even if the response is empty.

  • For transports that are inherently one-way, the transport must not specify response metadata.

40.4.4 Handling Errors

There are three different use cases to consider with respect to the effect runtime exceptions have on the transactional model. The use cases are:

  • The exception occurs somewhere in the request pipeline but before the outbound call to the business service.

  • The exception occurs during the business service call.

  • The exception occurs sometime after the business service call in the response pipeline.

40.4.4.1 Case 1: The Exception Occurs Before the Outbound Call

In this case, the exception occurs somewhere in the request pipeline but before the outbound call to the business service, as shown in Figure 40-2. For example, executing a specific XQuery against the contents of the request message raises an exception.

If there is a user-configured error handler configured for the request pipeline, the error is handled according to the user configuration. Otherwise, the proxy service either catches an exception when calling TransportManager.receiveMessage() or is notified in the InboundTransportMessageContext.close() method of the error through response metadata, based on the transport options passed as an argument to the receiveMessage() call. If the proxy service indicates that the exception should be thrown, surround receiveMessage() with a try/catch clause and mark the transaction for rollback.

40.4.4.2 Case 2: The Exception Occurs During the Outbound Call

In this case, exception occurs during the business service call, as shown in Figure 40-3. The outbound transport provider does one of the following:

  • Throws an exception from TransportProvider.sendMessageAsync(). For example, the outbound provider throws an exception if there was an error while establishing a socket connection to external service. This situation could occur if the business service cannot be called because of an incorrect URL, a faulty connection, or other reasons. In these cases, the transport provider must raise an exception.

  • Notifies the listener through TransportSendListener.onError(). For example, if the business service was called, but the call resulted in an error (such as a SOAP fault), the transport provider needs to call TransportSendListener.onError() instead of raising an exception.

In the first instance, the exception handling is the same as that described in Case 1: The Exception Occurs Before the Outbound Call. In the second instance, if there is an error handler configured for the response pipeline, the error is handled according to the user configuration. Otherwise, the exception is propagated back to the proxy service endpoint in InboundTransportMessageContext.close() through the response metadata.

40.4.4.3 Case 3: The Exception Occurs After the Outbound Call

In this case, the exception occurs sometime after the business service call in the response pipeline, as shown in Figure 40-4. Again, in the absence of a user-defined error handler for the response pipeline, the proxy service endpoint is notified of the error with the InboundTransportMessageContext.close() method with appropriate response metadata. If the inbound transport endpoint is a synchronous transactional endpoint, it is guaranteed that the transaction that was active at the time request was received is still active and it may be rolled back. If the inbound endpoint is not transactional or not synchronous, there is not an inbound transactional context to roll back, so some other action might need to be performed.

40.4.4.4 Catching Application Errors

When business services try to access an external service and an error occurs in the external service application, such as a SOAP fault, subsequent retries by the services are likely to produce errors until the application is fixed. Service Bus lets you identify application errors, giving you the option of preventing retries on application errors when your transport is used.

This section describes how to catch application errors in your transport and configure your transport to prevent application error retries.

40.4.4.4.1 Identifying Application Errors

In your transport provider code you must identify the conditions under which an application error occurs. For example, in the HTTP transport, an application error is one in which the HTTP response has a 500 status code, has a non-empty payload, and has a content type that is consistent with the service type, such as XML for SOAP. When an error meets the application error conditions you define, return a TRANSPORT_ERROR_APPLICATION type using one of the following methods:

  • Errors in the request: Throw a TransportException with the error code TRANSPORT_ERROR_APPLICATION in TransportProvider.sendMessageAsync().

  • Errors in the response: Schedule TransportSendListener.onError() with the error code TRANSPORT_ERROR_APPLICATION.

The Transport SDK can then identify application errors when they occur and handle them accordingly. The Transport SDK also sends application errors to the pipeline $fault variable.

40.4.4.4.2 Configuring Application Error Retries

In your <Transport>Config.xml file, enter the following element as a child of the <ProviderConfiguration> element, according to the TransportCommon.xsd schema located in /OSB_ORACLE_HOME/lib/modules/oracle.servicebus.kernel-api.jar:

<declare-application-errors>true</declare-application-errors>

This entry provides an option to retry application errors on the business service's main transport configuration page when a user selects your transport. If you do not provide this element, the default value is false, application errors are not caught, and no option is provided to retry application errors.

40.4.4.5 Catching Connection Errors

Service Bus lets you identify connection errors in your transport, which trigger the Transport SDK to take inaccessible endpoint URIs offline automatically. For example, in a cluster with Service Bus running on Managed Servers, a Managed Server that experiences a connection error on a service request can automatically mark the endpoint URI as offline.

You can re-enable endpoint URIs in the following ways:

  • On configuring the business service, you can set a retry count and retry iteration interval to determine the frequency and number of retries after connection errors. A successful connection to the service after a retry automatically reactivates the endpoint URI.

    A retry iteration interval of zero (0) takes the endpoint URI offline indefinitely and requires you to manually re-enable the endpoint URI.

  • You can manually re-enable offline endpoint URIs from Fusion Middleware Control on the Dashboard for the business service.

The automated connection error functionality does not apply to the following situations:

  • If a service pipeline dynamically sets an endpoint URI in $outbound/ctx:transport/ctx:uri, the Transport SDK cannot take the URI offline, because the endpoint URI is not defined in the service configuration.

  • The Transport SDK does not persist connection status. After a server restart, all endpoint URIs are considered online.

For more information, see "Managing and Monitoring Endpoint URIs for Business Services" in Administering Oracle Service Bus.

40.4.4.5.1 Identifying Connection Errors

Once caught, a connection error triggers the Transport SDK to take the affected endpoint URI offline automatically. In your transport provider code, you must identify the conditions under which a connection error occurs. For example, in the Service Bus HTTP transport, a connection error is one in which the HTTP response has a 404 status code or there is an IOException when a connection is attempted on the endpoint URI.

When an exception meets the connection error conditions you define, return a TRANSPORT_ERROR_CONNECTION type using one of the following methods:

  • Errors in the request: Throw a TransportException with the error code TRANSPORT_ERROR_CONNECTION in TransportProvider.sendMessageAsync().

  • Errors in the response: Schedule TransportSendListener.onError() with the error code TRANSPORT_ERROR_CONNECTION.

The Transport SDK can then identify connection errors when they occur and handle them accordingly. The Transport SDK also sends connection errors to the pipeline $fault variable and adds them to the response.

40.4.5 Defining Custom Environment Value Types

You can define custom environment value types to use in your transport implementation. When you use the TransportProvider.getEnvValues() method to return environment values for an endpoint, you can declare environment values of these custom types.

When your transport is used, custom environment value types can be used in the same ways that standard environment value types are used in Service Bus, such as for customization, find and replace, and preservation of values on configuration import. For example, you may want to be able to define and preserve references to a service account or a JMS queue in your transport configuration. Environment value types can be any of the following categories: environment, operational, and security.

Add custom variables to your <Transport>Config.xml file. The schema that determines the XML structure is TransportCommon.xsd, located in located in /OSB_ORACLE_HOME/lib/servicebus-schemas.jar.

Following is an example of a custom security variable in the JMS transport's JmsConfig.xml:

<env-value-type>
    <name>JMS Service Accounts</name>
    <localized-display-name>
        <localizer-class>com.bea.wli.sb.transports.messages.
         TransportsTextLocalizer</localizer-class>
        <message-id>JMS_SERVICE_ACCOUNTS</message-id>
    </localized-display-name>
    <localized-description>
        <localizer-class>com.bea.wli.sb.transports.messages.
          TransportsTextLocalizer</localizer-class>
        <message-id>JMS_SERVICE_ACCOUNTS</message-id>
    </localized-description>
    <simple-value>true</simple-value>
    <category>security</category>
</env-value-type>

Following are descriptions of key elements for custom environment value types:

  • name: The variable name used by the Transport SDK.

  • display-name: The name for the variable that appears in the Service Bus user interface. Following is the localization alternative:

    • localized-display-name: Alternative, localized version of display-name.

    • localizer-class: The localization properties text file containing the localized display-name. The .properties extension is not required.

    • message-id: The property in the localization properties file that provides the value of the display name.

  • description: Description of the environment value type. For localization, use the localized-description element instead with the localizer-class and message-id child elements as described in display-name.

  • simple-value: If the environment value type is of the category "environment," simple-value determines whether or not this type is searchable with find and replace functionality in Service Bus (value of true or false).

  • category: The category of the environment value type: environment, security, or operational. When the category is security or operational, you can decide whether or not to preserve the environment value type during configuration import. When the category is environment, the environment value type is available for find and replace.

40.4.6 Publishing Proxy Services to a UDDI Registry

Universal Description, Discovery, and Integration (UDDI) is a standard mechanism for describing and locating web services across the internet. You might want to publish proxy services based on a custom transport provider to a UDDI registry. This allows proxy services to be imported into another Service Bus server in a different domain from the one hosting the business service.

To publish a proxy service, the transport provider needs to define a tModel that represents the transport type in the UDDI section of TransportProviderConfiguration XML schema definition. This tModel must contain a CategoryBag with a keyedReference whose tModelKey is set to the UDDI Types Category System and whose keyValue is transport. You are required to provide only the UDDI V3 tModel key for this tModel. If UDDI already defines a tModel for this transport type, you should copy and paste the definition into the UDDI section. For more information on the schema-generated interfaces, see Schema-Generated Interfaces.

The following example provides an example of such a tModel.

Example tModel

<?xml version="1.0" encoding="UTF-8"?>
<ProviderConfiguration xmlns="http://www.bea.com/wli/sb/transports">
   . . .
   . . .
  <UDDI>
    <TModelDefinition>
      <tModel tModelKey="uddi:bea.uddi.org:transport:socket">
        <name>uddi-org:socket</name>
        <description>Socket transport based webservice</description>
        <overviewDoc>
          <overviewURL useType="text">
            http://www.bea.com/wli/sb/UDDIMapping#socket
          </overviewURL>
        </overviewDoc>
        <categoryBag>
          <keyedReference keyName="uddi-org:types:transport"
                          keyValue="transport"
                          tModelKey="uddi:uddi.org:categorization:types"/>
        </categoryBag>
      </tModel>
    </TModelDefinition>
  </UDDI>
</ProviderConfiguration>

If UDDI does not already define a tModel for this transport type, Service Bus can publish the tModel you define here to configured registries. When a UDDI registry is configured for Service Bus, the Load tModels into Registry option can be specified. That option causes all of the tModels of Service Bus, including the tModels for custom transport providers, to be published to the UDDI registry. After deploying your transport provider, you can update your UDDI registry configuration to publish your tModel.

During UDDI export, TransportProvider.getBusinessServicePropertiesForProxy(Ref) is called and the resulting map is published to the UDDI registry. The provider is responsible for making sure to preserve all important properties of the business service in the map so that during the UDDI import process the business service definition can be correctly constructed without loss of information.

During UDDI import, TransportProvider.getProviderSpecificConfiguration(Map) is called and the result is an XmlObject that conforms to the provider-specific endpoint configuration schema, which goes into the service definition.

40.4.7 When to Implement TransportWLSArtifactDeployer

Two sets of transport provider interfaces are provided that deal with individual service registration. TransportProvider has methods like create, update, delete, suspend, and resume, and TransportWLSArtifactDeployer has the same methods. This section discusses these two implementations and explains when you need to implement TransportWLSArtifactDeployer.

Only implement TransportWLSArtifactDeployer if your provider needs to make changes to Oracle WebLogic Server artifacts in the Service Bus domain. The methods in TransportWLSArtifactDeployer are only called on an Admin Server. In this case, changes are made through the DomainMBean argument that is passed in, and then the changes are propagated to the entire cluster.

The TransportProvider methods are called on all servers (Administration and Managed Servers) in the domain. Because you cannot make changes to Service Bus domain artifacts on a Managed Server, the purpose of the method calls on TransportProvider is to update its internal data structures only.

When a given Transport provider implements the TransportWLSArtifactDeployer interface, the methods on TransportWLSArtifactDeployer are called before the corresponding methods on TransportProvider. For example, TransportWLSArtifactDeployer.onCreate() is called before TransportProvider.createEndPoint(). For more information about TransportWLSArtifactDeployer, see Summary of General Interfaces.

40.5 Creating Help for Custom Transports

You can provide online help for your custom transports for both JDeveloper and Oracle Service Bus Console. Both provide their own integrated help system. In JDeveloper, you need to incorporate the help into the existing help system. The Oracle Service Bus Console displays custom transport help stand-alone in its own browser window, as shown in Figure 40-5. Custom transport help is displayed when you click the Help icon on the transport configuration page of the service definition editor. Providing help is optional, but it can be extremely useful in guiding service creators through the transport configuration process.

Figure 40-5 Custom Transport Help from the Service Bus Console

Description of Figure 40-5 follows
Description of "Figure 40-5 Custom Transport Help from the Service Bus Console"

40.5.1 About Custom Transport Online Help

You have a lot of flexibility in deciding what type of help content to provide, from a simple page of text with no graphics to multiple pages with many graphics, PDF files, embedded video and so on. For example, you could create a single HTML file and reference it from the help link; or you could create separate help files that describe the transport configuration fields for business services and proxy services and also provide a high-level overview. You can create separate help topics for Oracle Service Bus Console and JDeveloper or use the same ones.

Service Bus provides a sample help implementation in its sample socket transport, located at OSB_ORACLE_HOME/samples/servicebus/sample-transport. The sample transport is a good reference implementation for developing your own custom transports and help.

40.5.2 How to Provide Custom Transport Help in the Console

This section shows you how to provide help for your custom transport at runtime in the Oracle Service Bus Console. Service Bus displays custom transport help as a stand-alone help page in a browser, as shown in Figure 40-5.

Figure 40-6 provides a high-level view of the console help framework for custom transports.

Figure 40-6 Oracle Service Bus Console Help Framework

Description of Figure 40-6 follows
Description of "Figure 40-6 Oracle Service Bus Console Help Framework"

By implementing a specific Service Bus interface, you use the getHelpPage() method to launch a single HTML page when the user clicks the Help icon for the custom transport configuration page in the console. The HTML file can contain the following:

  • Text, inline CSS definitions, inline JavaScript functions

  • References to graphics and other resources, as long as those resources are hosted in a web application or an external web site

In most situations, you should be able to provide all the help for your custom transport with text and inline formatting.

However, if you want to provide full-featured web-based help that includes graphics and other external resources, those resources must be hosted in a web application or an external web site. You must either reference those external resources in the HTML file or provide a link from the HTML file to an external location. For example, the sample socket transport help provides a link from the starting HTML file to a help topic with graphics that is running in a custom web application. Using an embedded JavaScript call, you could also set up your HTML file to automatically redirect to the expanded help URL you want.

Perform the following tasks to implement online help in the console:

When you are done creating your help files, package the files as described in Packaging Help for the Transport Plug-in.

40.5.2.1 Implement the CustomHelpProvider Interface

To develop the configuration user interface for your custom transport, you implement the TransportUIBinding interface in a custom class. To provide help for your transport configuration user interface in the Oracle Service Bus Console, you must also implement the CustomHelpProvider interface. CustomHelpProvider contains the getHelpPage() method you need to launch help for your transport configuration page in the Oracle Service Bus Console.

The sample socket transport implements CustomHelpProvider in its SocketTransportUIBinding class (located at OSB_ORACLE_HOME/samples/servicebus/sample-transport/src/com/bea/alsb/transports/sock).

The following example contains snippets that illustrate the implementation of CustomHelpProvider.

Example - Implementing CustomHelpProvider to provide console online help

public class SocketTransportUIBinding
    implements TransportUIBinding, CustomHelpProvider {
  ...
  public Reader getHelpPage() {
    String helpFile = "help/en/contexts_socketTransport.html";
    ClassLoader clLoader = Thread.currentThread().getContextClassLoader();
    InputStream is = clLoader.getResourceAsStream(helpFile);
    InputStreamReader helpReader = null;
    if(is!=null)
      helpReader = new InputStreamReader(is);
    else
      SocketTransportUtil.logger
        .warning(SocketTransportUtil.formatText(uiContext.getLocale(), "800138"));
    return helpReader;
  }
}

In the previous example, getHelpPage() returns a Reader stream that the Oracle Service Bus Console uses to send the HTML page to the browser. The helpFile path is relative to the root within the transport JAR.

If you are providing help in multiple languages, use TransportUIContext.getLocale() to help provide the appropriate path to the localized content; in this case, providing the locale value for /help/<locale>/<localized>.html.

40.5.2.2 Create an HTML File to Launch

You can create an HTML file for the getHelpPage() method to launch, as illustrated by help/en/contexts_socketTransport.html in the example in Implement the CustomHelpProvider Interface. If you want to keep your help implementation simple, create the HTML file to use text, inline CSS definitions, and inline JavaScript functions. If you do this, you do not need to create a separate web application to host graphics or other external resources.

However, if you want to provide more expanded help with graphics and other resources, reference those external resources in your HTML file, such as

img src="/help_socket/help/en/wwimages/addProject.gif"

or

a href="http://www.yoursite.com"

You can also set the HTML file up to automatically redirect to the expanded help with an embedded JavaScript call, as shown in the following example, which redirects from the sample socket transport HTML page to the expanded help_socket web application help content.

Example - JavaScript function that provides a redirect

<script language="JavaScript" type="text/javascript">
<!-- Begin
window.location="/help_socket/help/en/example.html";
// End -->
</script>

The sample socket transport HTML file provides a link to its expanded help. The HTML file, contexts_socketTransport.html, is located at OSB_ORACLE_HOME/samples/servicebus/sample-transport/resources/help/en/.

40.5.2.3 Create a Simple Web Application to Display Expanded Help (Optional)

If you want to go beyond a basic text HTML file for your transport help, you can provide expanded help with graphics and other resources in various ways:

  • Link from the self-contained HTML file to an existing URL; for example, if you have an existing web site that contains your transport documentation. All that is required is that you provide a link to the URL from the self-contained HTML file. You can also insert references to graphics and other resources hosted on an external site.

  • Create a web application for the expanded help, bundle it with your transport, and link to it or reference graphics and other resources from the HTML file. This topic provides instructions on creating a web application that is bundled in your transport EAR to display your expanded transport help.

Create the files described in META-INF/application.xml and WEB-INF/web.xml for your web application.

40.5.2.3.1 META-INF/application.xml

In application.xml, give your web application a context root that is used for the web application's root URL. The following example shows the context root for the sample socket transport web application.

Example - application.xml for the sample socket transport Web application

<application>
    <display-name>Socket Transport</display-name>
    <description>Socket Transport</description>
    <module>
        <web>
            <web-uri>webapp</web-uri>
            <context-root>help_socket</context-root>
        </web>
    </module>
</application>

The sample socket transport application.xml file is located at OSB_ORACLE_HOME/samples/servicebus/sample-transport/META-INF/.

This entry maps the file system directory /webapp to an alias web application root URL:

http://server:port/help_socket/ 

With your help files inside the web application in a directory such as /help/en/, the full URL to your expanded help would be:

http://server:port/help_socket/help/en/index.html

But your internal links to it only need to be

/help_socket/help/en/index.html

where index.html is the landing HTML page.

40.5.2.3.2 WEB-INF/web.xml

In web.xml, enter a display name and description for the web application. This is standard deployment descriptor information. The following example shows the name and description of the sample socket transport web application.

Example - web.xml for the sample socket transport Web application

<web-app>
    <display-name>Sample Socket Transport Help WebApp</display-name>
    <description>
        This webapp implements the help webapp for the socket transport.
    </description>
</web-app>

The sample socket transport web.xml file is located at OSB_ORACLE_HOME/samples/servicebus/sample-transport/webapp/WEB-INF/.

40.5.2.3.3 Help Content and Resources

Create and package your expanded help files inside the web application directory. In the sample socket transport, the help files are stored in OSB_ORACLE_HOME/samples/servicebus/sample-transport/resources/help/en.

Note:

The reason the socket transport help files are not stored in the /webapp directory is because the help directory contains help files and resources for both JDeveloper and the Oracle Service Bus Console. When the sample socket ANT build creates the transport JAR and transport EAR, it packages the help in different ways. For the transport EAR build, it moves the help files under the /webapp directory.

40.5.3 How to Provide Custom Transport Help in JDeveloper

If you make your custom transport available for service configuration in JDeveloper, you can incorporate a help topic for the page that appears in the service definition editor. You do this by adding your help files to the existing help JAR file.

To provide online help in JDeveloper:

  1. Create your help topics. For more information, see About Custom Transport Online Help and How to Provide Custom Transport Help in the Console.
  2. Package all the help files. See Packaging Help for the Transport Plug-in.
  3. Navigate to MW_HOME/osb/plugins/jdeveloper/doc/studio_doc.
  4. Make a backup copy of osbjh.jar.
  5. Extract the files from osbjh.jar into a temporary directory.
  6. Copy the sample transport help files you created to the temporary directory, maintaining the structure of your help file system. You can copy your files into a subdirectory or at the same level as the existing f1*.html files.
  7. In the temporary directory, open map.xml, and add a mapID entry for each help topic to make available.

    The map ID includes the target (ID) and URL (relative path and file name) for each help topic, along with a description. Below is an example for the sample sockets transport provider.

      <mapID target="contexts_socketTransport" url="example.html" />
      <!-- "Sample Sockets Transport Configuration"-->
    
  8. Save and close the map file, and then JAR all the help files into a new osbjh.jar file.
  9. Replace the old osbjh.jar file in MW_HOME/osb/plugins/jdeveloper/doc/studio_doc. with the new osbjh.jar file you just created.

40.5.4 Packaging Help for the Transport Plug-in

Your transport plug-in should contain the following:

  • A transport JAR file containing your transport classes and supporting files. The JAR file includes help resources in a /help/en directory.

  • A transport EAR file that contains your runtime components, including the help system in a /webapp/help/en directory.

Notice that with the /en directory the help is packaged to support localization. To provide localization, you must create a plug-in for each locale. For more information about packaging the transport and help, see Packaging and Deploying a Custom Transport Provider.