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;
}