Mule : Writing Mule Transport Providers

http://docs.huihoo.com/mule/1.3/Writing%20Mule%20Transport%20Providers.html



Transport Provider Interfaces

There are set of interfaces that need to be implemented when writing a transport provider for Mule. These interfaces define the contract between Mule and the underlying technology.

org.mule.umo.UMOConnector

The connector is used by Mule to register listeners and create message dispatchers for the transport. Configuration parameters that should be shared by all Message Receivers and Dispatchers are stored on the connector. Usually, only one connector instance is needed for multiple inbound and outbound endpoints as multiple Message Receivers and Dispatchers can be associated with a connector. However, where the underlying transport API has the notion of a Connection such as the Jms or Jdbc API there should be a one to one mapping between Mule Connector and the underlying connection.

org.mule.umo.UMOMessageReceiver

The Message Receiver is used to receive incoming data from the underlying transport and package it up as an event. The Message Receiver is essentially the server implementation of the transport (where the Message Dispatcher is a client implementation). For example, the Http Message Receiver is a Http server implementation that accepts http requests. An implementation of this class is needed if the transport supports inbound communication.

org.mule.umo.UMOMessageDispatcher

The Message Dispatcher is used to send events, which is akin to making client calls with the underlying technology. For example the Axis Message Dispatcher will make a web service call. An implementation of this class is needed if the transport supports outbound communication.

org.mule.umo.UMOMessageDispatcherFactory

This is a factory class used to create UMOMessageDispatcher instances. An implementation of this class is needed if the transport supports outbound communication.

org.mule.umo.UMOMessageAdapter

The message adapter is used to provide a consistent way of reading messages in Mule. The massage adapter provides methods for reading the payload of the message and reading message properties. These properties may be message headers, custom properties or other meta information about the message.

Implementation

Where do I start?

Mule provides abstract implementations for all of the above interfaces. These implementations handle all the Mule specifics leaving a few abstract methods where custom transport code should be implemented. So writing a custom transport provider is as easy as writing/embedding client and or server code specific to the underlying technology. The following sections describes the implementations available to you.

Connectors

The org.mule.providers.AbstractConnector implements all the default functionality required for Mule connectors, such as threading configuration, and receiver/dispatcher management. Details about the standard connector properties can be foundhere.

Further properties can be set on the connector depending on the implementation. Often a connector will have a set of properties that can be overridden on a per endpoint basis, meaning the properties set on the connector serve as defaults when an endpoint configuration doesn't provide overrides.

Sometimes connector is responsible for managing a connection resource of the transport where the underlying technology has the notion of a Connection, such as in Jms or Jdbc. These types of connector will have a one to one mapping between a Mule connector and the underlying connection. So if you want to have two or more physical Jms connections in a single Mule instance a new connector should be created for each connection.
For other transports there will be only connector of a particular protocol in a Mule Instance that manages all endpoint connections. One such example would be socket based transports such as Tcp where each receiver manages its own ServerSocket and the connector manages multiple receivers.

Methods to Implement

Method Name Description Required
getProtocol() This should return the protocol of the provider such as 'smtp' or 'jms'. Yes
createReceiver() This method should create a UMOMessageReceiver instance based on resources and properties available on the connector. Yes
doInitialise() Is called once all bean properties have been set on the connector and can be used to validate and initialise the connectors state. No
doStart() If there is a single server instance or connection associated with the connector i.e. AxisServer or a Jms Connection or Jdbc Connection, this method should put the resource in a started state. No
doStop() Should put any associated resources into a stopped state. Mule will automatically call the stop() method No
doDispose() Should clean up any open resources associated with the connector. No

Message Receivers

Message Receivers will behave a bit differently for each transport, but Mule provides some standard implementations that can be used for polling resources and managing transactions for the resource. Usually there are 2 types of Message Receiver; Polling and Listener-based.

  1. Polling Receivers poll a resource such as the file system, database. And streams
  2. Listener-based are Receivers that register itself as a listener to a transport. Examples would be Jms (javax.message.MessageListener) or Pop3 (javax.mail.MessageCountListener). These base types may be transacted.

The abstract implementations provided by Mule are described below.

AbstractMessageReceiver

The AbstractMessageReceiver provides methods for routing events. Developers extending this class should set up the necessary code to register the object as a listener to the transport. This will usually be a case of implementing a listener interface and registering itself .

Methods to Implement

Method name Description Required
doConnect() Should make a connection to the underlying transport i.e. connect to a socket or register a soap service. When there is no connection to be made this method should be used to check that resources are available. For example the FileMessageReceiver checks that the directories it will be using are available and readable. The MessageReceiver should remain in a 'stopped' state even after the doConnect() method is called. This means that a connection has been made but no events will be received until the start() method is called. Calling start() on the MessageReceiver will call doConnect() if the receiver hasn't connected. Yes
doDisconnect() Disconnects and tidies up any rources allocted using the doConnect() method. This method should return the MessageReceiver into a disconnected state so that it can be connected again using the doConnect() method. Yes
doStart() Should perform any actions necessary to enable the reciever to start reciving events. This is different to the doConnect() method which actually makes a connection to the transport, but leaves the MessageReceiver in a stopped state. For polling-based MessageReceivers the start() method simply starts the polling thread, for the Axis Message receiver the start method on the SOAPService is called. What action is performed here depends on the transport being used. Most of the time a custom provider doesn't need to override this method. No
doStop() Should perform any actions necessary to stop the reciever from receiving events. No
doDispose() Is called when the Conector is being dispoed and should clean up any resources. The doStop() and doDisconnect() methods will be called implicitly when this method is called. No

PollingMessageReceiver

Its quite common for some transport providers to poll a resource periodically waiting for new data to arrive. The PollingMessageRecievers implements the code necessary to setup and destroy a listening thread and provides single methodpoll() that is invoked repeatedly at a given frequency.

Methods to Implement

Method name Description Required
poll() Is executed repeatedly at a configured frequency. This method should execute the logic necessary to read the data and return it. The data returned will be the payload of the new event. Returning null will cause no event to be fired. Yes

TransactedMessageReceiver

The TransactedMessageReceiver can be used by transaction-enabled transports to manage transactions for incoming requests. This receiver uses a transaction template to execute requests in transactions and the transactions themselves are created according to the endpoint configuration for the receiver.

Methods to Implement

Method name Description Required
getMessages() returns a list of objects that represents individual event payloads. The payload can be any type of object and will by sent to Mule Services wrapped in an MuleEvent object. Yes
processMessage(Object) - is called for each object in the list returned fromgetMesssages(). Each object processed is managed in its own transaction. Yes

Connection Strategy

Connection Strategy classes can be used to customise the way a connection is made to the transport when the connection fails or the Mule connector is started. Connection strategies are responsible for controlling how and when the doConnect() method is called on the UMOMessageReceiver and can provide fault tolerance when endpoints unexpectedly disconnect. For example a strategy may be to try and reconnect 4 times at 5 second intervals. Connection strategies can be configured on each connector independently or a global strategy can be set on the MuleConfiguration object (which is configured in Xml using the <mule-environment-properties> element). Custom connection strategies must implementorg.mule.providers.ConnectionStrategy. There is an AbstractConnectionStrategy that has support for executing the connection in a separate thread in case of a long-running strategy.

Thread Management

It's common for receivers to spawn a thread per request. All receiver threads are allocated using the WorkManager on the receiver. The WorkManager is responsible for executing units of work in a thread. It has a thread pool that allows threads to be reused and ensures that only a prescribed number of threads will be spawned.

The WorkManager is an implementation of UMOWorkManager which really just a wrapper ofjavax.resource.spi.work.WorkManager with some extra lifecycle methods. There is agetWorkManager() method on the AbstractMessageReceiver that can be used to get reference to the WorkMAnager for the receiver. Work items (i.e. the code to execute in a separate thread) must implementjavax.resource.spi.work.Work. This extends java.lang.Runnable and thus has a run() method which will be invoked by the WorkManager.

When scheduling work with the WorkManager it is recommended that Developers call scheduleWork(...) on the WorkManager rather than startWork(...).

Message Dispatchers

Messages Receivers are equivalent to a server for the transport in that it will serve up client requests. Whereas, Message Dispatchers are the client implementation of the transport. They are responsible for making client requests over the transport, such as writing to a socket or invoking a web service. The AbstractMessageDispater provides a good base implementation leaving 3 methods for the custom MessageDispatcher to implement.

Methods to Implement

Method name Description Required
doSend(UMOEvent) Should send the event payload over the transport. If there is a response from the transport it shuold be returned from this method. The sendEvent method is called when the endpoint is running synchronously and any response returned will ultimately be passed back to the callee. This method is executed in the same thread as the request thread. Yes
doDispatch(UMOEvent) Is invoked when the endpoint is asynchronous and should invoke the transport but not return any result. If a result is returned it should be ignored and if they underlying transport does have a notion of asynchronous processing, that should be invoked. This method is executed in a different thread to the request thread. Yes
doReceive(UMOImmutableEndpoint, long) Can be used to make arbitary requests to a transport resource. if the timeout is 0 the method should block until an event on the endpoint is received. Yes
doDispose() Is called when the Dispatcher is being disposed and should clean up any open resources. Yes
doConnect(UMOImmutableEndpoint) Should make a connection to the underlying transport i.e. connect to a socket or register a soap service. When there is no connection to be made this method should be used to chack that resources are available. For example the FileMessageDispatcher checks that the directories it will be using are available and readable. The MessageDispatcher should remain in a 'stopped' state even after the doConnect() method is called. Yes
 doDisconnect() Disconnects and tidies up any rources allocted using the doConnect() method. This method should return the MessageDispatcher into a disconnected state so that it can be connected again using the doConnect() method  Yes

Threads and Dispatcher Caching

Custom transports do not need to worry about dispatcher threading. Unless threading is turned off, the Dispatcher methods listed above will be executed in their own thread. This is managed by theAbstractMessageDispatcher.

When a request is made for a dispatcher, it is looked up from a dispatcher cache on theAbstractConnector. The cache is keyed by the endpoint being dispatched to. If a Dispatcher is not found one is created using the MessageDispatcherFactory and then stored in the cache for later. If developers want a new Dispatcher to be created for each request they can set the disposeDispatcherOnCompletion property on theAbstractConnector to true. This will essentially turn off dispatcher caching.

Message Adapters

MessageAdapters are usually simple objects that provide a uniform way of accessing an event payload and associated metadata from a format used by the underlying transport. Almost all messaging protocols have the notion of message payload and header properties, which means that a MessageAdapter just needs to allow access to the header properties using standard Map notation i.e.

//Jms message id
String id = (String)message.getProperty("JMSMssageID");

or
//Http content length
int contentLength = message.getIntProperty("Content-Length");

Note that the property names use the same name that is used by the underlying transport; 'Content-Length' is a standard Http header name and JMSMessageID is the equivalent bean property name on thejavax.jms.Message interface.

Message Adapter should extend the org.mule.provider.AbstractMessageAdapter as this abstract class implements much of the mundane methods needed by theorg.mule.providers.UMOMessageAdapter .

Methods to Implement

Method name Description Required
getPayload() Returns the message payload 'as is'. Yes
getPayloadAsString() Returns a java.lang.String representation of the message payload. Yes
getPayloadAsBytes() Returns a byte[] representation of the message payload. Yes
getUniqueId() Returns a java.lang.String unique id for the message. The Id can be an Id recognised by the underlying transport or it can be generated if the transport has no notion of a UniqueId. This id is used by various routers when correlating messages. Yes

Service Descriptors

A service descriptor is a file that contains a number of properties that describes how the internals of a transport provider is configured i.e. which Dispatcher factory to use or which endpoint builder to use.
The service descriptor must be a file with the same name as the protocol of the transport stored in the META-INF directory, where protocol might be Jms, Http or File.

META-INF/services/org/mule/providers/<protocol>

The following describes each of the properties that can be set in a Transport service descriptor.

Property Description Required
connector The class name of the Connector class to use. This must be an implementation of org.mule.umo.provider.UMOConnector. Yes
connector.factory The class name of the Connector factory class to use when creating the connector instance. This can be used to gain greater control over the connector when it is created by Mule. This must be an implementation or org.mule.util.ObjectFactory and must return an implementation of org.mule.umo.provider.UMOConnector. No
message.receiver The class name of the Message Receiver class to use. This must be an implementation or org.mule.umo.provider.UMOMessageReceiver. No (if outbound only)
transacted.message.receiver The class name of the Transacted Message Receiver class to use. Some transports implement a transacted Message Receiver separately, in which case the MessageReceiver class can be specified here so Mule knows which receiver to use when creating endpoints that are transacted. This must be an implementation or org.mule.umo.provider.UMOMessageReceiver. No
dispatcher.factory The class name of the Dispatcher factory class to use. This must be an implementation of org.mule.umo.provider.UMOMessageDispatcherFactory. No (if inbound only)
message.adapter The Message adapter to use for this connector when receiving events. This is the class name of the Message Adapter to use which must implement org.mule.umo.providers.UMOMessageAdapter No (if outbound only)
inbound.transformer The default transformer to use on inbound endpoints using this transport if no transform has been explicitly set on the endpoint. The property is the class name of a transformer that implements org.mule.umo.UMOTransformer. No
outbound.transformer The default transformer to use on outbound endpoints using this transport if no transform has been explicitly set on the endpoint. The property is the class name of a transformer that implements org.mule.umo.UMOTransformer. No
response.transformer The default transformer to use on inbound endpoints using this transport if no transformer has been explicitly set for the response message flow in Request/Response style messaging. The property is the class name of a transformer that implements org.mule.umo.UMOTransformer. No
endpoint.builder The class name of the endpoint builder used to build an UMOEndpointURI from an URI address. Mule provides a standard set of endpoint builders such as ResourceNameEndpointBuilder used by JMS and VM, SocketEndpointBuilder used by Tcp, http and Udp, UrlEndpointBuilder used by soap. Custom endpoint builders should extend org.mule.impl.endpoint.AbstractEndpointBuilder. Yes
service.finder Where a transport provider has more than one implementation (such as Soap which has Axis and Glue implementations), a service finder can be used to determine which implementation should be used when it hasn't been explicitly defined by the endpoint i.e. soap:http://... rather than axis:http://... Usually a service finder will check for a known class for each of the transport implementations and return the transport-specific name to use when creating the connector. No

Coding Standards

Package Structure

All mule providers have a similar package structure. They follow the convention of -

org.mule.providers.<protocol>

Where protocol is the protocol identifier of the transport such as 'tcp' or 'soap'. Any transformers and filters for the transport are stored either a 'transformers' or 'filters' package under the main package. Note that where a provider may have more than one implementation for a given protocol i.e. There are two Soap implementations in Mule, Axis and Glue, the package name to be used should be soap not axis or glue.

Internationalisation

Any exceptions messages used in your transport provider implementation should be stored in a resource bundle so that they can beinternationalised . The message bundle is a standard java properties file and must be located at -

META-INF/services/org/mule/i18n/<protocol>-message.properties

發佈了46 篇原創文章 · 獲贊 23 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章