Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I change the default logging in Java Transformer

Tags:

java

xml

I implemented the code to enable me to print formatted XML

import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class TransformThis implements ErrorListener {

    public static void main(String[] args) throws java.lang.Exception {

        TransformThis test = new TransformThis();
        String goodXML;
        String badXML;

       goodXML = "<root><level1>WellFormed</level1></root>";
       System.out.println(test.prettyPrint(goodXML));
       badXML = "<root><level1>Not Well Formed</level1>";
       System.out.println(test.prettyPrint(badXML));
   }

   public String prettyPrint(String xml) {

       Source xmlInput = new StreamSource(new StringReader(xml));
       StringWriter stringWriter = new StringWriter();
       StreamResult xmlOutput = new StreamResult(stringWriter);
       TransformerFactory transformerFactory = TransformerFactory.newInstance();
       transformerFactory.setAttribute("indent-number", 4);

       try {
           Transformer transformer = transformerFactory.newTransformer();
           transformer.setErrorListener(this);
           transformer.setOutputProperty(OutputKeys.INDENT, "yes");
           transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
           transformer.transform(xmlInput, xmlOutput);
       } catch (Exception ex) {
           System.out.println("My message: " + ex.getMessage());
       }

       return xmlOutput.getWriter().toString();
   }


   @Override
   public void warning(TransformerException exception) throws TransformerException {
       //throw new UnsupportedOperationException("Not supported yet.");
   }

   @Override
   public void error(TransformerException exception) throws TransformerException {
      //throw new UnsupportedOperationException("Not supported yet.");
   }

   @Override
   public void fatalError(TransformerException exception) throws TransformerException {
       //throw new UnsupportedOperationException("Not supported yet.");
   }
}

When the XML is well formed, I get the following output - exactly what I want

<root>
    <level1>WellFormed</level1>
</root>

If there is a problem with the XML I get the following output - fine, except for the [Fatal Error] output

[Fatal Error] :1:39: XML document structures must start and end within the same entity.
My message: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 39; XML document structures must start and end within the same entity.
<root>
   <level1>Not Well Formed</level1>

The transform function throws an exception as well as sends a [Fatal Error] to stderr/stdout. Is there a way to prevent the [Fatal Error] log from appearing?

like image 971
jhadley Avatar asked Jan 18 '14 18:01

jhadley


1 Answers

You have found a bug (two, actually) in the JDK. Congratulations! (Or, condolences, I suppose).

First bug is that it's a "fatal" error, but it will call ErrorListener.error() instead of ErrorListener.fatalError(). If you put a println statement in error() in your example, you'll see it's being called.

The second bug is that ignoring the first one above, what you are doing should work.

But it doesn't.

Throwing your example in the debugger and delving down into the JDK I found that the error listener isn't propagated down into the underlying XMLScanner and XMLErrorReporter.

What is happening is that the XMLErrorReporter is instantiating a com.sun.org.apache.xerces.internal.util.DefaultErrorHandler and calling its fatalError() method which is what is spitting that [fatal error] message out to stderr.

Specifically, this happens at line 422 of com.sun.org.apache.xerces.internal.impl.XMLErrorReporter

After that it rethrows the exception up the stack and the TransformerImpl fires it to your listener.

What should be happening is that those underlying classes should either have a proxy to the higher level listener you passed in, or a local no-op listener should be created to silence the output in the lower levels. I suspect it's the latter since otherwise you'd get notified twice.

I'll need to look at the abstraction tree more closely and debug the construction chain to find out why that's not happening but in short, unfortunately this is a bug in the JDK and there isn't a way to control/prevent it. (This is testing on Java 1.7.0_25-b15).

like image 141
Brian Roach Avatar answered Sep 22 '22 10:09

Brian Roach