cloud.tianai.rpc.common.URL Maven / Gradle / Ivy
package cloud.tianai.rpc.common;
import cloud.tianai.rpc.common.util.CollectionUtils;
import lombok.Data;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
/**
* 拷贝dubbo源码
*
* "dubbo://127.0.0.1:20880/com.xxx.XxxService"
* "dubbo.version=2.0.0&group=test&version=1.0.0"
* dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=test
*/
@Data
public final class URL implements Serializable {
public static final String METHODS_KEY = "methods";
public static final String INTERFACE_KEY = "interface";
public static final String PATH_SEPARATOR = "/";
public static final String ANY_VALUE = "*";
public static final Pattern COMMA_SPLIT_PATTERN = Pattern.compile("\\s*[,]+\\s*");
/**
* 协议.
*/
private String protocol;
/**
* 用户名.
*/
private String username;
/**
* 密码.
*/
private String password;
/**
* ip地址.
*/
private String host;
/**
* 端口.
*/
private int port;
/**
* 路径.
*/
private String path;
/**
* 参数.
*/
private Map parameters;
/**
* 方法参数.
*/
private Map> methodParameters;
// ==== cache ====
private volatile transient Map numbers;
private volatile transient Map> methodNumbers;
private volatile transient Map urls;
private volatile transient String ip;
private volatile transient String full;
private volatile transient String identity;
private volatile transient String parameter;
private volatile transient String string;
private transient String serviceKey;
public URL() {
this.protocol = null;
this.username = null;
this.password = null;
this.host = null;
this.port = 0;
this.path = null;
this.parameters = new ConcurrentHashMap<>(16);
this.methodParameters = new ConcurrentHashMap<>(16);
}
public URL(String protocol, String host, int port) {
this(protocol, null, null, host, port, null, (Map) null);
}
public URL(String protocol, String host, int port, String[] pairs) { // varargs ... conflict with the following path argument, use array instead.
this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs));
}
public URL(String protocol, String host, int port, Map parameters) {
this(protocol, null, null, host, port, null, parameters);
}
public URL(String protocol, String host, int port, String path) {
this(protocol, null, null, host, port, path, (Map) null);
}
public URL(String protocol, String host, int port, String path, String... pairs) {
this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs));
}
public URL(String protocol, String host, int port, String path, Map parameters) {
this(protocol, null, null, host, port, path, parameters);
}
public URL(String protocol, String username, String password, String host, int port, String path) {
this(protocol, username, password, host, port, path, (Map) null);
}
public URL(String protocol, String username, String password, String host, int port, String path, String... pairs) {
this(protocol, username, password, host, port, path, CollectionUtils.toStringMap(pairs));
}
public URL(String protocol,
String username,
String password,
String host,
int port,
String path,
Map parameters) {
this(protocol, username, password, host, port, path, parameters, toMethodParameters(parameters));
}
public URL(String protocol,
String username,
String password,
String host,
int port,
String path,
Map parameters,
Map> methodParameters) {
if (StringUtils.isEmpty(username)
&& StringUtils.isNotEmpty(password)) {
throw new IllegalArgumentException("Invalid url, password without username!");
}
this.protocol = protocol;
this.username = username;
this.password = password;
this.host = host;
this.port = (port < 0 ? 0 : port);
// trim the beginning "/"
while (path != null && path.startsWith("/")) {
path = path.substring(1);
}
this.path = path;
if (parameters == null) {
parameters = new HashMap<>();
} else {
parameters = new HashMap<>(parameters);
}
this.parameters = Collections.unmodifiableMap(parameters);
this.methodParameters = Collections.unmodifiableMap(methodParameters);
}
/**
* 拷贝 dubbo源码
*
* @param url
* @return
*/
public static URL valueOf(String url) {
if (url == null || (url = url.trim()).length() == 0) {
throw new IllegalArgumentException("url == null");
}
String protocol = null;
String username = null;
String password = null;
String host = null;
int port = 0;
String path = null;
Map parameters = null;
int i = url.indexOf("?"); // separator between body and parameters
if (i >= 0) {
String[] parts = url.substring(i + 1).split("&");
parameters = new HashMap<>();
for (String part : parts) {
part = part.trim();
if (part.length() > 0) {
int j = part.indexOf('=');
if (j >= 0) {
parameters.put(part.substring(0, j), part.substring(j + 1));
} else {
parameters.put(part, part);
}
}
}
url = url.substring(0, i);
}
i = url.indexOf("://");
if (i >= 0) {
if (i == 0) {
throw new IllegalStateException("url missing protocol: \"" + url + "\"");
}
protocol = url.substring(0, i);
url = url.substring(i + 3);
} else {
// case: file:/path/to/file.txt
i = url.indexOf(":/");
if (i >= 0) {
if (i == 0) {
throw new IllegalStateException("url missing protocol: \"" + url + "\"");
}
protocol = url.substring(0, i);
url = url.substring(i + 1);
}
}
i = url.indexOf("/");
if (i >= 0) {
path = url.substring(i + 1);
url = url.substring(0, i);
}
i = url.lastIndexOf("@");
if (i >= 0) {
username = url.substring(0, i);
int j = username.indexOf(":");
if (j >= 0) {
password = username.substring(j + 1);
username = username.substring(0, j);
}
url = url.substring(i + 1);
}
i = url.lastIndexOf(":");
if (i >= 0 && i < url.length() - 1) {
if (url.lastIndexOf("%") > i) {
// ipv6 address with scope id
// e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0
// see https://howdoesinternetwork.com/2013/ipv6-zone-id
// ignore
} else {
port = Integer.parseInt(url.substring(i + 1));
url = url.substring(0, i);
}
}
if (url.length() > 0) {
host = url;
}
return new URL(protocol, username, password, host, port, path, parameters);
}
public static Map> toMethodParameters(Map parameters) {
Map> methodParameters = new HashMap<>();
if (parameters != null) {
String methodsString = parameters.get(METHODS_KEY);
if (StringUtils.isNotEmpty(methodsString)) {
String[] methods = methodsString.split(",");
for (Map.Entry entry : parameters.entrySet()) {
String key = entry.getKey();
for (String method : methods) {
String methodPrefix = method + ".";
if (key.startsWith(methodPrefix)) {
String realKey = key.substring(methodPrefix.length());
URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters);
}
}
}
} else {
for (Map.Entry entry : parameters.entrySet()) {
String key = entry.getKey();
int methodSeparator = key.indexOf(".");
if (methodSeparator > 0) {
String method = key.substring(0, methodSeparator);
String realKey = key.substring(methodSeparator + 1);
URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters);
}
}
}
}
return methodParameters;
}
public static void putMethodParameter(String method, String key, String value, Map> methodParameters) {
Map subParameter = methodParameters.computeIfAbsent(method, k -> new HashMap<>());
subParameter.put(key, value);
}
public static void main(String[] args) {
URL url = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=test");
String urlStr = url.toString();
System.out.println(url);
System.out.println(urlStr);
}
public String getAddress() {
return port <= 0 ? host : host + ":" + port;
}
public String getParameter(String key) {
return parameters.get(key);
}
public String getParameter(String key, String defaultValue) {
String value = getParameter(key);
return StringUtils.isEmpty(value) ? defaultValue : value;
}
public String[] getParameter(String key, String[] defaultValue) {
String value = getParameter(key);
return StringUtils.isEmpty(value) ? defaultValue : COMMA_SPLIT_PATTERN.split(value);
}
public String getServiceInterface() {
return getParameter(INTERFACE_KEY, path);
}
@Override
public String toString() {
if (string != null) {
return string;
}
return string = buildString(false, true); // no show username and password
}
private String buildString(boolean appendUser, boolean appendParameter, String... parameters) {
return buildString(appendUser, appendParameter, false, false, parameters);
}
private String buildString(boolean appendUser, boolean appendParameter, boolean useIP, boolean useService, String... parameters) {
StringBuilder buf = new StringBuilder();
if (StringUtils.isNotEmpty(protocol)) {
buf.append(protocol);
buf.append("://");
}
if (appendUser && StringUtils.isNotEmpty(username)) {
buf.append(username);
if (StringUtils.isNotEmpty(password)) {
buf.append(":");
buf.append(password);
}
buf.append("@");
}
String host;
if (useIP) {
host = getIp();
} else {
host = getHost();
}
if (StringUtils.isNotEmpty(host)) {
buf.append(host);
if (port > 0) {
buf.append(":");
buf.append(port);
}
}
String path;
if (useService) {
path = getServiceKey();
} else {
path = getPath();
}
if (StringUtils.isNotEmpty(path)) {
buf.append("/");
buf.append(path);
}
if (appendParameter) {
buildParameters(buf, true, parameters);
}
return buf.toString();
}
public String toFullString() {
if (full != null) {
return full;
}
return full = buildString(true, true);
}
private void buildParameters(StringBuilder buf, boolean concat, String[] parameters) {
if (CollectionUtils.isNotEmptyMap(getParameters())) {
List includes = (ArrayUtils.isEmpty(parameters) ? null : Arrays.asList(parameters));
boolean first = true;
for (Map.Entry entry : new TreeMap<>(getParameters()).entrySet()) {
if (StringUtils.isNotEmpty(entry.getKey())
&& (includes == null || includes.contains(entry.getKey()))) {
if (first) {
if (concat) {
buf.append("?");
}
first = false;
} else {
buf.append("&");
}
buf.append(entry.getKey());
buf.append("=");
buf.append(entry.getValue() == null ? "" : entry.getValue().trim());
}
}
}
}
public static String encode(String value) {
if (StringUtils.isEmpty(value)) {
return "";
}
try {
return URLEncoder.encode(value, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public static String decode(String value) {
if (StringUtils.isEmpty(value)) {
return "";
}
try {
return URLDecoder.decode(value, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public Integer getParameter(String key, int defaultValue) {
String value = getParameter(key);
if(value != null) {
return Integer.parseInt(value);
}
return defaultValue;
}
public void addParameter(String key, String value) {
parameters.put(key, value);
}
}