/*
* Copyright (c) 2009-2015
* IT-Consulting Stephan Schloepke (http://www.schloepke.de/)
* klemm software consulting Mirko Klemm (http://www.klemm-scs.com/)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jbasics.jaxrs;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
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.core.Response.Status;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Providers;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.WeakHashMap;
/**
* @author Stephan Schl�pke, Christopher Stolle
*/
public class XHTMLStylesheetAttachedXMLProvider implements MessageBodyWriter<XHTMLStylesheetAttachedJAXB<?>> {
private static final Charset UTF8 = Charset.forName("UTF-8"); //$NON-NLS-1$
private static final Charset UTF16 = Charset.forName("UTF-16"); //$NON-NLS-1$
private static final Map<Class<?>, JAXBContext> JAXB_CONTEXT_CACHE = new WeakHashMap<Class<?>, JAXBContext>();
private final Providers providers;
public XHTMLStylesheetAttachedXMLProvider(@Context final Providers providers) {
this.providers = providers;
}
private static void writeToXhtml(final XHTMLStylesheetAttachedJAXB<?> t, final Charset charset, final Marshaller marshaller, final Writer w,
boolean htmlMethod)
throws JAXBException, IOException {
try {
final Transformer transformer = TransformerFactory.newInstance().newTransformer(
new StreamSource(t.getLocalResource().openStream(), t.getLocalResource().toExternalForm()));
Source source = new JAXBSource(marshaller, t.getEntity());
final Result result = new StreamResult(w);
if (t.isHandleXInclude()) {
final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setXIncludeAware(true);
final XMLReader reader = factory.newSAXParser().getXMLReader();
final ByteArrayOutputStream temp = new ByteArrayOutputStream();
marshaller.marshal(t.getEntity(), new OutputStreamWriter(temp, charset));
source = new SAXSource(reader, new InputSource(new ByteArrayInputStream(temp.toByteArray())));
}
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
if (htmlMethod) {
ByteArrayOutputStream s = new ByteArrayOutputStream();
Transformer htmlTransformer = TransformerFactory.newInstance().newTransformer(
new StreamSource(XHTMLStylesheetAttachedXMLProvider.class.getResourceAsStream("xhtmlToHtmlTransformer.xslt"),
t.getLocalResource().toExternalForm()));
transformer.transform(source, new StreamResult(s));
htmlTransformer.transform(new StreamSource(new ByteArrayInputStream(s.toByteArray())), result);
} else {
transformer.transform(source, result);
}
} catch (final Exception e) {
throw new WebApplicationException(e, Status.INTERNAL_SERVER_ERROR);
}
}
private static void writeToXml(final XHTMLStylesheetAttachedJAXB<?> t, final Charset charset, final Marshaller marshaller, final Writer wIn)
throws JAXBException, IOException {
try {
final Object jaxbFragmentProperty = marshaller.getProperty(Marshaller.JAXB_FRAGMENT);
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
Writer w = wIn;
ByteArrayOutputStream bos = null;
if (t.isHandleXInclude()) {
bos = new ByteArrayOutputStream();
w = new OutputStreamWriter(bos, charset);
}
w.write("<?xml version=\"1.0\" "); //$NON-NLS-1$
if (charset != XHTMLStylesheetAttachedXMLProvider.UTF8 && charset != XHTMLStylesheetAttachedXMLProvider.UTF16) {
w.write("encoding=\""); //$NON-NLS-1$
w.write(charset.name());
w.write("\" "); //$NON-NLS-1$
}
w.write("?>\n"); //$NON-NLS-1$
w.write("<?xml-stylesheet type=\"text/xsl\" href=\""); //$NON-NLS-1$
w.write(t.getStylesheet().toASCIIString());
w.write("\" ?>\n\n"); //$NON-NLS-1$
marshaller.marshal(t.getEntity(), w);
if (bos != null) {
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setXIncludeAware(true);
final XMLReader reader = factory.newSAXParser().getXMLReader();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
transformer.transform(new SAXSource(reader, new InputSource(new ByteArrayInputStream(bos.toByteArray()))), new StreamResult(wIn));
}
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, jaxbFragmentProperty);
} catch (final Exception e) {
throw new WebApplicationException(e, Status.INTERNAL_SERVER_ERROR);
}
}
@Override
public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
return XHTMLStylesheetAttachedJAXB.class.isAssignableFrom(type) && (MediaType.APPLICATION_XHTML_XML_TYPE.equals(mediaType) ||
MediaType.APPLICATION_XML_TYPE.equals(mediaType) ||
MediaType.TEXT_HTML_TYPE.equals(mediaType) ||
MediaType.TEXT_XML_TYPE.equals(mediaType) ||
mediaType != null && mediaType.getSubtype().endsWith("+xml")); //$NON-NLS-1$
}
@Override
public long getSize(final XHTMLStylesheetAttachedJAXB<?> t, final Class<?> type, final Type genericType, final Annotation[] annotations,
final MediaType mediaType) {
return -1;
}
@Override
public void writeTo(final XHTMLStylesheetAttachedJAXB<?> t, final Class<?> type, final Type genericType, final Annotation[] annotations,
final MediaType mediaType,
final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException {
try {
final Marshaller marshaller = getMarshaller(t.getEntityType(), mediaType);
Charset charset = XHTMLStylesheetAttachedXMLProvider.UTF8;
if (mediaType != null) {
final String temp = mediaType.getParameters().get("charset"); //$NON-NLS-1$
if (temp != null) {
charset = Charset.forName(temp);
}
}
final Writer w = new OutputStreamWriter(entityStream, charset);
if (MediaType.APPLICATION_XHTML_XML_TYPE.equals(mediaType) || MediaType.TEXT_HTML_TYPE.equals(mediaType)) {
XHTMLStylesheetAttachedXMLProvider.writeToXhtml(t, charset, marshaller, w, MediaType.TEXT_HTML_TYPE.equals(mediaType));
} else {
XHTMLStylesheetAttachedXMLProvider.writeToXml(t, charset, marshaller, w);
}
} catch (final JAXBException e) {
throw new WebApplicationException(e, Status.INTERNAL_SERVER_ERROR);
}
}
protected final Marshaller getMarshaller(final Class<?> type, final MediaType mt) throws JAXBException {
final ContextResolver<Marshaller> marshallerResolver = this.providers.getContextResolver(Marshaller.class, mt);
Marshaller marshaller = null;
if (marshallerResolver != null) {
marshaller = marshallerResolver.getContext(type);
}
if (marshaller == null) {
JAXBContext context = null;
final ContextResolver<JAXBContext> cr = this.providers.getContextResolver(JAXBContext.class, mt);
if (cr != null) {
context = cr.getContext(type);
}
if (context == null) {
synchronized (XHTMLStylesheetAttachedXMLProvider.JAXB_CONTEXT_CACHE) {
context = XHTMLStylesheetAttachedXMLProvider.JAXB_CONTEXT_CACHE.get(type);
if (context == null) {
context = JAXBContext.newInstance(type);
XHTMLStylesheetAttachedXMLProvider.JAXB_CONTEXT_CACHE.put(type, context);
}
}
}
marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
}
return marshaller;
}
}