blob: babbf7dc5e911f9b5d09167666d231c740194b55 [file] [log] [blame]
<!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 &gt; 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) &gt; -1) {
soapNamespaceURI = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;
} else if (mimeMessage.getContentType().indexOf(SOAP11Constants.SOAP_11_CONTENT_TYPE) &gt; -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>