org.noear.nami.NamiHandler Maven / Gradle / Ivy
/*
* Copyright 2017-2024 noear.org and authors
*
* 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
*
* https://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.noear.nami;
import org.noear.nami.annotation.NamiMapping;
import org.noear.nami.annotation.NamiClient;
import org.noear.nami.common.*;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Nami - 调用处理程序
*
* @author noear
* @since 1.0
* */
public class NamiHandler implements InvocationHandler {
private final static Pattern pathKeyExpr = Pattern.compile("\\{([^\\\\}]+)\\}");
private final Config config;
private final NamiClient client;
private final Map headers0 = new LinkedHashMap<>();
private final Class> clz0;
private final Map pathKeysCached = new ConcurrentHashMap<>();
/**
* @param config 配置
* @param client 客户端注解
*/
public NamiHandler(Class> clz, Config config, NamiClient client) {
this.config = config;
this.client = client;
this.clz0 = clz;
try {
init();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void init() throws Exception {
//1.运行配置器
if (client != null) {
//尝试添加全局拦截器
for (Filter mi : NamiManager.getFilters()) {
config.filterAdd(mi);
}
//尝试配置器配置
NamiConfiguration tmp = NamiManager.getConfigurator(client.configuration());
if (tmp != null) {
tmp.config(client, new NamiBuilder(config));
}
//尝试设置超时
if (client.timeout() > 0) {
config.setTimeout(client.timeout());
}
//尝试设置心跳
if (client.heartbeat() > 0) {
config.setHeartbeat(client.heartbeat());
}
//>>添加接口url
if (TextUtils.isNotEmpty(client.url())) {
config.setUrl(client.url());
}
//>>添加接口group
if (TextUtils.isNotEmpty(client.group())) {
config.setGroup(client.group());
}
//>>添加接口name
if (TextUtils.isNotEmpty(client.name())) {
config.setName(client.name());
}
//>>添加接口path
if (TextUtils.isNotEmpty(client.path())) {
config.setPath(client.path());
}
//>>添加接口header
if (client.headers().length > 0) {
for (String h : client.headers()) {
String[] ss = null;
if (h.contains(":")) {
ss = h.split(":");
} else {
ss = h.split("=");
}
if (ss.length == 2) {
headers0.put(ss[0].trim(), ss[1].trim());
config.setHeader(ss[0].trim(), ss[1].trim());
}
}
}
//>>添加upstream
if (client.upstream().length > 0) {
config.setUpstream(new UpstreamFixed(Arrays.asList(client.upstream())));
}
}
//2.配置初始化
config.init();
}
@Override
public Object invoke(Object proxy, Method method, Object[] vals) throws Throwable {
//检查upstream
if (TextUtils.isEmpty(config.getUrl()) && config.getUpstream() == null) {
StringBuilder buf = new StringBuilder();
buf.append("NamiClient: Not found upstream: ").append(clz0.getName());
if (TextUtils.isNotEmpty(config.getName())) {
buf.append(": '").append(config.getName()).append("'");
}
throw new NamiException(buf.toString());
}
//调用 Default 函数
if (method.isDefault()) {
return MethodHandlerUtils.invokeDefault(proxy, method, vals);
}
//调用 Object 函数
if (method.getDeclaringClass() == Object.class) {
return MethodHandlerUtils.invokeObject(clz0, proxy, method, vals);
}
MethodWrap methodWrap = MethodWrap.get(method);
//构建 headers
Map headers = new HashMap<>(headers0);
//构建 args
Map args = new LinkedHashMap<>();
Object body = null;
Parameter[] names = methodWrap.getParameters();
for (int i = 0, len = names.length; i < len; i++) {
if (vals[i] != null) {
args.put(names[i].getName(), vals[i]);
}
}
//确定body及默认编码
if (methodWrap.getBodyName() != null) {
body = args.get(methodWrap.getBodyName());
// body 已经单独处理了,从 args 中删除
args.remove(methodWrap.getBodyName());
if (config.getEncoder() == null) {
headers.putIfAbsent(Constants.HEADER_CONTENT_TYPE, methodWrap.getBodyAnno().contentType());
}
}
//构建 fun
String fun = method.getName();
String act = null;
//处理mapping
NamiMapping mapping = methodWrap.getMappingAnno();
if (mapping != null) {
if (methodWrap.getAct() != null) {
act = methodWrap.getAct();
}
if (methodWrap.getFun() != null) {
fun = methodWrap.getFun();
}
if (methodWrap.getMappingHeaders() != null) {
headers.putAll(methodWrap.getMappingHeaders());
}
}
//处理附加信息
Map contextMap = NamiAttachment.getData();
if (contextMap.size() > 0) {
headers.putAll(contextMap);
}
//构建 url
String url = null;
if (TextUtils.isEmpty(config.getUrl())) {
url = config.getUpstream().get();
if (url == null) {
StringBuilder buf = new StringBuilder();
buf.append("NamiClient: Upstream not found server instance: ").append(clz0.getName());
if (TextUtils.isEmpty(config.getName())) {
buf.append(": '").append(config.getName()).append("'");
}
throw new NamiException(buf.toString());
}
if (url.indexOf("://") < 0) {
url = "http://" + url;
}
if (TextUtils.isNotEmpty(config.getPath())) {
int idx = url.indexOf("/", 9);//https://a
if (idx > 0) {
url = url.substring(0, idx);
}
if (config.getPath().endsWith("/")) {
fun = config.getPath() + fun;
} else {
fun = config.getPath() + "/" + fun;
}
}
} else {
url = config.getUrl();
}
if (fun != null && fun.indexOf("{") > 0) {
//
//处理Path参数
//
Map pathKeys = buildPathKeys(fun);
for (Map.Entry kv : pathKeys.entrySet()) {
//
//处理path参数不为String类型时报错的问题
//String val = (String) args.get(kv.getValue());
//
Object arg = args.get(kv.getValue());
if (arg != null) {
String val = arg.toString();
fun = fun.replace(kv.getKey(), val);
args.remove(kv.getValue());
}
}
}
//确定返回类型
Type type = method.getGenericReturnType();
if (type == null) {
type = method.getReturnType();
}
if (url.startsWith("sd:")) {
url = url.substring(3);
}
//执行调用
Object rst = new Nami(config)
.method(proxy, method)
.action(act)
.url(url, fun)
.callOrThrow(headers, args, body) //使用 OrThrow,可减少异常包装
.getObjectOrThrow(type);
return rst;//调试时,方便看
}
private Map buildPathKeys(String path) {
Map pathKeys = pathKeysCached.computeIfAbsent(path, k -> {
Map pks = new LinkedHashMap<>();
Matcher pm = pathKeyExpr.matcher(path);
while (pm.find()) {
pks.put(pm.group(), pm.group(1));
}
return pks;
});
return pathKeys;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy