Move some SPI extensions to apache/dubbo-spi-extensions, add sub package modules (#6157)

This commit is contained in:
ken.lj 2020-08-17 13:38:04 +08:00 committed by GitHub
parent 0e4bbb8ead
commit 8bdecea7ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
450 changed files with 1916 additions and 59341 deletions

View File

@ -68,6 +68,16 @@
<artifactId>dubbo-rpc-thrift</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-cache</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-validation</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>

View File

@ -55,16 +55,6 @@
<artifactId>dubbo-rpc-injvm</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-validation</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-cache</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- FIXME, we shouldn't rely on these modules, even in test scope -->
<dependency>
@ -231,6 +221,19 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-cache</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-validation</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -16,12 +16,13 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
<version>2.7.7-SNAPSHOT</version>
</parent>
<artifactId>dubbo-configcenter-apollo</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-configcenter-consul</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.orbitz.consul</groupId>
<artifactId>consul-client</artifactId>
</dependency>
<dependency>
<groupId>com.pszymczyk.consul</groupId>
<artifactId>embedded-consul</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,181 +0,0 @@
/*
* 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.configcenter.consul;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.TreePathDynamicConfiguration;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import com.google.common.base.Charsets;
import com.google.common.net.HostAndPort;
import com.orbitz.consul.Consul;
import com.orbitz.consul.KeyValueClient;
import com.orbitz.consul.cache.KVCache;
import com.orbitz.consul.model.kv.Value;
import org.apache.dubbo.common.utils.StringUtils;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
/**
* config center implementation for consul
*/
public class ConsulDynamicConfiguration extends TreePathDynamicConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ConsulDynamicConfiguration.class);
private static final int DEFAULT_PORT = 8500;
private static final int DEFAULT_WATCH_TIMEOUT = 60 * 1000;
private static final String WATCH_TIMEOUT = "consul-watch-timeout";
private final Consul client;
private final KeyValueClient kvClient;
private final int watchTimeout;
private final ConcurrentMap<String, ConsulListener> watchers = new ConcurrentHashMap<>();
public ConsulDynamicConfiguration(URL url) {
super(url);
watchTimeout = url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT);
String host = url.getHost();
int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT;
Consul.Builder builder = Consul.builder()
.withHostAndPort(HostAndPort.fromParts(host, port));
String token = url.getParameter("token", (String) null);
if (StringUtils.isNotEmpty(token)) {
builder.withAclToken(token);
}
client = builder.build();
this.kvClient = client.keyValueClient();
}
@Override
public String getInternalProperty(String key) {
logger.info("getting config from: " + key);
return kvClient.getValueAsString(key, Charsets.UTF_8).orElse(null);
}
@Override
protected boolean doPublishConfig(String pathKey, String content) throws Exception {
return kvClient.putValue(pathKey, content);
}
@Override
protected String doGetConfig(String pathKey) throws Exception {
return getInternalProperty(pathKey);
}
@Override
protected boolean doRemoveConfig(String pathKey) throws Exception {
kvClient.deleteKey(pathKey);
return true;
}
@Override
protected Collection<String> doGetConfigKeys(String groupPath) {
List<String> keys = kvClient.getKeys(groupPath);
List<String> configKeys = new LinkedList<>();
if (CollectionUtils.isNotEmpty(keys)) {
keys.stream()
.filter(k -> !k.equals(groupPath))
.map(k -> k.substring(k.lastIndexOf(PATH_SEPARATOR) + 1))
.forEach(configKeys::add);
}
return configKeys;
}
@Override
protected void doAddListener(String pathKey, ConfigurationListener listener) {
logger.info("register listener " + listener.getClass() + " for config with key: " + pathKey);
ConsulListener watcher = watchers.computeIfAbsent(pathKey, k -> new ConsulListener(pathKey));
watcher.addListener(listener);
}
@Override
protected void doRemoveListener(String pathKey, ConfigurationListener listener) {
logger.info("unregister listener " + listener.getClass() + " for config with key: " + pathKey);
ConsulListener watcher = watchers.get(pathKey);
if (watcher != null) {
watcher.removeListener(listener);
}
}
@Override
protected void doClose() throws Exception {
client.destroy();
}
private class ConsulListener implements KVCache.Listener<String, Value> {
private KVCache kvCache;
private final Set<ConfigurationListener> listeners = new LinkedHashSet<>();
private final String normalizedKey;
public ConsulListener(String normalizedKey) {
this.normalizedKey = normalizedKey;
initKVCache();
}
private void initKVCache() {
this.kvCache = KVCache.newCache(kvClient, normalizedKey, watchTimeout);
kvCache.addListener(this);
kvCache.start();
}
@Override
public void notify(Map<String, Value> newValues) {
// Cache notifies all paths with "foo" the root path
// If you want to watch only "foo" value, you must filter other paths
Optional<Value> newValue = newValues.values().stream()
.filter(value -> value.getKey().equals(normalizedKey))
.findAny();
newValue.ifPresent(value -> {
// Values are encoded in key/value store, decode it if needed
Optional<String> decodedValue = newValue.get().getValueAsString();
decodedValue.ifPresent(v -> listeners.forEach(l -> {
ConfigChangedEvent event = new ConfigChangedEvent(normalizedKey, getGroup(), v, ConfigChangeType.MODIFIED);
l.process(event);
}));
});
}
private void addListener(ConfigurationListener listener) {
this.listeners.add(listener);
}
private void removeListener(ConfigurationListener listener) {
this.listeners.remove(listener);
}
}
}

View File

@ -1,32 +0,0 @@
/*
* 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.configcenter.consul;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
/**
* Config center factory for consul
*/
public class ConsulDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
@Override
protected DynamicConfiguration createDynamicConfiguration(URL url) {
return new ConsulDynamicConfiguration(url);
}
}

View File

@ -1 +0,0 @@
consul=org.apache.dubbo.configcenter.consul.ConsulDynamicConfigurationFactory

View File

@ -1,123 +0,0 @@
/*
* 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.configcenter.consul;
import org.apache.dubbo.common.URL;
import com.google.common.net.HostAndPort;
import com.orbitz.consul.Consul;
import com.orbitz.consul.KeyValueClient;
import com.orbitz.consul.cache.KVCache;
import com.orbitz.consul.model.kv.Value;
import com.pszymczyk.consul.ConsulProcess;
import com.pszymczyk.consul.ConsulStarterBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Optional;
import java.util.TreeSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
*
*/
public class ConsulDynamicConfigurationTest {
private static ConsulProcess consul;
private static URL configCenterUrl;
private static ConsulDynamicConfiguration configuration;
private static Consul client;
private static KeyValueClient kvClient;
@BeforeAll
public static void setUp() throws Exception {
consul = ConsulStarterBuilder.consulStarter()
.build()
.start();
configCenterUrl = URL.valueOf("consul://127.0.0.1:" + consul.getHttpPort());
configuration = new ConsulDynamicConfiguration(configCenterUrl);
client = Consul.builder().withHostAndPort(HostAndPort.fromParts("127.0.0.1", consul.getHttpPort())).build();
kvClient = client.keyValueClient();
}
@AfterAll
public static void tearDown() throws Exception {
consul.close();
configuration.close();
}
@Test
public void testGetConfig() {
kvClient.putValue("/dubbo/config/dubbo/foo", "bar");
// test equals
assertEquals("bar", configuration.getConfig("foo", "dubbo"));
// test does not block
assertEquals("bar", configuration.getConfig("foo", "dubbo"));
Assertions.assertNull(configuration.getConfig("not-exist", "dubbo"));
}
@Test
public void testPublishConfig() {
configuration.publishConfig("value", "metadata", "1");
// test equals
assertEquals("1", configuration.getConfig("value", "/metadata"));
assertEquals("1", kvClient.getValueAsString("/dubbo/config/metadata/value").get());
}
@Test
public void testAddListener() {
KVCache cache = KVCache.newCache(kvClient, "/dubbo/config/dubbo/foo");
cache.addListener(newValues -> {
// Cache notifies all paths with "foo" the root path
// If you want to watch only "foo" value, you must filter other paths
Optional<Value> newValue = newValues.values().stream()
.filter(value -> value.getKey().equals("foo"))
.findAny();
newValue.ifPresent(value -> {
// Values are encoded in key/value store, decode it if needed
Optional<String> decodedValue = newValue.get().getValueAsString();
decodedValue.ifPresent(v -> System.out.println(String.format("Value is: %s", v))); //prints "bar"
});
});
cache.start();
kvClient.putValue("/dubbo/config/dubbo/foo", "new-value");
kvClient.putValue("/dubbo/config/dubbo/foo/sub", "sub-value");
kvClient.putValue("/dubbo/config/dubbo/foo/sub2", "sub-value2");
kvClient.putValue("/dubbo/config/foo", "parent-value");
System.out.println(kvClient.getKeys("/dubbo/config/dubbo/foo"));
System.out.println(kvClient.getKeys("/dubbo/config"));
System.out.println(kvClient.getValues("/dubbo/config/dubbo/foo"));
}
@Test
public void testGetConfigKeys() {
configuration.publishConfig("v1", "metadata", "1");
configuration.publishConfig("v2", "metadata", "2");
configuration.publishConfig("v3", "metadata", "3");
// test equals
assertEquals(new TreeSet(Arrays.asList("v1", "v2", "v3")), configuration.getConfigKeys("metadata"));
}
}

View File

@ -1,74 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-configcenter-etcd</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The etcd implementation of the config-center api</description>
<properties>
<skipIntegrationTests>true</skipIntegrationTests>
</properties>
<dependencies>
<dependency>
<groupId>io.etcd</groupId>
<artifactId>jetcd-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-etcd3</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>${skipIntegrationTests}</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,197 +0,0 @@
/*
* 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.configcenter.support.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.etcd.StateListener;
import org.apache.dubbo.remoting.etcd.jetcd.JEtcdClient;
import com.google.protobuf.ByteString;
import io.etcd.jetcd.api.Event;
import io.etcd.jetcd.api.WatchCancelRequest;
import io.etcd.jetcd.api.WatchCreateRequest;
import io.etcd.jetcd.api.WatchGrpc;
import io.etcd.jetcd.api.WatchRequest;
import io.etcd.jetcd.api.WatchResponse;
import io.grpc.ManagedChannel;
import io.grpc.stub.StreamObserver;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
/**
* The etcd implementation of {@link DynamicConfiguration}
*/
public class EtcdDynamicConfiguration implements DynamicConfiguration {
/**
* The final root path would be: /$NAME_SPACE/config
*/
private String rootPath;
/**
* The etcd client
*/
private final JEtcdClient etcdClient;
/**
* The map store the key to {@link EtcdConfigWatcher} mapping
*/
private final ConcurrentMap<ConfigurationListener, EtcdConfigWatcher> watchListenerMap;
EtcdDynamicConfiguration(URL url) {
rootPath = PATH_SEPARATOR + url.getParameter(CONFIG_NAMESPACE_KEY, DEFAULT_GROUP) + "/config";
etcdClient = new JEtcdClient(url);
etcdClient.addStateListener(state -> {
if (state == StateListener.CONNECTED) {
try {
recover();
} catch (Exception e) {
// ignore
}
}
});
watchListenerMap = new ConcurrentHashMap<>();
}
@Override
public void addListener(String key, String group, ConfigurationListener listener) {
if (watchListenerMap.get(listener) == null) {
EtcdConfigWatcher watcher = new EtcdConfigWatcher(key, group, listener);
watchListenerMap.put(listener, watcher);
watcher.watch();
}
}
@Override
public void removeListener(String key, String group, ConfigurationListener listener) {
EtcdConfigWatcher watcher = watchListenerMap.get(listener);
watcher.cancelWatch();
}
@Override
public String getConfig(String key, String group, long timeout) throws IllegalStateException {
return (String) getInternalProperty(convertKey(group, key));
}
// @Override
// public String getConfigs(String key, String group, long timeout) throws IllegalStateException {
// if (StringUtils.isEmpty(group)) {
// group = DEFAULT_GROUP;
// }
// return (String) getInternalProperty(convertKey(group, key));
// }
@Override
public Object getInternalProperty(String key) {
return etcdClient.getKVValue(key);
}
private String buildPath(String group) {
String actualGroup = StringUtils.isEmpty(group) ? DEFAULT_GROUP : group;
return rootPath + PATH_SEPARATOR + actualGroup;
}
private String convertKey(String group, String key) {
return buildPath(group) + PATH_SEPARATOR + key;
}
private void recover() {
for (EtcdConfigWatcher watcher : watchListenerMap.values()) {
watcher.watch();
}
}
public class EtcdConfigWatcher implements StreamObserver<WatchResponse> {
private ConfigurationListener listener;
protected WatchGrpc.WatchStub watchStub;
private StreamObserver<WatchRequest> observer;
protected long watchId;
private ManagedChannel channel;
private final String key;
private final String group;
private String normalizedKey;
public EtcdConfigWatcher(String key, String group, ConfigurationListener listener) {
this.key = key;
this.group = group;
this.normalizedKey = convertKey(group, key);
this.listener = listener;
this.channel = etcdClient.getChannel();
}
@Override
public void onNext(WatchResponse watchResponse) {
this.watchId = watchResponse.getWatchId();
for (Event etcdEvent : watchResponse.getEventsList()) {
ConfigChangeType type = ConfigChangeType.MODIFIED;
if (etcdEvent.getType() == Event.EventType.DELETE) {
type = ConfigChangeType.DELETED;
}
ConfigChangedEvent event = new ConfigChangedEvent(key, group,
etcdEvent.getKv().getValue().toString(UTF_8), type);
listener.process(event);
}
}
@Override
public void onError(Throwable throwable) {
// ignore
}
@Override
public void onCompleted() {
// ignore
}
public long getWatchId() {
return watchId;
}
private void watch() {
watchStub = WatchGrpc.newStub(channel);
observer = watchStub.watch(this);
WatchCreateRequest.Builder builder = WatchCreateRequest.newBuilder()
.setKey(ByteString.copyFromUtf8(normalizedKey))
.setProgressNotify(true);
WatchRequest req = WatchRequest.newBuilder().setCreateRequest(builder).build();
observer.onNext(req);
}
private void cancelWatch() {
WatchCancelRequest watchCancelRequest =
WatchCancelRequest.newBuilder().setWatchId(watchId).build();
WatchRequest cancelRequest = WatchRequest.newBuilder()
.setCancelRequest(watchCancelRequest).build();
observer.onNext(cancelRequest);
}
}
}

View File

@ -1,33 +0,0 @@
/*
* 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.configcenter.support.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
/**
* The etcd implementation of {@link AbstractDynamicConfigurationFactory}
*/
public class EtcdDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
@Override
protected DynamicConfiguration createDynamicConfiguration(URL url) {
return new EtcdDynamicConfiguration(url);
}
}

View File

@ -1 +0,0 @@
etcd=org.apache.dubbo.configcenter.support.etcd.EtcdDynamicConfigurationFactory

View File

@ -1,154 +0,0 @@
/*
* 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.configcenter.support.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.launcher.EtcdCluster;
import io.etcd.jetcd.launcher.EtcdClusterFactory;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.dubbo.remoting.etcd.Constants.SESSION_TIMEOUT_KEY;
/**
* Unit test for etcd config center support
* Integrate with https://github.com/etcd-io/jetcd#launcher
*/
public class EtcdDynamicConfigurationTest {
private static EtcdDynamicConfiguration config;
public EtcdCluster etcdCluster = EtcdClusterFactory.buildCluster(getClass().getSimpleName(), 3, false, false);
private static Client client;
@Test
public void testGetConfig() {
put("/dubbo/config/org.apache.dubbo.etcd.testService/configurators", "hello");
put("/dubbo/config/test/dubbo.properties", "aaa=bbb");
Assert.assertEquals("hello", config.getConfig("org.apache.dubbo.etcd.testService.configurators", DynamicConfiguration.DEFAULT_GROUP));
Assert.assertEquals("aaa=bbb", config.getConfig("dubbo.properties", "test"));
}
@Test
public void testAddListener() throws Exception {
CountDownLatch latch = new CountDownLatch(4);
TestListener listener1 = new TestListener(latch);
TestListener listener2 = new TestListener(latch);
TestListener listener3 = new TestListener(latch);
TestListener listener4 = new TestListener(latch);
config.addListener("AService.configurators", listener1);
config.addListener("AService.configurators", listener2);
config.addListener("testapp.tagrouters", listener3);
config.addListener("testapp.tagrouters", listener4);
put("/dubbo/config/AService/configurators", "new value1");
Thread.sleep(200);
put("/dubbo/config/testapp/tagrouters", "new value2");
Thread.sleep(200);
put("/dubbo/config/testapp", "new value3");
Thread.sleep(1000);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
Assert.assertEquals(1, listener1.getCount("/dubbo/config/AService/configurators"));
Assert.assertEquals(1, listener2.getCount("/dubbo/config/AService/configurators"));
Assert.assertEquals(1, listener3.getCount("/dubbo/config/testapp/tagrouters"));
Assert.assertEquals(1, listener4.getCount("/dubbo/config/testapp/tagrouters"));
Assert.assertEquals("new value1", listener1.getValue());
Assert.assertEquals("new value1", listener2.getValue());
Assert.assertEquals("new value2", listener3.getValue());
Assert.assertEquals("new value2", listener4.getValue());
}
private class TestListener implements ConfigurationListener {
private CountDownLatch latch;
private String value;
private Map<String, Integer> countMap = new HashMap<>();
public TestListener(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void process(ConfigChangedEvent event) {
Integer count = countMap.computeIfAbsent(event.getKey(), k -> 0);
countMap.put(event.getKey(), ++count);
value = event.getContent();
latch.countDown();
}
public int getCount(String key) {
return countMap.get(key);
}
public String getValue() {
return value;
}
}
private void put(String key, String value) {
try {
client.getKVClient().put(ByteSequence.from(key, UTF_8), ByteSequence.from(value, UTF_8)).get();
} catch (Exception e) {
System.out.println("Error put value to etcd.");
}
}
@Before
public void setUp() {
etcdCluster.start();
client = Client.builder().endpoints(etcdCluster.getClientEndpoints()).build();
List<URI> clientEndPoints = etcdCluster.getClientEndpoints();
String ipAddress = clientEndPoints.get(0).getHost() + ":" + clientEndPoints.get(0).getPort();
String urlForDubbo = "etcd3://" + ipAddress + "/org.apache.dubbo.etcd.testService";
// timeout in 15 seconds.
URL url = URL.valueOf(urlForDubbo)
.addParameter(SESSION_TIMEOUT_KEY, 15000);
config = new EtcdDynamicConfiguration(url);
}
@After
public void tearDown() {
etcdCluster.close();
}
}

View File

@ -22,8 +22,7 @@
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
<version>2.7.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -33,8 +33,6 @@
<modules>
<module>dubbo-configcenter-zookeeper</module>
<module>dubbo-configcenter-apollo</module>
<module>dubbo-configcenter-consul</module>
<module>dubbo-configcenter-etcd</module>
<module>dubbo-configcenter-nacos</module>
</modules>
</project>

View File

@ -1,39 +0,0 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-container</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-container-log4j</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The log4j container module of dubbo project</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-container-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,103 +0,0 @@
/*
* 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.container.log4j;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.container.Container;
import org.apache.log4j.Appender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;
import java.util.Enumeration;
import java.util.Properties;
/**
* Log4jContainer. (SPI, Singleton, ThreadSafe)
*
* The container class implementation for Log4j
*/
public class Log4jContainer implements Container {
public static final String LOG4J_FILE = "dubbo.log4j.file";
public static final String LOG4J_LEVEL = "dubbo.log4j.level";
public static final String LOG4J_SUBDIRECTORY = "dubbo.log4j.subdirectory";
public static final String DEFAULT_LOG4J_LEVEL = "ERROR";
@Override
@SuppressWarnings("unchecked")
public void start() {
String file = ConfigurationUtils.getProperty(LOG4J_FILE);
if (file != null && file.length() > 0) {
String level = ConfigurationUtils.getProperty(LOG4J_LEVEL);
if (StringUtils.isEmpty(level)) {
level = DEFAULT_LOG4J_LEVEL;
}
Properties properties = new Properties();
properties.setProperty("log4j.rootLogger", level + ",application");
properties.setProperty("log4j.appender.application", "org.apache.log4j.DailyRollingFileAppender");
properties.setProperty("log4j.appender.application.File", file);
properties.setProperty("log4j.appender.application.Append", "true");
properties.setProperty("log4j.appender.application.DatePattern", "'.'yyyy-MM-dd");
properties.setProperty("log4j.appender.application.layout", "org.apache.log4j.PatternLayout");
properties.setProperty("log4j.appender.application.layout.ConversionPattern", "%d [%t] %-5p %C{6} (%F:%L) - %m%n");
PropertyConfigurator.configure(properties);
}
String subdirectory = ConfigurationUtils.getProperty(LOG4J_SUBDIRECTORY);
if (subdirectory != null && subdirectory.length() > 0) {
Enumeration<org.apache.log4j.Logger> ls = LogManager.getCurrentLoggers();
while (ls.hasMoreElements()) {
org.apache.log4j.Logger l = ls.nextElement();
if (l != null) {
Enumeration<Appender> as = l.getAllAppenders();
while (as.hasMoreElements()) {
Appender a = as.nextElement();
if (a instanceof FileAppender) {
FileAppender fa = (FileAppender) a;
String f = fa.getFile();
if (f != null && f.length() > 0) {
int i = f.replace('\\', '/').lastIndexOf('/');
String path;
if (i == -1) {
path = subdirectory;
} else {
path = f.substring(0, i);
if (!path.endsWith(subdirectory)) {
path = path + "/" + subdirectory;
}
f = f.substring(i + 1);
}
fa.setFile(path + "/" + f);
fa.activateOptions();
}
}
}
}
}
}
}
@Override
public void stop() {
}
}

View File

@ -1 +0,0 @@
log4j=org.apache.dubbo.container.log4j.Log4jContainer

View File

@ -1,36 +0,0 @@
/*
* 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.container.log4j;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.container.Container;
import org.junit.jupiter.api.Test;
/**
* StandaloneContainerTest
*/
public class Log4jContainerTest {
@Test
public void testContainer() {
Log4jContainer container = (Log4jContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("log4j");
container.start();
container.stop();
}
}

View File

@ -1,43 +0,0 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-container</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-container-logback</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The logback container module of dubbo project</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-container-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,108 +0,0 @@
/*
* 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.container.logback;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.container.Container;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import org.slf4j.LoggerFactory;
/**
* LogbackContainer. (SPI, Singleton, ThreadSafe)
*
* The container class implementation for Logback
*/
public class LogbackContainer implements Container {
public static final String LOGBACK_FILE = "dubbo.logback.file";
public static final String LOGBACK_LEVEL = "dubbo.logback.level";
public static final String LOGBACK_MAX_HISTORY = "dubbo.logback.maxhistory";
public static final String DEFAULT_LOGBACK_LEVEL = "ERROR";
@Override
public void start() {
String file = ConfigUtils.getProperty(LOGBACK_FILE);
if (file != null && file.length() > 0) {
String level = ConfigUtils.getProperty(LOGBACK_LEVEL);
if (StringUtils.isEmpty(level)) {
level = DEFAULT_LOGBACK_LEVEL;
}
// maxHistory=0 Infinite history
int maxHistory = StringUtils.parseInteger(ConfigUtils.getProperty(LOGBACK_MAX_HISTORY));
doInitializer(file, level, maxHistory);
}
}
@Override
public void stop() {
}
/**
* Initializer logback
*
* @param file
* @param level
* @param maxHistory
*/
private void doInitializer(String file, String level, int maxHistory) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.detachAndStopAllAppenders();
// appender
RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<ILoggingEvent>();
fileAppender.setContext(loggerContext);
fileAppender.setName("application");
fileAppender.setFile(file);
fileAppender.setAppend(true);
// policy
TimeBasedRollingPolicy<ILoggingEvent> policy = new TimeBasedRollingPolicy<ILoggingEvent>();
policy.setContext(loggerContext);
policy.setMaxHistory(maxHistory);
policy.setFileNamePattern(file + ".%d{yyyy-MM-dd}");
policy.setParent(fileAppender);
policy.start();
fileAppender.setRollingPolicy(policy);
// encoder
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(loggerContext);
encoder.setPattern("%date [%thread] %-5level %logger (%file:%line\\) - %msg%n");
encoder.start();
fileAppender.setEncoder(encoder);
fileAppender.start();
rootLogger.addAppender(fileAppender);
rootLogger.setLevel(Level.toLevel(level));
rootLogger.setAdditive(false);
}
}

View File

@ -1 +0,0 @@
logback=org.apache.dubbo.container.logback.LogbackContainer

View File

@ -1,47 +0,0 @@
/*
* 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.container.logback;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.container.Container;
import org.junit.jupiter.api.Test;
/**
* StandaloneContainerTest
*/
public class LogbackContainerTest {
private static final Logger logger = LoggerFactory.getLogger(LogbackContainerTest.class);
@Test
public void testContainer() {
LogbackContainer container = (LogbackContainer) ExtensionLoader.getExtensionLoader(Container.class)
.getExtension("logback");
container.start();
logger.debug("Test debug:" + this.getClass().getName());
logger.warn("Test warn:" + this.getClass().getName());
logger.info("Test info:" + this.getClass().getName());
logger.error("Test error:" + this.getClass().getName());
container.stop();
}
}

View File

@ -32,7 +32,5 @@
<modules>
<module>dubbo-container-api</module>
<module>dubbo-container-spring</module>
<module>dubbo-container-log4j</module>
<module>dubbo-container-logback</module>
</modules>
</project>

View File

@ -21,11 +21,11 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-parent</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>dubbo</artifactId>
<packaging>jar</packaging>
<name>dubbo-all</name>
<name>dubbo</name>
<description>The all in one project of dubbo</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
@ -59,20 +59,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-cache</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-validation</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-api</artifactId>
@ -96,35 +82,7 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-etcd3</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-mina</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-grizzly</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-p2p</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-http</artifactId>
<artifactId>dubbo-remoting-zookeeper</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
@ -150,62 +108,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-http</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-rmi</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-hessian</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-webservice</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-thrift</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-native-thrift</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-memcached</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-redis</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-rest</artifactId>
@ -213,13 +115,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-xml</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-grpc</artifactId>
@ -234,13 +129,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-default</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-multicast</artifactId>
@ -255,44 +143,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-redis</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-consul</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-etcd3</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
</exclusion>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-eureka</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
@ -300,13 +150,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-sofa</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-multiple</artifactId>
@ -335,20 +178,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-container-log4j</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-container-logback</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-qos</artifactId>
@ -363,20 +192,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-fastjson</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-fst</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
@ -384,13 +199,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-native-hession</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-jdk</artifactId>
@ -398,41 +206,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-kryo</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-avro</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-protostuff</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-gson</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-protobuf</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter-zookeeper</artifactId>
@ -454,30 +227,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter-consul</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter-etcd</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
</exclusion>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-compatible</artifactId>
@ -515,31 +264,7 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-report-consul</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-report-etcd</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
</exclusion>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-report-nacos</artifactId>
<artifactId>dubbo-auth</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
@ -620,71 +345,35 @@
<include>org.apache.dubbo:dubbo-remoting-api</include>
<include>org.apache.dubbo:dubbo-remoting-netty</include>
<include>org.apache.dubbo:dubbo-remoting-netty4</include>
<include>org.apache.dubbo:dubbo-remoting-etcd3</include>
<include>org.apache.dubbo:dubbo-remoting-mina</include>
<include>org.apache.dubbo:dubbo-remoting-grizzly</include>
<include>org.apache.dubbo:dubbo-remoting-p2p</include>
<include>org.apache.dubbo:dubbo-remoting-http</include>
<include>org.apache.dubbo:dubbo-remoting-zookeeper</include>
<include>org.apache.dubbo:dubbo-rpc-api</include>
<include>org.apache.dubbo:dubbo-rpc-dubbo</include>
<include>org.apache.dubbo:dubbo-rpc-injvm</include>
<include>org.apache.dubbo:dubbo-rpc-http</include>
<include>org.apache.dubbo:dubbo-rpc-rmi</include>
<include>org.apache.dubbo:dubbo-rpc-hessian</include>
<include>org.apache.dubbo:dubbo-rpc-webservice</include>
<include>org.apache.dubbo:dubbo-rpc-thrift</include>
<include>org.apache.dubbo:dubbo-rpc-native-thrift</include>
<include>org.apache.dubbo:dubbo-rpc-memcached</include>
<include>org.apache.dubbo:dubbo-rpc-redis</include>
<include>org.apache.dubbo:dubbo-rpc-rest</include>
<include>org.apache.dubbo:dubbo-rpc-xml</include>
<include>org.apache.dubbo:dubbo-rpc-grpc</include>
<include>org.apache.dubbo:dubbo-filter-validation</include>
<include>org.apache.dubbo:dubbo-filter-cache</include>
<include>org.apache.dubbo:dubbo-cluster</include>
<include>org.apache.dubbo:dubbo-registry-api</include>
<include>org.apache.dubbo:dubbo-registry-default</include>
<include>org.apache.dubbo:dubbo-registry-multicast</include>
<include>org.apache.dubbo:dubbo-registry-zookeeper</include>
<include>org.apache.dubbo:dubbo-registry-redis</include>
<include>org.apache.dubbo:dubbo-registry-consul</include>
<include>org.apache.dubbo:dubbo-registry-etcd3</include>
<include>org.apache.dubbo:dubbo-registry-eureka</include>
<include>org.apache.dubbo:dubbo-registry-nacos</include>
<include>org.apache.dubbo:dubbo-registry-sofa</include>
<include>org.apache.dubbo:dubbo-registry-multiple</include>
<include>org.apache.dubbo:dubbo-monitor-api</include>
<include>org.apache.dubbo:dubbo-monitor-default</include>
<include>org.apache.dubbo:dubbo-container-api</include>
<include>org.apache.dubbo:dubbo-container-spring</include>
<include>org.apache.dubbo:dubbo-container-log4j</include>
<include>org.apache.dubbo:dubbo-container-logback</include>
<include>org.apache.dubbo:dubbo-qos</include>
<include>org.apache.dubbo:dubbo-serialization-api</include>
<include>org.apache.dubbo:dubbo-serialization-fastjson</include>
<include>org.apache.dubbo:dubbo-serialization-hessian2</include>
<include>org.apache.dubbo:dubbo-serialization-fst</include>
<include>org.apache.dubbo:dubbo-serialization-kryo</include>
<include>org.apache.dubbo:dubbo-serialization-avro</include>
<include>org.apache.dubbo:dubbo-serialization-jdk</include>
<include>org.apache.dubbo:dubbo-serialization-protostuff</include>
<include>org.apache.dubbo:dubbo-serialization-gson</include>
<include>org.apache.dubbo:dubbo-serialization-protobuf</include>
<include>org.apache.dubbo:dubbo-configcenter-api</include>
<include>org.apache.dubbo:dubbo-configcenter-definition</include>
<include>org.apache.dubbo:dubbo-configcenter-apollo</include>
<include>org.apache.dubbo:dubbo-configcenter-zookeeper</include>
<include>org.apache.dubbo:dubbo-configcenter-consul</include>
<include>org.apache.dubbo:dubbo-configcenter-etcd</include>
<include>org.apache.dubbo:dubbo-configcenter-nacos</include>
<include>org.apache.dubbo:dubbo-metadata-api</include>
<include>org.apache.dubbo:dubbo-metadata-report-redis</include>
<include>org.apache.dubbo:dubbo-metadata-report-zookeeper</include>
<include>org.apache.dubbo:dubbo-metadata-report-consul</include>
<include>org.apache.dubbo:dubbo-metadata-report-etcd</include>
<include>org.apache.dubbo:dubbo-metadata-report-nacos</include>
<include>org.apache.dubbo:dubbo-serialization-native-hession</include>
<include>org.apache.dubbo:dubbo-qos</include>
<include>org.apache.dubbo:dubbo-auth</include>
</includes>
</artifactSet>
<transformers>
@ -980,6 +669,18 @@
META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.auth.spi.AccessKeyStorage
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.auth.spi.Authenticator
</resource>
</transformer>
<!-- @since 2.7.8 -->
<transformer
@ -994,7 +695,7 @@
<filter>
<artifact>org.apache.dubbo:dubbo</artifact>
<excludes>
<!-- These two line is optional, it can remove some warn log -->
<!-- These following two line is optional, it can remove some warn log -->
<exclude>com/**</exclude>
<exclude>org/**</exclude>
<!-- This one is required -->

View File

@ -0,0 +1,100 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-distribution</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-apache-release</artifactId>
<packaging>pom</packaging>
<name>dubbo-apache-release</name>
<description>The apache source release</description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-demo-api-provider</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-demo-api-consumer</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>release</id>
<build>
<finalName>apache-dubbo-${project.version}</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>bin</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/assembly/bin-release.xml</descriptor>
</descriptors>
</configuration>
</execution>
<execution>
<id>src</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/assembly/source-release.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -24,7 +24,7 @@
<baseDirectory>${project.build.finalName}-bin</baseDirectory>
<fileSets>
<fileSet>
<directory>../</directory>
<directory>../../</directory>
<includes>
<include>DISCLAIMER</include>
<include>NOTICE</include>
@ -32,7 +32,7 @@
</includes>
</fileSet>
<fileSet>
<directory>../dubbo-demo</directory>
<directory>../../dubbo-demo</directory>
<includes>
<include>README.md</include>
</includes>

View File

@ -25,7 +25,7 @@
<fileSets>
<fileSet>
<directory>../</directory>
<directory>../../</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>**/*</include>

View File

@ -23,68 +23,13 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-parent</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>dubbo-bom</artifactId>
<packaging>pom</packaging>
<name>dubbo-bom</name>
<description>Dubbo dependencies BOM</description>
<url>https://github.com/apache/dubbo</url>
<inceptionYear>2011</inceptionYear>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<url>https://github.com/apache/dubbo</url>
<connection>scm:git:https://github.com/apache/dubbo.git</connection>
<developerConnection>scm:git:https://github.com/apache/dubbo.git</developerConnection>
<tag>HEAD</tag>
</scm>
<mailingLists>
<mailingList>
<name>Development List</name>
<subscribe>dev-subscribe@dubbo.apache.org</subscribe>
<unsubscribe>dev-unsubscribe@dubbo.apache.org</unsubscribe>
<post>dev@dubbo.apache.org</post>
</mailingList>
<mailingList>
<name>Commits List</name>
<subscribe>commits-subscribe@dubbo.apache.org</subscribe>
<unsubscribe>commits-unsubscribe@dubbo.apache.org</unsubscribe>
<post>commits@dubbo.apache.org</post>
</mailingList>
<mailingList>
<name>Issues List</name>
<subscribe>issues-subscribe@dubbo.apache.org</subscribe>
<unsubscribe>issues-unsubscribe@dubbo.apache.org</unsubscribe>
<post>issues@dubbo.apache.org</post>
</mailingList>
</mailingLists>
<developers>
<developer>
<id>dubbo.io</id>
<name>The Dubbo Project Contributors</name>
<email>dev-subscribe@dubbo.apache.org</email>
<url>http://dubbo.apache.org/</url>
</developer>
</developers>
<organization>
<name>The Apache Software Foundation</name>
<url>http://www.apache.org/</url>
</organization>
<issueManagement>
<system>Github Issues</system>
<url>https://github.com/apache/dubbo/issues</url>
</issueManagement>
<dependencyManagement>
<dependencies>

View File

@ -0,0 +1,444 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-parent</artifactId>
<version>${revision}</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>dubbo-core-spi</artifactId>
<packaging>jar</packaging>
<name>dubbo-core-spi</name>
<description>All the SPI definitions of Dubbo</description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-cluster</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-monitor-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-qos</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- metadata -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createSourcesJar>true</createSourcesJar>
<promoteTransitiveDependencies>false</promoteTransitiveDependencies>
<artifactSet>
<includes>
<include>org.apache.dubbo:dubbo-common</include>
<include>org.apache.dubbo:dubbo-cluster</include>
<include>org.apache.dubbo:dubbo-qos</include>
<include>org.apache.dubbo:dubbo-remoting-api</include>
<include>org.apache.dubbo:dubbo-rpc-api</include>
<include>org.apache.dubbo:dubbo-cluster</include>
<include>org.apache.dubbo:dubbo-registry-api</include>
<include>org.apache.dubbo:dubbo-monitor-api</include>
<include>org.apache.dubbo:dubbo-container-api</include>
<include>org.apache.dubbo:dubbo-serialization-api</include>
<include>org.apache.dubbo:dubbo-metadata-api</include>
</includes>
</artifactSet>
<transformers>
<!-- dubbo-common beginning -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.infra.InfraAdapter
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool
</resource>
</transformer>
<!-- dubbo-common end -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Dispatcher</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.p2p.Networker
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.ExporterListener</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Merger</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ConfiguratorFactory
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.container.Container</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.monitor.MonitorFactory
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.validation.Validation</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
</resource>
</transformer>
<!-- @since 2.7.5 -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.event.EventDispatcher
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilder
</resource>
</transformer>
<!-- @since 2.7.6 -->
<!-- 'dubbo-common' module -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter
</resource>
</transformer>
<!-- 'dubbo-metadata-api' module -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver
</resource>
</transformer>
<!-- 'dubbo-metadata-processor' module -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.builder.TypeDefinitionBuilder
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.rest.AnnotatedMethodParameterProcessor
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.rest.ServiceRestMetadataResolver
</resource>
</transformer>
<!-- @since 2.7.7 -->
<!-- 'dubbo-common' module -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy
</resource>
</transformer>
</transformers>
<filters>
<filter>
<artifact>org.apache.dubbo:dubbo</artifact>
<excludes>
<!-- These following two line is optional, it can remove some warn log -->
<exclude>com/**</exclude>
<exclude>org/**</exclude>
<!-- This one is required -->
<exclude>META-INF/dubbo/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -14,86 +14,44 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-parent</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-distribution</artifactId>
<packaging>pom</packaging>
<name>dubbo-distribution</name>
<description>The binary distribution module for dubbo temporarily</description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-demo-api-provider</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-demo-api-consumer</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>release</id>
<build>
<finalName>apache-dubbo-${project.version}</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>bin</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/assembly/bin-release.xml</descriptor>
</descriptors>
</configuration>
</execution>
<execution>
<id>src</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/assembly/source-release.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<modules>
<module>dubbo-all</module>
<module>dubbo-apache-release</module>
<module>dubbo-bom</module>
<module>dubbo-core-spi</module>
</modules>
</profile>
<profile>
<id>dubbo-all</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>dubbo-all</module>
</modules>
</profile>
<profile>
<id>dubbo-core-spi</id>
<modules>
<module>dubbo-all</module>
</modules>
</profile>
</profiles>
</project>

View File

@ -19,8 +19,7 @@
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
<version>2.7.7-SNAPSHOT</version>
</parent>
<artifactId>dubbo-filter-cache</artifactId>
<packaging>jar</packaging>

View File

@ -1,43 +1,43 @@
/*
* 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.cache;
/**
* Cache interface to support storing and retrieval of value against a lookup key. It has two operation <b>get</b> and <b>put</b>.
* <li><b>put</b>-Storing value against a key.</li>
* <li><b>get</b>-Retrieval of object.</li>
* @see org.apache.dubbo.cache.support.lru.LruCache
* @see org.apache.dubbo.cache.support.jcache.JCache
* @see org.apache.dubbo.cache.support.expiring.ExpiringCache
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache
*/
public interface Cache {
/**
* API to store value against a key
* @param key Unique identifier for the object being store.
* @param value Value getting store
*/
void put(Object key, Object value);
/**
* API to return stored value using a key.
* @param key Unique identifier for cache lookup
* @return Return stored object against key
*/
Object get(Object key);
}
/*
* 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.cache;
/**
* Cache interface to support storing and retrieval of value against a lookup key. It has two operation <b>get</b> and <b>put</b>.
* <li><b>put</b>-Storing value against a key.</li>
* <li><b>get</b>-Retrieval of object.</li>
* @see org.apache.dubbo.cache.support.lru.LruCache
* @see org.apache.dubbo.cache.support.jcache.JCache
* @see org.apache.dubbo.cache.support.expiring.ExpiringCache
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache
*/
public interface Cache {
/**
* API to store value against a key
* @param key Unique identifier for the object being store.
* @param value Value getting store
*/
void put(Object key, Object value);
/**
* API to return stored value using a key.
* @param key Unique identifier for cache lookup
* @return Return stored object against key
*/
Object get(Object key);
}

View File

@ -1,43 +1,43 @@
/*
* 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.cache;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.rpc.Invocation;
/**
* Interface needs to be implemented by all the cache store provider.Along with implementing <b>CacheFactory</b> interface
* entry needs to be added in org.apache.dubbo.cache.CacheFactory file in a classpath META-INF sub directories.
*
* @see Cache
*/
@SPI("lru")
public interface CacheFactory {
/**
* CacheFactory implementation class needs to implement this return underlying cache instance for method against
* url and invocation.
* @param url
* @param invocation
* @return Instance of Cache containing cached value against method url and invocation.
*/
@Adaptive("cache")
Cache getCache(URL url, Invocation invocation);
}
/*
* 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.cache;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.rpc.Invocation;
/**
* Interface needs to be implemented by all the cache store provider.Along with implementing <b>CacheFactory</b> interface
* entry needs to be added in org.apache.dubbo.cache.CacheFactory file in a classpath META-INF sub directories.
*
* @see Cache
*/
@SPI("lru")
public interface CacheFactory {
/**
* CacheFactory implementation class needs to implement this return underlying cache instance for method against
* url and invocation.
* @param url
* @param invocation
* @return Instance of Cache containing cached value against method url and invocation.
*/
@Adaptive("cache")
Cache getCache(URL url, Invocation invocation);
}

View File

@ -1,133 +1,133 @@
/*
* 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.cache.filter;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.CacheFactory;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import java.io.Serializable;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.FilterConstants.CACHE_KEY;
/**
* CacheFilter is a core component of dubbo.Enabling <b>cache</b> key of service,method,consumer or provider dubbo will cache method return value.
* Along with cache key we need to configure cache type. Dubbo default implemented cache types are
* <li>lur</li>
* <li>threadlocal</li>
* <li>jcache</li>
* <li>expiring</li>
*
* <pre>
* e.g. 1)&lt;dubbo:service cache="lru" /&gt;
* 2)&lt;dubbo:service /&gt; &lt;dubbo:method name="method2" cache="threadlocal" /&gt; &lt;dubbo:service/&gt;
* 3)&lt;dubbo:provider cache="expiring" /&gt;
* 4)&lt;dubbo:consumer cache="jcache" /&gt;
*
*If cache type is defined in method level then method level type will get precedence. According to above provided
*example, if service has two method, method1 and method2, method2 will have cache type as <b>threadlocal</b> where others will
*be backed by <b>lru</b>
*</pre>
*
* @see org.apache.dubbo.rpc.Filter
* @see org.apache.dubbo.cache.support.lru.LruCacheFactory
* @see org.apache.dubbo.cache.support.lru.LruCache
* @see org.apache.dubbo.cache.support.jcache.JCacheFactory
* @see org.apache.dubbo.cache.support.jcache.JCache
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache
* @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory
* @see org.apache.dubbo.cache.support.expiring.ExpiringCache
*
*/
@Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY)
public class CacheFilter implements Filter {
private CacheFactory cacheFactory;
/**
* Dubbo will populate and set the cache factory instance based on service/method/consumer/provider configured
* cache attribute value. Dubbo will search for the class name implementing configured <b>cache</b> in file org.apache.dubbo.cache.CacheFactory
* under META-INF sub folders.
*
* @param cacheFactory instance of CacheFactory based on <b>cache</b> type
*/
public void setCacheFactory(CacheFactory cacheFactory) {
this.cacheFactory = cacheFactory;
}
/**
* If cache is configured, dubbo will invoke method on each method call. If cache value is returned by cache store
* then it will return otherwise call the remote method and return value. If remote method's return value has error
* then it will not cache the value.
* @param invoker service
* @param invocation invocation.
* @return Cache returned value if found by the underlying cache store. If cache miss it will call target method.
* @throws RpcException
*/
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), CACHE_KEY))) {
Cache cache = cacheFactory.getCache(invoker.getUrl(), invocation);
if (cache != null) {
String key = StringUtils.toArgumentString(invocation.getArguments());
Object value = cache.get(key);
if (value != null) {
if (value instanceof ValueWrapper) {
return AsyncRpcResult.newDefaultAsyncResult(((ValueWrapper) value).get(), invocation);
} else {
return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
}
}
Result result = invoker.invoke(invocation);
if (!result.hasException()) {
cache.put(key, new ValueWrapper(result.getValue()));
}
return result;
}
}
return invoker.invoke(invocation);
}
/**
* Cache value wrapper.
*/
static class ValueWrapper implements Serializable {
private static final long serialVersionUID = -1777337318019193256L;
private final Object value;
public ValueWrapper (Object value) {
this.value = value;
}
public Object get() {
return this.value;
}
}
}
/*
* 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.cache.filter;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.CacheFactory;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import java.io.Serializable;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.FilterConstants.CACHE_KEY;
/**
* CacheFilter is a core component of dubbo.Enabling <b>cache</b> key of service,method,consumer or provider dubbo will cache method return value.
* Along with cache key we need to configure cache type. Dubbo default implemented cache types are
* <li>lur</li>
* <li>threadlocal</li>
* <li>jcache</li>
* <li>expiring</li>
*
* <pre>
* e.g. 1)&lt;dubbo:service cache="lru" /&gt;
* 2)&lt;dubbo:service /&gt; &lt;dubbo:method name="method2" cache="threadlocal" /&gt; &lt;dubbo:service/&gt;
* 3)&lt;dubbo:provider cache="expiring" /&gt;
* 4)&lt;dubbo:consumer cache="jcache" /&gt;
*
*If cache type is defined in method level then method level type will get precedence. According to above provided
*example, if service has two method, method1 and method2, method2 will have cache type as <b>threadlocal</b> where others will
*be backed by <b>lru</b>
*</pre>
*
* @see org.apache.dubbo.rpc.Filter
* @see org.apache.dubbo.cache.support.lru.LruCacheFactory
* @see org.apache.dubbo.cache.support.lru.LruCache
* @see org.apache.dubbo.cache.support.jcache.JCacheFactory
* @see org.apache.dubbo.cache.support.jcache.JCache
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache
* @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory
* @see org.apache.dubbo.cache.support.expiring.ExpiringCache
*
*/
@Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY)
public class CacheFilter implements Filter {
private CacheFactory cacheFactory;
/**
* Dubbo will populate and set the cache factory instance based on service/method/consumer/provider configured
* cache attribute value. Dubbo will search for the class name implementing configured <b>cache</b> in file org.apache.dubbo.cache.CacheFactory
* under META-INF sub folders.
*
* @param cacheFactory instance of CacheFactory based on <b>cache</b> type
*/
public void setCacheFactory(CacheFactory cacheFactory) {
this.cacheFactory = cacheFactory;
}
/**
* If cache is configured, dubbo will invoke method on each method call. If cache value is returned by cache store
* then it will return otherwise call the remote method and return value. If remote method's return value has error
* then it will not cache the value.
* @param invoker service
* @param invocation invocation.
* @return Cache returned value if found by the underlying cache store. If cache miss it will call target method.
* @throws RpcException
*/
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), CACHE_KEY))) {
Cache cache = cacheFactory.getCache(invoker.getUrl(), invocation);
if (cache != null) {
String key = StringUtils.toArgumentString(invocation.getArguments());
Object value = cache.get(key);
if (value != null) {
if (value instanceof ValueWrapper) {
return AsyncRpcResult.newDefaultAsyncResult(((ValueWrapper) value).get(), invocation);
} else {
return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
}
}
Result result = invoker.invoke(invocation);
if (!result.hasException()) {
cache.put(key, new ValueWrapper(result.getValue()));
}
return result;
}
}
return invoker.invoke(invocation);
}
/**
* Cache value wrapper.
*/
static class ValueWrapper implements Serializable {
private static final long serialVersionUID = -1777337318019193256L;
private final Object value;
public ValueWrapper (Object value) {
this.value = value;
}
public Object get() {
return this.value;
}
}
}

View File

@ -1,72 +1,72 @@
/*
* 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.cache.support;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.CacheFactory;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY;
/**
* AbstractCacheFactory is a default implementation of {@link CacheFactory}. It abstract out the key formation from URL along with
* invocation method. It initially check if the value for key already present in own local in-memory store then it won't check underlying storage cache {@link Cache}.
* Internally it used {@link ConcurrentHashMap} to store do level-1 caching.
*
* @see CacheFactory
* @see org.apache.dubbo.cache.support.jcache.JCacheFactory
* @see org.apache.dubbo.cache.support.lru.LruCacheFactory
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
* @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory
*/
public abstract class AbstractCacheFactory implements CacheFactory {
/**
* This is used to store factory level-1 cached data.
*/
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
/**
* Takes URL and invocation instance and return cache instance for a given url.
* @param url url of the method
* @param invocation invocation context.
* @return Instance of cache store used as storage for caching return values.
*/
@Override
public Cache getCache(URL url, Invocation invocation) {
url = url.addParameter(METHOD_KEY, invocation.getMethodName());
String key = url.toFullString();
Cache cache = caches.get(key);
if (cache == null) {
caches.put(key, createCache(url));
cache = caches.get(key);
}
return cache;
}
/**
* Takes url as an method argument and return new instance of cache store implemented by AbstractCacheFactory subclass.
* @param url url of the method
* @return Create and return new instance of cache store used as storage for caching return values.
*/
protected abstract Cache createCache(URL url);
}
/*
* 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.cache.support;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.CacheFactory;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY;
/**
* AbstractCacheFactory is a default implementation of {@link CacheFactory}. It abstract out the key formation from URL along with
* invocation method. It initially check if the value for key already present in own local in-memory store then it won't check underlying storage cache {@link Cache}.
* Internally it used {@link ConcurrentHashMap} to store do level-1 caching.
*
* @see CacheFactory
* @see org.apache.dubbo.cache.support.jcache.JCacheFactory
* @see org.apache.dubbo.cache.support.lru.LruCacheFactory
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
* @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory
*/
public abstract class AbstractCacheFactory implements CacheFactory {
/**
* This is used to store factory level-1 cached data.
*/
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
/**
* Takes URL and invocation instance and return cache instance for a given url.
* @param url url of the method
* @param invocation invocation context.
* @return Instance of cache store used as storage for caching return values.
*/
@Override
public Cache getCache(URL url, Invocation invocation) {
url = url.addParameter(METHOD_KEY, invocation.getMethodName());
String key = url.toFullString();
Cache cache = caches.get(key);
if (cache == null) {
caches.put(key, createCache(url));
cache = caches.get(key);
}
return cache;
}
/**
* Takes url as an method argument and return new instance of cache store implemented by AbstractCacheFactory subclass.
* @param url url of the method
* @return Create and return new instance of cache store used as storage for caching return values.
*/
protected abstract Cache createCache(URL url);
}

View File

@ -1,87 +1,87 @@
/*
* 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.cache.support.jcache;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.StringUtils;
import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.cache.spi.CachingProvider;
import java.util.concurrent.TimeUnit;
import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY;
/**
* This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
* with value <b>jcache</b>, dubbo initialize the instance of this class using {@link JCacheFactory} to store method's returns value
* to server from store without making method call.
*
* @see Cache
* @see JCacheFactory
* @see org.apache.dubbo.cache.support.AbstractCacheFactory
* @see org.apache.dubbo.cache.filter.CacheFilter
*/
public class JCache implements org.apache.dubbo.cache.Cache {
private final Cache<Object, Object> store;
public JCache(URL url) {
String method = url.getParameter(METHOD_KEY, "");
String key = url.getAddress() + "." + url.getServiceKey() + "." + method;
// jcache parameter is the full-qualified class name of SPI implementation
String type = url.getParameter("jcache");
CachingProvider provider = StringUtils.isEmpty(type) ? Caching.getCachingProvider() : Caching.getCachingProvider(type);
CacheManager cacheManager = provider.getCacheManager();
Cache<Object, Object> cache = cacheManager.getCache(key);
if (cache == null) {
try {
//configure the cache
MutableConfiguration config =
new MutableConfiguration<>()
.setTypes(Object.class, Object.class)
.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MILLISECONDS, url.getMethodParameter(method, "cache.write.expire", 60 * 1000))))
.setStoreByValue(false)
.setManagementEnabled(true)
.setStatisticsEnabled(true);
cache = cacheManager.createCache(key, config);
} catch (CacheException e) {
// concurrent cache initialization
cache = cacheManager.getCache(key);
}
}
this.store = cache;
}
@Override
public void put(Object key, Object value) {
store.put(key, value);
}
@Override
public Object get(Object key) {
return store.get(key);
}
}
/*
* 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.cache.support.jcache;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.StringUtils;
import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.cache.spi.CachingProvider;
import java.util.concurrent.TimeUnit;
import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY;
/**
* This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
* with value <b>jcache</b>, dubbo initialize the instance of this class using {@link JCacheFactory} to store method's returns value
* to server from store without making method call.
*
* @see Cache
* @see JCacheFactory
* @see org.apache.dubbo.cache.support.AbstractCacheFactory
* @see org.apache.dubbo.cache.filter.CacheFilter
*/
public class JCache implements org.apache.dubbo.cache.Cache {
private final Cache<Object, Object> store;
public JCache(URL url) {
String method = url.getParameter(METHOD_KEY, "");
String key = url.getAddress() + "." + url.getServiceKey() + "." + method;
// jcache parameter is the full-qualified class name of SPI implementation
String type = url.getParameter("jcache");
CachingProvider provider = StringUtils.isEmpty(type) ? Caching.getCachingProvider() : Caching.getCachingProvider(type);
CacheManager cacheManager = provider.getCacheManager();
Cache<Object, Object> cache = cacheManager.getCache(key);
if (cache == null) {
try {
//configure the cache
MutableConfiguration config =
new MutableConfiguration<>()
.setTypes(Object.class, Object.class)
.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MILLISECONDS, url.getMethodParameter(method, "cache.write.expire", 60 * 1000))))
.setStoreByValue(false)
.setManagementEnabled(true)
.setStatisticsEnabled(true);
cache = cacheManager.createCache(key, config);
} catch (CacheException e) {
// concurrent cache initialization
cache = cacheManager.getCache(key);
}
}
this.store = cache;
}
@Override
public void put(Object key, Object value) {
store.put(key, value);
}
@Override
public Object get(Object key) {
return store.get(key);
}
}

View File

@ -1,48 +1,48 @@
/*
* 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.cache.support.jcache;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.support.AbstractCacheFactory;
import org.apache.dubbo.common.URL;
import javax.cache.spi.CachingProvider;
/**
* JCacheFactory is factory class to provide instance of javax spi cache.Implement {@link org.apache.dubbo.cache.CacheFactory} by
* extending {@link AbstractCacheFactory} and provide
* @see AbstractCacheFactory
* @see JCache
* @see org.apache.dubbo.cache.filter.CacheFilter
* @see Cache
* @see CachingProvider
* @see javax.cache.Cache
* @see javax.cache.CacheManager
*/
public class JCacheFactory extends AbstractCacheFactory {
/**
* Takes url as an method argument and return new instance of cache store implemented by JCache.
* @param url url of the method
* @return JCache instance of cache
*/
@Override
protected Cache createCache(URL url) {
return new JCache(url);
}
}
/*
* 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.cache.support.jcache;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.support.AbstractCacheFactory;
import org.apache.dubbo.common.URL;
import javax.cache.spi.CachingProvider;
/**
* JCacheFactory is factory class to provide instance of javax spi cache.Implement {@link org.apache.dubbo.cache.CacheFactory} by
* extending {@link AbstractCacheFactory} and provide
* @see AbstractCacheFactory
* @see JCache
* @see org.apache.dubbo.cache.filter.CacheFilter
* @see Cache
* @see CachingProvider
* @see javax.cache.Cache
* @see javax.cache.CacheManager
*/
public class JCacheFactory extends AbstractCacheFactory {
/**
* Takes url as an method argument and return new instance of cache store implemented by JCache.
* @param url url of the method
* @return JCache instance of cache
*/
@Override
protected Cache createCache(URL url) {
return new JCache(url);
}
}

View File

@ -1,80 +1,80 @@
/*
* 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.cache.support.lru;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.LRUCache;
import java.util.Map;
/**
* This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
* with value <b>lru</b>, dubbo initialize the instance of this class using {@link LruCacheFactory} to store method's returns value
* to server from store without making method call.
* <pre>
* e.g. 1) &lt;dubbo:service cache="lru" cache.size="5000"/&gt;
* 2) &lt;dubbo:consumer cache="lru" /&gt;
* </pre>
* <pre>
* LruCache uses url's <b>cache.size</b> value for its max store size, if nothing is provided then
* default value will be 1000
* </pre>
*
* @see Cache
* @see LruCacheFactory
* @see org.apache.dubbo.cache.support.AbstractCacheFactory
* @see org.apache.dubbo.cache.filter.CacheFilter
*/
public class LruCache implements Cache {
/**
* This is used to store cache records
*/
private final Map<Object, Object> store;
/**
* Initialize LruCache, it uses constructor argument <b>cache.size</b> value as its storage max size.
* If nothing is provided then it will use 1000 as default value.
* @param url A valid URL instance
*/
public LruCache(URL url) {
final int max = url.getParameter("cache.size", 1000);
this.store = new LRUCache<>(max);
}
/**
* API to store value against a key in the calling thread scope.
* @param key Unique identifier for the object being store.
* @param value Value getting store
*/
@Override
public void put(Object key, Object value) {
store.put(key, value);
}
/**
* API to return stored value using a key against the calling thread specific store.
* @param key Unique identifier for cache lookup
* @return Return stored object against key
*/
@Override
public Object get(Object key) {
return store.get(key);
}
}
/*
* 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.cache.support.lru;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.LRUCache;
import java.util.Map;
/**
* This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
* with value <b>lru</b>, dubbo initialize the instance of this class using {@link LruCacheFactory} to store method's returns value
* to server from store without making method call.
* <pre>
* e.g. 1) &lt;dubbo:service cache="lru" cache.size="5000"/&gt;
* 2) &lt;dubbo:consumer cache="lru" /&gt;
* </pre>
* <pre>
* LruCache uses url's <b>cache.size</b> value for its max store size, if nothing is provided then
* default value will be 1000
* </pre>
*
* @see Cache
* @see LruCacheFactory
* @see org.apache.dubbo.cache.support.AbstractCacheFactory
* @see org.apache.dubbo.cache.filter.CacheFilter
*/
public class LruCache implements Cache {
/**
* This is used to store cache records
*/
private final Map<Object, Object> store;
/**
* Initialize LruCache, it uses constructor argument <b>cache.size</b> value as its storage max size.
* If nothing is provided then it will use 1000 as default value.
* @param url A valid URL instance
*/
public LruCache(URL url) {
final int max = url.getParameter("cache.size", 1000);
this.store = new LRUCache<>(max);
}
/**
* API to store value against a key in the calling thread scope.
* @param key Unique identifier for the object being store.
* @param value Value getting store
*/
@Override
public void put(Object key, Object value) {
store.put(key, value);
}
/**
* API to return stored value using a key against the calling thread specific store.
* @param key Unique identifier for cache lookup
* @return Return stored object against key
*/
@Override
public Object get(Object key) {
return store.get(key);
}
}

View File

@ -1,43 +1,43 @@
/*
* 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.cache.support.lru;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.support.AbstractCacheFactory;
import org.apache.dubbo.common.URL;
/**
* Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide
* instance of new {@link LruCache}.
*
* @see AbstractCacheFactory
* @see LruCache
* @see Cache
*/
public class LruCacheFactory extends AbstractCacheFactory {
/**
* Takes url as an method argument and return new instance of cache store implemented by LruCache.
* @param url url of the method
* @return ThreadLocalCache instance of cache
*/
@Override
protected Cache createCache(URL url) {
return new LruCache(url);
}
}
/*
* 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.cache.support.lru;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.support.AbstractCacheFactory;
import org.apache.dubbo.common.URL;
/**
* Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide
* instance of new {@link LruCache}.
*
* @see AbstractCacheFactory
* @see LruCache
* @see Cache
*/
public class LruCacheFactory extends AbstractCacheFactory {
/**
* Takes url as an method argument and return new instance of cache store implemented by LruCache.
* @param url url of the method
* @return ThreadLocalCache instance of cache
*/
@Override
protected Cache createCache(URL url) {
return new LruCache(url);
}
}

View File

@ -1,77 +1,77 @@
/*
* 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.cache.support.threadlocal;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.common.URL;
import java.util.HashMap;
import java.util.Map;
/**
* This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
* with value <b>threadlocal</b>, dubbo initialize the instance of this class using {@link ThreadLocalCacheFactory} to store method's returns value
* to server from store without making method call.
* <pre>
* e.g. &lt;dubbo:service cache="threadlocal" /&gt;
* </pre>
* <pre>
* As this ThreadLocalCache stores key-value in memory without any expiry or delete support per thread wise, if number threads and number of key-value are high then jvm should be
* configured with appropriate memory.
* </pre>
*
* @see org.apache.dubbo.cache.support.AbstractCacheFactory
* @see org.apache.dubbo.cache.filter.CacheFilter
* @see Cache
*/
public class ThreadLocalCache implements Cache {
/**
* Thread local variable to store cached data.
*/
private final ThreadLocal<Map<Object, Object>> store;
/**
* Taken URL as an argument to create an instance of ThreadLocalCache. In this version of implementation constructor
* argument is not getting used in the scope of this class.
* @param url
*/
public ThreadLocalCache(URL url) {
this.store = ThreadLocal.withInitial(HashMap::new);
}
/**
* API to store value against a key in the calling thread scope.
* @param key Unique identifier for the object being store.
* @param value Value getting store
*/
@Override
public void put(Object key, Object value) {
store.get().put(key, value);
}
/**
* API to return stored value using a key against the calling thread specific store.
* @param key Unique identifier for cache lookup
* @return Return stored object against key
*/
@Override
public Object get(Object key) {
return store.get().get(key);
}
}
/*
* 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.cache.support.threadlocal;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.common.URL;
import java.util.HashMap;
import java.util.Map;
/**
* This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
* with value <b>threadlocal</b>, dubbo initialize the instance of this class using {@link ThreadLocalCacheFactory} to store method's returns value
* to server from store without making method call.
* <pre>
* e.g. &lt;dubbo:service cache="threadlocal" /&gt;
* </pre>
* <pre>
* As this ThreadLocalCache stores key-value in memory without any expiry or delete support per thread wise, if number threads and number of key-value are high then jvm should be
* configured with appropriate memory.
* </pre>
*
* @see org.apache.dubbo.cache.support.AbstractCacheFactory
* @see org.apache.dubbo.cache.filter.CacheFilter
* @see Cache
*/
public class ThreadLocalCache implements Cache {
/**
* Thread local variable to store cached data.
*/
private final ThreadLocal<Map<Object, Object>> store;
/**
* Taken URL as an argument to create an instance of ThreadLocalCache. In this version of implementation constructor
* argument is not getting used in the scope of this class.
* @param url
*/
public ThreadLocalCache(URL url) {
this.store = ThreadLocal.withInitial(HashMap::new);
}
/**
* API to store value against a key in the calling thread scope.
* @param key Unique identifier for the object being store.
* @param value Value getting store
*/
@Override
public void put(Object key, Object value) {
store.get().put(key, value);
}
/**
* API to return stored value using a key against the calling thread specific store.
* @param key Unique identifier for cache lookup
* @return Return stored object against key
*/
@Override
public Object get(Object key) {
return store.get().get(key);
}
}

View File

@ -1,43 +1,43 @@
/*
* 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.cache.support.threadlocal;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.support.AbstractCacheFactory;
import org.apache.dubbo.common.URL;
/**
* Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide
* instance of new {@link ThreadLocalCache}. Note about this class is, each thread does not have a local copy of factory.
*
* @see AbstractCacheFactory
* @see ThreadLocalCache
* @see Cache
*/
public class ThreadLocalCacheFactory extends AbstractCacheFactory {
/**
* Takes url as an method argument and return new instance of cache store implemented by ThreadLocalCache.
* @param url url of the method
* @return ThreadLocalCache instance of cache
*/
@Override
protected Cache createCache(URL url) {
return new ThreadLocalCache(url);
}
}
/*
* 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.cache.support.threadlocal;
import org.apache.dubbo.cache.Cache;
import org.apache.dubbo.cache.support.AbstractCacheFactory;
import org.apache.dubbo.common.URL;
/**
* Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide
* instance of new {@link ThreadLocalCache}. Note about this class is, each thread does not have a local copy of factory.
*
* @see AbstractCacheFactory
* @see ThreadLocalCache
* @see Cache
*/
public class ThreadLocalCacheFactory extends AbstractCacheFactory {
/**
* Takes url as an method argument and return new instance of cache store implemented by ThreadLocalCache.
* @param url url of the method
* @return ThreadLocalCache instance of cache
*/
@Override
protected Cache createCache(URL url) {
return new ThreadLocalCache(url);
}
}

View File

@ -1,4 +1,4 @@
threadlocal=org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
lru=org.apache.dubbo.cache.support.lru.LruCacheFactory
jcache=org.apache.dubbo.cache.support.jcache.JCacheFactory
threadlocal=org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
lru=org.apache.dubbo.cache.support.lru.LruCacheFactory
jcache=org.apache.dubbo.cache.support.jcache.JCacheFactory
expiring=org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory

View File

@ -19,8 +19,7 @@
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
<version>2.7.7-SNAPSHOT</version>
</parent>
<artifactId>dubbo-filter-validation</artifactId>
<packaging>jar</packaging>

View File

@ -1,39 +1,39 @@
/*
* 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.validation;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;
/**
* Instance of Validation interface provide instance of {@link Validator} based on the value of <b>validation</b> attribute.
*/
@SPI("jvalidation")
public interface Validation {
/**
* Return the instance of {@link Validator} for a given url.
* @param url Invocation url
* @return Instance of {@link Validator}
*/
@Adaptive(VALIDATION_KEY)
Validator getValidator(URL url);
}
/*
* 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.validation;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;
/**
* Instance of Validation interface provide instance of {@link Validator} based on the value of <b>validation</b> attribute.
*/
@SPI("jvalidation")
public interface Validation {
/**
* Return the instance of {@link Validator} for a given url.
* @param url Invocation url
* @return Instance of {@link Validator}
*/
@Adaptive(VALIDATION_KEY)
Validator getValidator(URL url);
}

View File

@ -1,27 +1,27 @@
/*
* 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.validation;
/**
* Instance of validator class is an extension to perform validation on method input parameter before the actual method invocation.
*
*/
public interface Validator {
void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception;
}
/*
* 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.validation;
/**
* Instance of validator class is an extension to perform validation on method input parameter before the actual method invocation.
*
*/
public interface Validator {
void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception;
}

View File

@ -1,104 +1,104 @@
/*
* 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.validation.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;
import javax.validation.ValidationException;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;
/**
* ValidationFilter invoke the validation by finding the right {@link Validator} instance based on the
* configured <b>validation</b> attribute value of invoker url before the actual method invocation.
*
* <pre>
* e.g. &lt;dubbo:method name="save" validation="jvalidation" /&gt;
* In the above configuration a validation has been configured of type jvalidation. On invocation of method <b>save</b>
* dubbo will invoke {@link org.apache.dubbo.validation.support.jvalidation.JValidator}
* </pre>
*
* To add a new type of validation
* <pre>
* e.g. &lt;dubbo:method name="save" validation="special" /&gt;
* where "special" is representing a validator for special character.
* </pre>
*
* developer needs to do
* <br/>
* 1)Implement a SpecialValidation.java class (package name xxx.yyy.zzz) either by implementing {@link Validation} or extending {@link org.apache.dubbo.validation.support.AbstractValidation} <br/>
* 2)Implement a SpecialValidator.java class (package name xxx.yyy.zzz) <br/>
* 3)Add an entry <b>special</b>=<b>xxx.yyy.zzz.SpecialValidation</b> under <b>META-INF folders org.apache.dubbo.validation.Validation file</b>.
*
* @see Validation
* @see Validator
* @see Filter
* @see org.apache.dubbo.validation.support.AbstractValidation
*/
@Activate(group = {CONSUMER, PROVIDER}, value = VALIDATION_KEY, order = 10000)
public class ValidationFilter implements Filter {
private Validation validation;
/**
* Sets the validation instance for ValidationFilter
* @param validation Validation instance injected by dubbo framework based on "validation" attribute value.
*/
public void setValidation(Validation validation) {
this.validation = validation;
}
/**
* Perform the validation of before invoking the actual method based on <b>validation</b> attribute value.
* @param invoker service
* @param invocation invocation.
* @return Method invocation result
* @throws RpcException Throws RpcException if validation failed or any other runtime exception occurred.
*/
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (validation != null && !invocation.getMethodName().startsWith("$")
&& ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), VALIDATION_KEY))) {
try {
Validator validator = validation.getValidator(invoker.getUrl());
if (validator != null) {
validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
}
} catch (RpcException e) {
throw e;
} catch (ValidationException e) {
// only use exception's message to avoid potential serialization issue
return AsyncRpcResult.newDefaultAsyncResult(new ValidationException(e.getMessage()), invocation);
} catch (Throwable t) {
return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
}
}
return invoker.invoke(invocation);
}
}
/*
* 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.validation.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;
import javax.validation.ValidationException;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;
/**
* ValidationFilter invoke the validation by finding the right {@link Validator} instance based on the
* configured <b>validation</b> attribute value of invoker url before the actual method invocation.
*
* <pre>
* e.g. &lt;dubbo:method name="save" validation="jvalidation" /&gt;
* In the above configuration a validation has been configured of type jvalidation. On invocation of method <b>save</b>
* dubbo will invoke {@link org.apache.dubbo.validation.support.jvalidation.JValidator}
* </pre>
*
* To add a new type of validation
* <pre>
* e.g. &lt;dubbo:method name="save" validation="special" /&gt;
* where "special" is representing a validator for special character.
* </pre>
*
* developer needs to do
* <br/>
* 1)Implement a SpecialValidation.java class (package name xxx.yyy.zzz) either by implementing {@link Validation} or extending {@link org.apache.dubbo.validation.support.AbstractValidation} <br/>
* 2)Implement a SpecialValidator.java class (package name xxx.yyy.zzz) <br/>
* 3)Add an entry <b>special</b>=<b>xxx.yyy.zzz.SpecialValidation</b> under <b>META-INF folders org.apache.dubbo.validation.Validation file</b>.
*
* @see Validation
* @see Validator
* @see Filter
* @see org.apache.dubbo.validation.support.AbstractValidation
*/
@Activate(group = {CONSUMER, PROVIDER}, value = VALIDATION_KEY, order = 10000)
public class ValidationFilter implements Filter {
private Validation validation;
/**
* Sets the validation instance for ValidationFilter
* @param validation Validation instance injected by dubbo framework based on "validation" attribute value.
*/
public void setValidation(Validation validation) {
this.validation = validation;
}
/**
* Perform the validation of before invoking the actual method based on <b>validation</b> attribute value.
* @param invoker service
* @param invocation invocation.
* @return Method invocation result
* @throws RpcException Throws RpcException if validation failed or any other runtime exception occurred.
*/
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (validation != null && !invocation.getMethodName().startsWith("$")
&& ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), VALIDATION_KEY))) {
try {
Validator validator = validation.getValidator(invoker.getUrl());
if (validator != null) {
validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
}
} catch (RpcException e) {
throw e;
} catch (ValidationException e) {
// only use exception's message to avoid potential serialization issue
return AsyncRpcResult.newDefaultAsyncResult(new ValidationException(e.getMessage()), invocation);
} catch (Throwable t) {
return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
}
}
return invoker.invoke(invocation);
}
}

View File

@ -1,51 +1,51 @@
/*
* 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.validation.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* AbstractValidation is abstract class for Validation interface. It helps low level Validation implementation classes
* by performing common task e.g. key formation, storing instance of validation class to avoid creation of unnecessary
* copy of validation instance and faster execution.
*
* @see Validation
* @see Validator
*/
public abstract class AbstractValidation implements Validation {
private final ConcurrentMap<String, Validator> validators = new ConcurrentHashMap<>();
@Override
public Validator getValidator(URL url) {
String key = url.toFullString();
Validator validator = validators.get(key);
if (validator == null) {
validators.put(key, createValidator(url));
validator = validators.get(key);
}
return validator;
}
protected abstract Validator createValidator(URL url);
}
/*
* 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.validation.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* AbstractValidation is abstract class for Validation interface. It helps low level Validation implementation classes
* by performing common task e.g. key formation, storing instance of validation class to avoid creation of unnecessary
* copy of validation instance and faster execution.
*
* @see Validation
* @see Validator
*/
public abstract class AbstractValidation implements Validation {
private final ConcurrentMap<String, Validator> validators = new ConcurrentHashMap<>();
@Override
public Validator getValidator(URL url) {
String key = url.toFullString();
Validator validator = validators.get(key);
if (validator == null) {
validators.put(key, createValidator(url));
validator = validators.get(key);
}
return validator;
}
protected abstract Validator createValidator(URL url);
}

View File

@ -1,40 +1,40 @@
/*
* 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.validation.support.jvalidation;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.validation.Validator;
import org.apache.dubbo.validation.support.AbstractValidation;
/**
* Creates a new instance of {@link Validator} using input argument url.
* @see AbstractValidation
* @see Validator
*/
public class JValidation extends AbstractValidation {
/**
* Return new instance of {@link JValidator}
* @param url Valid URL instance
* @return Instance of JValidator
*/
@Override
protected Validator createValidator(URL url) {
return new JValidator(url);
}
/*
* 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.validation.support.jvalidation;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.validation.Validator;
import org.apache.dubbo.validation.support.AbstractValidation;
/**
* Creates a new instance of {@link Validator} using input argument url.
* @see AbstractValidation
* @see Validator
*/
public class JValidation extends AbstractValidation {
/**
* Return new instance of {@link JValidator}
* @param url Valid URL instance
* @return Instance of JValidator
*/
@Override
protected Validator createValidator(URL url) {
return new JValidator(url);
}
}

View File

@ -1,330 +1,330 @@
/*
* 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.validation.support.jvalidation;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.bytecode.ClassGenerator;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ReflectUtils;
import org.apache.dubbo.validation.MethodValidated;
import org.apache.dubbo.validation.Validator;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.ByteMemberValue;
import javassist.bytecode.annotation.CharMemberValue;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.DoubleMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.FloatMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.LongMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.ShortMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import javax.validation.Constraint;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Implementation of JValidation. JValidation is invoked if configuration validation attribute value is 'jvalidation'.
* <pre>
* e.g. &lt;dubbo:method name="save" validation="jvalidation" /&gt;
* </pre>
*/
public class JValidator implements Validator {
private static final Logger logger = LoggerFactory.getLogger(JValidator.class);
private final Class<?> clazz;
private final Map<String, Class> methodClassMap;
private final javax.validation.Validator validator;
@SuppressWarnings({"unchecked", "rawtypes"})
public JValidator(URL url) {
this.clazz = ReflectUtils.forName(url.getServiceInterface());
String jvalidation = url.getParameter("jvalidation");
ValidatorFactory factory;
if (jvalidation != null && jvalidation.length() > 0) {
factory = Validation.byProvider((Class) ReflectUtils.forName(jvalidation)).configure().buildValidatorFactory();
} else {
factory = Validation.buildDefaultValidatorFactory();
}
this.validator = factory.getValidator();
this.methodClassMap = new ConcurrentHashMap<>();
}
private static Object getMethodParameterBean(Class<?> clazz, Method method, Object[] args) {
if (!hasConstraintParameter(method)) {
return null;
}
try {
String parameterClassName = generateMethodParameterClassName(clazz, method);
Class<?> parameterClass;
try {
parameterClass = Class.forName(parameterClassName, true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
parameterClass = generateMethodParameterClass(clazz, method, parameterClassName);
}
Object parameterBean = parameterClass.newInstance();
for (int i = 0; i < args.length; i++) {
Field field = parameterClass.getField(method.getName() + "Argument" + i);
field.set(parameterBean, args[i]);
}
return parameterBean;
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
return null;
}
}
/**
* try to generate methodParameterClass.
*
* @param clazz interface class
* @param method invoke method
* @param parameterClassName generated parameterClassName
* @return Class<?> generated methodParameterClass
* @throws Exception
*/
private static Class<?> generateMethodParameterClass(Class<?> clazz, Method method, String parameterClassName)
throws Exception {
ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
synchronized (parameterClassName.intern()) {
CtClass ctClass = null;
try {
ctClass = pool.getCtClass(parameterClassName);
} catch (NotFoundException ignore) {
}
if (null == ctClass) {
ctClass = pool.makeClass(parameterClassName);
ClassFile classFile = ctClass.getClassFile();
classFile.setVersionToJava5();
ctClass.addConstructor(CtNewConstructor.defaultConstructor(pool.getCtClass(parameterClassName)));
// parameter fields
Class<?>[] parameterTypes = method.getParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> type = parameterTypes[i];
Annotation[] annotations = parameterAnnotations[i];
AnnotationsAttribute attribute = new AnnotationsAttribute(classFile.getConstPool(), AnnotationsAttribute.visibleTag);
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation(
classFile.getConstPool(), pool.getCtClass(annotation.annotationType().getName()));
Method[] members = annotation.annotationType().getMethods();
for (Method member : members) {
if (Modifier.isPublic(member.getModifiers())
&& member.getParameterTypes().length == 0
&& member.getDeclaringClass() == annotation.annotationType()) {
Object value = member.invoke(annotation);
if (null != value) {
MemberValue memberValue = createMemberValue(
classFile.getConstPool(), pool.get(member.getReturnType().getName()), value);
ja.addMemberValue(member.getName(), memberValue);
}
}
}
attribute.addAnnotation(ja);
}
}
String fieldName = method.getName() + "Argument" + i;
CtField ctField = CtField.make("public " + type.getCanonicalName() + " " + fieldName + ";", pool.getCtClass(parameterClassName));
ctField.getFieldInfo().addAttribute(attribute);
ctClass.addField(ctField);
}
return ctClass.toClass(clazz.getClassLoader(), null);
} else {
return Class.forName(parameterClassName, true, clazz.getClassLoader());
}
}
}
private static String generateMethodParameterClassName(Class<?> clazz, Method method) {
StringBuilder builder = new StringBuilder().append(clazz.getName())
.append("_")
.append(toUpperMethodName(method.getName()))
.append("Parameter");
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
builder.append("_").append(parameterType.getName());
}
return builder.toString();
}
private static boolean hasConstraintParameter(Method method) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations != null && parameterAnnotations.length > 0) {
for (Annotation[] annotations : parameterAnnotations) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
return true;
}
}
}
}
return false;
}
private static String toUpperMethodName(String methodName) {
return methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
}
// Copy from javassist.bytecode.annotation.Annotation.createMemberValue(ConstPool, CtClass);
private static MemberValue createMemberValue(ConstPool cp, CtClass type, Object value) throws NotFoundException {
MemberValue memberValue = javassist.bytecode.annotation.Annotation.createMemberValue(cp, type);
if (memberValue instanceof BooleanMemberValue) {
((BooleanMemberValue) memberValue).setValue((Boolean) value);
} else if (memberValue instanceof ByteMemberValue) {
((ByteMemberValue) memberValue).setValue((Byte) value);
} else if (memberValue instanceof CharMemberValue) {
((CharMemberValue) memberValue).setValue((Character) value);
} else if (memberValue instanceof ShortMemberValue) {
((ShortMemberValue) memberValue).setValue((Short) value);
} else if (memberValue instanceof IntegerMemberValue) {
((IntegerMemberValue) memberValue).setValue((Integer) value);
} else if (memberValue instanceof LongMemberValue) {
((LongMemberValue) memberValue).setValue((Long) value);
} else if (memberValue instanceof FloatMemberValue) {
((FloatMemberValue) memberValue).setValue((Float) value);
} else if (memberValue instanceof DoubleMemberValue) {
((DoubleMemberValue) memberValue).setValue((Double) value);
} else if (memberValue instanceof ClassMemberValue) {
((ClassMemberValue) memberValue).setValue(((Class<?>) value).getName());
} else if (memberValue instanceof StringMemberValue) {
((StringMemberValue) memberValue).setValue((String) value);
} else if (memberValue instanceof EnumMemberValue) {
((EnumMemberValue) memberValue).setValue(((Enum<?>) value).name());
}
/* else if (memberValue instanceof AnnotationMemberValue) */
else if (memberValue instanceof ArrayMemberValue) {
CtClass arrayType = type.getComponentType();
int len = Array.getLength(value);
MemberValue[] members = new MemberValue[len];
for (int i = 0; i < len; i++) {
members[i] = createMemberValue(cp, arrayType, Array.get(value, i));
}
((ArrayMemberValue) memberValue).setValue(members);
}
return memberValue;
}
@Override
public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
List<Class<?>> groups = new ArrayList<>();
Class<?> methodClass = methodClass(methodName);
if (methodClass != null) {
groups.add(methodClass);
}
Set<ConstraintViolation<?>> violations = new HashSet<>();
Method method = clazz.getMethod(methodName, parameterTypes);
Class<?>[] methodClasses;
if (method.isAnnotationPresent(MethodValidated.class)){
methodClasses = method.getAnnotation(MethodValidated.class).value();
groups.addAll(Arrays.asList(methodClasses));
}
// add into default group
groups.add(0, Default.class);
groups.add(1, clazz);
// convert list to array
Class<?>[] classgroups = groups.toArray(new Class[groups.size()]);
Object parameterBean = getMethodParameterBean(clazz, method, arguments);
if (parameterBean != null) {
violations.addAll(validator.validate(parameterBean, classgroups ));
}
for (Object arg : arguments) {
validate(violations, arg, classgroups);
}
if (!violations.isEmpty()) {
logger.error("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations);
throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations);
}
}
private Class methodClass(String methodName) {
Class<?> methodClass = null;
String methodClassName = clazz.getName() + "$" + toUpperMethodName(methodName);
Class cached = methodClassMap.get(methodClassName);
if (cached != null) {
return cached == clazz ? null : cached;
}
try {
methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader());
methodClassMap.put(methodClassName, methodClass);
} catch (ClassNotFoundException e) {
methodClassMap.put(methodClassName, clazz);
}
return methodClass;
}
private void validate(Set<ConstraintViolation<?>> violations, Object arg, Class<?>... groups) {
if (arg != null && !ReflectUtils.isPrimitives(arg.getClass())) {
if (arg instanceof Object[]) {
for (Object item : (Object[]) arg) {
validate(violations, item, groups);
}
} else if (arg instanceof Collection) {
for (Object item : (Collection<?>) arg) {
validate(violations, item, groups);
}
} else if (arg instanceof Map) {
for (Map.Entry<?, ?> entry : ((Map<?, ?>) arg).entrySet()) {
validate(violations, entry.getKey(), groups);
validate(violations, entry.getValue(), groups);
}
} else {
violations.addAll(validator.validate(arg, groups));
}
}
}
}
/*
* 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.validation.support.jvalidation;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.bytecode.ClassGenerator;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ReflectUtils;
import org.apache.dubbo.validation.MethodValidated;
import org.apache.dubbo.validation.Validator;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.ByteMemberValue;
import javassist.bytecode.annotation.CharMemberValue;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.DoubleMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.FloatMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.LongMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.ShortMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import javax.validation.Constraint;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Implementation of JValidation. JValidation is invoked if configuration validation attribute value is 'jvalidation'.
* <pre>
* e.g. &lt;dubbo:method name="save" validation="jvalidation" /&gt;
* </pre>
*/
public class JValidator implements Validator {
private static final Logger logger = LoggerFactory.getLogger(JValidator.class);
private final Class<?> clazz;
private final Map<String, Class> methodClassMap;
private final javax.validation.Validator validator;
@SuppressWarnings({"unchecked", "rawtypes"})
public JValidator(URL url) {
this.clazz = ReflectUtils.forName(url.getServiceInterface());
String jvalidation = url.getParameter("jvalidation");
ValidatorFactory factory;
if (jvalidation != null && jvalidation.length() > 0) {
factory = Validation.byProvider((Class) ReflectUtils.forName(jvalidation)).configure().buildValidatorFactory();
} else {
factory = Validation.buildDefaultValidatorFactory();
}
this.validator = factory.getValidator();
this.methodClassMap = new ConcurrentHashMap<>();
}
private static Object getMethodParameterBean(Class<?> clazz, Method method, Object[] args) {
if (!hasConstraintParameter(method)) {
return null;
}
try {
String parameterClassName = generateMethodParameterClassName(clazz, method);
Class<?> parameterClass;
try {
parameterClass = Class.forName(parameterClassName, true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
parameterClass = generateMethodParameterClass(clazz, method, parameterClassName);
}
Object parameterBean = parameterClass.newInstance();
for (int i = 0; i < args.length; i++) {
Field field = parameterClass.getField(method.getName() + "Argument" + i);
field.set(parameterBean, args[i]);
}
return parameterBean;
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
return null;
}
}
/**
* try to generate methodParameterClass.
*
* @param clazz interface class
* @param method invoke method
* @param parameterClassName generated parameterClassName
* @return Class<?> generated methodParameterClass
* @throws Exception
*/
private static Class<?> generateMethodParameterClass(Class<?> clazz, Method method, String parameterClassName)
throws Exception {
ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
synchronized (parameterClassName.intern()) {
CtClass ctClass = null;
try {
ctClass = pool.getCtClass(parameterClassName);
} catch (NotFoundException ignore) {
}
if (null == ctClass) {
ctClass = pool.makeClass(parameterClassName);
ClassFile classFile = ctClass.getClassFile();
classFile.setVersionToJava5();
ctClass.addConstructor(CtNewConstructor.defaultConstructor(pool.getCtClass(parameterClassName)));
// parameter fields
Class<?>[] parameterTypes = method.getParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> type = parameterTypes[i];
Annotation[] annotations = parameterAnnotations[i];
AnnotationsAttribute attribute = new AnnotationsAttribute(classFile.getConstPool(), AnnotationsAttribute.visibleTag);
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation(
classFile.getConstPool(), pool.getCtClass(annotation.annotationType().getName()));
Method[] members = annotation.annotationType().getMethods();
for (Method member : members) {
if (Modifier.isPublic(member.getModifiers())
&& member.getParameterTypes().length == 0
&& member.getDeclaringClass() == annotation.annotationType()) {
Object value = member.invoke(annotation);
if (null != value) {
MemberValue memberValue = createMemberValue(
classFile.getConstPool(), pool.get(member.getReturnType().getName()), value);
ja.addMemberValue(member.getName(), memberValue);
}
}
}
attribute.addAnnotation(ja);
}
}
String fieldName = method.getName() + "Argument" + i;
CtField ctField = CtField.make("public " + type.getCanonicalName() + " " + fieldName + ";", pool.getCtClass(parameterClassName));
ctField.getFieldInfo().addAttribute(attribute);
ctClass.addField(ctField);
}
return ctClass.toClass(clazz.getClassLoader(), null);
} else {
return Class.forName(parameterClassName, true, clazz.getClassLoader());
}
}
}
private static String generateMethodParameterClassName(Class<?> clazz, Method method) {
StringBuilder builder = new StringBuilder().append(clazz.getName())
.append("_")
.append(toUpperMethoName(method.getName()))
.append("Parameter");
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
builder.append("_").append(parameterType.getName());
}
return builder.toString();
}
private static boolean hasConstraintParameter(Method method) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations != null && parameterAnnotations.length > 0) {
for (Annotation[] annotations : parameterAnnotations) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
return true;
}
}
}
}
return false;
}
private static String toUpperMethoName(String methodName) {
return methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
}
// Copy from javassist.bytecode.annotation.Annotation.createMemberValue(ConstPool, CtClass);
private static MemberValue createMemberValue(ConstPool cp, CtClass type, Object value) throws NotFoundException {
MemberValue memberValue = javassist.bytecode.annotation.Annotation.createMemberValue(cp, type);
if (memberValue instanceof BooleanMemberValue) {
((BooleanMemberValue) memberValue).setValue((Boolean) value);
} else if (memberValue instanceof ByteMemberValue) {
((ByteMemberValue) memberValue).setValue((Byte) value);
} else if (memberValue instanceof CharMemberValue) {
((CharMemberValue) memberValue).setValue((Character) value);
} else if (memberValue instanceof ShortMemberValue) {
((ShortMemberValue) memberValue).setValue((Short) value);
} else if (memberValue instanceof IntegerMemberValue) {
((IntegerMemberValue) memberValue).setValue((Integer) value);
} else if (memberValue instanceof LongMemberValue) {
((LongMemberValue) memberValue).setValue((Long) value);
} else if (memberValue instanceof FloatMemberValue) {
((FloatMemberValue) memberValue).setValue((Float) value);
} else if (memberValue instanceof DoubleMemberValue) {
((DoubleMemberValue) memberValue).setValue((Double) value);
} else if (memberValue instanceof ClassMemberValue) {
((ClassMemberValue) memberValue).setValue(((Class<?>) value).getName());
} else if (memberValue instanceof StringMemberValue) {
((StringMemberValue) memberValue).setValue((String) value);
} else if (memberValue instanceof EnumMemberValue) {
((EnumMemberValue) memberValue).setValue(((Enum<?>) value).name());
}
/* else if (memberValue instanceof AnnotationMemberValue) */
else if (memberValue instanceof ArrayMemberValue) {
CtClass arrayType = type.getComponentType();
int len = Array.getLength(value);
MemberValue[] members = new MemberValue[len];
for (int i = 0; i < len; i++) {
members[i] = createMemberValue(cp, arrayType, Array.get(value, i));
}
((ArrayMemberValue) memberValue).setValue(members);
}
return memberValue;
}
@Override
public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
List<Class<?>> groups = new ArrayList<>();
Class<?> methodClass = methodClass(methodName);
if (methodClass != null) {
groups.add(methodClass);
}
Set<ConstraintViolation<?>> violations = new HashSet<>();
Method method = clazz.getMethod(methodName, parameterTypes);
Class<?>[] methodClasses;
if (method.isAnnotationPresent(MethodValidated.class)){
methodClasses = method.getAnnotation(MethodValidated.class).value();
groups.addAll(Arrays.asList(methodClasses));
}
// add into default group
groups.add(0, Default.class);
groups.add(1, clazz);
// convert list to array
Class<?>[] classgroups = groups.toArray(new Class[groups.size()]);
Object parameterBean = getMethodParameterBean(clazz, method, arguments);
if (parameterBean != null) {
violations.addAll(validator.validate(parameterBean, classgroups ));
}
for (Object arg : arguments) {
validate(violations, arg, classgroups);
}
if (!violations.isEmpty()) {
logger.error("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations);
throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations);
}
}
private Class methodClass(String methodName) {
Class<?> methodClass = null;
String methodClassName = clazz.getName() + "$" + toUpperMethoName(methodName);
Class cached = methodClassMap.get(methodClassName);
if (cached != null) {
return cached == clazz ? null : cached;
}
try {
methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader());
methodClassMap.put(methodClassName, methodClass);
} catch (ClassNotFoundException e) {
methodClassMap.put(methodClassName, clazz);
}
return methodClass;
}
private void validate(Set<ConstraintViolation<?>> violations, Object arg, Class<?>... groups) {
if (arg != null && !ReflectUtils.isPrimitives(arg.getClass())) {
if (arg instanceof Object[]) {
for (Object item : (Object[]) arg) {
validate(violations, item, groups);
}
} else if (arg instanceof Collection) {
for (Object item : (Collection<?>) arg) {
validate(violations, item, groups);
}
} else if (arg instanceof Map) {
for (Map.Entry<?, ?> entry : ((Map<?, ?>) arg).entrySet()) {
validate(violations, entry.getKey(), groups);
validate(violations, entry.getValue(), groups);
}
} else {
violations.addAll(validator.validate(arg, groups));
}
}
}
}

View File

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-metadata-report-consul</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter-consul</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.ecwid.consul</groupId>
<artifactId>consul-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,139 +0,0 @@
/*
* 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.store.consul;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
import org.apache.dubbo.metadata.report.support.ConfigCenterBasedMetadataReport;
import org.apache.dubbo.rpc.RpcException;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.kv.model.GetValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* metadata report impl for consul
*
* @deprecated 2.7.8 This class will be removed in the future, {@link ConfigCenterBasedMetadataReport} as a substitute.
*/
@Deprecated
public class ConsulMetadataReport extends AbstractMetadataReport {
private static final Logger logger = LoggerFactory.getLogger(ConsulMetadataReport.class);
private static final int DEFAULT_PORT = 8500;
private ConsulClient client;
public ConsulMetadataReport(URL url) {
super(url);
String host = url.getHost();
int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT;
client = new ConsulClient(host, port);
}
@Override
protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) {
this.storeMetadata(providerMetadataIdentifier, serviceDefinitions);
}
@Override
protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) {
this.storeMetadata(consumerMetadataIdentifier, value);
}
@Override
protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) {
this.storeMetadata(serviceMetadataIdentifier, URL.encode(url.toFullString()));
}
@Override
protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) {
this.deleteMetadata(serviceMetadataIdentifier);
}
@Override
protected List<String> doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
//todo encode and decode
String content = getMetadata(metadataIdentifier);
if (StringUtils.isEmpty(content)) {
return Collections.emptyList();
}
return new ArrayList<String>(Arrays.asList(URL.decode(content)));
}
@Override
protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) {
this.storeMetadata(subscriberMetadataIdentifier, urlListStr);
}
@Override
protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
return getMetadata(subscriberMetadataIdentifier);
}
private void storeMetadata(BaseMetadataIdentifier identifier, String v) {
try {
client.setKVValue(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), v);
} catch (Throwable t) {
logger.error("Failed to put " + identifier + " to consul " + v + ", cause: " + t.getMessage(), t);
throw new RpcException("Failed to put " + identifier + " to consul " + v + ", cause: " + t.getMessage(), t);
}
}
private void deleteMetadata(BaseMetadataIdentifier identifier) {
try {
client.deleteKVValue(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
} catch (Throwable t) {
logger.error("Failed to delete " + identifier + " from consul , cause: " + t.getMessage(), t);
throw new RpcException("Failed to delete " + identifier + " from consul , cause: " + t.getMessage(), t);
}
}
private String getMetadata(BaseMetadataIdentifier identifier) {
try {
Response<GetValue> value = client.getKVValue(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
//FIXME CHECK
if (value != null && value.getValue() != null) {
//todo check decode value and value diff
return value.getValue().getValue();
}
return null;
} catch (Throwable t) {
logger.error("Failed to get " + identifier + " from consul , cause: " + t.getMessage(), t);
throw new RpcException("Failed to get " + identifier + " from consul , cause: " + t.getMessage(), t);
}
}
@Override
public String getServiceDefinition(MetadataIdentifier metadataIdentifier) {
return getMetadata(metadataIdentifier);
}
}

View File

@ -1,31 +0,0 @@
/*
* 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.store.consul;
import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
import org.apache.dubbo.metadata.report.support.ConfigCenterBasedMetadataReportFactory;
/**
* metadata report factory impl for consul
*/
public class ConsulMetadataReportFactory extends ConfigCenterBasedMetadataReportFactory {
public ConsulMetadataReportFactory() {
super(KeyTypeEnum.UNIQUE_KEY);
}
}

View File

@ -1 +0,0 @@
consul=org.apache.dubbo.metadata.store.consul.ConsulMetadataReportFactory

View File

@ -1,70 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-metadata-report-etcd</artifactId>
<properties>
<skipIntegrationTests>true</skipIntegrationTests>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-etcd3</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>io.etcd</groupId>
<artifactId>jetcd-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>${skipIntegrationTests}</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,150 +0,0 @@
/*
* 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.
*/
/*
* 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.store.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
import org.apache.dubbo.remoting.etcd.jetcd.JEtcdClient;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
/**
* Report Metadata to Etcd
*/
public class EtcdMetadataReport extends AbstractMetadataReport {
private final static Logger logger = LoggerFactory.getLogger(EtcdMetadataReport.class);
private final String root;
/**
* The etcd client
*/
private final JEtcdClient etcdClient;
public EtcdMetadataReport(URL url) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
if (!group.startsWith(PATH_SEPARATOR)) {
group = PATH_SEPARATOR + group;
}
this.root = group;
etcdClient = new JEtcdClient(url);
}
@Override
protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) {
storeMetadata(providerMetadataIdentifier, serviceDefinitions);
}
@Override
protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) {
storeMetadata(consumerMetadataIdentifier, value);
}
@Override
protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) {
String key = getNodeKey(serviceMetadataIdentifier);
if (!etcdClient.put(key, URL.encode(url.toFullString()))) {
logger.error("Failed to put " + serviceMetadataIdentifier + " to etcd, value: " + url);
}
}
@Override
protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) {
etcdClient.delete(getNodeKey(serviceMetadataIdentifier));
}
@Override
protected List<String> doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
String content = etcdClient.getKVValue(getNodeKey(metadataIdentifier));
if (StringUtils.isEmpty(content)) {
return Collections.emptyList();
}
return new ArrayList<String>(Arrays.asList(URL.decode(content)));
}
@Override
protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) {
String key = getNodeKey(subscriberMetadataIdentifier);
if (!etcdClient.put(key, urlListStr)) {
logger.error("Failed to put " + subscriberMetadataIdentifier + " to etcd, value: " + urlListStr);
}
}
@Override
protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
return etcdClient.getKVValue(getNodeKey(subscriberMetadataIdentifier));
}
@Override
public String getServiceDefinition(MetadataIdentifier metadataIdentifier) {
return etcdClient.getKVValue(getNodeKey(metadataIdentifier));
}
private void storeMetadata(MetadataIdentifier identifier, String v) {
String key = getNodeKey(identifier);
if (!etcdClient.put(key, v)) {
logger.error("Failed to put " + identifier + " to etcd, value: " + v);
}
}
String getNodeKey(BaseMetadataIdentifier identifier) {
return toRootDir() + identifier.getUniqueKey(KeyTypeEnum.PATH);
}
String toRootDir() {
if (root.equals(PATH_SEPARATOR)) {
return root;
}
return root + PATH_SEPARATOR;
}
}

View File

@ -1,50 +0,0 @@
/*
* 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.
*/
/*
* 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.store.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.metadata.report.MetadataReport;
import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory;
/**
* MetadataReportFactory to create an Etcd based {@link MetadataReport}.
*/
public class EtcdMetadataReportFactory extends AbstractMetadataReportFactory {
@Override
public MetadataReport createMetadataReport(URL url) {
return new EtcdMetadataReport(url);
}
}

View File

@ -1 +0,0 @@
etcd=org.apache.dubbo.metadata.store.etcd.EtcdMetadataReportFactory

View File

@ -1,28 +0,0 @@
/*
* 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.store.etcd;
/**
* Test interface for Etcd metadata report
*/
public interface EtcdMetadata4TstService {
int getCounter();
void printResult(String var);
}

View File

@ -1,259 +0,0 @@
/*
* 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.store.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
import com.google.gson.Gson;
import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.kv.GetResponse;
import io.etcd.jetcd.launcher.EtcdCluster;
import io.etcd.jetcd.launcher.EtcdClusterFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
/**
* Unit test for etcd metadata report
*/
public class EtcdMetadataReportTest {
private static final String TEST_SERVICE = "org.apache.dubbo.metadata.store.etcd.EtcdMetadata4TstService";
private EtcdCluster etcdCluster = EtcdClusterFactory.buildCluster(getClass().getSimpleName(), 1, false, false);
private Client etcdClientForTest;
private EtcdMetadataReport etcdMetadataReport;
private URL registryUrl;
private EtcdMetadataReportFactory etcdMetadataReportFactory;
@BeforeEach
public void setUp() {
etcdCluster.start();
etcdClientForTest = Client.builder().endpoints(etcdCluster.getClientEndpoints()).build();
List<URI> clientEndPoints = etcdCluster.getClientEndpoints();
this.registryUrl = URL.valueOf("etcd://" + clientEndPoints.get(0).getHost() + ":" + clientEndPoints.get(0).getPort());
etcdMetadataReportFactory = new EtcdMetadataReportFactory();
this.etcdMetadataReport = (EtcdMetadataReport) etcdMetadataReportFactory.createMetadataReport(registryUrl);
}
@AfterEach
public void tearDown() throws Exception {
etcdCluster.close();
}
@Test
public void testStoreProvider() throws Exception {
String version = "1.0.0";
String group = null;
String application = "etcd-metdata-report-test";
String r = etcdMetadataReport.getServiceDefinition(new MetadataIdentifier(TEST_SERVICE, version, group, "provider", application));
Assertions.assertNull(r);
MetadataIdentifier providerIdentifier =
storeProvider(etcdMetadataReport, TEST_SERVICE, version, group, application);
CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
etcdMetadataReport.getNodeKey(providerIdentifier), StandardCharsets.UTF_8));
String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
Assertions.assertNotNull(fileContent);
Gson gson = new Gson();
FullServiceDefinition fullServiceDefinition = gson.fromJson(fileContent, FullServiceDefinition.class);
Assertions.assertEquals(fullServiceDefinition.getParameters().get("paramTest"), "etcdTest");
r = etcdMetadataReport.getServiceDefinition(new MetadataIdentifier(TEST_SERVICE, version, group, "provider", application));
Assertions.assertNotNull(r);
}
@Test
public void testStoreConsumer() throws Exception {
String version = "1.0.0";
String group = null;
String application = "etc-metadata-report-consumer-test";
MetadataIdentifier consumerIdentifier = storeConsumer(etcdMetadataReport, TEST_SERVICE, version, group, application);
CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
etcdMetadataReport.getNodeKey(consumerIdentifier), StandardCharsets.UTF_8));
String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
Assertions.assertNotNull(fileContent);
Assertions.assertEquals(fileContent, "{\"paramConsumerTest\":\"etcdConsumer\"}");
}
@Test
public void testDoSaveMetadata() throws ExecutionException, InterruptedException {
String version = "1.0.0";
String group = null;
String application = "etc-metadata-report-consumer-test";
String revision = "90980";
String protocol = "xxx";
URL url = generateURL(TEST_SERVICE, version, group, application);
ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, version,
group, "provider", revision, protocol);
etcdMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
etcdMetadataReport.getNodeKey(serviceMetadataIdentifier), StandardCharsets.UTF_8));
String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
Assertions.assertNotNull(fileContent);
Assertions.assertEquals(fileContent, URL.encode(url.toFullString()));
}
@Test
public void testDoRemoveMetadata() throws ExecutionException, InterruptedException {
String version = "1.0.0";
String group = null;
String application = "etc-metadata-report-consumer-test";
String revision = "90980";
String protocol = "xxx";
URL url = generateURL(TEST_SERVICE, version, group, application);
ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, version,
group, "provider", revision, protocol);
etcdMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
etcdMetadataReport.getNodeKey(serviceMetadataIdentifier), StandardCharsets.UTF_8));
String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
Assertions.assertNotNull(fileContent);
etcdMetadataReport.doRemoveMetadata(serviceMetadataIdentifier);
response = etcdClientForTest.getKVClient().get(ByteSequence.from(
etcdMetadataReport.getNodeKey(serviceMetadataIdentifier), StandardCharsets.UTF_8));
Assertions.assertTrue(response.get().getKvs().isEmpty());
}
@Test
public void testDoGetExportedURLs() throws ExecutionException, InterruptedException {
String version = "1.0.0";
String group = null;
String application = "etc-metadata-report-consumer-test";
String revision = "90980";
String protocol = "xxx";
URL url = generateURL(TEST_SERVICE, version, group, application);
ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, version,
group, "provider", revision, protocol);
etcdMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
List<String> r = etcdMetadataReport.doGetExportedURLs(serviceMetadataIdentifier);
Assertions.assertTrue(r.size() == 1);
String fileContent = r.get(0);
Assertions.assertNotNull(fileContent);
Assertions.assertEquals(fileContent, url.toFullString());
}
@Test
public void testDoSaveSubscriberData() throws ExecutionException, InterruptedException {
String version = "1.0.0";
String group = null;
String application = "etc-metadata-report-consumer-test";
String revision = "90980";
String protocol = "xxx";
URL url = generateURL(TEST_SERVICE, version, group, application);
SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision);
Gson gson = new Gson();
String r = gson.toJson(Arrays.asList(url));
etcdMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r);
CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
etcdMetadataReport.getNodeKey(subscriberMetadataIdentifier), StandardCharsets.UTF_8));
String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
Assertions.assertNotNull(fileContent);
Assertions.assertEquals(fileContent, r);
}
@Test
public void testDoGetSubscribedURLs() throws ExecutionException, InterruptedException {
String version = "1.0.0";
String group = null;
String application = "etc-metadata-report-consumer-test";
String revision = "90980";
String protocol = "xxx";
URL url = generateURL(TEST_SERVICE, version, group, application);
SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision);
Gson gson = new Gson();
String r = gson.toJson(Arrays.asList(url));
etcdMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r);
CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
etcdMetadataReport.getNodeKey(subscriberMetadataIdentifier), StandardCharsets.UTF_8));
String fileContent = etcdMetadataReport.doGetSubscribedURLs(subscriberMetadataIdentifier);
Assertions.assertNotNull(fileContent);
Assertions.assertEquals(fileContent, r);
}
private MetadataIdentifier storeProvider(EtcdMetadataReport etcdMetadataReport, String interfaceName, String version,
String group, String application)
throws ClassNotFoundException, InterruptedException {
URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName +
"?paramTest=etcdTest&version=" + version + "&application="
+ application + (group == null ? "" : "&group=" + group));
MetadataIdentifier providerMetadataIdentifier =
new MetadataIdentifier(interfaceName, version, group, PROVIDER_SIDE, application);
Class interfaceClass = Class.forName(interfaceName);
FullServiceDefinition fullServiceDefinition =
ServiceDefinitionBuilder.buildFullDefinition(interfaceClass, url.getParameters());
etcdMetadataReport.storeProviderMetadata(providerMetadataIdentifier, fullServiceDefinition);
Thread.sleep(1000);
return providerMetadataIdentifier;
}
private URL generateURL(String interfaceName, String version, String group, String application) {
URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":8989/" + interfaceName +
"?paramTest=etcdTest&version=" + version + "&application="
+ application + (group == null ? "" : "&group=" + group));
return url;
}
private MetadataIdentifier storeConsumer(EtcdMetadataReport etcdMetadataReport, String interfaceName,
String version, String group, String application) throws InterruptedException {
MetadataIdentifier consumerIdentifier = new MetadataIdentifier(interfaceName, version, group, CONSUMER_SIDE, application);
Map<String, String> tmp = new HashMap<>();
tmp.put("paramConsumerTest", "etcdConsumer");
etcdMetadataReport.storeConsumerMetadata(consumerIdentifier, tmp);
Thread.sleep(1000);
return consumerIdentifier;
}
}

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-metadata-report-nacos</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter-nacos</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,149 +0,0 @@
/*
* 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.store.nacos;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
import org.apache.dubbo.metadata.report.support.ConfigCenterBasedMetadataReport;
import org.apache.dubbo.rpc.RpcException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* metadata report impl for nacos
*
* @deprecated 2.7.8 This class will be removed in the future, {@link ConfigCenterBasedMetadataReport} as a substitute.
*/
@Deprecated
public class NacosMetadataReport extends AbstractMetadataReport {
private static final Logger logger = LoggerFactory.getLogger(NacosMetadataReport.class);
private final DynamicConfiguration dynamicConfiguration;
/**
* The group used to store metadata in Nacos
*/
private String group;
public NacosMetadataReport(URL url, DynamicConfiguration dynamicConfiguration) {
super(url);
this.dynamicConfiguration = dynamicConfiguration;
}
@Override
protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) {
this.storeMetadata(providerMetadataIdentifier, serviceDefinitions);
}
@Override
protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) {
this.storeMetadata(consumerMetadataIdentifier, value);
}
@Override
protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) {
storeMetadata(serviceMetadataIdentifier, URL.encode(url.toFullString()));
}
@Override
protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) {
deleteMetadata(serviceMetadataIdentifier);
}
@Override
protected List<String> doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
String content = getConfig(metadataIdentifier);
if (StringUtils.isEmpty(content)) {
return Collections.emptyList();
}
return new ArrayList<String>(Arrays.asList(URL.decode(content)));
}
@Override
protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) {
storeMetadata(subscriberMetadataIdentifier, urlListStr);
}
@Override
protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
return getConfig(subscriberMetadataIdentifier);
}
@Override
public String getServiceDefinition(MetadataIdentifier metadataIdentifier) {
return getConfig(metadataIdentifier);
}
@Override
public boolean saveExportedURLs(String serviceName, String exportedServicesRevision, String exportedURLsContent) {
return dynamicConfiguration.publishConfig(serviceName, exportedServicesRevision, exportedURLsContent);
}
@Override
public String getExportedURLsContent(String serviceName, String exportedServicesRevision) {
return dynamicConfiguration.getConfig(serviceName, exportedServicesRevision, SECONDS.toMillis(3));
}
private void storeMetadata(BaseMetadataIdentifier identifier, String value) {
try {
boolean publishResult = dynamicConfiguration.publishConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group, value);
if (!publishResult) {
throw new RuntimeException("publish nacos metadata failed");
}
} catch (Throwable t) {
logger.error("Failed to put " + identifier + " to nacos " + value + ", cause: " + t.getMessage(), t);
throw new RpcException("Failed to put " + identifier + " to nacos " + value + ", cause: " + t.getMessage(), t);
}
}
private void deleteMetadata(BaseMetadataIdentifier identifier) {
try {
boolean publishResult = dynamicConfiguration.removeConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group);
if (!publishResult) {
throw new RuntimeException("remove nacos metadata failed");
}
} catch (Throwable t) {
logger.error("Failed to remove " + identifier + " from nacos , cause: " + t.getMessage(), t);
throw new RpcException("Failed to remove " + identifier + " from nacos , cause: " + t.getMessage(), t);
}
}
private String getConfig(BaseMetadataIdentifier identifier) {
try {
return dynamicConfiguration.getConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group, 300);
} catch (Throwable t) {
logger.error("Failed to get " + identifier + " from nacos , cause: " + t.getMessage(), t);
throw new RpcException("Failed to get " + identifier + " from nacos , cause: " + t.getMessage(), t);
}
}
}

View File

@ -1,31 +0,0 @@
/*
* 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.store.nacos;
import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
import org.apache.dubbo.metadata.report.support.ConfigCenterBasedMetadataReportFactory;
/**
* metadata report factory impl for nacos
*/
public class NacosMetadataReportFactory extends ConfigCenterBasedMetadataReportFactory {
public NacosMetadataReportFactory() {
super(KeyTypeEnum.UNIQUE_KEY);
}
}

View File

@ -1 +0,0 @@
nacos=org.apache.dubbo.metadata.store.nacos.NacosMetadataReportFactory

View File

@ -1,28 +0,0 @@
/*
* 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.store.nacos;
/**
* Test interface for Nacos metadata report
*/
public interface NacosMetadata4TstService {
int getCounter();
void printResult(String var);
}

View File

@ -18,8 +18,7 @@
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
<version>2.7.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -29,13 +29,10 @@
<packaging>pom</packaging>
<modules>
<module>dubbo-metadata-api</module>
<!-- <module>dubbo-metadata-definition-protobuf</module>-->
<module>dubbo-metadata-definition-protobuf</module>
<module>dubbo-metadata-processor</module>
<module>dubbo-metadata-report-zookeeper</module>
<!-- <module>dubbo-metadata-report-redis</module>-->
<!-- <module>dubbo-metadata-report-consul</module>-->
<!-- <module>dubbo-metadata-report-etcd</module>-->
<!-- <module>dubbo-metadata-report-nacos</module>-->
<!-- <module>dubbo-metadata-processor</module>-->
<module>dubbo-metadata-report-redis</module>
</modules>
</project>

View File

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dubbo-registry</artifactId>
<groupId>org.apache.dubbo</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-registry-consul</artifactId>
<properties>
<skipIntegrationTests>true</skipIntegrationTests>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.ecwid.consul</groupId>
<artifactId>consul-api</artifactId>
</dependency>
<dependency>
<groupId>com.pszymczyk.consul</groupId>
<artifactId>embedded-consul</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>${skipIntegrationTests}</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,39 +0,0 @@
/*
* 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.registry.consul;
/**
* @author cvictory ON 2019-08-02
*/
public class AbstractConsulRegistry {
static final String SERVICE_TAG = "dubbo";
static final String URL_META_KEY = "url";
static final String WATCH_TIMEOUT = "consul-watch-timeout";
static final String CHECK_PASS_INTERVAL = "consul-check-pass-interval";
static final String DEREGISTER_AFTER = "consul-deregister-critical-service-after";
static final int DEFAULT_PORT = 8500;
// default watch timeout in millisecond
static final int DEFAULT_WATCH_TIMEOUT = 60 * 1000;
// default time-to-live in millisecond
static final long DEFAULT_CHECK_PASS_INTERVAL = 16000L;
// default deregister critical server after
static final String DEFAULT_DEREGISTER_TIME = "20s";
}

View File

@ -1,380 +0,0 @@
/*
* 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.registry.consul;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.apache.dubbo.rpc.RpcException;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.NewService;
import com.ecwid.consul.v1.catalog.CatalogServicesRequest;
import com.ecwid.consul.v1.health.HealthServicesRequest;
import com.ecwid.consul.v1.health.model.HealthService;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static java.util.concurrent.Executors.newCachedThreadPool;
import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL;
import static org.apache.dubbo.registry.Constants.PROVIDER_PROTOCOL;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.CHECK_PASS_INTERVAL;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_CHECK_PASS_INTERVAL;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_DEREGISTER_TIME;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_PORT;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_WATCH_TIMEOUT;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEREGISTER_AFTER;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.SERVICE_TAG;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.URL_META_KEY;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.WATCH_TIMEOUT;
import static org.apache.dubbo.rpc.Constants.TOKEN_KEY;
/**
* registry center implementation for consul
*/
public class ConsulRegistry extends FailbackRegistry {
private static final Logger logger = LoggerFactory.getLogger(ConsulRegistry.class);
private ConsulClient client;
private long checkPassInterval;
private ExecutorService notifierExecutor = newCachedThreadPool(
new NamedThreadFactory("dubbo-consul-notifier", true));
private ConcurrentMap<URL, ConsulNotifier> notifiers = new ConcurrentHashMap<>();
private ScheduledExecutorService ttlConsulCheckExecutor;
/**
* The ACL token
*/
private String token;
public ConsulRegistry(URL url) {
super(url);
token = url.getParameter(TOKEN_KEY, (String) null);
String host = url.getHost();
int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT;
client = new ConsulClient(host, port);
checkPassInterval = url.getParameter(CHECK_PASS_INTERVAL, DEFAULT_CHECK_PASS_INTERVAL);
ttlConsulCheckExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("Ttl-Consul-Check-Executor", true));
ttlConsulCheckExecutor.scheduleAtFixedRate(this::checkPass, checkPassInterval / 8,
checkPassInterval / 8, TimeUnit.MILLISECONDS);
}
@Override
public void register(URL url) {
if (isConsumerSide(url)) {
return;
}
super.register(url);
}
@Override
public void doRegister(URL url) {
if (token == null) {
client.agentServiceRegister(buildService(url));
} else {
client.agentServiceRegister(buildService(url), token);
}
}
@Override
public void unregister(URL url) {
if (isConsumerSide(url)) {
return;
}
super.unregister(url);
}
@Override
public void doUnregister(URL url) {
if (token == null) {
client.agentServiceDeregister(buildId(url));
} else {
client.agentServiceDeregister(buildId(url), token);
}
}
@Override
public void subscribe(URL url, NotifyListener listener) {
if (isProviderSide(url)) {
return;
}
super.subscribe(url, listener);
}
@Override
public void doSubscribe(URL url, NotifyListener listener) {
Long index;
List<URL> urls;
if (ANY_VALUE.equals(url.getServiceInterface())) {
Response<Map<String, List<String>>> response = getAllServices(-1, buildWatchTimeout(url));
index = response.getConsulIndex();
List<HealthService> services = getHealthServices(response.getValue());
urls = convert(services, url);
} else {
String service = url.getServiceInterface();
Response<List<HealthService>> response = getHealthServices(service, -1, buildWatchTimeout(url));
index = response.getConsulIndex();
urls = convert(response.getValue(), url);
}
notify(url, listener, urls);
ConsulNotifier notifier = notifiers.computeIfAbsent(url, k -> new ConsulNotifier(url, index));
notifierExecutor.submit(notifier);
}
@Override
public void unsubscribe(URL url, NotifyListener listener) {
if (isProviderSide(url)) {
return;
}
super.unsubscribe(url, listener);
}
@Override
public void doUnsubscribe(URL url, NotifyListener listener) {
ConsulNotifier notifier = notifiers.remove(url);
notifier.stop();
}
@Override
public List<URL> lookup(URL url) {
if (url == null) {
throw new IllegalArgumentException("lookup url == null");
}
try {
String service = url.getServiceKey();
Response<List<HealthService>> result = getHealthServices(service, -1, buildWatchTimeout(url));
if (result == null || result.getValue() == null || result.getValue().isEmpty()) {
return new ArrayList<>();
} else {
return convert(result.getValue(), url);
}
} catch (Throwable e) {
throw new RpcException("Failed to lookup " + url + " from consul " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
@Override
public boolean isAvailable() {
return client.getAgentSelf() != null;
}
@Override
public void destroy() {
super.destroy();
notifierExecutor.shutdown();
ttlConsulCheckExecutor.shutdown();
}
private void checkPass() {
for (URL url : getRegistered()) {
String checkId = buildId(url);
try {
if (token == null) {
client.agentCheckPass("service:" + checkId);
} else {
client.agentCheckPass("service:" + checkId, null, token);
}
if (logger.isDebugEnabled()) {
logger.debug("check pass for url: " + url + " with check id: " + checkId);
}
} catch (Throwable t) {
logger.warn("fail to check pass for url: " + url + ", check id is: " + checkId, t);
}
}
}
private Response<List<HealthService>> getHealthServices(String service, long index, int watchTimeout) {
HealthServicesRequest request = HealthServicesRequest.newBuilder()
.setTag(SERVICE_TAG)
.setQueryParams(new QueryParams(watchTimeout, index))
.setPassing(true)
.setToken(token)
.build();
return client.getHealthServices(service, request);
}
private Response<Map<String, List<String>>> getAllServices(long index, int watchTimeout) {
CatalogServicesRequest request = CatalogServicesRequest.newBuilder()
.setQueryParams(new QueryParams(watchTimeout, index))
.setToken(token)
.build();
return client.getCatalogServices(request);
}
private List<HealthService> getHealthServices(Map<String, List<String>> services) {
return services.entrySet().stream()
.filter(s -> s.getValue().contains(SERVICE_TAG))
.map(s -> getHealthServices(s.getKey(), -1, -1).getValue())
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
private boolean isConsumerSide(URL url) {
return url.getProtocol().equals(CONSUMER_PROTOCOL);
}
private boolean isProviderSide(URL url) {
return url.getProtocol().equals(PROVIDER_PROTOCOL);
}
private List<URL> convert(List<HealthService> services, URL consumerURL) {
if (CollectionUtils.isEmpty(services)) {
return emptyURL(consumerURL);
}
return services.stream()
.map(HealthService::getService)
.filter(Objects::nonNull)
.map(HealthService.Service::getMeta)
.filter(m -> m != null && m.containsKey(URL_META_KEY))
.map(m -> m.get(URL_META_KEY))
.map(URL::valueOf)
.filter(url -> UrlUtils.isMatch(consumerURL, url))
.collect(Collectors.toList());
}
private List<URL> emptyURL(URL consumerURL) {
// No Category Parameter
URL empty = URLBuilder.from(consumerURL)
.setProtocol(EMPTY_PROTOCOL)
.removeParameter(CATEGORY_KEY)
.build();
List<URL> result = new ArrayList<URL>();
result.add(empty);
return result;
}
private NewService buildService(URL url) {
NewService service = new NewService();
service.setAddress(url.getHost());
service.setPort(url.getPort());
service.setId(buildId(url));
service.setName(url.getServiceInterface());
service.setCheck(buildCheck(url));
service.setTags(buildTags(url));
service.setMeta(Collections.singletonMap(URL_META_KEY, url.toFullString()));
return service;
}
private List<String> buildTags(URL url) {
Map<String, String> params = url.getParameters();
List<String> tags = params.entrySet().stream()
.map(k -> k.getKey() + "=" + k.getValue())
.collect(Collectors.toList());
tags.add(SERVICE_TAG);
return tags;
}
private String buildId(URL url) {
// let's simply use url's hashcode to generate unique service id for now
return Integer.toHexString(url.hashCode());
}
private NewService.Check buildCheck(URL url) {
NewService.Check check = new NewService.Check();
check.setTtl((checkPassInterval / 1000) + "s");
check.setDeregisterCriticalServiceAfter(url.getParameter(DEREGISTER_AFTER, DEFAULT_DEREGISTER_TIME));
return check;
}
private int buildWatchTimeout(URL url) {
return url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT) / 1000;
}
private class ConsulNotifier implements Runnable {
private URL url;
private long consulIndex;
private boolean running;
ConsulNotifier(URL url, long consulIndex) {
this.url = url;
this.consulIndex = consulIndex;
this.running = true;
}
@Override
public void run() {
while (this.running) {
if (ANY_VALUE.equals(url.getServiceInterface())) {
processServices();
} else {
processService();
}
}
}
private void processService() {
String service = url.getServiceKey();
Response<List<HealthService>> response = getHealthServices(service, consulIndex, buildWatchTimeout(url));
Long currentIndex = response.getConsulIndex();
if (currentIndex != null && currentIndex > consulIndex) {
consulIndex = currentIndex;
List<HealthService> services = response.getValue();
List<URL> urls = convert(services, url);
for (NotifyListener listener : getSubscribed().get(url)) {
doNotify(url, listener, urls);
}
}
}
private void processServices() {
Response<Map<String, List<String>>> response = getAllServices(consulIndex, buildWatchTimeout(url));
Long currentIndex = response.getConsulIndex();
if (currentIndex != null && currentIndex > consulIndex) {
consulIndex = currentIndex;
List<HealthService> services = getHealthServices(response.getValue());
List<URL> urls = convert(services, url);
for (NotifyListener listener : getSubscribed().get(url)) {
doNotify(url, listener, urls);
}
}
}
void stop() {
this.running = false;
}
}
}

View File

@ -1,32 +0,0 @@
/*
* 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.registry.consul;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
/**
* registry center factory implementation for consul
*/
public class ConsulRegistryFactory extends AbstractRegistryFactory {
@Override
protected Registry createRegistry(URL url) {
return new ConsulRegistry(url);
}
}

View File

@ -1,474 +0,0 @@
/*
* 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.registry.consul;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.event.EventListener;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceDiscovery;
import org.apache.dubbo.registry.client.ServiceInstance;
import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
import com.ecwid.consul.v1.ConsistencyMode;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.NewService;
import com.ecwid.consul.v1.catalog.CatalogServicesRequest;
import com.ecwid.consul.v1.health.HealthServicesRequest;
import com.ecwid.consul.v1.health.model.HealthService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static java.util.concurrent.Executors.newCachedThreadPool;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR;
import static org.apache.dubbo.common.constants.CommonConstants.SEMICOLON_SPLIT_PATTERN;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.CHECK_PASS_INTERVAL;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_CHECK_PASS_INTERVAL;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_DEREGISTER_TIME;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_PORT;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_WATCH_TIMEOUT;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEREGISTER_AFTER;
import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.WATCH_TIMEOUT;
import static org.apache.dubbo.registry.consul.ConsulParameter.ACL_TOKEN;
import static org.apache.dubbo.registry.consul.ConsulParameter.CONSISTENCY_MODE;
import static org.apache.dubbo.registry.consul.ConsulParameter.DEFAULT_ZONE_METADATA_NAME;
import static org.apache.dubbo.registry.consul.ConsulParameter.INSTANCE_GROUP;
import static org.apache.dubbo.registry.consul.ConsulParameter.INSTANCE_ZONE;
import static org.apache.dubbo.registry.consul.ConsulParameter.TAGS;
/**
* 2019-07-31
*/
public class ConsulServiceDiscovery implements ServiceDiscovery, EventListener<ServiceInstancesChangedEvent> {
private static final Logger logger = LoggerFactory.getLogger(ConsulServiceDiscovery.class);
private static final String QUERY_TAG = "consul_query_tag";
private static final String REGISTER_TAG = "consul_register_tag";
private List<String> registeringTags = new ArrayList<>();
private String tag;
private ConsulClient client;
private ExecutorService notifierExecutor = newCachedThreadPool(
new NamedThreadFactory("dubbo-service-discovery-consul-notifier", true));
private ConsulNotifier notifier;
private TtlScheduler ttlScheduler;
private long checkPassInterval;
private URL url;
private String aclToken;
private List<String> tags;
private ConsistencyMode consistencyMode;
private String defaultZoneMetadataName;
/**
* Service instance zone.
*/
private String instanceZone;
/**
* Service instance group.
*/
private String instanceGroup;
@Override
public void onEvent(ServiceInstancesChangedEvent event) {
}
@Override
public void initialize(URL registryURL) throws Exception {
this.url = registryURL;
String host = url.getHost();
int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT;
checkPassInterval = url.getParameter(CHECK_PASS_INTERVAL, DEFAULT_CHECK_PASS_INTERVAL);
client = new ConsulClient(host, port);
ttlScheduler = new TtlScheduler(checkPassInterval, client);
this.tag = registryURL.getParameter(QUERY_TAG);
this.registeringTags.addAll(getRegisteringTags(url));
this.aclToken = ACL_TOKEN.getValue(registryURL);
this.tags = getTags(registryURL);
this.consistencyMode = getConsistencyMode(registryURL);
this.defaultZoneMetadataName = DEFAULT_ZONE_METADATA_NAME.getValue(registryURL);
this.instanceZone = INSTANCE_ZONE.getValue(registryURL);
this.instanceGroup = INSTANCE_GROUP.getValue(registryURL);
}
/**
* Get the {@link ConsistencyMode}
*
* @param registryURL the {@link URL} of registry
* @return non-null, {@link ConsistencyMode#DEFAULT} as default
* @sine 2.7.8
*/
private ConsistencyMode getConsistencyMode(URL registryURL) {
String value = CONSISTENCY_MODE.getValue(registryURL);
if (StringUtils.isNotEmpty(value)) {
return ConsistencyMode.valueOf(value);
}
return ConsistencyMode.DEFAULT;
}
/**
* Get the "tags" from the {@link URL} of registry
*
* @param registryURL the {@link URL} of registry
* @return non-null
* @sine 2.7.8
*/
private List<String> getTags(URL registryURL) {
String value = TAGS.getValue(registryURL);
return StringUtils.splitToList(value, COMMA_SEPARATOR_CHAR);
}
@Override
public URL getUrl() {
return url;
}
private List<String> getRegisteringTags(URL url) {
List<String> tags = new ArrayList<>();
String rawTag = url.getParameter(REGISTER_TAG);
if (StringUtils.isNotEmpty(rawTag)) {
tags.addAll(Arrays.asList(SEMICOLON_SPLIT_PATTERN.split(rawTag)));
}
return tags;
}
@Override
public void destroy() {
if (notifier != null) {
notifier.stop();
}
notifier = null;
notifierExecutor.shutdownNow();
ttlScheduler.stop();
}
@Override
public void register(ServiceInstance serviceInstance) throws RuntimeException {
NewService consulService = buildService(serviceInstance);
ttlScheduler.add(consulService.getId());
client.agentServiceRegister(consulService, aclToken);
}
@Override
public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
if (notifier == null) {
String serviceName = listener.getServiceNames();
Response<List<HealthService>> response = getHealthServices(serviceName, -1, buildWatchTimeout());
Long consulIndex = response.getConsulIndex();
notifier = new ConsulNotifier(serviceName, consulIndex);
}
notifierExecutor.execute(notifier);
}
@Override
public void update(ServiceInstance serviceInstance) throws RuntimeException {
// TODO
// client.catalogRegister(buildCatalogService(serviceInstance));
}
@Override
public void unregister(ServiceInstance serviceInstance) throws RuntimeException {
String id = buildId(serviceInstance);
ttlScheduler.remove(id);
client.agentServiceDeregister(id, aclToken);
}
@Override
public Set<String> getServices() {
CatalogServicesRequest request = CatalogServicesRequest.newBuilder()
.setQueryParams(QueryParams.DEFAULT)
.setToken(aclToken)
.build();
return this.client.getCatalogServices(request).getValue().keySet();
}
@Override
public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
Response<List<HealthService>> response = getHealthServices(serviceName, -1, buildWatchTimeout());
Long consulIndex = response.getConsulIndex();
if (notifier == null) {
notifier = new ConsulNotifier(serviceName, consulIndex);
}
return convert(response.getValue());
}
private List<ServiceInstance> convert(List<HealthService> services) {
return services.stream()
.map(HealthService::getService)
.map(service -> {
ServiceInstance instance = new DefaultServiceInstance(
service.getId(),
service.getService(),
service.getAddress(),
service.getPort());
instance.getMetadata().putAll(getMetadata(service));
return instance;
})
.collect(Collectors.toList());
}
private Response<List<HealthService>> getHealthServices(String service, long index, int watchTimeout) {
HealthServicesRequest request = HealthServicesRequest.newBuilder()
.setTag(tag)
.setQueryParams(new QueryParams(watchTimeout, index))
.setPassing(true)
.build();
return client.getHealthServices(service, request);
}
private Map<String, String> getMetadata(HealthService.Service service) {
Map<String, String> metadata = service.getMeta();
metadata = decodeMetadata(metadata);
if (CollectionUtils.isEmptyMap(metadata)) {
metadata = getScCompatibleMetadata(service.getTags());
}
return metadata;
}
private Map<String, String> getScCompatibleMetadata(List<String> tags) {
LinkedHashMap<String, String> metadata = new LinkedHashMap<>();
if (tags != null) {
for (String tag : tags) {
String[] parts = StringUtils.delimitedListToStringArray(tag, "=");
switch (parts.length) {
case 0:
break;
case 1:
metadata.put(parts[0], parts[0]);
break;
case 2:
metadata.put(parts[0], parts[1]);
break;
default:
String[] end = Arrays.copyOfRange(parts, 1, parts.length);
metadata.put(parts[0], StringUtils.arrayToDelimitedString(end, "="));
break;
}
}
}
return metadata;
}
private NewService buildService(ServiceInstance serviceInstance) {
NewService service = new NewService();
service.setAddress(serviceInstance.getHost());
service.setPort(serviceInstance.getPort());
service.setId(buildId(serviceInstance));
service.setName(serviceInstance.getServiceName());
service.setCheck(buildCheck(serviceInstance));
service.setTags(buildTags(serviceInstance));
return service;
}
private String buildId(ServiceInstance serviceInstance) {
return Integer.toHexString(serviceInstance.hashCode());
}
private List<String> buildTags(ServiceInstance serviceInstance) {
List<String> tags = new LinkedList<>(this.tags);
if (StringUtils.isNotEmpty(instanceZone)) {
tags.add(defaultZoneMetadataName + "=" + instanceZone);
}
if (StringUtils.isNotEmpty(instanceGroup)) {
tags.add("group=" + instanceGroup);
}
Map<String, String> params = serviceInstance.getMetadata();
params.keySet().stream()
.map(k -> k + "=" + params.get(k))
.forEach(tags::add);
tags.addAll(registeringTags);
return tags;
}
private Map<String, String> buildMetadata(ServiceInstance serviceInstance) {
Map<String, String> metadata = new LinkedHashMap<>();
metadata.putAll(getScCompatibleMetadata(registeringTags));
if (CollectionUtils.isNotEmptyMap(serviceInstance.getMetadata())) {
metadata.putAll(serviceInstance.getMetadata());
}
metadata = encodeMetadata(metadata);
return metadata;
}
private Map<String, String> encodeMetadata(Map<String, String> metadata) {
if (metadata == null) {
return metadata;
}
Map<String, String> encoded = new HashMap<>(metadata.size());
metadata.forEach((k, v) -> encoded.put(Base64.getEncoder().encodeToString(k.getBytes()), v));
return encoded;
}
private Map<String, String> decodeMetadata(Map<String, String> metadata) {
if (metadata == null) {
return metadata;
}
Map<String, String> decoded = new HashMap<>(metadata.size());
metadata.forEach((k, v) -> decoded.put(new String(Base64.getDecoder().decode(k)), v));
return decoded;
}
private NewService.Check buildCheck(ServiceInstance serviceInstance) {
NewService.Check check = new NewService.Check();
check.setTtl((checkPassInterval / 1000) + "s");
String deregister = serviceInstance.getMetadata().get(DEREGISTER_AFTER);
check.setDeregisterCriticalServiceAfter(deregister == null ? DEFAULT_DEREGISTER_TIME : deregister);
return check;
}
private int buildWatchTimeout() {
return url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT) / 1000;
}
private class ConsulNotifier implements Runnable {
private String serviceName;
private long consulIndex;
private boolean running;
ConsulNotifier(String serviceName, long consulIndex) {
this.serviceName = serviceName;
this.consulIndex = consulIndex;
this.running = true;
}
@Override
public void run() {
while (this.running) {
processService();
}
}
private void processService() {
Response<List<HealthService>> response = getHealthServices(serviceName, consulIndex, Integer.MAX_VALUE);
Long currentIndex = response.getConsulIndex();
if (currentIndex != null && currentIndex > consulIndex) {
consulIndex = currentIndex;
List<HealthService> services = response.getValue();
List<ServiceInstance> serviceInstances = convert(services);
dispatchServiceInstancesChangedEvent(serviceName, serviceInstances);
}
}
void stop() {
this.running = false;
}
}
private static class TtlScheduler {
private static final Logger logger = LoggerFactory.getLogger(TtlScheduler.class);
private final Map<String, ScheduledFuture> serviceHeartbeats = new ConcurrentHashMap<>();
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private long checkInterval;
private ConsulClient client;
public TtlScheduler(long checkInterval, ConsulClient client) {
this.checkInterval = checkInterval;
this.client = client;
}
/**
* Add a service to the checks loop.
*
* @param instanceId instance id
*/
public void add(String instanceId) {
ScheduledFuture task = this.scheduler.scheduleAtFixedRate(
new ConsulHeartbeatTask(instanceId),
checkInterval / 8,
checkInterval / 8,
TimeUnit.MILLISECONDS);
ScheduledFuture previousTask = this.serviceHeartbeats.put(instanceId, task);
if (previousTask != null) {
previousTask.cancel(true);
}
}
public void remove(String instanceId) {
ScheduledFuture task = this.serviceHeartbeats.get(instanceId);
if (task != null) {
task.cancel(true);
}
this.serviceHeartbeats.remove(instanceId);
}
private class ConsulHeartbeatTask implements Runnable {
private String checkId;
ConsulHeartbeatTask(String serviceId) {
this.checkId = serviceId;
if (!this.checkId.startsWith("service:")) {
this.checkId = "service:" + this.checkId;
}
}
@Override
public void run() {
TtlScheduler.this.client.agentCheckPass(this.checkId);
if (logger.isDebugEnabled()) {
logger.debug("Sending consul heartbeat for: " + this.checkId);
}
}
}
public void stop() {
scheduler.shutdownNow();
}
}
}

View File

@ -1 +0,0 @@
consul=org.apache.dubbo.registry.consul.ConsulRegistryFactory

View File

@ -1,135 +0,0 @@
/*
* 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.registry.consul;
import com.pszymczyk.consul.ConsulProcess;
import com.pszymczyk.consul.ConsulStarterBuilder;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.status.Status;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.status.RegistryStatusChecker;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
public class ConsulRegistryTest {
private static ConsulProcess consul;
private ConsulRegistry consulRegistry;
private String service = "org.apache.dubbo.test.injvmServie";
private URL serviceUrl = URL.valueOf("consul://127.0.0.1:" + NetUtils.getAvailablePort() + "/" + service + "?notify=false&methods=test1,test2");
private URL registryUrl;
private ConsulRegistryFactory consulRegistryFactory;
@BeforeEach
public void setUp() throws Exception {
this.consul = ConsulStarterBuilder.consulStarter()
.build()
.start();
this.registryUrl = URL.valueOf("consul://localhost:" + consul.getHttpPort());
consulRegistryFactory = new ConsulRegistryFactory();
this.consulRegistry = (ConsulRegistry) consulRegistryFactory.createRegistry(registryUrl);
}
@AfterEach
public void tearDown() throws Exception {
consul.close();
this.consulRegistry.destroy();
}
@Test
public void testRegister() {
Set<URL> registered;
for (int i = 0; i < 2; i++) {
consulRegistry.register(serviceUrl);
registered = consulRegistry.getRegistered();
assertThat(registered.contains(serviceUrl), is(true));
}
registered = consulRegistry.getRegistered();
assertThat(registered.size(), is(1));
}
@Test
public void testSubscribe() {
NotifyListener listener = mock(NotifyListener.class);
consulRegistry.subscribe(serviceUrl, listener);
Map<URL, Set<NotifyListener>> subscribed = consulRegistry.getSubscribed();
assertThat(subscribed.size(), is(1));
assertThat(subscribed.get(serviceUrl).size(), is(1));
consulRegistry.unsubscribe(serviceUrl, listener);
subscribed = consulRegistry.getSubscribed();
assertThat(subscribed.size(), is(1));
assertThat(subscribed.get(serviceUrl).size(), is(0));
}
@Test
public void testAvailable() {
consulRegistry.register(serviceUrl);
assertThat(consulRegistry.isAvailable(), is(true));
// consulRegistry.destroy();
// assertThat(consulRegistry.isAvailable(), is(false));
}
@Test
public void testLookup() throws InterruptedException {
List<URL> lookup = consulRegistry.lookup(serviceUrl);
assertThat(lookup.size(), is(0));
consulRegistry.register(serviceUrl);
Thread.sleep(5000);
lookup = consulRegistry.lookup(serviceUrl);
assertThat(lookup.size(), is(1));
}
@Test
public void testStatusChecker() {
RegistryStatusChecker registryStatusChecker = new RegistryStatusChecker();
Status status = registryStatusChecker.check();
assertThat(status.getLevel(), is(Status.Level.UNKNOWN));
Registry registry = consulRegistryFactory.getRegistry(registryUrl);
assertThat(registry, not(nullValue()));
status = registryStatusChecker.check();
assertThat(status.getLevel(), is(Status.Level.OK));
registry.register(serviceUrl);
status = registryStatusChecker.check();
assertThat(status.getLevel(), is(Status.Level.OK));
}
}

View File

@ -1,108 +0,0 @@
/*
* 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.registry.consul;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceInstance;
import com.pszymczyk.consul.ConsulProcess;
import com.pszymczyk.consul.ConsulStarterBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static java.lang.String.valueOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ConsulServiceDiscoveryTest {
private URL url;
private ConsulServiceDiscovery consulServiceDiscovery;
private ConsulProcess consul;
private static final String SERVICE_NAME = "A";
private static final String LOCALHOST = "127.0.0.1";
@BeforeEach
public void init() throws Exception {
this.consul = ConsulStarterBuilder.consulStarter()
.build()
.start();
url = URL.valueOf("consul://localhost:" + consul.getHttpPort());
consulServiceDiscovery = new ConsulServiceDiscovery();
consulServiceDiscovery.initialize(url);
}
@AfterEach
public void close() {
consulServiceDiscovery.destroy();
consul.close();
}
@Test
public void testRegistration() throws InterruptedException {
DefaultServiceInstance serviceInstance = createServiceInstance(SERVICE_NAME, LOCALHOST, NetUtils.getAvailablePort());
consulServiceDiscovery.register(serviceInstance);
Thread.sleep(5000);
List<ServiceInstance> serviceInstances = consulServiceDiscovery.getInstances(SERVICE_NAME);
assertEquals(serviceInstances.size(), 1);
assertEquals(serviceInstances.get(0).getId(), Integer.toHexString(serviceInstance.hashCode()));
assertEquals(serviceInstances.get(0).getHost(), serviceInstance.getHost());
assertEquals(serviceInstances.get(0).getServiceName(), serviceInstance.getServiceName());
assertEquals(serviceInstances.get(0).getPort(), serviceInstance.getPort());
consulServiceDiscovery.unregister(serviceInstance);
Thread.sleep(5000);
serviceInstances = consulServiceDiscovery.getInstances(SERVICE_NAME);
System.out.println(serviceInstances.size());
assertTrue(serviceInstances.isEmpty());
}
private DefaultServiceInstance createServiceInstance(String serviceName, String host, int port) {
return new DefaultServiceInstance(host + ":" + port, serviceName, host, port);
}
@Test
public void testGetInstances() throws Exception {
String serviceName = "ConsulTest77Service";
assertTrue(consulServiceDiscovery.getInstances(serviceName).isEmpty());
int portA = NetUtils.getAvailablePort();
int portB = NetUtils.getAvailablePort();
consulServiceDiscovery.register(new DefaultServiceInstance(valueOf(System.nanoTime()), serviceName, "127.0.0.1", portA));
consulServiceDiscovery.register(new DefaultServiceInstance(valueOf(System.nanoTime()), serviceName, "127.0.0.1", portB));
Thread.sleep(5000);
Assertions.assertFalse(consulServiceDiscovery.getInstances(serviceName).isEmpty());
List<String> r = convertToIpPort(consulServiceDiscovery.getInstances(serviceName));
assertTrue(r.contains("127.0.0.1:" + portA));
assertTrue(r.contains("127.0.0.1:" + portB));
}
private List<String> convertToIpPort(List<ServiceInstance> serviceInstances) {
List<String> result = new ArrayList<>();
for (ServiceInstance serviceInstance : serviceInstances) {
result.add(serviceInstance.getHost() + ":" + serviceInstance.getPort());
}
return result;
}
}

View File

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-registry-default</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The default registry module of dubbo project</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-injvm</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,161 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ExecutorUtil;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryService;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.Invoker;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import static org.apache.dubbo.registry.Constants.REGISTRY_RECONNECT_PERIOD_KEY;
/**
* DubboRegistry
*/
public class DubboRegistry extends FailbackRegistry {
private final static Logger logger = LoggerFactory.getLogger(DubboRegistry.class);
// Reconnecting detection cycle: 3 seconds (unit:millisecond)
private static final int RECONNECT_PERIOD_DEFAULT = 3 * 1000;
// Scheduled executor service
private final ScheduledExecutorService reconnectTimer = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));
// Reconnection timer, regular check connection is available. If unavailable, unlimited reconnection.
private final ScheduledFuture<?> reconnectFuture;
// The lock for client acquisition process, lock the creation process of the client instance to prevent repeated clients
private final ReentrantLock clientLock = new ReentrantLock();
private final Invoker<RegistryService> registryInvoker;
private final RegistryService registryService;
/**
* The time in milliseconds the reconnectTimer will wait
*/
private final int reconnectPeriod;
public DubboRegistry(Invoker<RegistryService> registryInvoker, RegistryService registryService) {
super(registryInvoker.getUrl());
this.registryInvoker = registryInvoker;
this.registryService = registryService;
// Start reconnection timer
this.reconnectPeriod = registryInvoker.getUrl().getParameter(REGISTRY_RECONNECT_PERIOD_KEY, RECONNECT_PERIOD_DEFAULT);
reconnectFuture = reconnectTimer.scheduleWithFixedDelay(() -> {
// Check and connect to the registry
try {
connect();
} catch (Throwable t) { // Defensive fault tolerance
logger.error("Unexpected error occur at reconnect, cause: " + t.getMessage(), t);
}
}, reconnectPeriod, reconnectPeriod, TimeUnit.MILLISECONDS);
}
protected final void connect() {
try {
// Check whether or not it is connected
if (isAvailable()) {
return;
}
if (logger.isInfoEnabled()) {
logger.info("Reconnect to registry " + getUrl());
}
clientLock.lock();
try {
// Double check whether or not it is connected
if (isAvailable()) {
return;
}
recover();
} finally {
clientLock.unlock();
}
} catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
if (getUrl().getParameter(Constants.CHECK_KEY, true)) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
throw new RuntimeException(t.getMessage(), t);
}
logger.error("Failed to connect to registry " + getUrl().getAddress() + " from provider/consumer " + NetUtils.getLocalHost() + " use dubbo " + Version.getVersion() + ", cause: " + t.getMessage(), t);
}
}
@Override
public boolean isAvailable() {
if (registryInvoker == null) {
return false;
}
return registryInvoker.isAvailable();
}
@Override
public void destroy() {
super.destroy();
try {
// Cancel the reconnection timer
ExecutorUtil.cancelScheduledFuture(reconnectFuture);
} catch (Throwable t) {
logger.warn("Failed to cancel reconnect timer", t);
}
registryInvoker.destroy();
ExecutorUtil.gracefulShutdown(reconnectTimer, reconnectPeriod);
}
@Override
public void doRegister(URL url) {
registryService.register(url);
}
@Override
public void doUnregister(URL url) {
registryService.unregister(url);
}
@Override
public void doSubscribe(URL url, NotifyListener listener) {
registryService.subscribe(url, listener);
}
@Override
public void doUnsubscribe(URL url, NotifyListener listener) {
registryService.unsubscribe(url, listener);
}
@Override
public List<URL> lookup(URL url) {
return registryService.lookup(url);
}
}

View File

@ -1,118 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.bytecode.Wrapper;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryService;
import org.apache.dubbo.registry.integration.RegistryDirectory;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.ProxyFactory;
import org.apache.dubbo.rpc.cluster.Cluster;
import org.apache.dubbo.rpc.cluster.RouterChain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import static org.apache.dubbo.common.constants.CommonConstants.CALLBACK_INSTANCES_LIMIT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.LAZY_CONNECT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY;
import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL;
import static org.apache.dubbo.remoting.Constants.CONNECT_TIMEOUT_KEY;
import static org.apache.dubbo.remoting.Constants.RECONNECT_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_STICKY_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
/**
* DubboRegistryFactory
*
*/
public class DubboRegistryFactory extends AbstractRegistryFactory {
private Protocol protocol;
private ProxyFactory proxyFactory;
private Cluster cluster;
private static URL getRegistryURL(URL url) {
return URLBuilder.from(url)
.setPath(RegistryService.class.getName())
.removeParameter(EXPORT_KEY).removeParameter(REFER_KEY)
.addParameter(INTERFACE_KEY, RegistryService.class.getName())
.addParameter(CLUSTER_STICKY_KEY, "true")
.addParameter(LAZY_CONNECT_KEY, "true")
.addParameter(RECONNECT_KEY, "false")
.addParameterIfAbsent(TIMEOUT_KEY, "10000")
.addParameterIfAbsent(CALLBACK_INSTANCES_LIMIT_KEY, "10000")
.addParameterIfAbsent(CONNECT_TIMEOUT_KEY, "10000")
.addParameter(METHODS_KEY, StringUtils.join(new HashSet<>(Arrays.asList(Wrapper.getWrapper(RegistryService.class).getDeclaredMethodNames())), ","))
//.addParameter(Constants.STUB_KEY, RegistryServiceStub.class.getName())
//.addParameter(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString()) //for event dispatch
//.addParameter(Constants.ON_DISCONNECT_KEY, "disconnect")
.addParameter("subscribe.1.callback", "true")
.addParameter("unsubscribe.1.callback", "false")
.build();
}
public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}
public void setProxyFactory(ProxyFactory proxyFactory) {
this.proxyFactory = proxyFactory;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
@Override
public Registry createRegistry(URL url) {
url = getRegistryURL(url);
List<URL> urls = new ArrayList<>();
urls.add(url.removeParameter(BACKUP_KEY));
String backup = url.getParameter(BACKUP_KEY);
if (backup != null && backup.length() > 0) {
String[] addresses = COMMA_SPLIT_PATTERN.split(backup);
for (String address : addresses) {
urls.add(url.setAddress(address));
}
}
RegistryDirectory<RegistryService> directory = new RegistryDirectory<>(RegistryService.class, url.addParameter(INTERFACE_KEY, RegistryService.class.getName()).addParameterAndEncoded(REFER_KEY, url.toParameterString()));
Invoker<RegistryService> registryInvoker = cluster.join(directory);
RegistryService registryService = proxyFactory.getProxy(registryInvoker);
DubboRegistry registry = new DubboRegistry(registryInvoker, registryService);
directory.setRegistry(registry);
directory.setProtocol(protocol);
directory.setRouterChain(RouterChain.buildChain(url));
directory.notify(urls);
directory.subscribe(new URL(CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, RegistryService.class.getName(), url.getParameters()));
return registry;
}
}

View File

@ -1 +0,0 @@
dubbo=org.apache.dubbo.registry.dubbo.DubboRegistryFactory

View File

@ -1,237 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* AbstractRegistryService
*
*/
public abstract class AbstractRegistryService implements RegistryService {
// Log output
protected final Logger logger = LoggerFactory.getLogger(getClass());
// Registered services
// Map<serviceName, Map<url, queryString>>
private final ConcurrentMap<String, List<URL>> registered = new ConcurrentHashMap<String, List<URL>>();
// Subscribed services
// Map<serviceName, queryString>
private final ConcurrentMap<String, Map<String, String>> subscribed = new ConcurrentHashMap<String, Map<String, String>>();
// Notified services
// Map<serviceName, Map<url, queryString>>
private final ConcurrentMap<String, List<URL>> notified = new ConcurrentHashMap<String, List<URL>>();
// Listeners list for subscribed services
// Map<serviceName, List<notificationListener>>
private final ConcurrentMap<String, List<NotifyListener>> notifyListeners = new ConcurrentHashMap<String, List<NotifyListener>>();
@Override
public void register(URL url) {
if (logger.isInfoEnabled()) {
logger.info("Register service: " + url.getServiceKey() + ",url:" + url);
}
register(url.getServiceKey(), url);
}
@Override
public void unregister(URL url) {
if (logger.isInfoEnabled()) {
logger.info("Unregister service: " + url.getServiceKey() + ",url:" + url);
}
unregister(url.getServiceKey(), url);
}
@Override
public void subscribe(URL url, NotifyListener listener) {
if (logger.isInfoEnabled()) {
logger.info("Subscribe service: " + url.getServiceKey() + ",url:" + url);
}
subscribe(url.getServiceKey(), url, listener);
}
@Override
public void unsubscribe(URL url, NotifyListener listener) {
if (logger.isInfoEnabled()) {
logger.info("Unsubscribe service: " + url.getServiceKey() + ",url:" + url);
}
unsubscribe(url.getServiceKey(), url, listener);
}
@Override
public List<URL> lookup(URL url) {
return getRegistered(url.getServiceKey());
}
public void register(String service, URL url) {
if (service == null) {
throw new IllegalArgumentException("service == null");
}
if (url == null) {
throw new IllegalArgumentException("url == null");
}
List<URL> urls = registered.computeIfAbsent(service, k -> new CopyOnWriteArrayList<>());
if (!urls.contains(url)) {
urls.add(url);
}
}
public void unregister(String service, URL url) {
if (service == null) {
throw new IllegalArgumentException("service == null");
}
if (url == null) {
throw new IllegalArgumentException("url == null");
}
List<URL> urls = registered.get(service);
if (urls != null) {
URL deleteURL = null;
for (URL u : urls) {
if (u.toIdentityString().equals(url.toIdentityString())) {
deleteURL = u;
break;
}
}
if (deleteURL != null) {
urls.remove(deleteURL);
}
}
}
public void subscribe(String service, URL url, NotifyListener listener) {
if (service == null) {
throw new IllegalArgumentException("service == null");
}
if (url == null) {
throw new IllegalArgumentException("parameters == null");
}
if (listener == null) {
throw new IllegalArgumentException("listener == null");
}
subscribed.put(service, url.getParameters());
addListener(service, listener);
}
public void unsubscribe(String service, URL url, NotifyListener listener) {
if (service == null) {
throw new IllegalArgumentException("service == null");
}
if (url == null) {
throw new IllegalArgumentException("parameters == null");
}
if (listener == null) {
throw new IllegalArgumentException("listener == null");
}
subscribed.remove(service);
removeListener(service, listener);
}
//The listener of the consumer and the provider can be stored together, all based on the service name
private void addListener(final String service, final NotifyListener listener) {
if (listener == null) {
return;
}
List<NotifyListener> listeners = notifyListeners.computeIfAbsent(service, k -> new CopyOnWriteArrayList<>());
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
private void removeListener(final String service, final NotifyListener listener) {
if (listener == null) {
return;
}
List<NotifyListener> listeners = notifyListeners.get(service);
if (listeners != null) {
listeners.remove(listener);
}
}
private void doNotify(String service, List<URL> urls) {
notified.put(service, urls);
List<NotifyListener> listeners = notifyListeners.get(service);
if (listeners != null) {
for (NotifyListener listener : listeners) {
try {
notify(service, urls, listener);
} catch (Throwable t) {
logger.error("Failed to notify registry event, service: " + service + ", urls: " + urls + ", cause: " + t.getMessage(), t);
}
}
}
}
protected void notify(String service, List<URL> urls, NotifyListener listener) {
listener.notify(urls);
}
protected final void forbid(String service) {
doNotify(service, new ArrayList<URL>(0));
}
protected final void notify(String service, List<URL> urls) {
if (service == null || service.length() == 0
|| urls == null || urls.size() == 0) {
return;
}
doNotify(service, urls);
}
public Map<String, List<URL>> getRegistered() {
return Collections.unmodifiableMap(registered);
}
public List<URL> getRegistered(String service) {
return Collections.unmodifiableList(registered.get(service));
}
public Map<String, Map<String, String>> getSubscribed() {
return Collections.unmodifiableMap(subscribed);
}
public Map<String, String> getSubscribed(String service) {
return subscribed.get(service);
}
public Map<String, List<URL>> getNotified() {
return Collections.unmodifiableMap(notified);
}
public List<URL> getNotified(String service) {
return Collections.unmodifiableList(notified.get(service));
}
public Map<String, List<NotifyListener>> getListeners() {
return Collections.unmodifiableMap(notifyListeners);
}
}

View File

@ -1,27 +0,0 @@
/*
* 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.registry.dubbo;
/**
* <code>TestService</code>
*/
public interface DemoService {
void sayHello(String name);
int plus(int a, int b);
}

View File

@ -1,32 +0,0 @@
/*
* 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.registry.dubbo;
/**
*
*/
public class DemoServiceImpl implements DemoService {
@Override
public void sayHello(String name) {
}
@Override
public int plus(int a, int b) {
return 0;
}
}

View File

@ -1,155 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryService;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.mock;
public class DubboRegistryTest {
private static final Logger logger = LoggerFactory.getLogger(DubboRegistryTest.class);
private DubboRegistry dubboRegistry;
private URL registryURL;
private URL serviceURL;
private NotifyListener notifyListener;
private Invoker<RegistryService> invoker;
private RegistryService registryService;
@BeforeEach
public void setUp() {
registryURL = new URL(REGISTRY_PROTOCOL, NetUtils.getLocalHost(), NetUtils.getAvailablePort())
.addParameter(Constants.CHECK_KEY, false)
.setServiceInterface(RegistryService.class.getName());
serviceURL = new URL(DubboProtocol.NAME, NetUtils.getLocalHost(), NetUtils.getAvailablePort())
.addParameter(Constants.CHECK_KEY, false)
.setServiceInterface(RegistryService.class.getName());
registryService = new MockDubboRegistry(registryURL);
invoker = mock(Invoker.class);
given(invoker.getUrl()).willReturn(serviceURL);
given(invoker.getInterface()).willReturn(RegistryService.class);
given(invoker.invoke(new RpcInvocation())).willReturn(null);
dubboRegistry = new DubboRegistry(invoker, registryService);
notifyListener = mock(NotifyListener.class);
}
@Test
public void testRegister() {
dubboRegistry.register(serviceURL);
assertEquals(1, getRegisteredSize());
}
@Test
public void testUnRegister() {
assertEquals(0, getRegisteredSize());
dubboRegistry.register(serviceURL);
assertEquals(1, getRegisteredSize());
dubboRegistry.unregister(serviceURL);
assertEquals(0, getRegisteredSize());
}
@Test
public void testSubscribe() {
dubboRegistry.register(serviceURL);
assertEquals(1, getRegisteredSize());
dubboRegistry.subscribe(serviceURL, notifyListener);
assertEquals(1, getSubscribedSize());
assertEquals(1, getNotifiedListeners());
}
@Test
public void testUnsubscribe() {
dubboRegistry.subscribe(serviceURL, notifyListener);
assertEquals(1, getSubscribedSize());
assertEquals(1, getNotifiedListeners());
dubboRegistry.unsubscribe(serviceURL, notifyListener);
assertEquals(0, getNotifiedListeners());
}
private class MockDubboRegistry extends FailbackRegistry {
private volatile boolean isAvailable = false;
public MockDubboRegistry(URL url) {
super(url);
}
@Override
public void doRegister(URL url) {
logger.info("Begin to register: " + url);
isAvailable = true;
}
@Override
public void doUnregister(URL url) {
logger.info("Begin to ungister: " + url);
isAvailable = false;
}
@Override
public void doSubscribe(URL url, NotifyListener listener) {
logger.info("Begin to subscribe: " + url);
}
@Override
public void doUnsubscribe(URL url, NotifyListener listener) {
logger.info("Begin to unSubscribe: " + url);
}
@Override
public boolean isAvailable() {
return isAvailable;
}
}
private int getNotifiedListeners() {
return dubboRegistry.getSubscribed().get(serviceURL).size();
}
private int getRegisteredSize() {
return dubboRegistry.getRegistered().size();
}
private int getSubscribedSize() {
return dubboRegistry.getSubscribed().size();
}
}

View File

@ -1,141 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.exchange.ExchangeChannel;
import org.apache.dubbo.remoting.exchange.ExchangeHandler;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
public class MockChannel implements ExchangeChannel {
public static boolean closed = false;
public static boolean closing = true;
final InetSocketAddress localAddress;
final InetSocketAddress remoteAddress;
public MockChannel(String localHostname, int localPort, String remoteHostName, int remotePort) {
localAddress = new InetSocketAddress(localHostname, localPort);
remoteAddress = new InetSocketAddress(remoteHostName, remotePort);
closed = false;
}
@Override
public InetSocketAddress getLocalAddress() {
return localAddress;
}
@Override
public InetSocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public boolean isConnected() {
return true;
}
@Override
public void close() {
closed = true;
}
@Override
public void send(Object message) throws RemotingException {
}
@Override
public void close(int timeout) {
}
@Override
public void startClose() {
closing = true;
}
@Override
public URL getUrl() {
return null;
}
public CompletableFuture<Object> send(Object request, int timeout) throws RemotingException {
return null;
}
@Override
public ChannelHandler getChannelHandler() {
return null;
}
public CompletableFuture<Object> request(Object request) throws RemotingException {
return null;
}
public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
return null;
}
@Override
public CompletableFuture<Object> request(Object request, ExecutorService executor) throws RemotingException {
return null;
}
@Override
public CompletableFuture<Object> request(Object request, int timeout, ExecutorService executor) throws RemotingException {
return null;
}
public ExchangeHandler getExchangeHandler() {
return null;
}
@Override
public Object getAttribute(String key) {
return null;
}
@Override
public void setAttribute(String key, Object value) {
}
@Override
public boolean hasAttribute(String key) {
return false;
}
@Override
public boolean isClosed() {
return false;
}
@Override
public void removeAttribute(String key) {
}
@Override
public void send(Object message, boolean sent) throws RemotingException {
}
}

View File

@ -1,298 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.Codec;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.exchange.ExchangeClient;
import org.apache.dubbo.remoting.exchange.ExchangeHandler;
import org.apache.dubbo.remoting.exchange.support.Replier;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeoutException;
/**
* MockedClient
*
*/
public class MockedClient implements ExchangeClient {
//private String host;
//private int port;
private boolean connected;
private Object received;
private Object sent;
private Object invoked;
private Replier<?> handler;
private InetSocketAddress address;
private boolean closed = false;
//private ChannelListener listener;
public MockedClient(String host, int port, boolean connected) {
this(host, port, connected, null);
}
public MockedClient(String host, int port, boolean connected, Object received) {
this.address = new InetSocketAddress(host, port);
this.connected = connected;
this.received = received;
}
public void open() {
}
@Override
public void close() {
this.closed = true;
}
@Override
public void send(Object msg) throws RemotingException {
this.sent = msg;
}
public CompletableFuture<Object> request(Object msg) throws RemotingException {
return request(msg, null);
}
public CompletableFuture<Object> request(Object msg, int timeout) throws RemotingException {
return this.request(msg, timeout, null);
}
@Override
public CompletableFuture<Object> request(Object msg, ExecutorService executor) throws RemotingException {
return this.request(msg, 0, executor);
}
@Override
public CompletableFuture<Object> request(Object msg, int timeout, ExecutorService executor) throws RemotingException {
this.invoked = msg;
return new CompletableFuture<Object>() {
public Object get() throws InterruptedException, ExecutionException {
return received;
}
public Object get(int timeoutInMillis) throws InterruptedException, ExecutionException, TimeoutException {
return received;
}
public boolean isDone() {
return true;
}
};
}
public void registerHandler(Replier<?> handler) {
this.handler = handler;
}
public void unregisterHandler(Replier<?> handler) {
//this.handler = null;
}
public void addChannelListener(ChannelHandler listener) {
//this.listener = listener;
}
public void removeChannelListener(ChannelHandler listener) {
//this.listener = null;
}
@Override
public boolean isConnected() {
return connected;
}
/**
* @param connected the connected to set
*/
public void setConnected(boolean connected) {
this.connected = connected;
}
public Object getSent() {
return sent;
}
public Replier<?> getHandler() {
return handler;
}
public Object getInvoked() {
return invoked;
}
@Override
public InetSocketAddress getRemoteAddress() {
return address;
}
public String getName() {
return "mocked";
}
@Override
public InetSocketAddress getLocalAddress() {
return null;
}
public int getTimeout() {
return 0;
}
public void setTimeout(int timeout) {
}
@Override
public void close(int timeout) {
close();
}
@Override
public void startClose() {
}
public boolean isOpen() {
return closed;
}
public Codec getCodec() {
return null;
}
public void setCodec(Codec codec) {
}
public String getHost() {
return null;
}
public void setHost(String host) {
}
public int getPort() {
return 0;
}
public void setPort(int port) {
}
public int getThreadCount() {
return 0;
}
public void setThreadCount(int threadCount) {
}
@Override
public URL getUrl() {
return null;
}
public Replier<?> getReceiver() {
return null;
}
@Override
public ChannelHandler getChannelHandler() {
return null;
}
public void reset(Map<String, String> parameters) {
}
public Channel getChannel() {
return this;
}
public ExchangeHandler getExchangeHandler() {
return null;
}
@Override
public void reconnect() throws RemotingException {
}
@Override
public Object getAttribute(String key) {
return null;
}
@Override
public void setAttribute(String key, Object value) {
}
@Override
public boolean hasAttribute(String key) {
return false;
}
@Override
public boolean isClosed() {
return closed;
}
@Override
public void removeAttribute(String key) {
}
/**
* @return the received
*/
public Object getReceived() {
return received;
}
/**
* @param received the received to set
*/
public void setReceived(Object received) {
this.received = received;
}
@Override
public void send(Object message, boolean sent) throws RemotingException {
}
@Override
public void reset(URL url) {
}
@Deprecated
public void reset(org.apache.dubbo.common.Parameters parameters) {
}
}

View File

@ -1,232 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryFactory;
import org.apache.dubbo.registry.RegistryService;
import org.apache.dubbo.registry.integration.RegistryProtocol;
import org.apache.dubbo.registry.support.AbstractRegistry;
import org.apache.dubbo.remoting.exchange.ExchangeClient;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.protocol.AbstractInvoker;
import org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker;
import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS;
import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* RegistryProtocolTest
*/
public class RegistryProtocolTest {
static {
SimpleRegistryExporter.exportIfAbsent(9090);
}
final String service = DemoService.class.getName() + ":1.0.0";
final String serviceUrl = "dubbo://127.0.0.1:9453/" + service + "?notify=true&methods=test1,test2&side=con&side=consumer";
final URL registryUrl = URL.valueOf("registry://127.0.0.1:9090/");
final private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
public static RegistryProtocol getRegistryProtocol() {
return RegistryProtocol.getRegistryProtocol();
}
@BeforeEach
public void setUp() {
ApplicationModel.setApplication("RegistryProtocolTest");
ApplicationModel.getServiceRepository().registerService(RegistryService.class);
}
@Test
public void testDefaultPort() {
RegistryProtocol registryProtocol = getRegistryProtocol();
assertEquals(9090, registryProtocol.getDefaultPort());
}
@Test
public void testExportUrlNull() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
RegistryProtocol registryProtocol = getRegistryProtocol();
// registryProtocol.setCluster(new FailfastCluster());
Protocol dubboProtocol = DubboProtocol.getDubboProtocol();
registryProtocol.setProtocol(dubboProtocol);
Invoker<DemoService> invoker = new DubboInvoker<DemoService>(DemoService.class,
registryUrl, new ExchangeClient[]{new MockedClient("10.20.20.20", 2222, true)});
registryProtocol.export(invoker);
});
}
@Test
public void testExport() {
RegistryProtocol registryProtocol = getRegistryProtocol();
// registryProtocol.setCluster(new FailfastCluster());
registryProtocol.setRegistryFactory(ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension());
Protocol dubboProtocol = DubboProtocol.getDubboProtocol();
registryProtocol.setProtocol(dubboProtocol);
URL newRegistryUrl = registryUrl.addParameter(EXPORT_KEY, serviceUrl);
DubboInvoker<DemoService> invoker = new DubboInvoker<DemoService>(DemoService.class,
newRegistryUrl, new ExchangeClient[]{new MockedClient("10.20.20.20", 2222, true)});
Exporter<DemoService> exporter = registryProtocol.export(invoker);
Exporter<DemoService> exporter2 = registryProtocol.export(invoker);
//The same invoker, exporter that multiple exported are different
Assertions.assertNotSame(exporter, exporter2);
exporter.unexport();
exporter2.unexport();
}
// @Test
// public void testNotifyOverride() throws Exception {
// URL newRegistryUrl = registryUrl.addParameter(EXPORT_KEY, serviceUrl);
// Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);
//
// ServiceDescriptor descriptor = ApplicationModel.getServiceRepository().registerService(DemoService.class);
// ApplicationModel.getServiceRepository().registerProvider(service, new DemoServiceImpl(), descriptor, null, null);
//
// Exporter<?> exporter = protocol.export(invoker);
// RegistryProtocol rprotocol = getRegistryProtocol();
// NotifyListener listener = getListener(rprotocol);
// List<URL> urls = new ArrayList<URL>();
// urls.add(URL.valueOf("override://0.0.0.0/?timeout=1000"));
// urls.add(URL.valueOf("override://0.0.0.0/" + service + "?timeout=100"));
// urls.add(URL.valueOf("override://0.0.0.0/" + service + "?x=y"));
// listener.notify(urls);
//
// assertTrue(exporter.getInvoker().isAvailable());
// assertEquals("100", exporter.getInvoker().getUrl().getParameter("timeout"));
// assertEquals("y", exporter.getInvoker().getUrl().getParameter("x"));
//
// exporter.unexport();
//// int timeout = ConfigUtils.getServerShutdownTimeout();
//// Thread.sleep(timeout + 1000);
//// assertEquals(false, exporter.getInvoker().isAvailable());
// destroyRegistryProtocol();
//
// }
/**
* The name of the service does not match and can't override invoker
* Service name matching, service version number mismatch
*/
@Test
public void testNotifyOverride_notmatch() throws Exception {
URL newRegistryUrl = registryUrl.addParameter(EXPORT_KEY, serviceUrl);
Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);
ServiceDescriptor descriptor = ApplicationModel.getServiceRepository().registerService(DemoService.class);
ApplicationModel.getServiceRepository().registerProvider(service, new DemoServiceImpl(), descriptor, null, null);
Exporter<?> exporter = protocol.export(invoker);
RegistryProtocol rprotocol = getRegistryProtocol();
NotifyListener listener = getListener(rprotocol);
List<URL> urls = new ArrayList<URL>();
urls.add(URL.valueOf("override://0.0.0.0/org.apache.dubbo.registry.protocol.HackService?timeout=100"));
listener.notify(urls);
assertTrue(exporter.getInvoker().isAvailable());
assertNull(exporter.getInvoker().getUrl().getParameter("timeout"));
exporter.unexport();
destroyRegistryProtocol();
}
/**
* Test destory registry, exporter can be normal by destroyed
*/
@Test
public void testDestoryRegistry() {
URL newRegistryUrl = registryUrl.addParameter(EXPORT_KEY, serviceUrl);
Invoker<RegistryProtocolTest> invoker = new MockInvoker<RegistryProtocolTest>(RegistryProtocolTest.class, newRegistryUrl);
Exporter<?> exporter = protocol.export(invoker);
destroyRegistryProtocol();
try {
Thread.sleep(ConfigurationUtils.getServerShutdownTimeout() + 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
assertFalse(exporter.getInvoker().isAvailable());
}
@Test
public void testGetParamsToRegistry() {
RegistryProtocol registryProtocol = getRegistryProtocol();
String[] additionalParams = new String[]{"key1", "key2"};
String[] registryParams = registryProtocol.getParamsToRegistry(DEFAULT_REGISTER_PROVIDER_KEYS, additionalParams);
String[] expectParams = ArrayUtils.addAll(DEFAULT_REGISTER_PROVIDER_KEYS, additionalParams);
Assertions.assertArrayEquals(expectParams, registryParams);
}
private void destroyRegistryProtocol() {
Protocol registry = getRegistryProtocol();
registry.destroy();
}
private NotifyListener getListener(RegistryProtocol protocol) throws Exception {
return protocol.getOverrideListeners().values().iterator().next();
}
static class MockInvoker<T> extends AbstractInvoker<T> {
public MockInvoker(Class<T> type, URL url) {
super(type, url);
}
@Override
protected Result doInvoke(Invocation invocation) throws Throwable {
//do nothing
return null;
}
}
static class MockRegistry extends AbstractRegistry {
public MockRegistry(URL url) {
super(url);
}
@Override
public boolean isAvailable() {
return true;
}
}
}

View File

@ -1,69 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.status.Status;
import org.apache.dubbo.registry.RegistryFactory;
import org.apache.dubbo.registry.RegistryService;
import org.apache.dubbo.registry.status.RegistryStatusChecker;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* StatusTest
*
*/
public class RegistryStatusCheckerTest {
static {
SimpleRegistryExporter.exportIfAbsent(9090);
SimpleRegistryExporter.exportIfAbsent(9091);
}
URL registryUrl = URL.valueOf("dubbo://cat:cat@127.0.0.1:9090/");
URL registryUrl2 = URL.valueOf("dubbo://cat:cat@127.0.0.1:9091");
@BeforeEach
public void setUp() {
AbstractRegistryFactory.clearRegistryNotDestroy();
ApplicationModel.getServiceRepository().registerService(RegistryService.class);
}
@Test
public void testCheckUnknown() {
assertEquals(Status.Level.UNKNOWN, new RegistryStatusChecker().check().getLevel());
}
@Test
public void testCheckOK() {
ApplicationModel.setApplication("testCheckOK");
ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(registryUrl);
ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(registryUrl2);
assertEquals(Status.Level.OK, new RegistryStatusChecker().check().getLevel());
String message = new RegistryStatusChecker().check().getMessage();
Assertions.assertTrue(message.contains(registryUrl.getAddress() + "(connected)"));
Assertions.assertTrue(message.contains(registryUrl2.getAddress() + "(connected)"));
}
}

View File

@ -1,87 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.registry.RegistryService;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.ProxyFactory;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.model.ServiceRepository;
import java.io.IOException;
import java.net.ServerSocket;
import static org.apache.dubbo.common.constants.CommonConstants.CALLBACK_INSTANCES_LIMIT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_STICKY_KEY;
/**
* SimpleRegistryExporter
*
*/
public class SimpleRegistryExporter {
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
public synchronized static Exporter<RegistryService> exportIfAbsent(int port) {
try {
new ServerSocket(port).close();
return export(port);
} catch (IOException e) {
return null;
}
}
public static Exporter<RegistryService> export(int port) {
return export(port, new SimpleRegistryService());
}
public static Exporter<RegistryService> export(int port, RegistryService registryService) {
return protocol.export(PROXY_FACTORY.getInvoker(registryService, RegistryService.class,
new URLBuilder(DUBBO_PROTOCOL, NetUtils.getLocalHost(), port, RegistryService.class.getName())
.setPath(RegistryService.class.getName())
.addParameter(INTERFACE_KEY, RegistryService.class.getName())
.addParameter(CLUSTER_STICKY_KEY, "true")
.addParameter(CALLBACK_INSTANCES_LIMIT_KEY, "1000")
.addParameter("ondisconnect", "disconnect")
.addParameter("subscribe.1.callback", "true")
.addParameter("unsubscribe.1.callback", "false")
.build()));
}
private void registerProvider(URL url, RegistryService registryService) {
ServiceRepository repository = ApplicationModel.getServiceRepository();
ServiceDescriptor serviceDescriptor = repository.registerService(RegistryService.class);
repository.registerProvider(
url.getServiceKey(),
registryService,
serviceDescriptor,
null,
null
);
}
}

View File

@ -1,146 +0,0 @@
/*
* 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.registry.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryService;
import org.apache.dubbo.rpc.RpcContext;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* SimpleRegistryService
*
*/
public class SimpleRegistryService extends AbstractRegistryService {
private final static Logger logger = LoggerFactory.getLogger(SimpleRegistryService.class);
private final ConcurrentMap<String, ConcurrentMap<String, URL>> remoteRegistered = new ConcurrentHashMap<String, ConcurrentMap<String, URL>>();
private final ConcurrentMap<String, ConcurrentMap<String, NotifyListener>> remoteListeners = new ConcurrentHashMap<String, ConcurrentMap<String, NotifyListener>>();
private List<String> registries;
public SimpleRegistryService() {
}
@Override
public void register(String service, URL url) {
super.register(service, url);
String client = RpcContext.getContext().getRemoteAddressString();
Map<String, URL> urls = remoteRegistered.computeIfAbsent(client, k -> new ConcurrentHashMap<>());
urls.put(service, url);
notify(service, getRegistered().get(service));
}
@Override
public void unregister(String service, URL url) {
super.unregister(service, url);
String client = RpcContext.getContext().getRemoteAddressString();
Map<String, URL> urls = remoteRegistered.get(client);
if (urls != null && urls.size() > 0) {
urls.remove(service);
}
notify(service, getRegistered().get(service));
}
@Override
public void subscribe(String service, URL url, NotifyListener listener) {
String client = RpcContext.getContext().getRemoteAddressString();
if (logger.isInfoEnabled()) {
logger.info("[subscribe] service: " + service + ",client:" + client);
}
List<URL> urls = getRegistered().get(service);
if ((RegistryService.class.getName() + ":0.0.0").equals(service)
&& (urls == null || urls.size() == 0)) {
register(service, new URL("dubbo",
NetUtils.getLocalHost(),
RpcContext.getContext().getLocalPort(),
org.apache.dubbo.registry.RegistryService.class.getName(),
url.getParameters()));
List<String> rs = registries;
if (rs != null && rs.size() > 0) {
for (String registry : rs) {
register(service, UrlUtils.parseURL(registry, url.getParameters()));
}
}
}
super.subscribe(service, url, listener);
Map<String, NotifyListener> listeners = remoteListeners.computeIfAbsent(client, k -> new ConcurrentHashMap<>());
listeners.put(service, listener);
urls = getRegistered().get(service);
if (urls != null && urls.size() > 0) {
listener.notify(urls);
}
}
@Override
public void unsubscribe(String service, URL url, NotifyListener listener) {
super.unsubscribe(service, url, listener);
String client = RpcContext.getContext().getRemoteAddressString();
Map<String, NotifyListener> listeners = remoteListeners.get(client);
if (listeners != null && listeners.size() > 0) {
listeners.remove(service);
}
List<URL> urls = getRegistered().get(service);
if (urls != null && urls.size() > 0) {
listener.notify(urls);
}
}
public void disconnect() {
String client = RpcContext.getContext().getRemoteAddressString();
if (logger.isInfoEnabled()) {
logger.info("Disconnected " + client);
}
ConcurrentMap<String, URL> urls = remoteRegistered.get(client);
if (urls != null && urls.size() > 0) {
for (Map.Entry<String, URL> entry : urls.entrySet()) {
super.unregister(entry.getKey(), entry.getValue());
}
}
Map<String, NotifyListener> listeners = remoteListeners.get(client);
if (listeners != null && listeners.size() > 0) {
for (Map.Entry<String, NotifyListener> entry : listeners.entrySet()) {
String service = entry.getKey();
super.unsubscribe(service, new URL("subscribe",
RpcContext.getContext().getRemoteHost(),
RpcContext.getContext().getRemotePort(),
org.apache.dubbo.registry.RegistryService.class.getName(), getSubscribed(service)), entry.getValue());
}
}
}
public List<String> getRegistries() {
return registries;
}
public void setRegistries(List<String> registries) {
this.registries = registries;
}
}

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- ===================================================================== -->
<!-- The following is the definition of appender -->
<!-- ===================================================================== -->
<appender name="dubbo" class="org.apache.dubbo.common.utils.DubboAppender">
<param name="File" value="../../dubbo.log"/>
<param name="encoding" value="GBK"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %p [%c:%M] - %m%n"/>
</layout>
<!-- <filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="DEBUG" />
<param name="LevelMax" value="DEBUG" />
</filter> -->
</appender>
<root>
<level value="INFO"/>
<appender-ref ref="dubbo"/>
</root>
</log4j:configuration>

View File

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>dubbo-registry</artifactId>
<groupId>org.apache.dubbo</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-registry-etcd3</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The etcd3 registry module of Dubbo project</description>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-etcd3</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,359 +0,0 @@
/*
* 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.registry.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.apache.dubbo.remoting.etcd.ChildListener;
import org.apache.dubbo.remoting.etcd.Constants;
import org.apache.dubbo.remoting.etcd.EtcdClient;
import org.apache.dubbo.remoting.etcd.EtcdTransporter;
import org.apache.dubbo.remoting.etcd.StateListener;
import org.apache.dubbo.remoting.etcd.option.OptionUtil;
import org.apache.dubbo.rpc.RpcException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
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.CONSUMERS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
import static org.apache.dubbo.remoting.Constants.CHECK_KEY;
/**
* Support for ectd3 registry.
*/
public class EtcdRegistry extends FailbackRegistry {
private final static Logger logger = LoggerFactory.getLogger(EtcdRegistry.class);
private final static int DEFAULT_ETCD_PORT = 2379;
private final static String DEFAULT_ROOT = "dubbo";
private final String root;
private final Set<String> anyServices = new ConcurrentHashSet<>();
private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> etcdListeners = new ConcurrentHashMap<>();
private final EtcdClient etcdClient;
public EtcdRegistry(URL url, EtcdTransporter etcdTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address is invalid, actual: '" + url.getHost() + "'");
}
String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
if (!group.startsWith(PATH_SEPARATOR)) {
group = PATH_SEPARATOR + group;
}
this.root = group;
etcdClient = etcdTransporter.connect(url);
etcdClient.addStateListener(state -> {
if (state == StateListener.CONNECTED) {
try {
recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
});
}
protected static String appendDefaultPort(String address) {
if (address != null && address.length() > 0) {
int i = address.indexOf(':');
if (i < 0) {
return address + ":" + DEFAULT_ETCD_PORT;
} else if (Integer.parseInt(address.substring(i + 1)) == 0) {
return address.substring(0, i + 1) + DEFAULT_ETCD_PORT;
}
}
return address;
}
@Override
public void doRegister(URL url) {
try {
String path = toUrlPath(url);
if (url.getParameter(DYNAMIC_KEY, true)) {
etcdClient.createEphemeral(path);
return;
}
etcdClient.create(path);
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to etcd " + getUrl()
+ ", cause: " + (OptionUtil.isProtocolError(e)
? "etcd3 registry may not be supported yet or etcd3 registry is not available."
: e.getMessage()), e);
}
}
@Override
public void doUnregister(URL url) {
try {
String path = toUrlPath(url);
etcdClient.delete(path);
} catch (Throwable e) {
throw new RpcException("Failed to unregister " + url + " to etcd " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
@Override
public void doSubscribe(URL url, NotifyListener listener) {
try {
if (ANY_VALUE.equals(url.getServiceInterface())) {
String root = toRootPath();
/*
* if we are interested in all interfaces,
* find out the current container or create one for the url, put or get only once.
*/
ConcurrentMap<NotifyListener, ChildListener> listeners =
Optional.ofNullable(etcdListeners.get(url))
.orElseGet(() -> {
ConcurrentMap<NotifyListener, ChildListener> container, prev;
prev = etcdListeners.putIfAbsent(url, container = new ConcurrentHashMap<>());
return prev != null ? prev : container;
});
/*
* if we have no interface watcher listener,
* find the current listener or create one for the current root, put or get only once.
*/
ChildListener interfaceListener =
Optional.ofNullable(listeners.get(listener))
.orElseGet(() -> {
ChildListener childListener, prev;
prev = listeners.putIfAbsent(listener, childListener = (parentPath, currentChildren) -> {
/*
* because etcd3 does not support direct children watch events,
* we should filter not interface events. if we watch /dubbo
* and /dubbo/interface, when we put a key-value pair {/dubbo/interface/hello hello},
* we will got events in watching path /dubbo.
*/
for (String child : currentChildren) {
child = URL.decode(child);
if (!anyServices.contains(child)) {
anyServices.add(child);
/*
* if new interface event arrived, we watch their direct children,
* eg: /dubbo/interface, /dubbo/interface and so on.
*/
subscribe(url.setPath(child).addParameters(INTERFACE_KEY, child,
CHECK_KEY, String.valueOf(false)), listener);
}
}
});
return prev != null ? prev : childListener;
});
etcdClient.create(root);
/*
* at the first time, we want to pull already interface and then watch their direct children,
* eg: /dubbo/interface, /dubbo/interface and so on.
*/
List<String> services = etcdClient.addChildListener(root, interfaceListener);
for (String service : services) {
service = URL.decode(service);
anyServices.add(service);
subscribe(url.setPath(service).addParameters(INTERFACE_KEY, service,
CHECK_KEY, String.valueOf(false)), listener);
}
} else {
List<URL> urls = new ArrayList<>();
for (String path : toCategoriesPath(url)) {
/*
* if we are interested in special categories (providers, consumers, routers and so on),
* we find out the current container or create one for the url, put or get only once.
*/
ConcurrentMap<NotifyListener, ChildListener> listeners =
Optional.ofNullable(etcdListeners.get(url))
.orElseGet(() -> {
ConcurrentMap<NotifyListener, ChildListener> container, prev;
prev = etcdListeners.putIfAbsent(url,
container = new ConcurrentHashMap<>());
return prev != null ? prev : container;
});
/*
* if we have no category watcher listener,
* we find out the current listener or create one for the current category, put or get only once.
*/
ChildListener childListener =
Optional.ofNullable(listeners.get(listener))
.orElseGet(() -> {
ChildListener watchListener, prev;
prev = listeners.putIfAbsent(listener, watchListener = (parentPath, currentChildren) -> EtcdRegistry.this.notify(url, listener,
toUrlsWithEmpty(url, parentPath, currentChildren)));
return prev != null ? prev : watchListener;
});
etcdClient.create(path);
/*
* at the first time, we want to pull already category and then watch their direct children,
* eg: /dubbo/interface/providers, /dubbo/interface/consumers and so on.
*/
List<String> children = etcdClient.addChildListener(path, childListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
notify(url, listener, urls);
}
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to etcd " + getUrl()
+ ", cause: " + (OptionUtil.isProtocolError(e)
? "etcd3 registry may not be supported yet or etcd3 registry is not available."
: e.getMessage()), e);
}
}
@Override
public void doUnsubscribe(URL url, NotifyListener listener) {
ConcurrentMap<NotifyListener, ChildListener> listeners = etcdListeners.get(url);
if (listeners != null) {
ChildListener etcdListener = listeners.get(listener);
if (etcdListener != null) {
// maybe url has many subscribed paths
for (String path : toUnsubscribedPath(url)) {
etcdClient.removeChildListener(path, etcdListener);
}
}
}
}
@Override
public boolean isAvailable() {
return etcdClient.isConnected();
}
@Override
public void destroy() {
super.destroy();
try {
etcdClient.close();
} catch (Exception e) {
logger.warn("Failed to close etcd client " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
protected String toRootDir() {
if (root.startsWith(PATH_SEPARATOR)) {
return root;
}
return PATH_SEPARATOR + root;
}
protected String toRootPath() {
return root;
}
protected String toServicePath(URL url) {
String name = url.getServiceInterface();
if (ANY_VALUE.equals(name)) {
return toRootPath();
}
return toRootDir() + PATH_SEPARATOR + URL.encode(name);
}
protected String[] toCategoriesPath(URL url) {
String[] categories;
if (ANY_VALUE.equals(url.getParameter(CATEGORY_KEY))) {
categories = new String[]{PROVIDERS_CATEGORY, CONSUMERS_CATEGORY, ROUTERS_CATEGORY, CONFIGURATORS_CATEGORY};
} else {
categories = url.getParameter(CATEGORY_KEY, new String[]{DEFAULT_CATEGORY});
}
String[] paths = new String[categories.length];
for (int i = 0; i < categories.length; i++) {
paths[i] = toServicePath(url) + PATH_SEPARATOR + categories[i];
}
return paths;
}
protected String toCategoryPath(URL url) {
return toServicePath(url) + PATH_SEPARATOR + url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
}
protected String toUrlPath(URL url) {
return toCategoryPath(url) + PATH_SEPARATOR + URL.encode(url.toFullString());
}
protected List<String> toUnsubscribedPath(URL url) {
List<String> categories = new ArrayList<>();
if (ANY_VALUE.equals(url.getServiceInterface())) {
String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
if (!group.startsWith(PATH_SEPARATOR)) {
group = PATH_SEPARATOR + group;
}
categories.add(group);
return categories;
} else {
categories.addAll(Arrays.asList(toCategoriesPath(url)));
}
return categories;
}
protected List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
List<URL> urls = new ArrayList<>();
if (providers != null && providers.size() > 0) {
for (String provider : providers) {
provider = URL.decode(provider);
if (provider.contains(Constants.HTTP_SUBFIX_KEY)) {
URL url = URL.valueOf(provider);
if (UrlUtils.isMatch(consumer, url)) {
urls.add(url);
}
}
}
}
return urls;
}
protected List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
if (urls == null || urls.isEmpty()) {
int i = path.lastIndexOf('/');
String category = i < 0 ? path : path.substring(i + 1);
URL empty = consumer.setProtocol(EMPTY_PROTOCOL).addParameter(CATEGORY_KEY, category);
urls.add(empty);
}
return urls;
}
}

View File

@ -1,36 +0,0 @@
/*
* 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.registry.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.apache.dubbo.remoting.etcd.EtcdTransporter;
public class EtcdRegistryFactory extends AbstractRegistryFactory {
private EtcdTransporter etcdTransporter;
@Override
protected Registry createRegistry(URL url) {
return new EtcdRegistry(url, etcdTransporter);
}
public void setEtcdTransporter(EtcdTransporter etcdTransporter) {
this.etcdTransporter = etcdTransporter;
}
}

View File

@ -1,208 +0,0 @@
/*
* 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.registry.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.event.EventDispatcher;
import org.apache.dubbo.event.EventListener;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceDiscovery;
import org.apache.dubbo.registry.client.ServiceInstance;
import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
import org.apache.dubbo.remoting.etcd.ChildListener;
import org.apache.dubbo.remoting.etcd.EtcdClient;
import org.apache.dubbo.remoting.etcd.EtcdTransporter;
import org.apache.dubbo.remoting.etcd.StateListener;
import org.apache.dubbo.remoting.etcd.option.OptionUtil;
import org.apache.dubbo.rpc.RpcException;
import com.google.gson.Gson;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 2019-07-08
*/
public class EtcdServiceDiscovery implements ServiceDiscovery, EventListener<ServiceInstancesChangedEvent> {
private final static Logger logger = LoggerFactory.getLogger(EtcdServiceDiscovery.class);
private final String root = "/services";
private final Set<String> services = new ConcurrentHashSet<>();
private final Map<String, ChildListener> childListenerMap = new ConcurrentHashMap<>();
EtcdClient etcdClient;
EventDispatcher dispatcher;
ServiceInstance serviceInstance;
@Override
public void onEvent(ServiceInstancesChangedEvent event) {
registerServiceWatcher(event.getServiceName());
}
@Override
public void initialize(URL registryURL) throws Exception {
EtcdTransporter etcdTransporter = ExtensionLoader.getExtensionLoader(EtcdTransporter.class).getAdaptiveExtension();
if (registryURL.isAnyHost()) {
throw new IllegalStateException("Service discovery address is invalid, actual: '" + registryURL.getHost() + "'");
}
etcdClient = etcdTransporter.connect(registryURL);
etcdClient.addStateListener(state -> {
if (state == StateListener.CONNECTED) {
try {
recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
});
this.dispatcher = EventDispatcher.getDefaultExtension();
this.dispatcher.addEventListener(this);
}
@Override
public void destroy() {
if (etcdClient != null && etcdClient.isConnected()) {
etcdClient.close();
}
}
@Override
public void register(ServiceInstance serviceInstance) throws RuntimeException {
try {
this.serviceInstance = serviceInstance;
String path = toPath(serviceInstance);
// etcdClient.createEphemeral(path);
etcdClient.putEphemeral(path, new Gson().toJson(serviceInstance));
services.add(serviceInstance.getServiceName());
} catch (Throwable e) {
throw new RpcException("Failed to register " + serviceInstance + " to etcd " + etcdClient.getUrl()
+ ", cause: " + (OptionUtil.isProtocolError(e)
? "etcd3 registry may not be supported yet or etcd3 registry is not available."
: e.getMessage()), e);
}
}
String toPath(ServiceInstance serviceInstance) {
return root + File.separator + serviceInstance.getServiceName() + File.separator + serviceInstance.getHost()
+ ":" + serviceInstance.getPort();
}
String toParentPath(String serviceName) {
return root + File.separator + serviceName;
}
@Override
public void update(ServiceInstance serviceInstance) throws RuntimeException {
try {
String path = toPath(serviceInstance);
etcdClient.putEphemeral(path, new Gson().toJson(serviceInstance));
services.add(serviceInstance.getServiceName());
} catch (Throwable e) {
throw new RpcException("Failed to register " + serviceInstance + " to etcd " + etcdClient.getUrl()
+ ", cause: " + (OptionUtil.isProtocolError(e)
? "etcd3 registry may not be supported yet or etcd3 registry is not available."
: e.getMessage()), e);
}
}
@Override
public void unregister(ServiceInstance serviceInstance) throws RuntimeException {
try {
String path = toPath(serviceInstance);
etcdClient.delete(path);
services.remove(serviceInstance.getServiceName());
this.serviceInstance = null;
} catch (Throwable e) {
throw new RpcException("Failed to unregister " + serviceInstance + " to etcd " + etcdClient.getUrl() + ", cause: " + e.getMessage(), e);
}
}
@Override
public Set<String> getServices() {
return Collections.unmodifiableSet(services);
}
@Override
public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
registerServiceWatcher(listener.getServiceNames());
}
@Override
public List<ServiceInstance> getInstances(String serviceName) {
List<String> children = etcdClient.getChildren(toParentPath(serviceName));
if (CollectionUtils.isEmpty(children)) {
return Collections.EMPTY_LIST;
}
List<ServiceInstance> list = new ArrayList<>(children.size());
for (String child : children) {
ServiceInstance serviceInstance = new Gson().fromJson(etcdClient.getKVValue(child), DefaultServiceInstance.class);
list.add(serviceInstance);
}
return list;
}
protected void registerServiceWatcher(String serviceName) {
String path = root + File.separator + serviceName;
/*
* if we have no category watcher listener,
* we find out the current listener or create one for the current category, put or get only once.
*/
ChildListener childListener =
Optional.ofNullable(childListenerMap.get(serviceName))
.orElseGet(() -> {
ChildListener watchListener, prev;
prev = childListenerMap.putIfAbsent(serviceName, watchListener = (parentPath, currentChildren) ->
dispatchServiceInstancesChangedEvent(serviceName));
return prev != null ? prev : watchListener;
});
etcdClient.create(path);
etcdClient.addChildListener(path, childListener);
}
private void recover() throws Exception {
// register
if (serviceInstance != null) {
if (logger.isInfoEnabled()) {
logger.info("Recover application register: " + serviceInstance);
}
register(serviceInstance);
}
}
}

View File

@ -1 +0,0 @@
etcd3=org.apache.dubbo.registry.etcd.EtcdRegistryFactory

View File

@ -1 +0,0 @@
etcd3=org.apache.dubbo.registry.etcd.EtcdServiceDiscovery

View File

@ -1,327 +0,0 @@
/*
* 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.
*/
/*
* 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.
*/
/*
* 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.registry.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryFactory;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.apache.dubbo.remoting.Constants;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
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.ENABLED_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_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.CONSUMERS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
import static org.apache.dubbo.registry.Constants.ADMIN_PROTOCOL;
@Disabled
public class EtcdRegistryTest {
String service = "org.apache.dubbo.internal.test.DemoServie";
String outerService = "org.apache.dubbo.outer.test.OuterDemoServie";
URL serviceUrl = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/" + service + "?methods=test1,test2");
URL serviceUrl2 = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/" + service + "?methods=test1,test2,test3");
URL serviceUrl3 = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/" + outerService + "?methods=test1,test2");
URL registryUrl = URL.valueOf("etcd3://127.0.0.1:2379/org.apache.dubbo.registry.RegistryService");
URL consumerUrl = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":2018" + "/" + service + "?methods=test1,test2");
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
EtcdRegistry registry;
URL subscribe = new URL(
ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0, "",
INTERFACE_KEY, ANY_VALUE,
GROUP_KEY, ANY_VALUE,
VERSION_KEY, ANY_VALUE,
CLASSIFIER_KEY, ANY_VALUE,
CATEGORY_KEY, PROVIDERS_CATEGORY + "," + CONSUMERS_CATEGORY + "," + ROUTERS_CATEGORY + "," + CONFIGURATORS_CATEGORY,
ENABLED_KEY, ANY_VALUE,
Constants.CHECK_KEY, String.valueOf(false));
@Test
public void test_register() {
registry.register(serviceUrl);
Set<URL> registered = registry.getRegistered();
Assertions.assertEquals(1, registered.size());
Assertions.assertTrue(registered.contains(serviceUrl));
registry.unregister(serviceUrl);
}
@Test
public void test_unregister() {
registry.register(serviceUrl);
Set<URL> registered = registry.getRegistered();
Assertions.assertEquals(1, registered.size());
Assertions.assertTrue(registered.contains(serviceUrl));
registry.unregister(serviceUrl);
registered = registry.getRegistered();
Assertions.assertEquals(0, registered.size());
}
@Test
public void test_subscribe() {
registry.register(serviceUrl);
final AtomicReference<URL> notifiedUrl = new AtomicReference<URL>();
registry.subscribe(consumerUrl, new NotifyListener() {
public void notify(List<URL> urls) {
notifiedUrl.set(urls.get(0));
}
});
Assertions.assertEquals(serviceUrl.toFullString(), notifiedUrl.get().toFullString());
Map<URL, Set<NotifyListener>> arg = registry.getSubscribed();
Assertions.assertEquals(consumerUrl, arg.keySet().iterator().next());
}
@Test
public void test_subscribe_when_register() throws InterruptedException {
Assertions.assertEquals(0, registry.getRegistered().size());
Assertions.assertEquals(0, registry.getSubscribed().size());
CountDownLatch notNotified = new CountDownLatch(2);
final AtomicReference<URL> notifiedUrl = new AtomicReference<URL>();
registry.subscribe(consumerUrl, new NotifyListener() {
public void notify(List<URL> urls) {
notifiedUrl.set(urls.get(0));
notNotified.countDown();
}
});
registry.register(serviceUrl);
Assertions.assertTrue(notNotified.await(15, TimeUnit.SECONDS));
Assertions.assertEquals(serviceUrl.toFullString(), notifiedUrl.get().toFullString());
Map<URL, Set<NotifyListener>> subscribed = registry.getSubscribed();
Assertions.assertEquals(consumerUrl, subscribed.keySet().iterator().next());
}
@Test
public void test_subscribe_when_register0() throws InterruptedException {
Assertions.assertEquals(0, registry.getRegistered().size());
Assertions.assertEquals(0, registry.getSubscribed().size());
CountDownLatch notNotified = new CountDownLatch(3);
ConcurrentHashMap<URL, Boolean> notifiedUrls = new ConcurrentHashMap<>();
registry.subscribe(consumerUrl, new NotifyListener() {
public void notify(List<URL> urls) {
if (urls != null && urls.size() > 0) {
if (!urls.get(0).getProtocol().equals("empty")) {
for (Iterator<URL> iterator = urls.iterator(); iterator.hasNext(); ) {
notifiedUrls.put(iterator.next(), true);
}
}
}
notNotified.countDown();
}
});
registry.register(serviceUrl);
registry.register(serviceUrl2);
Assertions.assertTrue(notNotified.await(15, TimeUnit.SECONDS));
Assertions.assertTrue(notifiedUrls.containsKey(serviceUrl));
Assertions.assertTrue(notifiedUrls.containsKey(serviceUrl2));
Map<URL, Set<NotifyListener>> subscribed = registry.getSubscribed();
Assertions.assertEquals(consumerUrl, subscribed.keySet().iterator().next());
}
@Test
public void test_subscribe_when_register1() throws InterruptedException {
Assertions.assertEquals(0, registry.getRegistered().size());
Assertions.assertEquals(0, registry.getSubscribed().size());
CountDownLatch notNotified = new CountDownLatch(2);
final AtomicReference<URL> notifiedUrls = new AtomicReference<URL>();
registry.subscribe(consumerUrl, new NotifyListener() {
public void notify(List<URL> urls) {
notifiedUrls.set(urls.get(0));
notNotified.countDown();
}
});
registry.register(serviceUrl);
// register service3 should not trigger notify
registry.register(serviceUrl3);
Assertions.assertTrue(notNotified.await(15, TimeUnit.SECONDS));
Assertions.assertEquals(serviceUrl, notifiedUrls.get());
Map<URL, Set<NotifyListener>> subscribed = registry.getSubscribed();
Assertions.assertEquals(consumerUrl, subscribed.keySet().iterator().next());
}
@Test
public void test_subscribe_when_register2() throws InterruptedException {
Assertions.assertEquals(0, registry.getRegistered().size());
Assertions.assertEquals(0, registry.getSubscribed().size());
CountDownLatch notNotified = new CountDownLatch(3);
ConcurrentHashMap<URL, Boolean> notifiedUrls = new ConcurrentHashMap<>();
registry.subscribe(subscribe, new NotifyListener() {
public void notify(List<URL> urls) {
if (urls != null && urls.size() > 0) {
if (!urls.get(0).getProtocol().equals("empty")) {
for (Iterator<URL> iterator = urls.iterator(); iterator.hasNext(); ) {
notifiedUrls.put(iterator.next(), true);
}
notNotified.countDown();
}
}
}
});
registry.register(serviceUrl);
registry.register(serviceUrl2);
// service3 interface is not equals server2
registry.register(serviceUrl3);
Assertions.assertTrue(notNotified.await(15, TimeUnit.SECONDS));
Assertions.assertEquals(3, notifiedUrls.size());
Assertions.assertTrue(notifiedUrls.containsKey(serviceUrl));
Assertions.assertTrue(notifiedUrls.containsKey(serviceUrl2));
Assertions.assertTrue(notifiedUrls.containsKey(serviceUrl3));
}
@Test
public void test_unsubscribe() throws InterruptedException {
Assertions.assertEquals(0, registry.getRegistered().size());
Assertions.assertEquals(0, registry.getSubscribed().size());
CountDownLatch notNotified = new CountDownLatch(2);
final AtomicReference<URL> notifiedUrl = new AtomicReference<URL>();
NotifyListener listener = new NotifyListener() {
public void notify(List<URL> urls) {
if (urls != null) {
for (Iterator<URL> iterator = urls.iterator(); iterator.hasNext(); ) {
URL url = iterator.next();
if (!url.getProtocol().equals("empty")) {
notifiedUrl.set(url);
notNotified.countDown();
}
}
}
}
};
registry.subscribe(consumerUrl, listener);
registry.unsubscribe(consumerUrl, listener);
registry.register(serviceUrl);
Assertions.assertFalse(notNotified.await(2, TimeUnit.SECONDS));
// expect nothing happen
Assertions.assertNull(notifiedUrl.get());
}
@BeforeEach
public void setUp() {
registry = (EtcdRegistry) registryFactory.getRegistry(registryUrl);
Assertions.assertNotNull(registry);
if (!registry.isAvailable()) {
AbstractRegistryFactory.destroyAll();
registry = (EtcdRegistry) registryFactory.getRegistry(registryUrl);
}
}
@AfterEach
public void tearDown() {
registry.unregister(serviceUrl);
registry.unregister(serviceUrl2);
registry.unregister(serviceUrl3);
registry.unregister(subscribe);
registry.destroy();
}
}

View File

@ -1,124 +0,0 @@
/*
* 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.registry.etcd;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceInstance;
import com.google.gson.Gson;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static java.lang.String.valueOf;
/**
* 2019-08-30
* <p>
* There is no embedded server. so it works depend on etcd local server.
*/
@Disabled
public class EtcdServiceDiscoveryTest {
static EtcdServiceDiscovery etcdServiceDiscovery;
@BeforeAll
public static void setUp() throws Exception {
URL url = URL.valueOf("etcd3://127.0.0.1:2379/org.apache.dubbo.registry.RegistryService");
etcdServiceDiscovery = new EtcdServiceDiscovery();
Assertions.assertNull(etcdServiceDiscovery.etcdClient);
etcdServiceDiscovery.initialize(url);
}
@AfterAll
public static void destroy() throws Exception {
// etcdServiceDiscovery.destroy();
}
@Test
public void testLifecycle() throws Exception {
URL url = URL.valueOf("etcd3://127.0.0.1:2233/org.apache.dubbo.registry.RegistryService");
EtcdServiceDiscovery etcdServiceDiscoveryTmp = new EtcdServiceDiscovery();
Assertions.assertNull(etcdServiceDiscoveryTmp.etcdClient);
etcdServiceDiscoveryTmp.initialize(url);
Assertions.assertNotNull(etcdServiceDiscoveryTmp.etcdClient);
Assertions.assertTrue(etcdServiceDiscoveryTmp.etcdClient.isConnected());
etcdServiceDiscoveryTmp.destroy();
Assertions.assertFalse(etcdServiceDiscoveryTmp.etcdClient.isConnected());
}
@Test
public void testRegistry() throws Exception {
ServiceInstance serviceInstance = new DefaultServiceInstance(valueOf(System.nanoTime()), "EtcdTestService", "127.0.0.1", 8080);
Assertions.assertNull(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)));
etcdServiceDiscovery.register(serviceInstance);
Assertions.assertNotNull(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)));
}
@Test
public void testUnRegistry() throws Exception {
ServiceInstance serviceInstance = new DefaultServiceInstance(valueOf(System.nanoTime()), "EtcdTest2Service", "127.0.0.1", 8080);
Assertions.assertNull(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)));
etcdServiceDiscovery.register(serviceInstance);
Assertions.assertNotNull(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)));
etcdServiceDiscovery.unregister(serviceInstance);
Assertions.assertNull(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)));
}
@Test
public void testUpdate() throws Exception {
DefaultServiceInstance serviceInstance = new DefaultServiceInstance(valueOf(System.nanoTime()), "EtcdTest34Service", "127.0.0.1", 8080);
Assertions.assertNull(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)));
etcdServiceDiscovery.register(serviceInstance);
Assertions.assertNotNull(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)));
Assertions.assertEquals(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)),
new Gson().toJson(serviceInstance));
serviceInstance.setPort(9999);
etcdServiceDiscovery.update(serviceInstance);
Assertions.assertNotNull(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)));
Assertions.assertEquals(etcdServiceDiscovery.etcdClient.getKVValue(etcdServiceDiscovery.toPath(serviceInstance)),
new Gson().toJson(serviceInstance));
}
@Test
public void testGetInstances() throws Exception {
String serviceName = "EtcdTest77Service";
Assertions.assertTrue(etcdServiceDiscovery.getInstances(serviceName).isEmpty());
etcdServiceDiscovery.register(new DefaultServiceInstance(valueOf(System.nanoTime()), serviceName, "127.0.0.1", 8080));
etcdServiceDiscovery.register(new DefaultServiceInstance(valueOf(System.nanoTime()), serviceName, "127.0.0.1", 9809));
Assertions.assertFalse(etcdServiceDiscovery.getInstances(serviceName).isEmpty());
List<String> r = convertToIpPort(etcdServiceDiscovery.getInstances(serviceName));
Assertions.assertTrue(r.contains("127.0.0.1:8080"));
Assertions.assertTrue(r.contains("127.0.0.1:9809"));
}
private List<String> convertToIpPort(List<ServiceInstance> serviceInstances) {
List<String> result = new ArrayList<>();
for (ServiceInstance serviceInstance : serviceInstances) {
result.add(serviceInstance.getHost() + ":" + serviceInstance.getPort());
}
return result;
}
}

View File

@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-registry-eureka</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The Eureka registry module of Dubbo project</description>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- Eureka -->
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- <dependency>-->
<!-- <groupId>javax.ws.rs</groupId>-->
<!-- <artifactId>javax.ws.rs-api</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-core</artifactId>
</dependency>
<!-- Eureka core deps that are now optional -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.10</version>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
<version>1.19.1</version>
</dependency>
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
<version>0.7.6</version>
</dependency>
</dependencies>
</project>

Some files were not shown because too many files have changed in this diff Show More