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

mercyblitz pushed a commit to branch 2.7.8-dev
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/2.7.8-dev by this push:
     new bc48b41  2.7.8 release (#6403)
bc48b41 is described below

commit bc48b4145d6b796ad938c3099e5b90b02234809e
Author: Mercy Ma <mercybl...@gmail.com>
AuthorDate: Wed Jul 1 15:45:00 2020 +0800

    2.7.8 release (#6403)
    
    * Polish apache/dubbo#6296 : Adding the new methods into MetadataReport to 
manipulate the exported URLs for service introspection
    
    * Polish apache/dubbo#6296 : Adding the new methods into MetadataReport to 
manipulate the exported URLs for service introspection
    
    * Polish apache/dubbo#6171 : [Feature] Introducing the composite 
implementation of MetadataService
    
    * Revert "fix wrong check of InvokerListener when export a service (fix 
issue_6269) (#6271)"
    
    This reverts commit 91989cae508f8482f31ac335879da4a5975661c8.
    
    * Revert "fix wrong check of InvokerListener when export a service (fix 
issue_6269) (#6271)"
    
    This reverts commit 91989cae508f8482f31ac335879da4a5975661c8.
    
    * Revert the MetadataReport
    
    * Polish apache/dubbo#6305 : [Refactor] ServiceConfig and ReferenceConfig 
publish the ServiceDefinition based on the Dubbo Event
    
    * Polish apache/dubbo#6198 : [Issue] Fixing 
NacosDynamicConfiguration#publishConfig bug
    
    * Polish apache/dubbo#6310 : Refactoring MetadataReport's methods
    
    * Polish apache/dubbo#6198 : [Issue] Fixing 
NacosDynamicConfiguration#publishConfig bug
    
    * Polish apache/dubbo#6198 : [Issue] Fixing 
NacosDynamicConfiguration#publishConfig bug
    
    * Polish apache/dubbo#6315 : [Refactor] Refactoring the implementation of 
MetadataReport based on The Config-Center infrastructure
    
    Deprecated List :
    
    - NacosMetadataReport
    - ZookeeperMetadataReport
    
    * Polish apache/dubbo#6315 : Refactoring by TreePathDynamicConfiguration
    
    * Polish apache/dubbo#6315 : Refactoring ConsulDynamicConfiguration by 
TreePathDynamicConfiguration
    
    * Polish apache/dubbo#6315 : Reset the config base path to be "metadata" 
for ConfigCenterBasedMetadataReportFactory
    
    * Polish apache/dubbo#6315 : Bugfix
    
    * Polish apache/dubbo#6315 : Bugfix
    
    * Polish apache/dubbo#6315 : Correct words
    
    * sync wait netty server to finish shutdown (#6281)
    
    * Polish apache/dubbo#6333 : [Refactor] Using mandatory implementation of 
Service Instance registration instead of the event
    
    * maybe we can remove null judge in this case (#6321)
    
    * update
    
    * update
    
    * Polish apache/dubbo#6336 : [Refactor] 
org.apache.dubbo.metadata.ServiceNameMapping
    
    * Polish apache/dubbo#6170 : [Feature] Introducing the externalized 
configuration for ServiceNameMapping
    
    * Polish apache/dubbo#6342 : [Enhancement] Introducing the composite 
ServiceNameMapping
    
    * Refactor
    
    * fix method name typo in JValidator.java (#6344)
    
    * [Dubbo-6340]fix application cannot exit when use consul registry (#6341)
    
    * fix application cannot exit when use consul registry
    
    * make consul registry suppor ACL (#6313)
    
    * make consul registry suppor ACL
    
    * Polish apache/dubbo#6172 : [Feature] Adding the "services" attribute 
methods into @DubboReference
    
    * Polish apache/dubbo#6173 : [Feature] Adding the "services" attribute into 
<dubbo:reference> element
    
    * Polish apache/dubbo#6346 : [Issue] Merging all subscribied URLs from the 
multiple services
    
    * Polish apache/dubbo#6346 : [Issue] Merging all subscribied URLs from the 
multiple services
    
    * fix publish null value when use consul config center (#6351)
    
    * fix publish null value when use consul config center
    
    * Polish apache/dubbo#6252
    
    * Polish apache/dubbo#6356 & apache/dubbo#6171
    
    * Polish apache/dubbo#6356 & apache/dubbo#6171
    
    * Polish apache/dubbo#6224 : Filter chain was not invoked with local calls 
since v2.7.6
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : Adding 
META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter
    
    * fix the priority of ListenableRouter were not effective (#6148)
    
    fixes #4822
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * when the url is generic, the log level should be info (#6363)
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * fix NPE when check=false is set and provider is empty. (#6376)
    
    fixes #6228
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * fix #6306.  support TypeBuilder sort (#6365)
    
    * fix #6306. support TypeBuilder sort
    
    * fix #6306. support TypeBuilder sort
    
    * fix #6306. support TypeBuilder sort
    
    * remove unused import
    
    * add license for test file
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * enhance ClusterInvoker & ExtensionLoader (#6343)
    
    - Introduce ClusterInvoker to better support multiple registries 
subscription
    - Wrapper sort and enable/disable
    - some small fixes
    
    * Polish apache/dubbo#6322 : [Enhancement] Fix the issues of test-cases 
after refactoring
    
    * Fixed the test-cases
    
    * Polish apache/dubbo#6389 : [Issue] Resolving the issues with 
ConsulServiceDiscovery
    
    Co-authored-by: tswstarplanet <tswstarpla...@apache.org>
    Co-authored-by: Nine <nine.yang.cod...@gmail.com>
    Co-authored-by: 陈哈哈 <chenyongjia...@outlook.com>
    Co-authored-by: luoning810 <18311333...@163.com>
    Co-authored-by: cvictory <shenglic...@gmail.com>
    Co-authored-by: ken.lj <ken.lj...@gmail.com>
---
 .../src/main/java/org/apache/dubbo/common/URL.java |   39 +-
 .../org/apache/dubbo/common/convert/Converter.java |   17 +
 .../convert/multiple/MultiValueConverter.java      |   28 +
 .../test/java/org/apache/dubbo/common/URLTest.java | 1793 ++++++++++----------
 .../convert/ConverterTest.java}                    |   39 +-
 .../convert/StringToBooleanConverterTest.java      |    5 +-
 .../convert/StringToCharArrayConverterTest.java    |    5 +-
 .../convert/StringToCharacterConverterTest.java    |    5 +-
 .../convert/StringToDoubleConverterTest.java       |    5 +-
 .../convert/StringToFloatConverterTest.java        |    5 +-
 .../convert/StringToIntegerConverterTest.java      |    5 +-
 .../convert/StringToLongConverterTest.java         |    5 +-
 .../convert/StringToOptionalConverterTest.java     |    5 +-
 .../convert/StringToShortConverterTest.java        |    5 +-
 .../convert/StringToStringConverterTest.java       |    5 +-
 .../convert/multiple/MultiValueConverterTest.java  |   72 +
 .../multiple/StringToArrayConverterTest.java       |    4 +-
 .../StringToBlockingDequeConverterTest.java        |    4 +-
 .../StringToBlockingQueueConverterTest.java        |    4 +-
 .../multiple/StringToCollectionConverterTest.java  |    5 +-
 .../multiple/StringToDequeConverterTest.java       |    4 +-
 .../multiple/StringToListConverterTest.java        |    4 +-
 .../StringToNavigableSetConverterTest.java         |    6 +-
 .../multiple/StringToQueueConverterTest.java       |    3 +-
 .../convert/multiple/StringToSetConverterTest.java |    3 +-
 .../multiple/StringToSortedSetConverterTest.java   |    6 +-
 .../StringToTransferQueueConverterTest.java        |    6 +-
 .../dubbo/registry/consul/ConsulParameter.java     |   87 +
 .../registry/consul/ConsulServiceDiscovery.java    |   89 +-
 29 files changed, 1265 insertions(+), 998 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java 
b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index c39f042..6ab3a89 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -57,6 +57,8 @@ import static 
org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.common.convert.Converter.convertIfPossible;
+import static org.apache.dubbo.common.utils.StringUtils.isBlank;
 
 /**
  * URL - Uniform Resource Locator (Immutable, ThreadSafe)
@@ -621,6 +623,41 @@ class URL implements Serializable {
         return Arrays.asList(strArray);
     }
 
+    /**
+     * Get parameter
+     *
+     * @param key       the key of parameter
+     * @param valueType the type of parameter value
+     * @param <T>       the type of parameter value
+     * @return get the parameter if present, or <code>null</code>
+     * @since 2.7.8
+     */
+    public <T> T getParameter(String key, Class<T> valueType) {
+        return getParameter(key, valueType, null);
+    }
+
+    /**
+     * Get parameter
+     *
+     * @param key          the key of parameter
+     * @param valueType    the type of parameter value
+     * @param defaultValue the default value if parameter is absent
+     * @param <T>          the type of parameter value
+     * @return get the parameter if present, or <code>defaultValue</code> will 
be used.
+     * @since 2.7.8
+     */
+    public <T> T getParameter(String key, Class<T> valueType, T defaultValue) {
+        String value = getParameter(key);
+        T result = null;
+        if (!isBlank(value)) {
+            result = convertIfPossible(value, valueType);
+        }
+        if (result == null) {
+            result = defaultValue;
+        }
+        return result;
+    }
+
     private Map<String, Number> getNumbers() {
         // concurrent initialization is tolerant
         if (numbers == null) {
@@ -1435,7 +1472,7 @@ class URL implements Serializable {
 
     private void append(StringBuilder target, String parameterName, boolean 
first) {
         String parameterValue = this.getParameter(parameterName);
-        if (!StringUtils.isBlank(parameterValue)) {
+        if (!isBlank(parameterValue)) {
             if (!first) {
                 target.append(":");
             }
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/Converter.java 
b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/Converter.java
index 5bc2d4d..e36fdf2 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/Converter.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/Converter.java
@@ -88,4 +88,21 @@ public interface Converter<S, T> extends Prioritized {
                 .findFirst()
                 .orElse(null);
     }
+
+    /**
+     * Convert the value of source to target-type value if possible
+     *
+     * @param source     the value of source
+     * @param targetType the target type
+     * @param <T>        the target type
+     * @return <code>null</code> if can't be converted
+     * @since 2.7.8
+     */
+    static <T> T convertIfPossible(Object source, Class<T> targetType) {
+        Converter converter = getConverter(source.getClass(), targetType);
+        if (converter != null) {
+            return (T) converter.convert(source);
+        }
+        return null;
+    }
 }
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/MultiValueConverter.java
 
b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/MultiValueConverter.java
index 298b459..637d1a8 100644
--- 
a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/MultiValueConverter.java
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/MultiValueConverter.java
@@ -16,11 +16,13 @@
  */
 package org.apache.dubbo.common.convert.multiple;
 
+import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.extension.SPI;
 import org.apache.dubbo.common.lang.Prioritized;
 
 import java.util.Collection;
 
+import static 
org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
 import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument;
 
 /**
@@ -61,4 +63,30 @@ public interface MultiValueConverter<S> extends Prioritized {
         return findActualTypeArgument(getClass(), MultiValueConverter.class, 
0);
     }
 
+    /**
+     * Find the {@link MultiValueConverter} instance from {@link 
ExtensionLoader} with the specified source and target type
+     *
+     * @param sourceType the source type
+     * @param targetType the target type
+     * @return <code>null</code> if not found
+     * @see ExtensionLoader#getSupportedExtensionInstances()
+     * @since 2.7.8
+     */
+    static MultiValueConverter<?> find(Class<?> sourceType, Class<?> 
targetType) {
+        return getExtensionLoader(MultiValueConverter.class)
+                .getSupportedExtensionInstances()
+                .stream()
+                .filter(converter -> converter.accept(sourceType, targetType))
+                .findFirst()
+                .orElse(null);
+    }
+
+    static <T> T convertIfPossible(Object source, Class<?> multiValueType, 
Class<?> elementType) {
+        Class<?> sourceType = source.getClass();
+        MultiValueConverter converter = find(sourceType, multiValueType);
+        if (converter != null) {
+            return (T) converter.convert(source, multiValueType, elementType);
+        }
+        return null;
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java 
b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
index dc96595..5ba606d 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
@@ -1,893 +1,900 @@
-/*
- * 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.dubbo.common;
-
-import org.apache.dubbo.common.utils.CollectionUtils;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Predicate;
-
-import static org.hamcrest.CoreMatchers.anyOf;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-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 URLTest {
-
-    @Test
-    public void test_valueOf_noProtocolAndHost() throws Exception {
-        URL url = 
URL.valueOf("/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertNull(url.getHost());
-        assertNull(url.getAddress());
-        assertEquals(0, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-
-        url = URL.valueOf("context/path?version=1.0.0&application=morgan");
-        //                 ^^^^^^^ Caution , parse as host
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertEquals("context", url.getHost());
-        assertEquals(0, url.getPort());
-        assertEquals("path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-    }
-
-    private void assertURLStrDecoder(URL url) {
-        String fullURLStr = url.toFullString();
-        URL newUrl = URLStrParser.parseEncodedStr(URL.encode(fullURLStr));
-        assertEquals(URL.valueOf(fullURLStr), newUrl);
-
-        URL newUrl2 = URLStrParser.parseDecodedStr(fullURLStr);
-        assertEquals(URL.valueOf(fullURLStr), newUrl2);
-    }
-
-    @Test
-    public void test_valueOf_noProtocol() throws Exception {
-        URL url = URL.valueOf("10.20.130.230");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230", url.getAddress());
-        assertEquals(0, url.getPort());
-        assertNull(url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = URL.valueOf("10.20.130.230:20880");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertNull(url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = URL.valueOf("10.20.130.230/context/path");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230", url.getAddress());
-        assertEquals(0, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = URL.valueOf("10.20.130.230:20880/context/path");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = 
URL.valueOf("admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-    }
-
-    @Test
-    public void test_valueOf_noHost() throws Exception {
-        URL url = URL.valueOf("file:///home/user1/router.js");
-        assertURLStrDecoder(url);
-        assertEquals("file", url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertNull(url.getHost());
-        assertNull(url.getAddress());
-        assertEquals(0, url.getPort());
-        assertEquals("home/user1/router.js", url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        // Caution!!
-        url = URL.valueOf("file://home/user1/router.js");
-        //                      ^^ only tow slash!
-        assertURLStrDecoder(url);
-        assertEquals("file", url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertEquals("home", url.getHost());
-        assertEquals(0, url.getPort());
-        assertEquals("user1/router.js", url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-
-        url = URL.valueOf("file:/home/user1/router.js");
-        assertURLStrDecoder(url);
-        assertEquals("file", url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertNull(url.getHost());
-        assertNull(url.getAddress());
-        assertEquals(0, url.getPort());
-        assertEquals("home/user1/router.js", url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = URL.valueOf("file:///d:/home/user1/router.js");
-        assertURLStrDecoder(url);
-        assertEquals("file", url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertNull(url.getHost());
-        assertNull(url.getAddress());
-        assertEquals(0, url.getPort());
-        assertEquals("d:/home/user1/router.js", url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = URL.valueOf("file:///home/user1/router.js?p1=v1&p2=v2");
-        assertURLStrDecoder(url);
-        assertEquals("file", url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertNull(url.getHost());
-        assertNull(url.getAddress());
-        assertEquals(0, url.getPort());
-        assertEquals("home/user1/router.js", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        Map<String, String> params = new HashMap<String, String>();
-        params.put("p1", "v1");
-        params.put("p2", "v2");
-        assertEquals(params, url.getParameters());
-
-        url = URL.valueOf("file:/home/user1/router.js?p1=v1&p2=v2");
-        assertURLStrDecoder(url);
-        assertEquals("file", url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertNull(url.getHost());
-        assertNull(url.getAddress());
-        assertEquals(0, url.getPort());
-        assertEquals("home/user1/router.js", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        params = new HashMap<String, String>();
-        params.put("p1", "v1");
-        params.put("p2", "v2");
-        assertEquals(params, url.getParameters());
-    }
-
-    @Test
-    public void test_valueOf_WithProtocolHost() throws Exception {
-        URL url = URL.valueOf("dubbo://10.20.130.230");
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230", url.getAddress());
-        assertEquals(0, url.getPort());
-        assertNull(url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = URL.valueOf("dubbo://10.20.130.230:20880/context/path");
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertNull(url.getUsername());
-        assertNull(url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880");
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertNull(url.getPath());
-        assertEquals(0, url.getParameters().size());
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880?version=1.0.0");
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertNull(url.getPath());
-        assertEquals(1, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(3, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-        assertEquals("noValue", url.getParameter("noValue"));
-    }
-
-    // TODO Do not want to use spaces? See: DUBBO-502, URL class handles 
special conventions for special characters.
-    @Test
-    public void test_valueOf_spaceSafe() throws Exception {
-        URL url = URL.valueOf("http://1.2.3.4:8080/path?key=value1 value2");
-        assertURLStrDecoder(url);
-        assertEquals("http://1.2.3.4:8080/path?key=value1 value2", 
url.toString());
-        assertEquals("value1 value2", url.getParameter("key"));
-    }
-
-    @Test
-    public void test_noValueKey() throws Exception {
-        URL url = URL.valueOf("http://1.2.3.4:8080/path?k0&k1=v1";);
-
-        assertURLStrDecoder(url);
-        assertTrue(url.hasParameter("k0"));
-
-        // If a Key has no corresponding Value, then the Key also used as the 
Value.
-        assertEquals("k0", url.getParameter("k0"));
-    }
-
-    @Test
-    public void test_valueOf_Exception_noProtocol() throws Exception {
-        try {
-            URL.valueOf("://1.2.3.4:8080/path");
-            fail();
-        } catch (IllegalStateException expected) {
-            assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", 
expected.getMessage());
-        }
-
-        try {
-            String encodedURLStr = URL.encode("://1.2.3.4:8080/path");
-            URLStrParser.parseEncodedStr(encodedURLStr);
-            fail();
-        } catch (IllegalStateException expected) {
-            assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", 
URL.decode(expected.getMessage()));
-        }
-
-        try {
-            URLStrParser.parseDecodedStr("://1.2.3.4:8080/path");
-            fail();
-        } catch (IllegalStateException expected) {
-            assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", 
expected.getMessage());
-        }
-    }
-
-    @Test
-    public void test_getAddress() throws Exception {
-        URL url1 = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url1);
-        assertEquals("10.20.130.230:20880", url1.getAddress());
-    }
-
-    @Test
-    public void test_getAbsolutePath() throws Exception {
-        URL url = new URL("p1", "1.2.2.2", 33);
-        assertURLStrDecoder(url);
-        assertNull(url.getAbsolutePath());
-
-        url = new URL("file", null, 90, "/home/user1/route.js");
-        assertURLStrDecoder(url);
-        assertEquals("/home/user1/route.js", url.getAbsolutePath());
-    }
-
-    @Test
-    public void test_equals() throws Exception {
-        URL url1 = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url1);
-
-        Map<String, String> params = new HashMap<String, String>();
-        params.put("version", "1.0.0");
-        params.put("application", "morgan");
-        URL url2 = new URL("dubbo", "admin", "hello1234", "10.20.130.230", 
20880, "context/path", params);
-
-        assertURLStrDecoder(url2);
-        assertEquals(url1, url2);
-    }
-
-    @Test
-    public void test_toString() throws Exception {
-        URL url1 = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url1);
-        assertThat(url1.toString(), anyOf(
-                
equalTo("dubbo://10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
-                
equalTo("dubbo://10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
-        );
-    }
-
-    @Test
-    public void test_toFullString() throws Exception {
-        URL url1 = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url1);
-        assertThat(url1.toFullString(), anyOf(
-                
equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
-                
equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
-        );
-    }
-
-    @Test
-    public void test_set_methods() throws Exception {
-        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url);
-
-        url = url.setHost("host");
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("host", url.getHost());
-        assertEquals("host:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-
-        url = url.setPort(1);
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("host", url.getHost());
-        assertEquals("host:1", url.getAddress());
-        assertEquals(1, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-
-        url = url.setPath("path");
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("host", url.getHost());
-        assertEquals("host:1", url.getAddress());
-        assertEquals(1, url.getPort());
-        assertEquals("path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-
-        url = url.setProtocol("protocol");
-
-        assertURLStrDecoder(url);
-        assertEquals("protocol", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("host", url.getHost());
-        assertEquals("host:1", url.getAddress());
-        assertEquals(1, url.getPort());
-        assertEquals("path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-
-        url = url.setUsername("username");
-
-        assertURLStrDecoder(url);
-        assertEquals("protocol", url.getProtocol());
-        assertEquals("username", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("host", url.getHost());
-        assertEquals("host:1", url.getAddress());
-        assertEquals(1, url.getPort());
-        assertEquals("path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-
-        url = url.setPassword("password");
-
-        assertURLStrDecoder(url);
-        assertEquals("protocol", url.getProtocol());
-        assertEquals("username", url.getUsername());
-        assertEquals("password", url.getPassword());
-        assertEquals("host", url.getHost());
-        assertEquals("host:1", url.getAddress());
-        assertEquals(1, url.getPort());
-        assertEquals("path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-    }
-
-    @Test
-    public void test_removeParameters() throws Exception {
-        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
-        assertURLStrDecoder(url);
-
-        url = url.removeParameter("version");
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(3, url.getParameters().size());
-        assertEquals("morgan", url.getParameter("application"));
-        assertEquals("v1", url.getParameter("k1"));
-        assertEquals("v2", url.getParameter("k2"));
-        assertNull(url.getParameter("version"));
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
-        url = url.removeParameters("version", "application", "NotExistedKey");
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("v1", url.getParameter("k1"));
-        assertEquals("v2", url.getParameter("k2"));
-        assertNull(url.getParameter("version"));
-        assertNull(url.getParameter("application"));
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
-        url = url.removeParameters(Arrays.asList("version", "application"));
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("v1", url.getParameter("k1"));
-        assertEquals("v2", url.getParameter("k2"));
-        assertNull(url.getParameter("version"));
-        assertNull(url.getParameter("application"));
-    }
-
-    @Test
-    public void test_addParameter() throws Exception {
-        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
-        url = url.addParameter("k1", "v1");
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("morgan", url.getParameter("application"));
-        assertEquals("v1", url.getParameter("k1"));
-    }
-
-    @Test
-    public void test_addParameter_sameKv() throws Exception {
-        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1");
-        URL newUrl = url.addParameter("k1", "v1");
-
-        assertURLStrDecoder(url);
-        assertSame(newUrl, url);
-    }
-
-
-    @Test
-    public void test_addParameters() throws Exception {
-        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
-        url = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", 
"v2"));
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(3, url.getParameters().size());
-        assertEquals("morgan", url.getParameter("application"));
-        assertEquals("v1", url.getParameter("k1"));
-        assertEquals("v2", url.getParameter("k2"));
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
-        url = url.addParameters("k1", "v1", "k2", "v2", "application", "xxx");
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(3, url.getParameters().size());
-        assertEquals("xxx", url.getParameter("application"));
-        assertEquals("v1", url.getParameter("k1"));
-        assertEquals("v2", url.getParameter("k2"));
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
-        url = url.addParametersIfAbsent(CollectionUtils.toStringMap("k1", 
"v1", "k2", "v2", "application", "xxx"));
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(3, url.getParameters().size());
-        assertEquals("morgan", url.getParameter("application"));
-        assertEquals("v1", url.getParameter("k1"));
-        assertEquals("v2", url.getParameter("k2"));
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
-        url = url.addParameter("k1", "v1");
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("morgan", url.getParameter("application"));
-        assertEquals("v1", url.getParameter("k1"));
-
-        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
-        url = url.addParameter("application", "xxx");
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(1, url.getParameters().size());
-        assertEquals("xxx", url.getParameter("application"));
-    }
-
-    @Test
-    public void test_addParameters_SameKv() throws Exception {
-        {
-            URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1");
-            URL newUrl = url.addParameters(CollectionUtils.toStringMap("k1", 
"v1"));
-
-            assertURLStrDecoder(url);
-            assertSame(url, newUrl);
-        }
-        {
-            URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1&k2=v2");
-            URL newUrl = url.addParameters(CollectionUtils.toStringMap("k1", 
"v1", "k2", "v2"));
-
-            assertURLStrDecoder(url);
-            assertSame(newUrl, url);
-        }
-    }
-
-    @Test
-    public void test_addParameterIfAbsent() throws Exception {
-        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
-        url = url.addParameterIfAbsent("application", "xxx");
-
-        assertURLStrDecoder(url);
-        assertEquals("dubbo", url.getProtocol());
-        assertEquals("admin", url.getUsername());
-        assertEquals("hello1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(1, url.getParameters().size());
-        assertEquals("morgan", url.getParameter("application"));
-    }
-
-    @Test
-    public void test_windowAbsolutePathBeginWithSlashIsValid() throws 
Exception {
-        final String osProperty = 
System.getProperties().getProperty("os.name");
-        if (!osProperty.toLowerCase().contains("windows")) return;
-
-        System.out.println("Test Windows valid path string.");
-
-        File f0 = new File("C:/Windows");
-        File f1 = new File("/C:/Windows");
-
-        File f2 = new File("C:\\Windows");
-        File f3 = new File("/C:\\Windows");
-        File f4 = new File("\\C:\\Windows");
-
-        assertEquals(f0, f1);
-        assertEquals(f0, f2);
-        assertEquals(f0, f3);
-        assertEquals(f0, f4);
-    }
-
-    @Test
-    public void test_javaNetUrl() throws Exception {
-        java.net.URL url = new 
java.net.URL("http://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan#anchor1";);
-
-        assertEquals("http", url.getProtocol());
-        assertEquals("admin:hello1234", url.getUserInfo());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals(20880, url.getPort());
-        assertEquals("/context/path", url.getPath());
-        assertEquals("version=1.0.0&application=morgan", url.getQuery());
-        assertEquals("anchor1", url.getRef());
-
-        assertEquals("admin:hello1234@10.20.130.230:20880", 
url.getAuthority());
-        assertEquals("/context/path?version=1.0.0&application=morgan", 
url.getFile());
-    }
-
-    @Test
-    public void test_Anyhost() throws Exception {
-        URL url = URL.valueOf("dubbo://0.0.0.0:20880");
-        assertURLStrDecoder(url);
-        assertEquals("0.0.0.0", url.getHost());
-        assertTrue(url.isAnyHost());
-    }
-
-    @Test
-    public void test_Localhost() throws Exception {
-        URL url = URL.valueOf("dubbo://127.0.0.1:20880");
-        assertURLStrDecoder(url);
-        assertEquals("127.0.0.1", url.getHost());
-        assertEquals("127.0.0.1:20880", url.getAddress());
-        assertTrue(url.isLocalHost());
-
-        url = URL.valueOf("dubbo://127.0.1.1:20880");
-        assertURLStrDecoder(url);
-        assertEquals("127.0.1.1", url.getHost());
-        assertEquals("127.0.1.1:20880", url.getAddress());
-        assertTrue(url.isLocalHost());
-
-        url = URL.valueOf("dubbo://localhost:20880");
-        assertURLStrDecoder(url);
-        assertEquals("localhost", url.getHost());
-        assertEquals("localhost:20880", url.getAddress());
-        assertTrue(url.isLocalHost());
-    }
-
-    @Test
-    public void test_Path() throws Exception {
-        URL url = new URL("dubbo", "localhost", 20880, "////path");
-        assertURLStrDecoder(url);
-        assertEquals("path", url.getPath());
-    }
-
-    @Test
-    public void testAddParameters() throws Exception {
-        URL url = URL.valueOf("dubbo://127.0.0.1:20880");
-        assertURLStrDecoder(url);
-
-        Map<String, String> parameters = new HashMap<String, String>();
-        parameters.put("version", null);
-        url.addParameters(parameters);
-        assertURLStrDecoder(url);
-    }
-
-    @Test
-    public void testUserNamePasswordContainsAt() {
-        // Test username or password contains "@"
-        URL url = 
URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertEquals("ad@min", url.getUsername());
-        assertEquals("hello@1234", url.getPassword());
-        assertEquals("10.20.130.230", url.getHost());
-        assertEquals("10.20.130.230:20880", url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-    }
-
-
-    @Test
-    public void testIpV6Address() {
-        // Test username or password contains "@"
-        URL url = 
URL.valueOf("ad@min111:haha@1234@2001:0db8:85a3:08d3:1319:8a2e:0370:7344:20880/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertEquals("ad@min111", url.getUsername());
-        assertEquals("haha@1234", url.getPassword());
-        assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344", url.getHost());
-        assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344:20880", 
url.getAddress());
-        assertEquals(20880, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-    }
-
-    @Test
-    public void testIpV6AddressWithScopeId() {
-        URL url = 
URL.valueOf("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5/context/path?version=1.0.0&application=morgan");
-        assertURLStrDecoder(url);
-        assertNull(url.getProtocol());
-        assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5", 
url.getHost());
-        assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5", 
url.getAddress());
-        assertEquals(0, url.getPort());
-        assertEquals("context/path", url.getPath());
-        assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
-        assertEquals("morgan", url.getParameter("application"));
-    }
-
-    @Test
-    public void testDefaultPort() {
-        Assertions.assertEquals("10.20.153.10:2181", 
URL.appendDefaultPort("10.20.153.10:0", 2181));
-        Assertions.assertEquals("10.20.153.10:2181", 
URL.appendDefaultPort("10.20.153.10", 2181));
-    }
-
-    @Test
-    public void testGetServiceKey() {
-        URL url1 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
-        assertURLStrDecoder(url1);
-        Assertions.assertEquals("org.apache.dubbo.test.interfaceName", 
url1.getServiceKey());
-
-        URL url2 = 
URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName");
-        assertURLStrDecoder(url2);
-        Assertions.assertEquals("org.apache.dubbo.test.interfaceName", 
url2.getServiceKey());
-
-        URL url3 = 
URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName&group=group1&version=1.0.0");
-        assertURLStrDecoder(url3);
-        
Assertions.assertEquals("group1/org.apache.dubbo.test.interfaceName:1.0.0", 
url3.getServiceKey());
-
-        URL url4 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
-        assertURLStrDecoder(url4);
-        Assertions.assertEquals("context/path", url4.getPathKey());
-
-        URL url5 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group1&version=1.0.0");
-        assertURLStrDecoder(url5);
-        Assertions.assertEquals("group1/context/path:1.0.0", 
url5.getPathKey());
-    }
-
-    @Test
-    public void testGetColonSeparatedKey() {
-        URL url1 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0");
-        assertURLStrDecoder(url1);
-        
Assertions.assertEquals("org.apache.dubbo.test.interfaceName:1.0.0:group", 
url1.getColonSeparatedKey());
-
-        URL url2 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&version=1.0.0");
-        assertURLStrDecoder(url2);
-        Assertions.assertEquals("org.apache.dubbo.test.interfaceName:1.0.0:", 
url2.getColonSeparatedKey());
-
-        URL url3 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group");
-        assertURLStrDecoder(url3);
-        Assertions.assertEquals("org.apache.dubbo.test.interfaceName::group", 
url3.getColonSeparatedKey());
-
-        URL url4 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
-        assertURLStrDecoder(url4);
-        Assertions.assertEquals("org.apache.dubbo.test.interfaceName::", 
url4.getColonSeparatedKey());
-
-        URL url5 = 
URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName");
-        assertURLStrDecoder(url5);
-        Assertions.assertEquals("org.apache.dubbo.test.interfaceName::", 
url5.getColonSeparatedKey());
-
-        URL url6 = 
URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName1");
-        assertURLStrDecoder(url6);
-        Assertions.assertEquals("org.apache.dubbo.test.interfaceName1::", 
url6.getColonSeparatedKey());
-    }
-
-    @Test
-    public void testValueOf() {
-        URL url = URL.valueOf("10.20.130.230");
-        assertURLStrDecoder(url);
-
-        url = URL.valueOf("10.20.130.230:20880");
-        assertURLStrDecoder(url);
-
-        url = URL.valueOf("dubbo://10.20.130.230:20880");
-        assertURLStrDecoder(url);
-
-        url = URL.valueOf("dubbo://10.20.130.230:20880/path");
-        assertURLStrDecoder(url);
-    }
-
-
-    /**
-     * Test {@link URL#getParameters(Predicate)} method
-     *
-     * @since 2.7.8
-     */
-    @Test
-    public void testGetParameters() {
-        URL url = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0");
-        Map<String, String> parameters = url.getParameters(i -> 
"version".equals(i));
-        String version = parameters.get("version");
-        assertEquals(1, parameters.size());
-        assertEquals("1.0.0", version);
-    }
-}
+/*
+ * 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.dubbo.common;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+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 URLTest {
+
+    @Test
+    public void test_valueOf_noProtocolAndHost() throws Exception {
+        URL url = 
URL.valueOf("/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertNull(url.getHost());
+        assertNull(url.getAddress());
+        assertEquals(0, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+
+        url = URL.valueOf("context/path?version=1.0.0&application=morgan");
+        //                 ^^^^^^^ Caution , parse as host
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertEquals("context", url.getHost());
+        assertEquals(0, url.getPort());
+        assertEquals("path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+    }
+
+    private void assertURLStrDecoder(URL url) {
+        String fullURLStr = url.toFullString();
+        URL newUrl = URLStrParser.parseEncodedStr(URL.encode(fullURLStr));
+        assertEquals(URL.valueOf(fullURLStr), newUrl);
+
+        URL newUrl2 = URLStrParser.parseDecodedStr(fullURLStr);
+        assertEquals(URL.valueOf(fullURLStr), newUrl2);
+    }
+
+    @Test
+    public void test_valueOf_noProtocol() throws Exception {
+        URL url = URL.valueOf("10.20.130.230");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230", url.getAddress());
+        assertEquals(0, url.getPort());
+        assertNull(url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = URL.valueOf("10.20.130.230:20880");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertNull(url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = URL.valueOf("10.20.130.230/context/path");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230", url.getAddress());
+        assertEquals(0, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = URL.valueOf("10.20.130.230:20880/context/path");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = 
URL.valueOf("admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+    }
+
+    @Test
+    public void test_valueOf_noHost() throws Exception {
+        URL url = URL.valueOf("file:///home/user1/router.js");
+        assertURLStrDecoder(url);
+        assertEquals("file", url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertNull(url.getHost());
+        assertNull(url.getAddress());
+        assertEquals(0, url.getPort());
+        assertEquals("home/user1/router.js", url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        // Caution!!
+        url = URL.valueOf("file://home/user1/router.js");
+        //                      ^^ only tow slash!
+        assertURLStrDecoder(url);
+        assertEquals("file", url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertEquals("home", url.getHost());
+        assertEquals(0, url.getPort());
+        assertEquals("user1/router.js", url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+
+        url = URL.valueOf("file:/home/user1/router.js");
+        assertURLStrDecoder(url);
+        assertEquals("file", url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertNull(url.getHost());
+        assertNull(url.getAddress());
+        assertEquals(0, url.getPort());
+        assertEquals("home/user1/router.js", url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = URL.valueOf("file:///d:/home/user1/router.js");
+        assertURLStrDecoder(url);
+        assertEquals("file", url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertNull(url.getHost());
+        assertNull(url.getAddress());
+        assertEquals(0, url.getPort());
+        assertEquals("d:/home/user1/router.js", url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = URL.valueOf("file:///home/user1/router.js?p1=v1&p2=v2");
+        assertURLStrDecoder(url);
+        assertEquals("file", url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertNull(url.getHost());
+        assertNull(url.getAddress());
+        assertEquals(0, url.getPort());
+        assertEquals("home/user1/router.js", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        Map<String, String> params = new HashMap<String, String>();
+        params.put("p1", "v1");
+        params.put("p2", "v2");
+        assertEquals(params, url.getParameters());
+
+        url = URL.valueOf("file:/home/user1/router.js?p1=v1&p2=v2");
+        assertURLStrDecoder(url);
+        assertEquals("file", url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertNull(url.getHost());
+        assertNull(url.getAddress());
+        assertEquals(0, url.getPort());
+        assertEquals("home/user1/router.js", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        params = new HashMap<String, String>();
+        params.put("p1", "v1");
+        params.put("p2", "v2");
+        assertEquals(params, url.getParameters());
+    }
+
+    @Test
+    public void test_valueOf_WithProtocolHost() throws Exception {
+        URL url = URL.valueOf("dubbo://10.20.130.230");
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230", url.getAddress());
+        assertEquals(0, url.getPort());
+        assertNull(url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = URL.valueOf("dubbo://10.20.130.230:20880/context/path");
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertNull(url.getUsername());
+        assertNull(url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880");
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertNull(url.getPath());
+        assertEquals(0, url.getParameters().size());
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880?version=1.0.0");
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertNull(url.getPath());
+        assertEquals(1, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(3, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+        assertEquals("noValue", url.getParameter("noValue"));
+    }
+
+    // TODO Do not want to use spaces? See: DUBBO-502, URL class handles 
special conventions for special characters.
+    @Test
+    public void test_valueOf_spaceSafe() throws Exception {
+        URL url = URL.valueOf("http://1.2.3.4:8080/path?key=value1 value2");
+        assertURLStrDecoder(url);
+        assertEquals("http://1.2.3.4:8080/path?key=value1 value2", 
url.toString());
+        assertEquals("value1 value2", url.getParameter("key"));
+    }
+
+    @Test
+    public void test_noValueKey() throws Exception {
+        URL url = URL.valueOf("http://1.2.3.4:8080/path?k0&k1=v1";);
+
+        assertURLStrDecoder(url);
+        assertTrue(url.hasParameter("k0"));
+
+        // If a Key has no corresponding Value, then the Key also used as the 
Value.
+        assertEquals("k0", url.getParameter("k0"));
+    }
+
+    @Test
+    public void test_valueOf_Exception_noProtocol() throws Exception {
+        try {
+            URL.valueOf("://1.2.3.4:8080/path");
+            fail();
+        } catch (IllegalStateException expected) {
+            assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", 
expected.getMessage());
+        }
+
+        try {
+            String encodedURLStr = URL.encode("://1.2.3.4:8080/path");
+            URLStrParser.parseEncodedStr(encodedURLStr);
+            fail();
+        } catch (IllegalStateException expected) {
+            assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", 
URL.decode(expected.getMessage()));
+        }
+
+        try {
+            URLStrParser.parseDecodedStr("://1.2.3.4:8080/path");
+            fail();
+        } catch (IllegalStateException expected) {
+            assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", 
expected.getMessage());
+        }
+    }
+
+    @Test
+    public void test_getAddress() throws Exception {
+        URL url1 = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url1);
+        assertEquals("10.20.130.230:20880", url1.getAddress());
+    }
+
+    @Test
+    public void test_getAbsolutePath() throws Exception {
+        URL url = new URL("p1", "1.2.2.2", 33);
+        assertURLStrDecoder(url);
+        assertNull(url.getAbsolutePath());
+
+        url = new URL("file", null, 90, "/home/user1/route.js");
+        assertURLStrDecoder(url);
+        assertEquals("/home/user1/route.js", url.getAbsolutePath());
+    }
+
+    @Test
+    public void test_equals() throws Exception {
+        URL url1 = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url1);
+
+        Map<String, String> params = new HashMap<String, String>();
+        params.put("version", "1.0.0");
+        params.put("application", "morgan");
+        URL url2 = new URL("dubbo", "admin", "hello1234", "10.20.130.230", 
20880, "context/path", params);
+
+        assertURLStrDecoder(url2);
+        assertEquals(url1, url2);
+    }
+
+    @Test
+    public void test_toString() throws Exception {
+        URL url1 = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url1);
+        assertThat(url1.toString(), anyOf(
+                
equalTo("dubbo://10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
+                
equalTo("dubbo://10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
+        );
+    }
+
+    @Test
+    public void test_toFullString() throws Exception {
+        URL url1 = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url1);
+        assertThat(url1.toFullString(), anyOf(
+                
equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
+                
equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
+        );
+    }
+
+    @Test
+    public void test_set_methods() throws Exception {
+        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url);
+
+        url = url.setHost("host");
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("host", url.getHost());
+        assertEquals("host:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+
+        url = url.setPort(1);
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("host", url.getHost());
+        assertEquals("host:1", url.getAddress());
+        assertEquals(1, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+
+        url = url.setPath("path");
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("host", url.getHost());
+        assertEquals("host:1", url.getAddress());
+        assertEquals(1, url.getPort());
+        assertEquals("path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+
+        url = url.setProtocol("protocol");
+
+        assertURLStrDecoder(url);
+        assertEquals("protocol", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("host", url.getHost());
+        assertEquals("host:1", url.getAddress());
+        assertEquals(1, url.getPort());
+        assertEquals("path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+
+        url = url.setUsername("username");
+
+        assertURLStrDecoder(url);
+        assertEquals("protocol", url.getProtocol());
+        assertEquals("username", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("host", url.getHost());
+        assertEquals("host:1", url.getAddress());
+        assertEquals(1, url.getPort());
+        assertEquals("path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+
+        url = url.setPassword("password");
+
+        assertURLStrDecoder(url);
+        assertEquals("protocol", url.getProtocol());
+        assertEquals("username", url.getUsername());
+        assertEquals("password", url.getPassword());
+        assertEquals("host", url.getHost());
+        assertEquals("host:1", url.getAddress());
+        assertEquals(1, url.getPort());
+        assertEquals("path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+    }
+
+    @Test
+    public void test_removeParameters() throws Exception {
+        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+        assertURLStrDecoder(url);
+
+        url = url.removeParameter("version");
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(3, url.getParameters().size());
+        assertEquals("morgan", url.getParameter("application"));
+        assertEquals("v1", url.getParameter("k1"));
+        assertEquals("v2", url.getParameter("k2"));
+        assertNull(url.getParameter("version"));
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+        url = url.removeParameters("version", "application", "NotExistedKey");
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("v1", url.getParameter("k1"));
+        assertEquals("v2", url.getParameter("k2"));
+        assertNull(url.getParameter("version"));
+        assertNull(url.getParameter("application"));
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+        url = url.removeParameters(Arrays.asList("version", "application"));
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("v1", url.getParameter("k1"));
+        assertEquals("v2", url.getParameter("k2"));
+        assertNull(url.getParameter("version"));
+        assertNull(url.getParameter("application"));
+    }
+
+    @Test
+    public void test_addParameter() throws Exception {
+        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+        url = url.addParameter("k1", "v1");
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("morgan", url.getParameter("application"));
+        assertEquals("v1", url.getParameter("k1"));
+    }
+
+    @Test
+    public void test_addParameter_sameKv() throws Exception {
+        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1");
+        URL newUrl = url.addParameter("k1", "v1");
+
+        assertURLStrDecoder(url);
+        assertSame(newUrl, url);
+    }
+
+
+    @Test
+    public void test_addParameters() throws Exception {
+        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+        url = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", 
"v2"));
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(3, url.getParameters().size());
+        assertEquals("morgan", url.getParameter("application"));
+        assertEquals("v1", url.getParameter("k1"));
+        assertEquals("v2", url.getParameter("k2"));
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+        url = url.addParameters("k1", "v1", "k2", "v2", "application", "xxx");
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(3, url.getParameters().size());
+        assertEquals("xxx", url.getParameter("application"));
+        assertEquals("v1", url.getParameter("k1"));
+        assertEquals("v2", url.getParameter("k2"));
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+        url = url.addParametersIfAbsent(CollectionUtils.toStringMap("k1", 
"v1", "k2", "v2", "application", "xxx"));
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(3, url.getParameters().size());
+        assertEquals("morgan", url.getParameter("application"));
+        assertEquals("v1", url.getParameter("k1"));
+        assertEquals("v2", url.getParameter("k2"));
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+        url = url.addParameter("k1", "v1");
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("morgan", url.getParameter("application"));
+        assertEquals("v1", url.getParameter("k1"));
+
+        url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+        url = url.addParameter("application", "xxx");
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(1, url.getParameters().size());
+        assertEquals("xxx", url.getParameter("application"));
+    }
+
+    @Test
+    public void test_addParameters_SameKv() throws Exception {
+        {
+            URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1");
+            URL newUrl = url.addParameters(CollectionUtils.toStringMap("k1", 
"v1"));
+
+            assertURLStrDecoder(url);
+            assertSame(url, newUrl);
+        }
+        {
+            URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1&k2=v2");
+            URL newUrl = url.addParameters(CollectionUtils.toStringMap("k1", 
"v1", "k2", "v2"));
+
+            assertURLStrDecoder(url);
+            assertSame(newUrl, url);
+        }
+    }
+
+    @Test
+    public void test_addParameterIfAbsent() throws Exception {
+        URL url = 
URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+        url = url.addParameterIfAbsent("application", "xxx");
+
+        assertURLStrDecoder(url);
+        assertEquals("dubbo", url.getProtocol());
+        assertEquals("admin", url.getUsername());
+        assertEquals("hello1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(1, url.getParameters().size());
+        assertEquals("morgan", url.getParameter("application"));
+    }
+
+    @Test
+    public void test_windowAbsolutePathBeginWithSlashIsValid() throws 
Exception {
+        final String osProperty = 
System.getProperties().getProperty("os.name");
+        if (!osProperty.toLowerCase().contains("windows")) return;
+
+        System.out.println("Test Windows valid path string.");
+
+        File f0 = new File("C:/Windows");
+        File f1 = new File("/C:/Windows");
+
+        File f2 = new File("C:\\Windows");
+        File f3 = new File("/C:\\Windows");
+        File f4 = new File("\\C:\\Windows");
+
+        assertEquals(f0, f1);
+        assertEquals(f0, f2);
+        assertEquals(f0, f3);
+        assertEquals(f0, f4);
+    }
+
+    @Test
+    public void test_javaNetUrl() throws Exception {
+        java.net.URL url = new 
java.net.URL("http://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan#anchor1";);
+
+        assertEquals("http", url.getProtocol());
+        assertEquals("admin:hello1234", url.getUserInfo());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals(20880, url.getPort());
+        assertEquals("/context/path", url.getPath());
+        assertEquals("version=1.0.0&application=morgan", url.getQuery());
+        assertEquals("anchor1", url.getRef());
+
+        assertEquals("admin:hello1234@10.20.130.230:20880", 
url.getAuthority());
+        assertEquals("/context/path?version=1.0.0&application=morgan", 
url.getFile());
+    }
+
+    @Test
+    public void test_Anyhost() throws Exception {
+        URL url = URL.valueOf("dubbo://0.0.0.0:20880");
+        assertURLStrDecoder(url);
+        assertEquals("0.0.0.0", url.getHost());
+        assertTrue(url.isAnyHost());
+    }
+
+    @Test
+    public void test_Localhost() throws Exception {
+        URL url = URL.valueOf("dubbo://127.0.0.1:20880");
+        assertURLStrDecoder(url);
+        assertEquals("127.0.0.1", url.getHost());
+        assertEquals("127.0.0.1:20880", url.getAddress());
+        assertTrue(url.isLocalHost());
+
+        url = URL.valueOf("dubbo://127.0.1.1:20880");
+        assertURLStrDecoder(url);
+        assertEquals("127.0.1.1", url.getHost());
+        assertEquals("127.0.1.1:20880", url.getAddress());
+        assertTrue(url.isLocalHost());
+
+        url = URL.valueOf("dubbo://localhost:20880");
+        assertURLStrDecoder(url);
+        assertEquals("localhost", url.getHost());
+        assertEquals("localhost:20880", url.getAddress());
+        assertTrue(url.isLocalHost());
+    }
+
+    @Test
+    public void test_Path() throws Exception {
+        URL url = new URL("dubbo", "localhost", 20880, "////path");
+        assertURLStrDecoder(url);
+        assertEquals("path", url.getPath());
+    }
+
+    @Test
+    public void testAddParameters() throws Exception {
+        URL url = URL.valueOf("dubbo://127.0.0.1:20880");
+        assertURLStrDecoder(url);
+
+        Map<String, String> parameters = new HashMap<String, String>();
+        parameters.put("version", null);
+        url.addParameters(parameters);
+        assertURLStrDecoder(url);
+    }
+
+    @Test
+    public void testUserNamePasswordContainsAt() {
+        // Test username or password contains "@"
+        URL url = 
URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertEquals("ad@min", url.getUsername());
+        assertEquals("hello@1234", url.getPassword());
+        assertEquals("10.20.130.230", url.getHost());
+        assertEquals("10.20.130.230:20880", url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+    }
+
+
+    @Test
+    public void testIpV6Address() {
+        // Test username or password contains "@"
+        URL url = 
URL.valueOf("ad@min111:haha@1234@2001:0db8:85a3:08d3:1319:8a2e:0370:7344:20880/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertEquals("ad@min111", url.getUsername());
+        assertEquals("haha@1234", url.getPassword());
+        assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344", url.getHost());
+        assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344:20880", 
url.getAddress());
+        assertEquals(20880, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+    }
+
+    @Test
+    public void testIpV6AddressWithScopeId() {
+        URL url = 
URL.valueOf("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5/context/path?version=1.0.0&application=morgan");
+        assertURLStrDecoder(url);
+        assertNull(url.getProtocol());
+        assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5", 
url.getHost());
+        assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5", 
url.getAddress());
+        assertEquals(0, url.getPort());
+        assertEquals("context/path", url.getPath());
+        assertEquals(2, url.getParameters().size());
+        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("morgan", url.getParameter("application"));
+    }
+
+    @Test
+    public void testDefaultPort() {
+        Assertions.assertEquals("10.20.153.10:2181", 
URL.appendDefaultPort("10.20.153.10:0", 2181));
+        Assertions.assertEquals("10.20.153.10:2181", 
URL.appendDefaultPort("10.20.153.10", 2181));
+    }
+
+    @Test
+    public void testGetServiceKey() {
+        URL url1 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
+        assertURLStrDecoder(url1);
+        Assertions.assertEquals("org.apache.dubbo.test.interfaceName", 
url1.getServiceKey());
+
+        URL url2 = 
URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName");
+        assertURLStrDecoder(url2);
+        Assertions.assertEquals("org.apache.dubbo.test.interfaceName", 
url2.getServiceKey());
+
+        URL url3 = 
URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName&group=group1&version=1.0.0");
+        assertURLStrDecoder(url3);
+        
Assertions.assertEquals("group1/org.apache.dubbo.test.interfaceName:1.0.0", 
url3.getServiceKey());
+
+        URL url4 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
+        assertURLStrDecoder(url4);
+        Assertions.assertEquals("context/path", url4.getPathKey());
+
+        URL url5 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group1&version=1.0.0");
+        assertURLStrDecoder(url5);
+        Assertions.assertEquals("group1/context/path:1.0.0", 
url5.getPathKey());
+    }
+
+    @Test
+    public void testGetColonSeparatedKey() {
+        URL url1 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0");
+        assertURLStrDecoder(url1);
+        
Assertions.assertEquals("org.apache.dubbo.test.interfaceName:1.0.0:group", 
url1.getColonSeparatedKey());
+
+        URL url2 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&version=1.0.0");
+        assertURLStrDecoder(url2);
+        Assertions.assertEquals("org.apache.dubbo.test.interfaceName:1.0.0:", 
url2.getColonSeparatedKey());
+
+        URL url3 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group");
+        assertURLStrDecoder(url3);
+        Assertions.assertEquals("org.apache.dubbo.test.interfaceName::group", 
url3.getColonSeparatedKey());
+
+        URL url4 = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName");
+        assertURLStrDecoder(url4);
+        Assertions.assertEquals("org.apache.dubbo.test.interfaceName::", 
url4.getColonSeparatedKey());
+
+        URL url5 = 
URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName");
+        assertURLStrDecoder(url5);
+        Assertions.assertEquals("org.apache.dubbo.test.interfaceName::", 
url5.getColonSeparatedKey());
+
+        URL url6 = 
URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName1");
+        assertURLStrDecoder(url6);
+        Assertions.assertEquals("org.apache.dubbo.test.interfaceName1::", 
url6.getColonSeparatedKey());
+    }
+
+    @Test
+    public void testValueOf() {
+        URL url = URL.valueOf("10.20.130.230");
+        assertURLStrDecoder(url);
+
+        url = URL.valueOf("10.20.130.230:20880");
+        assertURLStrDecoder(url);
+
+        url = URL.valueOf("dubbo://10.20.130.230:20880");
+        assertURLStrDecoder(url);
+
+        url = URL.valueOf("dubbo://10.20.130.230:20880/path");
+        assertURLStrDecoder(url);
+    }
+
+
+    /**
+     * Test {@link URL#getParameters(Predicate)} method
+     *
+     * @since 2.7.8
+     */
+    @Test
+    public void testGetParameters() {
+        URL url = 
URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0");
+        Map<String, String> parameters = url.getParameters(i -> 
"version".equals(i));
+        String version = parameters.get("version");
+        assertEquals(1, parameters.size());
+        assertEquals("1.0.0", version);
+    }
+
+    @Test
+    public void testGetParameter() {
+        URL url = URL.valueOf("http://127.0.0.1:8080/path?i=1&b=false";);
+        assertEquals(Integer.valueOf(1), url.getParameter("i", Integer.class));
+        assertEquals(Boolean.FALSE, url.getParameter("b", Boolean.class));
+    }
+}
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.java
 b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/ConverterTest.java
similarity index 53%
copy from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.java
copy to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/ConverterTest.java
index 57806c3..1f5a36c 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/ConverterTest.java
@@ -14,41 +14,36 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
+package org.apache.dubbo.common.convert;
 
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToStringConverter;
-
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import static org.apache.dubbo.common.convert.Converter.convertIfPossible;
+import static org.apache.dubbo.common.convert.Converter.getConverter;
 import static 
org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertSame;
 
 /**
- * {@link StringToStringConverter} Test
+ * {@link Converter} Test-Cases
  *
- * @since 2.7.6
+ * @since 2.7.8
  */
-public class StringToStringConverterTest {
-
-    private StringToStringConverter converter;
-
-    @BeforeEach
-    public void init() {
-        converter = (StringToStringConverter) 
getExtensionLoader(Converter.class).getExtension("string-to-string");
-    }
+public class ConverterTest {
 
     @Test
-    public void testAccept() {
-        assertTrue(converter.accept(String.class, String.class));
+    public void testGetConverter() {
+        getExtensionLoader(Converter.class)
+                .getSupportedExtensionInstances()
+                .forEach(converter -> {
+                    assertSame(converter, 
getConverter(converter.getSourceType(), converter.getTargetType()));
+                });
     }
 
     @Test
-    public void testConvert() {
-        assertEquals("1", converter.convert("1"));
-        assertNull(converter.convert(null));
+    public void testConvertIfPossible() {
+        assertEquals(Integer.valueOf(2), convertIfPossible("2", 
Integer.class));
+        assertEquals(Boolean.FALSE, convertIfPossible("false", Boolean.class));
+        assertEquals(Double.valueOf(1), convertIfPossible("1", Double.class));
     }
 }
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToBooleanConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToBooleanConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToBooleanConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToBooleanConverterTest.java
index 3b1d75b..e955fa9 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToBooleanConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToBooleanConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToBooleanConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharArrayConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToCharArrayConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharArrayConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToCharArrayConverterTest.java
index 492a129..bc3a606 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharArrayConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToCharArrayConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToCharArrayConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharacterConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToCharacterConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharacterConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToCharacterConverterTest.java
index c9e88c2..87f3367 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharacterConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToCharacterConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToCharacterConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToDoubleConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToDoubleConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToDoubleConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToDoubleConverterTest.java
index 668f3e6..2c74736 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToDoubleConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToDoubleConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToDoubleConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToFloatConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToFloatConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToFloatConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToFloatConverterTest.java
index aa17499..b4b36f3 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToFloatConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToFloatConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToFloatConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToIntegerConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToIntegerConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToIntegerConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToIntegerConverterTest.java
index 9c7d24b..1ccebfd 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToIntegerConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToIntegerConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToIntegerConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToLongConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToLongConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToLongConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToLongConverterTest.java
index e14424a..c7cd926 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToLongConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToLongConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToLongConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToOptionalConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToOptionalConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToOptionalConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToOptionalConverterTest.java
index 242ae60..9cb79e2 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToOptionalConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToOptionalConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToOptionalConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToShortConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToShortConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToShortConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToShortConverterTest.java
index 3f1d493..9ecdc20 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToShortConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToShortConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToShortConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToStringConverterTest.java
similarity index 92%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToStringConverterTest.java
index 57806c3..517585d 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToStringConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert;
-
-import org.apache.dubbo.common.convert.Converter;
-import org.apache.dubbo.common.convert.StringToStringConverter;
+package org.apache.dubbo.common.convert;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/MultiValueConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/MultiValueConverterTest.java
new file mode 100644
index 0000000..ea64628
--- /dev/null
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/MultiValueConverterTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.dubbo.common.convert.multiple;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Collection;
+import java.util.Deque;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TransferQueue;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link MultiValueConverter} Test
+ *
+ * @since 2.7.8
+ */
+public class MultiValueConverterTest {
+
+    @Test
+    public void testFind() {
+        MultiValueConverter converter = MultiValueConverter.find(String.class, 
String[].class);
+        assertEquals(StringToArrayConverter.class, converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, 
BlockingDeque.class);
+        assertEquals(StringToBlockingDequeConverter.class, 
converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, 
BlockingQueue.class);
+        assertEquals(StringToBlockingQueueConverter.class, 
converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, Collection.class);
+        assertEquals(StringToCollectionConverter.class, converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, Deque.class);
+        assertEquals(StringToDequeConverter.class, converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, List.class);
+        assertEquals(StringToListConverter.class, converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, NavigableSet.class);
+        assertEquals(StringToNavigableSetConverter.class, 
converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, Queue.class);
+        assertEquals(StringToQueueConverter.class, converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, Set.class);
+        assertEquals(StringToSetConverter.class, converter.getClass());
+
+        converter = MultiValueConverter.find(String.class, 
TransferQueue.class);
+        assertEquals(StringToTransferQueueConverter.class, 
converter.getClass());
+    }
+}
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToArrayConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToArrayConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToArrayConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToArrayConverterTest.java
index 1781356..8279e07 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToArrayConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToArrayConverterTest.java
@@ -14,9 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
-
-import org.apache.dubbo.common.convert.multiple.StringToArrayConverter;
+package org.apache.dubbo.common.convert.multiple;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingDequeConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToBlockingDequeConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingDequeConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToBlockingDequeConverterTest.java
index 6f9597d..f0975ad 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingDequeConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToBlockingDequeConverterTest.java
@@ -14,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
-import org.apache.dubbo.common.convert.multiple.StringToBlockingDequeConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingQueueConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToBlockingQueueConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingQueueConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToBlockingQueueConverterTest.java
index 4fa7532..f1d8cb8 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingQueueConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToBlockingQueueConverterTest.java
@@ -14,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
-import org.apache.dubbo.common.convert.multiple.StringToBlockingQueueConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToCollectionConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToCollectionConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToCollectionConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToCollectionConverterTest.java
index f0b06ec..564ece3 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToCollectionConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToCollectionConverterTest.java
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
-
-import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
-import org.apache.dubbo.common.convert.multiple.StringToCollectionConverter;
+package org.apache.dubbo.common.convert.multiple;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToDequeConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToDequeConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToDequeConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToDequeConverterTest.java
index e810092..3d4b785 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToDequeConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToDequeConverterTest.java
@@ -14,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
-import org.apache.dubbo.common.convert.multiple.StringToDequeConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToListConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToListConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToListConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToListConverterTest.java
index af9ee91..4258199 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToListConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToListConverterTest.java
@@ -14,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
-import org.apache.dubbo.common.convert.multiple.StringToListConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToNavigableSetConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToNavigableSetConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToNavigableSetConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToNavigableSetConverterTest.java
index face60d..e7e1660 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToNavigableSetConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToNavigableSetConverterTest.java
@@ -14,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
-import org.apache.dubbo.common.convert.multiple.StringToListConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
@@ -47,7 +45,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
- * {@link StringToListConverter} Test
+ * {@link StringToNavigableSetConverter} Test
  *
  * @since 2.7.6
  */
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToQueueConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToQueueConverterTest.java
similarity index 97%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToQueueConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToQueueConverterTest.java
index 539693a..2933d04 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToQueueConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToQueueConverterTest.java
@@ -14,9 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.StringToQueueConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSetConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToSetConverterTest.java
similarity index 97%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSetConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToSetConverterTest.java
index 269d709..5925cec 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSetConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToSetConverterTest.java
@@ -14,9 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.StringToSetConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSortedSetConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToSortedSetConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSortedSetConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToSortedSetConverterTest.java
index 6af8f9d..2ed1252 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSortedSetConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToSortedSetConverterTest.java
@@ -14,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
-import org.apache.dubbo.common.convert.multiple.StringToListConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
@@ -47,7 +45,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
- * {@link StringToListConverter} Test
+ * {@link StringToSortedSetConverter} Test
  *
  * @since 2.7.6
  */
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToTransferQueueConverterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToTransferQueueConverterTest.java
similarity index 95%
rename from 
dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToTransferQueueConverterTest.java
rename to 
dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToTransferQueueConverterTest.java
index 4d8d66b..e4cc101 100644
--- 
a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToTransferQueueConverterTest.java
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToTransferQueueConverterTest.java
@@ -14,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.convert.multiple;
+package org.apache.dubbo.common.convert.multiple;
 
-import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
-import org.apache.dubbo.common.convert.multiple.StringToListConverter;
 import org.apache.dubbo.common.utils.CollectionUtils;
 
 import org.junit.jupiter.api.BeforeEach;
@@ -48,7 +46,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
- * {@link StringToListConverter} Test
+ * {@link StringToTransferQueueConverter} Test
  *
  * @since 2.7.6
  */
diff --git 
a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulParameter.java
 
b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulParameter.java
new file mode 100644
index 0000000..f3fb024
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulParameter.java
@@ -0,0 +1,87 @@
+/*
+ * 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.dubbo.registry.consul;
+
+import org.apache.dubbo.common.URL;
+
+import static org.apache.dubbo.common.utils.StringUtils.isBlank;
+
+/**
+ * The enumeration for the Consul's parameters on the {@link URL}
+ *
+ * @see URL#getParameters()
+ * @since 2.7.8
+ */
+public enum ConsulParameter {
+
+    ACL_TOKEN,
+
+    TAGS,
+
+    INSTANCE_ZONE,
+
+    DEFAULT_ZONE_METADATA_NAME("zone"),
+
+    INSTANCE_GROUP,
+
+    CONSISTENCY_MODE,
+
+    ;
+
+    private final String name;
+
+    private final String defaultValue;
+
+    ConsulParameter() {
+        this(null);
+    }
+
+    ConsulParameter(String defaultValue) {
+        this(null, defaultValue);
+    }
+
+    ConsulParameter(String name, String defaultValue) {
+        this.name = isBlank(name) ? defaultName() : name;
+        this.defaultValue = defaultValue;
+    }
+
+    private String defaultName() {
+        return name().toLowerCase().replace('_', '-');
+    }
+
+    /**
+     * The parameter value from the specified registry {@link URL}
+     *
+     * @param registryURL the specified registry {@link URL}
+     * @return <code>defaultValue</code> if not found
+     */
+    public String getValue(URL registryURL) {
+        return registryURL.getParameter(name, defaultValue);
+    }
+
+    /**
+     * The parameter value from the specified registry {@link URL}
+     *
+     * @param registryURL  the specified registry {@link URL}
+     * @param valueType    the type of parameter value
+     * @param defaultValue the default value if parameter is absent
+     * @return <code>defaultValue</code> if not found
+     */
+    public <T> T getValue(URL registryURL, Class<T> valueType, T defaultValue) 
{
+        return registryURL.getParameter(name, valueType, defaultValue);
+    }
+}
diff --git 
a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java
 
b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java
index 5d5e98b..b05f1d8 100644
--- 
a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java
+++ 
b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java
@@ -29,10 +29,12 @@ import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import 
org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 
+import com.ecwid.consul.v1.ConsistencyMode;
 import com.ecwid.consul.v1.ConsulClient;
 import com.ecwid.consul.v1.QueryParams;
 import com.ecwid.consul.v1.Response;
 import com.ecwid.consul.v1.agent.model.NewService;
+import com.ecwid.consul.v1.catalog.CatalogServicesRequest;
 import com.ecwid.consul.v1.health.HealthServicesRequest;
 import com.ecwid.consul.v1.health.model.HealthService;
 
@@ -41,6 +43,7 @@ import java.util.Arrays;
 import java.util.Base64;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -53,6 +56,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 import static java.util.concurrent.Executors.newCachedThreadPool;
+import static 
org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR;
 import static 
org.apache.dubbo.common.constants.CommonConstants.SEMICOLON_SPLIT_PATTERN;
 import static 
org.apache.dubbo.registry.consul.AbstractConsulRegistry.CHECK_PASS_INTERVAL;
 import static 
org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_CHECK_PASS_INTERVAL;
@@ -61,6 +65,12 @@ import static 
org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_PO
 import static 
org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_WATCH_TIMEOUT;
 import static 
org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEREGISTER_AFTER;
 import static 
org.apache.dubbo.registry.consul.AbstractConsulRegistry.WATCH_TIMEOUT;
+import static org.apache.dubbo.registry.consul.ConsulParameter.ACL_TOKEN;
+import static 
org.apache.dubbo.registry.consul.ConsulParameter.CONSISTENCY_MODE;
+import static 
org.apache.dubbo.registry.consul.ConsulParameter.DEFAULT_ZONE_METADATA_NAME;
+import static org.apache.dubbo.registry.consul.ConsulParameter.INSTANCE_GROUP;
+import static org.apache.dubbo.registry.consul.ConsulParameter.INSTANCE_ZONE;
+import static org.apache.dubbo.registry.consul.ConsulParameter.TAGS;
 
 /**
  * 2019-07-31
@@ -82,6 +92,25 @@ public class ConsulServiceDiscovery implements 
ServiceDiscovery, EventListener<S
     private long checkPassInterval;
     private URL url;
 
+    private String aclToken;
+
+    private List<String> tags;
+
+    private ConsistencyMode consistencyMode;
+
+    private String defaultZoneMetadataName;
+
+    /**
+     * Service instance zone.
+     */
+    private String instanceZone;
+
+    /**
+     * Service instance group.
+     */
+    private String instanceGroup;
+
+
     @Override
     public void onEvent(ServiceInstancesChangedEvent event) {
 
@@ -97,6 +126,39 @@ public class ConsulServiceDiscovery implements 
ServiceDiscovery, EventListener<S
         ttlScheduler = new TtlScheduler(checkPassInterval, client);
         this.tag = registryURL.getParameter(QUERY_TAG);
         this.registeringTags.addAll(getRegisteringTags(url));
+        this.aclToken = ACL_TOKEN.getValue(registryURL);
+        this.tags = getTags(registryURL);
+        this.consistencyMode = getConsistencyMode(registryURL);
+        this.defaultZoneMetadataName = 
DEFAULT_ZONE_METADATA_NAME.getValue(registryURL);
+        this.instanceZone = INSTANCE_ZONE.getValue(registryURL);
+        this.instanceGroup = INSTANCE_GROUP.getValue(registryURL);
+    }
+
+    /**
+     * Get the {@link ConsistencyMode}
+     *
+     * @param registryURL the {@link URL} of registry
+     * @return non-null, {@link ConsistencyMode#DEFAULT} as default
+     * @sine 2.7.8
+     */
+    private ConsistencyMode getConsistencyMode(URL registryURL) {
+        String value = CONSISTENCY_MODE.getValue(registryURL);
+        if (StringUtils.isNotEmpty(value)) {
+            return ConsistencyMode.valueOf(value);
+        }
+        return ConsistencyMode.DEFAULT;
+    }
+
+    /**
+     * Get the "tags" from the {@link URL} of registry
+     *
+     * @param registryURL the {@link URL} of registry
+     * @return non-null
+     * @sine 2.7.8
+     */
+    private List<String> getTags(URL registryURL) {
+        String value = TAGS.getValue(registryURL);
+        return StringUtils.splitToList(value, COMMA_SEPARATOR_CHAR);
     }
 
     private List<String> getRegisteringTags(URL url) {
@@ -122,7 +184,7 @@ public class ConsulServiceDiscovery implements 
ServiceDiscovery, EventListener<S
     public void register(ServiceInstance serviceInstance) throws 
RuntimeException {
         NewService consulService = buildService(serviceInstance);
         ttlScheduler.add(consulService.getId());
-        client.agentServiceRegister(consulService);
+        client.agentServiceRegister(consulService, aclToken);
     }
 
     @Override
@@ -146,12 +208,16 @@ public class ConsulServiceDiscovery implements 
ServiceDiscovery, EventListener<S
     public void unregister(ServiceInstance serviceInstance) throws 
RuntimeException {
         String id = buildId(serviceInstance);
         ttlScheduler.remove(id);
-        client.agentServiceDeregister(id);
+        client.agentServiceDeregister(id, aclToken);
     }
 
     @Override
     public Set<String> getServices() {
-        return null;
+        CatalogServicesRequest request = CatalogServicesRequest.newBuilder()
+                .setQueryParams(QueryParams.DEFAULT)
+                .setToken(aclToken)
+                .build();
+        return this.client.getCatalogServices(request).getValue().keySet();
     }
 
     @Override
@@ -231,7 +297,6 @@ public class ConsulServiceDiscovery implements 
ServiceDiscovery, EventListener<S
         service.setName(serviceInstance.getServiceName());
         service.setCheck(buildCheck(serviceInstance));
         service.setTags(buildTags(serviceInstance));
-//        service.setMeta(buildMetadata(serviceInstance));
         return service;
     }
 
@@ -240,10 +305,21 @@ public class ConsulServiceDiscovery implements 
ServiceDiscovery, EventListener<S
     }
 
     private List<String> buildTags(ServiceInstance serviceInstance) {
+        List<String> tags = new LinkedList<>(this.tags);
+
+        if (StringUtils.isNotEmpty(instanceZone)) {
+            tags.add(defaultZoneMetadataName + "=" + instanceZone);
+        }
+
+        if (StringUtils.isNotEmpty(instanceGroup)) {
+            tags.add("group=" + instanceGroup);
+        }
+
         Map<String, String> params = serviceInstance.getMetadata();
-        List<String> tags = params.keySet().stream()
+        params.keySet().stream()
                 .map(k -> k + "=" + params.get(k))
-                .collect(Collectors.toList());
+                .forEach(tags::add);
+
         tags.addAll(registeringTags);
         return tags;
     }
@@ -281,7 +357,6 @@ public class ConsulServiceDiscovery implements 
ServiceDiscovery, EventListener<S
         check.setTtl((checkPassInterval / 1000) + "s");
         String deregister = 
serviceInstance.getMetadata().get(DEREGISTER_AFTER);
         check.setDeregisterCriticalServiceAfter(deregister == null ? 
DEFAULT_DEREGISTER_TIME : deregister);
-
         return check;
     }
 

Reply via email to