Month: April 2015

CXF @WebService and XMLGregorianCalendar with JAXB

Lately we’ve done a lot of work with some different web-services and at one point we discovered that some people have trouble formatting their timestamps properly. Good solution would be to ask people to use a proper standard and not re-invent their own wheels, but that’s not always a possibility. Fortunately there’s a rather simple solution for this. All you need is to define a @XmlJavaTypeAdapter and teach it how to convert different values into suitable formats.

JAXB and XML by default uses a timestamp format of “yyyy-MM-dd’T’HH:mm:ss’Z'” which translates to 2015-04-11T14:15:12.000 roughly and some people think it’s ok to send us SQL ISO format timestamps that look very similar – specifically missing the T letter in the timestamp, like this: “yyyy-MM-dd HH:mm:ss”.

For example, in my web service entity I have a RelaxedXMLGregorianCalendarAdapter in use which is our own implementation:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
@XmlRootElement(name = "Metainfo")
public class Metainfo {
    @XmlJavaTypeAdapter(RelaxedXMLGregorianCalendarAdapter.class)
    protected XMLGregorianCalendar generationTime;
}

And here’s the implementation for the adapter (note that we turn null, empty string and specifically a word “infinity” into null’s):

import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

public class RelaxedXMLGregorianCalendarAdapter extends XmlAdapter<String, XMLGregorianCalendar> {

	@Override
	public String marshal(XMLGregorianCalendar v) {
		return v.toXMLFormat();
	}

	@Override
	public XMLGregorianCalendar unmarshal(String v) throws Exception {

		if (v == null) {
			return null;
		}
		
		if (v.trim().isEmpty()) {
			return null;
		}
		
		// Who thinks that this is OK for a date field?
		if ("infinity".equals(v.trim())) {
			return null;
		}
                // Replace the space with T to match our default pattern.
		return DatatypeFactory.newInstance().newXMLGregorianCalendar(v.replace(" ", "T"));
	}
}

So there you have it. This is how you can customize how JAXB is handling some type conversions and pretty much you can customize anything JAXB does with this approach. Like teach it to treat -1 for specific fields as null or teach it understand different date formats.

CXF, JAXRS, XML and CDATA

I was in a need to export some XML tags as CDATA in my CXF XML @WebService that generates some XML for our partner.

We had a field that contained an URL and in that we have ampersand characters. By default these get turned into “&amp;” but our specific XML partner was not OK with that so we had to adjust to CDATA. That turned out to be rather complex as I was hoping for something simple like @CDATA annotation – but as always, life is full of suprises.

Anyway, the trick is rather simple to make it work – We need to define our own XML Stream Writer and overwrite the default write method from “writeCharacters” to “writeCData”.

We had a very specific tags in mind only so we specified those as static string array.

You can do this like this:

import java.util.Arrays;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;

/**
 * Simple CDATA XML Stream Writer that exports some items as CDATA
 */
public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter {

	// All elements with these names will be turned into CDATA
	private static String[] CDATA_ELEMENTS = { "infoUrl", "description" };

	private String currentElementName;

	public CDataXMLStreamWriter(XMLStreamWriter del) {
		super(del);
	}

	@Override
	public void writeCharacters(String text) throws XMLStreamException {
		if (Arrays.asList(CDATA_ELEMENTS).contains(currentElementName)) {
			super.writeCData(text);
		} else {
			super.writeCharacters(text);
		}
	}

	public void writeStartElement(String prefix, String local, String uri) throws XMLStreamException {
		currentElementName = local;
		super.writeStartElement(prefix, local, uri);
	}
}

OK; So now we have this nice writer, but how do you use it? Well, we are running it all inside a CXF that’s running with Spring container so as it turns out, we need an “Interceptor” that would help us write stuff. You can make one easily like this.


import java.io.OutputStream;

import javax.xml.stream.XMLStreamWriter;

import org.apache.cxf.interceptor.AttachmentOutInterceptor;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.staxutils.StaxUtils;

public class CDataWriterInterceptor extends AbstractPhaseInterceptor {

	public CDataWriterInterceptor() {
		super(Phase.PRE_STREAM);
		addAfter(AttachmentOutInterceptor.class.getName());
	}

	@Override
	public void handleMessage(Message message) {
		// Required for CDATA to working
		message.put("disable.outputstream.optimization", Boolean.TRUE);
		XMLStreamWriter writer = StaxUtils.createXMLStreamWriter(message.getContent(OutputStream.class));
		message.setContent(XMLStreamWriter.class, new CDataXMLStreamWriter(writer));
	}
}

And now, how do you use this interceptor? In your applicationContext.xml or similar Spring context file, define the interceptor and use it!
Note that the LoggingFeature is not required but I’ve left it in here so that you could easily enable logging to see the incoming and outgoing messages better.


	<bean id="CDataWriterInterceptor" class="com.amivarius.cxf.util.CDataWriterInterceptor" />

	<jaxrs:server .... >

		<jaxrs:features>
			<bean class="org.apache.cxf.feature.LoggingFeature" />
		</jaxrs:features>

		<jaxrs:outInterceptors>
			<ref bean="CDataWriterInterceptor" />
		</jaxrs:outInterceptors>

	</jaxrs:server>

And that’s it folks!

The output will be <![CDATA[]]> nicely 🙂

Material Theme for Liferay

So yes, we’ve been busy and made a new theme called Material Theme for Liferay. You can take a wild guess what inspired us but in reality it’s just a simple Google Material Design based theme. I really love their design.

I don’t have time to write up a lot of marketing BS at this point so just look at the damn screenshots and admit it’s just what you have been looking for: Liferay Material Design theme.

 

screen5 screen4
screen2 screen3