<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | |
<html> | |
<head> | |
<meta http-equiv="content-type" content=""> | |
<title>Invoking a service using a mail</title> | |
</head> | |
<body> | |
<h1>Invoking a service using a mail</h1> | |
<h2>Prologue</h2> | |
<p>Most of Web services that we interact with are synchronous and | |
request-response in nature. However, we see that the synchronous | |
request-response type of interaction is only a part of the messaging | |
scenarios we encounter in real life. Asynchronous messaging is very important | |
in constructing loosely coupled systems. Take for instance a chain of stores, | |
at the end of the day all the stores all over can send a mail to the central | |
system telling it what that days business activity was and in the morning | |
when the store opens there will be a reply to that mail with new instructions | |
and updates. It is a lot like the way the old business worked but with a | |
modern touch. Similarly Axis2 mail transport can be used to implement | |
SETI@HOME through mail.</p> | |
<h2>Introduction</h2> | |
<p>To start you will first need to go through the Mail Transport <a | |
href="mail-configuration.html">configuration document</a>. It will give you | |
all the information you need on how to configure Axis2 to get mail transport | |
working.</p> | |
<p>Broadly speaking there are 3 ways of calling a service through mail.</p> | |
<blockquote> | |
1. Using the simple mail server included in Axis2.<br/> | |
2. Using a generic mail server.<br/> | |
3. Using mailets.<br/> | |
</blockquote> | |
<p></p> | |
<p>Option number 1 and 2 are fairly simple and easy to implement, whereas | |
option 3 is somewhat harder.The mailet scenario however does provide a more | |
robust and useful solution in a production environment.</p> | |
<p>If you are not a guru in mail related issues you should probably get | |
started on the simple mail server that has been provided with Axis2. Once you | |
get the hang of the Axis2 related issues then you can move on to tackle the | |
mail beast. Please do keep in mind that the mail server we have implemented | |
is ONLY FOR DEMONSTRATION/TESTING PURPOSES.</p> | |
<h3>1. Using the simple mail server included in Axis2</h3> | |
<p>The SMTP/POP server that we have included has the ability to function as a | |
standalone SMTP/POP server and also has the ability to work as a mailet. All | |
this is done through a small filter that keeps watch for certain | |
pre-configured email addresses. These pre-configured email addresses can be | |
changed by doing a simple edit of the filter class | |
org.apache.axis2.transport.mail.server.Sorter.</p> | |
<p>Now that we have the environment set up, let us start pumping out some | |
code to get the mail functionality off the ground. First we'll have a look at | |
it from the mail server side. <br/> | |
</p> | |
<source><pre> // Start the mail server using the default configurations. | |
ConfigurationContext configContext = UtilsMailServer.start(); | |
// Start the default mail listener. It will starting poling for mail | |
// using the configuration from the XML file. | |
SimpleMailListener ml = new SimpleMailListener(); | |
ml.init(configContext, configContext.getAxisConfiguration() | |
.getTransportIn(new QName(Constants.TRANSPORT_MAIL))); | |
ml.start(); | |
// Setup a service that will echo what we send to the server. | |
AxisService axisService = Utils.createSimpleService(serviceName, | |
Echo.class.getName(), new QName("echoOMElement")); | |
configContext.getAxisConfiguration().addService(axisService); | |
Utils.resolvePhases(configContext.getAxisConfiguration(), axisService); | |
ServiceContext serviceContext = configContext | |
.createServiceContext(new QName("EchoXMLService"));</pre> | |
</source> | |
<p>This code sets up your Axis2 server working through mail, with a single | |
service. If you need to have a look under the hood check out the MailServer | |
and UtilsMailServer classes.</p> | |
<p>Moving onto the client side have a look at the code listing below. It will | |
call the axisService that was setup on the previous code listing.</p> | |
<source><pre> ConfigurationContext configContext = UtilsMailServer | |
.createClientConfigurationContext(); | |
AxisService axisService = new AxisService(serviceName); | |
AxisOperation axisOperation = new AxisOperation(operationName); | |
axisOperation.setMessageReceiver(new MessageReceiver() { | |
public void receive(MessageContext messgeCtx) throws AxisFault { | |
envelope = messgeCtx.getEnvelope(); | |
} | |
}); | |
axisService.addOperation(axisOperation); | |
configContext.getAxisConfiguration().addService(axisService); | |
Utils.resolvePhases(configContext.getAxisConfiguration(), axisService); | |
ServiceContext serviceContext = configContext | |
.createServiceContext(serviceName); | |
Options options = new Options(); | |
options.setTo(targetEPR); | |
options.setTransportInProtocol(Constants.TRANSPORT_MAIL); | |
options.setUseSeparateListener(true); | |
// Create a callback to set to the axisService invocation. | |
Callback callback = new Callback() { | |
public void onComplete(AsyncResult result) { | |
try { | |
result.getResponseEnvelope().serialize( | |
XMLOutputFactory.newInstance() | |
.createXMLStreamWriter(System.out)); | |
} catch (XMLStreamException e) { | |
reportError(e); | |
} finally { | |
finish = true; | |
} | |
} | |
public void reportError(Exception e) { | |
log.info(e.getMessage()); | |
finish = true; | |
} | |
}; | |
ServiceClient sender = new ServiceClient(configContext, axisService); | |
sender.setOptions(options); | |
options.setTo(targetEPR); | |
// Call the service and start poling for the reply from the server. | |
sender.sendReceiveNonBlocking(operationName.getLocalPart(), createEnvelope(), callback); | |
int index = 0; | |
while (!finish) { | |
Thread.sleep(1000); | |
index++; | |
if (index > 10) { | |
throw new AxisFault( | |
"Server is being shutdown as the Async response is taking too long."); | |
} | |
} | |
</pre> | |
</source> | |
<p>This will call the service that was setup on the server and will poll the | |
mail server till the response is received.Thats all there is to it. Please do note that | |
serviceName and operationName need to be QNames. | |
</p> | |
<p></p> | |
<h3>2. Using a generic mail server</h3> | |
<p>First you need two email accounts that works with POP/SMTP. One will act | |
as a server and the other will act as the client. For the time being we will | |
use server@somewhere.org and client@somewhere.org as the server and the | |
client email addresses. Now that we have the email addresses you will have to | |
set up the client and the server with Mail Transport <a | |
href="http://ws.apache.org/axis2/mail-transport.html">introduction | |
document</a>.</p> | |
<p>When calling the generic mail server the client side code will remain the | |
same and there will be some modification to the server-side code.</p> | |
<p></p> | |
<source><pre> // Create a configuration context. This will also load the details about the mail | |
// address to listen to from the configuration file. | |
File file = new File(MAIL_TRANSPORT_SERVER_ENABLED_REPO_PATH); | |
ConfigurationContextFactory builder = new ConfigurationContextFactory(); | |
ConfigurationContext configContextbuilder | |
.buildConfigurationContext(file.getAbsolutePath()); | |
// Startup the default mail server and start listening to a | |
// mail address. | |
SimpleMailListener ml = new SimpleMailListener(); | |
ml.init(configContext, configContext.getAxisConfiguration() | |
.getTransportIn(new QName(Constants.TRANSPORT_MAIL))); | |
ml.start(); | |
// Setup a simple service. | |
AxisService axisService = Utils.createSimpleService(serviceName, | |
Echo.class.getName(), operationName); | |
configContext.getAxisConfiguration().addService(axisService); | |
Utils.resolvePhases(configContext.getAxisConfiguration(), axisService); | |
ServiceContext serviceContext = configContext.createServiceContext(serviceName);</pre> | |
</source> | |
<p>Note that a separate ConfigurationContext needs to be created and used.</p> | |
<h3>3. Calling Axis through a James mailet</h3> | |
<p>This process will be a bit more challenging than the other two methods but | |
will provide a really elegant way to use the mail transport. Before we get | |
started you will have to go though the James documents <a | |
href="http://james.apache.org/custom_matcher_2_1.html">Writing a Custom | |
Matcher</a> and <a | |
href="http://james.apache.org/custom_mailet_2_1.html">Writing a Custom | |
Mailet</a>.</p> | |
<p>Now that we know the James part of it lets dive into to the Axis2 part of | |
the code. Once you have set up the James side of business we need to use the | |
same functionality that is used in the above code. Have a look at the code | |
listing below for more details</p> | |
<source><pre> public void processMail(ConfigurationContext confContext, MimeMessage mimeMessage) { | |
// create an Axis server | |
AxisEngine engine = new AxisEngine(confContext); | |
MessageContext msgContext = null; | |
// create and initialize a message context | |
try { | |
// Create a message context with mail as in and out transports. | |
msgContext = | |
new MessageContext(confContext, | |
confContext.getAxisConfiguration().getTransportIn(new QName(Constants.TRANSPORT_MAIL)), | |
confContext.getAxisConfiguration().getTransportOut(new QName(Constants.TRANSPORT_MAIL))); | |
msgContext.setServerSide(true); | |
msgContext.setProperty(MailConstants.CONTENT_TYPE, mimeMessage.getContentType()); | |
msgContext.setWSAAction(getMailHeader(MailConstants.HEADER_SOAP_ACTION, mimeMessage)); | |
// The service path is in the subject of the mail. | |
String serviceURL = mimeMessage.getSubject(); | |
if (serviceURL == null) { | |
serviceURL = ""; | |
} | |
String replyTo = ((InternetAddress) mimeMessage.getReplyTo()[0]).getAddress(); | |
if (replyTo != null) { | |
msgContext.setReplyTo(new EndpointReference(replyTo)); | |
} | |
String recipients = ((InternetAddress) mimeMessage.getAllRecipients()[0]).getAddress(); | |
if (recipients != null) { | |
msgContext.setTo(new EndpointReference(recipients + "/" + serviceURL)); | |
} | |
// add the SOAPEnvelope | |
String message = mimeMessage.getContent().toString(); | |
ByteArrayInputStream bais = new ByteArrayInputStream(message.getBytes()); | |
XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(bais); | |
// This is just in place to work with SOAP 1.1 and 1.2. | |
String soapNamespaceURI = ""; | |
if (mimeMessage.getContentType().indexOf(SOAP12Constants.SOAP_12_CONTENT_TYPE) > -1) { | |
soapNamespaceURI = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI; | |
} else if (mimeMessage.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); | |
} | |
} catch (Exception e) { | |
try { | |
if (msgContext != null) { | |
MessageContext faultContext = engine.createFaultMessageContext(msgContext, e); | |
engine.sendFault(faultContext); | |
} | |
} catch (Exception e1) { | |
log.error(e); | |
} | |
} | |
}</pre> | |
</source> | |
<p>If you don't happen to have a ConfigurationContext lying around to call | |
this method you can use the following bit of code to get one. Once you create | |
one you can store that on the mailet and keep using it.</p> | |
<source><pre> File file = new File(MAIL_TRANSPORT_SERVER_ENABLED_REPO_PATH); | |
ConfigurationContextFactory builder = new ConfigurationContextFactory(); | |
ConfigurationContext configContextbuilder | |
.buildConfigurationContext(file.getAbsolutePath());</pre> | |
</source> | |
<p></p> | |
</body> | |
</html> |