/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*******************************************************************************/
package org.ebayopensource.turmeric.runtime.binding.impl.jaxb;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.ebayopensource.turmeric.runtime.binding.ISerializationContext;
import org.ebayopensource.turmeric.runtime.binding.ISerializer;
import org.ebayopensource.turmeric.runtime.binding.ITypeConversionContext;
import org.ebayopensource.turmeric.runtime.binding.exception.BindingSetupException;
import org.ebayopensource.turmeric.runtime.binding.exception.SerializationException;
import org.ebayopensource.turmeric.runtime.binding.exception.SerializationOutputException;
import org.ebayopensource.turmeric.runtime.binding.exception.TypeConversionAdapterCreationException;
import org.ebayopensource.turmeric.runtime.binding.impl.BaseBindingProcessor;
import com.ebay.kernel.logger.LogLevel;
import com.ebay.kernel.logger.Logger;
import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
/**
* JAXBSerializer is responsible for managing the process of serializing
* Java content trees back into an encoded data.
*/
public class JAXBSerializer extends BaseBindingProcessor
implements ISerializer {
private static Logger LOGGER = Logger.getInstance( JAXBSerializer.class );
private JAXBContextBuilder m_jaxbContextBuilder;
private boolean m_shouldOverrideNullObjectMarshalling;
private Map<String, String> m_options;
/**
* Initializes an instance of JAXBSerializer.
*/
public JAXBSerializer(
boolean shouldOverrideNullObjectMarshalling,
Map<String, String> options,
Class[] rootClasses) {
m_shouldOverrideNullObjectMarshalling = shouldOverrideNullObjectMarshalling;
m_options = options;
m_jaxbContextBuilder = new JAXBContextBuilder(rootClasses);
}
/**
* It takes a java content tree, marshall it into an encoded data and output it to the
* given output stream. It throws SerializationException when there is an error during the process.
*/
// public void serialize(OutboundMessage msg, Object in, QName xmlName, Class<?> clazz, XMLStreamWriter out)
// throws ServiceException
public void serialize(
ISerializationContext ctx,
Object in,
XMLStreamWriter out)
throws SerializationException,
SerializationOutputException,
BindingSetupException,
TypeConversionAdapterCreationException
{
try {
QName xmlName = ctx.getRootXMLName();
Class clazz = ctx.getRootClass();
JAXBElement<?> ele = createJAXBElement(xmlName, clazz, in);
if (null == in && m_shouldOverrideNullObjectMarshalling) {
marshalNullObject(out, ele);
return;
}
Marshaller m = createMarshaller(ctx);
m.marshal(ele, out);
}
catch (JAXBException e) {
throw new SerializationException(e);
}
catch (XMLStreamException e) {
throw new SerializationOutputException(e);
}
}
/**
* Marshals null object.
*
* @param ele - JAXB Element.
* @param out - The instance of XMLStreamWriter.
*/
protected void marshalNullObject(XMLStreamWriter out, JAXBElement ele) throws XMLStreamException {
QName elementName = ele.getName();
out.writeStartElement(elementName.getPrefix(), elementName.getLocalPart(), elementName.getNamespaceURI());
out.writeCharacters(null);
out.writeEndElement();
}
/**
* Returns a Marshaller object that can be used to convert java content tree into XML data.
* @param ctx - An instance of ISerializationContext
* @return a Marshaller object
* @throws JAXBException if an error was encountered while creating the Marshaller object
*/
protected Marshaller createMarshaller(ISerializationContext ctxt)
throws JAXBException, BindingSetupException, TypeConversionAdapterCreationException
{
if ( LOGGER.isDebugEnabled() ) {
LOGGER.log( LogLevel.DEBUG, "createMarshaller default namespace: " + ctxt.getDefaultNamespace() );
LOGGER.log( LogLevel.DEBUG, "getPayloadType: " + ctxt.getPayloadType() );
LOGGER.log( LogLevel.DEBUG, "getRootClass: " + ctxt.getRootClass() );
LOGGER.log( LogLevel.DEBUG, "getRootXMLName: " + ctxt.getRootXMLName() );
LOGGER.log( LogLevel.DEBUG, "getNamespaceToPrefixMap: " + ctxt.getNamespaceToPrefixMap() );
}
JAXBContext jc = m_jaxbContextBuilder.getContext(ctxt, m_options);
Marshaller m = jc.createMarshaller();
JAXBValidationEventHandler h = new JAXBValidationEventHandler(ctxt);
m.setEventHandler(h);
Map<String, List<String>> ns2pMap = ctxt.getNamespaceToPrefixMap();
PredefinedNamespacePrefixMapper mapper = new PredefinedNamespacePrefixMapper(ns2pMap, ctxt.getDefaultNamespace());
m.setProperty("com.sun.xml.bind.namespacePrefixMapper", mapper);
ITypeConversionContext tcCtxt = ctxt.getTypeConversionContext();
if (null != tcCtxt && !tcCtxt.isEmpty()) {
m.setAdapter(getTypeConversionAdapter(tcCtxt));
}
m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
return m;
}
public Class getBoundType() {
return null;
}
private JAXBElement<?> createJAXBElement(QName xmlName, Class<?> clazz, Object obj)
{
String nsURI = xmlName.getNamespaceURI();
String xmlLocalName = xmlName.getLocalPart();
if (null == xmlLocalName || xmlLocalName.length() <= 0) {
xmlLocalName = clazz.getSimpleName();
}
QName qName = new QName(nsURI, xmlLocalName);
@SuppressWarnings("unchecked")
JAXBElement result = new JAXBElement(qName, clazz, obj);
return result;
}
private class PredefinedNamespacePrefixMapper extends NamespacePrefixMapper {
private Map<String, List<String>> m_nsUriToPrefixMap = new HashMap<String, List<String>>();
private String m_defaultNS;
PredefinedNamespacePrefixMapper(Map<String, List<String>> nsUriToPrefixMap, String defaultNS) {
m_nsUriToPrefixMap = nsUriToPrefixMap;
m_defaultNS = defaultNS == null ? "" : defaultNS;
if ( LOGGER.isDebugEnabled() ) {
LOGGER.log( LogLevel.DEBUG, "PredefinedNamespacePrefixMapper.m_defaultNS: " + m_defaultNS );
LOGGER.log( LogLevel.DEBUG, "PredefinedNamespacePrefixMapper.m_nsUriToPrefixMap: " + m_nsUriToPrefixMap );
}
}
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
if (m_defaultNS.equals(namespaceUri)) {
if ( LOGGER.isDebugEnabled() )
LOGGER.log( LogLevel.DEBUG, "getPreferredPrefix default namespaceUri matched: " + namespaceUri );
return "";
}
if ( LOGGER.isDebugEnabled() )
LOGGER.log( LogLevel.DEBUG, "Default namespace " + m_defaultNS + " not matched!" );
List<String> prefixes = m_nsUriToPrefixMap.get(namespaceUri);
if (null != prefixes) {
if ( LOGGER.isDebugEnabled() )
LOGGER.log( LogLevel.DEBUG, "getPreferredPrefix found prefix: " + prefixes.get(0) + " for " + namespaceUri );
return prefixes.get(0);
}
if ( LOGGER.isDebugEnabled() )
LOGGER.log( LogLevel.DEBUG, "getPreferredPrefix no prefix found! Returning suggestion " + suggestion );
return suggestion;
}
}
}