/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.ext.jaxb.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.logging.Level;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBException;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import org.restlet.Context;
import org.restlet.ext.jaxb.JaxbRepresentation;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
/**
* This is a utility class to assist in unmarshaling XML into a new Java content
* tree.
*
* Each {@code unmarshal} method takes a different source for the XML. This
* class caches information to improve unmarshaling performance across calls
* using the same schema (package).
*
* @author Overstock.com
*/
public class Unmarshaller<T> {
/** The JAXB classloader. */
private final ClassLoader classLoader;
/** The JAXB context path. */
private final String contextPath;
/**
* Use thread identity to preserve safety of access to unmarshallers.
*/
private final ThreadLocal<javax.xml.bind.Unmarshaller> unmarshaller = new ThreadLocal<javax.xml.bind.Unmarshaller>() {
@Override
protected synchronized javax.xml.bind.Unmarshaller initialValue() {
javax.xml.bind.Unmarshaller m = null;
try {
m = JaxbRepresentation.getContext(getContextPath(),
getClassLoader()).createUnmarshaller();
} catch (Exception e) {
Context.getCurrentLogger().log(Level.WARNING,
"Problem creating Unmarshaller", e);
return null;
}
return m;
}
};
/**
* Constructor.
*
* @param contextPath
* The JAXB context path.
* @param classloader
* The JAXB classloader.
*/
public Unmarshaller(String contextPath, ClassLoader classloader) {
this.contextPath = contextPath;
this.classLoader = classloader;
}
/**
* Returns the JAXB classloader.
*
* @return The JAXB classloader.
*/
public ClassLoader getClassLoader() {
return this.classLoader;
}
/**
* Returns the JAXB context path.
*
* @return The JAXB context path.
*/
public String getContextPath() {
return this.contextPath;
}
/**
* Returns the JAXB unmarshaller.
*
* @return The JAXB unmarshaller.
* @throws JAXBException
*/
private javax.xml.bind.Unmarshaller getUnmarshaller() throws JAXBException {
final javax.xml.bind.Unmarshaller m = this.unmarshaller.get();
if (m == null) {
Context.getCurrentLogger()
.warning("Unable to locate unmarshaller.");
throw new JAXBException("Unable to locate unmarshaller.");
}
return m;
}
/**
* Sets the validation handler for this unmarshaller.
*
* @param handler
* A validation handler.
* @throws JAXBException
* If an error was encountered while setting the event handler.
*/
public void setEventHandler(ValidationEventHandler handler)
throws JAXBException {
getUnmarshaller().setEventHandler(handler);
}
/**
* Unmarshal XML data from the specified Restlet string representation and
* return the resulting Java content tree.
*
* @param jaxbRep
* The source JAXB representation.
* @return The newly created root object of the Java content tree.
* @throws JAXBException
* If any unexpected problem occurs during unmarshaling.
* @throws IOException
* If an error occurs accessing the string representation.
*/
public Object unmarshal(JaxbRepresentation<?> jaxbRep)
throws JAXBException, IOException {
return unmarshal(jaxbRep, jaxbRep.getReader());
}
/**
* Unmarshal XML data from the specified input stream and return the
* resulting Java content tree.
*
* @param stream
* The source input stream.
* @return The newly created root object of the Java content tree.
* @throws JAXBException
* If any unexpected problem occurs during unmarshaling.
* @throws IOException
* If an error occurs accessing the string representation.
*/
public Object unmarshal(JaxbRepresentation<?> jaxbRep, InputStream stream)
throws JAXBException {
return unmarshal(jaxbRep, new InputStreamReader(stream));
}
/**
* Unmarshal XML data from the specified reader and return the resulting
* Java content tree.
*
* @param reader
* The source reader.
* @return The newly created root object of the Java content tree.
* @throws JAXBException
* If any unexpected problem occurs during unmarshaling.
* @throws IOException
* If an error occurs accessing the string representation.
*/
public Object unmarshal(JaxbRepresentation<?> jaxbRep, Reader reader)
throws JAXBException {
SAXSource ss = null;
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
// Keep before the external entity preferences
spf.setNamespaceAware(true);
spf.setValidating(jaxbRep.isValidatingDtd());
spf.setXIncludeAware(jaxbRep.isXIncludeAware());
spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,
jaxbRep.isSecureProcessing());
spf.setFeature(
"http://xml.org/sax/features/external-general-entities",
jaxbRep.isExpandingEntityRefs());
spf.setFeature(
"http://xml.org/sax/features/external-parameter-entities",
jaxbRep.isExpandingEntityRefs());
XMLReader xmlReader = spf.newSAXParser().getXMLReader();
ss = new SAXSource(xmlReader, new InputSource(reader));
} catch (Exception e) {
throw new JAXBException("Unable to create customized SAX source", e);
}
getUnmarshaller().setEventHandler(jaxbRep.getValidationEventHandler());
return getUnmarshaller().unmarshal(ss);
}
}