package io.craft.atom.rpc; import io.craft.atom.protocol.rpc.model.RpcMessage; import io.craft.atom.protocol.rpc.model.RpcMethod; import io.craft.atom.rpc.api.RpcContext; import io.craft.atom.rpc.spi.RpcApi; import io.craft.atom.rpc.spi.RpcConnector; import io.craft.atom.rpc.spi.RpcInvoker; import io.craft.atom.rpc.spi.RpcRegistry; import java.lang.reflect.Method; import lombok.Getter; import lombok.Setter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.esotericsoftware.reflectasm.MethodAccess; /** * @author mindwind * @version 1.0, Aug 7, 2014 */ public class DefaultRpcServerInvoker implements RpcInvoker { private static final Logger LOG = LoggerFactory.getLogger(DefaultRpcServerInvoker.class); @Getter @Setter private RpcRegistry registry; @Override public RpcMessage invoke(RpcMessage req) throws RpcException { String rpcId = req.getBody().getRpcId(); Class<?> rpcInterface = req.getBody().getRpcInterface(); RpcMethod rpcMethod = req.getBody().getRpcMethod(); Class<?>[] paramTypes = rpcMethod.getParameterTypes(); Object[] params = rpcMethod.getParameters(); String methodName = rpcMethod.getName(); RpcApi api = registry.lookup(new DefaultRpcApi(rpcId, rpcInterface, rpcMethod)); if (api == null) { throw new RpcException(RpcException.SERVER_ERROR, "No exported api mapping"); } Object rpcObject = api.getRpcObject(); try { // Set rpc context RpcContext ctx = RpcContext.getContext(); ctx.setClientAddress(req.getClientAddress()); ctx.setServerAddress(req.getServerAddress()); ctx.setAttachments(req.getAttachments()); LOG.debug("[CRAFT-ATOM-RPC] Rpc server invoker is invoking, |rpcContext={}|", ctx); // Reflect invoke MethodAccess ma = MethodAccess.get(rpcInterface); int methodIndex = ma.getIndex(methodName, paramTypes); Object returnObject = ma.invoke(rpcObject, methodIndex, params); return RpcMessages.newRsponseRpcMessage(req.getId(), returnObject); } catch (Exception e) { LOG.warn("[CRAFT-ATOM-RPC] Rpc server invoker error", e); if (isDeclaredException(e, rpcInterface, methodName, paramTypes)) { return RpcMessages.newRsponseRpcMessage(req.getId(), e); } else { throw new RpcException(RpcException.SERVER_ERROR, "server error"); } } finally { RpcContext.removeContext(); } } private boolean isDeclaredException(Exception e, Class<?> rpcInterface, String methodName, Class<?>[] parameterTypes) { try { Method method = rpcInterface.getMethod(methodName, parameterTypes); Class<?>[] etypes = method.getExceptionTypes(); LOG.debug("[CRAFT-ATOM-RPC] Rpc server invoker throw exception, |declaredExceptions={}, thrownException={}|", etypes, e); for (Class<?> et : etypes) { if (et.equals(e.getClass())) return true; } } catch (Exception ex) { return false; } return false; } @Override public void setConnector(RpcConnector connector) {} }