Posts Tagged ‘marshalling’

JAXB part 7: increase performance with singleton JaxbContext instance

Currently I am developing an application to rename some jboss configuration content.
Using JAXB, I had made marshalling and unmarshalling methods, creating a new JAXBcontext instance each time the single methods were invoked (for each xml file!).
The application was incredibly slow. Taking up to 45 minutes to complete the tasks.
Then I have discovered that I could make it all in less than 30% of the time, by using a singleton design pattern to reuse a single instance of the JAXBContext.

All you need to do is:
1) creating a singleton class to instance the context
2) use it in your marshalling/unmarshalling method.

The singleton class is like this:

import javax.xml.bind.DataBindingException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import com.nexus.pacs.conf.xmdesc.Mbean;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

public class MbeanJAXBContext {

	private static JAXBContext instance;

	public XmdescJAXBContext() {
	}

	public static synchronized JAXBContext initContext() {
		try {
			if (instance == null)
				instance = JAXBContext.newInstance(Mbean.class);
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return instance;
	}

}

I am using the synchronized keyword to make it thread safe.
Then you can instance your context like this:

public static void marshaller(Mbean mbean, String filePath)
			throws CustomizerException {

	StringWriter stringWriter = new StringWriter();
	String xmlContent;
	File file = new File(filePath);

	try {
		xmlContent = FileUtils.readFileToString(file, "UTF-8");
		FileWriter fileWriter = new FileWriter(file);
		XmdescJAXBContext jaxbHelper = new XmdescJAXBContext();
		JaxbContext jaxbContext= jaxbHelper.initContext();
		Marshaller marshaller = jaxbContext.createMarshaller();

                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

		marshaller.setProperty(CharacterEscapeHandler.class.getName(),
					new XmlCharacterHandler());

		 synchronized(marshaller){
		     marshaller.marshal(mbean, stringWriter);
		    }
		
	xmlContent = XmdescContentHandler.addEntityReferences(xmlContent,
					stringWriter.toString());
		fileWriter.write(xmlContent);

		fileWriter.flush();
		fileWriter.close();

		} catch (MarshalException e) {

		throw new CustomizerException("Marshalling the file: " + filePath
					+ "not possible. Wrong file content");

		} catch (JAXBException e) {

		throw new CustomizerException("Could not parse the XML file: "
					+ filePath);

		} catch (IOException e) {

		throw new CustomizerException("Could not access the file: "
					+ filePath);
		}

	}

Awfully I have also noticed that with Ubuntu 12.04, with the singleton instance, the application now takes just a minute to run, while with Windows 7 it still takes 16 minutes. Long live Linux!

You can find a post about the Character Handler in this blog. Have fun!

JAXB part 5: XML Character handler

With JAXB you need to dhandle special characters you can just choose between two options.

Otherwise you need to define your own character handler.

The following class shows you how to handle both CDATA elements and special characters.

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

import com.sun.xml.bind.marshaller.CharacterEscapeHandler;

public class XmlCharacterHandler implements CharacterEscapeHandler {

	public void escape(char[] buf, int start, int len, boolean isAttValue,
			Writer out) throws IOException {
		StringWriter buffer = new StringWriter();

		for (int i = start; i < start + len; i++) {
			buffer.write(buf[i]);
		}

		String st = buffer.toString();

		if (!st.contains("CDATA")) {
			st = buffer.toString().replace("&", "&amp;").replace("<", "&lt;")
					.replace(">", "&gt;").replace("'", "&apos;")
					.replace("\"", "&quot;");

		}
		out.write(st);
//		System.out.println(st);
	}

}

In your marshaller method just add the following property:

	marshaller.setProperty(CharacterEscapeHandler.class.getName(),
					new XmlCharacterHandler());

Categories
Links: