/**
* 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.jaxrs.internal.provider;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.logging.Logger;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import org.restlet.Context;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
/**
* Provider for JAXB objects.
*
* @author Stephan Koops
*/
@Provider
@Produces({ "application/xml", MediaType.TEXT_XML, "application/*+xml" })
@Consumes({ "application/xml", MediaType.TEXT_XML, "application/*+xml" })
public class JaxbProvider extends AbstractJaxbProvider<Object> {
/**
* Specifies that the parser will expand entity reference nodes. By default
* the value of this is set to true.
*/
private volatile boolean expandingEntityRefs;
private final Logger logger = Context.getCurrentLogger();
/** Limits potential XML overflow attacks. */
private boolean secureProcessing;
/**
* Indicates the desire for validating this type of XML representations
* against a DTD. Note that for XML schema or Relax NG validation, use the
* "schema" property instead.
*
* @see DocumentBuilderFactory#setValidating(boolean)
*/
private volatile boolean validatingDtd;
/**
* Indicates the desire for processing <em>XInclude</em> if found in this
* type of XML representations. By default the value of this is set to
* false.
*
* @see DocumentBuilderFactory#setXIncludeAware(boolean)
*/
private volatile boolean xIncludeAware;
/**
* Default constructor.
*/
public JaxbProvider() {
this.expandingEntityRefs = false;
this.secureProcessing = true;
this.validatingDtd = false;
this.xIncludeAware = false;
}
@Override
Logger getLogger() {
return this.logger;
}
/**
* Indicates if the parser will expand entity reference nodes. By default
* the value of this is set to true.
*
* @return True if the parser will expand entity reference nodes.
*/
public boolean isExpandingEntityRefs() {
return expandingEntityRefs;
}
@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return type.isAnnotationPresent(XmlRootElement.class);
}
/**
* Indicates if it limits potential XML overflow attacks.
*
* @return True if it limits potential XML overflow attacks.
*/
public boolean isSecureProcessing() {
return secureProcessing;
}
/**
* Indicates the desire for validating this type of XML representations
* against an XML schema if one is referenced within the contents.
*
* @return True if the schema-based validation is enabled.
*/
public boolean isValidatingDtd() {
return validatingDtd;
}
@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return type.isAnnotationPresent(XmlRootElement.class);
}
/**
* Indicates the desire for processing <em>XInclude</em> if found in this
* type of XML representations. By default the value of this is set to
* false.
*
* @return The current value of the xIncludeAware flag.
*/
public boolean isXIncludeAware() {
return xIncludeAware;
}
/**
* @see MessageBodyReader#readFrom(Class, Type, MediaType, Annotation[],
* MultivaluedMap, InputStream)
*/
@Override
public Object readFrom(Class<Object> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException {
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setXIncludeAware(isXIncludeAware());
spf.setNamespaceAware(true);
spf.setValidating(isValidatingDtd());
spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,
isSecureProcessing());
spf.setFeature(
"http://xml.org/sax/features/external-general-entities",
isExpandingEntityRefs());
spf.setFeature(
"http://xml.org/sax/features/external-parameter-entities",
isExpandingEntityRefs());
XMLReader reader = spf.newSAXParser().getXMLReader();
JAXBContext jaxbContext = getJaxbContext(type);
Unmarshaller um = jaxbContext.createUnmarshaller();
return um.unmarshal(new SAXSource(reader, new InputSource(
entityStream)));
} catch (Exception e) {
throw new IOException("Could not unmarshal to " + type.getName(), e);
}
}
/**
* Indicates if the parser will expand entity reference nodes. By default
* the value of this is set to true.
*
* @param expandEntityRefs
* True if the parser will expand entity reference nodes.
*/
public void setExpandingEntityRefs(boolean expandEntityRefs) {
this.expandingEntityRefs = expandEntityRefs;
}
/**
* Indicates if it limits potential XML overflow attacks.
*
* @param secureProcessing
* True if it limits potential XML overflow attacks.
*/
public void setSecureProcessing(boolean secureProcessing) {
this.secureProcessing = secureProcessing;
}
/**
* Indicates the desire for validating this type of XML representations
* against an XML schema if one is referenced within the contents.
*
* @param validating
* The new validation flag to set.
*/
public void setValidatingDtd(boolean validating) {
this.validatingDtd = validating;
}
/**
* Indicates the desire for processing <em>XInclude</em> if found in this
* type of XML representations. By default the value of this is set to
* false.
*
* @param includeAware
* The new value of the xIncludeAware flag.
*/
public void setXIncludeAware(boolean includeAware) {
xIncludeAware = includeAware;
}
/**
* @see MessageBodyWriter#writeTo(Object, Class, Type, Annotation[],
* MediaType, MultivaluedMap, OutputStream)
*/
@Override
public void writeTo(Object object, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpResponseHeaders,
OutputStream entityStream) throws IOException {
marshal(object, entityStream);
}
}