Why am I getting "MalformedURLException: no protocol" when using SAXParser? - java

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;
}
}
}
);

Related

Sonarqube critical issue "Define and throw a dedicated exception instead of using a generic one"

Hi I have a below method getting one critical sonar issue(Define and throw a dedicated exception instead of using a generic one)
when I remove "Exception" and mention dedicated exception(IOException, SAXException, ParserConfigurationException). At that time sonar raising a major issue "use generic one".
public Element createDomElement(String xmlRequest) throws Exception {
DocumentBuilder documentBuilder;
Document document = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setNamespaceAware(true);
documentBuilder = dbf.newDocumentBuilder();
if (documentBuilder == null) {
throw new ABCException(null, "unexpected_error", "Error processing request", null,
INTERNAL_SERVER_ERROR);
}
synchronized (this) {
document = documentBuilder.parse(new ByteArrayInputStream(xmlRequest.getBytes(UTF_8)));
}
document.getDocumentElement().normalize();
return document.getDocumentElement();
}
Not sure how I can resolve could you please someone help me with this.
Is it because ABCException is missing from throws? Will you please try below method signature and run the sonarqube?
public Element createDomElement(String xmlRequest) throws ParserConfigurationException, ABCException, SAXException, IOException {}
Edit 1
Approaches to solve this issue
Throw only ABCException from createDomElement() method. Handle rest exceptions in createDomElement() itself. Not sure if its recommended one.
public Element createDomElement(String xmlRequest) throws ABCException {
DocumentBuilder documentBuilder;
Document document = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance();
try {
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (ParserConfigurationException e) {
throw new ABCException(null, "Parser Error", "Error processing request", null,
"Parser Error : " + e.getMessage());
}
//todo remaining part of method
}
Refactor code and divide createDomElement() method to follow Single Responsibility. This approach looks better than first one. Check this answer https://softwareengineering.stackexchange.com/a/264068

How to internationalization SAXParseException while parsing XML file?

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)

XML validation slow on build server

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.

How to extract useful information from TransformerException

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;
}
}

iText - Fail on second attempt to generate PDF

I have a Java desktop application that is using iText to generate PDFs from a resultset. The first time you generate a PDF, it works fine. The problem comes when you try to generate a second one. It throws a DocumentException saying that the document is closed. I have tried to find other examples of people having this problem, and I come up with very little, which leads me to believe that I have made a very simple mistake and I cannot find it.
The code below is a snippet of the event handler that calls the report class:
RptPotReport report = new RptPotReport();
try {
report.rptPot();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
And here is the code for the report class itself. The error occurs on the second run through this code:
public class RptPotReport {
public static void main(String[] args) throws IOException, DocumentException, SQLException {
new RptPotReport().rptPot();
}
String fileOutput = "Potting Report.pdf";
public void rptPot() throws DocumentException, IOException {
File f = new File("Potting Report.pdf");
if (f.exists()) {
f.delete();
}
Document document = new Document();
document = pdfSizes.getPdfLetter();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(fileOutput));
document.open();
Phrase title = new Phrase();
title.add(new Chunk("Potting Report"));
document.add(title); // ******* DocumentException here: "The document has been closed. You can't add any Elements."
document.close();
try {
File pdfFile = new File(fileOutput);
if (pdfFile.exists()) {
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(pdfFile);
} else {
System.out.println("Awt Desktop is not supported!");
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
EDIT: At someone's suggestion, I tried calling the RptPotReport from a second thread, but that did not change anything. Looking into it further, the Document class of iText creates a new thread when it's instantiated. So I'm right back where I started, still stuck.
What does this line do exactly in your application:
document = pdfSizes.getPdfLetter();
Without the code and with your explanation it seems like the line sets the reference of the document variable to the one that you receive from pdfSizes.getPdfLetter(), which is reused between run, thus you no longer have the reference of the new Document() statement.
I tend to think the pdfSizes.getPdfLetter() method is bugged.

Categories

Resources