blob: 78fe2653d49b5b7552a301c2b13abc7544a395ef [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Invoking a service using
a mail</title>
</head>
<body>
<h1>Invoking
a service using a mail</h1>
<h2>Prologue</h2>
<p>Most of the 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 an 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 get things started 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 about how to configure Axis2 to get the 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 [unless of course you are one of those guys who see mails going
around in your dreams.]. However the mailet scenario provides 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 onto 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>Before we start I want to
reiterate the fact that the mail server included with Axis2 is ONLY FOR
DEMONSTRATION/TESTING PURPOSES.</p>
<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 have a look at 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);
org.apache.axis2.clientapi.Call call = new org.apache.axis2.clientapi.Call(
serviceContext);
call.setTo(targetEPR);
call.setTransportInfo(Constants.TRANSPORT_MAIL,
Constants.TRANSPORT_MAIL, 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;
}
};
// Call the service and start poling for the reply from the server.
call.invokeNonBlocking(operationName.getLocalPart(), createEnvelope(),
callback);
int index = 0;
while (!finish) {
Thread.sleep(1000);
index++;
if (index &gt; 10) {
throw new AxisFault(
"Server is being shutdown as the Async response is taking too long.");
}
}
call.close();
</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. Well that is all there is to it.</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 looking at the 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>We have to create a separate ConfigurationContext and then use that. We are done; wasn't that simple?</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 just 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>Well that seems to be all from the wonderful world of mail for now. :)</p>
</body>
</html>