diff --git a/README-zh.md b/README-zh.md index 438568268..a52b26ffb 100644 --- a/README-zh.md +++ b/README-zh.md @@ -19,7 +19,7 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。 * **服务注册与发现**:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。 * **分布式配置管理**:支持分布式系统中的外部化配置,配置更改时自动刷新。 * **消息驱动能力**:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。 -* **分布式事务**:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。 +* **分布式事务**:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。 * **阿里云对象存储**:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。 * **分布式任务调度**:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。 * **阿里云短信服务**:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。 @@ -70,7 +70,7 @@ Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目 clone com.alibaba.cloud spring-cloud-alibaba-dependencies - 2.2.3.RELEASE + 2.2.5.RELEASE pom import diff --git a/README.md b/README.md index cc75f59c9..df9a1d1aa 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ With Spring Cloud Alibaba, you only need to add some annotations and a small amo For more features, please refer to [Roadmap](https://github.com/alibaba/spring-cloud-alibaba/blob/master/Roadmap.md). + ## Components **[Sentinel](https://github.com/alibaba/Sentinel)**: Sentinel takes "traffic flow" as the breakthrough point, and provides solutions in areas such as flow control, concurrency, circuit breaking, and load protection to protect service stability. @@ -71,7 +72,7 @@ These artifacts are available from Maven Central and Spring Release repository v com.alibaba.cloud spring-cloud-alibaba-dependencies - 2.2.3.RELEASE + 2.2.5.RELEASE pom import diff --git a/pom.xml b/pom.xml index 5af61288e..7f9f0f88b 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ - 2.2.4-SNAPSHOT + 2.2.5.RC2 2.2.5.RELEASE diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index 61c35ecc7..1fdaf8695 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -18,10 +18,10 @@ Spring Cloud Alibaba Dependencies - 2.2.4-SNAPSHOT + 2.2.5.RC2 1.8.0 1.3.0 - 1.4.0 + 1.4.1 0.8.0 1.0.10 @@ -245,6 +245,12 @@ ${revision} + + com.alibaba.cloud + spring-cloud-alibaba-commons + ${revision} + + com.alibaba.cloud diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc index 5300db702..5986ab0fd 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc @@ -10,7 +10,7 @@ Spring Cloud Alibaba BOM 包含了它所使用的所有依赖的版本。 com.alibaba.cloud spring-cloud-alibaba-dependencies - 2.2.3.RELEASE + 2.2.4.RELEASE pom import diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc index 9601c5094..1a9ce78c3 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc @@ -8,7 +8,7 @@ If you’re a Maven Central user, add our BOM to your pom.xml com.alibaba.cloud spring-cloud-alibaba-dependencies - 2.2.3.RELEASE + 2.2.4.RELEASE pom import diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/java/com/alibaba/cloud/examples/Application.java b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/java/com/alibaba/cloud/examples/Application.java index 69a5d4277..77dea4234 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/java/com/alibaba/cloud/examples/Application.java +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/java/com/alibaba/cloud/examples/Application.java @@ -161,29 +161,16 @@ class UserConfig { @Component class SampleRunner implements ApplicationRunner { - @Value("${user.name:zz}") - String userName; - - @Value("${user.age:25}") - int userAge; - @Autowired private NacosConfigManager nacosConfigManager; @Override public void run(ApplicationArguments args) throws Exception { - System.out.println( - String.format("Initial username=%s, userAge=%d", userName, userAge)); - nacosConfigManager.getConfigService().addListener( - "nacos-config-example.properties", "DEFAULT_GROUP", new Listener() { + "nacos-config-custom.properties", "DEFAULT_GROUP", new Listener() { /** * Callback with latest config data. - * - * For example, config data in Nacos is: - * - * user.name=Nacos user.age=25 * @param configInfo latest config data for specific dataId in Nacos * server */ diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/resources/bootstrap.properties b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/resources/bootstrap.properties index 9e500a4ee..4d880443f 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/resources/bootstrap.properties +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/resources/bootstrap.properties @@ -5,21 +5,27 @@ spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.username=nacos spring.cloud.nacos.password=nacos +## nacos-namespace cannot user 'public',cause by 'public' has special handing inside. #spring.cloud.nacos.config.namespace=public -spring.cloud.nacos.config.name=test-aaa -spring.cloud.nacos.config.file-extension=yaml +## you can specify a custom name if you don't want to use the application name. +#spring.cloud.nacos.config.name=test-aaa +#spring.cloud.nacos.config.file-extension=yaml + +## not recommended. #spring.cloud.nacos.config.refreshable-dataids=common.properties +## not recommended. #spring.cloud.nacos.config.shared-data-ids=common.properties,base-common.properties -#spring.cloud.nacos.config.shared-configs[0]= common333.properties -#spring.cloud.nacos.config.shared-configs[1].data-id= common111.properties -#spring.cloud.nacos.config.shared-configs[1].group= GROUP_APP1 -#spring.cloud.nacos.config.shared-configs[1].refresh= true -#spring.cloud.nacos.config.shared-configs[2]= common222.properties + +## recommended. spring.cloud.nacos.config.shared-configs[0].data-id= test2.yaml spring.cloud.nacos.config.shared-configs[0].refresh=true +## the default value is 'DEFAULT_GROUP' , if not specified. +spring.cloud.nacos.config.shared-configs[0].group= GROUP_APP1 +## not recommended. #spring.cloud.nacos.config.ext-config[0]=ext.properties +## recommended. spring.cloud.nacos.config.extension-configs[0].data-id= extension1.properties spring.cloud.nacos.config.extension-configs[0].refresh=true spring.cloud.nacos.config.extension-configs[1].data-id= test1.yml diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md index 0946cf9a7..3bf0cf3a2 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md @@ -72,7 +72,7 @@ Before we start the demo, let's learn how to connect Nacos Config to a Spring Cl #### Query Service -Enter `http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=service-provider` in the browser address bar and click Go to, we can see that the service node has been successfully registered to Nacos Server. +Enter `http://127.0.0.1:8848/nacos/#/serviceDetail?name=service-provider&groupName=DEFAULT_GROUP` in the browser address bar and click Go to, we can see that the service node has been successfully registered to Nacos Server. ![查询服务](https://cdn.nlark.com/lark/0/2018/png/54319/1536986288092-5cf96af9-9a26-466b-85f6-39ad1d92dfdc.png) diff --git a/spring-cloud-alibaba-examples/rocketmq-example/readme.md b/spring-cloud-alibaba-examples/rocketmq-example/readme.md index dc9490c60..eec6675f9 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/readme.md +++ b/spring-cloud-alibaba-examples/rocketmq-example/readme.md @@ -73,7 +73,7 @@ spring.cloud.stream.bindings.input.group=test-group You should startup Name Server and Broker before using RocketMQ Binder. -1. Download [RocketMQ](https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.3.2/rocketmq-all-4.3.2-bin-release.zip) and unzip it. +1. Download [RocketMQ](https://archive.apache.org/dist/rocketmq/4.3.2/rocketmq-all-4.3.2-bin-release.zip) and unzip it. 2. Startup Name Server diff --git a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/com/alibaba/cloud/examples/RocketMQConsumerApplication.java b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/com/alibaba/cloud/examples/RocketMQConsumerApplication.java index 2bd73880e..8634b8ca4 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/com/alibaba/cloud/examples/RocketMQConsumerApplication.java +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/com/alibaba/cloud/examples/RocketMQConsumerApplication.java @@ -74,10 +74,14 @@ public class RocketMQConsumerApplication { while (true) { mySink.input5().poll(m -> { String payload = (String) m.getPayload(); + if (payload.contains("0")) { + throw new IllegalArgumentException( + "111111111111111111111111111111111111111111"); + } System.out.println("pull msg: " + payload); }, new ParameterizedTypeReference() { }); - Thread.sleep(2_000); + Thread.sleep(5_00); } } diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml index b39b23ceb..f659fb10a 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml @@ -15,6 +15,11 @@ + + com.alibaba.cloud + spring-cloud-alibaba-commons + + org.springframework.boot spring-boot-starter-web diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/com/alibaba/cloud/dubbo/gateway/DubboGatewayServlet.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/com/alibaba/cloud/dubbo/gateway/DubboGatewayServlet.java index 8fe7f25d6..2cfb8db0d 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/com/alibaba/cloud/dubbo/gateway/DubboGatewayServlet.java +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/com/alibaba/cloud/dubbo/gateway/DubboGatewayServlet.java @@ -52,8 +52,8 @@ import org.springframework.util.StreamUtils; import org.springframework.web.servlet.HttpServletBean; import org.springframework.web.util.UriComponents; -import static org.apache.commons.lang3.StringUtils.substringAfter; -import static org.apache.commons.lang3.StringUtils.substringBetween; +import static com.alibaba.cloud.commons.lang.StringUtils.substringAfter; +import static com.alibaba.cloud.commons.lang.StringUtils.substringBetween; import static org.springframework.web.util.UriComponentsBuilder.fromUriString; @WebServlet(urlPatterns = "/dsc/*") diff --git a/spring-cloud-alibaba-starters/pom.xml b/spring-cloud-alibaba-starters/pom.xml index 1249b019e..568ea0180 100644 --- a/spring-cloud-alibaba-starters/pom.xml +++ b/spring-cloud-alibaba-starters/pom.xml @@ -27,6 +27,7 @@ spring-cloud-starter-alibaba-sentinel spring-cloud-alibaba-sentinel-datasource spring-cloud-alibaba-sentinel-gateway + spring-cloud-alibaba-commons diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/pom.xml new file mode 100644 index 000000000..73248d29b --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/pom.xml @@ -0,0 +1,18 @@ + + + + com.alibaba.cloud + spring-cloud-alibaba-starters + ${revision} + ../pom.xml + + + 4.0.0 + + spring-cloud-alibaba-commons + Spring Cloud Alibaba Commons + + + \ No newline at end of file diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/Charsets.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/Charsets.java new file mode 100644 index 000000000..f15ff0e65 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/Charsets.java @@ -0,0 +1,182 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.commons.io; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * The Charsets constants, copy from apache commons-io. + * + * @author theonefx + */ +public final class Charsets { + + private Charsets() { + } + + /** + * Constructs a sorted map from canonical charset names to charset objects required of + * every implementation of the Java platform. + *

+ * From the Java documentation + * + * Standard charsets: + *

+ * @return An immutable, case-insensitive map from canonical charset names to charset + * objects. + * @see Charset#availableCharsets() + */ + public static SortedMap requiredCharsets() { + // maybe cache? + final TreeMap m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + m.put(StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1); + m.put(StandardCharsets.US_ASCII.name(), StandardCharsets.US_ASCII); + m.put(StandardCharsets.UTF_16.name(), StandardCharsets.UTF_16); + m.put(StandardCharsets.UTF_16BE.name(), StandardCharsets.UTF_16BE); + m.put(StandardCharsets.UTF_16LE.name(), StandardCharsets.UTF_16LE); + m.put(StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8); + return Collections.unmodifiableSortedMap(m); + } + + /** + * Returns the given Charset or the default Charset if the given Charset is null. + * @param charset A charset or null. + * @return the given Charset or the default Charset if the given Charset is null + */ + public static Charset toCharset(final Charset charset) { + return charset == null ? Charset.defaultCharset() : charset; + } + + /** + * Returns a Charset for the named charset. If the name is null, return the default + * Charset. + * @param charset The name of the requested charset, may be null. + * @return a Charset for the named charset + * @throws java.nio.charset.UnsupportedCharsetException If the named charset is + * unavailable + */ + public static Charset toCharset(final String charset) { + return charset == null ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1. + *

+ * Every implementation of the Java platform is required to support this character + * encoding. + *

+ * + * @see Standard + * charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; + + /** + *

+ * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of + * the Unicode character set. + *

+ *

+ * Every implementation of the Java platform is required to support this character + * encoding. + *

+ * + * @see Standard + * charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset US_ASCII = StandardCharsets.US_ASCII; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory + * initial byte-order mark (either order accepted on input, big-endian used on output) + *

+ *

+ * Every implementation of the Java platform is required to support this character + * encoding. + *

+ * + * @see Standard + * charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset UTF_16 = StandardCharsets.UTF_16; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character + * encoding. + *

+ * + * @see Standard + * charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset UTF_16BE = StandardCharsets.UTF_16BE; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character + * encoding. + *

+ * + * @see Standard + * charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset UTF_16LE = StandardCharsets.UTF_16LE; + + /** + *

+ * Eight-bit Unicode Transformation Format. + *

+ *

+ * Every implementation of the Java platform is required to support this character + * encoding. + *

+ * + * @see Standard + * charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset UTF_8 = StandardCharsets.UTF_8; + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/FileUtils.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/FileUtils.java new file mode 100644 index 000000000..0635edce2 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/FileUtils.java @@ -0,0 +1,113 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.commons.io; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + +/** + * FileUtils. copy from apache commons-io. + * + * @author theonefx + */ +public final class FileUtils { + + private FileUtils() { + } + + // ----------------------------------------------------------------------- + /** + * Opens a {@link java.io.FileInputStream} for the specified file, providing better + * error messages than simply calling new FileInputStream(file). + *

+ * At the end of the method either the stream will be successfully opened, or an + * exception will have been thrown. + *

+ * An exception is thrown if the file does not exist. An exception is thrown if the + * file object exists but is a directory. An exception is thrown if the file exists + * but cannot be read. + * @param file the file to open for input, must not be {@code null} + * @return a new {@link java.io.FileInputStream} for the specified file + * @throws java.io.FileNotFoundException if the file does not exist + * @throws IOException if the file object is a directory + * @throws IOException if the file cannot be read + * @since 1.3 + */ + public static FileInputStream openInputStream(final File file) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + if (!file.canRead()) { + throw new IOException("File '" + file + "' cannot be read"); + } + } + else { + throw new FileNotFoundException("File '" + file + "' does not exist"); + } + return new FileInputStream(file); + } + + // ----------------------------------------------------------------------- + /** + * Reads the contents of a file into a String. The file is always closed. + * @param file the file to read, must not be {@code null} + * @param encoding the encoding to use, {@code null} means platform default + * @return the file contents, never {@code null} + * @throws IOException in case of an I/O error + */ + public static String readFileToString(final File file, final Charset encoding) + throws IOException { + try (InputStream in = openInputStream(file)) { + return IOUtils.toString(in, Charsets.toCharset(encoding)); + } + } + + /** + * Reads the contents of a file into a String. The file is always closed. + * @param file the file to read, must not be {@code null} + * @param encoding the encoding to use, {@code null} means platform default + * @return the file contents, never {@code null} + * @throws java.io.IOException in case of an I/O error + * @throws java.nio.charset.UnsupportedCharsetException thrown instead of + * {@link java.io .UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + */ + public static String readFileToString(final File file, final String encoding) + throws IOException { + return readFileToString(file, Charsets.toCharset(encoding)); + } + + /** + * Reads the contents of a file into a String using the default encoding for the VM. + * The file is always closed. + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + * @throws IOException in case of an I/O error + * @deprecated 2.5 use {@link #readFileToString(File, String)} instead (and specify + * the appropriate encoding) + */ + @Deprecated + public static String readFileToString(final File file) throws IOException { + return readFileToString(file, Charset.defaultCharset()); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/IOUtils.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/IOUtils.java new file mode 100644 index 000000000..85f61c5df --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/IOUtils.java @@ -0,0 +1,233 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.commons.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; + +/** + * The IOUtils. copy from apache commons-io. + * + * @author theonefx + */ +public final class IOUtils { + + /** + * Represents the end-of-file (or stream). + * @since 2.5 (made public) + */ + public static final int EOF = -1; + + /** + * The default buffer size ({@value}) to use for. + * {@link #copyLarge(InputStream, java.io.OutputStream)} and + * {@link #copyLarge(Reader, Writer)} + */ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + private IOUtils() { + + } + + /** + * Gets the contents of an InputStream as a String using the specified + * character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * @param input the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws java.io.IOException if an I/O error occurs + * @since 2.3 + */ + public static String toString(final InputStream input, final Charset encoding) + throws IOException { + try (StringBuilderWriter sw = new StringBuilderWriter()) { + copy(input, sw, encoding); + return sw.toString(); + } + } + + // copy from Reader + // ----------------------------------------------------------------------- + + /** + * Copies chars from a Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Large streams (over 2GB) will return a chars copied value of -1 after + * the copy has completed since the correct number of chars cannot be returned as an + * int. For large streams use the copyLarge(Reader, Writer) method. + * @param input the Reader to read from + * @param output the Writer to write to + * @return the number of characters copied, or -1 if > Integer.MAX_VALUE + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static int copy(final Reader input, final Writer output) throws IOException { + final long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; + } + + /** + * Copies bytes from an InputStream to chars on a Writer + * using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * This method uses {@link java.io.InputStreamReader}. + * @param input the InputStream to read from + * @param output the Writer to write to + * @param inputEncoding the encoding to use for the input stream, null means platform + * default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void copy(final InputStream input, final Writer output, + final Charset inputEncoding) throws IOException { + final InputStreamReader in = new InputStreamReader(input, + Charsets.toCharset(inputEncoding)); + copy(in, output); + } + + /** + * Copies bytes from an InputStream to an OutputStream using + * an internal buffer of the given size. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param bufferSize the bufferSize used to copy from the input to the output + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.5 + */ + public static long copy(final InputStream input, final OutputStream output, + final int bufferSize) throws IOException { + return copyLarge(input, output, new byte[bufferSize]); + } + + /** + * Copies chars from a large (over 2GB) Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * @param input the Reader to read from + * @param output the Writer to write to + * @return the number of characters copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.3 + */ + public static long copyLarge(final Reader input, final Writer output) + throws IOException { + return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copies chars from a large (over 2GB) Reader to a Writer. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedReader. + *

+ * @param input the Reader to read from + * @param output the Writer to write to + * @param buffer the buffer to be used for the copy + * @return the number of characters copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(final Reader input, final Writer output, + final char[] buffer) throws IOException { + long count = 0; + int n; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Copies bytes from a large (over 2GB) InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.3 + */ + public static long copyLarge(final InputStream input, final OutputStream output) + throws IOException { + return copy(input, output, DEFAULT_BUFFER_SIZE); + } + + /** + * Copies bytes from a large (over 2GB) InputStream to an + * OutputStream. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedInputStream. + *

+ * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param buffer the buffer to use for the copy + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(final InputStream input, final OutputStream output, + final byte[] buffer) throws IOException { + long count = 0; + int n; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/StringBuilderWriter.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/StringBuilderWriter.java new file mode 100644 index 000000000..9a0578162 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/StringBuilderWriter.java @@ -0,0 +1,152 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.commons.io; + +import java.io.Serializable; +import java.io.Writer; + +/** + * Copy from apache commons-io. + * + * @author theonefx + */ +public class StringBuilderWriter extends Writer implements Serializable { + + private static final long serialVersionUID = -146927496096066153L; + + private final StringBuilder builder; + + /** + * Constructs a new {@link StringBuilder} instance with default capacity. + */ + public StringBuilderWriter() { + this.builder = new StringBuilder(); + } + + /** + * Constructs a new {@link StringBuilder} instance with the specified capacity. + * @param capacity The initial capacity of the underlying {@link StringBuilder} + */ + public StringBuilderWriter(final int capacity) { + this.builder = new StringBuilder(capacity); + } + + /** + * Constructs a new instance with the specified {@link StringBuilder}. + * + *

+ * If {@code builder} is null a new instance with default capacity will be created. + *

+ * @param builder The String builder. May be null. + */ + public StringBuilderWriter(final StringBuilder builder) { + this.builder = builder != null ? builder : new StringBuilder(); + } + + /** + * Appends a single character to this Writer. + * @param value The character to append + * @return This writer instance + */ + @Override + public Writer append(final char value) { + builder.append(value); + return this; + } + + /** + * Appends a character sequence to this Writer. + * @param value The character to append + * @return This writer instance + */ + @Override + public Writer append(final CharSequence value) { + builder.append(value); + return this; + } + + /** + * Appends a portion of a character sequence to the {@link StringBuilder}. + * @param value The character to append + * @param start The index of the first character + * @param end The index of the last character + 1 + * @return This writer instance + */ + @Override + public Writer append(final CharSequence value, final int start, final int end) { + builder.append(value, start, end); + return this; + } + + /** + * Closing this writer has no effect. + */ + @Override + public void close() { + // no-op + } + + /** + * Flushing this writer has no effect. + */ + @Override + public void flush() { + // no-op + } + + /** + * Writes a String to the {@link StringBuilder}. + * @param value The value to write + */ + @Override + public void write(final String value) { + if (value != null) { + builder.append(value); + } + } + + /** + * Writes a portion of a character array to the {@link StringBuilder}. + * @param value The value to write + * @param offset The index of the first character + * @param length The number of characters to write + */ + @Override + public void write(final char[] value, final int offset, final int length) { + if (value != null) { + builder.append(value, offset, length); + } + } + + /** + * Returns the underlying builder. + * @return The underlying builder + */ + public StringBuilder getBuilder() { + return builder; + } + + /** + * Returns {@link StringBuilder#toString()}. + * @return The contents of the String builder. + */ + @Override + public String toString() { + return builder.toString(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/lang/StringUtils.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/lang/StringUtils.java new file mode 100644 index 000000000..d9c151361 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/lang/StringUtils.java @@ -0,0 +1,372 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.commons.lang; + +/** + * StringUtils. copy from apache common-lang3. + * + * @author theonefx + */ +public final class StringUtils { + + /** + * The empty String {@code ""}. + * + * @since 2.0 + */ + public static final String EMPTY = ""; + + /** + * Represents a failed index search. + * @since 2.1 + */ + public static final int INDEX_NOT_FOUND = -1; + + private StringUtils() { + } + + /** + *

+ * Checks if a CharSequence is empty ("") or null. + *

+ * + *
+	 * StringUtils.isEmpty(null)      = true
+	 * StringUtils.isEmpty("")        = true
+	 * StringUtils.isEmpty(" ")       = false
+	 * StringUtils.isEmpty("bob")     = false
+	 * StringUtils.isEmpty("  bob  ") = false
+	 * 
+ * + *

+ * NOTE: This method changed in Lang version 2.0. It no longer trims the CharSequence. + * That functionality is available in isBlank(). + *

+ * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is empty or null + * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) + */ + public static boolean isEmpty(final CharSequence cs) { + return cs == null || cs.length() == 0; + } + + /** + *

+ * Checks if a CharSequence is not empty ("") and not null. + *

+ * + *
+	 * StringUtils.isNotEmpty(null)      = false
+	 * StringUtils.isNotEmpty("")        = false
+	 * StringUtils.isNotEmpty(" ")       = true
+	 * StringUtils.isNotEmpty("bob")     = true
+	 * StringUtils.isNotEmpty("  bob  ") = true
+	 * 
+ * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not empty and not null + * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) + */ + public static boolean isNotEmpty(final CharSequence cs) { + return !isEmpty(cs); + } + + /** + *

+ * Checks if a CharSequence is whitespace, empty ("") or null. + *

+ * + *
+	 * StringUtils.isBlank(null)      = true
+	 * StringUtils.isBlank("")        = true
+	 * StringUtils.isBlank(" ")       = true
+	 * StringUtils.isBlank("bob")     = false
+	 * StringUtils.isBlank("  bob  ") = false
+	 * 
+ * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is null, empty or whitespace + */ + public static boolean isBlank(final CharSequence cs) { + if (cs == null || cs.length() == 0) { + return true; + } + int strLen = cs.length(); + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + return true; + } + + /** + *

+ * Checks if a CharSequence is not empty (""), not null and not whitespace only. + *

+ * + *

+ * Whitespace is defined by {@link Character#isWhitespace(char)}. + *

+ * + *
+	 * StringUtils.isNotBlank(null)      = false
+	 * StringUtils.isNotBlank("")        = false
+	 * StringUtils.isNotBlank(" ")       = false
+	 * StringUtils.isNotBlank("bob")     = true
+	 * StringUtils.isNotBlank("  bob  ") = true
+	 * 
+ * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not empty and not null and not + * whitespace only + * @since 2.0 + * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) + */ + public static boolean isNotBlank(final CharSequence cs) { + return !isBlank(cs); + } + + // Trim + // ----------------------------------------------------------------------- + + /** + *

+ * Removes control characters (char <= 32) from both ends of this String, handling + * {@code null} by returning {@code null}. + *

+ * + *

+ * The String is trimmed using {@link String#trim()}. Trim removes start and end + * characters <= 32. + *

+ * + *
+	 * StringUtils.trim(null)          = null
+	 * StringUtils.trim("")            = ""
+	 * StringUtils.trim("     ")       = ""
+	 * StringUtils.trim("abc")         = "abc"
+	 * StringUtils.trim("    abc    ") = "abc"
+	 * 
+ * @param str the String to be trimmed, may be null + * @return the trimmed string, {@code null} if null String input + */ + public static String trim(final String str) { + return str == null ? null : str.trim(); + } + + // Equals + // ----------------------------------------------------------------------- + + /** + *

+ * Compares two CharSequences, returning {@code true} if they represent equal + * sequences of characters. + *

+ * + *

+ * {@code null}s are handled without exceptions. Two {@code null} references are + * considered to be equal. The comparison is case sensitive. + *

+ * + *
+	 * StringUtils.equals(null, null)   = true
+	 * StringUtils.equals(null, "abc")  = false
+	 * StringUtils.equals("abc", null)  = false
+	 * StringUtils.equals("abc", "abc") = true
+	 * StringUtils.equals("abc", "ABC") = false
+	 * 
+ * @param cs1 the first CharSequence, may be {@code null} + * @param cs2 the second CharSequence, may be {@code null} + * @return {@code true} if the CharSequences are equal (case-sensitive), or both + * {@code null} + * @see Object#equals(Object) + */ + public static boolean equals(final CharSequence cs1, final CharSequence cs2) { + if (cs1 == cs2) { + return true; + } + if (cs1 == null || cs2 == null) { + return false; + } + if (cs1 instanceof String && cs2 instanceof String) { + return cs1.equals(cs2); + } + return StringUtils.regionMatches(cs1, false, 0, cs2, 0, + Math.max(cs1.length(), cs2.length())); + } + + /** + * Green implementation of regionMatches. + * @param cs the {@code CharSequence} to be processed + * @param ignoreCase whether or not to be case insensitive + * @param thisStart the index to start on the {@code cs} CharSequence + * @param substring the {@code CharSequence} to be looked for + * @param start the index to start on the {@code substring} CharSequence + * @param length character length of the region + * @return whether the region matched + */ + public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, + final int thisStart, final CharSequence substring, final int start, + final int length) { + if (cs instanceof String && substring instanceof String) { + return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, + start, length); + } + int index1 = thisStart; + int index2 = start; + int tmpLen = length; + + while (tmpLen-- > 0) { + final char c1 = cs.charAt(index1++); + final char c2 = substring.charAt(index2++); + + if (c1 == c2) { + continue; + } + + if (!ignoreCase) { + return false; + } + + // The same check as in String.regionMatches(): + if (Character.toUpperCase(c1) != Character.toUpperCase(c2) + && Character.toLowerCase(c1) != Character.toLowerCase(c2)) { + return false; + } + } + + return true; + } + + /** + *

+ * Gets the substring after the first occurrence of a separator. The separator is not + * returned. + *

+ * + *

+ * A null string input will return null. An empty ("") + * string input will return the empty string. A null separator will + * return the empty string if the input string is not null. + *

+ * + *

+ * If nothing is found, the empty string is returned. + *

+ * + *
+	 * StringUtils.substringAfter(null, *)      = null
+	 * StringUtils.substringAfter("", *)        = ""
+	 * StringUtils.substringAfter(*, null)      = ""
+	 * StringUtils.substringAfter("abc", "a")   = "bc"
+	 * StringUtils.substringAfter("abcba", "b") = "cba"
+	 * StringUtils.substringAfter("abc", "c")   = ""
+	 * StringUtils.substringAfter("abc", "d")   = ""
+	 * StringUtils.substringAfter("abc", "")    = "abc"
+	 * 
+ * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring after the first occurrence of the separator, + * null if null String input + * @since 2.0 + */ + public static String substringAfter(String str, String separator) { + if (isEmpty(str)) { + return str; + } + if (separator == null) { + return EMPTY; + } + int pos = str.indexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return EMPTY; + } + return str.substring(pos + separator.length()); + } + + // Substring between + // ----------------------------------------------------------------------- + /** + *

+ * Gets the String that is nested in between two instances of the same String. + *

+ * + *

+ * A null input String returns null. A null tag + * returns null. + *

+ * + *
+	 * StringUtils.substringBetween(null, *)            = null
+	 * StringUtils.substringBetween("", "")             = ""
+	 * StringUtils.substringBetween("", "tag")          = null
+	 * StringUtils.substringBetween("tagabctag", null)  = null
+	 * StringUtils.substringBetween("tagabctag", "")    = ""
+	 * StringUtils.substringBetween("tagabctag", "tag") = "abc"
+	 * 
+ * @param str the String containing the substring, may be null + * @param tag the String before and after the substring, may be null + * @return the substring, null if no match + * @since 2.0 + */ + public static String substringBetween(String str, String tag) { + return substringBetween(str, tag, tag); + } + + /** + *

+ * Gets the String that is nested in between two Strings. Only the first match is + * returned. + *

+ * + *

+ * A null input String returns null. A null + * open/close returns null (no match). An empty ("") open and close + * returns an empty string. + *

+ * + *
+	 * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b"
+	 * StringUtils.substringBetween(null, *, *)          = null
+	 * StringUtils.substringBetween(*, null, *)          = null
+	 * StringUtils.substringBetween(*, *, null)          = null
+	 * StringUtils.substringBetween("", "", "")          = ""
+	 * StringUtils.substringBetween("", "", "]")         = null
+	 * StringUtils.substringBetween("", "[", "]")        = null
+	 * StringUtils.substringBetween("yabcz", "", "")     = ""
+	 * StringUtils.substringBetween("yabcz", "y", "z")   = "abc"
+	 * StringUtils.substringBetween("yabczyabcz", "y", "z")   = "abc"
+	 * 
+ * @param str the String containing the substring, may be null + * @param open the String before the substring, may be null + * @param close the String after the substring, may be null + * @return the substring, null if no match + * @since 2.0 + */ + public static String substringBetween(String str, String open, String close) { + if (str == null || open == null || close == null) { + return null; + } + int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) { + int end = str.indexOf(close, start + open.length()); + if (end != INDEX_NOT_FOUND) { + return str.substring(start + open.length(), end); + } + } + return null; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/pom.xml index 3316d7bd8..080a35d00 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/pom.xml +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/pom.xml @@ -15,6 +15,10 @@ Spring Cloud Alibaba Sentinel DataSource + + com.alibaba.cloud + spring-cloud-alibaba-commons + diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java index d9b514038..a0b85d55a 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java @@ -16,9 +16,9 @@ package com.alibaba.cloud.sentinel.datasource.factorybean; +import com.alibaba.cloud.commons.lang.StringUtils; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.FactoryBean; diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/src/test/java/com/alibaba/cloud/sentinel/datasource/SentinelConverterTests.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/src/test/java/com/alibaba/cloud/sentinel/datasource/SentinelConverterTests.java index 1053c3679..f985984ee 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/src/test/java/com/alibaba/cloud/sentinel/datasource/SentinelConverterTests.java +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-sentinel-datasource/src/test/java/com/alibaba/cloud/sentinel/datasource/SentinelConverterTests.java @@ -19,13 +19,13 @@ package com.alibaba.cloud.sentinel.datasource; import java.io.IOException; import java.util.List; +import com.alibaba.cloud.commons.io.FileUtils; import com.alibaba.cloud.sentinel.datasource.converter.JsonConverter; import com.alibaba.cloud.sentinel.datasource.converter.XmlConverter; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import org.apache.commons.io.FileUtils; import org.junit.Test; import org.springframework.util.ResourceUtils; diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java index e8199832d..e27b3e06b 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java @@ -18,8 +18,6 @@ package com.alibaba.cloud.nacos; import java.util.Objects; -import javax.annotation.PreDestroy; - import com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureException; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; @@ -75,14 +73,6 @@ public class NacosConfigManager { return service; } - @PreDestroy - public void destroy() throws NacosException { - if (service != null) { - service.shutDown(); - service = null; - } - } - public NacosConfigProperties getNacosConfigProperties() { return nacosConfigProperties; } diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/AbstractPropertySourceLoader.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/AbstractPropertySourceLoader.java index 8ba0dd2e4..4990d94ed 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/AbstractPropertySourceLoader.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/AbstractPropertySourceLoader.java @@ -30,8 +30,6 @@ import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; import org.springframework.util.StringUtils; -import static com.alibaba.cloud.nacos.parser.NacosDataParserHandler.DOT; - /** * Nacos-specific loader, If need to support other methods of parsing,you need to do the * following steps: @@ -47,6 +45,11 @@ import static com.alibaba.cloud.nacos.parser.NacosDataParserHandler.DOT; */ public abstract class AbstractPropertySourceLoader implements PropertySourceLoader { + /** + * symbol: dot. + */ + static final String DOT = "."; + /** * Prevent interference with other loaders.Nacos-specific loader, unless the reload * changes it. diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataParserHandler.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataParserHandler.java index 82ad817ce..9d3c94ab8 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataParserHandler.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataParserHandler.java @@ -25,7 +25,10 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import com.alibaba.cloud.nacos.utils.NacosConfigUtils; + import org.springframework.boot.env.OriginTrackedMapPropertySource; +import org.springframework.boot.env.PropertiesPropertySourceLoader; import org.springframework.boot.env.PropertySourceLoader; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.PropertySource; @@ -33,25 +36,17 @@ import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import static com.alibaba.cloud.nacos.parser.AbstractPropertySourceLoader.DOT; + /** * @author zkz */ public final class NacosDataParserHandler { - /** - * symbol: dot. - */ - public static final String DOT = "."; - - /** - * constant. - */ - public static final String VALUE = "value"; - /** * default extension. */ - public static final String DEFAULT_EXTENSION = "properties"; + private static final String DEFAULT_EXTENSION = "properties"; private static List propertySourceLoaders; @@ -80,8 +75,18 @@ public final class NacosDataParserHandler { if (!canLoadFileExtension(propertySourceLoader, extension)) { continue; } - NacosByteArrayResource nacosByteArrayResource = new NacosByteArrayResource( - configValue.getBytes(), configName); + NacosByteArrayResource nacosByteArrayResource; + if (propertySourceLoader instanceof PropertiesPropertySourceLoader) { + // PropertiesPropertySourceLoader internal is to use the ISO_8859_1, + // the Chinese will be garbled, needs to transform into unicode. + nacosByteArrayResource = new NacosByteArrayResource( + NacosConfigUtils.selectiveConvertUnicode(configValue).getBytes(), + configName); + } + else { + nacosByteArrayResource = new NacosByteArrayResource( + configValue.getBytes(), configName); + } nacosByteArrayResource.setFilename(getFileName(configName, extension)); List> propertySourceList = propertySourceLoader .load(configName, nacosByteArrayResource); diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosJsonPropertySourceLoader.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosJsonPropertySourceLoader.java index 6c633ddfe..ed9aa0655 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosJsonPropertySourceLoader.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosJsonPropertySourceLoader.java @@ -28,14 +28,16 @@ import org.springframework.boot.env.OriginTrackedMapPropertySource; import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; -import static com.alibaba.cloud.nacos.parser.NacosDataParserHandler.DOT; -import static com.alibaba.cloud.nacos.parser.NacosDataParserHandler.VALUE; - /** * @author zkz */ public class NacosJsonPropertySourceLoader extends AbstractPropertySourceLoader { + /** + * constant. + */ + private static final String VALUE = "value"; + /** * Returns the file extensions that the loader supports (excluding the '.'). * @return the file extensions diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosXmlPropertySourceLoader.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosXmlPropertySourceLoader.java index 666c64b4c..f656bed3c 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosXmlPropertySourceLoader.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosXmlPropertySourceLoader.java @@ -127,8 +127,7 @@ public class NacosXmlPropertySourceLoader extends AbstractPropertySourceLoader continue; } - String key = StringUtils.isEmpty(parentKey) ? name - : parentKey + NacosDataParserHandler.DOT + name; + String key = StringUtils.isEmpty(parentKey) ? name : parentKey + DOT + name; NamedNodeMap nodeMap = node.getAttributes(); parseNodeAttr(nodeMap, map, key); if (node.getNodeType() == Node.ELEMENT_NODE && node.hasChildNodes()) { @@ -159,8 +158,8 @@ public class NacosXmlPropertySourceLoader extends AbstractPropertySourceLoader if (StringUtils.isEmpty(node.getNodeValue())) { continue; } - map.put(String.join(NacosDataParserHandler.DOT, parentKey, - node.getNodeName()), node.getNodeValue()); + map.put(String.join(DOT, parentKey, node.getNodeName()), + node.getNodeValue()); } } } diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/utils/NacosConfigUtils.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/utils/NacosConfigUtils.java new file mode 100644 index 000000000..b8a448dd7 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/utils/NacosConfigUtils.java @@ -0,0 +1,71 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.nacos.utils; + +/** + * @author zkzlx + */ +public final class NacosConfigUtils { + + private NacosConfigUtils() { + } + + /** + * Convert Chinese characters to Unicode. + * @param configValue value of config + * @return new string + */ + public static String selectiveConvertUnicode(String configValue) { + StringBuilder sb = new StringBuilder(); + char[] chars = configValue.toCharArray(); + for (char aChar : chars) { + if (isBaseLetter(aChar)) { + sb.append(aChar); + } + else { + sb.append(String.format("\\u%04x", (int) aChar)); + } + } + return sb.toString(); + } + + /** + * char is base latin or whitespace? + * @param ch a character + * @return true or false + */ + public static boolean isBaseLetter(char ch) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(ch); + return ub == Character.UnicodeBlock.BASIC_LATIN || Character.isWhitespace(ch); + } + + /** + * char is chinese? + * @param c a character + * @return true or false + */ + public static boolean isChinese(char c) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); + return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A + || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION + || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION + || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/pom.xml index 1751f4fca..02a3c3e39 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/pom.xml +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/pom.xml @@ -14,6 +14,11 @@ + + com.alibaba.cloud + spring-cloud-alibaba-commons + + org.springframework.boot spring-boot-actuator diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosDiscoveryEndpoint.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosDiscoveryEndpoint.java index b1aea4179..ad2e762e0 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosDiscoveryEndpoint.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosDiscoveryEndpoint.java @@ -24,6 +24,7 @@ import java.util.Map; import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosServiceManager; import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,6 +67,11 @@ public class NacosDiscoveryEndpoint { try { subscribe = namingService.getSubscribeServices(); + for (ServiceInfo serviceInfo : subscribe) { + List instances = namingService.getAllInstances( + serviceInfo.getName(), serviceInfo.getGroupName()); + serviceInfo.setHosts(instances); + } } catch (Exception e) { log.error("get subscribe services from nacos fail,", e); diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/NacosRule.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/NacosRule.java index 8d705b044..9873b8db3 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/NacosRule.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/NacosRule.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import com.alibaba.cloud.commons.lang.StringUtils; import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosServiceManager; import com.alibaba.nacos.api.naming.NamingService; @@ -28,7 +29,6 @@ import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.DynamicServerListLoadBalancer; import com.netflix.loadbalancer.Server; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignObjectWrapper.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignObjectWrapper.java index ae505058f..c13391dbd 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignObjectWrapper.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignObjectWrapper.java @@ -17,8 +17,8 @@ package com.alibaba.cloud.seata.feign; import feign.Client; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient; @@ -32,7 +32,8 @@ import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; */ public class SeataFeignObjectWrapper { - private static final Log LOG = LogFactory.getLog(SeataFeignObjectWrapper.class); + private static final Logger LOG = LoggerFactory + .getLogger(SeataFeignObjectWrapper.class); private final BeanFactory beanFactory; diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/DubboMetadataEndpointAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/DubboMetadataEndpointAutoConfiguration.java index bfcc87c72..5640f241b 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/DubboMetadataEndpointAutoConfiguration.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/DubboMetadataEndpointAutoConfiguration.java @@ -16,10 +16,11 @@ package com.alibaba.cloud.dubbo.actuate; +import com.alibaba.cloud.dubbo.actuate.endpoint.DubboDiscoveryEndpoint; +import com.alibaba.cloud.dubbo.actuate.endpoint.DubboExportedURLsEndpoint; import com.alibaba.cloud.dubbo.actuate.endpoint.DubboRestMetadataEndpoint; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; -import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -34,7 +35,7 @@ import org.springframework.context.annotation.PropertySource; @ConditionalOnClass( name = "org.springframework.boot.actuate.endpoint.annotation.Endpoint") @PropertySource("classpath:/META-INF/dubbo/default/actuator-endpoints.properties") -@ManagementContextConfiguration +@Configuration(proxyBeanMethods = false) public class DubboMetadataEndpointAutoConfiguration { @Bean @@ -44,4 +45,19 @@ public class DubboMetadataEndpointAutoConfiguration { return new DubboRestMetadataEndpoint(); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint + public DubboDiscoveryEndpoint dubboDiscoveryEndpoint() { + + return new DubboDiscoveryEndpoint(); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint + public DubboExportedURLsEndpoint dubboServiceMetadataEndpoint() { + return new DubboExportedURLsEndpoint(); + } + } diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboDiscoveryEndpoint.java b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboDiscoveryEndpoint.java new file mode 100644 index 000000000..2333e27c5 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboDiscoveryEndpoint.java @@ -0,0 +1,93 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.dubbo.actuate.endpoint; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.alibaba.cloud.dubbo.registry.DubboCloudRegistry; +import com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.registry.NotifyListener; +import org.apache.dubbo.registry.integration.RegistryDirectory; +import org.apache.dubbo.rpc.Invoker; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; +import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +/** + * Dubbo Registry Directory Metadata {@link DubboCloudRegistry}. + * + * @author Theonefx + */ +@Endpoint(id = "dubboRegistryDirectory") +public class DubboDiscoveryEndpoint { + + @ReadOperation(produces = APPLICATION_JSON_VALUE) + public Object get() { + DubboCloudRegistry registry = (DubboCloudRegistry) SpringCloudRegistryFactory + .getRegistries().stream().filter(o -> o instanceof DubboCloudRegistry) + .findFirst().orElse(null); + + if (registry == null) { + return Collections.emptyMap(); + } + + Map> subscribeMap = registry.getSubscribed(); + + Map>> result = new HashMap<>(); + subscribeMap.forEach((url, listeners) -> { + String side = url.getParameter(SIDE_KEY); + if (!CONSUMER_SIDE.equals(side)) { + return; + } + + List> pairs = result.computeIfAbsent(url.getServiceKey(), + o -> new ArrayList<>()); + + Map pair = new HashMap<>(); + List invokerServices = new ArrayList<>(); + for (NotifyListener listener : listeners) { + if (!(listener instanceof RegistryDirectory)) { + continue; + } + RegistryDirectory directory = (RegistryDirectory) listener; + List> invokers = directory.getAllInvokers(); + if (invokers == null) { + continue; + } + invokerServices.addAll(invokers.stream().map(Invoker::getUrl) + .map(URL::toServiceString).collect(Collectors.toList())); + } + pair.put("invokers", invokerServices); + pair.put("subscribeUrl", url.toMap()); + + pairs.add(pair); + }); + return result; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboExportedURLsEndpoint.java b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboExportedURLsEndpoint.java new file mode 100644 index 000000000..d13a8042f --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboExportedURLsEndpoint.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.dubbo.actuate.endpoint; + +import com.alibaba.cloud.dubbo.service.DubboMetadataService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; + +/** + * Dubbo exported URLs. + * {@link org.springframework.boot.actuate.endpoint.annotation.Endpoint}. + * + * @author Theonefx + */ +@Endpoint(id = "dubboExportedURLs") +public class DubboExportedURLsEndpoint { + + @Autowired + private DubboMetadataService dubboMetadataService; + + @ReadOperation(produces = APPLICATION_JSON_UTF8_VALUE) + public Object get() { + return dubboMetadataService.getAllExportedURLs(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DubboCloudRegistry.java b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DubboCloudRegistry.java index be24b11fb..2101a217b 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DubboCloudRegistry.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DubboCloudRegistry.java @@ -44,6 +44,8 @@ import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.util.CollectionUtils; import static java.lang.String.format; @@ -184,16 +186,30 @@ public class DubboCloudRegistry extends FailbackRegistry { subscribeURLs(url, getServices(url), listener); // Async subscription - registerServiceInstancesChangedListener(url, event -> { + registerServiceInstancesChangedListener(url, - Set serviceNames = getServices(url); + new ApplicationListener() { - String serviceName = event.getServiceName(); + private final URL url2subscribe = url; - if (serviceNames.contains(serviceName)) { - subscribeURLs(url, serviceNames, listener); - } - }); + @Override + @Order + public void onApplicationEvent(ServiceInstancesChangedEvent event) { + Set serviceNames = getServices(url); + + String serviceName = event.getServiceName(); + + if (serviceNames.contains(serviceName)) { + subscribeURLs(url, serviceNames, listener); + } + } + + @Override + public String toString() { + return "ServiceInstancesChangedEventListener:" + + url.getServiceKey(); + } + }); } private void subscribeURLs(URL url, Set serviceNames, @@ -375,12 +391,12 @@ public class DubboCloudRegistry extends FailbackRegistry { // Add the EMPTY_PROTOCOL URL subscribedURLs.add(emptyURL(url)); - if (isDubboMetadataServiceURL(url)) { - // if meta service change, and serviceInstances is zero, will clean up - // information about this client - String serviceName = url.getParameter(GROUP_KEY); - repository.removeMetadataAndInitializedService(serviceName, url); - } + // if (isDubboMetadataServiceURL(url)) { + // if meta service change, and serviceInstances is zero, will clean up + // information about this client + // String serviceName = url.getParameter(GROUP_KEY); + // repository.removeMetadataAndInitializedService(serviceName, url); + // } } if (logger.isDebugEnabled()) { @@ -415,7 +431,7 @@ public class DubboCloudRegistry extends FailbackRegistry { } private String generateId(URL url) { - return url.getServiceKey(); + return url.toString(); } private URL emptyURL(URL url) { @@ -450,16 +466,30 @@ public class DubboCloudRegistry extends FailbackRegistry { // Sync subscription if (containsProviderCategory(subscribedURL)) { - registerServiceInstancesChangedListener(subscribedURL, event -> { + registerServiceInstancesChangedListener(subscribedURL, + new ApplicationListener() { + + private final URL url2subscribe = subscribedURL; + + @Override + @Order(Ordered.LOWEST_PRECEDENCE - 1) + public void onApplicationEvent( + ServiceInstancesChangedEvent event) { + String sourceServiceName = event.getServiceName(); + String serviceName = getServiceName(subscribedURL); - String sourceServiceName = event.getServiceName(); - String serviceName = getServiceName(subscribedURL); + if (Objects.equals(sourceServiceName, serviceName)) { + subscribeDubboMetadataServiceURLs(subscribedURL, listener, + sourceServiceName); + } + } - if (Objects.equals(sourceServiceName, serviceName)) { - subscribeDubboMetadataServiceURLs(subscribedURL, listener, - sourceServiceName); - } - }); + @Override + public String toString() { + return "ServiceInstancesChangedEventListener:" + + subscribedURL.getServiceKey(); + } + }); } } diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java index ff70950d6..b6f65fa67 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java @@ -84,8 +84,6 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab */ public DubboMetadataService getProxy(List serviceInstances) { - DubboMetadataService dubboMetadataService = null; - // attempt to get the proxy of DubboMetadataService in maximum times int attempts = serviceInstances.size(); @@ -98,7 +96,8 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab serviceInstance.get()); for (URL dubboMetadataServiceURL : dubboMetadataServiceURLs) { - dubboMetadataService = createProxyIfAbsent(dubboMetadataServiceURL); + DubboMetadataService dubboMetadataService = createProxyIfAbsent( + dubboMetadataServiceURL); if (dubboMetadataService != null) { return dubboMetadataService; } @@ -106,7 +105,7 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab } } - return dubboMetadataService; + return null; } /** diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/resources/META-INF/spring.factories index 06c5b5921..3cf51ce58 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-dubbo/src/main/resources/META-INF/spring.factories @@ -5,8 +5,7 @@ com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration, com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationNonWebApplicationAutoConfiguration,\ com.alibaba.cloud.dubbo.autoconfigure.DubboLoadBalancedRestTemplateAutoConfiguration,\ com.alibaba.cloud.dubbo.autoconfigure.DubboServiceAutoConfiguration,\ -com.alibaba.cloud.dubbo.autoconfigure.DubboServiceDiscoveryAutoConfiguration -org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\ +com.alibaba.cloud.dubbo.autoconfigure.DubboServiceDiscoveryAutoConfiguration,\ com.alibaba.cloud.dubbo.actuate.DubboMetadataEndpointAutoConfiguration org.springframework.context.ApplicationContextInitializer=\ com.alibaba.cloud.dubbo.context.DubboServiceRegistrationApplicationContextInitializer