package org.jboss.resteasy.plugins.providers;

import org.jboss.resteasy.core.ResteasyContext;
import org.jboss.resteasy.core.messagebody.AsyncBufferedMessageBodyWriter;
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
import org.jboss.resteasy.resteasy_jaxrs.i18n.LogMessages;
import org.jboss.resteasy.spi.ReaderException;
import org.jboss.resteasy.spi.ResteasyConfiguration;
import org.jboss.resteasy.spi.WriterException;
import org.w3c.dom.Document;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

Provider that reads and writes org.w3c.dom.Document.
Author:Solomon Duskis
Version:$Revision: $
/** * Provider that reads and writes org.w3c.dom.Document. * * @author <a href="sduskis@gmail.com">Solomon Duskis</a> * @version $Revision: $ */
@Provider @Produces({"text/xml", "text/*+xml", "application/xml", "application/*+xml"}) @Consumes({"text/xml", "text/*+xml", "application/xml", "application/*+xml"}) public class DocumentProvider extends AbstractEntityProvider<Document> implements AsyncBufferedMessageBodyWriter<Document> { private TransformerFactory transformerFactory; private DocumentBuilderFactory documentBuilder; private boolean expandEntityReferences = false; private boolean enableSecureProcessingFeature = true; private boolean disableDTDs = true; private volatile boolean init; public DocumentProvider() { // nullary constructor so that GraalVM's native-image is able to process this file this(ResteasyContext.getContextData(ResteasyConfiguration.class)); } private void lazyInit() { if (!init) { synchronized (this) { if (!init) { this.documentBuilder = DocumentBuilderFactory.newInstance(); this.transformerFactory = TransformerFactory.newInstance(); this.init = true; } } } } public DocumentProvider(final @Context ResteasyConfiguration config) { LogMessages.LOGGER.debugf("Provider : %s, Method : DocumentProvider", getClass().getName()); try { String s = config.getParameter(ResteasyContextParameters.RESTEASY_EXPAND_ENTITY_REFERENCES); expandEntityReferences = (s == null ? false : Boolean.parseBoolean(s)); } catch (Exception e) { LogMessages.LOGGER.unableToRetrieveConfigExpand(); } try { String s = config.getParameter(ResteasyContextParameters.RESTEASY_SECURE_PROCESSING_FEATURE); enableSecureProcessingFeature = (s == null ? true : Boolean.parseBoolean(s)); } catch (Exception e) { LogMessages.LOGGER.unableToRetrieveConfigSecure(); } try { String s = config.getParameter(ResteasyContextParameters.RESTEASY_DISABLE_DTDS); disableDTDs = (s == null ? true : Boolean.parseBoolean(s)); } catch (Exception e) { LogMessages.LOGGER.unableToRetrieveConfigDTDs(); } } public boolean isReadable(Class<?> clazz, Type type, Annotation[] annotation, MediaType mediaType) { return Document.class.isAssignableFrom(clazz); } public Document readFrom(Class<Document> clazz, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> headers, InputStream input) throws IOException, WebApplicationException { LogMessages.LOGGER.debugf("Provider : %s, Method : readFrom", getClass().getName()); lazyInit(); try { documentBuilder.setExpandEntityReferences(expandEntityReferences); documentBuilder.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, enableSecureProcessingFeature); documentBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", disableDTDs); documentBuilder.setFeature("http://xml.org/sax/features/external-general-entities", expandEntityReferences); documentBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", expandEntityReferences); if (expandEntityReferences) { try { // documentBuilder.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all"); // backward compatibility for jdk 1.6 // we can't directly use XMLConstants.ACCESS_EXTERNAL_DTD here // because it doesn't exist in jdk 1.6 documentBuilder.setAttribute("http://javax.xml.XMLConstants/property/accessExternalDTD", "all"); } catch (IllegalArgumentException e) { //jaxp 1.5 feature not supported } } return documentBuilder.newDocumentBuilder().parse(input); } catch (Exception e) { throw new ReaderException(e); } } public boolean isWriteable(Class<?> clazz, Type type, Annotation[] annotation, MediaType mediaType) { return Document.class.isAssignableFrom(clazz); } public void writeTo(Document document, Class<?> clazz, Type type, Annotation[] annotation, MediaType mediaType, MultivaluedMap<String, Object> headers, OutputStream output) throws IOException, WebApplicationException { LogMessages.LOGGER.debugf("Provider : %s, Method : writeTo", getClass().getName()); lazyInit(); try { DOMSource source = new DOMSource(document); StreamResult result = new StreamResult(output); transformerFactory.newTransformer().transform(source, result); } catch (TransformerException te) { throw new WriterException(te); } } }