38.1 What is a Transport Provider?

A transport provider implements the interfaces of the Transport SDK and provides a bridge between Oracle Service Bus and mechanisms by which messages are sent or received. Such mechanisms can include specific transport protocols, such as HTTP, as well as other entities, such as a file or an email message. A transport provider manages the life cycle and runtime behavior of transport endpoints. An endpoint is a resource where messages originate or are targeted.

Figure 38-1 illustrates the basic flow of messages through Oracle Service Bus. A client sends a message to Oracle Service Bus using a specific transport protocol. A transport provider processes the inbound message, handling communication with the service client endpoint and acting as the entry point for messages into Oracle Service Bus.

By default, Oracle Service Bus includes transport providers that support several commonly used transport protocols, such as HTTP, JMS, File, FTP, and others. These native providers let you configure proxy and business services that require these common transport protocols.

38.2.1 Purpose of the SDK

Oracle Service Bus processes messages independently of how they flow into or out of the system. The Transport SDK provides a layer of abstraction between Oracle Service Bus and components that deal with the flow of data in and out of Oracle Service Bus. This layer of abstraction lets you develop new transport providers to handle unique transport protocols.

The SDK abstracts from the rest of Oracle Service Bus:

Handling specific transport bindings

Deploying service endpoints on the transport bindings. An endpoint is either capable of transmitting or receiving a message.

38.2.2 Transport SDK Features

38.2.2.1 Handling Inbound and Outbound Messages

A transport provider developed with the Transport SDK handles inbound and outbound messages as follows:

Inbound messages typically come into Oracle Service Bus from an outside source, such as an HTTP client. The Transport SDK packages the payload and transport level headers, if any, into a generic data structure. The Transport SDK then passes the message, in its generic format, to the Oracle Service Bus pipeline.

Outbound messages originate from Oracle Service Bus business services and go to an externally managed endpoint, such as a Web service or JMS queue. The Transport SDK receives a generic data structure from the Oracle Service Bus pipeline, converts it to the corresponding transport-specific headers and payload, and sends it out to an external system.

The Transport SDK handles outbound and inbound messages independently. An inbound message can be bound to one transport protocol and bound to a different transport protocol on the outbound endpoint.

38.2.2.2 Deploying Transport-Related Artifacts

Certain transports include artifacts that need to be deployed to Oracle WebLogic Server. For instance, a JMS proxy is implemented as a message-driven bean. This artifact, an EAR file, must be deployed when the new JMS proxy is registered. Similarly, the EJB transport provider employs an EAR file that must be deployed when a new EJB business service is registered. Other kinds of artifacts might require deployment, such as a JMS transport, which may create queues and topics as part of the service registration. The SDK lets you support these artifacts and lets you participate in the WLS deployment cycle. If the deployment of one of these artifacts fails, the Oracle Service Bus session is notified and the deployment is canceled. This feature of the SDK allows for the atomic creation of services. If something fails, the session reverts to its previous state.

38.2.2.3 Processing Messages Asynchronously

Because the server has a limited number of threads to work with when processing messages, asynchrony is important. This feature allows Oracle Service Bus to scale to handle large numbers of messages. After a request is processed, the thread is released. When the business service receives a response (or is finished with the request if it is a one-way message), it notifies Oracle Service Bus asynchronously through a callback.

38.2.3 Transport Provider Modes

With the Transport SDK, you can implement inbound property modes and outbound property modes. These connection and endpoint modes are specified in the transport provider's XML Schema definition file. For more information on this file, see Section 39.3.3, "3. Create an XML Schema File for Transport-Specific Artifacts." This schema is available to the Oracle Service Bus Pipeline for filtering and routing purposes.

38.2.4 Related Features

This section lists related features that are provided by the transport manager. The transport manager provides the main point of centralization for managing different transport providers, endpoint registration, control, processing of inbound and outbound messages, and other functions. These features do not require specific support by a transport provider.

38.3.1 When to Use the Transport SDK

One of the prime use cases for the Transport SDK is to support a specialized transport that you already employ for communication between your internal applications. Such a transport may have its own concept of setup handshake, header fields, metadata, or transport-level security. Using the Transport SDK, you can create a transport implementation for Oracle Service Bus that allows configuring individual endpoints, either inbound, outbound or both. With a custom transport implementation, the metadata and header fields of the specialized transport can be mapped to context variables available in a proxy service pipeline.

Use the Transport SDK when the transport provider needs to be seamlessly integrated into all aspects of Oracle Service Bus for reliability, security, performance, management, user interface, and the use of the UDDI registry.

Some cases where it is appropriate to use the Transport SDK to develop a custom transport include:

Using a proprietary transport that requires custom interfaces and supports an organization's existing applications.

Using a CORBA or IIOP protocol for communicating with CORBA applications.

Using other legacy systems, such as IMS and Mainframe.

Using variations on existing transports, such as SFTP (Secure FTP) and the native IBM WebSphere MQ API (instead of WebSphere MQ JMS).

Alternatively, you can use the Transport SDK to support a specialized protocol over one of the existing transports provided with Oracle Service Bus. Examples of this could include supporting:

Messages consisting of parsed or binary XML over HTTP.

WS-RM or other new Web service standards over HTTP.

Request-response messaging over JMS, but with a different response pattern than either of the two patterns supported by the Oracle Service Bus JMS transport (for example, a response queue defined in the message context).

38.3.2 When Alternative Approaches are Recommended

Creating a new Oracle Service Bus transport provider using the Transport SDK can be a significant effort. The Transport SDK provides a rich, full featured environment so that a custom transport has all of the usefulness and capabilities of the transports that come natively with Oracle Service Bus. But such richness brings with it some complexity. For certain cases, you might want to consider easier alternatives.

If you need an extension merely to support a different format message sent or received over an existing protocol, it may be possible to use the existing transport and use a Java Callout to convert the message. For example, suppose you have a specialized binary format (such as ASN.1 or a serialized Java object) being sent over the standard JMS protocol. In this case, you might consider defining the service using the standard JMS transport with the service type being a messaging service with binary input/output messages. Then, if the contents of the message are needed in the pipeline, a Java Callout action can be used to convert the message to or from XML. For information on using Java Callouts, see "Extensibility Using Java Callouts and POJOs" in the Oracle Fusion Middleware Administrator's Guide for Oracle Service Bus.

Other cases where it is best not to use the Transport SDK to develop a custom transport provider include:

38.4.1 Overview

In general, a custom transport provider consists of a design-time part and a runtime part. The design-time part is concerned with registering endpoints with the transport provider. This configuration behavior is provided by the implementation of the UI interfaces. The runtime part implements the mechanism of sending and receiving messages.

When you develop a new custom transport provider, you need to implement a number of interfaces provided by the SDK. This section includes UML diagrams that model the organization of the design-time and runtime parts of the SDK.

Tip:

In Oracle Service Bus, implementations of the TransportProvider interface represent the central point for management of transport protocol-specific configuration and runtime properties. A single instance of a TransportProvider object exists for every supported protocol. For example, there are single instances of HTTP transport provider, JMS transport provider, and others.

38.4.2 Design-Time Component

The design-time part of a custom transport provider consists of the user interface configuration. This configuration is called by the Oracle Service Bus Administration Console or IDE when a new business or proxy service is being registered. Figure 38-2 shows a UML diagram that depicts the structure of the design time part of a transport provider. Some of the interfaces described in the diagram include:

TransportManager – A transport provider communicates with the transport manager through this interface. The implementation is not public.

TransportProvider – Third parties must implement this interface. The TransportProvider keeps track of TransportEndpoint objects. TransportProvider also manages the life cycle of the endpoints. For example, you can suspend a transport endpoint, which is managed through the TransportProvider interface.

Each transport endpoint has a configuration that consists of some properties that are generic to all endpoints of any transport provider, such as a URI, and some properties that are specific to endpoints of that provider only. Figure 38-3 shows the relationship between the shared endpoint configuration properties and transport provider specific configuration properties. See Section 38.5.1, "Overview of Transport Endpoint Properties." for more information.

38.4.3 Runtime Component

In the runtime framework, the transport provider calls the transport manager to acknowledge that an inbound message has been received. The transport message context contains the header and body of the inbound message. For the outbound message, there is a TransportSendListener and TransportSender. The transport provider retrieves the header and body from the message.

Figure 38-2 shows a UML diagram that depicts the structure of the runtime part of a transport provider.

38.5 The Transaction Model

Before you develop a new transport provider using the Transport SDK, it is important to consider the transaction model for your message endpoints. This section discusses the transaction model used by Oracle Service Bus and how that model relates to the Transport SDK.

38.5.1 Overview of Transport Endpoint Properties

A transport endpoint is an Oracle Service Bus resource, such as a JMS proxy service, where messages are originated or targeted. In Oracle Service Bus, transport endpoints are managed by protocol-specific transport providers, plug-in objects that manage the life cycle and runtime behavior of transport endpoints.

To understand the transactional model of Oracle Service Bus, it is useful to review some of the properties of service transport endpoints.

38.5.1.1 Transactional vs. Non-Transactional Endpoints

A given endpoint may or may not be transactional. A transactional endpoint has potential to start or enlist in a global transaction context when processing a message. The following examples illustrate how transactional properties vary depending on the endpoint:

A JMS proxy service that uses the XA connection factory is a transactional endpoint. When the message is received, the container ensures that a transaction is started so that the message is processed in the context of a transaction.

A Tuxedo proxy service may or may not be a transactional endpoint. A Tuxedo proxy service is only transactional if a transaction was started by the Tuxedo client application before the message is received.

While an HTTP proxy service will not typically have an associated transaction when invoked by an HTTP client, you can set an option in the HTTP proxy service configuration that starts a transaction and executes the message flow in the context of that transaction.

38.5.1.2 Supported Message Patterns

A given endpoint can use one of the following message patterns:

One Way – No responses are expected. An example of a one-way endpoint is a JMS proxy service that does not expect a response.

Synchronous – A request or response is implied. In this case, the response message is paired with the request message implicitly because no other traffic can occur on the transport channel from the time the request is issued until the time the response is received. In most cases, a synchronous message implies blocking calls for outbound requests. An EJB endpoint is synchronous. An HTTP endpoint is also synchronous: a new request cannot be sent until a response is received.

Asynchronous – A request and response is implied. The response is correlated to a request through a transport-specific mechanism, such as a JMS transport and correlation through a JMSCorrelationID message property. For example, a JMS business service endpoint with request and response is asynchronous.

38.5.2 Support for Synchronous Transactions

All Oracle Service Bus proxy services support transaction propagation, can start a transaction if none already exists, and can optionally ensure that the response occurs in the context of the transaction, even if the outbound business service is asynchronous—in essence transforming an asynchronous pattern effectively into a synchronous pattern. Outbound business services can provide additional transaction support, such as suspending an existing transaction.

Synchronous transactional transports support the following use cases:

38.5.2.1 Use Case 1 (Response Pipeline Processing)

Response pipeline processing is included in an incoming transaction when the inbound transport supports synchronous transactions or when you configure a proxy service to propagate a transaction to the response. This case is supported when the inbound transport is paired with any other outbound transport, with the exception described in the note below.

Note:

A deadlock situation occurs when the inbound transport is synchronous transactional and the outbound transport is asynchronous transactional. The deadlock occurs because the outbound request is not available to be received by the business service until after the transaction commits, but the transaction was started externally and does not commit until Oracle Service Bus gets the response and returns. The transport manager recognizes this situation and avoids the deadlock by throwing a runtime error.

For example, if a synchronous transactional inbound endpoint is used, such as a Tuxedo proxy service, and the outbound endpoint is asynchronous transactional, such as a JMS business service, the outbound request does not commit the transaction until the response is received. It cannot be received until the external entity receives the request and processes it.

Also in this case, the Oracle Service Bus Publish action performed in the response pipeline is part of the transaction just like publish actions in the request pipeline are part of the transaction.

Note:

There are several actions that can potentially participate in a transaction (in either the request or response pipeline). These include Publish, Service Callout, and Report actions.

For example, if an inbound Tuxedo transport is synchronous transactional, it can be committed only after the request and response pipeline have been completed. In this case, the transport manager transfers the transaction context from the inbound to the outbound thread. When the response thread is finished, the transaction control and outcome are returned to the invoking client.

38.5.2.2 Use Case 2 (Service Callout Processing)

Oracle Service Bus Service Callouts allow you to make a callout from a proxy service to another service. If a Service Callout action is made to a synchronous transactional transport, the case of Exactly Once quality of service is supported in addition to Best Effort quality of service. Exactly Once means that messages are delivered from inbound to outbound exactly once, assuming a terminating error does not occur before the outbound message send is initiated. Best Effort means that each dispatch defines its own transactional context (if the transport is transactional). When Best Effort is specified, there is no reliable messaging and no elimination of duplicate messages; however, performance is optimized. See also Section 39.7, "Working with TransportOptions."

Callouts to synchronous transactional transports are optionally part of an existing transaction. For example, while the request pipeline is executing during a global transaction, Service Callouts are permitted to participate in the transaction. For example, if there is a callout to an EJB service, the service can participate in that transaction if it wants to by setting its quality of service value to Exactly Once.

38.5.2.3 Use Case 3 (Suspending Transactions)

Before calling the transport provider to send an outbound request the transport framework will suspend a transaction if the following conditions apply:

The outbound service endpoint is transactional.

There is a global XA transaction in progress.

The quality of service is set to Best Effort.

The suspended transaction will resume, after the "send" operation is complete.

38.5.2.4 Use Case 4 (Multiple URIs)

If a given outbound service endpoint has multiple URIs associated with it, and is transactional, failover only occurs while the transaction, if any, is not marked for rollback. For example, if a URI is called, and the service returns an error, a failover is normally triggered. In this event, the transport framework detects that the transaction has been marked for rollback; therefore, the framework does not perform a failover to a different URI.

38.6 The Security Model

The Transport SDK allows customers and third-parties to plug in new transports into Oracle Service Bus. Within the Oracle Service Bus security model, transport providers are considered trusted code. It is critical that transport provider implementations are carefully designed to avoid potential security threats by creating security holes. Although this document does not contain specific guidelines on how to develop secure transport providers, this section discusses the following security goals of the Transport SDK:

38.6.1 Inbound Request Authentication

Transport providers are free to implement whatever inbound authentication mechanisms are appropriate to that transport. For example: the HTTP transport provider supports these authentication methods:

HTTP BASIC

Custom authentication tokens carried in HTTP headers

The HTTPS transport provider supports SSL client authentication, in addition to the ones listed above. Both HTTP and HTTPS transport providers also support anonymous client requests.

The transport provider is responsible for implementing any applicable transport level authentication schemes, if any. If the transport provider authenticates the client it must make the client Subject object available to Oracle Service Bus by calling TransportManager.receiveMessage() within the scope of weblogic.security.Security.runAs(subject). For information on this method, see the Oracle Fusion Middleware Java API Reference for Oracle Service Bus.

As the input for identity propagation and credential mapping (unless there is also message-level client authentication)

If the transport provider does not support authentication, or if it supports anonymous requests, it must make sure the anonymous subject is on the thread before dispatching the request. Typically the transport provider will already be running as anonymous, but if this is not the case, then the provider must call:

38.6.2.1 Outbound Username/Password Authentication

Outbound username/password authentication can be implemented by leveraging Oracle Service Bus service accounts. Service accounts are first-class, top-level Oracle Service Bus resources. Service accounts are created and managed in the Oracle Service Bus Administration Console. Transport providers are free to design their transport-specific configuration to include references to service accounts. That way the transport provider can make use of the credential management mechanisms provided by Oracle Service Bus service accounts.

Transport providers don't have to worry about the details of service account configuration. There are three types of service accounts:

Static – A static service account is configured with a fixed username/password.

Mapped – A mapped service account contains a list of remote-users/remote-passwords and a map from local-users to remote-users. Mapped service accounts can optionally map the anonymous subject to a given remote user.

Pass-through – A pass-through service account indicates that the username/password of the Oracle Service Bus client must be sent to the back-end.

An outbound endpoint can have a reference to a service account. The reference to the service account must be stored in the transport-specific endpoint configuration. When a proxy service routes a message to this outbound endpoint, the transport provider passes the service account reference to CredentialCallback.getUsernamePasswordCredential(ref). Oracle Service Bus returns the username/password according to the service account configuration. This has the advantage of separating identity propagation and credential mapping configuration from the transport-specific details, simplifying the transport SDK. It also allows sharing this configuration. Any number of endpoints can reference the same service account.

Note:

The CredentialCallback object is made available to the transport provider by calling TransportSender.getCredentialCallback().

CredentialCallback.getUsernamePasswordCredential() returns a weblogic.security.UsernameAndPassword instance. This is a simple class which has methods to get the username and password. The username/password returned depends on the type of service account. If the service account is of type static, the fixed username/password is returned. If it is mapped, the client subject is used to look up the remote username/password. If it is pass-through, the client's username/password is returned.

Note:

A mapped service account throws CredentialNotFoundException if:

if there is no map for the inbound client, or

the inbound security context is anonymous and there is no anonymous map

38.6.2.2 Outbound SSL Client Authentication (Two-Way SSL)

Oracle Service Bus also supports outbound SSL client authentication. In this case, the proxy making the outbound SSL request must be configured with a PKI key-pair for SSL. (This is done with a reference to a proxy service provider, the details are out of the scope of this document. To obtain the key-pair for SSL client authentication, the transport provider must call CredentialCallback.getKeyPair(). The HTTPS transport provider is an example of this.

38.6.2.3 Outbound JAAS Subject Authentication

Some transport providers send a serialized JAAS Subject on the wire as an authentication token. To obtain the inbound subject the transport provider must call CredentialCallback.getSubject().

Note:

The return value may be the anonymous subject.

38.6.3 Link-Level or Connection-Level Credentials

Some transports require credentials to connect to services. For example, FTP endpoints may be required to authenticate to the FTP server. Transport providers can make use of static service accounts to retrieve a username/password for establishing the connection. Mapped or pass-through service accounts cannot be used in this case because these connections are not made on behalf of a particular client request. If a transport provider decides to follow this approach, the endpoint must be configured with a reference to a service account. At runtime, the provider must call TransportManagerHelper.getUsernamePassword(), passing the reference to the static service account.

38.6.4 Uniform Access Control to Proxy Services

Oracle Service Bus enforces access control to proxy services for every inbound request. Transport providers are not required to enforce access control or to provide interfaces to manage the access control policy.

Note:

The access control policy covers the majority of the use cases; however, a transport provider can implement its own access control mechanisms (in addition to the access control check done by Oracle Service Bus) if required for transport provider specific reasons. If that is the case, please contact your Oracle representative. In general Oracle recommends transport providers let Oracle Service Bus handle access control.

When access is denied, TransportManager.receiveMessage() throws an AccessNotAllowedException wrapped inside a TransportException. Transport providers are responsible for checking the root-cause of the TransportException. A transport provider may do special error handling when the root cause is an AccessNotAllowedException. For example, the HTTP/S transport provider returns an HTTP 403 (forbidden) error code in this case.

Note:

Oracle Service Bus makes the request headers available to the authorization providers for making access control decisions.

38.6.5 Identity Propagation and Credential Mapping

As explained in Section 38.6.2, "Outbound Request Authentication," Oracle Service Bus provides three types of service accounts. A transport provider can make use of service accounts to get access to the username/password for outbound authentication. A service account hides all of the details of identity propagation and credential mapping from Oracle Service Bus transport providers.

38.7 The Threading Model

This section discusses the threading model used by Oracle Service Bus and how the model relates to the Transport SDK. This section includes these topics:

38.7.1 Overview

Figure 38-5 illustrates the Oracle Service Bus threading model for a hypothetical transport endpoint processing a single inbound message.

A front end artifact, such as a Servlet, is responsible for getting the inbound message. A request can be routed to an outbound endpoint and sent asynchronously. At this point, the thread is released. At some later point, a response is sent back to Oracle Service Bus (using a callback). The response is received, packaged, and handed to the Oracle Service Bus pipeline. Later, the pipeline notifies the inbound endpoint that the response is ready to be sent to the client. This processing is scalable because a thread is only tied up as long as it is needed.

The pipeline performs request pipeline actions configured for the proxy.

While processing the inbound message in Oracle Service Bus pipeline, in the same (request) thread, Oracle Service Bus runtime calls on the registered outbound transport endpoint, which may or may not be managed by the same provider, to deliver an outbound message to an external service.

At some later point, the external service asynchronously calls on the outbound endpoint to deliver the response message. The outbound endpoint must have been registered previously with a transport specific callback object.

Note:

At this point, the initial request thread is released and placed back into the Oracle WebLogic Server thread pool for use by another request.

38.7.3 Outbound Response Message Thread

The following actions occur in the same thread:

The response message is packaged into a TransportMessageContext object and delivered back to Oracle Service Bus runtime for response processing. This processing occurs in a different thread than the request thread. This new thread is called the response thread.

If the transport provider does not have a native implementation of an asynchronous (non-blocking) outbound call, it still needs to deliver the response back to Oracle Service Bus runtime on a separate thread than that on which the inbound request message was received. To do this, it can execute the call in a blocking fashion in the request thread and then use a Transport SDK helper method to deliver the response back to Oracle Service Bus runtime.

For example, the EJB transport provider does not have an asynchronous (non-blocking) outbound call. The underlying API is a blocking API. To work around this, the provider makes its blocking call, then schedules the response for processing with TransportManagerHelper.schedule(). For more information on the EJB transport provider, see Chapter 28, "EJB Transport."

38.7.4 Support for Asynchrony

By design, the transport subsystem interacts asynchronously with Oracle Service Bus. The reason for this is that asynchronous behavior is more scalable, and therefore, more desirable than synchronous behavior. Rather than create two separate APIs, one for asynchronous and one for synchronous interaction, Oracle Service Bus runtime expects asynchronous interaction. It is up to the transport developer to work around this by a method such as posting a blocking call and posting the response in a callback. In any case, the response must be executed in a different thread from the request.

38.7.5 Publish and Service Callout Threading

The threading diagram shown in Figure 38-5 focuses on routing. The transport subsystem behaves the same way for Oracle Service Bus Publish and Service Callout actions which can occur in the middle of the request or response pipeline processing. These actions occur outside the scope of the transport subsystem and in the scope of an Oracle Service Bus pipeline. Therefore, some differences exist between the threading behavior of Publish and Service Callout actions and transport providers.

Note, however, the following cases:

Service Callout – The pipeline processor will block the thread until the response arrives asynchronously. The blocked thread would then resume execution of the pipeline. The purpose is to bind variables that can later be used in pipeline actions to perform business logic. Therefore, these actions must block so that the business logic can be performed before the response comes back.

Publish – The pipeline processor may or may not block the thread until the response arrives asynchronously. This thread then continues execution of the rest of the request or response pipeline processing.

38.8 Designing for Message Content

38.8.1 Overview

Transport providers have their own native representation of message content. For example, HTTP transport uses java.io.InputStream, JMS has Message objects of various types, Tuxedo has buffers, and the WLS Web Services stack uses SAAJ. However, within the runtime of a proxy service, the native representation of content is the Message Context. While Oracle Service Bus supports some common conversion scenarios, such as InputStream to/from Message Context, this conversion between transport representation and the Message Context is ultimately the transport provider's responsibility.

In general, the Transport SDK is not concerned with converting directly between two different transport representations of content. However, if two transports use compatible representations and the content does not require re-encoding, the SDK may allow the source content to be passed-through directly (for example, passing a FileInputStream from an inbound File transport to an outbound HTTP transport). However, if the source content requires any sort of processing, it makes more sense to unmarshall the source content into the Message Context first and then use the standard mechanisms to generate content for the outgoing transport.

38.8.2 Sources and Transformers

Content is represented as an instance of the Source interface. Transport SDK interfaces that deal with message content, such as TransportSender and TransportMessageContext, all use the Source interface when passing message payloads. The requirements on a Source are minimal. A Source must support push- and pull-based conversions to byte-based streams using the two methods defined in the base Source interface. A Source may or may not take into account various transformation options, such as character-set encoding, during serialization, as specified by the TransformOptions parameter.

While all Source objects must implement the base serialization interface, the underlying representation of the Source object's content is implementation specific. This allows for Source objects based on InputStreams, JMS Message objects, Strings, or whatever representation is most natural to a particular transport. Typically, Source implementations allow direct access to the underlying content, in addition to the base serialization methods. For example, StringSource, which internally uses a String object to store its content offers a getString() method to get at the internal data. The ultimate consumer of a Source can then extract the underlying content by calling these source-specific APIs and potentially avoid any serialization overheads.

Sources may also be transformed into other types of Sources using a Transformer object. If a Source consumer, such as a transport provider, is given a Source instance that it does not recognize, it can often transform it into a Source instance that it does recognize. The underlying content can then be extracted from that known Source using the source-specific APIs. However, often a transport provider simply serializes the content and send it using the base serialization methods. See also Section 41.4, "Source and Transformer Classes and Interfaces."

38.8.3 Sources and the MessageContext Object

Sources are the common content representation between the transport layer and the binding layer. The binding layer is the entity responsible for converting content between the Source representation used by the transport layer and the Message Context used by the pipeline runtime. How that conversion happens depends upon the type of service (its binding type) and the presence of attachments. While not strictly part of the Transport SDK, any transport provider that defines its own Source objects should be familiar with this conversion process.

When attachments are not present, the incoming Source represents just the core message content. The MessageContext is initialized by converting the received Source to a specific type of Source and then extracting the underlying content. For example, for XML-based services, the incoming Source is converted to an XmlObjectSource. The XmlObject is then extracted from the XmlObjectSource and used as the payload inside the $body context variable. SOAP services are similarly converted to XmlObjectSource except that the extracted XmlObject must be a SOAP Envelope so that the <SOAP:Header> and <SOAP:Body> elements can be extracted to initialize the $header and $body context variables.

Below are the canonical Source types used for the set of defined service-types:

SOAP – XmlObjectSource

XML – XmlObjectSource

TEXT – StringSource

MFL – MFLSource

For binary services, no Source conversion is done. Instead, the Source is registered with a SourceRepository and the resulting <binary-content/> XML is used as the payload inside $body.

When attachments are present, the incoming Source is first converted to a MessageContextSource. From the MessageContextSource, two untyped Source objects are obtained, one representing the attachments and one representing the core message. The Source for the core message is handled as described previously. The Source representing attachments is converted to an AttachmentsSource. From the AttachmentsSource, XML is obtained and is used to initialize the $attachments context variable and a SourceRepository containing the registered Sources that represent any binary attachment content. This entire process is illustrated in Figure 38-6.

A similar conversion occurs when creating a Source from data in the MessageContext to be passed to the transport layer. The core message is represented by a Source instance that can be converted to the canonical Source for the service type. In most cases, the Source will already be an instance of the canonical Source, but not always. When attachments are present, the Source delivered to the transport layer will be a source that can be converted to an instance of MessageContextSource. If the transport provider supports Content-Type as a pre-defined transport header, then the delivered Source will likely be an instance of MessageContextSource. Otherwise, the delivered Source will likely be an instance of MimeSource, but this can also be converted to a MessageContextSource.

The reason for this difference is that transports that natively support Content-Type as a transport header require that the top-level MIME headers appear in the transport headers rather than in the payload. Examples of this are HTTP and Email. Transports that do not natively support Content-Type must have these top-level MIME headers as part of the payload, as the Content-Type header is critical for decoding a multipart MIME package.

38.8.4 Built-In Transformations

Table 38-1 shows sources and lists the source types to which they can be converted by built-in transformers. For example, there is a built-in transformer that handles converting a StringSource into an XmlObjectSource; however, there is no transformer that can convert a StringSource into an XmlObjectSource. Typically, these transformers take advantage of their knowledge of the internal data representation used by both Source types.

Table 38-1 Built-In Transformations

Public Source

Can Be Transformed To

Source

StreamSource

ByteArraySource

StringSource

XmlObjectSource

DOMSource

MFLSource

SAAJSource

StreamSource

StreamSource

ByteArraySource

ByteArraySource

StringSource

StringSource

XmlObjectSource

DOMSource

XmlObjectSource

StringSource

XmlObjectSource

DOMSource

MFLSource

DOMSource

StringSource

XmlObjectSource

DOMSource

MFLSource

MFLSource

XmlObjectSource

DOMSource

MFLSource

MimeSource

MimeSource

SAAJSource

MessageContextSource

SAAJSource

MimeSource

SAAJSource

MessageContextSource

MessageContextSource

MimeSource

SAAJSource

MessageContextSource

These generic transformations are done without any knowledge of the initial Source type but instead rely on the base serialization methods that are implemented by all Sources: getInputStream() and writeTo(). So, although it is ultimately possible to convert an XmlObjectSource to a ByteArraySource, it is not done using any special knowledge of the internal details of XmlObjectSource.

Note:

Many custom Sources implemented by Transports can be handled by these generic transformations, especially if the underlying data is an unstructured collection of bytes. For example, the File Transport uses a custom Source that pulls its content directly from a file on disk. However, as that data is just a set of bytes without structure, there is no need to provide custom transformations to, for example, XmlObjectSource. The generic transformation Source XmlObjectSource can handle this custom FileSource using just the base serialization methods that all Sources must implement.