This chapter describes the concepts, functionality, and design considerations for developing a custom transport provider for use with Service Bus services. Careful planning of development activities can greatly reduce the time and effort you spend developing a custom transport provider.
This chapter includes the following sections:
A transport provider implements the interfaces of the Transport Software Development Kit (SDK) and provides a bridge between 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, which are resource where messages originate or are targeted.
A client sends a message to 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 Service Bus. The binding layer packs and unpacks messages, handles message security, and hands messages off to the pipeline. For an illustration of the basic flow of messages through Service Bus, see Figure 1-3.
For more information about Service Bus message brokering and the role of the transport layer, see Learning About Oracle Service Bus . For more detailed sequence diagrams that describe the message flow through Service Bus, see Transport SDK UML Sequence Diagrams.
By default, 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.
For information about using and configuring native transport providers, see Working with JCA Adapters, Transports, and Bindings.
The SDK abstracts the following from the rest of Service Bus:
Handling specific transport bindings.
Deploying service endpoints on the transport bindings. An endpoint is either capable of transmitting or receiving a message.
Collecting monitoring information.
Managing endpoints (such as performing suspend and resume operations and setting connection properties).
Enforcing Service Level Agreement (SLA) behavior (such as timing out connections).
This section describes the primary features of the Transport SDK.
A transport provider developed with the Transport SDK handles inbound and outbound messages as follows:
Inbound messages typically come into 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 Service Bus pipeline.
Outbound messages originate from 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 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.
Certain transports include artifacts that need to be deployed to Oracle WebLogic Server. For instance, a JMS proxy service is implemented as a message-driven bean. This artifact, an EAR file, must be deployed when the new JMS proxy service 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 might create queues and topics as part of the service registration. The SDK allows you to support these artifacts and lets you participate in the Oracle WebLogic Server deployment cycle. If the deployment of one of these artifacts fails, the Service Bus session is notified and the deployment is canceled. This feature of the SDK allows for the atomic creation of services. If one part fails, the session reverts to its previous state.
To participate in Oracle WebLogic Server deployment cycle, the transport provider must implement the
TransportWLSArtifactDeployer interface. The primary benefit of this technique is atomic Oracle WebLogic Server deployment, which can be rolled back if needed. For more information on this interface, see Summary of General Interfaces, and When to Implement TransportWLSArtifactDeployer.
The server has a limited number of threads to work with when processing messages, so asynchrony is important. This feature allows 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 Service Bus asynchronously through a callback.
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 (XSD) file. For more information about this file, see Step 3. Create an XML Schema File for Transport-Specific Artifacts. This schema is available to the Service Bus pipeline for filtering and routing purposes.
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.
The Transport SDK supports load balancing and failover for outbound messages. The following load balancing options are supported:
None: For each outbound request, the transport provider cycles through the URIs in the list in which they were entered and attempts to send a message to each URI until a successful send is completed.
Round Robin: Similar to None, but in this case, the transport provider keeps track of the last URI that was tried. Each time a message is sent, the provider starts from the last position in the list.
Random: The transport provider tries random URIs from the list in which they were entered.
Weighted Random: Each URI is associated with a weight. An algorithm is used to pick a URI based on this weight.
This section explains the basic use cases for writing a custom transport provider.
In some cases, it is appropriate to choose an alternative approach.
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 Service Bus that allows you to configure individual endpoints, which can be inbound, outbound or both. With a custom transport implementation, you can map the metadata and header fields of the specialized transport 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 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 the following:
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.
Using industry-specific transports, such as LLP, AS3, and ACCORD.
Alternatively, you can use the Transport SDK to support a specialized protocol over one of the existing transports provided with Service Bus. Examples of this could include supporting any of the following:
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 Service Bus JMS transport (for example, a response queue defined in the message context).
Creating a new Service Bus transport provider using the Transport SDK can be a significant effort. The Transport SDK provides a rich, full featured environment so a custom transport can have all of the usefulness and capabilities of the transports that come natively with 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 Using Java Callouts and POJOs..
Below are some additional cases where it is best not to use the Transport SDK to develop a custom transport provider:
When combining existing Oracle solutions with Service Bus satisfies the transport requirement; for example, Oracle WebLogic Server, Oracle WebLogic Integration, Oracle Data Service Integrator, Oracle Business Process Management, Oracle Tuxedo, and Oracle WebLogic Portal.
When service enablement tools provide a simpler and more standards-based mechanism to implement SOA practices.
When alternative connectivity solutions (certified with Service Bus) also address the requirement; for example: iWay adapters and Cyclone B2B.
When EJBs can be used instead as a means to abstract some type of simple Java functionality.
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.
In 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.
For a list of the required interfaces, see Developing Custom Transport Providers. Transport SDK Interfaces and Classes. provides a summary of the interfaces and classes provided by the Transport SDK. The Java API Reference for Oracle Service Bus provides detailed descriptions.
The design-time part of a custom transport provider consists of the user interface configuration. This configuration is called by the Oracle Service Bus Console or IDE when a new business or proxy service is being registered. Figure 39-1 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 and also manages the life cycle of the endpoints. For example, you can suspend a transport endpoint managed through the
TransportUIBinding: This interface helps the Oracle Service Bus Console render the transport specific pages.
Figure 39-1 Design Time UML Diagram
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 39-2 shows the relationship between the shared endpoint configuration properties and transport provider specific configuration properties. For more information, see Overview of Transport Endpoint Properties.
Figure 39-2 EndPointConfiguration Properties
The runtime part of a custom transport provider receives messages and delivers them to the Service Bus runtime. It also delivers outbound messages from the Service Bus runtime to external services.
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
TransportSender. The transport provider retrieves the header and body from the message.
Figure 39-1 shows a UML diagram that depicts the structure of the runtime part of a transport provider.
Figure 39-3 Runtime UML Diagram
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 Service Bus and how that model relates to the Transport SDK.
A transport endpoint is a Service Bus resource, such as a JMS proxy service, where messages are originated or targeted. In 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 Service Bus, it is useful to review some of the properties of service transport 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 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.
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.
All 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 this transforms 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:
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. Service Bus supports this case when the inbound transport is paired with any other outbound transport, with one exception, as described in the following paragraph.
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 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 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.
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.
Service Callout pipeline actions allow you to make a callout from the pipeline to another service. If a Service Callout action is made to a synchronous transactional transport, Exactly Once and Best Effort quality of service are supported. 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. For more information, see 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.
For more information on Service Callouts, see Adding Service Callout Actions in the Console.
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 resumes after the "send" operation is complete.
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.
Although this document does not contain specific guidelines on how to develop secure transport providers, this section discusses certain security goals of the Transport SDK.
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 authentication
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 the transport provider authenticates the client it must make the client Subject object available to Service Bus by calling
TransportManager.receiveMessage() within the scope of
weblogic.security.Security.runAs(subject). For information on this method, see the Java API Reference for Oracle Service Bus.
For information on the Java class Subject, see
The proxy services uses this Subject in the following ways:
During access control to the proxy service
To populate the message context variable
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 make the following calls:
Subject anonymous = SubjectUtils.getAnonymousUser() Security.runAs(anonymous, action)
The transport provider is also responsible for providing any Oracle Service Bus Console configuration pages required to configure inbound client authentication. The transport provider must clearly document its inbound authentication model.
Transport providers are free to implement whatever outbound authentication schemes are appropriate to that transport. The Transport SDK includes methods to facilitate outbound user name and password authentication, (two-way) SSL client authentication, and JAAS Subject authentication.
Outbound user name and password authentication can be implemented by leveraging Service Bus service accounts. Service accounts are first-class, top-level Service Bus resources.You create and manage service accounts in the Oracle Service Bus Console or in JDeveloper. 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 the service accounts.
Transport providers are not concerned with the details of service account configuration. There are three types of service accounts:
Static: A static service account is configured with a fixed user name and password.
Mapped: A mapped service account contains a list of remote-users and remote-passwords, along with 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 user name and password of the 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). Service Bus returns the user name and 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.
CredentialCallback object is made available to the transport provider by calling
CredentialCallback.getUsernamePasswordCredential() returns a
weblogic.security.UsernameAndPassword instance. This is a simple class that has methods to get the user name and password. The user name and password returned depends on the type of service account. If the service account is of type static, the fixed user name and password is returned. If it is mapped, the client subject is used to look up the remote user name and password. If it is pass-through, the client's user name and password is returned.
A mapped service account throws a
CredentialNotFoundException if one of the following occurs:
There is no map for the inbound client.
The inbound security context is anonymous and there is no anonymous map.
Service Bus supports outbound SSL client authentication. In this case, the proxy service 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, and 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.
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 user name and password for establishing the connection. Note that 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.
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.
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 Service Bus if they are needed for reasons specific to the transport provider. If that is the case, contact your Oracle representative. In general Oracle recommends transport providers let 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.
Service Bus makes the request headers available to the authorization providers for making access control decisions.
As explained in Outbound Request Authentication , Service Bus provides three types of service accounts. A transport provider can make use of service accounts to get access to the user name and password for outbound authentication. A service account hides all of the details of identity propagation and credential mapping from Service Bus transport providers.
This section provides an illustration 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 Service Bus (using a callback). The response is received, packaged, and handed to the 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.
Figure 39-4 Sample Service Bus Threading Model
During inbound request message processing, the following actions occur in the same thread:
An inbound message is received by the front end artifact of the transport endpoint. This front end artifact could be something like an HTTP servlet or JMS message-driven bean instance.
The message is packaged into a
TransportMessageContext object by the transport endpoint implementation and is passed to the Service Bus runtime. For more information on the
TransportMessageContext interface, see Metadata and Header Representation for Request and Response Messages.
The pipeline performs the configured request pipeline actions.
While processing the inbound message in the pipeline, in the same (request) thread, 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.
At this point, the initial request thread is released and placed back into the Oracle WebLogic Server thread pool for use by another request.
During outbound response message processing, the following actions occur in the same thread:
The response message is packaged into a
TransportMessageContext object and delivered back to the 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.
After the response message is processed, Service Bus runtime calls on the
InboundTransportMessageContext object to notify it to send the response back to the original caller. For more information on the
InboundTransportMessageContext interface, see Metadata and Header Representation for Request and Response Messages.
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 the 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 the 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 Using the EJB Transport.
By design, the transport subsystem interacts asynchronously with Service Bus. This is because 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, the 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.
The transport subsystem behaves the same way for 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 a Service Bus pipeline. Therefore, some differences exist between the threading behavior of Publish and Service Callout actions and transport providers.
Note the following cases:
Service Callout: The pipeline processor blocks the thread until the response arrives asynchronously. The blocked thread then resumes 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.
A Service Callout action allows you to configure a synchronous (blocking) call to a proxy or business service that is already registered with Service Bus. Use a Publish action to identify a target service for a message and configure how the message is packaged and sent to that service. For more information on Service Callout and Publish actions, see Adding Service Callout Actions in the Console and Adding Publish Actions in the Console.
Transport providers have their own native representation of message content.
For example, the HTTP transport uses
java.io.InputStream, JMS has Message objects of various types, Tuxedo has buffers, and the Oracle WebLogic Server Web Services stack uses SAAJ. However, within the runtime of a proxy service, the native representation of content is the Message Context. While Service Bus supports some common conversion scenarios, such as InputStream to and 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 unmarshal the source content into the Message Context first and then use the standard mechanisms to generate content for the outgoing transport.
Content is represented as an instance of the
Source interface. Transport SDK interfaces that deal with message content, such as
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
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. For more information, see Source and Transformer Classes and Interfaces.
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
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:Body> elements can be extracted to initialize the
$body context variables.
Below are the canonical
Source types used for the set of defined service-types:
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
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 39-5.
Figure 39-5 Flow of Attachments
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 is already 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 is likely an instance of
MimeSource, but this can also be converted to a
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.
Table 39-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
MFLSource. Typically, these transformers take advantage of their knowledge of the internal data representation used by both
Table 39-1 Built-In Transformations
|Public Source||Can Be Transformed To|
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:
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
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, that data is just a set of bytes without structure, so there is no need to provide custom transformations to, for example,
XmlObjectSource. The generic transformation can handle this custom
FileSource using just the base serialization methods that all Sources must implement.
For more information, see Source and Transformer Classes and Interfaces.