To stop you from reinventing the wheel I will quickly list the transports that are already supported in Axis2 with a small description before we get started.
To understand the rest of this document you will need some understanding of the Architecture of Axis2. Therefor if you are not familiar with the Architecture of Axis2 you will have to first read the Axis2 Architecture Guide before you read any further.
Broadly speaking a transport inside Axis2 can be classified as a way of getting a messages that arrive though some channel into the Axis2 engine. The core of Axis2 is transport independent. All data that is transport specific is striped out of the incoming message and inserted into the MessageContext and on the outgoing message all transport specific information like headers are added and sent.
To write your own transport you will need to primarily write two classes, one is the TransportSender and the other is the TransportReceiver. To register a transport with Axis2 you will need to put two entries in the axis2.xml file. One for the transport receiver and the other for the transport sender. We will walk though the process of adding the entries in the relevent sections.
Any message that is comming into Axis2 needs to go though a transport receiver. All information about how the message is received at the Axis2 server from the wire [or by snail mail for that matter:)] is isolated inside the transport receiver. It extracts the data that is coming on the wire and transforms it into a state that the Axis2 server understands.
So now that we have some background information about how transports work inside Axis2 with out further delay why don't we dive into some coding and start building out own transport.
To get things stared you will first need to extend from the org.apache.Axis2.transport.TransportListener class and write you own transport listener. To create an engine to process the MessageContext we need a configuration context. The following code fragment will do this. This should ideally be only done once for the lifetime of the Transport receiver.
try {
//Create a factory
ConfigurationContextFactory factory = new ConfigurationContextFactory();
//Use the factory and an Axis2 repository to create a new Configuration Context
configurationContext = factory.buildConfigurationContext(repository_directory);
} catch (Exception e) {
log.info(e.getMessage());
}
Now we need some kind of a listener to listen to the requests that come in. This you will need to implement according to the transport that you are trying to build. After a message is received at the receiver you can use the following code to process the request and then forward the message context to the engine using thing the engine.receive(msgContext) method.{Following code snippet extracted from MailListener as an example}
AxisEngine engine = new AxisEngine(configurationContext); MessageContext msgContext = null; // create and initialize a message context try { TransportInDescription transportIn = reg.getAxisConfiguration().getTransportIn(new QName(Constants.TRANSPORT_NAME)); TransportOutDescription transportOut = reg.getAxisConfiguration().getTransportOut(new QName(Constants.TRANSPORT_NAME)); if (transportIn != null && transportOut != null) { //create Message Context msgContext = new MessageContext(configurationContext, transportIn, transportOut); msgContext.setServerSide(true); msgContext.setProperty(MailSrvConstants.CONTENT_TYPE, message.getContentType()); msgContext.setProperty(MessageContext.CHARACTER_SET_ENCODING, message.getEncoding()); String soapAction = message.getSOAPActionHeader(); msgContext.setWSAAction(soapAction); msgContext.setSoapAction(soapAction); // Here we are trying to set the reply to if it is present in the transport information. msgContext.setReplyTo(new EndpointReference(message.getReplyTo()); //Create the SOAP Message -- This code in from the mail transport and will change depending //on how the data is handled in each transport. ByteArrayInputStream bais = new ByteArrayInputStream(message.getContent().toString().getBytes()); XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(bais); String soapNamespaceURI = ""; if(message.getContentType().indexOf(SOAP12Constants.SOAP_12_CONTENT_TYPE) > -1){ soapNamespaceURI = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI; }else if(message.getContentType().indexOf(SOAP11Constants.SOAP_11_CONTENT_TYPE) > -1){ soapNamespaceURI = SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI; } StAXBuilder builder = new StAXSOAPModelBuilder(reader, soapNamespaceURI); SOAPEnvelope envelope = (SOAPEnvelope) builder.getDocumentElement(); msgContext.setEnvelope(envelope); if (envelope.getBody().hasFault()) { engine.receiveFault(msgContext); } else { engine.receive(msgContext); } } else { throw new AxisFault(Messages.getMessage("unknownTransport",Constants.TRANSPORT_NAME)); } } catch (Exception e) { try { if (msgContext != null) { MessageContext faultContext = engine.createFaultMessageContext(msgContext, e); engine.sendFault(faultContext); } else { log.error(e); } } catch (AxisFault e1) { log.error(e); } }
Now we have the coding in place and we need to let Axis2 know about our new transport receiver. How we do this is by addind an entry into the axis2.xml file. If you need to pass any properties for the transport to operate it can also be done through the axis2.xml file.
<transportReceiver name="TRANSPORT_NAME" class="org.apache.Axis2.transport.TRANSPORT_NAME.TRANSPORT_LISTNER_CLASS"> <parameter name="PROPERTY_NAME" locked="false">PROPERTY_VALUE</parameter> <parameter name="PROPERTY_NAME_2" locked="false">PROPERTY_VALUE_2</parameter> </transportReceiver>
Using a code fragment like Utils.getParameterValue(transportOut.getParameter(MailSrvConstants.SMTP_USER)) we can extract the parameters that we insert into the axis2.xml file.
So as you can see getting a new transport receiver up and running is a task that requires very little effort.
Any message that is to be sent out from Axis2 is sent through the Transport Sender. The Transport Sender needs to be extended from the org.apache.Axis2.transport.AbstractTransportSender class.
The following bit of code from the abstract transport sender will call the Transport Sender that you wrote.
// If an EPR is present then the message is going on a diffrent channel. if (epr != null) { out = openTheConnection(epr, msgContext); OutputStream newOut = startSendWithToAddress(msgContext, out); if (newOut != null) { out = newOut; } writeMessage(msgContext, out); finalizeSendWithToAddress(msgContext, out); } else { out = (OutputStream) msgContext.getProperty(MessageContext.TRANSPORT_OUT); if (out != null) { startSendWithOutputStreamFromIncomingConnection(msgContext, out); writeMessage(msgContext, out); finalizeSendWithOutputStreamFromIncomingConnection(msgContext, out); } else { throw new AxisFault( "Both the TO and Property MessageContext.TRANSPORT_WRITER is Null, No way to send response."); } }
So depending on whether your transport is using the same channel to send the responce or using a different channel you will need to implement a sub-set of the methods from the abstract class.
After implementing the necessary methods you can let Axis2 know about your new transport sender by adding an entry to the axis2.xml file like you did for the Transport Receiver.
<transportSender name="TRANSPORT_NAME" class="org.apache.Axis2.transport.TRANSPORT_NAME.TRANSPORT_SENDER_CLASS"> <parameter name="PROPERTY_NAME" locked="false">PROPERTY_VALUE</parameter> <parameter name="PROPERTY_NAME_2" locked="false">PROPERTY_VALUE_2</parameter> </transportSender>
Have a look at org.apache.Axis2.transport.mail.MailTransportSender for a very simple Transport Sender. Also have a look at org.apache.Axis2.transport.http.CommonsHTTPTransportSender which is used to send HTTP responses.
Once we have written our transport receiver and our transport sender, and inserted the needed entries into the axis2.xml file we are done. It is as simple as that. :-)