RE: sending xml response from servlet

Here are some hints, building on Aaron's implementation:

1. You might need to set the resulting content type to "application/xml; charset=UTF-8" instead of whatever the input was, because the output looks like it will be UTF-8 below.

2. There are some efficiency considerations here that are subtle and hard to find. 
See http://www.nabble.com/JAXP-performance-problems-t2279828.html
Be careful of  the textbook pattern DocumentBuilderFactory.newInstance().newDocumentBuilder().parse().
Sun's factory implementation does really expensive calls (scans all JAR files in the system, for example), and Tomcat's classloaders make them more expensive (builds two string copies of the list of all JAR files in useless debug routines called by the Sun code), so you could wind up adding many hundred milliseconds by using this textbook pattern.  IBM's Java implementation may have better performance, but still you should take the opportunity to reduce calls to the two factory methods, because by spec they have expensive behavior.

Cache the result of DocumentBuilderFactory.newInstance() statically, and create one directly if you have an implementation in mind (Saxon, Xerces).  Finally, if you application has thread control, you can re-use the DocumentBuilder if you can do it safely in a thread, and just call reset on it aftewards.

public abstract class DOMUtil {

    public static org.w3c.dom.Document newDocument() {
        return getPrivateDocumentBuilder().newDocument();
    }

    public static org.w3c.dom.Document domParse(InputSource source) throws SAXException, IOException {
        return newDocumentBuilder().parse(source);
    }

    public static org.w3c.dom.Document domParse(InputStream stream) throws SAXException, IOException {
        return newDocumentBuilder().parse(stream);
    }

    public static org.w3c.dom.Document domParse(String uri) throws SAXException, IOException {
        return newDocumentBuilder().parse(uri);
    }

    public static org.w3c.dom.Document domParse(File file) throws SAXException, IOException {
        return newDocumentBuilder().parse(file);
    }

    static DocumentBuilderFactory documentBuilderFactory;
    static DocumentBuilder privateDocumentBuilder;

    /**
     * DocumentBuilderFactory is expensive as it searches classpath, and calls
     * javax.xml.parsers.FactoryFinder.findJarServiceProvider calls loader.toString
     * on the Tomcat loader, and that's really expensive as it contains a list of jars.
     * See somewhat related bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5047031
     * and discussion at http://www.nabble.com/JAXP-performance-problems-t2279828.html
     */
    public static DocumentBuilderFactory getDocumentBuilderFactory() {
        synchronized(DOMUtil.class) {
            if (documentBuilderFactory == null) {
                // Don't do DocumentBuilderFactory.newInstance()
                // because JDK 1.5.0_008 has debug statements in javax.xml.parsers.DocumentBuilderFactory
                // that needlessly call .toString() on the classloader, and the toString method on the Tomcat classloader
                // allocates two StringBuffers and creates a 20K character String.  So we just skip the
		    //  DocumentBuilderFactory.newInstance() and use its default
                // implementation for JDK 1.5 directly.  Change to use Saxon or your favorite implementation.
                // documentBuilderFactory = DocumentBuilderFactory.newInstance();
                documentBuilderFactory = new com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl();
                documentBuilderFactory.setValidating(false);
                documentBuilderFactory.setNamespaceAware(true);
                documentBuilderFactory.setExpandEntityReferences(true);
            }
            return documentBuilderFactory;
        }
    }

    /** 
     * Don't call this if you can avoid it; use newDocument or domParse instead.
     */
    public static DocumentBuilder newDocumentBuilder() {
        try {
            return getDocumentBuilderFactory().newDocumentBuilder();
        } catch (ParserConfigurationException pex) {
            // This can't happen unless the system is misconfigured.
            throw new RuntimeException(pex.getMessage());
        }
    }

    // never pass this static object this class as it's not Thread-safe
    private static DocumentBuilder getPrivateDocumentBuilder() {
        if (privateDocumentBuilder == null) {
            try {
                privateDocumentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
            } catch (ParserConfigurationException pex) {
                // This can't happen unless the system is misconfigured.
                throw new RuntimeException(pex.getMessage());
            }
        } else {
            privateDocumentBuilder.reset();
        }
        return privateDocumentBuilder;
    }

}


-----Original Message-----
From: www-forms-request@w3.org [mailto:www-forms-request@w3.org] On Behalf Of Aaron Reed
Sent: Friday, February 16, 2007 12:04 PM
To: www-forms@w3.org
Subject: Re: sending xml response from servlet


Hi Iņaki,

I am no servlet or java guru by any stretch of the imagination, but this 
is what I did to build a simple servlet that got a xml document from 
xforms, tweaked a few text nodes in it (via the method 
changePassword()), and then sent back a xml response to replace the 
instance that was sent:

public void doWork(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
			
	  String contentType = req.getContentType();
	  if(contentType.equalsIgnoreCase("text/xml") || 
contentType.equalsIgnoreCase("application/xml")) {
	  	InputStream inputStream = req.getInputStream();
	    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	    try {
	      DocumentBuilder docBuilder = factory.newDocumentBuilder();
	      Document doc = docBuilder.parse(inputStream);
	      if(doc != null) {
	      	boolean result = changePassword(doc);
	      	if (result == true) {
	          resp.setContentType(contentType);
	          // following code serializes dom to xml file

               OutputFormat of =  new OutputFormat(doc);
               of.setIndenting(false);
               ServletOutputStream outputStream = resp.getOutputStream();
               XMLSerializer serializer = new XMLSerializer();
               serializer.setOutputFormat(of);
               serializer.setOutputByteStream(outputStream);
               DOMSerializer domSerializer = serializer.asDOMSerializer();
               domSerializer.serialize(doc);
               outputStream.flush();
               outputStream.close();
	      	}
	      }
	    }
	    catch (Exception e) {
	    	e.printStackTrace();
	    }
	  }

	}

Again, I don't know if this is the way a 'real' app designer would do 
it, but it worked for me in my little test scenario.

Good luck,
--Aaron


Iņaki Salinas Bueno wrote:
> Hello,
> 
> Can someone recommend me a set of libraries that allow a servlet 
> receive/send XML documents from/to xforms? I have found several 
> libraries, but I don't know which is more adapted for what I want to do.
> 
> I'm using xforms in client side and a servlet for xindice (DB manager) 
> calls in server side.
> 
> The servlet gets the xml document from xforms and add it in the DB 
> correctly (I used a Xindice web application example and the tip 'Xforms 
> tip: Accepting XForms data in Java 
> <http://www-128.ibm.com/developerworks/java/library/x-xformstipjava/index.html>' 
> for its construction), but I don't know how can I get a XML document 
> from DB and put it in the response object of the servlet.
> 
> The example of the tip works with strings, so following it for the 
> response I would have to take the XML document of the DB, transform it 
> into a string, and then send it. Cannot be the XML document sent as 
> application/xml without transforming it into a string?
> 
> Maybe questions are more java related than xforms, but they are 
> working-with-xml related so I think that I can found help in this forum.
> 
> Thanks
> Iņaki

Received on Friday, 16 February 2007 22:21:43 UTC