filters = loader.getActivateExtension(url, "params-filter");
- // generate service level metadata
- ServiceInfo serviceInfo = new ServiceInfo(url, filters);
- this.services.put(serviceInfo.getMatchKey(), serviceInfo);
- // extract common instance level params
- extractInstanceParams(url, filters);
-
- if (exportedServiceURLs == null) {
- exportedServiceURLs = new ConcurrentSkipListMap<>();
- }
- addURL(exportedServiceURLs, url);
- updated = true;
- }
-
- public synchronized void removeService(URL url) {
- if (url == null) {
- return;
- }
- this.services.remove(url.getProtocolServiceKey());
- if (exportedServiceURLs != null) {
- removeURL(exportedServiceURLs, url);
- }
-
- updated = true;
- }
-
- public String getRevision() {
- return revision;
- }
-
- /**
- * Calculation of this instance's status like revision and modification of the same instance must be synchronized among different threads.
- *
- * Usage of this method is strictly restricted to certain points such as when during registration. Always try to use {@link this#getRevision()} instead.
- */
- public synchronized String calAndGetRevision() {
- if (revision != null && !updated) {
- return revision;
- }
-
- updated = false;
-
- if (CollectionUtils.isEmptyMap(services)) {
- this.revision = EMPTY_REVISION;
- } else {
- StringBuilder sb = new StringBuilder();
- sb.append(app);
- for (Map.Entry entry : new TreeMap<>(services).entrySet()) {
- sb.append(entry.getValue().toDescString());
- }
- String tempRevision = RevisionResolver.calRevision(sb.toString());
- if (!StringUtils.isEquals(this.revision, tempRevision)) {
- if (logger.isInfoEnabled()) {
- logger.info(String.format("metadata revision changed: %s -> %s, app: %s, services: %d", this.revision, tempRevision, this.app, this.services.size()));
- }
- this.revision = tempRevision;
- this.rawMetadataInfo = JsonUtils.getJson().toJson(this);
- }
- }
- return revision;
- }
-
- public void setRevision(String revision) {
- this.revision = revision;
- }
-
- @Transient
- public String getContent() {
- return this.rawMetadataInfo;
- }
-
- public String getApp() {
- return app;
- }
-
- public void setApp(String app) {
- this.app = app;
- }
-
- public Map getServices() {
- return services;
- }
-
- /**
- * Get service info of an interface with specified group, version and protocol
- * @param protocolServiceKey key is of format '{group}/{interface name}:{version}:{protocol}'
- * @return the specific service info related to protocolServiceKey
- */
- public ServiceInfo getServiceInfo(String protocolServiceKey) {
- return services.get(protocolServiceKey);
- }
-
- /**
- * Get service infos of an interface with specified group, version.
- * There may have several service infos of different protocols, this method will simply pick the first one.
- *
- * @param serviceKeyWithoutProtocol key is of format '{group}/{interface name}:{version}'
- * @return the first service info related to serviceKey
- */
- public ServiceInfo getNoProtocolServiceInfo(String serviceKeyWithoutProtocol) {
- if (CollectionUtils.isEmptyMap(subscribedServices)) {
- return null;
- }
- Set subServices = subscribedServices.get(serviceKeyWithoutProtocol);
- if (CollectionUtils.isNotEmpty(subServices)) {
- return subServices.iterator().next();
- }
- return null;
- }
-
- public ServiceInfo getValidServiceInfo(String serviceKey) {
- ServiceInfo serviceInfo = getServiceInfo(serviceKey);
- if (serviceInfo == null) {
- serviceInfo = getNoProtocolServiceInfo(serviceKey);
- if (serviceInfo == null) {
- return null;
- }
- }
- return serviceInfo;
- }
-
- public List getMatchedServiceInfos(ProtocolServiceKey consumerProtocolServiceKey) {
- return getServices().values()
- .stream()
- .filter(serviceInfo -> serviceInfo.matchProtocolServiceKey(consumerProtocolServiceKey))
- .collect(Collectors.toList());
- }
-
- public Map getExtendParams() {
- return extendParams;
- }
-
- public Map getInstanceParams() {
- return instanceParams;
- }
-
- public String getParameter(String key, String serviceKey) {
- ServiceInfo serviceInfo = getValidServiceInfo(serviceKey);
- if (serviceInfo == null) return null;
- return serviceInfo.getParameter(key);
- }
-
- public Map getParameters(String serviceKey) {
- ServiceInfo serviceInfo = getValidServiceInfo(serviceKey);
- if (serviceInfo == null) {
- return Collections.emptyMap();
- }
- return serviceInfo.getAllParams();
- }
-
- public String getServiceString(String protocolServiceKey) {
- if (protocolServiceKey == null) {
- return null;
- }
-
- ServiceInfo serviceInfo = getValidServiceInfo(protocolServiceKey);
- if (serviceInfo == null) {
- return null;
- }
- return serviceInfo.toFullString();
- }
-
- public synchronized void addSubscribedURL(URL url) {
- if (subscribedServiceURLs == null) {
- subscribedServiceURLs = new ConcurrentSkipListMap<>();
- }
- addURL(subscribedServiceURLs, url);
- }
-
- public boolean removeSubscribedURL(URL url) {
- if (subscribedServiceURLs == null) {
- return true;
- }
- return removeURL(subscribedServiceURLs, url);
- }
-
- public ConcurrentNavigableMap> getSubscribedServiceURLs() {
- return subscribedServiceURLs;
- }
-
- public ConcurrentNavigableMap> getExportedServiceURLs() {
- return exportedServiceURLs;
- }
-
- private boolean addURL(Map> serviceURLs, URL url) {
- SortedSet urls = serviceURLs.computeIfAbsent(url.getServiceKey(), this::newSortedURLs);
- // make sure the parameters of tmpUrl is variable
- return urls.add(url);
- }
-
- boolean removeURL(Map> serviceURLs, URL url) {
- String key = url.getServiceKey();
- SortedSet urls = serviceURLs.getOrDefault(key, null);
- if (urls == null) {
- return true;
- }
- boolean r = urls.remove(url);
- // if it is empty
- if (urls.isEmpty()) {
- serviceURLs.remove(key);
- }
- return r;
- }
-
- private SortedSet newSortedURLs(String serviceKey) {
- return new TreeSet<>(URLComparator.INSTANCE);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(app, services);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
-
- if (!(obj instanceof MetadataInfo)) {
- return false;
- }
-
- MetadataInfo other = (MetadataInfo)obj;
-
- return Objects.equals(app, other.getApp())
- && ((services == null && other.services == null)
- || (services != null && services.equals(other.services)));
- }
-
- private void extractInstanceParams(URL url, List filters) {
- if (CollectionUtils.isEmpty(filters)) {
- return;
- }
-
- String[] included, excluded;
- if (filters.size() == 1) {
- MetadataParamsFilter filter = filters.get(0);
- included = filter.instanceParamsIncluded();
- excluded = filter.instanceParamsExcluded();
- } else {
- Set includedList = new HashSet<>();
- Set excludedList = new HashSet<>();
- filters.forEach(filter -> {
- if (ArrayUtils.isNotEmpty(filter.instanceParamsIncluded())) {
- includedList.addAll(Arrays.asList(filter.instanceParamsIncluded()));
- }
- if (ArrayUtils.isNotEmpty(filter.instanceParamsExcluded())) {
- excludedList.addAll(Arrays.asList(filter.instanceParamsExcluded()));
- }
- });
- included = includedList.toArray(new String[0]);
- excluded = excludedList.toArray(new String[0]);
- }
-
- Map tmpInstanceParams = new HashMap<>();
- if (ArrayUtils.isNotEmpty(included)) {
- for (String p : included) {
- String value = url.getParameter(p);
- if (value != null) {
- tmpInstanceParams.put(p, value);
- }
- }
- } else if (ArrayUtils.isNotEmpty(excluded)) {
- tmpInstanceParams.putAll(url.getParameters());
- for (String p : excluded) {
- tmpInstanceParams.remove(p);
- }
- }
-
- tmpInstanceParams.forEach((key, value) -> {
- String oldValue = instanceParams.put(key, value);
- if (!TIMESTAMP_KEY.equals(key) && oldValue != null && !oldValue.equals(value)) {
- throw new IllegalStateException(String.format("Inconsistent instance metadata found in different services: %s, %s", oldValue, value));
- }
- });
- }
-
- @Override
- public String toString() {
- return "metadata{" +
- "app='" + app + "'," +
- "revision='" + revision + "'," +
- "size=" + (services == null ? 0 : services.size()) + "," +
- "services=" + getSimplifiedServices(services) +
- "}";
- }
-
- public String toFullString() {
- return "metadata{" +
- "app='" + app + "'," +
- "revision='" + revision + "'," +
- "services=" + services +
- "}";
- }
-
- private String getSimplifiedServices(Map services) {
- if (services == null) {
- return "[]";
- }
-
- return services.keySet().toString();
- }
-
- @Override
- public synchronized MetadataInfo clone() {
- return new MetadataInfo(app, revision, services, initiated, extendParams, instanceParams, updated, subscribedServiceURLs, exportedServiceURLs, loader);
- }
-
- private Object readResolve() {
- // create a new object from the deserialized one, in order to call constructor
- return new MetadataInfo(this.app, this.revision, this.services);
- }
-
- public static class ServiceInfo implements Serializable {
- private String name;
- private String group;
- private String version;
- private String protocol;
- private int port = -1;
- private String path; // most of the time, path is the same with the interface name.
- private Map params;
-
- // params configured on consumer side,
- private volatile transient Map consumerParams;
- // cached method params
- private volatile transient Map> methodParams;
- private volatile transient Map> consumerMethodParams;
- // cached numbers
- private volatile transient Map numbers;
- private volatile transient Map> methodNumbers;
- // service + group + version
- private volatile transient String serviceKey;
- // service + group + version + protocol
- private volatile transient String matchKey;
-
- private volatile transient ProtocolServiceKey protocolServiceKey;
-
- private transient URL url;
-
- public ServiceInfo() {}
-
- public ServiceInfo(URL url, List filters) {
- this(url.getServiceInterface(), url.getGroup(), url.getVersion(), url.getProtocol(), url.getPort(), url.getPath(), null);
- this.url = url;
- Map params = extractServiceParams(url, filters);
- // initialize method params caches.
- this.methodParams = URLParam.initMethodParameters(params);
- this.consumerMethodParams = URLParam.initMethodParameters(consumerParams);
- }
-
- public ServiceInfo(String name, String group, String version, String protocol, int port, String path, Map params) {
- this.name = name;
- this.group = group;
- this.version = version;
- this.protocol = protocol;
- this.port = port;
- this.path = path;
- this.params = params == null ? new ConcurrentHashMap<>() : params;
-
- this.serviceKey = buildServiceKey(name, group, version);
- this.matchKey = buildMatchKey();
- }
-
- private Map extractServiceParams(URL url, List filters) {
- Map params = new HashMap<>();
-
- if (CollectionUtils.isEmpty(filters)) {
- params.putAll(url.getParameters());
- this.params = params;
- return params;
- }
-
- String[] included, excluded;
- if (filters.size() == 1) {
- included = filters.get(0).serviceParamsIncluded();
- excluded = filters.get(0).serviceParamsExcluded();
- } else {
- Set includedList = new HashSet<>();
- Set excludedList = new HashSet<>();
- for (MetadataParamsFilter filter : filters) {
- if (ArrayUtils.isNotEmpty(filter.serviceParamsIncluded())) {
- includedList.addAll(Arrays.asList(filter.serviceParamsIncluded()));
- }
- if (ArrayUtils.isNotEmpty(filter.serviceParamsExcluded())) {
- excludedList.addAll(Arrays.asList(filter.serviceParamsExcluded()));
- }
- }
- included = includedList.toArray(new String[0]);
- excluded = excludedList.toArray(new String[0]);
- }
-
- if (ArrayUtils.isNotEmpty(included)) {
- String[] methods = url.getParameter(METHODS_KEY, (String[]) null);
- for (String p : included) {
- String value = url.getParameter(p);
- if (StringUtils.isNotEmpty(value) && params.get(p) == null) {
- params.put(p, value);
- }
- appendMethodParams(url, params, methods, p);
- }
- } else if (ArrayUtils.isNotEmpty(excluded)) {
- for (Map.Entry entry : url.getParameters().entrySet()) {
- String key = entry.getKey();
- String value = entry.getValue();
- boolean shouldAdd = true;
- for (String excludeKey : excluded) {
- if (key.equalsIgnoreCase(excludeKey) || key.contains("." + excludeKey)) {
- shouldAdd = false;
- break;
- }
- }
- if (shouldAdd) {
- params.put(key, value);
- }
- }
- }
-
- this.params = params;
- return params;
- }
-
- private void appendMethodParams(URL url, Map params, String[] methods, String p) {
- if (methods != null) {
- for (String method : methods) {
- String mValue = url.getMethodParameterStrict(method, p);
- if (StringUtils.isNotEmpty(mValue)) {
- params.put(method + DOT_SEPARATOR + p, mValue);
- }
- }
- }
- }
-
- /**
- * Initialize necessary caches right after deserialization on the consumer side
- */
- protected void init() {
- buildMatchKey();
- buildServiceKey(name, group, version);
- // init method params
- this.methodParams = URLParam.initMethodParameters(params);
- // Actually, consumer params is empty after deserialized on the consumer side, so no need to initialize.
- // Check how InstanceAddressURL operates on consumer url for more detail.
-// this.consumerMethodParams = URLParam.initMethodParameters(consumerParams);
- // no need to init numbers for it's only for cache purpose
- }
-
- 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 boolean matchProtocolServiceKey(ProtocolServiceKey protocolServiceKey) {
- return ProtocolServiceKey.Matcher.isMatch(protocolServiceKey, getProtocolServiceKey());
- }
-
- public ProtocolServiceKey getProtocolServiceKey() {
- if (protocolServiceKey != null) {
- return protocolServiceKey;
- }
- protocolServiceKey = new ProtocolServiceKey(name, version, group, protocol);
- return protocolServiceKey;
- }
-
- private String buildServiceKey(String name, String group, String version) {
- this.serviceKey = URL.buildKey(name, group, version);
- return this.serviceKey;
- }
-
- public String getServiceKey() {
- if (serviceKey != null) {
- return serviceKey;
- }
- buildServiceKey(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 String getProtocol() {
- return protocol;
- }
-
- public void setProtocol(String protocol) {
- this.protocol = protocol;
- }
-
- public int getPort() {
- return port;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public Map getParams() {
- if (params == null) {
- return Collections.emptyMap();
- }
- return params;
- }
-
- public void setParams(Map params) {
- this.params = params;
- }
-
- @Transient
- 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) {
- 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) {
- String value = null;
- if (map == null) {
- return value;
- }
-
- Map keyMap = map.get(method);
- if (keyMap != null) {
- value = keyMap.get(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) {
- return (consumerMethodParams != null && consumerMethodParams.containsKey(method))
- || (methodParams != null && methodParams.containsKey(method));
- }
-
- public String toDescString() {
- return this.getMatchKey() + port + path + new TreeMap<>(getParams());
- }
-
- public void addParameter(String key, String value) {
- if (consumerParams != null) {
- this.consumerParams.put(key, value);
- }
- // refresh method params
- consumerMethodParams = URLParam.initMethodParameters(consumerParams);
- }
-
- public void addParameterIfAbsent(String key, String value) {
- if (consumerParams != null) {
- this.consumerParams.putIfAbsent(key, value);
- }
- // refresh method params
- consumerMethodParams = URLParam.initMethodParameters(consumerParams);
- }
-
- public void addConsumerParams(Map params) {
- // copy once for one service subscription
- if (consumerParams == null) {
- consumerParams = new ConcurrentHashMap<>(params);
- // init method params
- consumerMethodParams = URLParam.initMethodParameters(consumerParams);
- }
- }
-
- 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;
- /**
- * Equals to Objects.equals(this.getMatchKey(), serviceInfo.getMatchKey()), but match key will not get initialized
- * on json deserialization.
- */
- return Objects.equals(this.getVersion(), serviceInfo.getVersion())
- && Objects.equals(this.getGroup(), serviceInfo.getGroup())
- && Objects.equals(this.getName(), serviceInfo.getName())
- && Objects.equals(this.getProtocol(), serviceInfo.getProtocol())
- && Objects.equals(this.getPort(), serviceInfo.getPort())
- && this.getParams().equals(serviceInfo.getParams());
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getVersion(), getGroup(), getName(), getProtocol(), getPort(), getParams());
- }
-
- @Override
- public String toString() {
- return getMatchKey();
- }
-
- public String toFullString() {
- return "service{" +
- "name='" + name + "'," +
- "group='" + group + "'," +
- "version='" + version + "'," +
- "protocol='" + protocol + "'," +
- "port='" + port + "'," +
- "params=" + params + "," +
- "}";
- }
- }
-
- static class URLComparator implements Comparator {
-
- public static final URLComparator INSTANCE = new URLComparator();
-
- @Override
- public int compare(URL o1, URL o2) {
- return o1.toFullString().compareTo(o2.toFullString());
- }
- }
-}