XSL:FO to PDF Mediator fails in "no object DCH for MIME type application/pdf"

193 Views Asked by At

I’ve written a simple XSL:FO to PDF mediator. The transform of the XSL:FO document seems a success, but returning a PDF does not seem to work.

In the axis2.xml file I’ve added the rules for application/pdf:

<messageFormatter contentType="application/pdf" class="org.wso2.carbon.relay.ExpandingMessageFormatter"/>   
<messageBuilder contentType="application/pdf" class="org.wso2.carbon.relay.BinaryRelayBuilder"/>

Error Message:

org.apache.axiom.om.OMException: Error reading data handler
        at org.apache.axiom.om.impl.llom.OMTextImpl.internalSerialize(OMTextImpl.java:455)
        at org.apache.axiom.om.impl.util.OMSerializerUtil.serializeChildren(OMSerializerUtil.java:555)
        at org.apache.axiom.om.impl.llom.OMElementImpl.internalSerialize(OMElementImpl.java:879)
        at org.apache.axiom.soap.impl.llom.SOAPEnvelopeImpl.internalSerialize(SOAPEnvelopeImpl.java:230)
        at org.apache.axiom.om.impl.llom.OMSerializableImpl.serialize(OMSerializableImpl.java:125)
        at org.apache.axiom.om.impl.llom.OMSerializableImpl.serialize(OMSerializableImpl.java:113)
        at org.apache.axiom.om.impl.llom.OMElementImpl.toString(OMElementImpl.java:992)
        at java.lang.String.valueOf(String.java:2994)
        at java.lang.StringBuffer.append(StringBuffer.java:263)
        at org.apache.synapse.mediators.builtin.LogMediator.getFullLogMessage(LogMediator.java:208)
        at org.apache.synapse.mediators.builtin.LogMediator.getLogMessage(LogMediator.java:139)
        at org.apache.synapse.mediators.builtin.LogMediator.mediate(LogMediator.java:102)
        at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:109)
        at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:71)
        at org.apache.synapse.mediators.base.SequenceMediator.mediate(SequenceMediator.java:158)
        at org.apache.synapse.mediators.MediatorFaultHandler.onFault(MediatorFaultHandler.java:96)
        at org.apache.synapse.FaultHandler.handleFault(FaultHandler.java:101)
        at org.apache.synapse.core.axis2.ProxyServiceMessageReceiver.receive(ProxyServiceMessageReceiver.java:253)
        at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:180)
        at org.apache.synapse.transport.passthru.ServerWorker.processEntityEnclosingRequest(ServerWorker.java:415)
        at org.apache.synapse.transport.passthru.ServerWorker.run(ServerWorker.java:152)
        at org.apache.axis2.transport.base.threads.NativeWorkerPool$1.run(NativeWorkerPool.java:172)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
Caused by: javax.activation.UnsupportedDataTypeException: no object DCH for MIME type application/pdf
        at javax.activation.ObjectDataContentHandler.writeTo(DataHandler.java:896)
        at javax.activation.DataHandler.writeTo(DataHandler.java:317)
        at org.apache.axiom.util.stax.XMLStreamWriterUtils.writeBase64(XMLStreamWriterUtils.java:62)
        at org.apache.axiom.util.stax.XMLStreamWriterUtils.writeDataHandler(XMLStreamWriterUtils.java:138)
        at org.apache.axiom.om.impl.llom.OMTextImpl.internalSerialize(OMTextImpl.java:452)

The mediator code is currently:

public class PDFMediator extends AbstractMediator {

    public boolean mediate(MessageContext context) {
        trace.info(new String("PdfMediator Process Started"));

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            OMElement body = context.getEnvelope().getBody();
            String text = body.getFirstElement().toString();
            Source src = new StreamSource(new StringReader(text));

            final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);

            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();

            Result res = new SAXResult(fop.getDefaultHandler());
            transformer.transform(src, res);
            trace.info("PDF: " + out.toString());

            DataHandler dataHandler = new DataHandler(out, MimeConstants.MIME_PDF);
            OMFactory fac = OMAbstractFactory.getOMFactory();
            OMText textData = fac.createOMText(dataHandler, true);
            body.addChild(textData);

            context.setProperty(Constants.Configuration.ENABLE_MTOM, Constants.VALUE_TRUE);
            context.setProperty(Constants.Configuration.MESSAGE_TYPE, MimeConstants.MIME_PDF);
            context.setProperty(Constants.Configuration.CONTENT_TYPE, MimeConstants.MIME_PDF);
            context.setProperty(org.apache.axis2.Constants.Configuration.CONTENT_TYPE, MimeConstants.MIME_PDF);
            Object o = context.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
            Map<String, String> headers = (Map<String, String>) o;
            if (headers != null) {
                headers.remove(HTTP.CONTENT_TYPE);
                headers.put(HTTP.CONTENT_TYPE, MimeConstants.MIME_PDF);
            }

            trace.info(new String("PdfMediator Process Finished successfully"));
        } catch (FOPException e) {
            trace.error(new String("PdfMediator Process Failed (FOP): ")+e.toString());
            e.printStackTrace();
            context.setProperty(NhttpConstants.HTTP_SC, 500);
            context.setProperty(NhttpConstants.ERROR_DETAIL, "XSL:FO to PDF transformation failed");
            context.setProperty(NhttpConstants.ERROR_MESSAGE, "XSL:FO to PDF transformation failed: "+e.toString());
            handleException("XSL:FO to PDF transformation failed (FOP): "+e.toString(), context);
        } catch (TransformerException e) {
            trace.error(new String("PdfMediator Process Failed (transform): ")+e.toString());
            e.printStackTrace();
            context.setProperty(NhttpConstants.HTTP_SC, 500);
            context.setProperty(NhttpConstants.ERROR_DETAIL, "XSL:FO to PDF transformation failed");
            context.setProperty(NhttpConstants.ERROR_MESSAGE, "XSL:FO to PDF transformation failed: "+e.toString());
            handleException("XSL:FO to PDF transformation failed (transform): "+e.toString(), context);
        } finally {
            // Clean-up
            try {
                out.close();
            } catch (IOException e) {
                trace.error(new String("PdfMediator Process Failed"));
                handleException("IOError: "+e.toString(), context);
                e.printStackTrace();
            }
        }
        return true;
    }
}

The 'trace.info("PDF: " + out.toString());' is printed with the correct PDF. So is the 'trace.info(new String("PdfMediator Process Finished successfully"));'

The document that is used for testing is:

<?xml version="1.1" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
 <fo:layout-master-set>
  <fo:simple-page-master master-name="my_page" margin="0.5in">
   <fo:region-body/>
  </fo:simple-page-master>
 </fo:layout-master-set>
 <fo:page-sequence master-reference="my_page">
  <fo:flow flow-name="xsl-region-body">
   <fo:block>Hello world!</fo:block>
  </fo:flow>
 </fo:page-sequence>
</fo:root>

The proxy in WSO2 looks like:

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="PDFTest"
       startOnLoad="true"
       statistics="disable"
       trace="disable"
       transports="http,https">
   <target>
      <inSequence>
         <log level="custom">
            <property expression="/" name="input"/>
         </log>
         <class name="com.example.PDFMediator"/>
         <log level="custom">
            <property expression="/" name="output"/>
         </log>
         <send/>
      </inSequence>
   </target>
   <description/>
</proxy>
1

There are 1 best solutions below

0
René de Vries On

The solution is to use SynapseBinaryDataSource, this will use the correct translation. The only remaining issue is that the output is not seen as PDF, therefor the calling proxy needs to add the message type.

<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="PDFTest"
       startOnLoad="true"
       statistics="disable"
       trace="disable"
       transports="http,https">
   <target>
      <inSequence>
         <class name="com.example.PDFMediator"/>
         <loopback/>
      </inSequence>
      <outSequence>
         <property name="messageType"
                   scope="axis2"
                   type="STRING"
                   value="application/pdf"/>
         <respond/>
      </outSequence>
   </target>
   <description/>
</proxy>
public class PDFMediator extends AbstractMediator {

    private static final Log log = LogFactory.getLog(PDFMediator.class);

    private ByteArrayOutputStream convertToPdf(MessageContext context, String text) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            Source src = new StreamSource(new StringReader(text));

            final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);

            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();

            Result res = new SAXResult(fop.getDefaultHandler());
            transformer.transform(src, res);
        } catch (FOPException e) {
            log.error(new String("PdfMediator Process Failed (FOP): ") + e.toString());
            handleException("XSL:FO to PDF transformation failed (FOP): " + e.toString(), context);
        } catch (TransformerException e) {
            log.error(new String("PdfMediator Process Failed (transform): ") + e.toString());
            handleException("XSL:FO to PDF transformation failed (transform): " + e.toString(), context);
        }
        return out;
    }

    private void replaceBody(MessageContext context, ByteArrayInputStream data) throws IOException {
        OMFactory factory = OMAbstractFactory.getOMFactory();
        OMNamespace ns = factory.createOMNamespace("http://ws.apache.org/commons/ns/payload", "ns");
        OMElement element = factory.createOMElement("binary", ns);

        DataHandler dataHandler = new DataHandler(
            new SynapseBinaryDataSource(data, MimeConstants.MIME_PDF));
        OMText textData = factory.createOMText(dataHandler, true);
        textData.setBinary(true);
        element.addChild(textData);

        // remove all child elements
        for (Iterator itr = context.getEnvelope().getBody().getChildElements(); itr.hasNext(); ) {
            OMElement child = (OMElement) itr.next();
            child.detach();
        }
        // add current node
        context.getEnvelope().getBody().addChild(element);

        // set message context properties
        context.setProperty(Constants.Configuration.MESSAGE_TYPE, MimeConstants.MIME_PDF);
        context.setProperty(Constants.Configuration.CONTENT_TYPE, MimeConstants.MIME_PDF);
        Object o = context.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
        Map<String, String> headers = (Map<String, String>) o;
        if (headers != null) {
            headers.remove(HTTP.CONTENT_TYPE);
            headers.put(HTTP.CONTENT_TYPE, MimeConstants.MIME_PDF);
        }
    }

    public boolean mediate(MessageContext context) {
        log.info("PdfMediator Process Started");

        ByteArrayOutputStream out;
        try {
            OMElement body = context.getEnvelope().getBody();
            String text = body.getFirstElement().toString();

            out = convertToPdf(context, text);
            replaceBody(context, new ByteArrayInputStream(out.toByteArray()));

            log.info("PdfMediator Process Finished successfully");
            out.close();
        } catch (IOException e) {
            log.error(new String("PdfMediator Process Failed"));
            handleException("IOError: " + e.toString(), context);
            e.printStackTrace();
        }
        return true;
    }
}