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

zhfeng 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 2b4f6b275bf CAMEL-21216: Apache Camel LRA does not work with Oracle 
MicroTX LRA coordinator (#15558)
2b4f6b275bf is described below

commit 2b4f6b275bf924ed4a881f9b3233e9175f015f46
Author: DFiedler <[email protected]>
AuthorDate: Mon Sep 23 15:26:35 2024 +0200

    CAMEL-21216: Apache Camel LRA does not work with Oracle MicroTX LRA 
coordinator (#15558)
    
    * CAMEL-21216: the call from the lra-coordinator is not correctly 
url-decoded. As long as 'CAMEL-21197' is not solved, this workaround is needed.
    
    * CAMEL-21216: The encoding of the query parameter is not needed, because 
the generated url is part of the http put body.
    
    * adding toNonnullString check
    
    * implement own query parse method
    
    * format file
    
    * sorting imports
    
    ---------
    
    Co-authored-by: Dirk Fiedler <[email protected]>
---
 .../apache/camel/service/lra/LRASagaRoutes.java    | 86 +++++++++++++------
 .../apache/camel/service/lra/LRAUrlBuilder.java    | 19 ++---
 .../camel/service/lra/LRASagaRoutesTest.java       | 99 ++++++++++++++++++++++
 3 files changed, 162 insertions(+), 42 deletions(-)

diff --git 
a/components/camel-lra/src/main/java/org/apache/camel/service/lra/LRASagaRoutes.java
 
b/components/camel-lra/src/main/java/org/apache/camel/service/lra/LRASagaRoutes.java
index 6d3b269872b..4d442cff869 100644
--- 
a/components/camel-lra/src/main/java/org/apache/camel/service/lra/LRASagaRoutes.java
+++ 
b/components/camel-lra/src/main/java/org/apache/camel/service/lra/LRASagaRoutes.java
@@ -16,24 +16,23 @@
  */
 package org.apache.camel.service.lra;
 
-import java.net.URISyntaxException;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.stream.Collectors;
 
 import org.apache.camel.Exchange;
-import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.util.URISupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import static 
org.apache.camel.service.lra.LRAConstants.PARTICIPANT_PATH_COMPENSATE;
-import static 
org.apache.camel.service.lra.LRAConstants.PARTICIPANT_PATH_COMPLETE;
-import static org.apache.camel.service.lra.LRAConstants.URL_COMPENSATION_KEY;
-import static org.apache.camel.service.lra.LRAConstants.URL_COMPLETION_KEY;
+import static org.apache.camel.service.lra.LRAConstants.*;
 
 public class LRASagaRoutes extends RouteBuilder {
 
-    private LRASagaService sagaService;
+    private static final Logger LOG = 
LoggerFactory.getLogger(LRASagaRoutes.class);
+
+    private final LRASagaService sagaService;
 
     public LRASagaRoutes(LRASagaService sagaService) {
         this.sagaService = sagaService;
@@ -61,6 +60,39 @@ public class LRASagaRoutes extends RouteBuilder {
                 .end();
     }
 
+    private Map<String, String> parseQuery(String queryStr) {
+
+        Map<String, String> result;
+
+        if (queryStr != null && !queryStr.isEmpty()) {
+
+            // first, split by parameter separator '&'
+            // then collect the map with the variable name '[0]' and value 
'[1]', both url decoded
+            result = Arrays.stream(queryStr.split("&")).collect(
+                    Collectors.toMap(element -> 
decode(saveArrayAccess(element.split("="), 0)),
+                            element -> 
decode(saveArrayAccess(element.split("="), 1))));
+
+        } else {
+            LOG.debug("query param is empty, nothing to parse.");
+            result = new HashMap<>();
+        }
+
+        return result;
+    }
+
+    private String saveArrayAccess(String[] keyValuePair, int index) {
+        try {
+            return keyValuePair[index];
+        } catch (Exception ex) {
+            LOG.warn("unable to read array index '{}' from '{}'", index, 
keyValuePair, ex);
+            return "";
+        }
+    }
+
+    private String decode(String encodedString) {
+        return URLDecoder.decode(encodedString, StandardCharsets.UTF_8);
+    }
+
     /**
      * Check if the request is pointing to an allowed URI to prevent 
unauthorized remote uri invocation
      */
@@ -81,24 +113,22 @@ public class LRASagaRoutes extends RouteBuilder {
 
         // CAMEL-17751: Extract URIs from the CamelHttpQuery header
         if (usedURIs.isEmpty()) {
-            try {
-                Map<String, Object> queryParams
-                        = 
URISupport.parseQuery(exchange.getIn().getHeader(Exchange.HTTP_QUERY, 
String.class));
-                if (!queryParams.isEmpty()) {
-                    if (queryParams.get(URL_COMPENSATION_KEY) != null) {
-                        compensationURI = 
queryParams.get(URL_COMPENSATION_KEY).toString();
-                        usedURIs.add(compensationURI);
-                        exchange.getIn().setHeader(URL_COMPENSATION_KEY, 
compensationURI);
-                    }
-
-                    if (queryParams.get(URL_COMPLETION_KEY) != null) {
-                        completionURI = 
queryParams.get(URL_COMPLETION_KEY).toString();
-                        usedURIs.add(completionURI);
-                        exchange.getIn().setHeader(URL_COMPLETION_KEY, 
completionURI);
-                    }
+            Map<String, String> queryParams
+                    = 
parseQuery(exchange.getIn().getHeader(Exchange.HTTP_QUERY, String.class));
+
+            if (!queryParams.isEmpty()) {
+
+                if (queryParams.get(URL_COMPENSATION_KEY) != null) {
+                    compensationURI = queryParams.get(URL_COMPENSATION_KEY);
+                    usedURIs.add(compensationURI);
+                    exchange.getIn().setHeader(URL_COMPENSATION_KEY, 
compensationURI);
+                }
+
+                if (queryParams.get(URL_COMPLETION_KEY) != null) {
+                    completionURI = queryParams.get(URL_COMPLETION_KEY);
+                    usedURIs.add(completionURI);
+                    exchange.getIn().setHeader(URL_COMPLETION_KEY, 
completionURI);
                 }
-            } catch (URISyntaxException ex) {
-                throw new RuntimeCamelException("URISyntaxException during " + 
Exchange.HTTP_QUERY + " header parsing");
             }
         }
 
diff --git 
a/components/camel-lra/src/main/java/org/apache/camel/service/lra/LRAUrlBuilder.java
 
b/components/camel-lra/src/main/java/org/apache/camel/service/lra/LRAUrlBuilder.java
index 4c247877f90..e3f4bf80c9c 100644
--- 
a/components/camel-lra/src/main/java/org/apache/camel/service/lra/LRAUrlBuilder.java
+++ 
b/components/camel-lra/src/main/java/org/apache/camel/service/lra/LRAUrlBuilder.java
@@ -16,9 +16,6 @@
  */
 package org.apache.camel.service.lra;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
 import java.util.Map;
 import java.util.Optional;
 
@@ -91,18 +88,12 @@ public class LRAUrlBuilder {
 
     public LRAUrlBuilder query(String key, Object value) {
         LRAUrlBuilder copy = copy();
-        try {
-            key = URLEncoder.encode(toNonnullString(key), 
StandardCharsets.UTF_8.name());
-            value = URLEncoder.encode(toNonnullString(value), 
StandardCharsets.UTF_8.name());
-            if (copy.query.isEmpty()) {
-                copy.query += "?";
-            } else {
-                copy.query += "&";
-            }
-            copy.query += key + "=" + value;
-        } catch (UnsupportedEncodingException e) {
-            throw new IllegalStateException(e);
+        if (copy.query.isEmpty()) {
+            copy.query += "?";
+        } else {
+            copy.query += "&";
         }
+        copy.query += toNonnullString(key) + "=" + toNonnullString(value);
         return copy;
     }
 
diff --git 
a/components/camel-lra/src/test/java/org/apache/camel/service/lra/LRASagaRoutesTest.java
 
b/components/camel-lra/src/test/java/org/apache/camel/service/lra/LRASagaRoutesTest.java
new file mode 100644
index 00000000000..ae9ecfd7416
--- /dev/null
+++ 
b/components/camel-lra/src/test/java/org/apache/camel/service/lra/LRASagaRoutesTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+package org.apache.camel.service.lra;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+public class LRASagaRoutesTest {
+
+    private Method getParseQueryMethod() throws NoSuchMethodException {
+        Method method = LRASagaRoutes.class.getDeclaredMethod("parseQuery", 
String.class);
+        method.setAccessible(true);
+        return method;
+    }
+
+    @DisplayName("Tests whether parseQuery() is splitting unencoded query 
params correct.")
+    @Test
+    void testParseQuerySuccessUnencoded() throws InvocationTargetException, 
IllegalAccessException, NoSuchMethodException {
+
+        Map<String, String> testResult = (Map<String, String>) 
getParseQueryMethod()
+                .invoke(new LRASagaRoutes(null),
+                        
"Camel-Saga-Compensate=direct://saga1_participant1_compensate&Camel-Saga-Complete=direct://saga1_participant1_complete");
+
+        System.out.println(testResult.toString());
+
+        Assertions.assertNotNull(testResult, "pared query must not be null");
+        Assertions.assertEquals(2, testResult.size(), "query parameter count 
must be two");
+        Assertions.assertNotNull(testResult.get("Camel-Saga-Compensate"),
+                "query parameter value for name 'Camel-Saga-Compensate' must 
not be null");
+        Assertions.assertEquals("direct://saga1_participant1_compensate", 
testResult.get("Camel-Saga-Compensate"),
+                "query parameter value for name 'Camel-Saga-Compensate' has 
unexpected content");
+        Assertions.assertNotNull(testResult.get("Camel-Saga-Complete"),
+                "query parameter value for name 'Camel-Saga-Complete' must not 
be null");
+        Assertions.assertEquals("direct://saga1_participant1_complete", 
testResult.get("Camel-Saga-Complete"),
+                "query parameter value for name 'Camel-Saga-Complete' has 
unexpected content");
+    }
+
+    @DisplayName("Tests parseQuery() to handle incorrect query string (no 
value is given)")
+    @Test
+    void testParseQueryInvalidUnencoded() throws InvocationTargetException, 
IllegalAccessException, NoSuchMethodException {
+
+        Map<String, String> testResult = (Map<String, String>) 
getParseQueryMethod()
+                .invoke(new LRASagaRoutes(null), "key1=value1&key2");
+
+        System.out.println(testResult.toString());
+
+        Assertions.assertNotNull(testResult, "pared query must not be null");
+        Assertions.assertEquals(2, testResult.size(), "query parameter count 
must be two");
+        Assertions.assertNotNull(testResult.get("key1"),
+                "query parameter value for name 'key1' must not be null");
+        Assertions.assertEquals("value1", testResult.get("key1"),
+                "query parameter value for name 'key1' has unexpected 
content");
+        Assertions.assertNotNull(testResult.get("key2"),
+                "query parameter value for name 'key2' must not be null");
+        Assertions.assertEquals("", testResult.get("key2"),
+                "query parameter value for name 'key2' has unexpected 
content");
+    }
+
+    @DisplayName("Tests parseQuery() to handle incorrect query string (no 
value is given)")
+    @Test
+    void testParseQuerySuccessEncoded() throws InvocationTargetException, 
IllegalAccessException, NoSuchMethodException {
+
+        Map<String, String> testResult = (Map<String, String>) 
getParseQueryMethod()
+                .invoke(new LRASagaRoutes(null),
+                        
"Camel-Saga-Compensate=direct%3A%2F%2Fsaga1_participant1_compensate&Camel-Saga-Complete=direct%3A%2F%2Fsaga1_participant1_complete");
+
+        System.out.println(testResult.toString());
+
+        Assertions.assertNotNull(testResult, "pared query must not be null");
+        Assertions.assertEquals(2, testResult.size(), "query parameter count 
must be two");
+        Assertions.assertNotNull(testResult.get("Camel-Saga-Compensate"),
+                "query parameter value for name 'Camel-Saga-Compensate' must 
not be null");
+        Assertions.assertEquals("direct://saga1_participant1_compensate", 
testResult.get("Camel-Saga-Compensate"),
+                "query parameter value for name 'Camel-Saga-Compensate' has 
unexpected content");
+        Assertions.assertNotNull(testResult.get("Camel-Saga-Complete"),
+                "query parameter value for name 'Camel-Saga-Complete' must not 
be null");
+        Assertions.assertEquals("direct://saga1_participant1_complete", 
testResult.get("Camel-Saga-Complete"),
+                "query parameter value for name 'Camel-Saga-Complete' has 
unexpected content");
+    }
+}

Reply via email to