blob: 5e9e74a9a6bcc29f0f1912b8a26589c6f7fe2b97 [file] [log] [blame]
/*
* 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.rpc.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ReflectUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.InvokeMode;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.service.GenericService;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE;
import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE_ASYNC;
import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_PARAMETER_DESC;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_ATTACHMENT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
import static org.apache.dubbo.rpc.Constants.$ECHO;
import static org.apache.dubbo.rpc.Constants.$ECHO_PARAMETER_DESC;
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY;
import static org.apache.dubbo.rpc.Constants.ID_KEY;
import static org.apache.dubbo.rpc.Constants.RETURN_KEY;
/**
* RpcUtils
*/
public class RpcUtils {
private static final Logger logger = LoggerFactory.getLogger(RpcUtils.class);
private static final AtomicLong INVOKE_ID = new AtomicLong(0);
public static Class<?> getReturnType(Invocation invocation) {
try {
if (invocation != null && invocation.getInvoker() != null
&& invocation.getInvoker().getUrl() != null
&& invocation.getInvoker().getInterface() != GenericService.class
&& !invocation.getMethodName().startsWith("$")) {
String service = invocation.getInvoker().getUrl().getServiceInterface();
if (StringUtils.isNotEmpty(service)) {
Method method = getMethodByService(invocation, service);
return method.getReturnType();
}
}
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
return null;
}
public static Type[] getReturnTypes(Invocation invocation) {
try {
if (invocation != null && invocation.getInvoker() != null
&& invocation.getInvoker().getUrl() != null
&& invocation.getInvoker().getInterface() != GenericService.class
&& !invocation.getMethodName().startsWith("$")) {
String service = invocation.getInvoker().getUrl().getServiceInterface();
if (StringUtils.isNotEmpty(service)) {
Method method = getMethodByService(invocation, service);
return ReflectUtils.getReturnTypes(method);
}
}
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
return null;
}
public static Long getInvocationId(Invocation inv) {
String id = inv.getAttachment(ID_KEY);
return id == null ? null : new Long(id);
}
/**
* Idempotent operation: invocation id will be added in async operation by default
*
* @param url
* @param inv
*/
public static void attachInvocationIdIfAsync(URL url, Invocation inv) {
if (isAttachInvocationId(url, inv) && getInvocationId(inv) == null && inv instanceof RpcInvocation) {
inv.setAttachment(ID_KEY, String.valueOf(INVOKE_ID.getAndIncrement()));
}
}
private static boolean isAttachInvocationId(URL url, Invocation invocation) {
String value = url.getMethodParameter(invocation.getMethodName(), AUTO_ATTACH_INVOCATIONID_KEY);
if (value == null) {
// add invocationid in async operation by default
return isAsync(url, invocation);
} else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
return true;
} else {
return false;
}
}
public static String getMethodName(Invocation invocation) {
if ($INVOKE.equals(invocation.getMethodName())
&& invocation.getArguments() != null
&& invocation.getArguments().length > 0
&& invocation.getArguments()[0] instanceof String) {
return (String) invocation.getArguments()[0];
}
return invocation.getMethodName();
}
public static Object[] getArguments(Invocation invocation) {
if ($INVOKE.equals(invocation.getMethodName())
&& invocation.getArguments() != null
&& invocation.getArguments().length > 2
&& invocation.getArguments()[2] instanceof Object[]) {
return (Object[]) invocation.getArguments()[2];
}
return invocation.getArguments();
}
public static Class<?>[] getParameterTypes(Invocation invocation) {
if ($INVOKE.equals(invocation.getMethodName())
&& invocation.getArguments() != null
&& invocation.getArguments().length > 1
&& invocation.getArguments()[1] instanceof String[]) {
String[] types = (String[]) invocation.getArguments()[1];
if (types == null) {
return new Class<?>[0];
}
Class<?>[] parameterTypes = new Class<?>[types.length];
for (int i = 0; i < types.length; i++) {
parameterTypes[i] = ReflectUtils.forName(types[0]);
}
return parameterTypes;
}
return invocation.getParameterTypes();
}
public static boolean isAsync(URL url, Invocation inv) {
boolean isAsync;
if (inv instanceof RpcInvocation) {
RpcInvocation rpcInvocation = (RpcInvocation) inv;
if (rpcInvocation.getInvokeMode() != null) {
return rpcInvocation.getInvokeMode() == InvokeMode.ASYNC;
}
}
if (Boolean.TRUE.toString().equals(inv.getAttachment(ASYNC_KEY))) {
isAsync = true;
} else {
isAsync = url.getMethodParameter(getMethodName(inv), ASYNC_KEY, false);
}
return isAsync;
}
public static boolean isReturnTypeFuture(Invocation inv) {
Class<?> clazz;
if (inv instanceof RpcInvocation) {
clazz = ((RpcInvocation) inv).getReturnType();
} else {
clazz = getReturnType(inv);
}
return (clazz != null && CompletableFuture.class.isAssignableFrom(clazz)) || isGenericAsync(inv);
}
public static boolean isGenericAsync(Invocation inv) {
return $INVOKE_ASYNC.equals(inv.getMethodName());
}
// check parameterTypesDesc to fix CVE-2020-1948
public static boolean isGenericCall(String parameterTypesDesc, String method) {
return ($INVOKE.equals(method) || $INVOKE_ASYNC.equals(method)) && GENERIC_PARAMETER_DESC.equals(parameterTypesDesc);
}
// check parameterTypesDesc to fix CVE-2020-1948
public static boolean isEcho(String parameterTypesDesc, String method) {
return $ECHO.equals(method) && $ECHO_PARAMETER_DESC.equals(parameterTypesDesc);
}
public static InvokeMode getInvokeMode(URL url, Invocation inv) {
if (inv instanceof RpcInvocation) {
RpcInvocation rpcInvocation = (RpcInvocation) inv;
if (rpcInvocation.getInvokeMode() != null) {
return rpcInvocation.getInvokeMode();
}
}
if (isReturnTypeFuture(inv)) {
return InvokeMode.FUTURE;
} else if (isAsync(url, inv)) {
return InvokeMode.ASYNC;
} else {
return InvokeMode.SYNC;
}
}
public static boolean isOneway(URL url, Invocation inv) {
boolean isOneway;
if (Boolean.FALSE.toString().equals(inv.getAttachment(RETURN_KEY))) {
isOneway = true;
} else {
isOneway = !url.getMethodParameter(getMethodName(inv), RETURN_KEY, true);
}
return isOneway;
}
private static Method getMethodByService(Invocation invocation, String service) throws NoSuchMethodException {
Class<?> invokerInterface = invocation.getInvoker().getInterface();
Class<?> cls = invokerInterface != null ? ReflectUtils.forName(invokerInterface.getClassLoader(), service)
: ReflectUtils.forName(service);
Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes());
if (method.getReturnType() == void.class) {
return null;
}
return method;
}
public static long getTimeout(Invocation invocation, long defaultTimeout) {
long timeout = defaultTimeout;
Object genericTimeout = invocation.getObjectAttachment(TIMEOUT_ATTACHMENT_KEY);
if (genericTimeout != null) {
timeout = convertToNumber(genericTimeout, defaultTimeout);
}
return timeout;
}
public static long getTimeout(URL url, String methodName, RpcContext context, long defaultTimeout) {
long timeout = defaultTimeout;
Object genericTimeout = context.getObjectAttachment(TIMEOUT_KEY);
if (genericTimeout != null) {
timeout = convertToNumber(genericTimeout, defaultTimeout);
} else if (url != null) {
timeout = url.getMethodPositiveParameter(methodName, TIMEOUT_KEY, defaultTimeout);
}
return timeout;
}
private static long convertToNumber(Object obj, long defaultTimeout) {
long timeout = 0;
try {
if (obj instanceof String) {
timeout = Long.parseLong((String) obj);
} else if (obj instanceof Number) {
timeout = ((Number) obj).longValue();
} else {
timeout = Long.parseLong(obj.toString());
}
} catch (Exception e) {
// ignore
}
return timeout;
}
}