Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CAMEL-21420: XML and YAML DSL should better support languages with namespaces such as xpath when parsing and writing outputs #16219

Merged
merged 5 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.camel.model.language;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -80,6 +81,10 @@ public void setNamespaces(Map<String, String> namespaces) {
}

public List<PropertyDefinition> getNamespace() {
if (namespace == null && namespaces != null && !namespaces.isEmpty()) {
namespace = new ArrayList<>();
namespaces.forEach((k, v) -> namespace.add(new PropertyDefinition(k, v)));
}
return namespace;
}

Expand Down
11 changes: 11 additions & 0 deletions core/camel-xml-io/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-assertj3</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io-version}</version>
<scope>test</scope>
</dependency>
<!-- logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@

public class BaseParser {

public static final String DEFAULT_NAMESPACE = "http://camel.apache.org/schema/spring";

protected final MXParser parser;
protected String namespace;
protected final Set<String> secondaryNamespaces = new HashSet<>();
Expand Down Expand Up @@ -80,14 +82,14 @@ public BaseParser(InputStream input, String namespace) throws IOException, XmlPu
this.parser = new MXParser();
this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
this.parser.setInput(input, null);
this.namespace = namespace != null ? namespace : "";
this.namespace = namespace != null && !namespace.isEmpty() ? namespace : DEFAULT_NAMESPACE;
}

public BaseParser(Reader reader, String namespace) throws IOException, XmlPullParserException {
this.parser = new MXParser();
this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
this.parser.setInput(reader);
this.namespace = namespace != null ? namespace : "";
this.namespace = namespace != null && !namespace.isEmpty() ? namespace : DEFAULT_NAMESPACE;
}

public void addSecondaryNamespace(String namespace) {
Expand Down Expand Up @@ -528,9 +530,17 @@ protected static boolean matchNamespace(String ns, String namespace, Set<String>
if (Objects.equals(ns, namespace)) {
return true;
}
for (String second : secondaryNamespaces) {
if (Objects.equals(ns, second)) {
return true;
if (DEFAULT_NAMESPACE.equals(ns) && namespace.isEmpty()) {
return true;
}
if (DEFAULT_NAMESPACE.equals(namespace) && ns.isEmpty()) {
return true;
}
if (secondaryNamespaces != null) {
for (String second : secondaryNamespaces) {
if (Objects.equals(ns, second)) {
return true;
}
}
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,19 @@

public class BaseWriter {

public static final String DEFAULT_NAMESPACE = "http://camel.apache.org/schema/spring";

protected final XMLWriter writer;
protected final Deque<String> namespacesStack = new LinkedList<>();
protected boolean namespaceWritten;
protected boolean skipCustomId = true;

public BaseWriter(Writer writer, String namespace) throws IOException {
this.writer = new XMLWriter(writer);
if (namespace != null) {
if (namespace != null && !namespace.isEmpty()) {
this.namespacesStack.push(namespace);
} else {
this.namespacesStack.push(DEFAULT_NAMESPACE);
}
}

Expand Down Expand Up @@ -96,6 +101,9 @@ protected void value(String value) throws IOException {
}

protected void attribute(String name, Object value) throws IOException {
if (skipCustomId && "customId".equals(name)) {
return;
}
if (value != null) {
writer.addAttribute(name, value.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package org.apache.camel.xml.out;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
Expand Down Expand Up @@ -44,13 +46,19 @@
import org.apache.camel.model.rest.RestsDefinition;
import org.apache.camel.util.StringHelper;
import org.apache.camel.xml.in.ModelParser;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.xmlunit.assertj3.XmlAssert;
import org.xmlunit.diff.DefaultNodeMatcher;
import org.xmlunit.diff.ElementSelectors;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.fail;

Expand All @@ -73,6 +81,42 @@ void testRoutes(String xml, String ns) throws Exception {
}
}

@ParameterizedTest
@MethodSource("routes")
@DisplayName("Test xml roundtrip for <routes>, then compare generated XML")
void testRoutesWithDiff(String xml, String ns) throws Exception {
String original;
try (InputStream is = getClass().getClassLoader().getResourceAsStream(xml);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
assertNotNull(is);
IOUtils.copy(is, baos);
original = baos.toString();
}

assertThat(original).isNotBlank();
RoutesDefinition expected
= new ModelParser(new ByteArrayInputStream(original.getBytes()), NAMESPACE).parseRoutesDefinition().get();
StringWriter sw = new StringWriter();
new ModelWriter(sw, ns).writeRoutesDefinition(expected);
String generatedXml = sw.toString();
assertThat(generatedXml).isNotBlank();

XmlAssert.assertThat(generatedXml)
.and(original)
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.withNodeFilter(node -> {
// skip comparing namespace as original have namespaces scattered in other places than inside <xpath>
if ("namespace".equals(node.getLocalName())) {
return false;
}
return true;
})
.ignoreWhitespace()
.ignoreElementContentWhitespace()
.ignoreComments()
.areSimilar();
}

@ParameterizedTest
@MethodSource("rests")
@DisplayName("Test xml roundtrip for <rests>")
Expand All @@ -86,6 +130,44 @@ void testRests(String xml, String ns) throws Exception {
}
}

@ParameterizedTest
@MethodSource("rests")
@DisplayName("Test xml roundtrip for <rests>, then compare generated XML")
void testRestsWithDiff(String xml, String ns) throws Exception {
String original;
try (InputStream is = getClass().getClassLoader().getResourceAsStream(xml);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
assertNotNull(is);
IOUtils.copy(is, baos);
original = baos.toString();
}

RestsDefinition expected
= new ModelParser(new ByteArrayInputStream(original.getBytes()), NAMESPACE).parseRestsDefinition().get();
StringWriter sw = new StringWriter();
new ModelWriter(sw, ns).writeRestsDefinition(expected);
String generatedXml = sw.toString();
assertThat(generatedXml).isNotBlank();

XmlAssert.assertThat(generatedXml)
.and(original)
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.withAttributeFilter(attr -> {
// skip default values for rest-dsl header params
if ("header".equals(attr.getOwnerElement().getTagName()) && "arrayType".equals(attr.getName())) {
return false;
}
if ("header".equals(attr.getOwnerElement().getTagName()) && "collectionFormat".equals(attr.getName())) {
return false;
}
return true;
})
.ignoreWhitespace()
.ignoreElementContentWhitespace()
.ignoreComments()
.areSimilar();
}

@ParameterizedTest
@MethodSource("routeTemplates")
@DisplayName("Test xml roundtrip for <routeTemplates>")
Expand All @@ -100,6 +182,34 @@ void testRouteTemplates(String xml, String ns) throws Exception {
}
}

@ParameterizedTest
@MethodSource("routeTemplates")
@DisplayName("Test xml roundtrip for <routeTemplates> then compare generated XML")
void testRouteTemplatesWithDiff(String xml, String ns) throws Exception {
String original;
try (InputStream is = getClass().getClassLoader().getResourceAsStream(xml);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
assertNotNull(is);
IOUtils.copy(is, baos);
original = baos.toString();
}

RouteTemplatesDefinition expected = new ModelParser(new ByteArrayInputStream(original.getBytes()), NAMESPACE)
.parseRouteTemplatesDefinition().get();
StringWriter sw = new StringWriter();
new ModelWriter(sw, ns).writeRouteTemplatesDefinition(expected);
String generatedXml = sw.toString();
assertThat(generatedXml).isNotBlank();

XmlAssert.assertThat(generatedXml)
.and(original)
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.ignoreWhitespace()
.ignoreElementContentWhitespace()
.ignoreComments()
.areSimilar();
}

@ParameterizedTest
@MethodSource("templatedRoutes")
@DisplayName("Test xml roundtrip for <templatedRoutes>")
Expand All @@ -114,6 +224,33 @@ void testTemplatedRoutes(String xml, String ns) throws Exception {
}
}

@ParameterizedTest
@MethodSource("templatedRoutes")
@DisplayName("Test xml roundtrip for <templatedRoutes> then compare generated XML")
void testTemplatedRoutesWithDiff(String xml, String ns) throws Exception {
String original;
try (InputStream is = getClass().getClassLoader().getResourceAsStream(xml);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
assertNotNull(is);
IOUtils.copy(is, baos);
original = baos.toString();
}
TemplatedRoutesDefinition expected = new ModelParser(new ByteArrayInputStream(original.getBytes()), NAMESPACE)
.parseTemplatedRoutesDefinition().get();
StringWriter sw = new StringWriter();
new ModelWriter(sw, ns).writeTemplatedRoutesDefinition(expected);
String generatedXml = sw.toString();
assertThat(generatedXml).isNotBlank();

XmlAssert.assertThat(generatedXml)
.and(original)
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.ignoreWhitespace()
.ignoreElementContentWhitespace()
.ignoreComments()
.areSimilar();
}

@ParameterizedTest
@MethodSource("beans")
@DisplayName("Test xml roundtrip for <beans>")
Expand All @@ -128,6 +265,40 @@ void testBeans(String xml, String ns) throws Exception {
}
}

@ParameterizedTest
@MethodSource("beans")
@DisplayName("Test xml roundtrip for <beans> then compare generated XML")
void testBeansWithDiff(String xml, String ns) throws Exception {
String original;
try (InputStream is = getClass().getClassLoader().getResourceAsStream(xml);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
assertNotNull(is);
IOUtils.copy(is, baos);
original = baos.toString();
}
BeansDefinition expected
= new ModelParser(new ByteArrayInputStream(original.getBytes()), NAMESPACE).parseBeansDefinition().get();
StringWriter sw = new StringWriter();
new ModelWriter(sw, ns).writeBeansDefinition(expected);
String generatedXml = sw.toString();
assertThat(generatedXml).isNotBlank();

XmlAssert.assertThat(generatedXml)
.and(original)
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.withAttributeFilter(attr -> {
// bean constructor index is optional
if ("constructor".equals(attr.getOwnerElement().getTagName()) && "index".equals(attr.getName())) {
return false;
}
return true;
})
.ignoreWhitespace()
.ignoreElementContentWhitespace()
.ignoreComments()
.areSimilar();
}

private static Stream<Arguments> routes() {
return definitions("routes");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ xsi:schemaLocation="http://camel.apache.org/schema/spring file:/data/sources/git

<bean name="b1" type="org.apache.camel.xml.in.ModelParserTest.MyBean" factoryMethod="createMyBean">
<constructors>
<!-- index is optional -->
<constructor value="c1" />
<constructor value="c2" />
</constructors>
Expand Down
2 changes: 1 addition & 1 deletion core/camel-xml-io/src/test/resources/resequencerStream.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<route>
<from uri="direct:start" />
<resequence>
<streamConfig timeout="2000"/> <!-- Use default capacity -->
<streamConfig capacity="1000" timeout="2000"/>
<simple>in.header.seqnum</simple>
<to uri="mock:result" />
</resequence>
Expand Down
Loading