org.apache.dubbo.metadata.MetadataInfo 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.metadata;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.compiler.support.ClassUtils;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
public class MetadataInfo implements Serializable {
public static String DEFAULT_REVISION = "0";
private String app;
private String revision;
private Map services;
// used at runtime
private transient Map extendParams;
private transient AtomicBoolean reported = new AtomicBoolean(false);
public MetadataInfo(String app) {
this(app, null, null);
}
public MetadataInfo(String app, String revision, Map services) {
this.app = app;
this.revision = revision;
this.services = services == null ? new HashMap<>() : services;
this.extendParams = new HashMap<>();
}
public void addService(ServiceInfo serviceInfo) {
if (serviceInfo == null) {
return;
}
this.services.put(serviceInfo.getMatchKey(), serviceInfo);
markChanged();
}
public void removeService(ServiceInfo serviceInfo) {
if (serviceInfo == null) {
return;
}
this.services.remove(serviceInfo.getMatchKey());
markChanged();
}
public void removeService(String key) {
if (key == null) {
return;
}
this.services.remove(key);
markChanged();
}
public String calAndGetRevision() {
if (revision != null && hasReported()) {
return revision;
}
if (CollectionUtils.isEmptyMap(services)) {
return DEFAULT_REVISION;
}
StringBuilder sb = new StringBuilder();
sb.append(app);
for (Map.Entry entry : services.entrySet()) {
sb.append(entry.getValue().toDescString());
}
this.revision = RevisionResolver.calRevision(sb.toString());
return revision;
}
public void setRevision(String revision) {
this.revision = revision;
}
public boolean hasReported() {
return reported.get();
}
public void markReported() {
reported.compareAndSet(false, true);
}
public void markChanged() {
reported.compareAndSet(true, false);
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public Map getServices() {
return services;
}
public void setServices(Map services) {
this.services = services;
}
public ServiceInfo getServiceInfo(String serviceKey) {
return services.get(serviceKey);
}
public Map getExtendParams() {
return extendParams;
}
public String getParameter(String key, String serviceKey) {
ServiceInfo serviceInfo = services.get(serviceKey);
if (serviceInfo == null) {
return null;
}
return serviceInfo.getParameter(key);
}
public Map getParameters(String serviceKey) {
ServiceInfo serviceInfo = services.get(serviceKey);
if (serviceInfo == null) {
return Collections.emptyMap();
}
return serviceInfo.getAllParams();
}
@Override
public String toString() {
return "metadata{" +
"app='" + app + "'," +
"revision='" + revision + "'," +
"services=" + services +
"}";
}
public static class ServiceInfo implements Serializable {
private static ExtensionLoader loader = ExtensionLoader.getExtensionLoader(MetadataParamsFilter.class);
private String name;
private String group;
private String version;
private String protocol;
private String path; // most of the time, path is the same with the interface name.
private Map params;
// params configured on consumer side,
private transient Map consumerParams;
// cached method params
private transient Map> methodParams;
private transient Map> consumerMethodParams;
// cached numbers
private transient Map numbers;
private transient Map> methodNumbers;
// service + group + version
private transient String serviceKey;
// service + group + version + protocol
private transient String matchKey;
private transient URL url;
public ServiceInfo() {
}
public ServiceInfo(URL url) {
this(url.getServiceInterface(), url.getParameter(GROUP_KEY), url.getParameter(VERSION_KEY), url.getProtocol(), url.getPath(), null);
this.url = url;
Map params = new HashMap<>();
List filters = loader.getActivateExtension(url, "params-filter");
for (MetadataParamsFilter filter : filters) {
String[] paramsIncluded = filter.serviceParamsIncluded();
if (ArrayUtils.isNotEmpty(paramsIncluded)) {
for (String p : paramsIncluded) {
String value = url.getParameter(p);
if (StringUtils.isNotEmpty(value) && params.get(p) == null) {
params.put(p, value);
}
String[] methods = url.getParameter(METHODS_KEY, (String[]) null);
if (methods != null) {
for (String method : methods) {
String mValue = url.getMethodParameterStrict(method, p);
if (StringUtils.isNotEmpty(mValue)) {
params.put(method + DOT_SEPARATOR + p, mValue);
}
}
}
}
}
}
this.params = params;
}
public ServiceInfo(String name, String group, String version, String protocol, String path, Map params) {
this.name = name;
this.group = group;
this.version = version;
this.protocol = protocol;
this.path = path;
this.params = params == null ? new HashMap<>() : params;
this.serviceKey = URL.buildKey(name, group, version);
this.matchKey = buildMatchKey();
}
public String getMatchKey() {
if (matchKey != null) {
return matchKey;
}
buildMatchKey();
return matchKey;
}
private String buildMatchKey() {
matchKey = getServiceKey();
if (StringUtils.isNotEmpty(protocol)) {
matchKey = getServiceKey() + GROUP_CHAR_SEPARATOR + protocol;
}
return matchKey;
}
public String getServiceKey() {
if (serviceKey != null) {
return serviceKey;
}
this.serviceKey = URL.buildKey(name, group, version);
return serviceKey;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Map getParams() {
if (params == null) {
return Collections.emptyMap();
}
return params;
}
public void setParams(Map params) {
this.params = params;
}
public Map getAllParams() {
if (consumerParams != null) {
Map allParams = new HashMap<>((int) ((params.size() + consumerParams.size()) / 0.75f + 1));
allParams.putAll(params);
allParams.putAll(consumerParams);
return allParams;
}
return params;
}
public String getParameter(String key) {
if (consumerParams != null) {
String value = consumerParams.get(key);
if (value != null) {
return value;
}
}
return params.get(key);
}
public String getMethodParameter(String method, String key, String defaultValue) {
// set consumerMethodParams firstly to avoid NPE at race condition.
if (methodParams == null) {
consumerMethodParams = URL.toMethodParameters(consumerParams);
methodParams = URL.toMethodParameters(params);
}
String value = getMethodParameter(method, key, consumerMethodParams);
if (value != null) {
return value;
}
value = getMethodParameter(method, key, methodParams);
return value == null ? defaultValue : value;
}
private String getMethodParameter(String method, String key, Map> map) {
Map keyMap = null;
if (CollectionUtils.isNotEmptyMap(map)) {
keyMap = map.get(method);
}
String value = null;
if (keyMap != null) {
value = keyMap.get(key);
}
if (StringUtils.isEmpty(value)) {
value = getParameter(key);
}
return value;
}
public boolean hasMethodParameter(String method, String key) {
String value = this.getMethodParameter(method, key, (String) null);
return StringUtils.isNotEmpty(value);
}
public boolean hasMethodParameter(String method) {
// set consumerMethodParams firstly to NPE at race condition.
if (methodParams == null) {
consumerMethodParams = URL.toMethodParameters(consumerParams);
methodParams = URL.toMethodParameters(params);
}
return (CollectionUtils.isNotEmptyMap(consumerMethodParams) && consumerMethodParams.containsKey(method))
|| (CollectionUtils.isNotEmptyMap(methodParams) && methodParams.containsKey(method));
}
public String toDescString() {
return this.getMatchKey() + getMethodSignaturesString() + getParams();
}
private String getMethodSignaturesString() {
SortedSet methodStrings = new TreeSet();
Method[] methods = ClassUtils.forName(name).getMethods();
for (Method method : methods) {
methodStrings.add(method.toString());
}
return methodStrings.toString();
}
public void addParameter(String key, String value) {
if (consumerParams != null) {
this.consumerParams.put(key, value);
}
}
public void addParameterIfAbsent(String key, String value) {
if (consumerParams != null) {
this.consumerParams.putIfAbsent(key, value);
}
}
public void addConsumerParams(Map params) {
// copy once for one service subscription
if (consumerParams == null) {
consumerParams = new HashMap<>(params);
}
}
public Map getNumbers() {
// concurrent initialization is tolerant
if (numbers == null) {
numbers = new ConcurrentHashMap<>();
}
return numbers;
}
public Map> getMethodNumbers() {
if (methodNumbers == null) { // concurrent initialization is tolerant
methodNumbers = new ConcurrentHashMap<>();
}
return methodNumbers;
}
public URL getUrl() {
return url;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof ServiceInfo)) {
return false;
}
ServiceInfo serviceInfo = (ServiceInfo) obj;
return this.getMatchKey().equals(serviceInfo.getMatchKey()) && this.getParams().equals(serviceInfo.getParams());
}
@Override
public int hashCode() {
return Objects.hash(getMatchKey(), getParams());
}
@Override
public String toString() {
return "service{" +
"name='" + name + "'," +
"group='" + group + "'," +
"version='" + version + "'," +
"protocol='" + protocol + "'," +
"params=" + params + "," +
"consumerParams=" + consumerParams +
"}";
}
}
}