/* ========================================== * Laverca Project * https://sourceforge.net/projects/laverca/ * ========================================== * Copyright 2015 Laverca Project * * 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 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package fi.laverca.util; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.PropertyException; import javax.xml.bind.Unmarshaller; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Element; import org.xml.sax.helpers.DefaultHandler; /** * Helper routines for instantiating JAXB Marshaller and Unmarshaller of things. * These provide context shared instance of Mapping data so that it should * be read only once. *
* These work from Oracle Java/OpenJDK 8 onwards.
* These MAY work also from Java 7 onwards, but never tested there.
*/
public class JMarshallerFactory {
protected static final Log log = LogFactory.getLog(JMarshallerFactory.class);
private static NSPfxMapper nsp;
/**
* JAXB RI NamespacePrefixMapper, Oracle Java 8 JRE
*/
private static class NSPfxMapper extends com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper {
private HashMap
* It is possible that the JAXBContext.newInstance(clazz) is invoked multiple times in parallel,
* but eventually it should become stored in one of its instances, and there after that one will
* be used for all needs.
*
* The JAXBContext is thread safe, it is then used to instantiate Marshaller/Unmarshaller,
* which are not thread safe themselves.
*/
private static JAXBContext getJAXBContext(final Class> clazz)
throws JAXBException
{
JAXBContextDelayedLoad ret = jaxbCache.get(clazz);
if (ret == null) {
ret = new JAXBContextDelayedLoad(clazz);
jaxbCache.put(clazz, ret);
}
return ret.get();
}
/**
* Register given class for contained JAXB metadata parsing
*/
public static void registerForJAXBContext(final Class> clazz)
{
final JAXBContextDelayedLoad d2 = jaxbCache.get(clazz);
if (d2 == null) {
jaxbCache.put(clazz, new JAXBContextDelayedLoad(clazz));
}
}
/**
* Make unmarshaller with globally shared mapping database
*
* @param clazz Expected unmarshalling output class.
* It must be pointing to a class with JAXB {{@XmlRootElement}} annotation.
* @throws JAXBException base type for JAXB exceptions, many possible reasons
*/
public static Unmarshaller createUnmarshaller(final Class> clazz)
throws JAXBException
{
final JAXBContext jctx = getJAXBContext(clazz);
final Unmarshaller m = jctx.createUnmarshaller();
return m;
}
/**
* @param clazz A JAXB @XmlRootElement annotated class reference.
* @return JAXB Marshaller instance
* @throws JAXBException base type for JAXB exceptions, many possible reasons
*/
public static Marshaller createMarshaller(Class extends Object> clazz)
throws JAXBException
{
final JAXBContext jctx = getJAXBContext(clazz);
final Marshaller m = jctx.createMarshaller();
JMarshallerFactory.setNamespaceMappings(m);
return m;
}
/**
* Marshal given object using JAXB Marshaller to SAX(2) DefaultHandler.
*
* @param hand A SAX2 Event Handler receiving serialization events.
* @param value A JAXB @XmlRootElement annotated object instance.
*/
public static void marshal( final Object value, final DefaultHandler hand)
throws JAXBException
{
final Marshaller m = JMarshallerFactory.createMarshaller(value.getClass());
// Don't include the DOCTYPE, otherwise an exception occurs due to
// two DOCTYPEs defined in the document. The XML fragment is included
// in an XML document containing already a DOCTYPE.
m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
// Marshall the Jaxb object into the stream (sink)
if (log.isDebugEnabled()) {
log.debug("Running marshal(value,handler); value="+value);
}
m.marshal(value, hand);
}
/**
* @param object A JAXB @XmlRootElement annotated object instance.
* @param writer A Writer instance receiving marshalling output as characters
* @throws JAXBException base type for JAXB exceptions, many possible reasons
*/
public static void marshal(final Object object, final Writer writer)
throws JAXBException
{
final Marshaller m = JMarshallerFactory.createMarshaller(object.getClass());
m.marshal(object, writer);
}
/**
* Marshall the JAXB object to a String with XML headers and all.
*
* @param object A JAXB @XmlRootElement annotated object instance.
* @return Marshalled string representation of the input data
* @throws JAXBException base type for JAXB exceptions, many possible reasons
*/
public static String toString(final Object object)
throws JAXBException
{
final StringWriter writer = new StringWriter();
JMarshallerFactory.marshal(object, writer);
return writer.toString();
}
/**
* Convert source data (W3C DOM Element) to JAXB data type specified
* by the Class reference instance.
*
* @param javaType Conversion target type as a Class.
* @param elt A W3C DOM data source
* @return Converted type
* @throws JAXBException base type for JAXB exceptions, many possible reasons
*/
public static Object unmarshal(final Class> javaType, final Element elt)
throws JAXBException
{
final JAXBContext jctx = getJAXBContext(javaType);
final Unmarshaller unm = jctx.createUnmarshaller();
return unm.unmarshal(elt);
}
/**
* Convert source data to JAXB data type specified
* by the Class reference instance.
*
* @param javaType Conversion target type as a Class.
* @param reader A character reader for input source.
* @return Converted type
* @throws JAXBException base type for JAXB exceptions, many possible reasons
*/
public static Object unmarshal(final Class> javaType, final Reader reader)
throws JAXBException
{
final JAXBContext jctx = getJAXBContext(javaType);
final Unmarshaller unm = jctx.createUnmarshaller();
return unm.unmarshal(reader);
}
/**
* Convert source data to JAXB data type specified
* by the Class reference instance.
*
* @param clazz Conversion target type as a Class.
* @param string A string for input source.
* @return Converted type
* @throws JAXBException base type for JAXB exceptions, many possible reasons
*/
public static Object unmarshal(final Class extends Object> clazz, final String string)
throws JAXBException
{
return unmarshal(clazz, new StringReader(string));
}
}