Archive

Archive for December, 2010

XOP Converter

December 18, 2010 2 comments

My most recent task at work involves XOP, or XML-binary Optimized Packaging. Basically, if you have an XML document that needs to have binary data in it, like images, you have two ways of doing it. You can base64 encode the data and put it directly in an XML element, or you can use XOP to keep the data in binary form and attach the file to the message using MIME. XOP is usually used to represent SOAP messages with MTOM (Message Transmission Optimization Mechanism).

My app uses JiBX to convert my Java objects into XML documents, and base64 encodes them. But I got a new requirement to also support XOP encoding. So to minimize impact on the app, I decided to write a converter that would take in a XML document with base64 encoded data and spit out a properly encoded XOP document. Then it would also be useful to convert existing documents and creating new ones. The module that helped me do this was Apache Axiom, a component of the Apache Axis2 web services engine.

The main problem with Axiom is its poor documentation. Almost all of the docs I could find about Axiom and Axis2 were from a server-side perspective, and I am writing a client-side app. So I thought I’d share the bit of code I wrote to do this conversion.

public class XOPConverter {

    @SuppressWarnings("rawtypes")
    public static void convertToXOP(String xml, OutputStream os) 
        throws XMLStreamException, JaxenException, JiBXException, IOException {

        // set up all the AXIOM objects
        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLStreamReader parser = factory.createXMLStreamReader(new StringReader(xml));
        StAXOMBuilder builder = new StAXOMBuilder(parser);
        OMDocument document = builder.getDocument();
        OMElement documentElement =  builder.getDocumentElement();
        OMFactory fac = OMAbstractFactory.getOMFactory();
        
        // find all the BinaryBase64Object nodes
        AXIOMXPath xpathExpression = new AXIOMXPath("//niem-nc:BinaryBase64Object");
        xpathExpression.addNamespace("niem-nc", "http://niem.gov/niem/niem-core/2.0");
        List nodeList = xpathExpression.selectNodes(documentElement);
        
        for (Iterator i = nodeList.iterator(); i.hasNext(); ) {
            Object obj = i.next();
            if (!(obj instanceof OMElement)) {
                continue;
            }
            OMElement element = (OMElement)obj;
            // get bytes data out of that node
            String base64data = element.getText();
            // decode base 64 encoding
            byte[] data = Base64Serializer.deserializeBase64(base64data);
            // create ByteArrayDataSource
            ByteArrayDataSource ds = new ByteArrayDataSource(data);
            // set the MIME type
            ds.setType("image/" + XOPConverter.getMIMEType(data));
            // create OMText object
            OMText textElement = fac.createOMText(new DataHandler(ds), true);
            // replace BinaryBase64Object node text with OMText object
            element.setText("");
            element.addChild(textElement);
        }
        
        // do the XOP conversion
        OMOutputFormat format = new OMOutputFormat();
        format.setDoOptimize(true);
        format.setMimeBoundary("MIME");
        format.setContentType("Multipart/Related");
        MTOMXMLStreamWriter mtomWriter = new MTOMXMLStreamWriter(os, format);
        mtomWriter.setDoOptimize(true);
        document.serializeAndConsume(mtomWriter);
        document.close(false);
    }
    
    /**
     * @param args
     * @throws IOException 
     * @throws XMLStreamException 
     * @throws JaxenException 
     * @throws JiBXException 
     */
    public static void main(String[] args) 
        throws IOException, XMLStreamException, JaxenException, JiBXException {
        
        if (args.length == 0) {
            System.out.println("Usage: <filename>");
            return;
        }
        String inputFileName = args[0];
        String outputFileName = inputFileName.substring(0, 
                inputFileName.lastIndexOf(".")) + ".xop";
        boolean overwrite = false;
        if (args.length > 1) {
            String overwriteStr = args[1];
            overwrite = Boolean.parseBoolean(overwriteStr);
        }
        // check if output file exists
        if (new java.io.File(outputFileName).exists() && !overwrite) {
            System.out.println("ERROR: Output file " + outputFileName + 
                    " exists, please move it.");
            return;
        }
        
        java.io.BufferedReader reader = 
            new java.io.BufferedReader(new java.io.FileReader(args[0]));
        String line = null;
        StringBuilder stringBuilder = new StringBuilder();
        String ls = System.getProperty("line.separator");
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(ls);
        }
        String xml = stringBuilder.toString();
        
        FileOutputStream os = new FileOutputStream(outputFileName);
        convertToXOP(xml, os);
        System.out.println("done");
    }
}

Now I’ll step through the code and explain it a bit.

The convertToXOP method takes the XML text as a String, and an OutputStream where the XOP is written. In my original document, all the base64 encoded objects I need to extract and attach are in elements labeled . So I cook up an XPath expression to find all those elements for processing. The code gets the data out of the element and un-base64 encodes it. I didn’t write that code, so you’ll have to do that yourself. Then we put the byte array in a ByteArrayDataSource and tag it with the MIME type we want to use. In a later post I’ll show how to figure out what encoding an image uses. Then use the OMFactory to create a new OMText element containing the byte array and set it to be optimized. Add it to the original element and reset its text, clearing out the base64 encoded data.

The key to doing the conversion is Axiom’s MTOMXMLStreamWriter class. You send the MTOMXMLStreamWriter the OutputStream where you want the XOP document to go, and an OMOutputFormat object. Setting the doOptimize flag on the OMOutputFormat object enables the optimization step that moves the binary objects to MIME attachments. Sending the MTOMXMLStreamWriter to the OMDocument’s serializeAndConsume method writes the XOP output.

I hope this code and explanation has been helpful to you.

Advertisements
Categories: code Tags: , ,

Links

December 12, 2010 Leave a comment

I wanted to share some links to some cool stuff I’ve found recently.

PragPub
This is a free monthly magazine by the folks to publish the Pragmatic Programmers series of books. They release the issues in pdf, epub and mobi formats in addition to HTML on the site. Lots of great stuff to load onto your ereader.

JAXenter
This site is a hub for software news. There’s a lot of press releases for Apache projects you’ve never heard of, but there’s also some Q&A with project managers and commentary on the goings on with Oracle and whatnot. Add the RSS feed to your feed reader and subscribe to their free occasional PDF magazine JAXmag.

The Java Posse
This is a group of four high-level developers who do a semi-regular podcast discussing Java. It can get really annoying when they talk over each other, and the episodes sometimes approach two rambling hours in length, but they are really smart and well-connected guys who are usually worth listening to.

Categories: miscellaneous Tags: , ,

DevIgnition

December 6, 2010 Leave a comment

Last Friday I took the day off of work to attend NOVAJUG’s free DevIgnition conference at Oracle HQ in Reston. It was a very interesting and informative series of presentations, and I’m definitely glad I set aside the time to check it out.

The first presenter was Suman Cuddapah, an Oracle SOA Solutions Specialist. He gave an overview of the recently released Java EE 6 updates and talked about the path forward for OpenJDK 7. His talk was pretty high level, obviously with no surprise revelations. The most interesting bit I took from his talk was the modularity introduced with Java EE 6, so you can deploy only the modules you need and keep the server stack lightweight.

The second talk was about Netbeans projects, presented by Ryan Cuprak. He did a quick survey of IDE usage in the crowd and the overwhelming favorite was Eclipse. I felt like his talk was intended to be evangelism for the Netbeans IDE, but it got mired down in an exhaustive list of the various project templates Netbeans provides. I think his talk would have been more effective if he had tried to highlight the advantages Netbeans has over Eclipse. It seemed like there were a lot of issues with Netbeans’s ability to import projects from Eclipse, further putting off potential switchers.

The third presenter was Reza Rahman, talking about how Spring 3 and Java EE interact. He went through a lot of Java EE technologies like JPA, EJB3, JMS and web services and showed how they can interoperate with Spring. If you are familiar with Spring and like some of the flexibility it gives you, you can leverage the best of both worlds.

The fourth presenter was Jay Garcia, talking about the Sencha Touch product. Sencha Touch is a JavaScript framework which came from ExtJS. It helps you build websites for mobile devices. The functionality Jay demoed was very slick. It allows you to code sites to function just like apps do on iOS, with HTML5, CSS and JavaScript. This is not an area I have done any work in before, but it looked quite snazzy.

The next presenters were David Bock and Arild Shirazi, talking about their effort to rewrite a legacy Swing and EJB app with JRuby and Rails. Their demo was kind of hard to follow, but the prospect of leaving Swing behind for something better was appealing.

The next talk was by self-described Clojure ninja Bryan Weber. After working with Scala for a couple of years, Bryan grew frustrated by the complexity and made the switch to Clojure, a variant of Lisp that runs on the JVM. It seems like there is a bit of a rivalry between Scala and Clojure to be the functional programming king on the JVM. I admit that I am someone who harbors deep reservations about Lisp programming. Bryan talked about the sometimes steep learning curve with Clojure, but his enthusiasm for the language was inspiring. These days I’m diving into learning Scala, but I’ll be keeping an ear out for Clojure and trying to see how the two compare.

The final presenter was Arun Gupta, talking about Java EE 6 and GlassFish. He knew that it’s tough for the last presenter of the day, so he really brought the energy to his talk. He had a huge slide deck but skipped most of it to focus on demoing code. He showed how Netbeans and GlassFish tightly integrate to speed development. I was highly pleased to see that Java EE 6 makes most of the XML configuration files optional, because I hate those cryptic things. Instead you can annotate your code. You can set up GlassFish as a deployment destination for your Netbeans project, and then every time you save a code change, it is automatically deployed.

I know a lot of very smart people are anti-IDE because it hides a lot of complexity from developers, and then they don’t really know what’s going on under the hood so they can’t fix problems. I was thinking about that position while I watched Arun Gupta fly through his very well prepared demo, inserting code templates and modifying existing code to demonstrate features. I am just a lot more productive in an IDE than I would be without it. I guess that IDEs can hide bad coders’ lack of abilities, but they also allow good coders to get more done faster. And they can make for some great demos!

All in all, it was an enlightening day and I’m already looking forward to the next conference!

Categories: socializing Tags: