Repository: camel
Updated Branches:
  refs/heads/master a60df1f94 -> c5ad91ee0


CAMEL-10383: Align explain endpoint options code from camel-core with working 
code from camel catalog which fixes some problems.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/c5ad91ee
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/c5ad91ee
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/c5ad91ee

Branch: refs/heads/master
Commit: c5ad91ee08838cdf174e5f880ace25f81f262159
Parents: a60df1f
Author: Claus Ibsen <davscl...@apache.org>
Authored: Sun Oct 16 12:46:49 2016 +0200
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Sun Oct 16 12:46:49 2016 +0200

----------------------------------------------------------------------
 .../org/apache/camel/util/EndpointHelper.java   | 213 ++++++++++++++++---
 .../org/apache/camel/util/JsonSchemaHelper.java |  48 +++++
 .../java/org/apache/camel/util/URISupport.java  |  18 ++
 .../jms/JmsExplainEndpointOptionsTest.java      |  33 +++
 .../org/apache/camel/catalog/URISupport.java    |   2 +-
 .../apache/camel/catalog/CamelCatalogTest.java  |  15 ++
 6 files changed, 294 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/c5ad91ee/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java 
b/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java
index fb0c91e..ec71ddb 100644
--- a/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java
@@ -20,7 +20,6 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -44,6 +43,12 @@ import org.apache.camel.spi.BrowsableEndpoint;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.camel.util.JsonSchemaHelper.getPropertyDefaultValue;
+import static org.apache.camel.util.JsonSchemaHelper.getPropertyPrefix;
+import static org.apache.camel.util.JsonSchemaHelper.isPropertyMultiValue;
+import static org.apache.camel.util.JsonSchemaHelper.isPropertyRequired;
+import static org.apache.camel.util.ObjectHelper.after;
+
 /**
  * Some helper methods for working with {@link Endpoint} instances
  */
@@ -132,14 +137,14 @@ public final class EndpointHelper {
         if (uri.contains("://")) {
             // try without :// also
             String scheme = ObjectHelper.before(uri, "://");
-            String path = ObjectHelper.after(uri, "://");
+            String path = after(uri, "://");
             if (matchPattern(scheme + ":" + path, pattern)) {
                 return true;
             }
         } else {
             // try with :// also
             String scheme = ObjectHelper.before(uri, ":");
-            String path = ObjectHelper.after(uri, ":");
+            String path = after(uri, ":");
             if (matchPattern(scheme + "://" + path, pattern)) {
                 return true;
             }
@@ -521,12 +526,15 @@ public final class EndpointHelper {
      * @return a map for each option in the uri with the corresponding 
information from the json
      * @throws Exception is thrown in case of error
      */
+    // CHECKSTYLE:OFF
     public static Map<String, Object> endpointProperties(CamelContext 
camelContext, String uri) throws Exception {
-        // NOTICE: This logic is similar to 
org.apache.camel.catalog.DefaultCamelCatalog#endpointProperties
+        // NOTICE: This logic is similar to 
org.apache.camel.util.EndpointHelper#endpointProperties
         // as the catalog also offers similar functionality (without having 
camel-core on classpath)
 
+        // need to normalize uri first
+
         // parse the uri
-        URI u = new URI(uri);
+        URI u = normalizeUri(uri);
         String scheme = u.getScheme();
 
         String json = 
camelContext.getComponentParameterJsonSchema(u.getScheme());
@@ -536,18 +544,80 @@ public final class EndpointHelper {
 
         // grab the syntax
         String syntax = null;
+        String alternativeSyntax = null;
         List<Map<String, String>> rows = 
JsonSchemaHelper.parseJsonSchema("component", json, false);
         for (Map<String, String> row : rows) {
             if (row.containsKey("syntax")) {
                 syntax = row.get("syntax");
-                break;
+            }
+            if (row.containsKey("alternativeSyntax")) {
+                alternativeSyntax = row.get("alternativeSyntax");
             }
         }
         if (syntax == null) {
             throw new IllegalArgumentException("Endpoint with scheme " + 
scheme + " has no syntax defined in the json schema");
         }
 
-        // parse the syntax and find the same group in the uri
+        // only if we support alternative syntax, and the uri contains the 
username and password in the authority
+        // part of the uri, then we would need some special logic to capture 
that information and strip those
+        // details from the uri, so we can continue parsing the uri using the 
normal syntax
+        Map<String, String> userInfoOptions = new LinkedHashMap<String, 
String>();
+        if (alternativeSyntax != null && alternativeSyntax.contains("@")) {
+            // clip the scheme from the syntax
+            alternativeSyntax = after(alternativeSyntax, ":");
+            // trim so only userinfo
+            int idx = alternativeSyntax.indexOf("@");
+            String fields = alternativeSyntax.substring(0, idx);
+            String[] names = fields.split(":");
+
+            // grab authority part and grab username and/or password
+            String authority = u.getAuthority();
+            if (authority != null && authority.contains("@")) {
+                String username = null;
+                String password = null;
+
+                // grab unserinfo part before @
+                String userInfo = authority.substring(0, 
authority.indexOf("@"));
+                String[] parts = userInfo.split(":");
+                if (parts.length == 2) {
+                    username = parts[0];
+                    password = parts[1];
+                } else {
+                    // only username
+                    username = userInfo;
+                }
+
+                // remember the username and/or password which we add later to 
the options
+                if (names.length == 2) {
+                    userInfoOptions.put(names[0], username);
+                    if (password != null) {
+                        // password is optional
+                        userInfoOptions.put(names[1], password);
+                    }
+                }
+            }
+        }
+
+        // clip the scheme from the syntax
+        syntax = after(syntax, ":");
+        // clip the scheme from the uri
+        uri = after(uri, ":");
+        String uriPath = stripQuery(uri);
+
+        // strip user info from uri path
+        if (!userInfoOptions.isEmpty()) {
+            int idx = uriPath.indexOf('@');
+            if (idx > -1) {
+                uriPath = uriPath.substring(idx + 1);
+            }
+        }
+
+        // strip double slash in the start
+        if (uriPath != null && uriPath.startsWith("//")) {
+            uriPath = uriPath.substring(2);
+        }
+
+        // parse the syntax and find the names of each option
         Matcher matcher = SYNTAX_PATTERN.matcher(syntax);
         List<String> word = new ArrayList<String>();
         while (matcher.find()) {
@@ -556,29 +626,51 @@ public final class EndpointHelper {
                 word.add(s);
             }
         }
+        // parse the syntax and find each token between each option
+        String[] tokens = SYNTAX_PATTERN.split(syntax);
 
-        String uriPath = stripQuery(uri);
-
-        // if there is only one, then use uriPath as is
+        // find the position where each option start/end
         List<String> word2 = new ArrayList<String>();
+        int prev = 0;
+        int prevPath = 0;
+
+        // special for activemq/jms where the enum for destinationType causes 
a token issue as it includes a colon
+        // for 'temp:queue' and 'temp:topic' values
+        if ("activemq".equals(scheme) || "jms".equals("scheme")) {
+            if (uriPath.startsWith("temp:")) {
+                prevPath = 5;
+            }
+        }
 
-        if (word.size() == 1) {
-            String s = uriPath;
-            s = URISupport.stripPrefix(s, scheme);
-            // strip any leading : or / after the scheme
-            while (s.startsWith(":") || s.startsWith("/")) {
-                s = s.substring(1);
+        for (String token : tokens) {
+            if (token.isEmpty()) {
+                continue;
             }
-            word2.add(s);
-        } else {
-            Matcher matcher2 = SYNTAX_PATTERN.matcher(uriPath);
-            while (matcher2.find()) {
-                String s = matcher2.group(1);
-                if (!scheme.equals(s)) {
-                    word2.add(s);
-                }
+
+            // special for some tokens where :// can be used also, eg 
http://foo
+            int idx = -1;
+            int len = 0;
+            if (":".equals(token)) {
+                idx = uriPath.indexOf("://", prevPath);
+                len = 3;
+            }
+            if (idx == -1) {
+                idx = uriPath.indexOf(token, prevPath);
+                len = token.length();
+            }
+
+            if (idx > 0) {
+                String option = uriPath.substring(prev, idx);
+                word2.add(option);
+                prev = idx + len;
+                prevPath = prev;
             }
         }
+        // special for last or if we did not add anyone
+        if (prev > 0 || word2.isEmpty()) {
+            String option = uriPath.substring(prev);
+            word2.add(option);
+        }
 
         rows = JsonSchemaHelper.parseJsonSchema("properties", json, true);
 
@@ -587,14 +679,19 @@ public final class EndpointHelper {
         // now parse the uri to know which part isw what
         Map<String, String> options = new LinkedHashMap<String, String>();
 
+        // include the username and password from the userinfo section
+        if (!userInfoOptions.isEmpty()) {
+            options.putAll(userInfoOptions);
+        }
+
         // word contains the syntax path elements
         Iterator<String> it = word2.iterator();
         for (int i = 0; i < word.size(); i++) {
             String key = word.get(i);
 
             boolean allOptions = word.size() == word2.size();
-            boolean required = JsonSchemaHelper.isPropertyRequired(rows, key);
-            String defaultValue = 
JsonSchemaHelper.getPropertyDefaultValue(rows, key);
+            boolean required = isPropertyRequired(rows, key);
+            String defaultValue = getPropertyDefaultValue(rows, key);
 
             // we have all options so no problem
             if (allOptions) {
@@ -603,12 +700,27 @@ public final class EndpointHelper {
             } else {
                 // we have a little problem as we do not not have all options
                 if (!required) {
-                    String value = defaultValue;
-                    options.put(key, value);
-                    defaultValueAdded = true;
+                    String value = null;
+
+                    boolean last = i == word.size() - 1;
+                    if (last) {
+                        // if its the last value then use it instead of the 
default value
+                        value = it.hasNext() ? it.next() : null;
+                        if (value != null) {
+                            options.put(key, value);
+                        } else {
+                            value = defaultValue;
+                        }
+                    }
+                    if (value != null) {
+                        options.put(key, value);
+                        defaultValueAdded = true;
+                    }
                 } else {
-                    String value = it.next();
-                    options.put(key, value);
+                    String value = it.hasNext() ? it.next() : null;
+                    if (value != null) {
+                        options.put(key, value);
+                    }
                 }
             }
         }
@@ -621,8 +733,8 @@ public final class EndpointHelper {
             String value = entry.getValue();
 
             if (defaultValueAdded) {
-                boolean required = JsonSchemaHelper.isPropertyRequired(rows, 
key);
-                String defaultValue = 
JsonSchemaHelper.getPropertyDefaultValue(rows, key);
+                boolean required = isPropertyRequired(rows, key);
+                String defaultValue = getPropertyDefaultValue(rows, key);
 
                 if (!required && defaultValue != null) {
                     if (defaultValue.equals(value)) {
@@ -639,14 +751,47 @@ public final class EndpointHelper {
         Map<String, Object> parameters = URISupport.parseParameters(u);
 
         // and covert the values to String so its JMX friendly
-        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
+        while (!parameters.isEmpty()) {
+            Map.Entry<String, Object> entry = 
parameters.entrySet().iterator().next();
             String key = entry.getKey();
             String value = entry.getValue() != null ? 
entry.getValue().toString() : "";
+
+            boolean multiValued = isPropertyMultiValue(rows, key);
+            if (multiValued) {
+                String prefix = getPropertyPrefix(rows, key);
+                // extra all the multi valued options
+                Map<String, Object> values = 
URISupport.extractProperties(parameters, prefix);
+                // build a string with the extra multi valued options with the 
prefix and & as separator
+                CollectionStringBuffer csb = new CollectionStringBuffer("&");
+                for (Map.Entry<String, Object> multi : values.entrySet()) {
+                    String line = prefix + multi.getKey() + "=" + 
(multi.getValue() != null ? multi.getValue().toString() : "");
+                    csb.append(line);
+                }
+                // append the extra multi-values to the existing (which 
contains the first multi value)
+                if (!csb.isEmpty()) {
+                    value = value + "&" + csb.toString();
+                }
+            }
+
             answer.put(key, value);
+            // remove the parameter as we run in a while loop until no more 
parameters
+            parameters.remove(key);
         }
 
         return answer;
     }
+    // CHECKSTYLE:ON
+
+    /**
+     * Normalizes the URI so unsafe characters is encoded
+     *
+     * @param uri the input uri
+     * @return as URI instance
+     * @throws URISyntaxException is thrown if syntax error in the input uri
+     */
+    private static URI normalizeUri(String uri) throws URISyntaxException {
+        return new URI(UnsafeUriCharactersEncoder.encode(uri, true));
+    }
 
     /**
      * Strips the query parameters from the uri

http://git-wip-us.apache.org/repos/asf/camel/blob/c5ad91ee/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java 
b/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java
index 41dffe3..df7ff01 100644
--- a/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java
@@ -243,4 +243,52 @@ public final class JsonSchemaHelper {
         return null;
     }
 
+    /**
+     * Is the property multi valued
+     *
+     * @param rows the rows of properties
+     * @param name name of the property
+     * @return <tt>true</tt> if multi valued, or <tt>false</tt> if not
+     */
+    public static boolean isPropertyMultiValue(List<Map<String, String>> rows, 
String name) {
+        for (Map<String, String> row : rows) {
+            boolean multiValue = false;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("multiValue")) {
+                multiValue = "true".equals(row.get("multiValue"));
+            }
+            if (found) {
+                return multiValue;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gets the prefix value of the property
+     *
+     * @param rows the rows of properties
+     * @param name name of the property
+     * @return the prefix value or <tt>null</tt> if no prefix value exists
+     */
+    public static String getPropertyPrefix(List<Map<String, String>> rows, 
String name) {
+        for (Map<String, String> row : rows) {
+            String prefix = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("prefix")) {
+                prefix = row.get("prefix");
+            }
+            if (found) {
+                return prefix;
+            }
+        }
+        return null;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/c5ad91ee/camel-core/src/main/java/org/apache/camel/util/URISupport.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/URISupport.java 
b/camel-core/src/main/java/org/apache/camel/util/URISupport.java
index 20dd1c2..192b03b 100644
--- a/camel-core/src/main/java/org/apache/camel/util/URISupport.java
+++ b/camel-core/src/main/java/org/apache/camel/util/URISupport.java
@@ -600,4 +600,22 @@ public final class URISupport {
         // must include :// to do a correct URI all components can work with
         return scheme + "://" + path + (query != null ? "?" + query : "");
     }
+
+    public static Map<String, Object> extractProperties(Map<String, Object> 
properties, String optionPrefix) {
+        Map<String, Object> rc = new LinkedHashMap<String, 
Object>(properties.size());
+
+        for (Iterator<Map.Entry<String, Object>> it = 
properties.entrySet().iterator(); it.hasNext();) {
+            Map.Entry<String, Object> entry = it.next();
+            String name = entry.getKey();
+            if (name.startsWith(optionPrefix)) {
+                Object value = properties.get(name);
+                name = name.substring(optionPrefix.length());
+                rc.put(name, value);
+                it.remove();
+            }
+        }
+
+        return rc;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/c5ad91ee/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsExplainEndpointOptionsTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsExplainEndpointOptionsTest.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsExplainEndpointOptionsTest.java
new file mode 100644
index 0000000..31ee5fb
--- /dev/null
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsExplainEndpointOptionsTest.java
@@ -0,0 +1,33 @@
+/**
+ * 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.component.jms;
+
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class JmsExplainEndpointOptionsTest extends CamelTestSupport {
+
+    @Test
+    public void testExplain() throws Exception {
+        String json = context.explainEndpointJson("jms:browse.me", true);
+        assertNotNull(json);
+
+        assertTrue(json.contains("\"defaultValue\": \"queue\""));
+        assertTrue(json.contains("\"value\": \"browse.me\""));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/c5ad91ee/platforms/catalog/src/main/java/org/apache/camel/catalog/URISupport.java
----------------------------------------------------------------------
diff --git 
a/platforms/catalog/src/main/java/org/apache/camel/catalog/URISupport.java 
b/platforms/catalog/src/main/java/org/apache/camel/catalog/URISupport.java
index f3fe621..8cc0f69 100644
--- a/platforms/catalog/src/main/java/org/apache/camel/catalog/URISupport.java
+++ b/platforms/catalog/src/main/java/org/apache/camel/catalog/URISupport.java
@@ -42,7 +42,7 @@ public final class URISupport {
     }
 
     /**
-     * Normalizes the URI so unsafe charachters is encoded
+     * Normalizes the URI so unsafe characters is encoded
      *
      * @param uri the input uri
      * @return as URI instance

http://git-wip-us.apache.org/repos/asf/camel/blob/c5ad91ee/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
----------------------------------------------------------------------
diff --git 
a/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
 
b/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
index 39fe6e4..271ca3f 100644
--- 
a/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
+++ 
b/platforms/catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
@@ -405,6 +405,21 @@ public class CamelCatalogTest {
     }
 
     @Test
+    public void testEndpointPropertiesJmsWithDotInName() throws Exception {
+        Map<String, String> map = catalog.endpointProperties("jms:browse.me");
+        assertNotNull(map);
+        assertEquals(1, map.size());
+
+        assertEquals("browse.me", map.get("destinationName"));
+
+        map = catalog.endpointProperties("jms:browse.me");
+        assertNotNull(map);
+        assertEquals(1, map.size());
+
+        assertEquals("browse.me", map.get("destinationName"));
+    }
+
+    @Test
     public void testEndpointPropertiesJmsRequired() throws Exception {
         Map<String, String> map = catalog.endpointProperties("jms:foo");
         assertNotNull(map);

Reply via email to