You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/10/02 14:34:40 UTC

[camel] branch main updated: CAMEL-19936: camel-xml-io - Parsing error with line precise error (#11627)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new c74b0a25af9 CAMEL-19936: camel-xml-io - Parsing error with line precise error (#11627)
c74b0a25af9 is described below

commit c74b0a25af97f7d7be03a824c8f594e528a5adb8
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Oct 2 16:34:33 2023 +0200

    CAMEL-19936: camel-xml-io - Parsing error with line precise error (#11627)
---
 .../main/java/org/apache/camel/util/IOHelper.java  | 28 ++++++++
 .../java/org/apache/camel/xml/in/BaseParser.java   | 25 +++++++
 .../xml/io/XmlPullParserLocationException.java     | 77 ++++++++++++++++++++++
 .../org/apache/camel/xml/in/ModelParserTest.java   | 21 ++++++
 .../resources/invalid/convertBodyParseError.xml    | 25 +++++++
 5 files changed, 176 insertions(+)

diff --git a/core/camel-util/src/main/java/org/apache/camel/util/IOHelper.java b/core/camel-util/src/main/java/org/apache/camel/util/IOHelper.java
index e18d2168e46..c5892043305 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/IOHelper.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/IOHelper.java
@@ -475,6 +475,34 @@ public final class IOHelper {
         }
     }
 
+    /**
+     * Loads the entire stream into memory as a String and returns the given line number.
+     * <p/>
+     * Warning, don't use for crazy big streams :)
+     */
+    public static String loadTextLine(InputStream in, int lineNumber) throws IOException {
+        int i = 0;
+        InputStreamReader isr = new InputStreamReader(in);
+        try {
+            BufferedReader reader = buffered(isr);
+            while (true) {
+                String line = reader.readLine();
+                if (line != null) {
+                    i++;
+                    if (i >= lineNumber) {
+                        return line;
+                    }
+                } else {
+                    break;
+                }
+            }
+        } finally {
+            close(isr, in);
+        }
+
+        return null;
+    }
+
     /**
      * Appends the text to the file.
      */
diff --git a/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java b/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
index 1a181110fae..aa168ff15f5 100644
--- a/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
+++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
@@ -49,6 +49,7 @@ import org.apache.camel.util.URISupport;
 import org.apache.camel.xml.io.MXParser;
 import org.apache.camel.xml.io.XmlPullParser;
 import org.apache.camel.xml.io.XmlPullParserException;
+import org.apache.camel.xml.io.XmlPullParserLocationException;
 
 public class BaseParser {
 
@@ -103,6 +104,30 @@ public class BaseParser {
             T definition, AttributeHandler<T> attributeHandler, ElementHandler<T> elementHandler, ValueHandler<T> valueHandler,
             boolean supportsExternalNamespaces)
             throws IOException, XmlPullParserException {
+
+        try {
+            return doParseXml(definition, attributeHandler, elementHandler, valueHandler, supportsExternalNamespaces);
+        } catch (Exception e) {
+            if (e instanceof XmlPullParserLocationException) {
+                throw e;
+            }
+            // wrap in XmlPullParserLocationException so we have line-precise error
+            String msg = e.getMessage();
+            Throwable cause = e;
+            if (e instanceof XmlPullParserException) {
+                if (e.getCause() != null) {
+                    cause = e.getCause();
+                    msg = e.getCause().getMessage();
+                }
+            }
+            throw new XmlPullParserLocationException(msg, resource, parser.getLineNumber(), parser.getColumnNumber(), cause);
+        }
+    }
+
+    protected <T> T doParseXml(
+            T definition, AttributeHandler<T> attributeHandler, ElementHandler<T> elementHandler, ValueHandler<T> valueHandler,
+            boolean supportsExternalNamespaces)
+            throws IOException, XmlPullParserException {
         if (definition instanceof LineNumberAware) {
             // we want to get the line number where the tag starts (in case its multi-line)
             int line = parser.getStartLineNumber();
diff --git a/core/camel-xml-io/src/main/java/org/apache/camel/xml/io/XmlPullParserLocationException.java b/core/camel-xml-io/src/main/java/org/apache/camel/xml/io/XmlPullParserLocationException.java
new file mode 100644
index 00000000000..ca7e21f9b35
--- /dev/null
+++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/io/XmlPullParserLocationException.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* -*-             c-basic-offset: 4; indent-tabs-mode: nil; -*-  //------100-columns-wide------>|*/
+// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)
+package org.apache.camel.xml.io;
+
+import org.apache.camel.spi.Resource;
+import org.apache.camel.util.IOHelper;
+
+/**
+ * This exception is thrown to signal XML Pull Parser related faults.
+ */
+public class XmlPullParserLocationException extends RuntimeException {
+    private final int row;
+    private final int column;
+    private final Resource resource;
+
+    public XmlPullParserLocationException(String s, Resource resource, int row, int column, Throwable cause) {
+        super(createMessage(s, resource, row, column, cause), cause);
+        this.row = row;
+        this.column = column;
+        this.resource = resource;
+    }
+
+    public int getLineNumber() {
+        return row;
+    }
+
+    public int getColumnNumber() {
+        return column;
+    }
+
+    public Resource getResource() {
+        return resource;
+    }
+
+    private static String createMessage(String s, Resource resource, int row, int column, Throwable cause) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(cause.getMessage()).append("\n");
+        if (resource != null) {
+            sb.append("in ").append(resource.getLocation()).append(", line ")
+                    .append(row).append(", column ").append(column)
+                    .append(":\n");
+            try {
+                String line = IOHelper.loadTextLine(resource.getInputStream(), row);
+                if (line != null) {
+                    sb.append(line).append("\n");
+                    if (column > 1) {
+                        String pad = " ".repeat(column - 2);
+                        sb.append(pad);
+                    }
+                    sb.append("^\n");
+                }
+            } catch (Exception e) {
+                // ignore
+            }
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/core/camel-xml-io/src/test/java/org/apache/camel/xml/in/ModelParserTest.java b/core/camel-xml-io/src/test/java/org/apache/camel/xml/in/ModelParserTest.java
index d5c68160ae9..51e8864348f 100644
--- a/core/camel-xml-io/src/test/java/org/apache/camel/xml/in/ModelParserTest.java
+++ b/core/camel-xml-io/src/test/java/org/apache/camel/xml/in/ModelParserTest.java
@@ -50,6 +50,9 @@ import org.apache.camel.model.rest.ParamDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.rest.RestsDefinition;
 import org.apache.camel.model.rest.VerbDefinition;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.xml.io.XmlPullParserLocationException;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -60,6 +63,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 public class ModelParserTest {
 
@@ -420,6 +424,23 @@ public class ModelParserTest {
         assertEquals("myPolicy", dlc.getRedeliveryPolicyRef());
     }
 
+    @Test
+    public void testParseError() throws Exception {
+        Path dir = getResourceFolder();
+        Path path = new File(dir.toFile() + "/invalid", "convertBodyParseError.xml").toPath();
+        Resource resource = ResourceHelper.fromString("file:convertBodyParseError.xml", Files.readString(path));
+        try {
+            ModelParser parser = new ModelParser(resource, NAMESPACE);
+            parser.parseRoutesDefinition();
+            fail("Should throw exception");
+        } catch (XmlPullParserLocationException e) {
+            assertEquals(22, e.getLineNumber());
+            assertEquals(25, e.getColumnNumber());
+            assertEquals("file:convertBodyParseError.xml", e.getResource().getLocation());
+            assertTrue(e.getMessage().startsWith("Unexpected attribute '{}ref'"));
+        }
+    }
+
     private Path getResourceFolder() {
         String childFileString = getClass().getClassLoader().getResource("barInterceptorRoute.xml").getFile();
         File parentFile = new File(childFileString).getParentFile();
diff --git a/core/camel-xml-io/src/test/resources/invalid/convertBodyParseError.xml b/core/camel-xml-io/src/test/resources/invalid/convertBodyParseError.xml
new file mode 100644
index 00000000000..bf2c966f6bf
--- /dev/null
+++ b/core/camel-xml-io/src/test/resources/invalid/convertBodyParseError.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<routes id="camel" xmlns="http://camel.apache.org/schema/spring">
+  <route>
+    <from ref="seda:a"/>
+    <convertBodyTo type="java.lang.Integer"/>
+  </route>
+</routes>