From: SourceForge.net <no...@so...> - 2008-06-10 09:10:40
|
Bugs item #1985229, was opened at 2008-06-05 10:45 Message generated for change (Comment added) made by bodewig You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=377768&aid=1985229&group_id=23187 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: None Group: Java 1.1 >Status: Closed Resolution: Fixed Priority: 5 Private: No Submitted By: MATSUHASHI KAZUAKI (kmatsuhashi) Assigned to: Stefan Bodewig (bodewig) Summary: SimpleXpathEngine throws NPE for invalid xpath given Initial Comment: My environment: xmlunit-1.1, Windows 2000, JDK 1.5.0_7 with Xalan-j2.7.0, ant-1.7.0, junit3.8.1 <<What I noticed about xmlunit-1.1>> I modified the org.custommonkey.xmlunit.test_SimpleXpathEngine class: add a test method "testEvaluateInvalidXPath". ============== org.custommonkey.xmlunit.test_SimpleXpathEngine ====================== public void testEvaluateInvalidXPath() throws Exception { String xpath = "count(test//*[@attrOne='open source])"; String result = newXpathEngine().evaluate(xpath, testDocument); } ------------------------------------------------------------------------------------- When I ran the junit, I got the following report: ============== TEST-org.custommonkey.xmlunit.test_SimpleXpathEngine.xml ============= <testcase classname="org.custommonkey.xmlunit.test_SimpleXpathEngine" name="testEvaluateInvalidXPath" time="0.032"> <error type="java.lang.NullPointerException">java.lang.NullPointerException at org.custommonkey.xmlunit.SimpleXpathEngine.performTransform(SimpleXpathEngine.java:136) at org.custommonkey.xmlunit.SimpleXpathEngine.evaluate(SimpleXpathEngine.java:202) at org.custommonkey.xmlunit.test_SimpleXpathEngine.testEvaluateInvalidXPathWith(test_SimpleXpathEngine.java:85) </error> </testcase> ------------------------------------------------------------------------------------- The root cause was obvious: the XPath I gave was syntactically wrong. It had odd number of single quotation. It had to be 'open source' rather than 'open source. Also I noticed some lines of messages from the underlying XSLT processor (in my case, Xalan-j 2.7.0) were written into System.err. These messages were valuable. They told me what was the cause. ============== TEST-org.custommonkey.xmlunit.test_SimpleXpathEngine.xml ============= <system-err><![CDATA[SystemId は不明; 行 #1; 桁 #251; 引用符が誤っているリテラル... 単一引用符が必要でした! SystemId は不明; 行 #1; 桁 #251; ] が必要でしたが、source が見つかりました (エラーのロケーションは不明)java.lang.ClassCastException: org.apache.xpath.objects.XString ]]></system-err> ------------------------------------------------------------------------------------- <<Discussion>> Point.1: NullPointerException is terrible. Why I got a NullPointerException? The XMLunit document says the design idea is to throw org.custommonkey.xmlunit.exceptions.ConfiguratinException when a invalid XPath given rather than NPE. Point.2: Why the XSLT processor diagnostics goes to System.err? I want the message to be carried by the thrown ConfigurationException object rather than immediately swept away into System.err. Let me tell why. I've developed some custom Ant tasks (namely "AntUnit4XML") which drives the xmlunit's SimpleXpathEngine object under the AntUnit assertion mechanism. When I ran my assertion tasks, the messages from XSLT processor behind the SimpleXpathEngine vanished somehow (actually swept away into System.err of the forked process, which is invisible). I tried to figure out how to salvage the diagnostics but I couldn't. Eventually I misses the XSLT processor diagnostics. <<How I investigated>> I changed my testcase to make it more informative: ============== org.custommonkey.xmlunit.test_SimpleXpathEngine ====================== public void testEvaluateInvalidXPath() throws Exception { String xpath = "count(test//*[@attrOne='open 'source'])"; try { String result = newXpathEngine().evaluate(xpath, testDocument); fail("expected Exception to be thrown but not"); } catch (NullPointerException npe) { String msg = "NullPointerException has been thrown." + ((npe.getMessage() != null) ? " with message \"" + npe.getMessage() + "\"" : ""); fail(msg); } catch (ConfigurationException ce) { String msg = ce.getClass().getName() + " has been thrown with message \"" + ce.getMessage() + "\""; //assertTrue(msg + "\", expected \"Error\" contained but not", // ce.getMessage().indexOf("Error")>=0); fail(msg); } catch (Exception e) { String msg = "unexpected " + e.getClass().getName() + ((e.getMessage() != null) ? " " + e.getMessage() : ""); fail(msg); } } ------------------------------------------------------------------------------------- Then I examined Saxon6.5.5 as alternative XSLT processor to see how it deals with my invalid XPath. I put the saxon.jar in the JRE/lib/endorsed dir, and slightly modified the build.xml' test target: ============================= build.xml ================================================= <target name="test" depends="compile" description="runs the tests"> <junit ... <sysproperty key="javax.xml.transform.TransformerFactory" value="com.icl.saxon.TransformerFactoryImpl"/> ... ----------------------------------------------------------------------------------------- When I run the test, I got the following report. ============== TEST-org.custommonkey.xmlunit.test_SimpleXpathEngine.xml ============= <testcase classname="org.custommonkey.xmlunit.test_SimpleXpathEngine" name="testEvaluateInvalidXPath" time="0.25"> <failure message="expected:<...test><nodeWithoutAttributes>intellectual property rights </nodeWithoutAttributes><nodeWithoutAttributes>make us all poorer </nodeWithoutAttributes><nodeWithAttributes attrOne="open source " attrTwo="is the answer ">free your code from its chains</nodeWithAttributes></tes...> but was:<...xpathResult><test><nodeWithoutAttributes>intellectual property rights </nodeWithoutAttributes><nodeWithoutAttributes>make us all poorer </nodeWithoutAttributes><nodeWithAttributes attrOne="open source " attrTwo="is the answer ">free your code from its chains</nodeWithAttributes></test></xpathResul...>" type="junit.framework.ComparisonFailure">junit.framework.ComparisonFailure: expected:<...test><nodeWithoutAttributes>intellectual property rights </nodeWithoutAttributes><nodeWithoutAttributes>make us all poorer </nodeWithoutAttributes><nodeWithAttributes attrOne="open source " attrTwo="is the answer ">free your code from its chains</nodeWithAttributes></tes...> but was:<...xpathResult><test><nodeWithoutAttributes>intellectual property rights </nodeWithoutAttributes><nodeWithoutAttributes>make us all poorer </nodeWithoutAttributes><nodeWithAttributes attrOne="open source " attrTwo="is the answer ">free your code from its chains</nodeWithAttributes></test></xpathResul...> at org.custommonkey.xmlunit.test_SimpleXpathEngine.testGetXPathResultNode(test_SimpleXpathEngine.java:60) </failure> </testcase> ... <system-out><![CDATA[[testEvaluateInvalidXPath] org.custommonkey.xmlunit.exceptions.ConfigurationException has been thrown. message="Failed to compile stylesheet. 1 error detected." ]]></system-out> <system-err><![CDATA[Error at xsl:value-of on line 1 of file:/C:/eclipse/workspace/xmlunit-1.1/: Error in expression count(test//*[@attrOne='open source]): expected "]", found "<name>" ]]></system-err> ------------------------------------------------------------------------------------- Saxon did not throw NPE. Saxon threw TransformerConfigurationException, which was caught by try ... catch ... block in the test method, and wrapped by SimpleXpathEngine into a ConfigurationException. Goodness. This is the way we want it work. This examination proved that it was Xalan who actually threw the NPE. Then my next question I got was how can I make Xalan not to throw NPE? .. well, let me tell about it later. I wanted the ConfigurationException contain the diagnostics from SAXON rather than System.err. Why the diagnostics goes to System.err? I checked the source code of SimpleXpathEngine#performTransform() and found no javax.xml.transform.ErrorListener is set to the TrasnformerFactory. If no ErrorListener is provided, the TransformerFactory will print the messages into System.err. This portion can be improved. I added a SimpleErrorListener class and changed the performTransform() method as follows: ============== org.custommonkey.xmlunit.SimpleXpathEngine =========================== private void performTransform(String xslt, Document document, Result result) throws TransformerException, ConfigurationException { StringWriter sw = new StringWriter(); ErrorListener errorListener = new SimpleErrorListener(new PrintWriter(sw)); try { StreamSource source = new StreamSource(new StringReader(xslt)); TransformerFactory trf = XMLUnit.getTransformerFactory(); trf.setErrorListener(errorListener); Transformer tr = trf.newTransformer(source); tr.setErrorListener(errorListener); tr.transform(new DOMSource(document), result); } catch (javax.xml.transform.TransformerConfigurationException ex) { throw new ConfigurationException(sw.toString()); } } ------------------------------------------------------------------------------------- With this code change applied, the testcase gave me following report ============== TEST-org.custommonkey.xmlunit.test_SimpleXpathEngine.xml ============= <testcase classname="org.custommonkey.xmlunit.test_SimpleXpathEngine" name="testEvaluateInvalidXPath" time="0.0"> <failure message="org.custommonkey.xmlunit.exceptions.ConfigurationException has been thrown with message "Error at xsl:value-of on line 1 of file:/C:/eclipse/workspace/xmlunit-1.1/: Unmatched quote in expression count(test//*[@attrOne='open source])"" type="junit.framework.AssertionFailedError">junit.framework.AssertionFailedError: org.custommonkey.xmlunit.exceptions.ConfigurationException has been thrown with message "Error at xsl:value-of on line 1 of file:/C:/eclipse/workspace/xmlunit-1.1/: Unmatched quote in expression count(test//*[@attrOne='open source])" at org.custommonkey.xmlunit.test_SimpleXpathEngine.testEvaluateInvalidXPath(test_SimpleXpathEngine.java:98) </failure> </testcase> ------------------------------------------------------------------------------------- OK, now the message is wrapped in the ConfigurationException. This is the way things should be. With the changes to performTransform() applied, I tried Xalan to see if the NPE still be raised. This time I got the following report: ============== TEST-org.custommonkey.xmlunit.test_SimpleXpathEngine.xml ============= <testcase classname="org.custommonkey.xmlunit.test_SimpleXpathEngine" name="testEvaluateInvalidXPath" time="0.046"> <failure message="org.custommonkey.xmlunit.exceptions.ConfigurationException has been thrown with message "Error on line 1 column 179: 引用符が誤っているリテラル... 単一引用符が必要でした!Errorjavax.xml.transform.TransformerException: 引用符が誤っているリテラル... 単一引用符が必要でした!Errorjavax.xml.transform.TransformerException: javax.xml.transform.TransformerException: 引用符が誤っているリテラル... 単一引用符が必要でした!"" type="junit.framework.AssertionFailedError">junit.framework.AssertionFailedError: org.custommonkey.xmlunit.exceptions.ConfigurationException has been thrown with message "Error on line 1 column 179: 引用符が誤っているリテラル... 単一引用符が必要でした!Errorjavax.xml.transform.TransformerException: 引用符が誤っているリテラル... 単一引用符が必要でした!Errorjavax.xml.transform.TransformerException: javax.xml.transform.TransformerException: 引用符が誤っているリテラル... 単一引用符が必要でした!" at org.custommonkey.xmlunit.test_SimpleXpathEngine.testEvaluateInvalidXPath(test_SimpleXpathEngine.java:98) </failure> </testcase> ------------------------------------------------------------------------------------- Now Xalan ceased throwing NPE. Fine. It seems that setting a ErrorListener object explicitly to the TransformerFactory fixed the NPE problem of Xalan. The message produced by Xalan tends to be verbose, uselessly repeating a single sentence twice or three times. I wanted to make it concise but could not find how to. <<Summary of changes I propose>> - modify src/java/org.custommonkey.xmlunit.SimpleXpathEngine - add src/java/org.custommonkey.xmlunit.SimpleErrorListener - add src/java/org.custommonkey.xmlunit.SAXSourceLocator - modify tests/java/org.custommonkey.xmlunit.test_SimpleXpathEngine - add tests/java/org.custommonkey.xmlunit.test_SimpleErrorListener ---------------------------------------------------------------------- >Comment By: Stefan Bodewig (bodewig) Date: 2008-06-10 11:10 Message: Logged In: YES user_id=113148 Originator: NO fine, so we can close this issue. Thanks. ---------------------------------------------------------------------- Comment By: MATSUHASHI KAZUAKI (kmatsuhashi) Date: 2008-06-10 10:38 Message: Logged In: YES user_id=823604 Originator: YES Stefan, Thank you for looking at the issue. I totally agree with your idea. ---------------------------------------------------------------------- Comment By: Stefan Bodewig (bodewig) Date: 2008-06-06 16:23 Message: Logged In: YES user_id=113148 Originator: NO I've introduced an easier mechanism which on Java 1.4 produces org.custommonkey.xmlunit.exceptions.ConfigurationException: javax.xml.transform.TransformerConfigurationException: javax.xml.transform.TransformerException: javax.xml.transform.TransformerException: Literalwert mit falschen Anführungszeichen... einfache Anführungszeichen erwartet! for me (the German text means "literal with wrong quotes, single quotes expected") which I think should be good enough. JDK 1.6 produces a generic "couldn't compile stylesheet warning" here, but JAXP13XpathEngine gives the same exception message as 1.4 in the second exception nested into the resulting XpathException. I'd leave it at that, feel free to reopen the issue if you feel trunk could be improved further. http://xmlunit.svn.sourceforge.net/viewvc/xmlunit?view=rev&revision=268 ---------------------------------------------------------------------- Comment By: Stefan Bodewig (bodewig) Date: 2008-06-06 16:02 Message: Logged In: YES user_id=113148 Originator: NO personally I'd rather expect an XpathException than a ConfigurationException. I've written and committed a testcase based on yours which only expects either an XpathException or a ConfigurationException and it passes on stock JDK 1.4.2, JDK 1.5 and JDK 1.6, so it really only seems to be Xalan 2.7.0 - you might think about upgrading Xalan 2.7.1. The NPE seems to be coming from XMLUnit and occurs because your version of Xalan returns null for TransformerFactory.newTransformer which is evil. I'll add an explicit null guard. While I agree that having better diagnostics here would be nice, I'm more than a bit wary of adding three classes to get at it - in particular since this is more of an implementation specific problem with an obsolete version of Xalan. ---------------------------------------------------------------------- Comment By: Stefan Bodewig (bodewig) Date: 2008-06-05 13:16 Message: Logged In: YES user_id=113148 Originator: NO many thanks for the very thorough analysis, I'll look into your patch later today. For your project you'd probably better use XMLUnit.newXpathEngine which is supposed to provide you with a JAXP 1.3 XPath engine in your setup. In my experience there are too many problems with broken XSLT processors when using SimpleXpathEngine. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=377768&aid=1985229&group_id=23187 |