You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2018/11/07 06:24:59 UTC

[incubator-dubbo] branch 2.6.x updated: [DUBBO-2489] MockClusterInvoker provides local forced mock,I tested it locally, but it doesn't work (#2739)

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

liujun pushed a commit to branch 2.6.x
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git


The following commit(s) were added to refs/heads/2.6.x by this push:
     new 5da01b1  [DUBBO-2489] MockClusterInvoker provides local forced mock,I tested it locally, but it doesn't work (#2739)
5da01b1 is described below

commit 5da01b1d278bc723152e0ef7139b1b1fdab562f8
Author: Ian Luo <ia...@gmail.com>
AuthorDate: Wed Nov 7 14:24:53 2018 +0800

    [DUBBO-2489] MockClusterInvoker provides local forced mock,I tested it locally, but it doesn't work (#2739)
---
 .../com/alibaba/dubbo/config/AbstractConfig.java   |   2 +-
 .../dubbo/config/AbstractInterfaceConfig.java      |  53 ++++----
 .../alibaba/dubbo/config/AbstractMethodConfig.java |  10 +-
 .../com/alibaba/dubbo/config/ReferenceConfig.java  |   3 +-
 .../com/alibaba/dubbo/config/ServiceConfig.java    |   3 +-
 .../alibaba/dubbo/config/AbstractConfigTest.java   |   3 +-
 .../dubbo/config/AbstractInterfaceConfigTest.java  |  27 ++--
 .../com/alibaba/dubbo/rpc/support/MockInvoker.java | 144 +++++++++++++--------
 8 files changed, 151 insertions(+), 94 deletions(-)

diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
index 782215d..548205e 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
@@ -56,7 +56,7 @@ public abstract class AbstractConfig implements Serializable {
 
     private static final Pattern PATTERN_PATH = Pattern.compile("[/\\-$._0-9a-zA-Z]+");
 
-    private static final Pattern PATTERN_NAME_HAS_SYMBOL = Pattern.compile("[:*,/\\-._0-9a-zA-Z]+");
+    private static final Pattern PATTERN_NAME_HAS_SYMBOL = Pattern.compile("[:*,\\s/\\-._0-9a-zA-Z]+");
 
     private static final Pattern PATTERN_KEY = Pattern.compile("[*,\\-._0-9a-zA-Z]+");
     private static final Map<String, String> legacyProperties = new HashMap<String, String>();
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractInterfaceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractInterfaceConfig.java
index 87b50eb..4eec460 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractInterfaceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractInterfaceConfig.java
@@ -288,7 +288,36 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
         }
     }
 
-    protected void checkStubAndMock(Class<?> interfaceClass) {
+    void checkMock(Class<?> interfaceClass) {
+        if (ConfigUtils.isEmpty(mock)) {
+            return;
+        }
+
+        String normalizedMock = MockInvoker.normalizeMock(mock);
+        if (normalizedMock.startsWith(Constants.RETURN_PREFIX)) {
+            normalizedMock = normalizedMock.substring(Constants.RETURN_PREFIX.length()).trim();
+            try {
+                MockInvoker.parseMockValue(normalizedMock);
+            } catch (Exception e) {
+                throw new IllegalStateException("Illegal mock return in <dubbo:service/reference ... " +
+                        "mock=\"" + mock + "\" />");
+            }
+        } else if (normalizedMock.startsWith(Constants.THROW_PREFIX)) {
+            normalizedMock = normalizedMock.substring(Constants.THROW_PREFIX.length()).trim();
+            if (ConfigUtils.isNotEmpty(normalizedMock)) {
+                try {
+                    MockInvoker.getThrowable(normalizedMock);
+                } catch (Exception e) {
+                    throw new IllegalStateException("Illegal mock throw in <dubbo:service/reference ... " +
+                            "mock=\"" + mock + "\" />");
+                }
+            }
+        } else {
+            MockInvoker.getMockObject(normalizedMock, interfaceClass);
+        }
+    }
+
+    void checkStub(Class<?> interfaceClass) {
         if (ConfigUtils.isNotEmpty(local)) {
             Class<?> localClass = ConfigUtils.isDefault(local) ? ReflectUtils.forName(interfaceClass.getName() + "Local") : ReflectUtils.forName(local);
             if (!interfaceClass.isAssignableFrom(localClass)) {
@@ -311,26 +340,6 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
                 throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "(" + interfaceClass.getName() + ")\" in local implementation class " + localClass.getName());
             }
         }
-        if (ConfigUtils.isNotEmpty(mock)) {
-            if (mock.startsWith(Constants.RETURN_PREFIX)) {
-                String value = mock.substring(Constants.RETURN_PREFIX.length());
-                try {
-                    MockInvoker.parseMockValue(value);
-                } catch (Exception e) {
-                    throw new IllegalStateException("Illegal mock json value in <dubbo:service ... mock=\"" + mock + "\" />");
-                }
-            } else {
-                Class<?> mockClass = ConfigUtils.isDefault(mock) ? ReflectUtils.forName(interfaceClass.getName() + "Mock") : ReflectUtils.forName(mock);
-                if (!interfaceClass.isAssignableFrom(mockClass)) {
-                    throw new IllegalStateException("The mock implementation class " + mockClass.getName() + " not implement interface " + interfaceClass.getName());
-                }
-                try {
-                    mockClass.getConstructor(new Class<?>[0]);
-                } catch (NoSuchMethodException e) {
-                    throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implementation class " + mockClass.getName());
-                }
-            }
-        }
     }
 
     /**
@@ -525,4 +534,4 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
         this.scope = scope;
     }
 
-}
\ No newline at end of file
+}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java
index 266c5db..92df4a1 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java
@@ -127,8 +127,14 @@ public abstract class AbstractMethodConfig extends AbstractConfig {
     }
 
     public void setMock(String mock) {
-        if (mock != null && mock.startsWith(Constants.RETURN_PREFIX)) {
+        if (mock == null) {
+            return;
+        }
+
+        if (mock.startsWith(Constants.RETURN_PREFIX) || mock.startsWith(Constants.THROW_PREFIX + " ")) {
             checkLength("mock", mock);
+        } else if (mock.startsWith(Constants.FAIL_PREFIX) || mock.startsWith(Constants.FORCE_PREFIX)) {
+            checkNameHasSymbol("mock", mock);
         } else {
             checkName("mock", mock);
         }
@@ -168,4 +174,4 @@ public abstract class AbstractMethodConfig extends AbstractConfig {
         this.parameters = parameters;
     }
 
-}
\ No newline at end of file
+}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
index 5fe6c89..c761d9c 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
@@ -276,7 +276,8 @@ public class ReferenceConfig<T> extends AbstractReferenceConfig {
             }
         }
         checkApplication();
-        checkStubAndMock(interfaceClass);
+        checkStub(interfaceClass);
+        checkMock(interfaceClass);
         Map<String, String> map = new HashMap<String, String>();
         Map<Object, Object> attributes = new HashMap<Object, Object>();
         map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
index 846b216..8435db5 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
@@ -310,7 +310,8 @@ public class ServiceConfig<T> extends AbstractServiceConfig {
         checkRegistry();
         checkProtocol();
         appendProperties(this);
-        checkStubAndMock(interfaceClass);
+        checkStub(interfaceClass);
+        checkMock(interfaceClass);
         if (path == null || path.length() == 0) {
             path = interfaceName;
         }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/AbstractConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/AbstractConfigTest.java
index 765c429..2dc595f 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/AbstractConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/AbstractConfigTest.java
@@ -198,7 +198,8 @@ public class AbstractConfigTest {
     @Test
     public void checkNameHasSymbol() throws Exception {
         try {
-            AbstractConfig.checkNameHasSymbol("hello", ":*,/-0123abcdABCD");
+            AbstractConfig.checkNameHasSymbol("hello", ":*,/ -0123\tabcdABCD");
+            AbstractConfig.checkNameHasSymbol("mock", "force:return world");
         } catch (Exception e) {
             TestCase.fail("the value should be legal.");
         }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/AbstractInterfaceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/AbstractInterfaceConfigTest.java
index 4f08d29..7056c72 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/AbstractInterfaceConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/AbstractInterfaceConfigTest.java
@@ -178,63 +178,72 @@ public class AbstractInterfaceConfigTest {
     public void checkStubAndMock1() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setLocal(GreetingLocal1.class.getName());
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test(expected = IllegalStateException.class)
     public void checkStubAndMock2() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setLocal(GreetingLocal2.class.getName());
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test
     public void checkStubAndMock3() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setLocal(GreetingLocal3.class.getName());
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test(expected = IllegalStateException.class)
     public void checkStubAndMock4() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setStub(GreetingLocal1.class.getName());
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test(expected = IllegalStateException.class)
     public void checkStubAndMock5() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setStub(GreetingLocal2.class.getName());
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test
     public void checkStubAndMock6() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setStub(GreetingLocal3.class.getName());
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test(expected = IllegalStateException.class)
     public void checkStubAndMock7() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setMock("return {a, b}");
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test(expected = IllegalStateException.class)
     public void checkStubAndMock8() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setMock(GreetingMock1.class.getName());
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test(expected = IllegalStateException.class)
     public void checkStubAndMock9() throws Exception {
         InterfaceConfig interfaceConfig = new InterfaceConfig();
         interfaceConfig.setMock(GreetingMock2.class.getName());
-        interfaceConfig.checkStubAndMock(Greeting.class);
+        interfaceConfig.checkStub(Greeting.class);
+        interfaceConfig.checkMock(Greeting.class);
     }
 
     @Test
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
index 7ee2b20..fc99bdc 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
@@ -96,26 +96,21 @@ final public class MockInvoker<T> implements Invoker<T> {
         if (StringUtils.isBlank(mock)) {
             throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));
         }
-        mock = normallizeMock(URL.decode(mock));
-        if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mock.trim())) {
-            RpcResult result = new RpcResult();
-            result.setValue(null);
-            return result;
-        } else if (mock.startsWith(Constants.RETURN_PREFIX)) {
+        mock = normalizeMock(URL.decode(mock));
+        if (mock.startsWith(Constants.RETURN_PREFIX)) {
             mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();
-            mock = mock.replace('`', '"');
             try {
                 Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
                 Object value = parseMockValue(mock, returnTypes);
                 return new RpcResult(value);
             } catch (Exception ew) {
-                throw new RpcException("mock return invoke error. method :" + invocation.getMethodName() + ", mock:" + mock + ", url: " + url, ew);
+                throw new RpcException("mock return invoke error. method :" + invocation.getMethodName()
+                        + ", mock:" + mock + ", url: " + url, ew);
             }
         } else if (mock.startsWith(Constants.THROW_PREFIX)) {
             mock = mock.substring(Constants.THROW_PREFIX.length()).trim();
-            mock = mock.replace('`', '"');
             if (StringUtils.isBlank(mock)) {
-                throw new RpcException(" mocked exception for Service degradation. ");
+                throw new RpcException("mocked exception for service degradation.");
             } else { // user customized class
                 Throwable t = getThrowable(mock);
                 throw new RpcException(RpcException.BIZ_EXCEPTION, t);
@@ -125,29 +120,29 @@ final public class MockInvoker<T> implements Invoker<T> {
                 Invoker<T> invoker = getInvoker(mock);
                 return invoker.invoke(invocation);
             } catch (Throwable t) {
-                throw new RpcException("Failed to create mock implemention class " + mock, t);
+                throw new RpcException("Failed to create mock implementation class " + mock, t);
             }
         }
     }
 
-    private Throwable getThrowable(String throwstr) {
-        Throwable throwable = (Throwable) throwables.get(throwstr);
+    public static Throwable getThrowable(String throwstr) {
+        Throwable throwable = throwables.get(throwstr);
         if (throwable != null) {
             return throwable;
-        } else {
-            Throwable t = null;
-            try {
-                Class<?> bizException = ReflectUtils.forName(throwstr);
-                Constructor<?> constructor;
-                constructor = ReflectUtils.findConstructor(bizException, String.class);
-                t = (Throwable) constructor.newInstance(new Object[]{" mocked exception for Service degradation. "});
-                if (throwables.size() < 1000) {
-                    throwables.put(throwstr, t);
-                }
-            } catch (Exception e) {
-                throw new RpcException("mock throw error :" + throwstr + " argument error.", e);
+        }
+
+        try {
+            Throwable t;
+            Class<?> bizException = ReflectUtils.forName(throwstr);
+            Constructor<?> constructor;
+            constructor = ReflectUtils.findConstructor(bizException, String.class);
+            t = (Throwable) constructor.newInstance(new Object[]{"mocked exception for service degradation."});
+            if (throwables.size() < 1000) {
+                throwables.put(throwstr, t);
             }
             return t;
+        } catch (Exception e) {
+            throw new RpcException("mock throw error :" + throwstr + " argument error.", e);
         }
     }
 
@@ -156,49 +151,84 @@ final public class MockInvoker<T> implements Invoker<T> {
         Invoker<T> invoker = (Invoker<T>) mocks.get(mockService);
         if (invoker != null) {
             return invoker;
-        } else {
-            Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface());
-            if (ConfigUtils.isDefault(mockService)) {
-                mockService = serviceType.getName() + "Mock";
-            }
+        }
 
-            Class<?> mockClass = ReflectUtils.forName(mockService);
-            if (!serviceType.isAssignableFrom(mockClass)) {
-                throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
-            }
+        Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface());
+        T mockObject = (T) getMockObject(mockService, serviceType);
+        invoker = proxyFactory.getInvoker(mockObject, serviceType, url);
+        if (mocks.size() < 10000) {
+            mocks.put(mockService, invoker);
+        }
+        return invoker;
+    }
 
-            if (!serviceType.isAssignableFrom(mockClass)) {
-                throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
-            }
-            try {
-                T mockObject = (T) mockClass.newInstance();
-                invoker = proxyFactory.getInvoker(mockObject, (Class<T>) serviceType, url);
-                if (mocks.size() < 10000) {
-                    mocks.put(mockService, invoker);
-                }
-                return invoker;
-            } catch (InstantiationException e) {
-                throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);
-            } catch (IllegalAccessException e) {
-                throw new IllegalStateException(e);
-            }
+    @SuppressWarnings("unchecked")
+    public static Object getMockObject(String mockService, Class serviceType) {
+        if (ConfigUtils.isDefault(mockService)) {
+            mockService = serviceType.getName() + "Mock";
+        }
+
+        Class<?> mockClass = ReflectUtils.forName(mockService);
+        if (!serviceType.isAssignableFrom(mockClass)) {
+            throw new IllegalStateException("The mock class " + mockClass.getName() +
+                    " not implement interface " + serviceType.getName());
+        }
+
+        try {
+            return mockClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new IllegalStateException("No default constructor from mock class " + mockClass.getName(), e);
+        } catch (IllegalAccessException e) {
+            throw new IllegalStateException(e);
         }
     }
 
-    //mock=fail:throw
-    //mock=fail:return
-    //mock=xx.Service
-    private String normallizeMock(String mock) {
-        if (mock == null || mock.trim().length() == 0) {
+
+    /**
+     * Normalize mock string:
+     *
+     * <ol>
+     * <li>return => return null</li>
+     * <li>fail => default</li>
+     * <li>force => default</li>
+     * <li>fail:throw/return foo => throw/return foo</li>
+     * <li>force:throw/return foo => throw/return foo</li>
+     * </ol>
+     *
+     * @param mock mock string
+     * @return normalized mock string
+     */
+    public static String normalizeMock(String mock) {
+        if (mock == null) {
             return mock;
-        } else if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock.trim()) || "force".equalsIgnoreCase(mock.trim())) {
-            mock = url.getServiceInterface() + "Mock";
         }
+
+        mock = mock.trim();
+
+        if (mock.length() == 0) {
+            return mock;
+        }
+
+        if (Constants.RETURN_KEY.equalsIgnoreCase(mock)) {
+            return Constants.RETURN_PREFIX + "null";
+        }
+
+        if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock) || "force".equalsIgnoreCase(mock)) {
+            return "default";
+        }
+
         if (mock.startsWith(Constants.FAIL_PREFIX)) {
             mock = mock.substring(Constants.FAIL_PREFIX.length()).trim();
-        } else if (mock.startsWith(Constants.FORCE_PREFIX)) {
+        }
+
+        if (mock.startsWith(Constants.FORCE_PREFIX)) {
             mock = mock.substring(Constants.FORCE_PREFIX.length()).trim();
         }
+
+        if (mock.startsWith(Constants.RETURN_PREFIX) || mock.startsWith(Constants.THROW_PREFIX)) {
+            mock = mock.replace('`', '"');
+        }
+
         return mock;
     }