org.apache.dubbo.common.utils.UrlUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dubbo Show documentation
Show all versions of dubbo Show documentation
The all in one project of dubbo
/*
* 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.common.utils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.RemotingConstants;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.util.Collections.emptyMap;
import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
import static org.apache.dubbo.common.constants.CommonConstants.CLASSIFIER_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PASSWORD_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.REMOVE_VALUE_PREFIX;
import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.OVERRIDE_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
public class UrlUtils {
/**
* in the url string,mark the param begin
*/
private static final String URL_PARAM_STARTING_SYMBOL = "?";
public static URL parseURL(String address, Map defaults) {
if (StringUtils.isEmpty(address)) {
throw new IllegalArgumentException("Address is not allowed to be empty.");
}
String url;
if (address.contains("://") || address.contains(URL_PARAM_STARTING_SYMBOL)) {
url = address;
} else {
String[] addresses = COMMA_SPLIT_PATTERN.split(address);
url = addresses[0];
if (addresses.length > 1) {
StringBuilder backup = new StringBuilder();
for (int i = 1; i < addresses.length; i++) {
if (i > 1) {
backup.append(',');
}
backup.append(addresses[i]);
}
url += URL_PARAM_STARTING_SYMBOL + RemotingConstants.BACKUP_KEY + "=" + backup.toString();
}
}
String defaultProtocol = defaults == null ? null : defaults.get(PROTOCOL_KEY);
if (StringUtils.isEmpty(defaultProtocol)) {
defaultProtocol = DUBBO_PROTOCOL;
}
String defaultUsername = defaults == null ? null : defaults.get(USERNAME_KEY);
String defaultPassword = defaults == null ? null : defaults.get(PASSWORD_KEY);
int defaultPort = StringUtils.parseInteger(defaults == null ? null : defaults.get(PORT_KEY));
String defaultPath = defaults == null ? null : defaults.get(PATH_KEY);
Map defaultParameters = defaults == null ? null : new HashMap<>(defaults);
if (defaultParameters != null) {
defaultParameters.remove(PROTOCOL_KEY);
defaultParameters.remove(USERNAME_KEY);
defaultParameters.remove(PASSWORD_KEY);
defaultParameters.remove(HOST_KEY);
defaultParameters.remove(PORT_KEY);
defaultParameters.remove(PATH_KEY);
}
URL u = URL.valueOf(url);
boolean changed = false;
String protocol = u.getProtocol();
String username = u.getUsername();
String password = u.getPassword();
String host = u.getHost();
int port = u.getPort();
String path = u.getPath();
Map parameters = new HashMap<>(u.getParameters());
if (StringUtils.isEmpty(protocol)) {
changed = true;
protocol = defaultProtocol;
}
if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(defaultUsername)) {
changed = true;
username = defaultUsername;
}
if (StringUtils.isEmpty(password) && StringUtils.isNotEmpty(defaultPassword)) {
changed = true;
password = defaultPassword;
}
/*if (u.isAnyHost() || u.isLocalHost()) {
changed = true;
host = NetUtils.getLocalHost();
}*/
if (port <= 0) {
if (defaultPort > 0) {
changed = true;
port = defaultPort;
} else {
changed = true;
port = 9090;
}
}
if (StringUtils.isEmpty(path)) {
if (StringUtils.isNotEmpty(defaultPath)) {
changed = true;
path = defaultPath;
}
}
if (defaultParameters != null && defaultParameters.size() > 0) {
for (Map.Entry entry : defaultParameters.entrySet()) {
String key = entry.getKey();
String defaultValue = entry.getValue();
if (StringUtils.isNotEmpty(defaultValue)) {
String value = parameters.get(key);
if (StringUtils.isEmpty(value)) {
changed = true;
parameters.put(key, defaultValue);
}
}
}
}
if (changed) {
u = new URL(protocol, username, password, host, port, path, parameters);
}
return u;
}
public static List parseURLs(String address, Map defaults) {
if (StringUtils.isEmpty(address)) {
throw new IllegalArgumentException("Address is not allowed to be empty.");
}
String[] addresses = REGISTRY_SPLIT_PATTERN.split(address);
if (addresses == null || addresses.length == 0) {
throw new IllegalArgumentException("Addresses is not allowed to be empty."); //here won't be empty
}
List registries = new ArrayList<>();
for (String addr : addresses) {
registries.add(parseURL(addr, defaults));
}
return registries;
}
public static Map> convertRegister(Map> register) {
Map> newRegister = new HashMap<>();
for (Map.Entry> entry : register.entrySet()) {
String serviceName = entry.getKey();
Map serviceUrls = entry.getValue();
if (StringUtils.isNotContains(serviceName, ':') && StringUtils.isNotContains(serviceName, '/')) {
for (Map.Entry entry2 : serviceUrls.entrySet()) {
String serviceUrl = entry2.getKey();
String serviceQuery = entry2.getValue();
Map params = StringUtils.parseQueryString(serviceQuery);
String group = params.get("group");
String version = params.get("version");
//params.remove("group");
//params.remove("version");
String name = serviceName;
if (StringUtils.isNotEmpty(group)) {
name = group + "/" + name;
}
if (StringUtils.isNotEmpty(version)) {
name = name + ":" + version;
}
Map newUrls = newRegister.computeIfAbsent(name, k -> new HashMap<>());
newUrls.put(serviceUrl, StringUtils.toQueryString(params));
}
} else {
newRegister.put(serviceName, serviceUrls);
}
}
return newRegister;
}
public static Map convertSubscribe(Map subscribe) {
Map newSubscribe = new HashMap();
for (Map.Entry entry : subscribe.entrySet()) {
String serviceName = entry.getKey();
String serviceQuery = entry.getValue();
if (StringUtils.isNotContains(serviceName, ':') && StringUtils.isNotContains(serviceName, '/')) {
Map params = StringUtils.parseQueryString(serviceQuery);
String group = params.get("group");
String version = params.get("version");
//params.remove("group");
//params.remove("version");
String name = serviceName;
if (StringUtils.isNotEmpty(group)) {
name = group + "/" + name;
}
if (StringUtils.isNotEmpty(version)) {
name = name + ":" + version;
}
newSubscribe.put(name, StringUtils.toQueryString(params));
} else {
newSubscribe.put(serviceName, serviceQuery);
}
}
return newSubscribe;
}
public static Map> revertRegister(Map> register) {
Map> newRegister = new HashMap>();
for (Map.Entry> entry : register.entrySet()) {
String serviceName = entry.getKey();
Map serviceUrls = entry.getValue();
if (StringUtils.isContains(serviceName, ':') && StringUtils.isContains(serviceName, '/')) {
for (Map.Entry entry2 : serviceUrls.entrySet()) {
String serviceUrl = entry2.getKey();
String serviceQuery = entry2.getValue();
Map params = StringUtils.parseQueryString(serviceQuery);
String name = serviceName;
int i = name.indexOf('/');
if (i >= 0) {
params.put("group", name.substring(0, i));
name = name.substring(i + 1);
}
i = name.lastIndexOf(':');
if (i >= 0) {
params.put("version", name.substring(i + 1));
name = name.substring(0, i);
}
Map newUrls = newRegister.computeIfAbsent(name, k -> new HashMap());
newUrls.put(serviceUrl, StringUtils.toQueryString(params));
}
} else {
newRegister.put(serviceName, serviceUrls);
}
}
return newRegister;
}
public static Map revertSubscribe(Map subscribe) {
Map newSubscribe = new HashMap();
for (Map.Entry entry : subscribe.entrySet()) {
String serviceName = entry.getKey();
String serviceQuery = entry.getValue();
if (StringUtils.isContains(serviceName, ':') && StringUtils.isContains(serviceName, '/')) {
Map params = StringUtils.parseQueryString(serviceQuery);
String name = serviceName;
int i = name.indexOf('/');
if (i >= 0) {
params.put("group", name.substring(0, i));
name = name.substring(i + 1);
}
i = name.lastIndexOf(':');
if (i >= 0) {
params.put("version", name.substring(i + 1));
name = name.substring(0, i);
}
newSubscribe.put(name, StringUtils.toQueryString(params));
} else {
newSubscribe.put(serviceName, serviceQuery);
}
}
return newSubscribe;
}
public static Map> revertNotify(Map> notify) {
if (notify != null && notify.size() > 0) {
Map> newNotify = new HashMap>();
for (Map.Entry> entry : notify.entrySet()) {
String serviceName = entry.getKey();
Map serviceUrls = entry.getValue();
if (StringUtils.isNotContains(serviceName, ':') && StringUtils.isNotContains(serviceName, '/')) {
if (serviceUrls != null && serviceUrls.size() > 0) {
for (Map.Entry entry2 : serviceUrls.entrySet()) {
String url = entry2.getKey();
String query = entry2.getValue();
Map params = StringUtils.parseQueryString(query);
String group = params.get("group");
String version = params.get("version");
// params.remove("group");
// params.remove("version");
String name = serviceName;
if (StringUtils.isNotEmpty(group)) {
name = group + "/" + name;
}
if (StringUtils.isNotEmpty(version)) {
name = name + ":" + version;
}
Map newUrls = newNotify.computeIfAbsent(name, k -> new HashMap());
newUrls.put(url, StringUtils.toQueryString(params));
}
}
} else {
newNotify.put(serviceName, serviceUrls);
}
}
return newNotify;
}
return notify;
}
//compatible for dubbo-2.0.0
public static List revertForbid(List forbid, Set subscribed) {
if (CollectionUtils.isNotEmpty(forbid)) {
List newForbid = new ArrayList();
for (String serviceName : forbid) {
if (StringUtils.isNotContains(serviceName, ':') && StringUtils.isNotContains(serviceName, '/')) {
for (URL url : subscribed) {
if (serviceName.equals(url.getServiceInterface())) {
newForbid.add(url.getServiceKey());
break;
}
}
} else {
newForbid.add(serviceName);
}
}
return newForbid;
}
return forbid;
}
public static URL getEmptyUrl(String service, String category) {
String group = null;
String version = null;
int i = service.indexOf('/');
if (i > 0) {
group = service.substring(0, i);
service = service.substring(i + 1);
}
i = service.lastIndexOf(':');
if (i > 0) {
version = service.substring(i + 1);
service = service.substring(0, i);
}
return URL.valueOf(EMPTY_PROTOCOL + "://0.0.0.0/" + service + URL_PARAM_STARTING_SYMBOL
+ CATEGORY_KEY + "=" + category
+ (group == null ? "" : "&" + GROUP_KEY + "=" + group)
+ (version == null ? "" : "&" + VERSION_KEY + "=" + version));
}
public static boolean isMatchCategory(String category, String categories) {
if (StringUtils.isEmpty(categories)) {
return DEFAULT_CATEGORY.equals(category);
} else if (categories.contains(ANY_VALUE)) {
return true;
} else if (categories.contains(REMOVE_VALUE_PREFIX)) {
return !categories.contains(REMOVE_VALUE_PREFIX + category);
} else {
return categories.contains(category);
}
}
public static boolean isMatch(URL consumerUrl, URL providerUrl) {
String consumerInterface = consumerUrl.getServiceInterface();
String providerInterface = providerUrl.getServiceInterface();
//FIXME accept providerUrl with '*' as interface name, after carefully thought about all possible scenarios I think it's ok to add this condition.
if (!(ANY_VALUE.equals(consumerInterface)
|| ANY_VALUE.equals(providerInterface)
|| StringUtils.isEquals(consumerInterface, providerInterface))) {
return false;
}
if (!isMatchCategory(providerUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY),
consumerUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY))) {
return false;
}
if (!providerUrl.getParameter(ENABLED_KEY, true)
&& !ANY_VALUE.equals(consumerUrl.getParameter(ENABLED_KEY))) {
return false;
}
String consumerGroup = consumerUrl.getParameter(GROUP_KEY);
String consumerVersion = consumerUrl.getParameter(VERSION_KEY);
String consumerClassifier = consumerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
String providerGroup = providerUrl.getParameter(GROUP_KEY);
String providerVersion = providerUrl.getParameter(VERSION_KEY);
String providerClassifier = providerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
return (ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
&& (ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
&& (consumerClassifier == null || ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
}
public static boolean isMatchGlobPattern(String pattern, String value, URL param) {
if (Objects.nonNull(param) && pattern.startsWith("$")) {
pattern = param.getRawParameter(pattern.substring(1));
}
return isMatchGlobPattern(pattern, value);
}
public static boolean isMatchGlobPattern(String pattern, String value) {
if ("*".equals(pattern)) {
return true;
}
if (StringUtils.isEmpty(pattern) && StringUtils.isEmpty(value)) {
return true;
}
if (StringUtils.isEmpty(pattern) || StringUtils.isEmpty(value)) {
return false;
}
int i = pattern.lastIndexOf('*');
// doesn't find "*"
if (i == -1) {
return value.equals(pattern);
}
// "*" is at the end
else if (i == pattern.length() - 1) {
return value.startsWith(pattern.substring(0, i));
}
// "*" is at the beginning
else if (i == 0) {
return value.endsWith(pattern.substring(i + 1));
}
// "*" is in the middle
else {
String prefix = pattern.substring(0, i);
String suffix = pattern.substring(i + 1);
return value.startsWith(prefix) && value.endsWith(suffix);
}
}
public static boolean isServiceKeyMatch(URL pattern, URL value) {
return pattern.getParameter(INTERFACE_KEY).equals(
value.getParameter(INTERFACE_KEY))
&& isItemMatch(pattern.getParameter(GROUP_KEY),
value.getParameter(GROUP_KEY))
&& isItemMatch(pattern.getParameter(VERSION_KEY),
value.getParameter(VERSION_KEY));
}
public static List classifyUrls(List urls, Predicate predicate) {
return urls.stream().filter(predicate).collect(Collectors.toList());
}
public static boolean isConfigurator(URL url) {
return OVERRIDE_PROTOCOL.equals(url.getProtocol()) ||
CONFIGURATORS_CATEGORY.equals(url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY));
}
public static boolean isRoute(URL url) {
return ROUTE_PROTOCOL.equals(url.getProtocol()) ||
ROUTERS_CATEGORY.equals(url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY));
}
public static boolean isProvider(URL url) {
return !OVERRIDE_PROTOCOL.equals(url.getProtocol()) &&
!ROUTE_PROTOCOL.equals(url.getProtocol()) &&
PROVIDERS_CATEGORY.equals(url.getParameter(CATEGORY_KEY, PROVIDERS_CATEGORY));
}
public static boolean isRegistry(URL url) {
return REGISTRY_PROTOCOL.equals(url.getProtocol()) || SERVICE_REGISTRY_PROTOCOL.equalsIgnoreCase(url.getProtocol());
}
/**
* The specified {@link URL} is service discovery registry type or not
*
* @param url the {@link URL} connects to the registry
* @return If it is, return true
, or false
* @since 2.7.5
*/
public static boolean isServiceDiscoveryRegistryType(URL url) {
return isServiceDiscoveryRegistryType(url == null ? emptyMap() : url.getParameters());
}
/**
* The specified parameters of {@link URL} is service discovery registry type or not
*
* @param parameters the parameters of {@link URL} that connects to the registry
* @return If it is, return true
, or false
* @since 2.7.5
*/
public static boolean isServiceDiscoveryRegistryType(Map parameters) {
if (parameters == null || parameters.isEmpty()) {
return false;
}
return SERVICE_REGISTRY_TYPE.equals(parameters.get(REGISTRY_TYPE_KEY));
}
/**
* Check if the given value matches the given pattern. The pattern supports wildcard "*".
*
* @param pattern pattern
* @param value value
* @return true if match otherwise false
*/
static boolean isItemMatch(String pattern, String value) {
if (StringUtils.isEmpty(pattern)) {
return value == null;
} else {
return "*".equals(pattern) || pattern.equals(value);
}
}
/**
* @param serviceKey, {group}/{interfaceName}:{version}
* @return [group, interfaceName, version]
*/
public static String[] parseServiceKey(String serviceKey) {
String[] arr = new String[3];
int i = serviceKey.indexOf('/');
if (i > 0) {
arr[0] = serviceKey.substring(0, i);
serviceKey = serviceKey.substring(i + 1);
}
int j = serviceKey.indexOf(':');
if (j > 0) {
arr[2] = serviceKey.substring(j + 1);
serviceKey = serviceKey.substring(0, j);
}
arr[1] = serviceKey;
return arr;
}
}