blob: 9ecb9f6bff0359b0475815f185c167bc90ddf2c6 [file] [log] [blame]
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed 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 com.alibaba.dubbo.rpc.support;
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.json.JSON;
import com.alibaba.dubbo.common.utils.ConfigUtils;
import com.alibaba.dubbo.common.utils.PojoUtils;
import com.alibaba.dubbo.common.utils.ReflectUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.ProxyFactory;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.RpcInvocation;
import com.alibaba.dubbo.rpc.RpcResult;
/**
* @author chao.liuc
* @author william.liangf
*
*/
final public class MockInvoker<T> implements Invoker<T> {
private final static ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
private final static Map<String, Invoker<?>> mocks = new ConcurrentHashMap<String, Invoker<?>>();
private final static Map<String, Throwable> throwables = new ConcurrentHashMap<String, Throwable>();
private final URL url ;
public MockInvoker(URL url) {
this.url = url;
}
public Result invoke(Invocation invocation) throws RpcException {
String mock = getUrl().getParameter(invocation.getMethodName()+"."+Constants.MOCK_KEY);
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(this);
}
if (StringUtils.isBlank(mock)){
mock = getUrl().getParameter(Constants.MOCK_KEY);
}
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 = 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 .invocation :" + invocation + ", 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. ");
} else { //用户自定义类
Throwable t = getThrowable(mock);
throw new RpcException(RpcException.BIZ_EXCEPTION, t);
}
} else { //impl mock
try {
Invoker<T> invoker = getInvoker(mock);
return invoker.invoke(invocation);
} catch (Throwable t) {
throw new RpcException("Failed to create mock implemention class " + mock , t);
}
}
}
private Throwable getThrowable(String throwstr){
Throwable 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);
}
return t;
}
}
@SuppressWarnings("unchecked")
private Invoker<T> getInvoker(String mockService){
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());
}
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);
}
}
}
//mock=fail:throw
//mock=fail:return
//mock=xx.Service
private String normallizeMock(String mock) {
if (mock == null || mock.trim().length() ==0){
return mock;
} else if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock.trim()) || "force".equalsIgnoreCase(mock.trim())){
mock = url.getServiceInterface()+"Mock";
}
if (mock.startsWith(Constants.FAIL_PREFIX)) {
mock = mock.substring(Constants.FAIL_PREFIX.length()).trim();
} else if (mock.startsWith(Constants.FORCE_PREFIX)) {
mock = mock.substring(Constants.FORCE_PREFIX.length()).trim();
}
return mock;
}
public static Object parseMockValue(String mock) throws Exception {
return parseMockValue(mock, null);
}
public static Object parseMockValue(String mock, Type[] returnTypes) throws Exception {
Object value = null;
if ("empty".equals(mock)) {
value = ReflectUtils.getEmptyObject(returnTypes != null && returnTypes.length > 0 ? (Class<?>)returnTypes[0] : null);
} else if ("null".equals(mock)) {
value = null;
} else if ("true".equals(mock)) {
value = true;
} else if ("false".equals(mock)) {
value = false;
} else if (mock.length() >=2 && (mock.startsWith("\"") && mock.endsWith("\"")
|| mock.startsWith("\'") && mock.endsWith("\'"))) {
value = mock.subSequence(1, mock.length() - 1);
} else if (returnTypes !=null && returnTypes.length >0 && returnTypes[0] == String.class) {
value = mock;
} else if (StringUtils.isNumeric(mock)) {
value = JSON.parse(mock);
}else if (mock.startsWith("{")) {
value = JSON.parse(mock, Map.class);
} else if (mock.startsWith("[")) {
value = JSON.parse(mock, List.class);
} else {
value = mock;
}
if (returnTypes != null && returnTypes.length > 0) {
value = PojoUtils.realize(value, (Class<?>)returnTypes[0], returnTypes.length > 1 ? returnTypes[1] : null);
}
return value;
}
public URL getUrl() {
return this.url;
}
public boolean isAvailable() {
return true;
}
public void destroy() {
//do nothing
}
public Class<T> getInterface() {
//FIXME
return null;
}
}