We moved to a new build server and now XML validation is several orders of magnitude slower than previously, causing my builds to time out. I can restore previous build performance by commenting out validation logic which is coded as follows:
public static void validate(File xsd, final File xmlSample) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false); //disable DTD validation
factory.setNamespaceAware(true);
SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setSchema(schemaFactory.newSchema(new Source[]{new StreamSource(xsd)}));
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setErrorHandler(new ErrorHandler() {
#Override
public void warning(SAXParseException exception) {
message("An XSD validation warning occurred: " + exception.getMessage(), true, exception);
exception.printStackTrace();
}
#Override
public void error(SAXParseException exception) {
message("An XSD validation error occurred: " + exception.getMessage(), true, exception);
exception.printStackTrace();
}
#Override
public void fatalError(SAXParseException exception) {
message("An XSD validation fatal error occurred: " + exception.getMessage() + "; " + xmlSample.getName(), true, exception);
exception.printStackTrace();
}
});
reader.parse(new InputSource(new FileInputStream(xmlSample)));
} catch (SAXException e) {
message("ERROR: An XSD validation error occurred.", true, e);
} catch (ParserConfigurationException e) {
message("ERROR: A parser configuration error occurred.", true, e);
} catch (IOException e) {
message("ERROR: An input/output exception occurred.", true, e);
}
}
The new build server is running Java 7 and 64 bit Linux 2.6.32.
It's been suggested that some kind of network access occurs during validation, leading to this issue, but the schema and XML document are passed in as local files.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://********/ddgen/indadm_xsd_gso_3"
targetNamespace="http://********/ddgen/indadm_xsd_gso_3"
elementFormDefault="qualified">
<!--Schema generator version: 3.5.3-->
<xsd:element name="indadm" type="indadm"/>
....
EDIT: The schema used to import the XML schema from w3.org; removing the import statement (it was a bug) has made the test run times more stable (1 minute per test) compared to sub-10-second runs on the old server, which of course had the buggy code to import from w3.org. So I would say the w3.org import was one of the problems but not the only problem.
Related
I woudlike to get all errors from my validating xml. Actually my method stop after catch the first error :
public List<String> validation(File file) {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
List<String> statut = new ArrayList<>();
try {
assert xsd != null;
Schema schema = schemaFactory.newSchema(xsd);
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new File(file.getPath() + file.getname())));
statut.add(file.getname() + " is valid");
} catch (SAXException | IOException e) {
statut.add(file.getname() + " not valid :\n");
statut.add(e.getMessage());
}
return statut;
}
How can I get all exception detected ?
You can use Validator.setErrorHandler(ErrorHandler) to register your own error handler.
public abstract void setErrorHandler(ErrorHandler errorHandler)
Sets the ErrorHandler to receive errors encountered during the validate method invocation.
Error handler can be used to customize the error handling process during a validation. When an ErrorHandler is set, errors found during the validation will be first sent to the ErrorHandler.
The error handler can abort further validation immediately by throwing SAXException from the handler. Or for example it can print an error to the screen and try to continue the validation by returning normally from the ErrorHandler
If any Throwable is thrown from an ErrorHandler, the caller of the validate method will be thrown the same Throwable object.
Validator is not allowed to throw SAXException without first reporting it to ErrorHandler.
In your own error handler, you can aggregate the exceptions and then after validation, process as needed/wanted.
Specifically, it allows you to continue validation if errors are encountered enabling you to catch them all in the handler.
I've got a problem similar to this question: SAXParseException localized
I'm trying to parse a XML file and get a list of parser errors (SAXParseException) in a several languages for example:
XmlImporter.importFile(params, "en") should return a list of errors in English, XmlImporter.importFile(params, "fr") should return a list of errors in French, XmlImporter.importFile(params, "pl") should return a list of errors in Polish language.
Every call of XmlImporter.importFile(params, "...") may be with a different locale.
This is my validation method:
private void validate(String xmlFilePath, String schemaFilePath) throws Exception {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new File(schemaFilePath));
Validator validator = schema.newValidator();
XmlErrorHandler errorHandler = new XmlErrorHandler();
validator.setErrorHandler(errorHandler);
try (InputStream stream = new FileInputStream(new File(xmlFilePath))) {
validator.validate(new StreamSource(stream));
}
XmlErrorHandler:
public class XmlErrorHandler implements ErrorHandler {
private List<String> errorsList = new ArrayList<>();
public List<String> getErrorsList() {
return errorsList;
}
#Override
public void warning(SAXParseException exception) throws SAXException {
errorsList.add(prepareExceptionDescription(exception));
}
#Override
public void error(SAXParseException exception) throws SAXException {
errorsList.add(prepareExceptionDescription(exception));
}
#Override
public void fatalError(SAXParseException exception) throws SAXException {
errorsList.add(prepareExceptionDescription(exception));
}
private String prepareExceptionDescription(SAXParseException exception) {
return "Error: " +
"colNumber: " + exception.getColumnNumber() +
" line number: " + exception.getLineNumber() +
" message: " + exception.getLocalizedMessage();
}
}
I assume, that I need to pass somehow/somewhere java.util.Locale/String to get in exception.getLocalizedMessage() custom message (in en, fr or pl)?
By the default Xerces (Java Parser which is used to convert XML file to Java object) could provide internationalization for given languages:
XMLSchemaMessages_de.properties XMLSchemaMessages_es.properties
XMLSchemaMessages_fr.properties XMLSchemaMessages_it.properties
XMLSchemaMessages_ja.properties XMLSchemaMessages_ko.properties
XMLSchemaMessages_pt_BR.properties XMLSchemaMessages_sv.properties
XMLSchemaMessages_zh_CN.properties XMLSchemaMessages_zh_TW.properties
To provide internationalization in other language:
Get XMLSchemaMessages.properties file from Apache Xerces and rename file to a new file XMLSchemaMessages_LANG.properties, where LANG needs to be changed to a new language.
Update file's messages to a new language and place this file in a classpath (You can add this file to src\main\resources\com\sun\org\apache\xerces\internal\impl\msg)
Exceptions will be visible in a new language (messages will be taken from XMLSchemaMessages_LANG.properties file)
I am using javax.xml.transform.* to do XSLT transformation. Since the xslt file to be used comes from the outside world there could be errors in that file, and I am going to give back some meaningful response to the user.
Although I can easily catch the TransformationExceptions, I found no way to obtain enough information from it. For example, if there is a tag to be terminated by an end-tag, printStackTrace() gives scarring message
javax.xml.transform.TransformerConfigurationException: Could not compile stylesheet
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTemplates(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(Unknown Source)
... (100 lines)
and getMessage() gives only
Could not compile stylesheet
None of them gives the real reason of the error.
I noticed that in Eclipse test console I can see the following
[Fatal Error] :259:155: The element type "sometag" must be terminated by the matching end-tag "</sometag>".
ERROR: 'The element type "sometag" must be terminated by the matching end-tag "</sometag>".'
FATAL ERROR: 'Could not compile stylesheet'
This is exactly what I want. Unfortunately, since this is a web application, the user cannot see this.
How can I display the correct error message to the user?
Put your own ErrorListener on your Transformer instance using Transformer.setErrorListener, like so:
final List<TransformationException> errors = new ArrayList<TransformationException>();
Transformer transformer = ... ;
transformer.setErrorListener(new ErrorListener() {
#Override
public void error(TransformerException exception) {
errors.add(exception);
}
#Override
public void fatalError(TransformerException exception) {
errors.add(exception);
}
#Override
public void warning(TransformerException exception) {
// handle warnings as well if you want them
}
});
// Any other transformer setup
Source xmlSource = ... ;
Result outputTarget = ... ;
try {
transformer.transform(xmlSource, outputTarget);
} catch (TransformerException e) {
errors.add(e); // Just in case one is thrown that isn't handled
}
if (!errors.isEmpty()) {
// Handle errors
} else {
// Handle output since there were no errors
}
This will log all the errors that occur into the errors list, then you can use the messages off those errors to get what you want. This has the added benefit that it will try to resume the transformation after the errors occur. If this causes any problems, just rethrow the exception by doing:
#Override
public void error(TransformerException exception) throws TransformationException {
errors.add(exception);
throw exception;
}
#Override
public void fatalError(TransformerException exception) throws TransformationException {
errors.add(exception);
throw exception;
}
Firstly, it's likely that any solution will dependent on your choice of XSLT processor. Different implementations of the JAXP interface might well provide different information in the exceptions they generate.
It's possible that the error from the XML parser is available in a wrapped exception. For historic reasons, TransformerConfigurationException offers both getException() and getCause() to access wrapped exceptions, and it may be worth checking them both.
Alternatively it's possible that the information was supplied in a separate call to the ErrorListener.
Finally, this particular error is detected by the XML parser (not the XSLT processor) so in the first instance it will be handled by the parser. It may well be worth setting the parser's ErrorHandler and catching parsing errors at that level. If you want explicit control over the XML parser used by the transformation, use a SAXSource whose XMLReader is suitably initialized.
You can configure System.out to write in your own OutputStream.
Use of ErrorListener don't catch all output.
If you work with threads you can look here (http://maiaco.com/articles/java/threadOut.php) to avoid change of System.out for other threads.
example
public final class XslUtilities {
private XslUtilities() {
// only static methods
}
public static class ConvertWithXslException extends Exception {
public ConvertWithXslException(String message, Throwable cause) {
super(message, cause);
}
}
public static String convertWithXsl(String input, String xsl) throws ConvertWithXslException {
ByteArrayOutputStream systemOutByteArrayOutputStream = new ByteArrayOutputStream();
PrintStream oldSystemOutPrintStream = System.out;
System.setOut(new PrintStream(systemOutByteArrayOutputStream));
ByteArrayOutputStream systemErrByteArrayOutputStream = new ByteArrayOutputStream();
PrintStream oldSystemErrPrintStream = System.err;
System.setErr(new PrintStream(systemErrByteArrayOutputStream));
String resultXml;
try {
System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer(new StreamSource(new StringReader(xsl)));
StringWriter stringWriter = new StringWriter();
transformer.transform(new StreamSource(new StringReader(input)), new StreamResult(stringWriter));
resultXml = stringWriter.toString();
} catch (TransformerException e) {
System.out.flush();
final String systemOut = systemOutByteArrayOutputStream.toString();
System.err.flush();
final String systemErr = systemErrByteArrayOutputStream.toString();
throw new ConvertWithXslException("TransformerException - " + e.getMessageAndLocation()
+ (systemOut.length() > 0 ? ("\nSystem.out:" + systemOut) : "")
+ (systemErr.length() > 0 ? ("\nSystem.err:" + systemErr) : ""), e);
} finally {
System.setOut(oldSystemOutPrintStream);
System.setErr(oldSystemErrPrintStream);
}
return resultXml;
}
}
I have a Java program (running in JDK 1.5 for now) which is getting a strange exception while processing an XSLT stylesheet using Xalan. I'm not looking for how to fix the exception: there's plenty of information online about that. I just want to know how to capture the exception in my code:
try {
TransformerFactory tf = TransformerFactory.newInstance();
Source src = new SAXSource(new InputSource(new FileInputStream("doc.xsl")));
Transformer t = tf.newTransformer(src);
System.out.println(t);
} catch (TransformerConfigurationException e) {
System.out.println("the exception was " + e + " and its cause is " + e.getCause());
}
and the output:
com.sun.org.apache.bcel.internal.generic.ClassGenException: Branch target offset too large for short
at com.sun.org.apache.bcel.internal.generic.BranchInstruction.dump(BranchInstruction.java:99)
at com.sun.org.apache.bcel.internal.generic.InstructionList.getByteCode(InstructionList.java:980)
at com.sun.org.apache.bcel.internal.generic.MethodGen.getMethod(MethodGen.java:616)
at com.sun.org.apache.xalan.internal.xsltc.compiler.Mode.compileNamedTemplate(Mode.java:556)
at com.sun.org.apache.xalan.internal.xsltc.compiler.Mode.compileTemplates(Mode.java:566)
at com.sun.org.apache.xalan.internal.xsltc.compiler.Mode.compileApplyTemplates(Mode.java:818)
at com.sun.org.apache.xalan.internal.xsltc.compiler.Stylesheet.compileModes(Stylesheet.java:615)
at com.sun.org.apache.xalan.internal.xsltc.compiler.Stylesheet.translate(Stylesheet.java:730)
at com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC.compile(XSLTC.java:354)
at com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC.compile(XSLTC.java:429)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTemplates(TransformerFactoryImpl.java:792)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:614)
at main.Main.main(Main.java:61)
ERROR: 'Branch target offset too large for short'
FATAL ERROR: 'Could not compile stylesheet'
the exception was javax.xml.transform.TransformerConfigurationException: Could not compile stylesheet and its cause is null
What I want to do is capture the inner exception - the ClassGenException - inside my code. Simply having it printed to STDERR as above is not useful in my application. Is there a way of doing that?
Did you try to set an ErrorListener on your TransformerFactory?
tf.setErrorListener(new ErrorListener() {
#Override
public void warning(TransformerException exception) throws TransformerException {
...
}
#Override
public void fatalError(TransformerException exception) throws TransformerException {
...
}
#Override
public void error(TransformerException exception) throws TransformerException {
...
}
});
Your ClassGenException might be available via exception.getCause().
I'm copying code from one part of our application (an applet) to inside the app. I'm parsing XML as a String. It's been awhile since I parsed XML, but from the error that's thrown it looks like it might have to do with not finding the .dtd. The stack trace makes it difficult to find the exact cause of the error, but here's the message:
java.net.MalformedURLException: no protocol: http://www.mycomp.com/MyComp.dtd
and the XML has this as the first couple lines:
<?xml version='1.0'?>
<!DOCTYPE MYTHING SYSTEM 'http://www.mycomp.com/MyComp.dtd'>
and here's the relevant code snippets
class XMLImportParser extends DefaultHandler {
private SAXParser m_SaxParser = null;
private String is_InputString = "";
XMLImportParser(String xmlStr) throws SAXException, IOException {
super();
is_InputString = xmlStr;
createParser();
try {
preparseString();
parseString(is_InputString);
} catch (Exception e) {
throw new SAXException(e); //"Import Error : "+e.getMessage());
}
}
void createParser() throws SAXException {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
try {
factory.setFeature("http://xml.org/sax/features/namespaces", true);
factory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
m_SaxParser = factory.newSAXParser();
m_SaxParser.getXMLReader().setFeature("http://xml.org/sax/features/namespaces", true);
m_SaxParser.getXMLReader().setFeature("http://xml.org/sax/features/namespace-prefixes", true);
} catch (SAXNotRecognizedException snre){
throw new SAXException("Failed to create XML parser");
} catch (SAXNotSupportedException snse) {
throw new SAXException("Failed to create XML parser");
} catch (Exception ex) {
throw new SAXException(ex);
}
}
void preparseString() throws SAXException {
try {
InputSource lSource = new InputSource(new StringReader(is_InputString));
lSource.setEncoding("UTF-8");
m_SaxParser.parse(lSource, this);
} catch (Exception ex) {
throw new SAXException(ex);
}
}
}
It looks like the error is happening in the preparseString() method, on the line that actually does the parsing, the m_SaxParser.parse(lSource, this); line.
FYI, the 'MyComp.dtd' file does exist at that location and is accessible via http. The XML file comes from a different service on the server, so I can't change it to a file:// format and put the .dtd file on the classpath.
I think you have some extra code in the XML declaration. Try this:
<?xml version='1.0'?>
<!DOCTYPE MYTHING SYSTEM "http://www.mycomp.com/MyComp.dtd">
The above was captured from the W3C Recommendations: http://www.w3.org/QA/2002/04/valid-dtd-list.html
You can use the http link to set the Schema on the SAXParserFactory before creating your parser.
void createParser() throws SAXException {
Schema schema = SchemaFactory.newSchema(new URL("http://www.mycomp.com/MyComp.dtd"));
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
factory.setSchema(schema);
The problem is that this:
http://www.mycomp.com/MyComp.dtd
is an HTML hyperlink, not a URL. Replace it with this:
http://www.mycomp.com/MyComp.dtd
Since this XML comes from an external source, the first thing to do would be to complain to them that they are sending invalid XML.
As a workaround, you can set an EntityResolver on your parser that compares the SystemId to this invalid url and returns a correct http url:
m_SaxParser.getXMLReader().setEntityResolver(
new EntityResolver() {
public InputSource resolveEntity(final String publicId, final String systemId) throws SAXException {
if ("http://www.mycomp.com/MyComp.dtd".equals(systemId)) {
return new InputSource("http://www.mycomp.com/MyComp.dtd");
} else {
return null;
}
}
}
);