Feeds:
Posts
Comments

Archive for the ‘SOA’ Category

Recently I came across to the need to write a custom Message Formatter to handle a custom message type in Axis2.  As it turned out writing a Message Formatter is a darn simple thing. Mainly you only have to implement three methods. :).

My scenario was to extract out a set of attachments within an incoming SOAP (not SwA or MTOM. A custom XML format to hold attachments) and send out a HTTP MIME multi-part message without SOAP. It has a custom content type “application/x-mime”. Incoming XML format is roughly as follows.


<soap:Body>

<attachments>

<!-- 1 or more occurrances -->

<attachment>[base64 encoded binary]</attachment>

</attachments>

<text>[xml content here]</text>

</soap:Body>

Attachments are of “application/pdf” content-type while text content is normal “application/xml” content-type.  Out bound message should be  a HTTP MIME multi-part (with multipart/related content-type) message as follows. (this listing is without root http headers)


--MIME-Boundary1223233

Content-Type: application/xml

[xml content here]

--MIME-Boundary1223233

Content-Type: application/pdf

Content-Transfer-Encoding: base64

Content-ID: 1.urn:uuid:2F3457E2A5DC1F05631300801064296@apache.org>

[base64 attachment-1]

.

.

.

--MIME-Boundary1223233

Content-Type: application/pdf

Content-Transfer-Encoding: base64

Content-ID: 1.urn:uuid:3243457E2A5DC1F05631364801064523@apache.org>

[base64 attachment-n]

--MIME-Boundary1223233--

Now with this requirement in mind let’s go through how to achieve this with an Axis2 Message Formatter. The interface to implement is org.apache.axis2.transport.MessageFormatter.  It’s methods are..

public byte[] getBytes(MessageContext messageContext, OMOutputFormat format)
throws AxisFault;

public void writeTo(MessageContext messageContext, OMOutputFormat format,
OutputStream outputStream, boolean preserve) throws AxisFault;

public String getContentType(MessageContext messageContext, OMOutputFormat format,
String soapAction);

public URL getTargetAddress(MessageContext messageContext, OMOutputFormat format,
URL targetURL) throws AxisFault;

public String formatSOAPAction(MessageContext messageContext, OMOutputFormat format,
String soapAction);

Main message formatting logic goes inside writeTo method and content type of output message should be returned from the getContentType method implementation. The getBytes method should return output message as a byte array formatted according to the given message format.. Other methods are auxiliary and in my case I just left empty implementations in them.
Now let’s go through the main implementation..

public void writeTo(MessageContext messageContext, OMOutputFormat omOutputFormat,
OutputStream outputStream, boolean b) throws AxisFault {

   MultipartWriter mpWriter = JavaMailMultipartWriterFactory.INSTANCE.createMultipartWriter(
   outputStream, omOutputFormat.getMimeBoundary());

   // Write text content
   OMElement textElement = body.getFirstChildWithName(
   new QName("text"));

   if (textElement != null) {
      writeText(mpWriter, textElement, getContentID());
   }

   // Write attachments..
   SOAPBody body = messageContext.getEnvelope().getBody();
   OMElement attachmentsElement = body.getFirstChildWithName(new QName("attachments"));

   if (attachmentsElement != null) {

      Iterator<OMElement> attachments = attachmentsElement.getChildElements();
      while (attachments.hasNext()) {
        writeAttachment(mpWriter, attachments.next(), getContentID());
      }
   }

   // Complete writing the message and flush the stream
   try {
      mpWriter.complete();
   } catch (IOException e) {
      throw new AxisFault("Failed to complete writing the MIME message..", e);
   }
}

Here I have used MultipartWriter API from Axiom to write MIME multi-parts to the output stream. After initialization of the MultipartWriter text content and attachments are written using private helper methods shown in the next listing. Finally the call to mpWriter.complete() flushes the stream and finish writing the MIME message.

private String getContentID() {
   return "1.urn:uuid:" + UUID.randomUUID() + "@altisource.com>";
}

private void writeAttachment(MultipartWriter mpWriter, OMElement attachment, String contentID)
throws AxisFault {
   try {
      mpWriter.writePart(new DataHandler(new ByteArrayDataSource(attachment.getText()
      .getBytes(), "application/pdf")),
      "base64", contentID);
   } catch (IOException e) {
      throw new AxisFault("Failed to write attachment..", e);
   }
}

private void writeText(MultipartWriter mpWriter, OMElement text, String contentID)
throws AxisFault {
   try {
      mpWriter.writePart(new DataHandler(new ByteArrayDataSource(text.toString().getBytes(),
      "application/xml")), "",
      contentID);
   } catch (IOException e) {
      throw new AxisFault("Failed to write text content..", e);
   }
}

The getContentID() method returns a unique ID for each part using java.util.UUID. The getBytes method uses writeTo implementation to a ByteArrayStream and return a byte array.

public byte[] getBytes(MessageContext messageContext, OMOutputFormat omOutputFormat)
throws AxisFault {

   ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
   writeTo(messageContext, omOutputFormat, bytesOut, false);

   return bytesOut.toByteArray();

}

The complete listing is shown below.

import org.apache.axiom.attachments.ByteArrayDataSource;
import org.apache.axiom.mime.MultipartWriter;
import org.apache.axiom.mime.impl.javamail.JavaMailMultipartWriterFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.MessageFormatter;

import javax.activation.DataHandler;
import javax.xml.namespace.QName;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.Iterator;
import java.util.UUID;

public class MIMEFormatter implements MessageFormatter {

public byte[] getBytes(MessageContext messageContext, OMOutputFormat omOutputFormat)
throws AxisFault {

   ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
   writeTo(messageContext, omOutputFormat, bytesOut, false);

   return bytesOut.toByteArray();

}

public void writeTo(MessageContext messageContext, OMOutputFormat omOutputFormat,
OutputStream outputStream, boolean b) throws AxisFault {

   MultipartWriter mpWriter = JavaMailMultipartWriterFactory.INSTANCE.createMultipartWriter(
   outputStream, omOutputFormat.getMimeBoundary());

   // Write attachments..
   SOAPBody body = messageContext.getEnvelope().getBody();
   OMElement attachmentsElement = body.getFirstChildWithName(new QName("attachments"));

   if (attachmentsElement != null) {

      Iterator<OMElement> attachments = attachmentsElement.getChildElements();
      while (attachments.hasNext()) {
      writeAttachment(mpWriter, attachments.next(), getContentID());
   }

   // Write text content
   OMElement textElement = body.getFirstChildWithName(new QName("text"));

   if (textElement != null) {
      writeText(mpWriter, textElement, getContentID());
   }

   // Complete writing the message and flush the stream
   try {
      mpWriter.complete();
   } catch (IOException e) {
      throw new AxisFault("Failed to complete writing the MIME message..", e);
   }
}

public String getContentType(MessageContext messageContext, OMOutputFormat omOutputFormat,
String s) {
   return "multipart/related; " + " boundary=\"" + omOutputFormat.getMimeBoundary() + "\"; " +
   "type=\"application/xml\"";
}

public URL getTargetAddress(MessageContext messageContext, OMOutputFormat omOutputFormat,
URL url) throws AxisFault {
   return null;
}

public String formatSOAPAction(MessageContext messageContext, OMOutputFormat omOutputFormat,
String s) {
   return null;
}

/*******  Private Helper Methods *****/

private String getContentID() {
   return "1.urn:uuid:" + UUID.randomUUID() + "@altisource.com>";
}

private void writeAttachment(MultipartWriter mpWriter, OMElement attachment, String contentID)
throws AxisFault {
   try {
      mpWriter.writePart(new DataHandler(new ByteArrayDataSource(attachment.getText()
      .getBytes(), "application/pdf")),
      "base64", contentID);
   } catch (IOException e) {
      throw new AxisFault("Failed to write attachment..", e);
   }
}

private void writeText(MultipartWriter mpWriter, OMElement text, String contentID)
throws AxisFault {
   try {
      mpWriter.writePart(new DataHandler(new ByteArrayDataSource(text.toString().getBytes(),
      "application/xml")), "",
      contentID);
   } catch (IOException e) {
      throw new AxisFault("Failed to write text content..", e);
   }
}

}

Now you have to make a jar out of this class and put it in the classpath of Axis2 installation. Put the following entry under <messageFormatters> section in axis2.xml to engage this formatter for the content type “application/x-mime”.


<messageFormatter contentType="application/x-mime" class="MIMEFormatter"/>

That’s it. Pretty simple. Isn’t it. :).

Read Full Post »

Continuing my adventure with Apache Rampart from my earlier article I decided to follow the DeveloperWorks article on WS-Security. Again I ran in to some issues when I tried using signatures within the messages. Here is a brief account of what happened this time.

First road block I encountered was the error shown below. The error was on the client side and was like this.

org.apache.axis2.AxisFault: CryptoFactory: Cannot load properties:crypto.properties

Client didn’t seem to know where to pick up the properties file describing trust store details. After fiddling with the file paths and failing, I tried the Axis2 list and luckily found a suggestion which helped out with my predicament. The solution was to add the properties file location to the client’s classpath. So with that obstacle out of the way the next break point to hit was this.

problemAction: anonOutInOp
[ERROR] The [action] cannot be processed at the receiver.
org.apache.axis2.AxisFault: The [action] cannot be processed at the receiver.

This time around it was the server side which was giving me troubles. After scavenging a little on the Internet for information I found this article which described the issue as not properly setting the SOAP action when setting up the client. I was guilty as charged. I had forgotten to set the SOAP action at my client. Thanks to that tip I pressed ahead again to come up with this.

org.apache.axis2.AxisFault: WSHandler: Signature: error during message processingorg.apache.ws.security.WSSecurityException: An unsupported token was provided (Problem with SKI information: Support for RSA key only)

Again luckily I found a suggestion on Axis2 list which helped me through. The error was due to the choice of the algorithm that I used generate the key store. Instead of the default DSA using RSA algorithm was the solution.

So after another bumpy ride I was finally able to get it working for signatures and luckily providing encryption capability was pretty much a smooth ride afterwards.

Read Full Post »

It seems web service security is not the easiest thing to get right the first time as some things in life (at least for me :D). I found this the hard way by trying to secure a service web service deployed in Axis2 with Rampart. Idea was simple. To make a service require a time stamped message from client vice versa. So I added the required parameters for both services.xml at the service side and the axis2.xml of the client side and added and enagaged Rampart module at both sides. Naturally I now expected to things work and see time stamped message flying in and out from my client through the Apache TCPMon setup to intercept the message flow. But I was expecting things to happen bit too early.

Troubles started with Axis2 server installation spitting a ClassNotFoundException for some org.apache.rampart.xxx.RampartXXX . After playing with the installation and Rampart module for some time I found out that there is a set of jars within Rampart module download which I should add to Axis2 installation lib to get over this obstacle. I don’t remember finding any documentation about Rampart stating this fact. Of course I may not have been looking hard enough or looking in correct locations.

But anyway straight ahead I hit another exception. This time it was a depressing org.apache.axis2.deployment.DeploymentException: javax/jms/BytesMessage. This was promising to be a show stopper. Luckily this same issue had come in the Axis2 list and I found that this was due to a version mismatch. I had been using Rampart 1.4 module with Axis2 1.5.1 version. Solution was clear. To use recently released Rampart 1.5 version. Magically that error was no more.

Next up in the “exception line up” was java.lang.ClassNotFoundException: org.apache.axis2.transport.tcp.TCPTransportSender. Apparently this was due to some related jar missing from Axis2 distribution. As per some discussion on Axis2 list I tried commenting out in server axis2.xml to trivially to make things work. I got over the next ClassNotFoundException by commenting out the transportReceiver for TCP following the same logic.

I ran the service client once again not even half expecting things to work. All of a sudden… You can guess my jubilation. Yeah finally my little time stamped SOAP has seen the light of the day. Though a painful experience I learnt that perseverance pays in security business.

PS.

I came across some other types of exceptions during my endeavour which I think can be useful to know for some other person venturing to this area.

org.apache.axis2.AxisFault: Must Understand check failed for header http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd : Security
This can occur due to two reasons as I observed. Firstly if the rampart module has not been correctly engaged to the service this error can occur when invoked. Secondly if either both endpoints are not correctly configured so that one end is not providing for what other end is expecting this error can occur. Say for example the service is configured to accept only time stamped messages then if a client sends it a non time stamped message then this error will occur. The same way if the client is expecting a time stamped message on return and if the service is not providing that this exception will be raised at the client side.

Read Full Post »

SOA Workshop

Today I attended the SOA workshop conducted by WSO2 Inc. with colloboration with ICTA SriLanka and SLASSCOM. Sessions were conducted by engineers from WSO2. The workshop sessions were at an introductory level of major SOA concepts. Below are some of the notes I took down during the course of the sessions.

SOA Security

Why just using https may not be enough for securing web services?

  1. Https security is present only inside the transport channel. It’s more or like point to point security. Imagine your service container app is deployed within a Tomcat instance running in your local machine. All the encryption security is present until the message comes to Tomcat. Then the message decrypted and transfered in plaintext to the service container application and subsequently to the service instance itself. What if some one within the organisation having access to the deployment server devise a method to capture the plaintext message in transit to the service from application server? Though bit contrived this may be a possiblity and where message security is of utmost importance we may not be able to get away with just using https to secure our services.
  2. What if we want to encrypt just a part of the message? What if the business requirement specified that a  certain part should be visible to every party involved and only a certain part is subject to authorized access. This cannot be clearly achieved using https since all of the message is encrypted as it is sent over the wire.
  3. Soap messages can be sent using different transport applications or protocols like HTTP, SMTP, etc., which might have a very high security model to no security model at all. Hence, there is a need for a comprehensive security framework for Web services applications, which is common to all types of transport protocols.

What is then the SOA approach to security?

The SOA approach is to use XML Encryption to encrypt the message itself. This ensures data integrity of the message (ensure that the message has not been tampered while in transit) and non repudiation (making claims refuting sent messages impossible). WS-Security specifies the related concepts for SOAP related communications. UsernameTokens are used for user authentication. This typically requires username, password in clear text, created date of message and a nonce to be included in the message where it can after be secured using message or transport level security. This requires the user store information be present within the server to authenticate users. This works fine until our users are within our domain. What if we want to authenticate users from an external domain? Then we would require user store information of  that external domain which is not a practical solution to the issue. This where authentication delegation comes in. Basically we don’t have to trust users. We trust a set of external security token services which ensures the users are what they claim to be. WS-Trust specification specifies how communications between the related parties should happen in these scenarios. There is one piece of the puzzle still missing. How can we communicate our security requirements to other parties. WS-Security Policy provides for this requirement.

ESB and SOA

Service bus all about connecting things. The main idea is to be able to plug in a service and make it work with minimum configurations.

Key ideas behind using an ESB

– Make service accessible independant of the transport level protocols used to access them.

–  Monitoring and managing service with minimal configurations

– Transforming and mediating messages.

– Facilitating implementation of service governance

ESB SOA patterns

Concentrator pattern

Federated ESB Pattern

ESB Anti Patterns

Anti Pattern 1

* Implementing all the business logic in the ESB

* Why not

– Mixing infrastructure logic and business loigc

– Maintainability issues

Anti Pattern 2

* Apply waterfall and application deployment approach to ESB

* Why not?

– Lose the flexibility and agile development requirements of ESB

ESB and Governance

Governance ensures standards around enterprise IT. It includes poilicies, processes and people interactions. ESBs can be very important for governance models. It can act as an central policy enforcement point.

Read Full Post »