diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..5c2cb13c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,34 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
+We recommend using English. If you are non-native English speaker, you can use the translation software.
+
+**Which Component**
+eg. Nacos Discovery, Sentinel
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Additional context**
+Add any other context about the problem here.
+e.g. MacOS 、Java8 、 Version 0.2.1.RELEASE
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..f4117418
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,27 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
+We recommend using English. If you are non-native English speaker, you can use the translation software.
+
+
+**Which Component**
+eg. Nacos Discovery, Sentinel
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
new file mode 100644
index 00000000..6ef2bf0a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -0,0 +1,21 @@
+---
+name: Question
+about: how to ask an valid question
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
+We recommend using English. If you are non-native English speaker, you can use the translation software.
+
+
+**Which Component**
+eg. Nacos Discovery, Sentinel
+
+**Describe what problem you have encountered**
+A clear and concise description of what you want to do.
+
+**Describe what information you have read**
+eg. I have read the reference doc of Sentinel
diff --git a/CODE_OF_CONDUCT.adoc b/CODE_OF_CONDUCT.adoc
new file mode 100644
index 00000000..77a7e0ac
--- /dev/null
+++ b/CODE_OF_CONDUCT.adoc
@@ -0,0 +1,45 @@
+
+= Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of fostering an open
+and welcoming community, we pledge to respect all people who contribute through reporting
+issues, posting feature requests, updating documentation, submitting pull requests or
+patches, and other activities.
+
+We are committed to making participation in this project a harassment-free experience for
+everyone, regardless of level of experience, gender, gender identity and expression,
+sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
+religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery
+* Personal attacks
+* Trolling or insulting/derogatory comments
+* Public or private harassment
+* Publishing other's private information, such as physical or electronic addresses,
+ without explicit permission
+* Other unethical or unprofessional conduct
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments,
+commits, code, wiki edits, issues, and other contributions that are not aligned to this
+Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
+that they deem inappropriate, threatening, offensive, or harmful.
+
+By adopting this Code of Conduct, project maintainers commit themselves to fairly and
+consistently applying these principles to every aspect of managing this project. Project
+maintainers who do not follow or enforce the Code of Conduct may be permanently removed
+from the project team.
+
+This Code of Conduct applies both within project spaces and in public spaces when an
+individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
+contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
+be reviewed and investigated and will result in a response that is deemed necessary and
+appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
+with regard to the reporter of an incident.
+
+This Code of Conduct is adapted from the
+http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
+http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..7146573a
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,29 @@
+## Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+- The use of sexualized language or imagery
+
+- Personal attacks
+
+- Trolling or insulting/derogatory comments
+
+- Public or private harassment
+
+- Publishing other’s private information, such as physical or electronic addresses, without explicit permission
+
+- Other unethical or unprofessional conduct
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
+
+This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org/), version 1.3.0, available at [contributor-covenant.org/version/1/3/0/](http://contributor-covenant.org/version/1/3/0/)
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..7261357c
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,15 @@
+
+### Describe what this PR does / why we need it
+
+
+### Does this pull request fix one issue?
+
+
+
+### Describe how you did it
+
+
+### Describe how to verify it
+
+
+### Special notes for reviews
diff --git a/README-zh.md b/README-zh.md
index c76e149e..bfe834aa 100644
--- a/README-zh.md
+++ b/README-zh.md
@@ -19,8 +19,10 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
* **服务注册与发现**:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
* **分布式配置管理**:支持分布式系统中的外部化配置,配置更改时自动刷新。
* **消息驱动能力**:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
+* **分布式事务**:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
* **阿里云对象存储**:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
* **分布式任务调度**:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
+* **阿里云短信服务**:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
更多功能请参考 [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。
@@ -33,11 +35,17 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
**[RocketMQ](https://rocketmq.apache.org/)**:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
+**[Dubbo](https://github.com/apache/incubator-dubbo)**:Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。
+
+**[Fescar](https://github.com/alibaba/fescar)**:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
+
**[Alibaba Cloud ACM](https://www.aliyun.com/product/acm)**:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
**[Alibaba Cloud OSS](https://www.aliyun.com/product/oss)**: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
-**[Alibaba Cloud SchedulerX](https://help.aliyun.com/document_detail/43136.html?spm=a2c4g.11186623.6.709.baef7da9QVICiD)**: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
+**[Alibaba Cloud SchedulerX](https://help.aliyun.com/document_detail/43136.html)**: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
+
+**[Alibaba Cloud SMS](https://www.aliyun.com/product/sms)**: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
更多组件请参考 [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。
@@ -100,6 +108,8 @@ Example 列表:
[RocketMQ Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme-zh.md)
+[Fescar Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/fescar-example/readme-zh.md)
+
[Alibaba Cloud OSS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme-zh.md)
[Alibaba Cloud ANS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/ans-example/ans-provider-example/readme-zh.md)
diff --git a/README.md b/README.md
index 8b073b61..ad4672cd 100644
--- a/README.md
+++ b/README.md
@@ -20,21 +20,33 @@ With Spring Cloud Alibaba, you only need to add some annotations and a small amo
* **Service registration and discovery**:Service can be registered and clients can discover the instances using Spring-managed beans, auto integration Ribbon.
* **Distributed configuration**:support for externalized configuration in a distributed system, auto refresh when configuration changes.
* **Event-driven**:support for building highly scalable event-driven microservices connected with shared messaging systems.
+* **Distributed Transaction**:support for distributed transaction solution with high performance and ease of use.
* **Alibaba Cloud Object Storage**:massive, secure, low-cost, and highly reliable cloud storage services. Support for storing and accessing any type of data in any application, anytime, anywhere.
+* **Alibaba Cloud SchedulerX**:accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds.
+* **Alibaba Cloud SMS**: A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers.
+
For more features, please refer to [Roadmap](https://github.com/spring-cloud-incubator/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.
-**[Nacos](https://github.com/alibaba/Nacos)**: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
+**[Nacos](https://github.com/alibaba/Nacos)**: An easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
-**[RocketMQ](https://rocketmq.apache.org/)**:a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
+**[RocketMQ](https://rocketmq.apache.org/)**:A distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
-**[Alibaba Cloud ACM](https://www.aliyun.com/product/acm)**:an application configuration center that enables you to centralize the management of application configurations, and accomplish real-time configuration push in a distributed environment.
+**[Dubbo](https://github.com/apache/incubator-dubbo)**:A high-performance, Java based open source RPC framework.
+
+**[Fescar](https://github.com/alibaba/fescar)**:A distributed transaction solution with high performance and ease of use for microservices architecture.
+
+**[Alibaba Cloud ACM](https://www.aliyun.com/product/acm)**:An application configuration center that enables you to centralize the management of application configurations, and accomplish real-time configuration push in a distributed environment.
**[Alibaba Cloud OSS](https://www.aliyun.com/product/oss)**: An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
+**[Alibaba Cloud SMS](https://www.aliyun.com/product/sms)**: A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers.
+
+**[Alibaba Cloud SchedulerX](https://www.aliyun.com/product/SchedulerX)**:accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds..
+
For more features please refer to [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap.md).
## How to build
@@ -120,6 +132,28 @@ During the incubation period, the version management of the project will follow
* Functional updates will be reflected in the 3rd number of the version, for example, the next version of 0.1.0 will be 0.1.1.
+## Code of Conduct
+This project is a sub-project of Spring Cloud, it adheres to the Contributor Covenant [code of conduct](https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
+
+## Code Conventions and Housekeeping
+None of these is essential for a pull request, but they will all help. They can also be added after the original pull request but before a merge.
+
+Use the Spring Framework code format conventions. If you use Eclipse you can import formatter settings using the eclipse-code-formatter.xml file from the Spring Cloud Build project. If using IntelliJ, you can use the Eclipse Code Formatter Plugin to import the same file.
+
+Make sure all new .java files to have a simple Javadoc class comment with at least an @author tag identifying you, and preferably at least a paragraph on what the class is for.
+
+Add the ASF license header comment to all new .java files (copy from existing files in the project)
+
+Add yourself as an @author to the .java files that you modify substantially (more than cosmetic changes).
+
+Add some Javadocs and, if you change the namespace, some XSD doc elements.
+
+A few unit tests would help a lot as well — someone has to do it.
+
+If no-one else is using your branch, please rebase it against the current master (or other target branch in the main project).
+
+When writing a commit message please follow these conventions, if you are fixing an existing issue please add Fixes gh-XXXX at the end of the commit message (where XXXX is the issue number).
+
## Contact Us
Mailing list is recommended for discussing almost anything related to spring-cloud-alibaba.
diff --git a/Roadmap-zh.md b/Roadmap-zh.md
index 78e09e0e..8bf69a79 100644
--- a/Roadmap-zh.md
+++ b/Roadmap-zh.md
@@ -17,21 +17,10 @@ Spring Cloud Alibaba 致力于提供分布式应用服务开发的一站式解
阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
-**Alibaba Cloud OSS**
-
-阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
-
**RocketMQ**
Apache RocketMQ™ 基于 Java 的高性能、高吞吐量的分布式消息和流计算平台。
-**Alibaba Cloud SchedulerX**
-
-阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
-
-
-## 即将加入的组件
-
**Dubbo**
Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。
@@ -40,6 +29,14 @@ Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。
阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
-**Alibaba Cloud SLS**
+**Alibaba Cloud OSS**
-针对日志类数据的一站式服务,在阿里巴巴集团经历大量大数据场景锤炼而成。您无需开发就能快捷完成日志数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建立 DT 时代海量日志处理能力。
\ No newline at end of file
+阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
+
+**Alibaba Cloud SchedulerX**
+
+阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
+
+**Alibaba Cloud SMS**
+
+覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
diff --git a/Roadmap.md b/Roadmap.md
index 66cb84f6..e7b99498 100644
--- a/Roadmap.md
+++ b/Roadmap.md
@@ -24,16 +24,6 @@ An opensource project of Alibaba, an easy-to-use dynamic service discovery, conf
Apache RocketMQ™ is an open source distributed messaging and streaming data platform.
-**Alibaba Cloud OSS**
-
-Alibaba Cloud Object Storage Service, An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
-
-**Alibaba Cloud Schedulerx**
-
-A distributed task scheduling product developed by Alibaba Middleware team. It supports both periodical tasks and tasks to be triggered at specified time points.
-
-More components will be supported by Spring Cloud Alibaba in the future, which may include but are not limited to the following:
-
**Dubbo**
Apache Dubbo™ (incubating) is a high-performance, Java based open source RPC framework.
@@ -42,6 +32,15 @@ Apache Dubbo™ (incubating) is a high-performance, Java based open source RPC f
A distributed transaction solution with high performance and ease of use for microservices architecture.
-**Alibaba Cloud SLS**
+**Alibaba Cloud OSS**
+
+Alibaba Cloud Object Storage Service, An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
+
+**Alibaba Cloud Schedulerx**
+
+A distributed task scheduling product developed by Alibaba Middleware team. It supports both periodical tasks and tasks to be triggered at specified time points.
+
+**Alibaba Cloud SMS**
+
+A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers.
-Aliyun Log Service is an all-in-one service for log-type data. It helps increase Operations & Management and operational efficiency, as well as build the processing capability to deal with massive logs.
diff --git a/pom.xml b/pom.xml
index 37b31ae4..57652f34 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,6 +91,7 @@
spring-cloud-alibaba-sentinel-datasource
spring-cloud-alibaba-nacos-config
spring-cloud-alibaba-nacos-discovery
+ spring-cloud-alibaba-fescar
spring-cloud-stream-binder-rocketmq
spring-cloud-alibaba-nacos-config-server
spring-cloud-alibaba-dubbo
diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml
index 51b95c49..9c55f47f 100644
--- a/spring-cloud-alibaba-dependencies/pom.xml
+++ b/spring-cloud-alibaba-dependencies/pom.xml
@@ -19,8 +19,9 @@
1.4.1
3.1.0
- 0.6.2
- 0.6.1
+ 0.1.3
+ 0.8.0
+ 0.8.0
1.0.8
1.0.1
4.0.1
@@ -179,6 +180,29 @@
+
+
+
+ com.alibaba.fescar
+ fescar-core
+ ${fescar.version}
+
+
+ com.alibaba.fescar
+ fescar-common
+ ${fescar.version}
+
+
+ com.alibaba.fescar
+ fescar-spring
+ ${fescar.version}
+
+
+ com.alibaba.fescar
+ fescar-rm-datasource
+ ${fescar.version}
+
+
com.aliyun.oss
@@ -261,6 +285,11 @@
spring-cloud-alibaba-nacos-config-server
${project.version}
+
+ org.springframework.cloud
+ spring-cloud-alibaba-fescar
+ ${project.version}
+
org.springframework.cloud
spring-cloud-alicloud-acm
@@ -304,6 +333,12 @@
${project.version}
+
+ org.springframework.cloud
+ spring-cloud-starter-alibaba-fescar
+ ${project.version}
+
+
org.springframework.cloud
spring-cloud-starter-alibaba-nacos-discovery
diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml
new file mode 100644
index 00000000..219187bc
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+ spring-cloud-alibaba-examples
+ org.springframework.cloud
+ 0.2.2.BUILD-SNAPSHOT
+ ../../pom.xml
+
+ 4.0.0
+ account-service
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-alibaba-fescar
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountApplication.java b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountApplication.java
new file mode 100644
index 00000000..893d3464
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author xiaojing
+ */
+@SpringBootApplication
+public class AccountApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AccountApplication.class, args);
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountController.java b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountController.java
new file mode 100644
index 00000000..3738fb87
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import java.util.Random;
+
+import com.alibaba.fescar.core.context.RootContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author xiaojing
+ */
+@RestController
+public class AccountController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AccountController.class);
+
+ private static final String SUCCESS = "SUCCESS";
+ private static final String FAIL = "FAIL";
+
+ private final JdbcTemplate jdbcTemplate;
+ private Random random;
+
+ public AccountController(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ this.random = new Random();
+ }
+
+ @RequestMapping(value = "/account", method = RequestMethod.POST, produces = "application/json")
+ public String account(String userId, int money) {
+ LOGGER.info("Account Service ... xid: " + RootContext.getXID());
+
+ if (random.nextBoolean()) {
+ throw new RuntimeException("this is a mock Exception");
+ }
+
+ int result = jdbcTemplate.update(
+ "update account_tbl set money = money - ? where user_id = ?",
+ new Object[] { money, userId });
+ LOGGER.info("Account Service End ... ");
+ if (result == 1) {
+ return SUCCESS;
+ }
+ return FAIL;
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java
new file mode 100644
index 00000000..62e11cd6
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import java.sql.SQLException;
+import java.util.Random;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.fescar.rm.datasource.DataSourceProxy;
+
+/**
+ * @author xiaojing
+ */
+@Configuration
+public class DatabaseConfiguration {
+
+ private final ApplicationContext applicationContext;
+
+ public DatabaseConfiguration(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Bean(initMethod = "init", destroyMethod = "close")
+ public DruidDataSource storageDataSource() throws SQLException {
+
+ Environment environment = applicationContext.getEnvironment();
+
+ String ip = environment.getProperty("mysql.server.ip");
+ String port = environment.getProperty("mysql.server.port");
+ String dbName = environment.getProperty("mysql.db.name");
+
+ String userName = environment.getProperty("mysql.user.name");
+ String password = environment.getProperty("mysql.user.password");
+
+ DruidDataSource druidDataSource = new DruidDataSource();
+ druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
+ druidDataSource.setUsername(userName);
+ druidDataSource.setPassword(password);
+ druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
+ druidDataSource.setInitialSize(0);
+ druidDataSource.setMaxActive(180);
+ druidDataSource.setMaxWait(60000);
+ druidDataSource.setMinIdle(0);
+ druidDataSource.setValidationQuery("Select 'x' from DUAL");
+ druidDataSource.setTestOnBorrow(false);
+ druidDataSource.setTestOnReturn(false);
+ druidDataSource.setTestWhileIdle(true);
+ druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
+ druidDataSource.setMinEvictableIdleTimeMillis(25200000);
+ druidDataSource.setRemoveAbandoned(true);
+ druidDataSource.setRemoveAbandonedTimeout(1800);
+ druidDataSource.setLogAbandoned(true);
+ druidDataSource.setFilters("mergeStat");
+ return druidDataSource;
+ }
+
+ @Bean
+ public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
+ return new DataSourceProxy(druidDataSource);
+ }
+
+ @Bean
+ public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
+ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
+
+ jdbcTemplate.update("delete from account_tbl where user_id = 'U100001'");
+ jdbcTemplate.update(
+ "insert into account_tbl(user_id, money) values ('U100001', 10000)");
+
+ return jdbcTemplate;
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf
new file mode 100644
index 00000000..b264dd9d
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf
@@ -0,0 +1,30 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #thread factory for netty
+ thread-factory {
+ boss-thread-prefix = "NettyBoss"
+ worker-thread-prefix = "NettyServerNIOWorker"
+ server-executor-thread-prefix = "NettyServerBizHandler"
+ share-boss-worker = false
+ client-selector-thread-prefix = "NettyClientSelector"
+ client-selector-thread-size = 1
+ client-worker-thread-prefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ boss-thread-size = 1
+ #auto default pin or 8
+ worker-thread-size = 8
+ }
+}
+service {
+ #vgroup->rgroup
+ vgroup_mapping.account-service-fescar-service-group = "localRgroup"
+ #only support single node
+ localRgroup.grouplist = "127.0.0.1:8091"
+ #degrade current not support
+ enableDegrade = false
+ #disable
+ disable = false
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.properties b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.properties
new file mode 100644
index 00000000..10f45c59
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.properties
@@ -0,0 +1,9 @@
+spring.application.name=account-service
+server.port=18084
+
+mysql.server.ip=127.0.0.1
+mysql.server.port=3306
+mysql.db.name=demo
+
+mysql.user.name=xxxxx
+mysql.user.password=xxxxx
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml
new file mode 100644
index 00000000..6d77872a
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml
@@ -0,0 +1,62 @@
+
+
+
+ spring-cloud-alibaba-examples
+ org.springframework.cloud
+ 0.2.2.BUILD-SNAPSHOT
+ ../../pom.xml
+
+ 4.0.0
+ business-service
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-alibaba-fescar
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-hystrix
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.cloud
+ spring-cloud-starter-alibaba-sentinel
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/BusinessApplication.java b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/BusinessApplication.java
new file mode 100644
index 00000000..c0ed45fc
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/BusinessApplication.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author xiaojing
+ */
+@SpringBootApplication
+@EnableFeignClients
+public class BusinessApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(BusinessApplication.class, args);
+ }
+
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ @FeignClient(value = "storage", url = "http://127.0.0.1:18082")
+ public interface StorageService {
+
+ @RequestMapping(path = "/storage/{commodityCode}/{count}")
+ String storage(@RequestParam("commodityCode") String commodityCode,
+ @RequestParam("count") int count);
+
+ }
+
+ @FeignClient(value = "order", url = "http://127.0.0.1:18083")
+ public interface OrderService {
+
+ @RequestMapping(path = "/order", method = RequestMethod.POST)
+ String order(@RequestParam("userId") String userId,
+ @RequestParam("commodityCode") String commodityCode,
+ @RequestParam("orderCount") int orderCount);
+
+ }
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/HomeController.java b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/HomeController.java
new file mode 100644
index 00000000..039074ec
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/HomeController.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import com.alibaba.fescar.spring.annotation.GlobalTransactional;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.alibaba.cloud.examples.BusinessApplication.OrderService;
+import org.springframework.cloud.alibaba.cloud.examples.BusinessApplication.StorageService;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author xiaojing
+ */
+@RestController
+public class HomeController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
+
+ private static final String SUCCESS = "SUCCESS";
+ private static final String FAIL = "FAIL";
+ private static final String USER_ID = "U100001";
+ private static final String COMMODITY_CODE = "C00321";
+ private static final int ORDER_COUNT = 2;
+
+ private final RestTemplate restTemplate;
+ private final OrderService orderService;
+ private final StorageService storageService;
+
+ public HomeController(RestTemplate restTemplate, OrderService orderService,
+ StorageService storageService) {
+ this.restTemplate = restTemplate;
+ this.orderService = orderService;
+ this.storageService = storageService;
+ }
+
+ @GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx")
+ @RequestMapping(value = "/fescar/rest", method = RequestMethod.GET, produces = "application/json")
+ public String rest() {
+
+ String result = restTemplate.getForObject(
+ "http://127.0.0.1:18082/storage/" + COMMODITY_CODE + "/" + ORDER_COUNT,
+ String.class);
+
+ if (!SUCCESS.equals(result)) {
+ throw new RuntimeException();
+ }
+
+ String url = "http://127.0.0.1:18083/order";
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+
+ MultiValueMap map = new LinkedMultiValueMap();
+ map.add("userId", USER_ID);
+ map.add("commodityCode", COMMODITY_CODE);
+ map.add("orderCount", ORDER_COUNT + "");
+
+ HttpEntity> request = new HttpEntity>(
+ map, headers);
+
+ ResponseEntity response = restTemplate.postForEntity(url, request,
+ String.class);
+
+ result = response.getBody();
+
+ if (!SUCCESS.equals(result)) {
+ throw new RuntimeException();
+ }
+
+ return SUCCESS;
+ }
+
+ @GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx")
+ @RequestMapping(value = "/fescar/feign", method = RequestMethod.GET, produces = "application/json")
+ public String feign() {
+
+ String result = storageService.storage(COMMODITY_CODE, ORDER_COUNT);
+
+ if (!SUCCESS.equals(result)) {
+ throw new RuntimeException();
+ }
+
+ result = orderService.order(USER_ID, COMMODITY_CODE, ORDER_COUNT);
+
+ if (!SUCCESS.equals(result)) {
+ throw new RuntimeException();
+ }
+
+ return SUCCESS;
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java
new file mode 100644
index 00000000..6594793a
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import java.io.Serializable;
+
+public class Order implements Serializable {
+ public long id;
+ public String userId;
+ public String commodityCode;
+ public int count;
+ public int money;
+
+ @Override
+ public String toString() {
+ return "Order{" + "id=" + id + ", userId='" + userId + '\'' + ", commodityCode='"
+ + commodityCode + '\'' + ", count=" + count + ", money=" + money + '}';
+ }
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf
new file mode 100644
index 00000000..65b69508
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf
@@ -0,0 +1,30 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #thread factory for netty
+ thread-factory {
+ boss-thread-prefix = "NettyBoss"
+ worker-thread-prefix = "NettyServerNIOWorker"
+ server-executor-thread-prefix = "NettyServerBizHandler"
+ share-boss-worker = false
+ client-selector-thread-prefix = "NettyClientSelector"
+ client-selector-thread-size = 1
+ client-worker-thread-prefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ boss-thread-size = 1
+ #auto default pin or 8
+ worker-thread-size = 8
+ }
+}
+service {
+ #vgroup->rgroup
+ vgroup_mapping.business-service-fescar-service-group = "localRgroup"
+ #only support single node
+ localRgroup.grouplist = "127.0.0.1:8091"
+ #degrade current not support
+ enableDegrade = false
+ #disable
+ disable = false
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.properties b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.properties
new file mode 100644
index 00000000..1832ce6f
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.properties
@@ -0,0 +1,8 @@
+server.port=18081
+spring.application.name=business-service
+# The following configuration can be omitted.
+
+#feign.hystrix.enabled=true
+#feign.sentinel.enabled=true
+
+logging.level.com.alibaba.fescar=debug
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml
new file mode 100644
index 00000000..7840c546
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+ spring-cloud-alibaba-examples
+ org.springframework.cloud
+ 0.2.2.BUILD-SNAPSHOT
+ ../../pom.xml
+
+ 4.0.0
+ order-service
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-alibaba-fescar
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java
new file mode 100644
index 00000000..8e5c7d87
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import java.sql.SQLException;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.fescar.rm.datasource.DataSourceProxy;
+
+/**
+ * @author xiaojing
+ */
+@Configuration
+public class DatabaseConfiguration {
+
+ private final ApplicationContext applicationContext;
+
+ public DatabaseConfiguration(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Bean(initMethod = "init", destroyMethod = "close")
+ public DruidDataSource storageDataSource() throws SQLException {
+
+ Environment env = applicationContext.getEnvironment();
+
+ String ip = env.getProperty("mysql.server.ip");
+ String port = env.getProperty("mysql.server.port");
+ String dbName = env.getProperty("mysql.db.name");
+
+ String userName = env.getProperty("mysql.user.name");
+ String password = env.getProperty("mysql.user.password");
+
+ DruidDataSource druidDataSource = new DruidDataSource();
+ druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
+ druidDataSource.setUsername(userName);
+ druidDataSource.setPassword(password);
+ druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
+ druidDataSource.setInitialSize(0);
+ druidDataSource.setMaxActive(180);
+ druidDataSource.setMaxWait(60000);
+ druidDataSource.setMinIdle(0);
+ druidDataSource.setValidationQuery("Select 'x' from DUAL");
+ druidDataSource.setTestOnBorrow(false);
+ druidDataSource.setTestOnReturn(false);
+ druidDataSource.setTestWhileIdle(true);
+ druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
+ druidDataSource.setMinEvictableIdleTimeMillis(25200000);
+ druidDataSource.setRemoveAbandoned(true);
+ druidDataSource.setRemoveAbandonedTimeout(1800);
+ druidDataSource.setLogAbandoned(true);
+ druidDataSource.setFilters("mergeStat");
+ return druidDataSource;
+ }
+
+ @Bean
+ public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
+ return new DataSourceProxy(druidDataSource);
+ }
+
+ @Bean
+ public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
+ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
+
+ jdbcTemplate.execute("TRUNCATE TABLE order_tbl");
+
+ return jdbcTemplate;
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OderApplication.java b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OderApplication.java
new file mode 100644
index 00000000..1ab0dab1
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OderApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author xiaojing
+ */
+@SpringBootApplication
+public class OderApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(OderApplication.class, args);
+ }
+
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java
new file mode 100644
index 00000000..6594793a
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import java.io.Serializable;
+
+public class Order implements Serializable {
+ public long id;
+ public String userId;
+ public String commodityCode;
+ public int count;
+ public int money;
+
+ @Override
+ public String toString() {
+ return "Order{" + "id=" + id + ", userId='" + userId + '\'' + ", commodityCode='"
+ + commodityCode + '\'' + ", count=" + count + ", money=" + money + '}';
+ }
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OrderController.java b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OrderController.java
new file mode 100644
index 00000000..2e0e0f81
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OrderController.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Random;
+
+import com.alibaba.fescar.core.context.RootContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author xiaojing
+ */
+@RestController
+public class OrderController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);
+ private static final String SUCCESS = "SUCCESS";
+ private static final String FAIL = "FAIL";
+ private static final String USER_ID = "U100001";
+ private static final String COMMODITY_CODE = "C00321";
+
+ private final JdbcTemplate jdbcTemplate;
+ private final RestTemplate restTemplate;
+ private Random random;
+
+ public OrderController(JdbcTemplate jdbcTemplate, RestTemplate restTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ this.restTemplate = restTemplate;
+ this.random = new Random();
+ }
+
+ @RequestMapping(value = "/order", method = RequestMethod.POST, produces = "application/json")
+ public String order(String userId, String commodityCode, int orderCount) {
+ LOGGER.info("Order Service Begin ... xid: " + RootContext.getXID());
+
+ int orderMoney = calculate(commodityCode, orderCount);
+
+ invokerAccountService(orderMoney);
+
+ final Order order = new Order();
+ order.userId = userId;
+ order.commodityCode = commodityCode;
+ order.count = orderCount;
+ order.money = orderMoney;
+
+ KeyHolder keyHolder = new GeneratedKeyHolder();
+
+ int result = jdbcTemplate.update(new PreparedStatementCreator() {
+
+ @Override
+ public PreparedStatement createPreparedStatement(Connection con)
+ throws SQLException {
+ PreparedStatement pst = con.prepareStatement(
+ "insert into order_tbl (user_id, commodity_code, count, money) values (?, ?, ?, ?)",
+ PreparedStatement.RETURN_GENERATED_KEYS);
+ pst.setObject(1, order.userId);
+ pst.setObject(2, order.commodityCode);
+ pst.setObject(3, order.count);
+ pst.setObject(4, order.money);
+ return pst;
+ }
+ }, keyHolder);
+
+ order.id = (long) keyHolder.getKey();
+
+ if (random.nextBoolean()) {
+ throw new RuntimeException("this is a mock Exception");
+ }
+
+ LOGGER.info("Order Service End ... Created " + order);
+
+ if (result == 1) {
+ return SUCCESS;
+ }
+ return FAIL;
+ }
+
+ private int calculate(String commodityId, int orderCount) {
+ return 2 * orderCount;
+ }
+
+ private void invokerAccountService(int orderMoney) {
+ String url = "http://127.0.0.1:18084/account";
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+
+ MultiValueMap map = new LinkedMultiValueMap();
+
+ map.add("userId", USER_ID);
+ map.add("money", orderMoney + "");
+
+ HttpEntity> request = new HttpEntity>(
+ map, headers);
+
+ ResponseEntity response = restTemplate.postForEntity(url, request,
+ String.class);
+ }
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf
new file mode 100644
index 00000000..c298ca0c
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf
@@ -0,0 +1,30 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #thread factory for netty
+ thread-factory {
+ boss-thread-prefix = "NettyBoss"
+ worker-thread-prefix = "NettyServerNIOWorker"
+ server-executor-thread-prefix = "NettyServerBizHandler"
+ share-boss-worker = false
+ client-selector-thread-prefix = "NettyClientSelector"
+ client-selector-thread-size = 1
+ client-worker-thread-prefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ boss-thread-size = 1
+ #auto default pin or 8
+ worker-thread-size = 8
+ }
+}
+service {
+ #vgroup->rgroup
+ vgroup_mapping.order-service-fescar-service-group = "localRgroup"
+ #only support single node
+ localRgroup.grouplist = "127.0.0.1:8091"
+ #degrade current not support
+ enableDegrade = false
+ #disable
+ disable = false
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.properties b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.properties
new file mode 100644
index 00000000..f3e417d8
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.properties
@@ -0,0 +1,9 @@
+spring.application.name=order-service
+server.port=18083
+
+mysql.server.ip=127.0.0.1
+mysql.server.port=3306
+mysql.db.name=demo
+
+mysql.user.name=xxxxx
+mysql.user.password=xxxxx
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/fescar-example/readme-zh.md b/spring-cloud-alibaba-examples/fescar-example/readme-zh.md
new file mode 100644
index 00000000..e93f6866
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/readme-zh.md
@@ -0,0 +1,156 @@
+# Fescar Example
+
+## 项目说明
+
+
+本项目演示如何使用 Fescar Starter 完成 Spring Cloud 应用的分布式事务接入。
+
+[Fescar](https://github.com/alibaba/fescar) 是 阿里巴巴 开源的 分布式事务中间件,以 高效 并且对业务 0 侵入 的方式,解决 微服务 场景下面临的分布式事务问题。
+
+
+
+## 准备工作
+
+在运行此示例之前,你需要先完成如下几步准备工作:
+
+1. 配置数据库
+
+1. 创建 UNDO_LOG 表
+
+1. 创建 示例中 业务所需要的数据库表
+
+1. 启动 Fescar Server
+
+
+### 配置数据库
+
+首先,你需要有一个支持 InnoDB 引擎的 MySQL 数据库。
+
+**注意**: 实际上,Fescar 支持不同的应用使用完全不相干的数据库,但是这里为了简单地演示一个原理,所以我们选择了只使用一个数据库。
+
+将 `account-server`、`order-service`、`storage-service` 这三个应用中的 resources 目录下的 `application.properties` 文件中的如下配置修改成你运行环境中的实际配置。
+
+```
+mysql.server.ip=your mysql server ip address
+mysql.server.port=your mysql server listening port
+mysql.db.name=your database name for test
+
+mysql.user.name=your mysql server username
+mysql.user.password=your mysql server password
+
+```
+
+### 创建 UNDO_LOG 表
+
+[Fescar AT 模式]() 需要使用到 UNDO_LOG 表。
+
+``` $sql
+CREATE TABLE `undo_log` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `branch_id` bigint(20) NOT NULL,
+ `xid` varchar(100) NOT NULL,
+ `rollback_info` longblob NOT NULL,
+ `log_status` int(11) NOT NULL,
+ `log_created` datetime NOT NULL,
+ `log_modified` datetime NOT NULL,
+ `ext` varchar(100) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx_unionkey` (`xid`,`branch_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8
+```
+
+### 创建 示例中 业务所需要的数据库表
+
+```$sql
+DROP TABLE IF EXISTS `storage_tbl`;
+CREATE TABLE `storage_tbl` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `commodity_code` varchar(255) DEFAULT NULL,
+ `count` int(11) DEFAULT 0,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY (`commodity_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `order_tbl`;
+CREATE TABLE `order_tbl` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` varchar(255) DEFAULT NULL,
+ `commodity_code` varchar(255) DEFAULT NULL,
+ `count` int(11) DEFAULT 0,
+ `money` int(11) DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `account_tbl`;
+CREATE TABLE `account_tbl` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` varchar(255) DEFAULT NULL,
+ `money` int(11) DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+```
+
+### 启动 Fescar Server
+
+点击这个页面 [https://github.com/alibaba/fescar/releases](https://github.com/alibaba/fescar/releases),下载最新版本的 Fescar Server 端.
+
+
+进入解压之后的 bin 目录,执行如下命令来启动
+
+```$shell
+sh fescar-server.sh $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA
+```
+
+在这个示例中,采用如下命令来启动 Fescar Server
+
+```$shell
+sh fescar-server.sh 8091 ~/fescar/data/
+```
+
+**注意** 如果你修改了端口号,那么记得需要在各个示例工程中的 `application.conf` 文件中,修改 grouplist 的值。
+
+
+## 运行示例
+
+分别运行 `account-server`、`order-service`、`storage-service` 和 `business-service` 这三个应用的 Main 函数,启动示例。
+
+启动示例后,通过 HTTP 的 GET 方法访问如下两个 URL,可以分别验证在 `business-service` 中 通过 RestTemplate 和 FeignClient 调用其他服务的场景。
+
+```$xslt
+http://127.0.0.1:18081/fescar/feign
+
+http://127.0.0.1:18081/fescar/rest
+```
+
+## 如何验证分布式事务成功?
+
+### Xid 信息是否成功传递
+
+在 `account-server`、`order-service` 和 `storage-service` 三个 服务的 Controller 中,第一个执行的逻辑都是输出 RootContext 中的 Xid 信息,如果看到都输出了正确的 Xid 信息,即每次都发生变化,且同一次调用中所有服务的 Xid 都一致。则表明 Fescar 的 Xid 的传递和还原是正常的。
+
+### 数据库中数据是否一致
+
+在本示例中,我们模拟了一个用户购买货物的场景,StorageService 负责扣减库存数量,OrderService 负责保存订单,AccountService 负责扣减用户账户余额。
+
+为了演示样例,我们在 OrderService 和 AccountService 中 使用 Random.nextBoolean() 的方式来随机抛出异常,模拟了在服务调用时随机发生异常的场景。
+
+如果分布式事务生效的话, 那么以下等式应该成立
+
+
+- 用户原始金额(1000) = 用户现存的金额 + 货物单价 (2) * 订单数量 * 每单的货物数量(2)
+
+- 货物的初始数量(100) = 货物的现存数量 + 订单数量 * 每单的货物数量(2)
+
+## 对 Spring Cloud 支持点
+
+- 通过 Spring MVC 提供服务的服务提供者,在收到 header 中含有 Fescar 信息的 HTTP 请求时,可以自动还原 Fescar 上下文。
+
+- 支持服务调用者通过 RestTemplate 调用时,自动传递 Fescar 上下文。
+
+- 支持服务调用者通过 FeignClient 调用时,自动传递 Fescar 上下文。
+
+- 支持 FeignClient 和 Hystrix 同时使用的场景。
+
+- 支持 FeignClient 和 Sentinel 同时使用的场景。
diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml
new file mode 100644
index 00000000..bcdca22c
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml
@@ -0,0 +1,68 @@
+
+
+
+ spring-cloud-alibaba-examples
+ org.springframework.cloud
+ 0.2.2.BUILD-SNAPSHOT
+ ../../pom.xml
+
+ 4.0.0
+ storage-service
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-alibaba-fescar
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ com.alibaba
+ druid
+ 1.1.10
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java
new file mode 100644
index 00000000..2973f21a
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import java.sql.SQLException;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.fescar.rm.datasource.DataSourceProxy;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ * @author xiaojing
+ */
+@Configuration
+public class DatabaseConfiguration {
+
+ private final ApplicationContext applicationContext;
+
+ public DatabaseConfiguration(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Bean(initMethod = "init", destroyMethod = "close")
+ public DruidDataSource storageDataSource() throws SQLException {
+
+ Environment environment = applicationContext.getEnvironment();
+
+ String ip = environment.getProperty("mysql.server.ip");
+ String port = environment.getProperty("mysql.server.port");
+ String dbName = environment.getProperty("mysql.db.name");
+
+ String userName = environment.getProperty("mysql.user.name");
+ String password = environment.getProperty("mysql.user.password");
+
+ DruidDataSource druidDataSource = new DruidDataSource();
+ druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
+ druidDataSource.setUsername(userName);
+ druidDataSource.setPassword(password);
+ druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
+ druidDataSource.setInitialSize(0);
+ druidDataSource.setMaxActive(180);
+ druidDataSource.setMaxWait(60000);
+ druidDataSource.setMinIdle(0);
+ druidDataSource.setValidationQuery("Select 'x' from DUAL");
+ druidDataSource.setTestOnBorrow(false);
+ druidDataSource.setTestOnReturn(false);
+ druidDataSource.setTestWhileIdle(true);
+ druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
+ druidDataSource.setMinEvictableIdleTimeMillis(25200000);
+ druidDataSource.setRemoveAbandoned(true);
+ druidDataSource.setRemoveAbandonedTimeout(1800);
+ druidDataSource.setLogAbandoned(true);
+ druidDataSource.setFilters("mergeStat");
+ return druidDataSource;
+ }
+
+ @Bean
+ public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
+ return new DataSourceProxy(druidDataSource);
+ }
+
+ @Bean
+ public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
+
+ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
+
+ jdbcTemplate.update("delete from storage_tbl where commodity_code = 'C00321'");
+ jdbcTemplate.update(
+ "insert into storage_tbl(commodity_code, count) values ('C00321', 100)");
+
+ return jdbcTemplate;
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageApplication.java b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageApplication.java
new file mode 100644
index 00000000..94bbac7f
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author xiaojing
+ */
+@SpringBootApplication
+public class StorageApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(StorageApplication.class, args);
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageController.java b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageController.java
new file mode 100644
index 00000000..fd97ab2c
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageController.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.cloud.examples;
+
+import com.alibaba.fescar.core.context.RootContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author xiaojing
+ */
+@RestController
+public class StorageController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StorageController.class);
+
+ private static final String SUCCESS = "SUCCESS";
+ private static final String FAIL = "FAIL";
+
+ private final JdbcTemplate jdbcTemplate;
+
+ public StorageController(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
+ @RequestMapping(value = "/storage/{commodityCode}/{count}", method = RequestMethod.GET, produces = "application/json")
+ public String echo(@PathVariable String commodityCode, @PathVariable int count) {
+ LOGGER.info("Storage Service Begin ... xid: " + RootContext.getXID());
+ int result = jdbcTemplate.update(
+ "update storage_tbl set count = count - ? where commodity_code = ?",
+ new Object[] { count, commodityCode });
+ LOGGER.info("Storage Service End ... ");
+ if (result == 1) {
+ return SUCCESS;
+ }
+ return FAIL;
+ }
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf
new file mode 100644
index 00000000..4bc5989e
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf
@@ -0,0 +1,30 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #thread factory for netty
+ thread-factory {
+ boss-thread-prefix = "NettyBoss"
+ worker-thread-prefix = "NettyServerNIOWorker"
+ server-executor-thread-prefix = "NettyServerBizHandler"
+ share-boss-worker = false
+ client-selector-thread-prefix = "NettyClientSelector"
+ client-selector-thread-size = 1
+ client-worker-thread-prefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ boss-thread-size = 1
+ #auto default pin or 8
+ worker-thread-size = 8
+ }
+}
+service {
+ #vgroup->rgroup
+ vgroup_mapping.storage-service-fescar-service-group = "localRgroup"
+ #only support single node
+ localRgroup.grouplist = "127.0.0.1:8091"
+ #degrade current not support
+ enableDegrade = false
+ #disable
+ disable = false
+}
diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.properties b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.properties
new file mode 100644
index 00000000..832eaecd
--- /dev/null
+++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.properties
@@ -0,0 +1,9 @@
+spring.application.name=storage-service
+server.port=18082
+
+mysql.server.ip=127.0.0.1
+mysql.server.port=3306
+mysql.db.name=demo
+
+mysql.user.name=xxxxx
+mysql.user.password=xxxxx
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md
index e7339058..390b567e 100644
--- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md
+++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md
@@ -113,11 +113,11 @@ Nacos Client 从 Nacos Server 端获取数据时,调用的是此接口 `Config
在 Nacos Config Starter 中,dataId 的拼接格式如下
- ${prefix} - ${spring.active.profile} . ${file-extension}
+ ${prefix} - ${spring.profiles.active} . ${file-extension}
* `prefix` 默认为 `spring.application.name` 的值,也可以通过配置项 `spring.cloud.nacos.config.prefix`来配置。
-* `spring.active.profile` 即为当前环境对应的 profile,详情可以参考 [Spring Boot文档](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
+* `spring.profiles.active` 即为当前环境对应的 profile,详情可以参考 [Spring Boot文档](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
**注意,当 activeprofile 为空时,对应的连接符 `-` 也将不存在,dataId 的拼接格式变成 `${prefix}`.`${file-extension}`**
diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md
index 58b499ae..702a5107 100644
--- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md
+++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md
@@ -114,11 +114,11 @@ Nacos Client gets data from Nacos Server through this method. `ConfigService.get
In Nacos Config Starter, the splicing format of dataId is as follows
- ${prefix} - ${spring.active.profile} . ${file-extension}
+ ${prefix} - ${spring.profiles.active} . ${file-extension}
* `prefix` default value is `spring.application.name` value, which can also be configured via the configuration item `spring.cloud.nacos.config.prefix`.
-* `spring.active.profile` is the profile corresponding to the current environment. For details, please refer to [Spring Boot Doc](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
+* `spring.profiles.active` is the profile corresponding to the current environment. For details, please refer to [Spring Boot Doc](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
**Note: when the activeprofile is empty, the corresponding connector `-` will also not exist, and the splicing format of the dataId becomes `${prefix}`.`${file-extension}`**
diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties
index fbc9736e..02b7ce32 100644
--- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties
+++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties
@@ -9,4 +9,5 @@ spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.eager=true
spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
-spring.cloud.sentinel.datasource.ds1.file.data-type=json
\ No newline at end of file
+spring.cloud.sentinel.datasource.ds1.file.data-type=json
+spring.cloud.sentinel.datasource.ds1.file.rule-type=flow
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ProviderApplication.java b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ProviderApplication.java
index 843f5cbe..c8b4a1c2 100644
--- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ProviderApplication.java
+++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ProviderApplication.java
@@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.RestController;
* @author xiaojing
*/
@SpringBootApplication
-@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml
index 6e2790f9..6cd8e731 100644
--- a/spring-cloud-alibaba-examples/pom.xml
+++ b/spring-cloud-alibaba-examples/pom.xml
@@ -29,6 +29,10 @@
ans-example/ans-consumer-feign-example
ans-example/ans-consumer-ribbon-example
ans-example/ans-provider-example
+ fescar-example/business-service
+ fescar-example/order-service
+ fescar-example/storage-service
+ fescar-example/account-service
acm-example/acm-local-example
rocketmq-example
spring-cloud-bus-rocketmq-example
diff --git a/spring-cloud-alibaba-fescar/pom.xml b/spring-cloud-alibaba-fescar/pom.xml
new file mode 100644
index 00000000..15613f57
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/pom.xml
@@ -0,0 +1,117 @@
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-alibaba
+ 0.2.2.BUILD-SNAPSHOT
+
+ 4.0.0
+
+ org.springframework.cloud
+ spring-cloud-alibaba-fescar
+ Spring Cloud Alibaba Fescar
+
+
+
+
+ com.alibaba.fescar
+ fescar-core
+
+
+ com.alibaba.fescar
+ fescar-common
+
+
+ com.alibaba.fescar
+ fescar-spring
+
+
+ com.alibaba.fescar
+ fescar-rm-datasource
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+ provided
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-alibaba-sentinel
+ provided
+ true
+
+
+
+ org.springframework.cloud
+ spring-cloud-commons
+ true
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-ribbon
+ provided
+ true
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ provided
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ provided
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ provided
+ true
+
+
+
+ org.springframework.boot
+ spring-boot
+ provided
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ provided
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ provided
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/FescarProperties.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/FescarProperties.java
new file mode 100644
index 00000000..f7847d96
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/FescarProperties.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author xiaojing
+ */
+@ConfigurationProperties("spring.cloud.alibaba.fescar")
+public class FescarProperties {
+
+ // todo support config Fescar server information
+
+ /**
+ * Fescar tx service group.default is ${spring.application.name}-fescar-service-group.
+ */
+ private String txServiceGroup;
+
+ public String getTxServiceGroup() {
+ return txServiceGroup;
+ }
+
+ public void setTxServiceGroup(String txServiceGroup) {
+ this.txServiceGroup = txServiceGroup;
+ }
+
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/GlobalTransactionAutoConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/GlobalTransactionAutoConfiguration.java
new file mode 100644
index 00000000..d876ad3b
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/GlobalTransactionAutoConfiguration.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar;
+
+import com.alibaba.fescar.spring.annotation.GlobalTransactionScanner;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author xiaojing
+ */
+
+@Configuration
+@EnableConfigurationProperties(FescarProperties.class)
+public class GlobalTransactionAutoConfiguration {
+
+ private final ApplicationContext applicationContext;
+
+ private final FescarProperties fescarProperties;
+
+ public GlobalTransactionAutoConfiguration(ApplicationContext applicationContext,
+ FescarProperties fescarProperties) {
+ this.applicationContext = applicationContext;
+ this.fescarProperties = fescarProperties;
+ }
+
+ @Bean
+ public GlobalTransactionScanner globalTransactionScanner() {
+
+ String applicationName = applicationContext.getEnvironment()
+ .getProperty("spring.application.name");
+
+ String txServiceGroup = fescarProperties.getTxServiceGroup();
+
+ if (StringUtils.isEmpty(txServiceGroup)) {
+ txServiceGroup = applicationName + "-fescar-service-group";
+ fescarProperties.setTxServiceGroup(txServiceGroup);
+ }
+
+ return new GlobalTransactionScanner(applicationName, txServiceGroup);
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarBeanPostProcessor.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarBeanPostProcessor.java
new file mode 100644
index 00000000..02bd6c32
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarBeanPostProcessor.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+
+/**
+ * @author xiaojing
+ */
+final class FescarBeanPostProcessor implements BeanPostProcessor {
+
+ private final FescarFeignObjectWrapper fescarFeignObjectWrapper;
+
+ FescarBeanPostProcessor(FescarFeignObjectWrapper fescarFeignObjectWrapper) {
+ this.fescarFeignObjectWrapper = fescarFeignObjectWrapper;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ return this.fescarFeignObjectWrapper.wrap(bean);
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarContextBeanPostProcessor.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarContextBeanPostProcessor.java
new file mode 100644
index 00000000..ffd8e88e
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarContextBeanPostProcessor.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.cloud.openfeign.FeignContext;
+
+/**
+ * @author xiaojing
+ */
+public class FescarContextBeanPostProcessor implements BeanPostProcessor {
+
+ private final BeanFactory beanFactory;
+ private FescarFeignObjectWrapper fescarFeignObjectWrapper;
+
+ FescarContextBeanPostProcessor(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof FeignContext && !(bean instanceof FescarFeignContext)) {
+ return new FescarFeignContext(getFescarFeignObjectWrapper(),
+ (FeignContext) bean);
+ }
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ private FescarFeignObjectWrapper getFescarFeignObjectWrapper() {
+ if (this.fescarFeignObjectWrapper == null) {
+ this.fescarFeignObjectWrapper = this.beanFactory
+ .getBean(FescarFeignObjectWrapper.class);
+ }
+ return this.fescarFeignObjectWrapper;
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignBuilder.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignBuilder.java
new file mode 100644
index 00000000..acab33ff
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import org.springframework.beans.factory.BeanFactory;
+
+import feign.Feign;
+
+/**
+ * @author xiaojing
+ */
+final class FescarFeignBuilder {
+
+ private FescarFeignBuilder() {
+ }
+
+ static Feign.Builder builder(BeanFactory beanFactory) {
+ return Feign.builder().client(new FescarFeignClient(beanFactory));
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClient.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClient.java
new file mode 100644
index 00000000..9fc9d527
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClient.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.fescar.core.context.RootContext;
+
+import org.springframework.beans.factory.BeanFactory;
+
+import feign.Client;
+import feign.Request;
+import feign.Response;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author xiaojing
+ */
+public class FescarFeignClient implements Client {
+
+ private final Client delegate;
+ private final BeanFactory beanFactory;
+
+ FescarFeignClient(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ this.delegate = new Client.Default(null, null);
+ }
+
+ FescarFeignClient(BeanFactory beanFactory, Client delegate) {
+ this.delegate = delegate;
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public Response execute(Request request, Request.Options options) throws IOException {
+
+ Request modifiedRequest = getModifyRequest(request);
+
+ try {
+ return this.delegate.execute(modifiedRequest, options);
+ }
+ finally {
+
+ }
+ }
+
+ private Request getModifyRequest(Request request) {
+
+ String xid = RootContext.getXID();
+
+ if (StringUtils.isEmpty(xid)) {
+ return request;
+ }
+
+ Map> headers = new HashMap<>();
+ headers.putAll(request.headers());
+
+ List fescarXid = new ArrayList<>();
+ fescarXid.add(xid);
+ headers.put(RootContext.KEY_XID, fescarXid);
+
+ return Request.create(request.method(), request.url(), headers, request.body(),
+ request.charset());
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClientAutoConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClientAutoConfiguration.java
new file mode 100644
index 00000000..1cb2c0cb
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClientAutoConfiguration.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.openfeign.FeignAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Scope;
+
+import feign.Client;
+import feign.Feign;
+
+/**
+ * @author xiaojing
+ */
+
+@Configuration
+@ConditionalOnClass(Client.class)
+@AutoConfigureBefore(FeignAutoConfiguration.class)
+public class FescarFeignClientAutoConfiguration {
+
+ @Bean
+ @Scope("prototype")
+ @ConditionalOnClass(name = "com.netflix.hystrix.HystrixCommand")
+ @ConditionalOnProperty(name = "feign.hystrix.enabled", havingValue = "true")
+ Feign.Builder feignHystrixBuilder(BeanFactory beanFactory) {
+ return FescarHystrixFeignBuilder.builder(beanFactory);
+ }
+
+ @Bean
+ @Scope("prototype")
+ @ConditionalOnClass(name = "com.alibaba.csp.sentinel.SphU")
+ @ConditionalOnProperty(name = "feign.sentinel.enabled", havingValue = "true")
+ Feign.Builder feignSentinelBuilder(BeanFactory beanFactory) {
+ return FescarSentinelFeignBuilder.builder(beanFactory);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @Scope("prototype")
+ Feign.Builder feignBuilder(BeanFactory beanFactory) {
+ return FescarFeignBuilder.builder(beanFactory);
+ }
+
+ @Configuration
+ protected static class FeignBeanPostProcessorConfiguration {
+
+ @Bean
+ FescarBeanPostProcessor fescarBeanPostProcessor(
+ FescarFeignObjectWrapper fescarFeignObjectWrapper) {
+ return new FescarBeanPostProcessor(fescarFeignObjectWrapper);
+ }
+
+ @Bean
+ FescarContextBeanPostProcessor fescarContextBeanPostProcessor(
+ BeanFactory beanFactory) {
+ return new FescarContextBeanPostProcessor(beanFactory);
+ }
+
+ @Bean
+ FescarFeignObjectWrapper fescarFeignObjectWrapper(BeanFactory beanFactory) {
+ return new FescarFeignObjectWrapper(beanFactory);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignContext.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignContext.java
new file mode 100644
index 00000000..bd8d2251
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignContext.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import feign.Client;
+import org.springframework.cloud.openfeign.FeignContext;
+
+/**
+ *
+ * @author xiaojing
+ */
+public class FescarFeignContext extends FeignContext {
+
+ private final FescarFeignObjectWrapper fescarFeignObjectWrapper;
+ private final FeignContext delegate;
+
+ FescarFeignContext(FescarFeignObjectWrapper fescarFeignObjectWrapper,
+ FeignContext delegate) {
+ this.fescarFeignObjectWrapper = fescarFeignObjectWrapper;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public T getInstance(String name, Class type) {
+ T object = this.delegate.getInstance(name, type);
+ if (object instanceof Client) {
+ return object;
+ }
+ return (T) this.fescarFeignObjectWrapper.wrap(object);
+ }
+
+ @Override
+ public Map getInstances(String name, Class type) {
+ Map instances = this.delegate.getInstances(name, type);
+ if (instances == null) {
+ return null;
+ }
+ Map convertedInstances = new HashMap<>();
+ for (Map.Entry entry : instances.entrySet()) {
+ if (entry.getValue() instanceof Client) {
+ convertedInstances.put(entry.getKey(), entry.getValue());
+ }
+ else {
+ convertedInstances.put(entry.getKey(),
+ (T) this.fescarFeignObjectWrapper.wrap(entry.getValue()));
+ }
+ }
+ return convertedInstances;
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignObjectWrapper.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignObjectWrapper.java
new file mode 100644
index 00000000..9b42b2f9
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignObjectWrapper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
+
+import feign.Client;
+import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
+import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
+
+/**
+ * @author xiaojing
+ */
+public class FescarFeignObjectWrapper {
+
+ private final BeanFactory beanFactory;
+
+ private CachingSpringLoadBalancerFactory cachingSpringLoadBalancerFactory;
+ private SpringClientFactory springClientFactory;
+
+ FescarFeignObjectWrapper(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ Object wrap(Object bean) {
+ if (bean instanceof Client && !(bean instanceof FescarFeignClient)) {
+ if (bean instanceof LoadBalancerFeignClient) {
+ LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
+ return new FescarLoadBalancerFeignClient(client.getDelegate(), factory(),
+ clientFactory(), this.beanFactory);
+ }
+ return new FescarFeignClient(this.beanFactory, (Client) bean);
+ }
+ return bean;
+ }
+
+ CachingSpringLoadBalancerFactory factory() {
+ if (this.cachingSpringLoadBalancerFactory == null) {
+ this.cachingSpringLoadBalancerFactory = this.beanFactory
+ .getBean(CachingSpringLoadBalancerFactory.class);
+ }
+ return this.cachingSpringLoadBalancerFactory;
+ }
+
+ SpringClientFactory clientFactory() {
+ if (this.springClientFactory == null) {
+ this.springClientFactory = this.beanFactory
+ .getBean(SpringClientFactory.class);
+ }
+ return this.springClientFactory;
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarHystrixFeignBuilder.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarHystrixFeignBuilder.java
new file mode 100644
index 00000000..b67a9d84
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarHystrixFeignBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import feign.Retryer;
+import feign.hystrix.HystrixFeign;
+import org.springframework.beans.factory.BeanFactory;
+
+import feign.Feign;
+
+/**
+ * @author xiaojing
+ */
+final class FescarHystrixFeignBuilder {
+
+ private FescarHystrixFeignBuilder() {
+ }
+
+ static Feign.Builder builder(BeanFactory beanFactory) {
+ return HystrixFeign.builder().retryer(Retryer.NEVER_RETRY)
+ .client(new FescarFeignClient(beanFactory));
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarLoadBalancerFeignClient.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarLoadBalancerFeignClient.java
new file mode 100644
index 00000000..7f00d058
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarLoadBalancerFeignClient.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import java.io.IOException;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
+
+import feign.Client;
+import feign.Request;
+import feign.Response;
+import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
+import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
+
+/**
+ * @author xiaojing
+ */
+public class FescarLoadBalancerFeignClient extends LoadBalancerFeignClient {
+
+ private final BeanFactory beanFactory;
+
+ FescarLoadBalancerFeignClient(Client delegate,
+ CachingSpringLoadBalancerFactory lbClientFactory,
+ SpringClientFactory clientFactory, BeanFactory beanFactory) {
+ super(wrap(delegate, beanFactory), lbClientFactory, clientFactory);
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public Response execute(Request request, Request.Options options) throws IOException {
+ return super.execute(request, options);
+ }
+
+ private static Client wrap(Client delegate, BeanFactory beanFactory) {
+ return (Client) new FescarFeignObjectWrapper(beanFactory).wrap(delegate);
+ }
+
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarSentinelFeignBuilder.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarSentinelFeignBuilder.java
new file mode 100644
index 00000000..42f63214
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarSentinelFeignBuilder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign;
+
+import org.springframework.beans.factory.BeanFactory;
+
+import feign.Feign;
+import feign.Retryer;
+import feign.hystrix.HystrixFeign;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
+import org.springframework.cloud.alibaba.sentinel.feign.SentinelFeign;
+
+/**
+ * @author xiaojing
+ */
+final class FescarSentinelFeignBuilder {
+
+ private FescarSentinelFeignBuilder() {
+ }
+
+ static Feign.Builder builder(BeanFactory beanFactory) {
+ return SentinelFeign.builder().retryer(Retryer.NEVER_RETRY)
+ .client(new FescarFeignClient(beanFactory));
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixAutoConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixAutoConfiguration.java
new file mode 100644
index 00000000..f7db4b0d
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixAutoConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign.hystrix;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.netflix.hystrix.HystrixCommand;
+
+/**
+ * @author xiaojing
+ */
+
+@Configuration
+@ConditionalOnClass(HystrixCommand.class)
+public class FescarHystrixAutoConfiguration {
+
+ @Bean
+ FescarHystrixConcurrencyStrategy fescarHystrixConcurrencyStrategy() {
+ return new FescarHystrixConcurrencyStrategy();
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixConcurrencyStrategy.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixConcurrencyStrategy.java
new file mode 100644
index 00000000..e21ff168
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixConcurrencyStrategy.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.feign.hystrix;
+
+import java.util.concurrent.Callable;
+
+import com.alibaba.fescar.core.context.RootContext;
+
+import com.netflix.hystrix.strategy.HystrixPlugins;
+import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
+
+/**
+ * @author xiaojing
+ */
+public class FescarHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
+
+ private HystrixConcurrencyStrategy delegate;
+
+ public FescarHystrixConcurrencyStrategy() {
+ this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
+ HystrixPlugins.reset();
+ HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
+ }
+
+ @Override
+ public Callable wrapCallable(Callable c) {
+ if (c instanceof FescarContextCallable) {
+ return c;
+ }
+
+ Callable wrappedCallable;
+ if (this.delegate != null) {
+ wrappedCallable = this.delegate.wrapCallable(c);
+ }
+ else {
+ wrappedCallable = c;
+ }
+ if (wrappedCallable instanceof FescarContextCallable) {
+ return wrappedCallable;
+ }
+
+ return new FescarContextCallable<>(wrappedCallable);
+ }
+
+ private static class FescarContextCallable implements Callable {
+
+ private final Callable actual;
+ private final String xid;
+
+ FescarContextCallable(Callable actual) {
+ this.actual = actual;
+ this.xid = RootContext.getXID();
+ }
+
+ @Override
+ public K call() throws Exception {
+ try {
+ RootContext.bind(xid);
+ return actual.call();
+ }
+ finally {
+ RootContext.unbind();
+ }
+ }
+
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateAutoConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateAutoConfiguration.java
new file mode 100644
index 00000000..91191ed0
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateAutoConfiguration.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.
+ */
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.rest;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author xiaojing
+ */
+
+@Configuration
+public class FescarRestTemplateAutoConfiguration {
+
+ @Bean
+ public FescarRestTemplateInterceptor fescarRestTemplateInterceptor() {
+ return new FescarRestTemplateInterceptor();
+ }
+
+ @Autowired(required = false)
+ private Collection restTemplates;
+
+ @Autowired
+ private FescarRestTemplateInterceptor fescarRestTemplateInterceptor;
+
+ @PostConstruct
+ public void init() {
+ if (this.restTemplates != null) {
+ for (RestTemplate restTemplate : restTemplates) {
+ List interceptors = new ArrayList(
+ restTemplate.getInterceptors());
+ interceptors.add(this.fescarRestTemplateInterceptor);
+ restTemplate.setInterceptors(interceptors);
+ }
+ }
+ }
+
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateInterceptor.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateInterceptor.java
new file mode 100644
index 00000000..5a5b2a10
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateInterceptor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.rest;
+
+import java.io.IOException;
+
+import com.alibaba.fescar.core.context.RootContext;
+
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.http.client.support.HttpRequestWrapper;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author xiaojing
+ */
+public class FescarRestTemplateInterceptor implements ClientHttpRequestInterceptor {
+ @Override
+ public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
+ ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
+ HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
+
+ String xid = RootContext.getXID();
+
+ if (!StringUtils.isEmpty(xid)) {
+ requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
+ }
+ return clientHttpRequestExecution.execute(requestWrapper, bytes);
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptor.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptor.java
new file mode 100644
index 00000000..84ffee8e
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptor.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.web;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.fescar.core.context.RootContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+/**
+ * @author xiaojing
+ *
+ * Fescar HandlerInterceptor, Convert Fescar information into
+ * @see com.alibaba.fescar.core.context.RootContext from http request's header in
+ * {@link org.springframework.web.servlet.HandlerInterceptor#preHandle(HttpServletRequest , HttpServletResponse , Object )},
+ * And clean up Fescar information after servlet method invocation in
+ * {@link org.springframework.web.servlet.HandlerInterceptor#afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)}
+ */
+public class FescarHandlerInterceptor implements HandlerInterceptor {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(FescarHandlerInterceptor.class);
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
+ Object handler) throws Exception {
+
+ String xid = RootContext.getXID();
+ String rpcXid = request.getHeader(RootContext.KEY_XID);
+ if (log.isDebugEnabled()) {
+ log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
+ }
+
+ if (xid == null && rpcXid != null) {
+ RootContext.bind(rpcXid);
+ if (log.isDebugEnabled()) {
+ log.debug("bind {} to RootContext", rpcXid);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
+ Object handler, Exception e) throws Exception {
+
+ String rpcXid = request.getHeader(RootContext.KEY_XID);
+
+ if (StringUtils.isEmpty(rpcXid)) {
+ return;
+ }
+
+ String unbindXid = RootContext.unbind();
+ if (log.isDebugEnabled()) {
+ log.debug("unbind {} from RootContext", unbindXid);
+ }
+ if (!rpcXid.equalsIgnoreCase(unbindXid)) {
+ log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
+ if (unbindXid != null) {
+ RootContext.bind(unbindXid);
+ log.warn("bind {} back to RootContext", unbindXid);
+ }
+ }
+ }
+
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptorConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptorConfiguration.java
new file mode 100644
index 00000000..c510b1ac
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptorConfiguration.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.fescar.web;
+
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @author xiaojing
+ */
+public class FescarHandlerInterceptorConfiguration implements WebMvcConfigurer {
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(new FescarHandlerInterceptor()).addPathPatterns("/**");
+ }
+}
diff --git a/spring-cloud-alibaba-fescar/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-fescar/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..3560bcee
--- /dev/null
+++ b/spring-cloud-alibaba-fescar/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,7 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.springframework.cloud.alibaba.fescar.rest.FescarRestTemplateAutoConfiguration,\
+org.springframework.cloud.alibaba.fescar.web.FescarHandlerInterceptorConfiguration,\
+org.springframework.cloud.alibaba.fescar.GlobalTransactionAutoConfiguration,\
+org.springframework.cloud.alibaba.fescar.feign.FescarFeignClientAutoConfiguration,\
+org.springframework.cloud.alibaba.fescar.feign.hystrix.FescarHystrixAutoConfiguration
+
diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryProperties.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryProperties.java
index fa6e126c..775a502b 100644
--- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryProperties.java
+++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryProperties.java
@@ -19,7 +19,6 @@ package org.springframework.cloud.alibaba.nacos;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -28,375 +27,366 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
-
import javax.annotation.PostConstruct;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Properties;
+import java.util.*;
-import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY;
-import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME;
-import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
-import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
-import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_LOAD_CACHE_AT_START;
-import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY;
-import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
+import static com.alibaba.nacos.api.PropertyKeyConst.*;
/**
* @author dungu.zpf
* @author xiaojing
* @author Mercy
*/
+
@ConfigurationProperties("spring.cloud.nacos.discovery")
public class NacosDiscoveryProperties {
- private static final Logger LOGGER = LoggerFactory
- .getLogger(NacosDiscoveryProperties.class);
-
- /**
- * nacos discovery server address
- */
- private String serverAddr;
-
- /**
- * the domain name of a service, through which the server address can be dynamically
- * obtained.
- */
- private String endpoint;
-
- /**
- * namespace, separation registry of different environments.
- */
- private String namespace;
-
- /**
- * nacos naming log file name
- */
- private String logName;
-
- /**
- * service name to registry
- */
- @Value("${spring.cloud.nacos.discovery.service:${spring.application.name:}}")
- private String service;
-
- /**
- * weight for service instance, the larger the value, the larger the weight.
- */
- private float weight = 1;
-
- /**
- * cluster name for nacos server.
- */
- private String clusterName = "DEFAULT";
-
- /**
- * naming load from local cache at application start. true is load
- */
- private String namingLoadCacheAtStart = "false";
-
- /**
- * extra metadata to register.
- */
- private Map metadata = new HashMap<>();
-
- /**
- * if you just want to subscribe, but don't want to register your service, set it to
- * false.
- */
- private boolean registerEnabled = true;
-
- /**
- * The ip address your want to register for your service instance, needn't to set it
- * if the auto detect ip works well.
- */
- private String ip;
-
- /**
- * which network interface's ip you want to register
- */
- private String networkInterface = "";
-
- /**
- * The port your want to register for your service instance, needn't to set it if the
- * auto detect port works well
- */
- private int port = -1;
-
- /**
- * whether your service is a https service
- */
- private boolean secure = false;
-
- /**
- * access key for namespace.
- */
- private String accessKey;
-
- /**
- * secret key for namespace.
- */
- private String secretKey;
-
- @Autowired
- private InetUtils inetUtils;
-
- @Autowired
- private Environment environment;
-
- private NamingService namingService;
-
- @PostConstruct
- public void init() throws SocketException {
-
- if (secure) {
- metadata.put("secure", "true");
- }
-
- serverAddr = Objects.toString(serverAddr, "");
- endpoint = Objects.toString(endpoint, "");
- namespace = Objects.toString(namespace, "");
- logName = Objects.toString(logName, "");
-
- if (StringUtils.isEmpty(ip)) {
- // traversing network interfaces if didn't specify a interface
- if (StringUtils.isEmpty(networkInterface)) {
- ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
- } else {
- NetworkInterface netInterface = NetworkInterface
- .getByName(networkInterface);
- if (null == networkInterface) {
- throw new IllegalArgumentException(
- "no such interface " + networkInterface);
- }
-
- Enumeration inetAddress = netInterface.getInetAddresses();
- while (inetAddress.hasMoreElements()) {
- InetAddress currentAddress = inetAddress.nextElement();
- if (currentAddress instanceof Inet4Address
- && !currentAddress.isLoopbackAddress()) {
- ip = currentAddress.getHostAddress();
- break;
- }
- }
-
- if (StringUtils.isEmpty(ip)) {
- throw new RuntimeException("cannot find available ip from"
- + " network interface " + networkInterface);
- }
-
- }
- }
-
- this.overrideFromEnv(environment);
- }
-
- public String getEndpoint() {
- return endpoint;
- }
-
- public void setEndpoint(String endpoint) {
- this.endpoint = endpoint;
- }
-
- public String getNamespace() {
- return namespace;
- }
-
- public void setNamespace(String namespace) {
- this.namespace = namespace;
- }
-
- public String getLogName() {
- return logName;
- }
-
- public void setLogName(String logName) {
- this.logName = logName;
- }
-
- public void setInetUtils(InetUtils inetUtils) {
- this.inetUtils = inetUtils;
- }
-
- public float getWeight() {
- return weight;
- }
-
- public void setWeight(float weight) {
- this.weight = weight;
- }
-
- public String getClusterName() {
- return clusterName;
- }
-
- public void setClusterName(String clusterName) {
- this.clusterName = clusterName;
- }
-
- public String getService() {
- return service;
- }
-
- public void setService(String service) {
- this.service = service;
- }
-
- public boolean isRegisterEnabled() {
- return registerEnabled;
- }
-
- public void setRegisterEnabled(boolean registerEnabled) {
- this.registerEnabled = registerEnabled;
- }
-
- public String getIp() {
- return ip;
- }
-
- public void setIp(String ip) {
- this.ip = ip;
- }
-
- public String getNetworkInterface() {
- return networkInterface;
- }
-
- public void setNetworkInterface(String networkInterface) {
- this.networkInterface = networkInterface;
- }
-
- public int getPort() {
- return port;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public boolean isSecure() {
- return secure;
- }
-
- public void setSecure(boolean secure) {
- this.secure = secure;
- }
-
- public Map getMetadata() {
- return metadata;
- }
-
- public void setMetadata(Map metadata) {
- this.metadata = metadata;
- }
-
- public String getServerAddr() {
- return serverAddr;
- }
-
- public void setServerAddr(String serverAddr) {
- this.serverAddr = serverAddr;
- }
-
- public String getAccessKey() {
- return accessKey;
- }
-
- public void setAccessKey(String accessKey) {
- this.accessKey = accessKey;
- }
-
- public String getSecretKey() {
- return secretKey;
- }
-
- public void setSecretKey(String secretKey) {
- this.secretKey = secretKey;
- }
-
- public String getNamingLoadCacheAtStart() {
- return namingLoadCacheAtStart;
- }
-
- public void setNamingLoadCacheAtStart(String namingLoadCacheAtStart) {
- this.namingLoadCacheAtStart = namingLoadCacheAtStart;
- }
-
- @Override
- public String toString() {
- return "NacosDiscoveryProperties{" + "serverAddr='" + serverAddr + '\''
- + ", endpoint='" + endpoint + '\'' + ", namespace='" + namespace + '\''
- + ", logName='" + logName + '\'' + ", service='" + service + '\''
- + ", weight=" + weight + ", clusterName='" + clusterName + '\''
- + ", metadata=" + metadata + ", registerEnabled=" + registerEnabled
- + ", ip='" + ip + '\'' + ", networkInterface='" + networkInterface + '\''
- + ", port=" + port + ", secure=" + secure + ", accessKey='" + accessKey
- + ", namingLoadCacheAtStart=" + namingLoadCacheAtStart + '\''
- + ", secretKey='" + secretKey + '\'' + '}';
- }
-
- public void overrideFromEnv(Environment env) {
-
- if (StringUtils.isEmpty(this.getServerAddr())) {
- this.setServerAddr(env
- .resolvePlaceholders("${spring.cloud.nacos.discovery.server-addr:}"));
- }
- if (StringUtils.isEmpty(this.getNamespace())) {
- this.setNamespace(env
- .resolvePlaceholders("${spring.cloud.nacos.discovery.namespace:}"));
- }
- if (StringUtils.isEmpty(this.getAccessKey())) {
- this.setAccessKey(env
- .resolvePlaceholders("${spring.cloud.nacos.discovery.access-key:}"));
- }
- if (StringUtils.isEmpty(this.getSecretKey())) {
- this.setSecretKey(env
- .resolvePlaceholders("${spring.cloud.nacos.discovery.secret-key:}"));
- }
- if (StringUtils.isEmpty(this.getLogName())) {
- this.setLogName(
- env.resolvePlaceholders("${spring.cloud.nacos.discovery.log-name:}"));
- }
- if (StringUtils.isEmpty(this.getClusterName())) {
- this.setClusterName(env.resolvePlaceholders(
- "${spring.cloud.nacos.discovery.cluster-name:}"));
- }
- if (StringUtils.isEmpty(this.getEndpoint())) {
- this.setEndpoint(
- env.resolvePlaceholders("${spring.cloud.nacos.discovery.endpoint:}"));
- }
- }
-
- public NamingService namingServiceInstance() {
-
- if (null != namingService) {
- return namingService;
- }
-
- Properties properties = new Properties();
- properties.put(SERVER_ADDR, serverAddr);
- properties.put(NAMESPACE, namespace);
- properties.put(UtilAndComs.NACOS_NAMING_LOG_NAME, logName);
- properties.put(ENDPOINT, endpoint);
- properties.put(ACCESS_KEY, accessKey);
- properties.put(SECRET_KEY, secretKey);
- properties.put(CLUSTER_NAME, clusterName);
- properties.put(NAMING_LOAD_CACHE_AT_START, namingLoadCacheAtStart);
+ private static final Logger LOGGER = LoggerFactory
+ .getLogger(NacosDiscoveryProperties.class);
+
+ /**
+ * nacos discovery server address
+ */
+ private String serverAddr;
+
+ /**
+ * the domain name of a service, through which the server address can be dynamically
+ * obtained.
+ */
+ private String endpoint;
+
+ /**
+ * namespace, separation registry of different environments.
+ */
+ private String namespace;
+
+ /**
+ * nacos naming log file name
+ */
+ private String logName;
+
+ /**
+ * service name to registry
+ */
+ @Value("${spring.cloud.nacos.discovery.service:${spring.application.name:}}")
+ private String service;
+
+ /**
+ * weight for service instance, the larger the value, the larger the weight.
+ */
+ private float weight = 1;
+
+ /**
+ * cluster name for nacos server.
+ */
+ private String clusterName = "DEFAULT";
+
+ /**
+ * naming load from local cache at application start. true is load
+ */
+ private String namingLoadCacheAtStart = "false";
+
+ /**
+ * extra metadata to register.
+ */
+ private Map metadata = new HashMap<>();
+
+ /**
+ * if you just want to subscribe, but don't want to register your service, set it to
+ * false.
+ */
+ private boolean registerEnabled = true;
+
+ /**
+ * The ip address your want to register for your service instance, needn't to set it
+ * if the auto detect ip works well.
+ */
+ private String ip;
+
+ /**
+ * which network interface's ip you want to register
+ */
+ private String networkInterface = "";
+
+ /**
+ * The port your want to register for your service instance, needn't to set it if the
+ * auto detect port works well
+ */
+ private int port = -1;
+
+ /**
+ * whether your service is a https service
+ */
+ private boolean secure = false;
+
+ /**
+ * access key for namespace.
+ */
+ private String accessKey;
+
+ /**
+ * secret key for namespace.
+ */
+ private String secretKey;
+
+ @Autowired
+ private InetUtils inetUtils;
+
+ @Autowired
+ private Environment environment;
+
+ private NamingService namingService;
+
+ @PostConstruct
+ public void init() throws SocketException {
+
+ if (secure) {
+ metadata.put("secure", "true");
+ }
+
+ serverAddr = Objects.toString(serverAddr, "");
+ endpoint = Objects.toString(endpoint, "");
+ namespace = Objects.toString(namespace, "");
+ logName = Objects.toString(logName, "");
+
+ if (StringUtils.isEmpty(ip)) {
+ // traversing network interfaces if didn't specify a interface
+ if (StringUtils.isEmpty(networkInterface)) {
+ ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
+ }
+ else {
+ NetworkInterface netInterface = NetworkInterface
+ .getByName(networkInterface);
+ if (null == netInterface) {
+ throw new IllegalArgumentException(
+ "no such interface " + networkInterface);
+ }
+
+ Enumeration inetAddress = netInterface.getInetAddresses();
+ while (inetAddress.hasMoreElements()) {
+ InetAddress currentAddress = inetAddress.nextElement();
+ if (currentAddress instanceof Inet4Address
+ && !currentAddress.isLoopbackAddress()) {
+ ip = currentAddress.getHostAddress();
+ break;
+ }
+ }
+
+ if (StringUtils.isEmpty(ip)) {
+ throw new RuntimeException("cannot find available ip from"
+ + " network interface " + networkInterface);
+ }
+
+ }
+ }
+
+ this.overrideFromEnv(environment);
+ }
+
+ public String getEndpoint() {
+ return endpoint;
+ }
+
+ public void setEndpoint(String endpoint) {
+ this.endpoint = endpoint;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getLogName() {
+ return logName;
+ }
+
+ public void setLogName(String logName) {
+ this.logName = logName;
+ }
+
+ public void setInetUtils(InetUtils inetUtils) {
+ this.inetUtils = inetUtils;
+ }
+
+ public float getWeight() {
+ return weight;
+ }
+
+ public void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ public String getClusterName() {
+ return clusterName;
+ }
+
+ public void setClusterName(String clusterName) {
+ this.clusterName = clusterName;
+ }
+
+ public String getService() {
+ return service;
+ }
+
+ public void setService(String service) {
+ this.service = service;
+ }
+
+ public boolean isRegisterEnabled() {
+ return registerEnabled;
+ }
+
+ public void setRegisterEnabled(boolean registerEnabled) {
+ this.registerEnabled = registerEnabled;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public String getNetworkInterface() {
+ return networkInterface;
+ }
+
+ public void setNetworkInterface(String networkInterface) {
+ this.networkInterface = networkInterface;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public boolean isSecure() {
+ return secure;
+ }
+
+ public void setSecure(boolean secure) {
+ this.secure = secure;
+ }
+
+ public Map getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(Map metadata) {
+ this.metadata = metadata;
+ }
+
+ public String getServerAddr() {
+ return serverAddr;
+ }
+
+ public void setServerAddr(String serverAddr) {
+ this.serverAddr = serverAddr;
+ }
+
+ public String getAccessKey() {
+ return accessKey;
+ }
+
+ public void setAccessKey(String accessKey) {
+ this.accessKey = accessKey;
+ }
+
+ public String getSecretKey() {
+ return secretKey;
+ }
+
+ public void setSecretKey(String secretKey) {
+ this.secretKey = secretKey;
+ }
+
+ public String getNamingLoadCacheAtStart() {
+ return namingLoadCacheAtStart;
+ }
+
+ public void setNamingLoadCacheAtStart(String namingLoadCacheAtStart) {
+ this.namingLoadCacheAtStart = namingLoadCacheAtStart;
+ }
+
+ @Override
+ public String toString() {
+ return "NacosDiscoveryProperties{" + "serverAddr='" + serverAddr + '\''
+ + ", endpoint='" + endpoint + '\'' + ", namespace='" + namespace + '\''
+ + ", logName='" + logName + '\'' + ", service='" + service + '\''
+ + ", weight=" + weight + ", clusterName='" + clusterName + '\''
+ + ", metadata=" + metadata + ", registerEnabled=" + registerEnabled
+ + ", ip='" + ip + '\'' + ", networkInterface='" + networkInterface + '\''
+ + ", port=" + port + ", secure=" + secure + ", accessKey='" + accessKey
+ + ", namingLoadCacheAtStart=" + namingLoadCacheAtStart + '\''
+ + ", secretKey='" + secretKey + '\'' + '}';
+ }
+
+ public void overrideFromEnv(Environment env) {
+
+ if (StringUtils.isEmpty(this.getServerAddr())) {
+ this.setServerAddr(env
+ .resolvePlaceholders("${spring.cloud.nacos.discovery.server-addr:}"));
+ }
+ if (StringUtils.isEmpty(this.getNamespace())) {
+ this.setNamespace(env
+ .resolvePlaceholders("${spring.cloud.nacos.discovery.namespace:}"));
+ }
+ if (StringUtils.isEmpty(this.getAccessKey())) {
+ this.setAccessKey(env
+ .resolvePlaceholders("${spring.cloud.nacos.discovery.access-key:}"));
+ }
+ if (StringUtils.isEmpty(this.getSecretKey())) {
+ this.setSecretKey(env
+ .resolvePlaceholders("${spring.cloud.nacos.discovery.secret-key:}"));
+ }
+ if (StringUtils.isEmpty(this.getLogName())) {
+ this.setLogName(
+ env.resolvePlaceholders("${spring.cloud.nacos.discovery.log-name:}"));
+ }
+ if (StringUtils.isEmpty(this.getClusterName())) {
+ this.setClusterName(env.resolvePlaceholders(
+ "${spring.cloud.nacos.discovery.cluster-name:}"));
+ }
+ if (StringUtils.isEmpty(this.getEndpoint())) {
+ this.setEndpoint(
+ env.resolvePlaceholders("${spring.cloud.nacos.discovery.endpoint:}"));
+ }
+ }
+
+ public NamingService namingServiceInstance() {
+
+ if (null != namingService) {
+ return namingService;
+ }
+
+ Properties properties = new Properties();
+ properties.put(SERVER_ADDR, serverAddr);
+ properties.put(NAMESPACE, namespace);
+ properties.put(UtilAndComs.NACOS_NAMING_LOG_NAME, logName);
+ properties.put(ENDPOINT, endpoint);
+ properties.put(ACCESS_KEY, accessKey);
+ properties.put(SECRET_KEY, secretKey);
+ properties.put(CLUSTER_NAME, clusterName);
+ properties.put(NAMING_LOAD_CACHE_AT_START, namingLoadCacheAtStart);
try {
namingService = NacosFactory.createNamingService(properties);
} catch (Exception e) {
- LOGGER.error("create naming service error!properties={},e=,", this, e);
- return null;
- }
+ LOGGER.error("create naming service error!properties={},e=,", this, e);
+ return null;
+ }
return namingService;
- }
+ }
}
diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/spring.factories
index 723407fe..91d5ff59 100644
--- a/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/spring.factories
@@ -1,6 +1,5 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.nacos.NacosDiscoveryAutoConfiguration,\
org.springframework.cloud.alibaba.nacos.ribbon.RibbonNacosAutoConfiguration,\
- org.springframework.cloud.alibaba.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration
-org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
-org.springframework.cloud.alibaba.nacos.NacosDiscoveryClientAutoConfiguration
+ org.springframework.cloud.alibaba.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
+ org.springframework.cloud.alibaba.nacos.NacosDiscoveryClientAutoConfiguration
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceConstants.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceConstants.java
index 8903b700..345b443d 100644
--- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceConstants.java
+++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceConstants.java
@@ -23,14 +23,4 @@ public interface SentinelDataSourceConstants {
String PROPERTY_PREFIX = "spring.cloud.sentinel";
- String NACOS_DATASOURCE_AK = PROPERTY_PREFIX + ".nacos.config.access-key";
-
- String NACOS_DATASOURCE_SK = PROPERTY_PREFIX + ".nacos.config.secret-key";
-
- String NACOS_DATASOURCE_NAMESPACE = PROPERTY_PREFIX + ".nacos.config.namespace";
-
- String NACOS_DATASOURCE_ENDPOINT = PROPERTY_PREFIX + ".nacos.config.endpoint";
-
- String PROJECT_NAME = PROPERTY_PREFIX + ".nacos.config.project-name";
-
}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java
index ba6500bb..272252c2 100644
--- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java
+++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java
@@ -27,7 +27,7 @@ public class AbstractDataSourceProperties {
private RuleType ruleType;
private String converterClass;
@JsonIgnore
- protected String factoryBeanName;
+ private final String factoryBeanName;
public AbstractDataSourceProperties(String factoryBeanName) {
this.factoryBeanName = factoryBeanName;
@@ -61,10 +61,6 @@ public class AbstractDataSourceProperties {
return factoryBeanName;
}
- public void setFactoryBeanName(String factoryBeanName) {
- this.factoryBeanName = factoryBeanName;
- }
-
public void preCheck(String dataSourceName) {
}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/NacosDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/NacosDataSourceProperties.java
index e5795caa..9df4d473 100644
--- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/NacosDataSourceProperties.java
+++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/NacosDataSourceProperties.java
@@ -1,12 +1,12 @@
package org.springframework.cloud.alibaba.sentinel.datasource.config;
+import javax.validation.constraints.NotEmpty;
+
import org.springframework.cloud.alibaba.sentinel.datasource.RuleType;
import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourceConstants;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean;
import org.springframework.util.StringUtils;
-import javax.validation.constraints.NotEmpty;
-
/**
* Nacos Properties class Using by {@link DataSourcePropertiesConfiguration} and
* {@link NacosDataSourceFactoryBean}
@@ -18,13 +18,11 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties {
private String serverAddr;
@NotEmpty
- private String groupId;
+ private String groupId = "DEFAULT_GROUP";
@NotEmpty
private String dataId;
- // commercialized usage
-
private String endpoint;
private String namespace;
private String accessKey;
@@ -36,17 +34,9 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties {
@Override
public void preCheck(String dataSourceName) {
- if (!StringUtils.isEmpty(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT))) {
- this.setServerAddr(null);
- this.setEndpoint(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT));
- this.setNamespace(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_NAMESPACE));
- this.setAccessKey(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_AK));
- this.setSecretKey(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_SK));
+ if (StringUtils.isEmpty(serverAddr) && acmPropertiesInvalid()) {
+ throw new IllegalArgumentException(
+ "NacosDataSource properties value not correct. serverAddr is empty but there is empty value in accessKey, secretKey, endpoint, namespace property");
}
}
@@ -106,34 +96,9 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties {
this.secretKey = secretKey;
}
- public static NacosDataSourceProperties buildFlowByEDAS() {
- return buildByEDAS("flow");
+ public boolean acmPropertiesInvalid() {
+ return StringUtils.isEmpty(endpoint) || StringUtils.isEmpty(accessKey)
+ || StringUtils.isEmpty(secretKey) || StringUtils.isEmpty(namespace);
}
- public static NacosDataSourceProperties buildDegradeByEDAS() {
- return buildByEDAS("degrade");
- }
-
- public static NacosDataSourceProperties buildByEDAS(String type) {
- NacosDataSourceProperties result = new NacosDataSourceProperties();
- result.setEndpoint(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT));
- result.setNamespace(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_NAMESPACE));
- result.setAccessKey(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_AK));
- result.setSecretKey(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_SK));
- result.setDataType("json");
- result.setDataId(System.getProperties()
- .getProperty(SentinelDataSourceConstants.PROJECT_NAME) + "-" + type);
- result.setGroupId("nacos-sentinel");
- if (type.equals(RuleType.FLOW.getName())) {
- result.setRuleType(RuleType.FLOW);
- }
- else {
- result.setRuleType(RuleType.DEGRADE);
- }
- return result;
- }
}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java
index 1683b182..d7894418 100644
--- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java
+++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java
@@ -4,7 +4,6 @@ import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.nacos.api.PropertyKeyConst;
import org.springframework.beans.factory.FactoryBean;
-import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourceConstants;
import org.springframework.util.StringUtils;
import java.util.Properties;
@@ -29,16 +28,19 @@ public class NacosDataSourceFactoryBean implements FactoryBean
@Override
public NacosDataSource getObject() throws Exception {
- if (!StringUtils.isEmpty(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT))) {
- Properties properties = new Properties();
- properties.setProperty(PropertyKeyConst.ACCESS_KEY, this.accessKey);
- properties.setProperty(PropertyKeyConst.SERVER_ADDR, this.secretKey);
- properties.setProperty(PropertyKeyConst.ENDPOINT, this.endpoint);
- properties.setProperty(PropertyKeyConst.NAMESPACE, this.namespace);
- return new NacosDataSource(properties, groupId, dataId, converter);
+ Properties properties = new Properties();
+ if (!StringUtils.isEmpty(this.serverAddr)) {
+ properties.setProperty(PropertyKeyConst.SERVER_ADDR, this.serverAddr);
}
- return new NacosDataSource(serverAddr, groupId, dataId, converter);
+ else {
+ properties.setProperty(PropertyKeyConst.ACCESS_KEY, this.accessKey);
+ properties.setProperty(PropertyKeyConst.SECRET_KEY, this.secretKey);
+ properties.setProperty(PropertyKeyConst.ENDPOINT, this.endpoint);
+ }
+ if (!StringUtils.isEmpty(this.namespace)) {
+ properties.setProperty(PropertyKeyConst.NAMESPACE, this.namespace);
+ }
+ return new NacosDataSource(properties, groupId, dataId, converter);
}
@Override
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/ApolloDataSourceFactoryBeanTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/ApolloDataSourceFactoryBeanTests.java
new file mode 100644
index 00000000..bc210f2f
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/ApolloDataSourceFactoryBeanTests.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
+import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource;
+
+/**
+ * @author Jim
+ */
+public class ApolloDataSourceFactoryBeanTests {
+
+ private String flowRuleKey = "sentinel";
+ private String namespace = "namespace";
+ private String defaultFlowValue = "{}";
+
+ @Test
+ public void testApolloFactoryBean() throws Exception {
+ ApolloDataSourceFactoryBean factoryBean = spy(new ApolloDataSourceFactoryBean());
+
+ Converter converter = mock(JsonConverter.class);
+
+ factoryBean.setDefaultFlowRuleValue(defaultFlowValue);
+ factoryBean.setFlowRulesKey(flowRuleKey);
+ factoryBean.setNamespaceName(namespace);
+ factoryBean.setConverter(converter);
+
+ ApolloDataSource apolloDataSource = mock(ApolloDataSource.class);
+
+ when(apolloDataSource.readSource()).thenReturn("{}");
+ doReturn(apolloDataSource).when(factoryBean).getObject();
+
+ assertEquals("ApolloDataSourceFactoryBean getObject error", apolloDataSource,
+ factoryBean.getObject());
+ assertEquals("ApolloDataSource read source value was wrong", "{}",
+ factoryBean.getObject().readSource());
+ assertEquals("ApolloDataSource converter was wrong", converter,
+ factoryBean.getConverter());
+ assertEquals("ApolloDataSourceFactoryBean flowRuleKey was wrong", flowRuleKey,
+ factoryBean.getFlowRulesKey());
+ assertEquals("ApolloDataSourceFactoryBean namespace was wrong", namespace,
+ factoryBean.getNamespaceName());
+ assertEquals("ApolloDataSourceFactoryBean defaultFlowValue was wrong",
+ defaultFlowValue, factoryBean.getDefaultFlowRuleValue());
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourcePropertiesConfigurationTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourcePropertiesConfigurationTests.java
new file mode 100644
index 00000000..4845d486
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourcePropertiesConfigurationTests.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.ApolloDataSourceProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.FileDataSourceProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.NacosDataSourceProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.ZookeeperDataSourceProperties;
+
+/**
+ * @author Jim
+ */
+public class DataSourcePropertiesConfigurationTests {
+
+ @Test
+ public void testFileAttr() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration();
+ assertEquals("DataSourcePropertiesConfiguration valid field size was wrong", 0,
+ dataSourcePropertiesConfiguration.getValidField().size());
+ assertNull("DataSourcePropertiesConfiguration valid properties was not null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+
+ FileDataSourceProperties fileDataSourceProperties = buildFileProperties();
+
+ dataSourcePropertiesConfiguration.setFile(fileDataSourceProperties);
+
+ assertEquals(
+ "DataSourcePropertiesConfiguration valid field size was wrong after set file attribute",
+ 1, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration file properties was null after set file attribute",
+ dataSourcePropertiesConfiguration.getFile());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration valid properties was null after set file attribute",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ @Test
+ public void testNacosAttr() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration();
+ assertEquals("DataSourcePropertiesConfiguration valid field size was wrong", 0,
+ dataSourcePropertiesConfiguration.getValidField().size());
+ assertNull("DataSourcePropertiesConfiguration valid properties was not null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+
+ NacosDataSourceProperties nacosDataSourceProperties = buildNacosProperties();
+
+ dataSourcePropertiesConfiguration.setNacos(nacosDataSourceProperties);
+
+ assertEquals(
+ "DataSourcePropertiesConfiguration valid field size was wrong after set nacos attribute",
+ 1, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration nacos properties was null after set nacos attribute",
+ dataSourcePropertiesConfiguration.getNacos());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration valid properties was null after set nacos attribute",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ @Test
+ public void testZKAttr() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration();
+ assertEquals("DataSourcePropertiesConfiguration valid field size was wrong", 0,
+ dataSourcePropertiesConfiguration.getValidField().size());
+ assertNull("DataSourcePropertiesConfiguration valid properties was not null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+
+ ZookeeperDataSourceProperties zookeeperDataSourceProperties = buildZKProperties();
+
+ dataSourcePropertiesConfiguration.setZk(zookeeperDataSourceProperties);
+
+ assertEquals(
+ "DataSourcePropertiesConfiguration valid field size was wrong after set zk attribute",
+ 1, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration zk properties was null after set zk attribute",
+ dataSourcePropertiesConfiguration.getZk());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration valid properties was null after set zk attribute",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ @Test
+ public void testApolloAttr() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration();
+ assertEquals("DataSourcePropertiesConfiguration valid field size was wrong", 0,
+ dataSourcePropertiesConfiguration.getValidField().size());
+ assertNull("DataSourcePropertiesConfiguration valid properties was not null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+
+ ApolloDataSourceProperties apolloDataSourceProperties = buildApolloProperties();
+
+ dataSourcePropertiesConfiguration.setApollo(apolloDataSourceProperties);
+
+ assertEquals(
+ "DataSourcePropertiesConfiguration valid field size was wrong after set apollo attribute",
+ 1, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration apollo properties was null after set apollo attribute",
+ dataSourcePropertiesConfiguration.getApollo());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration valid properties was null after set apollo attribute",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ @Test
+ public void testMultiAttr() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration();
+ assertEquals("DataSourcePropertiesConfiguration valid field size was wrong", 0,
+ dataSourcePropertiesConfiguration.getValidField().size());
+ assertNull("DataSourcePropertiesConfiguration valid properties was not null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+
+ FileDataSourceProperties fileDataSourceProperties = buildFileProperties();
+ NacosDataSourceProperties nacosDataSourceProperties = buildNacosProperties();
+
+ dataSourcePropertiesConfiguration.setFile(fileDataSourceProperties);
+ dataSourcePropertiesConfiguration.setNacos(nacosDataSourceProperties);
+
+ assertEquals(
+ "DataSourcePropertiesConfiguration valid field size was wrong after set file and nacos attribute",
+ 2, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNull(
+ "DataSourcePropertiesConfiguration valid properties was not null after set file and nacos attribute",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ @Test
+ public void testFileConstructor() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration(
+ buildFileProperties());
+ assertEquals(
+ "DataSourcePropertiesConfiguration file constructor valid field size was wrong",
+ 1, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration file constructor valid properties was null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ @Test
+ public void testNacosConstructor() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration(
+ buildNacosProperties());
+ assertEquals(
+ "DataSourcePropertiesConfiguration nacos constructor valid field size was wrong",
+ 1, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration nacos constructor valid properties was null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ @Test
+ public void testApolloConstructor() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration(
+ buildApolloProperties());
+ assertEquals(
+ "DataSourcePropertiesConfiguration apollo constructor valid field size was wrong",
+ 1, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration apollo constructor valid properties was null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ @Test
+ public void testZKConstructor() {
+ DataSourcePropertiesConfiguration dataSourcePropertiesConfiguration = new DataSourcePropertiesConfiguration(
+ buildZKProperties());
+ assertEquals(
+ "DataSourcePropertiesConfiguration zk constructor valid field size was wrong",
+ 1, dataSourcePropertiesConfiguration.getValidField().size());
+ assertNotNull(
+ "DataSourcePropertiesConfiguration zk constructor valid properties was null",
+ dataSourcePropertiesConfiguration.getValidDataSourceProperties());
+ }
+
+ private FileDataSourceProperties buildFileProperties() {
+ FileDataSourceProperties fileDataSourceProperties = new FileDataSourceProperties();
+
+ fileDataSourceProperties.setFile("/tmp/test.json");
+ fileDataSourceProperties.setBufSize(1024);
+ fileDataSourceProperties.setRecommendRefreshMs(2000);
+ return fileDataSourceProperties;
+ }
+
+ private NacosDataSourceProperties buildNacosProperties() {
+ NacosDataSourceProperties nacosDataSourceProperties = new NacosDataSourceProperties();
+ nacosDataSourceProperties.setServerAddr("127.0.0.1:8848");
+ nacosDataSourceProperties.setDataId("sentinel");
+ nacosDataSourceProperties.setGroupId("custom-group");
+ return nacosDataSourceProperties;
+ }
+
+ private ApolloDataSourceProperties buildApolloProperties() {
+ ApolloDataSourceProperties apolloDataSourceProperties = new ApolloDataSourceProperties();
+ apolloDataSourceProperties.setFlowRulesKey("test-key");
+ apolloDataSourceProperties.setDefaultFlowRuleValue("dft-val");
+ apolloDataSourceProperties.setNamespaceName("namespace");
+ return apolloDataSourceProperties;
+ }
+
+ private ZookeeperDataSourceProperties buildZKProperties() {
+ ZookeeperDataSourceProperties zookeeperDataSourceProperties = new ZookeeperDataSourceProperties();
+
+ zookeeperDataSourceProperties.setServerAddr("localhost:2181");
+ zookeeperDataSourceProperties.setPath("/path");
+ return zookeeperDataSourceProperties;
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourcePropertiesTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourcePropertiesTests.java
new file mode 100644
index 00000000..e96373c8
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourcePropertiesTests.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+import org.junit.Test;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.ApolloDataSourceProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.FileDataSourceProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.ZookeeperDataSourceProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean;
+import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.FileRefreshableDataSourceFactoryBean;
+import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ZookeeperDataSourceFactoryBean;
+import org.springframework.util.ResourceUtils;
+import org.springframework.util.StringUtils;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
+
+/**
+ * @author Jim
+ */
+public class DataSourcePropertiesTests {
+
+ @Test
+ public void testApollo() {
+ ApolloDataSourceProperties apolloDataSourceProperties = new ApolloDataSourceProperties();
+ apolloDataSourceProperties.setFlowRulesKey("test-key");
+ apolloDataSourceProperties.setDefaultFlowRuleValue("dft-val");
+ apolloDataSourceProperties.setNamespaceName("namespace");
+ apolloDataSourceProperties.setRuleType(RuleType.DEGRADE);
+ assertEquals("Apollo flow rule key was wrong", "test-key",
+ apolloDataSourceProperties.getFlowRulesKey());
+ assertEquals("Apollo namespace was wrong", "namespace",
+ apolloDataSourceProperties.getNamespaceName());
+ assertEquals("Apollo default data type was wrong", "json",
+ apolloDataSourceProperties.getDataType());
+ assertEquals("Apollo rule type was wrong", RuleType.DEGRADE,
+ apolloDataSourceProperties.getRuleType());
+ assertEquals("Apollo default flow value was wrong", "dft-val",
+ apolloDataSourceProperties.getDefaultFlowRuleValue());
+ assertEquals("Apollo factory bean was wrong",
+ ApolloDataSourceFactoryBean.class.getName(),
+ apolloDataSourceProperties.getFactoryBeanName());
+ assertNull("Apollo converterClass was not null",
+ apolloDataSourceProperties.getConverterClass());
+ }
+
+ @Test
+ public void testZK() {
+ ZookeeperDataSourceProperties zookeeperDataSourceProperties = new ZookeeperDataSourceProperties();
+
+ zookeeperDataSourceProperties.setServerAddr("localhost:2181");
+ zookeeperDataSourceProperties.setGroupId("groupId");
+ zookeeperDataSourceProperties.setDataId("dataId");
+ zookeeperDataSourceProperties.setPath("/path");
+ zookeeperDataSourceProperties.setConverterClass("test.ConverterClass");
+ zookeeperDataSourceProperties.setRuleType(RuleType.AUTHORITY);
+
+ assertEquals("ZK serverAddr was wrong", "localhost:2181",
+ zookeeperDataSourceProperties.getServerAddr());
+ assertEquals("ZK groupId was wrong", "groupId",
+ zookeeperDataSourceProperties.getGroupId());
+ assertEquals("ZK dataId was wrong", "dataId",
+ zookeeperDataSourceProperties.getDataId());
+ assertEquals("ZK path was wrong", "/path",
+ zookeeperDataSourceProperties.getPath());
+ assertEquals("ZK factory bean was wrong",
+ ZookeeperDataSourceFactoryBean.class.getName(),
+ zookeeperDataSourceProperties.getFactoryBeanName());
+ assertEquals("ZK custom converter class was wrong", "test.ConverterClass",
+ zookeeperDataSourceProperties.getConverterClass());
+ assertEquals("ZK rule type was wrong", RuleType.AUTHORITY,
+ zookeeperDataSourceProperties.getRuleType());
+ }
+
+ @Test
+ public void testFileDefaultValue() {
+ FileDataSourceProperties fileDataSourceProperties = new FileDataSourceProperties();
+
+ fileDataSourceProperties.setFile("/tmp/test.json");
+ fileDataSourceProperties.setRuleType(RuleType.PARAM_FLOW);
+
+ assertEquals("File path was wrong", "/tmp/test.json",
+ fileDataSourceProperties.getFile());
+ assertEquals("File charset was wrong", "utf-8",
+ fileDataSourceProperties.getCharset());
+ assertEquals("File refresh time was wrong", 3000L,
+ fileDataSourceProperties.getRecommendRefreshMs());
+ assertEquals("File buf size was wrong", 1024 * 1024,
+ fileDataSourceProperties.getBufSize());
+ assertEquals("File factory bean was wrong",
+ FileRefreshableDataSourceFactoryBean.class.getName(),
+ fileDataSourceProperties.getFactoryBeanName());
+ assertEquals("File rule type was wrong", RuleType.PARAM_FLOW,
+ fileDataSourceProperties.getRuleType());
+ }
+
+ @Test
+ public void testFileCustomValue() {
+ FileDataSourceProperties fileDataSourceProperties = new FileDataSourceProperties();
+
+ fileDataSourceProperties.setFile("/tmp/test.json");
+ fileDataSourceProperties.setBufSize(1024);
+ fileDataSourceProperties.setRecommendRefreshMs(2000);
+ fileDataSourceProperties.setCharset("ISO8859-1");
+
+ assertEquals("File path was wrong", "/tmp/test.json",
+ fileDataSourceProperties.getFile());
+ assertEquals("File charset was wrong", "ISO8859-1",
+ fileDataSourceProperties.getCharset());
+ assertEquals("File refresh time was wrong", 2000L,
+ fileDataSourceProperties.getRecommendRefreshMs());
+ assertEquals("File buf size was wrong", 1024,
+ fileDataSourceProperties.getBufSize());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testFileException() {
+ FileDataSourceProperties fileDataSourceProperties = new FileDataSourceProperties();
+ fileDataSourceProperties.setFile("classpath: 1.json");
+ fileDataSourceProperties.preCheck("test-ds");
+ }
+
+ @Test
+ public void testPostRegister() throws Exception {
+ FileDataSourceProperties fileDataSourceProperties = new FileDataSourceProperties();
+
+ fileDataSourceProperties.setFile("classpath: flowrule.json");
+ fileDataSourceProperties.setRuleType(RuleType.FLOW);
+
+ FileRefreshableDataSource fileRefreshableDataSource = new FileRefreshableDataSource(
+ ResourceUtils
+ .getFile(StringUtils
+ .trimAllWhitespace(fileDataSourceProperties.getFile()))
+ .getAbsolutePath(),
+ new Converter>() {
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ @Override
+ public List convert(String source) {
+ try {
+ return objectMapper.readValue(source,
+ new TypeReference>() {
+ });
+ }
+ catch (IOException e) {
+ // ignore
+ }
+ return null;
+ }
+ });
+ fileDataSourceProperties.postRegister(fileRefreshableDataSource);
+ assertEquals("DataSourceProperties postRegister error",
+ fileRefreshableDataSource.loadConfig(), FlowRuleManager.getRules());
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/FileRefreshableDataSourceFactoryBeanTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/FileRefreshableDataSourceFactoryBeanTests.java
new file mode 100644
index 00000000..58b87775
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/FileRefreshableDataSourceFactoryBeanTests.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+import org.junit.Test;
+import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.FileRefreshableDataSourceFactoryBean;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.ResourceUtils;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+
+/**
+ * @author Jim
+ */
+public class FileRefreshableDataSourceFactoryBeanTests {
+
+ @Test
+ public void testFile() throws Exception {
+ AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(
+ TestConfig.class);
+ assertNotNull("FileRefreshableDataSourceFactoryBean was not created",
+ annotationConfigApplicationContext.getBean("fileBean"));
+ FileRefreshableDataSource fileRefreshableDataSource = annotationConfigApplicationContext
+ .getBean("fileBean", FileRefreshableDataSource.class);
+ assertEquals("FileRefreshableDataSourceFactoryBean flow rule size was wrong", 1,
+ ((List) fileRefreshableDataSource.loadConfig()).size());
+ FileRefreshableDataSourceFactoryBean factoryBean = annotationConfigApplicationContext
+ .getBean("&fileBean", FileRefreshableDataSourceFactoryBean.class);
+ assertEquals("FileRefreshableDataSourceFactoryBean buf size was wrong", 1024,
+ factoryBean.getBufSize());
+ assertEquals("FileRefreshableDataSourceFactoryBean charset was wrong", "utf-8",
+ factoryBean.getCharset());
+ assertEquals("FileRefreshableDataSourceFactoryBean recommendRefreshMs was wrong",
+ 2000, factoryBean.getRecommendRefreshMs());
+ assertNotNull("FileRefreshableDataSourceFactoryBean file was null",
+ factoryBean.getFile());
+ assertNotNull("FileRefreshableDataSourceFactoryBean converter was null",
+ factoryBean.getConverter());
+ }
+
+ @Configuration
+ public static class TestConfig {
+
+ @Bean
+ public FileRefreshableDataSourceFactoryBean fileBean() {
+ FileRefreshableDataSourceFactoryBean factoryBean = new FileRefreshableDataSourceFactoryBean();
+ factoryBean.setBufSize(1024);
+ factoryBean.setCharset("utf-8");
+ factoryBean.setRecommendRefreshMs(2000);
+ try {
+ factoryBean.setFile(ResourceUtils.getFile("classpath:flowrule.json")
+ .getAbsolutePath());
+ }
+ catch (FileNotFoundException e) {
+ // ignore
+ }
+ factoryBean.setConverter(buildConverter());
+ return factoryBean;
+ }
+
+ private Converter buildConverter() {
+ return new Converter>() {
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ @Override
+ public List convert(String source) {
+ try {
+ return objectMapper.readValue(source,
+ new TypeReference>() {
+ });
+ }
+ catch (IOException e) {
+ // ignore
+ }
+ return null;
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/NacosDataSourceFactoryBeanTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/NacosDataSourceFactoryBeanTests.java
new file mode 100644
index 00000000..e724278c
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/NacosDataSourceFactoryBeanTests.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.springframework.cloud.alibaba.sentinel.datasource.converter.SentinelConverter;
+import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
+
+/**
+ * @author Jim
+ */
+public class NacosDataSourceFactoryBeanTests {
+
+ private String dataId = "sentinel";
+ private String groupId = "DEFAULT_GROUP";
+ private String serverAddr = "localhost:8848";
+ private String accessKey = "ak";
+ private String secretKey = "sk";
+ private String endpoint = "endpoint";
+ private String namespace = "namespace";
+
+ @Test
+ public void testNacosFactoryBeanServerAddr() throws Exception {
+ NacosDataSourceFactoryBean factoryBean = spy(new NacosDataSourceFactoryBean());
+
+ Converter converter = mock(SentinelConverter.class);
+
+ factoryBean.setDataId(dataId);
+ factoryBean.setGroupId(groupId);
+ factoryBean.setServerAddr(serverAddr);
+ factoryBean.setConverter(converter);
+
+ NacosDataSource nacosDataSource = mock(NacosDataSource.class);
+
+ doReturn(nacosDataSource).when(factoryBean).getObject();
+ when(nacosDataSource.readSource()).thenReturn("{}");
+
+ assertEquals("NacosDataSourceFactoryBean getObject was wrong", nacosDataSource,
+ factoryBean.getObject());
+ assertEquals("NacosDataSource read source value was wrong", "{}",
+ factoryBean.getObject().readSource());
+ assertEquals("NacosDataSource converter was wrong", converter,
+ factoryBean.getConverter());
+ assertEquals("NacosDataSourceFactoryBean dataId was wrong", dataId,
+ factoryBean.getDataId());
+ assertEquals("NacosDataSourceFactoryBean groupId was wrong", groupId,
+ factoryBean.getGroupId());
+ assertEquals("NacosDataSourceFactoryBean serverAddr was wrong", serverAddr,
+ factoryBean.getServerAddr());
+ }
+
+ @Test
+ public void testNacosFactoryBeanProperties() throws Exception {
+ NacosDataSourceFactoryBean factoryBean = spy(new NacosDataSourceFactoryBean());
+
+ Converter converter = mock(SentinelConverter.class);
+
+ factoryBean.setDataId(dataId);
+ factoryBean.setGroupId(groupId);
+ factoryBean.setAccessKey(accessKey);
+ factoryBean.setSecretKey(secretKey);
+ factoryBean.setEndpoint(endpoint);
+ factoryBean.setNamespace(namespace);
+ factoryBean.setConverter(converter);
+
+ NacosDataSource nacosDataSource = mock(NacosDataSource.class);
+
+ doReturn(nacosDataSource).when(factoryBean).getObject();
+ when(nacosDataSource.readSource()).thenReturn("{}");
+
+ assertEquals("NacosDataSourceFactoryBean getObject was wrong", nacosDataSource,
+ factoryBean.getObject());
+ assertEquals("NacosDataSource read source value was wrong", "{}",
+ factoryBean.getObject().readSource());
+ assertEquals("NacosDataSource converter was wrong", converter,
+ factoryBean.getConverter());
+ assertEquals("NacosDataSourceFactoryBean dataId was wrong", dataId,
+ factoryBean.getDataId());
+ assertEquals("NacosDataSourceFactoryBean groupId was wrong", groupId,
+ factoryBean.getGroupId());
+ assertEquals("NacosDataSourceFactoryBean namespace was wrong", namespace,
+ factoryBean.getNamespace());
+ assertEquals("NacosDataSourceFactoryBean endpoint was wrong", endpoint,
+ factoryBean.getEndpoint());
+ assertEquals("NacosDataSourceFactoryBean ak was wrong", accessKey,
+ factoryBean.getAccessKey());
+ assertEquals("NacosDataSourceFactoryBean sk was wrong", secretKey,
+ factoryBean.getSecretKey());
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/NacosDataSourcePropertiesTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/NacosDataSourcePropertiesTests.java
new file mode 100644
index 00000000..fd2a157c
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/NacosDataSourcePropertiesTests.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.NacosDataSourceProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean;
+
+/**
+ * @author Jim
+ */
+public class NacosDataSourcePropertiesTests {
+
+ @Test
+ public void testNacosWithAddr() {
+ NacosDataSourceProperties nacosDataSourceProperties = new NacosDataSourceProperties();
+ nacosDataSourceProperties.setServerAddr("127.0.0.1:8848");
+ nacosDataSourceProperties.setRuleType(RuleType.FLOW);
+ nacosDataSourceProperties.setDataId("sentinel");
+ nacosDataSourceProperties.setGroupId("custom-group");
+ nacosDataSourceProperties.setDataType("xml");
+
+ assertEquals("Nacos groupId was wrong", "custom-group",
+ nacosDataSourceProperties.getGroupId());
+ assertEquals("Nacos dataId was wrong", "sentinel",
+ nacosDataSourceProperties.getDataId());
+ assertEquals("Nacos default data type was wrong", "xml",
+ nacosDataSourceProperties.getDataType());
+ assertEquals("Nacos rule type was wrong", RuleType.FLOW,
+ nacosDataSourceProperties.getRuleType());
+ assertEquals("Nacos default factory bean was wrong",
+ NacosDataSourceFactoryBean.class.getName(),
+ nacosDataSourceProperties.getFactoryBeanName());
+ }
+
+ @Test
+ public void testNacosWithProperties() {
+ NacosDataSourceProperties nacosDataSourceProperties = new NacosDataSourceProperties();
+ nacosDataSourceProperties.setAccessKey("ak");
+ nacosDataSourceProperties.setSecretKey("sk");
+ nacosDataSourceProperties.setEndpoint("endpoint");
+ nacosDataSourceProperties.setNamespace("namespace");
+ nacosDataSourceProperties.setRuleType(RuleType.SYSTEM);
+
+ assertEquals("Nacos ak was wrong", "ak",
+ nacosDataSourceProperties.getAccessKey());
+ assertEquals("Nacos sk was wrong", "sk",
+ nacosDataSourceProperties.getSecretKey());
+ assertEquals("Nacos endpoint was wrong", "endpoint",
+ nacosDataSourceProperties.getEndpoint());
+ assertEquals("Nacos namespace was wrong", "namespace",
+ nacosDataSourceProperties.getNamespace());
+ assertEquals("Nacos rule type was wrong", RuleType.SYSTEM,
+ nacosDataSourceProperties.getRuleType());
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/RuleTypeTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/RuleTypeTests.java
new file mode 100644
index 00000000..a540ab86
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/RuleTypeTests.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.slots.block.AbstractRule;
+import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
+import com.alibaba.csp.sentinel.slots.system.SystemRule;
+
+/**
+ * @author Jim
+ */
+public class RuleTypeTests {
+
+ @Test
+ public void testGetByName() {
+ assertFalse("empty str rule name was not null",
+ RuleType.getByName("").isPresent());
+ assertFalse("test rule name was not null",
+ RuleType.getByName("test").isPresent());
+ assertFalse("param_flow rule name was not null",
+ RuleType.getByName("param_flow").isPresent());
+ assertFalse("param rule name was not null",
+ RuleType.getByName("param").isPresent());
+ assertFalse("FLOW rule name was not null",
+ RuleType.getByName("FLOW").isPresent());
+ assertTrue("flow rule name was null", RuleType.getByName("flow").isPresent());
+ assertTrue("degrade rule name was null",
+ RuleType.getByName("degrade").isPresent());
+ assertTrue("param-flow rule name was null",
+ RuleType.getByName("param-flow").isPresent());
+ assertTrue("system rule name was null", RuleType.getByName("system").isPresent());
+ assertTrue("authority rule name was null",
+ RuleType.getByName("authority").isPresent());
+ assertEquals("flow rule name was not equals RuleType.FLOW", RuleType.FLOW,
+ RuleType.getByName("flow").get());
+ assertEquals("flow rule name was not equals RuleType.DEGRADE", RuleType.DEGRADE,
+ RuleType.getByName("degrade").get());
+ assertEquals("flow rule name was not equals RuleType.PARAM_FLOW",
+ RuleType.PARAM_FLOW, RuleType.getByName("param-flow").get());
+ assertEquals("flow rule name was not equals RuleType.SYSTEM", RuleType.SYSTEM,
+ RuleType.getByName("system").get());
+ assertEquals("flow rule name was not equals RuleType.AUTHORITY",
+ RuleType.AUTHORITY, RuleType.getByName("authority").get());
+ }
+
+ @Test
+ public void testGetByClass() {
+ assertFalse("Object.class type type was not null",
+ RuleType.getByClass(Object.class).isPresent());
+ assertFalse("AbstractRule.class rule type was not null",
+ RuleType.getByClass(AbstractRule.class).isPresent());
+ assertTrue("FlowRule.class rule type was null",
+ RuleType.getByClass(FlowRule.class).isPresent());
+ assertTrue("DegradeRule.class rule type was null",
+ RuleType.getByClass(DegradeRule.class).isPresent());
+ assertTrue("ParamFlowRule.class rule type was null",
+ RuleType.getByClass(ParamFlowRule.class).isPresent());
+ assertTrue("SystemRule.class rule type was null",
+ RuleType.getByClass(SystemRule.class).isPresent());
+ assertTrue("AuthorityRule.class rule type was null",
+ RuleType.getByClass(AuthorityRule.class).isPresent());
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelConverterTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelConverterTests.java
new file mode 100644
index 00000000..c6ae7a67
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelConverterTests.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Test;
+import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
+import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
+import org.springframework.util.ResourceUtils;
+import org.springframework.util.StringUtils;
+
+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;
+
+/**
+ * @author Jim
+ */
+public class SentinelConverterTests {
+
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ private XmlMapper xmlMapper = new XmlMapper();
+
+ @Test
+ public void testJsonConverter() {
+ JsonConverter jsonConverter = new JsonConverter(objectMapper, FlowRule.class);
+ List flowRules = jsonConverter
+ .convert(readFileContent("classpath: flowrule.json"));
+ assertEquals("json converter flow rule size was wrong", 1, flowRules.size());
+ assertEquals("json converter flow rule resource name was wrong", "resource",
+ flowRules.get(0).getResource());
+ assertEquals("json converter flow rule limit app was wrong", "default",
+ flowRules.get(0).getLimitApp());
+ assertEquals("json converter flow rule count was wrong", "1.0",
+ String.valueOf(flowRules.get(0).getCount()));
+ assertEquals("json converter flow rule control behavior was wrong",
+ RuleConstant.CONTROL_BEHAVIOR_DEFAULT,
+ flowRules.get(0).getControlBehavior());
+ assertEquals("json converter flow rule strategy was wrong",
+ RuleConstant.STRATEGY_DIRECT, flowRules.get(0).getStrategy());
+ assertEquals("json converter flow rule grade was wrong",
+ RuleConstant.FLOW_GRADE_QPS, flowRules.get(0).getGrade());
+ }
+
+ @Test
+ public void testConverterEmptyContent() {
+ JsonConverter jsonConverter = new JsonConverter(objectMapper, FlowRule.class);
+ List flowRules = jsonConverter.convert("");
+ assertEquals("json converter flow rule size was not empty", 0, flowRules.size());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testConverterErrorFormat() {
+ JsonConverter jsonConverter = new JsonConverter(objectMapper, FlowRule.class);
+ jsonConverter.convert(readFileContent("classpath: flowrule-errorformat.json"));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testConverterErrorContent() {
+ JsonConverter jsonConverter = new JsonConverter(objectMapper, FlowRule.class);
+ jsonConverter.convert(readFileContent("classpath: flowrule-errorcontent.json"));
+ }
+
+ @Test
+ public void testXmlConverter() {
+ XmlConverter jsonConverter = new XmlConverter(xmlMapper, FlowRule.class);
+ List flowRules = jsonConverter
+ .convert(readFileContent("classpath: flowrule.xml"));
+ assertEquals("xml converter flow rule size was wrong", 2, flowRules.size());
+ assertEquals("xml converter flow rule1 resource name was wrong", "resource",
+ flowRules.get(0).getResource());
+ assertEquals("xml converter flow rule2 limit app was wrong", "default",
+ flowRules.get(0).getLimitApp());
+ assertEquals("xml converter flow rule1 count was wrong", "1.0",
+ String.valueOf(flowRules.get(0).getCount()));
+ assertEquals("xml converter flow rule1 control behavior was wrong",
+ RuleConstant.CONTROL_BEHAVIOR_DEFAULT,
+ flowRules.get(0).getControlBehavior());
+ assertEquals("xml converter flow rule1 strategy was wrong",
+ RuleConstant.STRATEGY_DIRECT, flowRules.get(0).getStrategy());
+ assertEquals("xml converter flow rule1 grade was wrong",
+ RuleConstant.FLOW_GRADE_QPS, flowRules.get(0).getGrade());
+
+ assertEquals("xml converter flow rule2 resource name was wrong", "test",
+ flowRules.get(1).getResource());
+ assertEquals("xml converter flow rule2 limit app was wrong", "default",
+ flowRules.get(1).getLimitApp());
+ assertEquals("xml converter flow rule2 count was wrong", "1.0",
+ String.valueOf(flowRules.get(1).getCount()));
+ assertEquals("xml converter flow rule2 control behavior was wrong",
+ RuleConstant.CONTROL_BEHAVIOR_DEFAULT,
+ flowRules.get(1).getControlBehavior());
+ assertEquals("xml converter flow rule2 strategy was wrong",
+ RuleConstant.STRATEGY_DIRECT, flowRules.get(1).getStrategy());
+ assertEquals("xml converter flow rule2 grade was wrong",
+ RuleConstant.FLOW_GRADE_QPS, flowRules.get(1).getGrade());
+ }
+
+ private String readFileContent(String file) {
+ try {
+ return FileUtils.readFileToString(
+ ResourceUtils.getFile(StringUtils.trimAllWhitespace(file)));
+ }
+ catch (IOException e) {
+ return "";
+ }
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/ZookeeperDataSourceFactoryBeanTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/ZookeeperDataSourceFactoryBeanTests.java
new file mode 100644
index 00000000..df7158ce
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/ZookeeperDataSourceFactoryBeanTests.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alibaba.sentinel.datasource;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
+import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ZookeeperDataSourceFactoryBean;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource;
+
+/**
+ * @author Jim
+ */
+public class ZookeeperDataSourceFactoryBeanTests {
+
+ private String dataId = "dataId";
+ private String groupId = "groupId";
+ private String serverAddr = "localhost:2181";
+
+ private String path = "/sentinel";
+
+ @Test
+ public void testZKWithoutPathFactoryBean() throws Exception {
+ ZookeeperDataSourceFactoryBean factoryBean = spy(
+ ZookeeperDataSourceFactoryBean.class);
+
+ Converter converter = mock(XmlConverter.class);
+
+ ZookeeperDataSource zookeeperDataSource = mock(ZookeeperDataSource.class);
+
+ factoryBean.setConverter(converter);
+ factoryBean.setDataId(dataId);
+ factoryBean.setGroupId(groupId);
+ factoryBean.setServerAddr(serverAddr);
+
+ when(zookeeperDataSource.readSource()).thenReturn("{}");
+ doReturn(zookeeperDataSource).when(factoryBean).getObject();
+
+ assertEquals("ZookeeperDataSource getObject was wrong", zookeeperDataSource,
+ factoryBean.getObject());
+ assertEquals("ZookeeperDataSource read source value was wrong", "{}",
+ factoryBean.getObject().readSource());
+ assertEquals("ZookeeperDataSourceFactoryBean dataId was wrong", dataId,
+ factoryBean.getDataId());
+ assertEquals("ZookeeperDataSourceFactoryBean converter was wrong", converter,
+ factoryBean.getConverter());
+ assertEquals("ZookeeperDataSourceFactoryBean groupId was wrong", groupId,
+ factoryBean.getGroupId());
+ assertEquals("ZookeeperDataSourceFactoryBean serverAddr was wrong", serverAddr,
+ factoryBean.getServerAddr());
+ }
+
+ @Test
+ public void testZKWithPathFactoryBean() throws Exception {
+ ZookeeperDataSourceFactoryBean factoryBean = spy(
+ ZookeeperDataSourceFactoryBean.class);
+
+ Converter converter = mock(XmlConverter.class);
+
+ ZookeeperDataSource zookeeperDataSource = mock(ZookeeperDataSource.class);
+
+ factoryBean.setConverter(converter);
+ factoryBean.setPath(path);
+ factoryBean.setServerAddr(serverAddr);
+
+ when(zookeeperDataSource.readSource()).thenReturn("{}");
+ doReturn(zookeeperDataSource).when(factoryBean).getObject();
+
+ assertEquals("ZookeeperDataSource value was wrong", zookeeperDataSource,
+ factoryBean.getObject());
+ assertEquals("ZookeeperDataSource read source value was wrong", "{}",
+ factoryBean.getObject().readSource());
+ assertEquals("ZookeeperDataSourceFactoryBean converter was wrong", converter,
+ factoryBean.getConverter());
+ assertEquals("ZookeeperDataSourceFactoryBean path was wrong", path,
+ factoryBean.getPath());
+ assertEquals("ZookeeperDataSourceFactoryBean serverAddr was wrong", serverAddr,
+ factoryBean.getServerAddr());
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule-errorcontent.json b/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule-errorcontent.json
new file mode 100644
index 00000000..7bf60e61
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule-errorcontent.json
@@ -0,0 +1,11 @@
+[
+ {
+ "test": 1,
+ "controlBehavior": 0,
+ "count": 1,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ }
+]
+
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule-errorformat.json b/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule-errorformat.json
new file mode 100644
index 00000000..48c738b6
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule-errorformat.json
@@ -0,0 +1,10 @@
+[
+ {
+ "resource": "resource",
+ "controlBehavior": 0,
+ "count": 1,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ }==
+]
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule.json b/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule.json
new file mode 100644
index 00000000..0f8fbe2b
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule.json
@@ -0,0 +1,10 @@
+[
+ {
+ "resource": "resource",
+ "controlBehavior": 0,
+ "count": 1,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ }
+]
diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule.xml b/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule.xml
new file mode 100644
index 00000000..7f1926d3
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-datasource/src/test/resources/flowrule.xml
@@ -0,0 +1,21 @@
+
+
+
+ resource
+ 0
+ 1
+ 1
+ default
+ 0
+
+
+ test
+ 0
+ 1
+ 1
+ default
+ 0
+
+
+
+
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
index 6b3e6e8b..f781a4ab 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
@@ -1,35 +1,29 @@
package org.springframework.cloud.alibaba.sentinel.custom;
-import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
-import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
-import com.alibaba.csp.sentinel.slots.block.AbstractRule;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
-import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.SmartInitializingSingleton;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.cloud.alibaba.sentinel.SentinelConstants;
-import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
-import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourceConstants;
-import org.springframework.cloud.alibaba.sentinel.datasource.config.AbstractDataSourceProperties;
-import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
-import org.springframework.cloud.alibaba.sentinel.datasource.config.NacosDataSourceProperties;
-import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
-import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.ReflectionUtils;
-import org.springframework.util.StringUtils;
-
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.TreeMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.config.AbstractDataSourceProperties;
+import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
+import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
+
+import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
+import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
+import com.alibaba.csp.sentinel.slots.block.AbstractRule;
/**
* Sentinel {@link ReadableDataSource} Handler Handle the configurations of
@@ -62,27 +56,6 @@ public class SentinelDataSourceHandler implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
- // commercialization
- if (!StringUtils.isEmpty(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT))) {
- Map newDataSourceMap = new TreeMap<>(
- String.CASE_INSENSITIVE_ORDER);
- for (Map.Entry entry : sentinelProperties
- .getDatasource().entrySet()) {
- if (entry.getValue().getValidDataSourceProperties()
- .getClass() != NacosDataSourceProperties.class) {
- newDataSourceMap.put(entry.getKey(), entry.getValue());
- }
- }
- newDataSourceMap.put(SentinelConstants.FLOW_DATASOURCE_NAME,
- new DataSourcePropertiesConfiguration(
- NacosDataSourceProperties.buildFlowByEDAS()));
- newDataSourceMap.put(SentinelConstants.DEGRADE_DATASOURCE_NAME,
- new DataSourcePropertiesConfiguration(
- NacosDataSourceProperties.buildDegradeByEDAS()));
- sentinelProperties.setDatasource(newDataSourceMap);
- }
-
sentinelProperties.getDatasource()
.forEach((dataSourceName, dataSourceProperties) -> {
try {
@@ -213,17 +186,6 @@ public class SentinelDataSourceHandler implements SmartInitializingSingleton {
// register property in RuleManager
dataSourceProperties.postRegister(newDataSource);
-
- // commercialization
- if (!StringUtils.isEmpty(System.getProperties()
- .getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT))) {
- if (dataSourceName.contains(SentinelConstants.FLOW_DATASOURCE_NAME)) {
- FlowRuleManager.register2Property(newDataSource.getProperty());
- }
- else if (dataSourceName.contains(SentinelConstants.DEGRADE_DATASOURCE_NAME)) {
- DegradeRuleManager.register2Property(newDataSource.getProperty());
- }
- }
}
private void logAndCheckRuleType(AbstractDataSource dataSource, String dataSourceName,
diff --git a/spring-cloud-alicloud-acm/src/main/java/org/springframework/cloud/alicloud/acm/endpoint/AcmEndpointAutoConfiguration.java b/spring-cloud-alicloud-acm/src/main/java/org/springframework/cloud/alicloud/acm/endpoint/AcmEndpointAutoConfiguration.java
index 86c7fa18..a2f7d312 100644
--- a/spring-cloud-alicloud-acm/src/main/java/org/springframework/cloud/alicloud/acm/endpoint/AcmEndpointAutoConfiguration.java
+++ b/spring-cloud-alicloud-acm/src/main/java/org/springframework/cloud/alicloud/acm/endpoint/AcmEndpointAutoConfiguration.java
@@ -51,6 +51,7 @@ public class AcmEndpointAutoConfiguration {
}
@Bean
+ @ConditionalOnMissingBean
public AcmHealthIndicator acmHealthIndicator(AcmProperties acmProperties,
AcmPropertySourceRepository acmPropertySourceRepository) {
return new AcmHealthIndicator(acmProperties, acmPropertySourceRepository);
diff --git a/spring-cloud-alicloud-ans/pom.xml b/spring-cloud-alicloud-ans/pom.xml
index d1fdf2c9..3aa33c0b 100644
--- a/spring-cloud-alicloud-ans/pom.xml
+++ b/spring-cloud-alicloud-ans/pom.xml
@@ -103,6 +103,18 @@
spring-cloud-test-support
test
+
+ org.powermock
+ powermock-module-junit4
+ 2.0.0
+ test
+
+
+ org.powermock
+ powermock-api-mockito2
+ 2.0.0
+ test
+
diff --git a/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/AnsAutoConfiguration.java b/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/AnsAutoConfiguration.java
index d563632e..d5ab0935 100644
--- a/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/AnsAutoConfiguration.java
+++ b/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/AnsAutoConfiguration.java
@@ -16,7 +16,7 @@
package org.springframework.cloud.alicloud.ans;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -42,8 +42,7 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(name = "org.springframework.boot.web.context.WebServerInitializedEvent")
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@ConditionalOnAnsEnabled
-@AutoConfigureBefore({ AutoServiceRegistrationAutoConfiguration.class,
- AnsDiscoveryClientAutoConfiguration.class })
+@AutoConfigureAfter(AutoServiceRegistrationAutoConfiguration.class)
public class AnsAutoConfiguration {
@Bean
diff --git a/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/registry/AnsRegistration.java b/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/registry/AnsRegistration.java
index cd763fdd..5cbcb732 100644
--- a/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/registry/AnsRegistration.java
+++ b/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/registry/AnsRegistration.java
@@ -34,9 +34,9 @@ import java.util.Map;
*/
public class AnsRegistration implements Registration, ServiceInstance {
- private static final String MANAGEMENT_PORT = "management.port";
- private static final String MANAGEMENT_CONTEXT_PATH = "management.context-path";
- private static final String MANAGEMENT_ADDRESS = "management.address";
+ static final String MANAGEMENT_PORT = "management.port";
+ static final String MANAGEMENT_CONTEXT_PATH = "management.context-path";
+ static final String MANAGEMENT_ADDRESS = "management.address";
private AnsProperties ansProperties;
private ApplicationContext context;
diff --git a/spring-cloud-alicloud-ans/src/main/resources/META-INF/spring.factories b/spring-cloud-alicloud-ans/src/main/resources/META-INF/spring.factories
index f733a74e..63f54329 100644
--- a/spring-cloud-alicloud-ans/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-alicloud-ans/src/main/resources/META-INF/spring.factories
@@ -3,6 +3,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alicloud.ans.ribbon.RibbonAnsAutoConfiguration,\
org.springframework.cloud.alicloud.ans.AnsAutoConfiguration,\
org.springframework.cloud.alicloud.ans.migrate.MigrateEndpointAutoConfiguration,\
- org.springframework.cloud.alicloud.ans.migrate.MigrationAutoconfiguration
-org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
+ org.springframework.cloud.alicloud.ans.migrate.MigrationAutoconfiguration,\
org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration
\ No newline at end of file
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/AnsDiscoveryClientTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/AnsDiscoveryClientTests.java
new file mode 100644
index 00000000..3e02bc3d
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/AnsDiscoveryClientTests.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+import static org.springframework.cloud.alicloud.ans.test.AnsMockTest.hostInstance;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.springframework.cloud.client.ServiceInstance;
+
+import com.alibaba.ans.core.NamingService;
+import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
+
+/**
+ * @author xiaojing
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(NamingService.class)
+public class AnsDiscoveryClientTests {
+
+ private String host = "123.123.123.123";
+ private int port = 8888;
+ private String serviceName = "test-service";
+
+ @Test
+ public void testGetServers() throws Exception {
+
+ ArrayList hosts = new ArrayList<>();
+
+ HashMap map = new HashMap<>();
+ map.put("test-key", "test-value");
+ map.put("secure", "true");
+
+ hosts.add(hostInstance(serviceName, false, host, port, map));
+
+ PowerMockito.mockStatic(NamingService.class);
+ when(NamingService.getHosts(eq(serviceName))).thenReturn(hosts);
+
+ AnsDiscoveryClient discoveryClient = new AnsDiscoveryClient();
+
+ List serviceInstances = discoveryClient
+ .getInstances(serviceName);
+
+ assertThat(serviceInstances.size()).isEqualTo(1);
+
+ ServiceInstance serviceInstance = serviceInstances.get(0);
+
+ assertThat(serviceInstance.getServiceId()).isEqualTo(serviceName);
+ assertThat(serviceInstance.getHost()).isEqualTo(host);
+ assertThat(serviceInstance.getPort()).isEqualTo(port);
+ // assertThat(serviceInstance.isSecure()).isEqualTo(true);
+ // ans doesn't support metadata
+ assertThat(serviceInstance.getUri().toString())
+ .isEqualTo(getUri(serviceInstance));
+ // assertThat(serviceInstance.getMetadata().get("test-key")).isEqualTo("test-value");
+ // ans doesn't support metadata
+
+ }
+
+ @Test
+ public void testGetAllService() throws Exception {
+
+ Set subscribedServices = new HashSet<>();
+
+ subscribedServices.add(serviceName + "1");
+ subscribedServices.add(serviceName + "2");
+ subscribedServices.add(serviceName + "3");
+
+ PowerMockito.mockStatic(NamingService.class);
+ when(NamingService.getDomsSubscribed()).thenReturn(subscribedServices);
+
+ AnsDiscoveryClient discoveryClient = new AnsDiscoveryClient();
+ List services = discoveryClient.getServices();
+
+ assertThat(services.size()).isEqualTo(3);
+ assertThat(services.contains(serviceName + "1"));
+ assertThat(services.contains(serviceName + "2"));
+ assertThat(services.contains(serviceName + "3"));
+
+ }
+
+ private String getUri(ServiceInstance instance) {
+
+ if (instance.isSecure()) {
+ return "https://" + host + ":" + port;
+ }
+
+ return "http://" + host + ":" + port;
+ }
+}
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationIpNetworkInterfaceTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationIpNetworkInterfaceTests.java
new file mode 100644
index 00000000..7a31f30d
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationIpNetworkInterfaceTests.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans.registry;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.Enumeration;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alicloud.ans.AnsAutoConfiguration;
+import org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration;
+import org.springframework.cloud.alicloud.context.ans.AnsProperties;
+import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
+import org.springframework.cloud.commons.util.InetUtils;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author xiaojing
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AnsAutoServiceRegistrationIpNetworkInterfaceTests.TestConfig.class, properties = {
+ "spring.application.name=myTestService1",
+ "spring.cloud.alicloud.ans.server-list=127.0.0.1",
+ "spring.cloud.alicloud.ans.server-port=8080" }, webEnvironment = RANDOM_PORT)
+public class AnsAutoServiceRegistrationIpNetworkInterfaceTests {
+
+ @Autowired
+ private AnsRegistration registration;
+
+ @Autowired
+ private AnsAutoServiceRegistration ansAutoServiceRegistration;
+
+ @Autowired
+ private AnsProperties properties;
+
+ @Autowired
+ private InetUtils inetUtils;
+
+ @Test
+ public void contextLoads() throws Exception {
+
+ assertNotNull("AnsRegistration was not created", registration);
+ assertNotNull("AnsProperties was not created", properties);
+ assertNotNull("AnsAutoServiceRegistration was not created",
+ ansAutoServiceRegistration);
+
+ checkoutAnsDiscoveryServiceIP();
+
+ }
+
+ private void checkoutAnsDiscoveryServiceIP() {
+ assertEquals("AnsProperties service IP was wrong",
+ getIPFromNetworkInterface(TestConfig.netWorkInterfaceName),
+ registration.getHost());
+
+ }
+
+ private String getIPFromNetworkInterface(String networkInterface) {
+
+ if (!TestConfig.hasValidNetworkInterface) {
+ return inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
+ }
+
+ try {
+ NetworkInterface netInterface = NetworkInterface.getByName(networkInterface);
+
+ Enumeration inetAddress = netInterface.getInetAddresses();
+ while (inetAddress.hasMoreElements()) {
+ InetAddress currentAddress = inetAddress.nextElement();
+ if (currentAddress instanceof Inet4Address
+ && !currentAddress.isLoopbackAddress()) {
+ return currentAddress.getHostAddress();
+ }
+ }
+ return networkInterface;
+ }
+ catch (Exception e) {
+ return networkInterface;
+ }
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ AutoServiceRegistrationConfiguration.class,
+ AnsDiscoveryClientAutoConfiguration.class, AnsAutoConfiguration.class })
+ public static class TestConfig {
+
+ static boolean hasValidNetworkInterface = false;
+ static String netWorkInterfaceName;
+
+ static {
+
+ try {
+ Enumeration enumeration = NetworkInterface
+ .getNetworkInterfaces();
+ while (enumeration.hasMoreElements() && !hasValidNetworkInterface) {
+ NetworkInterface networkInterface = enumeration.nextElement();
+ Enumeration inetAddress = networkInterface
+ .getInetAddresses();
+ while (inetAddress.hasMoreElements()) {
+ InetAddress currentAddress = inetAddress.nextElement();
+ if (currentAddress instanceof Inet4Address
+ && !currentAddress.isLoopbackAddress()) {
+ hasValidNetworkInterface = true;
+ netWorkInterfaceName = networkInterface.getName();
+ System.setProperty(
+ "spring.cloud.alicloud.ans.client-interface-name",
+ networkInterface.getName());
+ break;
+ }
+ }
+ }
+
+ }
+ catch (Exception e) {
+
+ }
+ }
+ }
+
+}
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationIpTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationIpTests.java
new file mode 100644
index 00000000..53d7b5c2
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationIpTests.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans.registry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alicloud.ans.AnsAutoConfiguration;
+import org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration;
+import org.springframework.cloud.alicloud.context.ans.AnsProperties;
+import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author xiaojing
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AnsAutoServiceRegistrationIpTests.TestConfig.class, properties = {
+ "spring.application.name=myTestService1",
+ "spring.cloud.alicloud.ans.server-list=127.0.0.1",
+ "spring.cloud.alicloud.ans.server-port=8080",
+ "spring.cloud.alicloud.ans.client-ip=123.123.123.123" }, webEnvironment = RANDOM_PORT)
+public class AnsAutoServiceRegistrationIpTests {
+
+ @Autowired
+ private AnsRegistration registration;
+
+ @Autowired
+ private AnsAutoServiceRegistration ansAutoServiceRegistration;
+
+ @Autowired
+ private AnsProperties properties;
+
+ @Test
+ public void contextLoads() throws Exception {
+
+ assertNotNull("AnsRegistration was not created", registration);
+ assertNotNull("AnsProperties was not created", properties);
+ assertNotNull("AnsAutoServiceRegistration was not created",
+ ansAutoServiceRegistration);
+
+ checkoutAnsDiscoveryServiceIP();
+
+ }
+
+ private void checkoutAnsDiscoveryServiceIP() {
+ assertEquals("AnsProperties service IP was wrong", "123.123.123.123",
+ registration.getHost());
+
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ AutoServiceRegistrationConfiguration.class,
+ AnsDiscoveryClientAutoConfiguration.class, AnsAutoConfiguration.class })
+ public static class TestConfig {
+ }
+}
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationManagementPortTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationManagementPortTests.java
new file mode 100644
index 00000000..a73f25c2
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationManagementPortTests.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans.registry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+import static org.springframework.cloud.alicloud.ans.registry.AnsRegistration.MANAGEMENT_CONTEXT_PATH;
+import static org.springframework.cloud.alicloud.ans.registry.AnsRegistration.MANAGEMENT_PORT;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alicloud.ans.AnsAutoConfiguration;
+import org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration;
+import org.springframework.cloud.alicloud.context.ans.AnsProperties;
+import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author xiaojing
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AnsAutoServiceRegistrationManagementPortTests.TestConfig.class, properties = {
+ "spring.application.name=myTestService1", "management.server.port=8888",
+ "management.server.servlet.context-path=/test-context-path",
+ "spring.cloud.alicloud.ans.server-list=127.0.0.1",
+ "spring.cloud.alicloud.ans.server-port=8080" }, webEnvironment = RANDOM_PORT)
+public class AnsAutoServiceRegistrationManagementPortTests {
+
+ @Autowired
+ private AnsRegistration registration;
+
+ @Autowired
+ private AnsAutoServiceRegistration ansAutoServiceRegistration;
+
+ @Autowired
+ private AnsProperties properties;
+
+ @Test
+ public void contextLoads() throws Exception {
+
+ assertNotNull("AnsRegistration was not created", registration);
+ assertNotNull("AnsProperties was not created", properties);
+ assertNotNull("AnsAutoServiceRegistration was not created",
+ ansAutoServiceRegistration);
+
+ checkoutNacosDiscoveryManagementData();
+
+ }
+
+ private void checkoutNacosDiscoveryManagementData() {
+ assertEquals("AnsProperties management port was wrong", "8888",
+ properties.getClientMetadata().get(MANAGEMENT_PORT));
+
+ assertEquals("AnsProperties management context path was wrong",
+ "/test-context-path",
+ properties.getClientMetadata().get(MANAGEMENT_CONTEXT_PATH));
+
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ AutoServiceRegistrationConfiguration.class,
+ AnsDiscoveryClientAutoConfiguration.class, AnsAutoConfiguration.class })
+ public static class TestConfig {
+ }
+}
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationPortTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationPortTests.java
new file mode 100644
index 00000000..20e03eb4
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationPortTests.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans.registry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alicloud.ans.AnsAutoConfiguration;
+import org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration;
+import org.springframework.cloud.alicloud.context.ans.AnsProperties;
+import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author xiaojing
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AnsAutoServiceRegistrationPortTests.TestConfig.class, properties = {
+ "spring.application.name=myTestService1",
+ "spring.cloud.alicloud.ans.server-list=127.0.0.1",
+ "spring.cloud.alicloud.ans.server-port=8080",
+ "spring.cloud.alicloud.ans.client-port=8888" }, webEnvironment = RANDOM_PORT)
+public class AnsAutoServiceRegistrationPortTests {
+
+ @Autowired
+ private AnsRegistration registration;
+
+ @Autowired
+ private AnsAutoServiceRegistration ansAutoServiceRegistration;
+
+ @Autowired
+ private AnsProperties properties;
+
+ @Test
+ public void contextLoads() throws Exception {
+
+ assertNotNull("AnsRegistration was not created", registration);
+ assertNotNull("AnsDiscoveryProperties was not created", properties);
+ assertNotNull("AnsAutoServiceRegistration was not created",
+ ansAutoServiceRegistration);
+
+ checkoutAnsDiscoveryServicePort();
+
+ }
+
+ private void checkoutAnsDiscoveryServicePort() {
+ assertEquals("AnsDiscoveryProperties service Port was wrong", 8888,
+ registration.getPort());
+
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ AutoServiceRegistrationConfiguration.class,
+ AnsDiscoveryClientAutoConfiguration.class, AnsAutoConfiguration.class })
+ public static class TestConfig {
+ }
+}
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationTests.java
new file mode 100644
index 00000000..93381408
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/registry/AnsAutoServiceRegistrationTests.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans.registry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.ans.core.NamingService;
+import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.cloud.alicloud.ans.AnsAutoConfiguration;
+import org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration;
+import org.springframework.cloud.alicloud.ans.endpoint.AnsEndpoint;
+import org.springframework.cloud.alicloud.context.ans.AnsProperties;
+import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
+import org.springframework.cloud.commons.util.InetUtils;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author xiaojing
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AnsAutoServiceRegistrationTests.TestConfig.class, properties = {
+ "spring.application.name=myTestService1",
+ "spring.cloud.alicloud.ans.server-list=127.0.0.1",
+ "spring.cloud.alicloud.ans.server-port=8080",
+ "spring.cloud.alicloud.ans.endpoint=test-endpoint" }, webEnvironment = RANDOM_PORT)
+public class AnsAutoServiceRegistrationTests {
+
+ @Autowired
+ private AnsRegistration registration;
+
+ @Autowired
+ private AnsAutoServiceRegistration ansAutoServiceRegistration;
+
+ @LocalServerPort
+ private int port;
+
+ @Autowired
+ private AnsProperties properties;
+
+ @Autowired
+ private InetUtils inetUtils;
+
+ @Test
+ public void contextLoads() throws Exception {
+
+ assertNotNull("AnsRegistration was not created", registration);
+ assertNotNull("AnsProperties was not created", properties);
+ assertNotNull("AnsAutoServiceRegistration was not created",
+ ansAutoServiceRegistration);
+
+ checkoutAnsDiscoveryServerList();
+ checkoutAnsDiscoveryServerPort();
+
+ checkoutAnsDiscoveryServiceName();
+ checkoutAnsDiscoveryServiceIP();
+ checkoutAnsDiscoveryServicePort();
+
+ checkAutoRegister();
+
+ checkoutEndpoint();
+
+ }
+
+ private void checkAutoRegister() {
+ assertTrue("Ans Auto Registration was not start",
+ ansAutoServiceRegistration.isRunning());
+ }
+
+ private void checkoutAnsDiscoveryServerList() {
+ assertEquals("AnsDiscoveryProperties server list was wrong", "127.0.0.1",
+ properties.getServerList());
+
+ }
+
+ private void checkoutAnsDiscoveryServerPort() {
+ assertEquals("AnsDiscoveryProperties server port was wrong", "8080",
+ properties.getServerPort());
+
+ }
+
+ private void checkoutAnsDiscoveryServiceName() {
+ assertEquals("AnsDiscoveryProperties service name was wrong", "myTestService1",
+ properties.getClientDomains());
+
+ }
+
+ private void checkoutAnsDiscoveryServiceIP() {
+ assertEquals("AnsDiscoveryProperties service IP was wrong",
+ inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(),
+ registration.getHost());
+
+ }
+
+ private void checkoutAnsDiscoveryServicePort() {
+ assertEquals("AnsDiscoveryProperties service Port was wrong", port,
+ registration.getPort());
+
+ }
+
+ private void checkoutEndpoint() throws Exception {
+ AnsEndpoint ansEndpoint = new AnsEndpoint(properties);
+ Map map = ansEndpoint.invoke();
+ assertEquals(map.get("ansProperties"), properties);
+
+ Map subscribes = new HashMap<>();
+ Set subscribeServices = NamingService.getDomsSubscribed();
+ for (String service : subscribeServices) {
+ try {
+ List hosts = NamingService.getHosts(service);
+ subscribes.put(service, hosts);
+ }
+ catch (Exception ignoreException) {
+
+ }
+ }
+
+ assertEquals(map.get("subscribes"), subscribes);
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ AutoServiceRegistrationConfiguration.class,
+ AnsDiscoveryClientAutoConfiguration.class, AnsAutoConfiguration.class })
+ public static class TestConfig {
+ }
+}
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsRibbonClientConfigurationTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsRibbonClientConfigurationTests.java
new file mode 100644
index 00000000..beb825c7
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsRibbonClientConfigurationTests.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans.ribbon;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alicloud.ans.AnsAutoConfiguration;
+import org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+
+import com.netflix.client.config.DefaultClientConfigImpl;
+import com.netflix.client.config.IClientConfig;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+/**
+ * @author xiaojing
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AnsRibbonClientConfigurationTests.TestConfig.class, properties = {
+ "spring.application.name=myTestService1",
+ "spring.cloud.alicloud.ans.server-list=127.0.0.1",
+ "spring.cloud.alicloud.ans.server-port=8080",
+ "spring.cloud.alicloud.ans.endpoint=test-endpoint" }, webEnvironment = RANDOM_PORT)
+public class AnsRibbonClientConfigurationTests {
+
+ @Autowired
+ private AnsServerList serverList;
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertThat(serverList.getDom()).isEqualTo("myapp");
+ }
+
+ @Configuration
+ public static class AnsRibbonTestConfiguration {
+
+ @Bean
+ IClientConfig iClientConfig() {
+ DefaultClientConfigImpl config = new DefaultClientConfigImpl();
+ config.setClientName("myapp");
+ return config;
+ }
+
+ @Bean
+ @LoadBalanced
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ AutoServiceRegistrationConfiguration.class,
+ AnsDiscoveryClientAutoConfiguration.class, AnsAutoConfiguration.class,
+ AnsRibbonTestConfiguration.class, RibbonAnsAutoConfiguration.class,
+ AnsRibbonClientConfiguration.class })
+ public static class TestConfig {
+ }
+
+}
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsServerListTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsServerListTests.java
new file mode 100644
index 00000000..815fa89d
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsServerListTests.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans.ribbon;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.springframework.cloud.alicloud.ans.test.AnsMockTest.hostInstance;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.alibaba.ans.core.NamingService;
+import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
+
+import com.netflix.client.config.IClientConfig;
+
+/**
+ * @author xiaojing
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ NamingService.class, AnsServer.class })
+public class AnsServerListTests {
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testEmptyInstancesReturnsEmptyList() throws Exception {
+
+ PowerMockito.mockStatic(NamingService.class);
+ when(NamingService.getHosts(anyString())).thenReturn(Collections.EMPTY_LIST);
+
+ IClientConfig clientConfig = mock(IClientConfig.class);
+ when(clientConfig.getClientName()).thenReturn("test-service");
+ AnsServerList serverList = new AnsServerList("test-service");
+ serverList.initWithNiwsConfig(clientConfig);
+ List servers = serverList.getInitialListOfServers();
+ assertThat(servers).isEmpty();
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testGetServers() throws Exception {
+
+ ArrayList hosts = new ArrayList<>();
+ hosts.add(hostInstance("test-service", true, Collections.emptyMap()));
+
+ PowerMockito.mockStatic(NamingService.class);
+ when(NamingService.getHosts(anyString())).thenReturn(hosts);
+ PowerMockito.stub(PowerMockito.method(AnsServer.class, "isAlive", long.class))
+ .toReturn(true);
+
+ IClientConfig clientConfig = mock(IClientConfig.class);
+ when(clientConfig.getClientName()).thenReturn("test-service");
+ AnsServerList serverList = new AnsServerList("test-service");
+ serverList.initWithNiwsConfig(clientConfig);
+ List servers = serverList.getInitialListOfServers();
+ assertThat(servers).hasSize(1);
+
+ servers = serverList.getUpdatedListOfServers();
+ assertThat(servers).hasSize(1);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testGetServersWithInstanceStatus() throws Exception {
+ ArrayList hosts = new ArrayList<>();
+
+ HashMap map1 = new HashMap<>();
+ map1.put("instanceNum", "1");
+ HashMap map2 = new HashMap<>();
+ map2.put("instanceNum", "2");
+ hosts.add(hostInstance("test-service", false, map1));
+ hosts.add(hostInstance("test-service", true, map2));
+
+ PowerMockito.mockStatic(NamingService.class);
+ when(NamingService.getHosts(eq("test-service"))).thenReturn(
+ hosts.stream().filter(Host::isValid).collect(Collectors.toList()));
+
+ PowerMockito.stub(PowerMockito.method(AnsServer.class, "isAlive", long.class))
+ .toReturn(true);
+
+ IClientConfig clientConfig = mock(IClientConfig.class);
+ when(clientConfig.getClientName()).thenReturn("test-service");
+ AnsServerList serverList = new AnsServerList("test-service");
+ serverList.initWithNiwsConfig(clientConfig);
+ List servers = serverList.getInitialListOfServers();
+ assertThat(servers).hasSize(1);
+
+ AnsServer ansServer = servers.get(0);
+ Host host = ansServer.getHealthService();
+
+ assertThat(ansServer.getMetaInfo().getInstanceId()).isEqualTo(
+ host.getIp() + ":" + host.getHostname() + ":" + host.getPort());
+ assertThat(ansServer.getHealthService().isValid()).isEqualTo(true);
+ assertThat(ansServer.getHealthService().getHostname()).isEqualTo("test-service");
+
+ }
+
+ @Test
+ public void testUpdateServers() throws Exception {
+ ArrayList hosts = new ArrayList<>();
+
+ HashMap map = new HashMap<>();
+ map.put("instanceNum", "1");
+ hosts.add(hostInstance("test-service", true, map));
+
+ PowerMockito.mockStatic(NamingService.class);
+ when(NamingService.getHosts(eq("test-service"))).thenReturn(
+ hosts.stream().filter(Host::isValid).collect(Collectors.toList()));
+ PowerMockito.stub(PowerMockito.method(AnsServer.class, "isAlive", long.class))
+ .toReturn(true);
+
+ IClientConfig clientConfig = mock(IClientConfig.class);
+ when(clientConfig.getClientName()).thenReturn("test-service");
+ AnsServerList serverList = new AnsServerList("test-service");
+ serverList.initWithNiwsConfig(clientConfig);
+
+ List servers = serverList.getUpdatedListOfServers();
+ assertThat(servers).hasSize(1);
+
+ assertThat(servers.get(0).getHealthService().isValid()).isEqualTo(true);
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alibaba/ans/ribbon/AnsServiceListTests.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsServiceListTests.java
similarity index 92%
rename from spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alibaba/ans/ribbon/AnsServiceListTests.java
rename to spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsServiceListTests.java
index 5cb38532..3e3d59e4 100644
--- a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alibaba/ans/ribbon/AnsServiceListTests.java
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/ribbon/AnsServiceListTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.alibaba.ans.ribbon;
+package org.springframework.cloud.alicloud.ans.ribbon;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.given;
@@ -24,8 +24,6 @@ import java.util.Arrays;
import java.util.List;
import org.junit.Test;
-import org.springframework.cloud.alicloud.ans.ribbon.AnsServer;
-import org.springframework.cloud.alicloud.ans.ribbon.AnsServerList;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import com.netflix.loadbalancer.Server;
diff --git a/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/test/AnsMockTest.java b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/test/AnsMockTest.java
new file mode 100644
index 00000000..795f2b49
--- /dev/null
+++ b/spring-cloud-alicloud-ans/src/test/java/org/springframework/cloud/alicloud/ans/test/AnsMockTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 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
+ *
+ * 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.springframework.cloud.alicloud.ans.test;
+
+import java.util.Map;
+
+import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
+
+/**
+ * @author xiaojing
+ */
+public class AnsMockTest {
+
+ public static Host hostInstance(String serviceName, boolean valid,
+ Map metadata) {
+ Host host = new Host();
+ host.setHostname(serviceName);
+ host.setValid(valid);
+ return host;
+ }
+
+ public static Host hostInstance(String serviceName, boolean valid, String ip,
+ int port, Map metadata) {
+ Host host = new Host();
+ host.setIp(ip);
+ host.setPort(port);
+ host.setValid(valid);
+ host.setHostname(serviceName);
+ return host;
+ }
+}
diff --git a/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/ans/AnsProperties.java b/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/ans/AnsProperties.java
index 2852ff57..1952a524 100644
--- a/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/ans/AnsProperties.java
+++ b/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/ans/AnsProperties.java
@@ -42,84 +42,94 @@ import com.alibaba.cloud.context.ans.AnsConfiguration;
public class AnsProperties implements AnsConfiguration {
/**
- * 服务端模式,默认为LOCAL
+ * Server side mode,the default is LOCAL.
*/
private AliCloudServerMode serverMode = AliCloudServerMode.LOCAL;
/**
- * 服务端列表
+ * Server list.
*/
private String serverList = "127.0.0.1";
/**
- * 服务端列表
+ * Server port.
*/
private String serverPort = "8080";
/**
- * 注册的服务名,默认从 spring.cloud.alicloud.ans.doms 中获取,当没有配置时,使用 spring.application.name
+ * Service names,default value is ${spring.cloud.alicloud.ans.doms}. When not
+ * configured, use ${spring.application.name}.
*/
@Value("${spring.cloud.alicloud.ans.client-domains:${spring.application.name:}}")
private String clientDomains;
/**
- * 注册服务的权重,从配置 spring.cloud.alicloud.ans.weight 中获取,默认为 1
+ * The weight of the registration service, obtained from the configuration
+ * ${spring.cloud.alicloud.ans.weight}, the default is 1.
*/
private float clientWeight = 1;
/**
- * 当存在多个doms,需要对应不同的 weight 时,通过 spring.cloud.alicloud.ans.weight.dom1=weight1 的方式配置
+ * When there are multiple doms and need to correspond to different weights, configure
+ * them by spring.cloud.alicloud.ans.weight.dom1=weight1.
*/
private Map clientWeights = new HashMap();
/**
- * 注册服务的 token ,从 spring.cloud.alicloud.ans.token 中获取
+ * The token of the registration service, obtained from
+ * ${spring.cloud.alicloud.ans.token}.
*/
private String clientToken;
/**
- * 当存在多个doms,需要对应不同的token时,通过 spring.cloud.alicloud.ans.tokens.dom1=token1 的方式配置
+ * When there are multiple doms and need to correspond to different tokens, configure
+ * them by spring.cloud.alicloud.ans.tokens.dom1=token1.
*/
private Map clientTokens = new HashMap();
/**
- * 配置注册到哪个集群,从 spring.cloud.alicloud.ans.cluster 中获取,默认为 DEFAULT
+ * Configure which cluster to register with, obtained from
+ * ${spring.cloud.alicloud.ans.cluster}, defaults to DEFAULT.
*/
private String clientCluster = "DEFAULT";
/**
- * metadata 实现 serviceInstance 接口所需的字段,但 ans 目前尚不支持此字段,配置了也没用
+ * Temporarily not supported, reserved fields.
*/
private Map clientMetadata = new HashMap<>();
/**
- * 默认打开注册,可以通过 spring.cloud.alicloud.ans.register-enabled=false 的配置来关闭注册
+ * Registration is turned on by default, and registration can be turned off by the
+ * configuration of spring.cloud.alicloud.ans.register-enabled=false.
*/
private boolean registerEnabled = true;
/**
- * 想要发布的服务的ip,从 spring.cloud.alicloud.ans.client-ip 中获取
+ * The ip of the service you want to publish, obtained from
+ * ${spring.cloud.alicloud.ans.client-ip}.
*/
private String clientIp;
/**
- * 想要发布的服务的ip从哪一块网卡中获取
+ * Configure which NIC the ip of the service you want to publish is obtained from.
*/
private String clientInterfaceName;
/**
- * 想要发布的服务的端口,从 spring.cloud.alicloud.ans.port 中获取
+ * The port of the service you want to publish.
*/
private int clientPort = -1;
/**
- * 租户下的环境隔离配置,相同租户的相同环境下的服务才能互相发现
+ * The environment isolation configuration under the tenant, the services in the same
+ * environment of the same tenant can discover each other.
*/
@Value("${spring.cloud.alicloud.ans.env:${env.id:DEFAULT}}")
private String env;
/**
- * 是否注册成 https 的形式,通过 spring.cloud.alicloud.ans.secure 来配置,默认为false
+ * Whether to register as https, configured by ${spring.cloud.alicloud.ans.secure},
+ * default is false.
*/
private boolean secure = false;
@@ -131,11 +141,10 @@ public class AnsProperties implements AnsConfiguration {
@PostConstruct
public void init() throws SocketException {
- // 增加注册类型,标记为 spring cloud 应用
+ // Marked as spring cloud application
tags.put("ANS_SERVICE_TYPE", "SPRING_CLOUD");
if (StringUtils.isEmpty(clientIp)) {
- // 如果没有指定注册的ip对应的网卡名,则通过遍历网卡去获取
if (StringUtils.isEmpty(clientInterfaceName)) {
clientIp = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
}
diff --git a/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/nacos/NacosParameterInitListener.java b/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/nacos/NacosParameterInitListener.java
index 73dcc75d..496dd5a5 100644
--- a/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/nacos/NacosParameterInitListener.java
+++ b/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/nacos/NacosParameterInitListener.java
@@ -27,12 +27,13 @@ public class NacosParameterInitListener
EdasChangeOrderConfiguration edasChangeOrderConfiguration = EdasChangeOrderConfigurationFactory
.getEdasChangeOrderConfiguration();
- log.info("Initialize Nacos Parameter from edas change order,is edas managed {}.",
- edasChangeOrderConfiguration.isEdasManaged());
-
if (!edasChangeOrderConfiguration.isEdasManaged()) {
return;
}
+
+ log.info("Initialize Nacos Parameter from edas change order,is edas managed {}.",
+ edasChangeOrderConfiguration.isEdasManaged());
+
// initialize nacos configuration
System.getProperties().setProperty("spring.cloud.nacos.config.server-addr", "");
System.getProperties().setProperty("spring.cloud.nacos.config.endpoint",
diff --git a/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/OssApplicationListener.java b/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/OssApplicationListener.java
index 23120e38..fa0473d6 100644
--- a/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/OssApplicationListener.java
+++ b/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/OssApplicationListener.java
@@ -26,22 +26,23 @@ import org.springframework.context.event.ContextClosedEvent;
import com.aliyun.oss.OSS;
/**
- * Shutdown All OSS Clients when {@code ApplicationContext} gets closed {@link ApplicationListener}
+ * Shutdown All OSS Clients when {@code ApplicationContext} gets closed
+ * {@link ApplicationListener}
*
* @author Jim
*/
public class OssApplicationListener implements ApplicationListener {
- private static final Logger logger = LoggerFactory
+ private static final Logger log = LoggerFactory
.getLogger(OssApplicationListener.class);
@Override
public void onApplicationEvent(ContextClosedEvent event) {
Map ossClientMap = event.getApplicationContext()
.getBeansOfType(OSS.class);
- logger.info("{} OSSClients will be shutdown soon", ossClientMap.size());
+ log.info("{} OSSClients will be shutdown soon", ossClientMap.size());
ossClientMap.keySet().forEach(beanName -> {
- logger.info("shutdown ossClient: {}", beanName);
+ log.info("shutdown ossClient: {}", beanName);
ossClientMap.get(beanName).shutdown();
});
}
diff --git a/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/endpoint/OssEndpointAutoConfiguration.java b/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/endpoint/OssEndpointAutoConfiguration.java
index 0378dd4f..aec1cae9 100644
--- a/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/endpoint/OssEndpointAutoConfiguration.java
+++ b/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/endpoint/OssEndpointAutoConfiguration.java
@@ -34,7 +34,7 @@ public class OssEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
- public OssEndpoint sentinelEndPoint() {
+ public OssEndpoint ossEndpoint() {
return new OssEndpoint();
}
diff --git a/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/resource/OssStorageProtocolResolver.java b/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/resource/OssStorageProtocolResolver.java
index 681f3e88..b062e644 100644
--- a/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/resource/OssStorageProtocolResolver.java
+++ b/spring-cloud-alicloud-oss/src/main/java/org/springframework/cloud/alicloud/oss/resource/OssStorageProtocolResolver.java
@@ -39,7 +39,7 @@ public class OssStorageProtocolResolver
public static final String PROTOCOL = "oss://";
- private static final Logger logger = LoggerFactory
+ private static final Logger log = LoggerFactory
.getLogger(OssStorageProtocolResolver.class);
private ConfigurableListableBeanFactory beanFactory;
@@ -49,8 +49,9 @@ public class OssStorageProtocolResolver
private OSS getOSS() {
if (this.oss == null) {
if (this.beanFactory.getBeansOfType(OSS.class).size() > 1) {
- logger.warn(
- "There are multiple OSS instances, consider marking one of them as @Primary to resolve oss protocol.");
+ log.warn(
+ "There are multiple OSS instances, consider marking one of them as @Primary to resolve oss "
+ + "protocol.");
}
this.oss = this.beanFactory.getBean(OSS.class);
}
@@ -71,7 +72,7 @@ public class OssStorageProtocolResolver
((DefaultResourceLoader) resourceLoader).addProtocolResolver(this);
}
else {
- logger.warn("The provided delegate resource loader is not an implementation "
+ log.warn("The provided delegate resource loader is not an implementation "
+ "of DefaultResourceLoader. Custom Protocol using oss:// prefix will not be enabled.");
}
}
diff --git a/spring-cloud-alicloud-schedulerx/pom.xml b/spring-cloud-alicloud-schedulerx/pom.xml
index e3c1b8e8..04b948a5 100644
--- a/spring-cloud-alicloud-schedulerx/pom.xml
+++ b/spring-cloud-alicloud-schedulerx/pom.xml
@@ -38,6 +38,12 @@
provided
true
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ provided
+ true
+
org.springframework.boot
spring-boot-actuator
diff --git a/spring-cloud-alicloud-schedulerx/src/main/java/org/springframework/cloud/alicloud/scx/endpoint/ScxEndpointAutoConfiguration.java b/spring-cloud-alicloud-schedulerx/src/main/java/org/springframework/cloud/alicloud/scx/endpoint/ScxEndpointAutoConfiguration.java
index 9ca280b5..46d749d2 100644
--- a/spring-cloud-alicloud-schedulerx/src/main/java/org/springframework/cloud/alicloud/scx/endpoint/ScxEndpointAutoConfiguration.java
+++ b/spring-cloud-alicloud-schedulerx/src/main/java/org/springframework/cloud/alicloud/scx/endpoint/ScxEndpointAutoConfiguration.java
@@ -16,8 +16,10 @@
package org.springframework.cloud.alicloud.scx.endpoint;
+import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.cloud.alicloud.context.edas.EdasProperties;
import org.springframework.cloud.alicloud.context.scx.ScxProperties;
@@ -30,8 +32,12 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnClass(Endpoint.class)
public class ScxEndpointAutoConfiguration {
- @Bean
- public ScxEndpoint scxEndpoint(EdasProperties edasProperties, ScxProperties scxProperties) {
- return new ScxEndpoint(edasProperties, scxProperties);
- }
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnEnabledEndpoint
+ public ScxEndpoint scxEndpoint(EdasProperties edasProperties,
+ ScxProperties scxProperties) {
+ return new ScxEndpoint(edasProperties, scxProperties);
+ }
+
}
diff --git a/spring-cloud-starter-alibaba/pom.xml b/spring-cloud-starter-alibaba/pom.xml
index 3c91f8d7..c0de3644 100644
--- a/spring-cloud-starter-alibaba/pom.xml
+++ b/spring-cloud-starter-alibaba/pom.xml
@@ -16,6 +16,7 @@
spring-cloud-starter-alibaba-nacos-config-server
spring-cloud-starter-alibaba-nacos-discovery
spring-cloud-starter-alibaba-sentinel
+ spring-cloud-starter-alibaba-fescar
spring-cloud-starter-stream-rocketmq
spring-cloud-starter-bus-rocketmq
diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml
new file mode 100644
index 00000000..8376bfc1
--- /dev/null
+++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml
@@ -0,0 +1,20 @@
+
+ 4.0.0
+
+
+ org.springframework.cloud
+ spring-cloud-starter-alibaba
+ 0.2.2.BUILD-SNAPSHOT
+
+ spring-cloud-starter-alibaba-fescar
+ Spring Cloud Starter Alibaba Fescar
+
+
+
+ org.springframework.cloud
+ spring-cloud-alibaba-fescar
+
+
+
+