Merge remote-tracking branch 'upstream/master'

pull/279/head
fangjian0423
commit 490d02af7e

@ -10,23 +10,36 @@ Spring Cloud Alibaba 致力于提供分布式应用服务开发的一站式解
## 已包含的组件
**Sentinel**
阿里巴巴开源产品,把流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。
**Nacos**
阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
**Alibaba Cloud OSS**
阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
## 即将加入的组件
**Dubbo**
Apache Dubbo™ (incubating) 是一款高性能Java RPC框架。
阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
**RocketMQ**
Apache RocketMQ™ 基于Java的高性能、高吞吐量的分布式消息和流计算平台。
**Alibaba Cloud Schedulerx**
阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
## 即将加入的组件
**Dubbo**
Apache Dubbo™ (incubating) 是一款高性能Java RPC框架。
**Fescar**
阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
**Alibaba Cloud SLS**
针对日志类数据的一站式服务,在阿里巴巴集团经历大量大数据场景锤炼而成。您无需开发就能快捷完成日志数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建立 DT 时代海量日志处理能力。
针对日志类数据的一站式服务,在阿里巴巴集团经历大量大数据场景锤炼而成。您无需开发就能快捷完成日志数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建立 DT 时代海量日志处理能力。

@ -20,20 +20,28 @@ An open-source project of Alibaba, Sentinel takes "flow" as breakthrough point,
An opensource project of Alibaba, an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
**RocketMQ**
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.
**RocketMQ**
Apache RocketMQ™ is an open source distributed messaging and streaming data platform.
**Fescar**
**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.
A distributed transaction solution with high performance and ease of use for microservices architecture.
**Alibaba Cloud SLS**
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.
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.

@ -101,6 +101,7 @@
<module>spring-cloud-alicloud-acm</module>
<module>spring-cloud-alicloud-ans</module>
<module>spring-cloud-alicloud-schedulerx</module>
<module>spring-cloud-alicloud-sms</module>
</modules>
<dependencyManagement>
@ -188,6 +189,7 @@
<format>html</format>
<format>xml</format>
</formats>
<check/>
<check />
<instrumentation>
<excludes>

@ -28,6 +28,10 @@
<aliyun.sdk.edas.version>2.16.0</aliyun.sdk.edas.version>
<rocketmq.version>4.3.1</rocketmq.version>
<schedulerX.client.version>2.1.6</schedulerX.client.version>
<aliyun.java.sdk.dysmsapi>1.1.0</aliyun.java.sdk.dysmsapi>
<aliyun.sdk.mns>1.1.8</aliyun.sdk.mns>
<aliyun.java.sdk.dysmsapi>1.1.0</aliyun.java.sdk.dysmsapi>
<aliyun.java.sdk.dyvmsapi>1.1.1</aliyun.java.sdk.dyvmsapi>
</properties>
<dependencyManagement>
@ -71,6 +75,19 @@
<version>${schedulerX.client.version}</version>
</dependency>
<!-- SMS -->
<dependency>
<groupId>com.aliyun.mns</groupId>
<artifactId>aliyun-sdk-mns</artifactId>
<version>${aliyun.sdk.mns}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>${aliyun.java.sdk.dysmsapi}</version>
</dependency>
<!--Nacos-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
@ -213,6 +230,11 @@
<artifactId>spring-cloud-alicloud-schedulerx</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-sms</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-context</artifactId>
@ -283,6 +305,13 @@
<version>${project.version}</version>
</dependency>
<!-- SMS -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-sms</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Testing Dependencies -->
</dependencies>

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>alibaba.com</groupId>
<artifactId>env-extension</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<name>env-extension</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,12 @@
package org.springframework.alicloud.env.extension;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ImportExtraConfig {
String[] name() default "";
}

@ -0,0 +1,61 @@
/*
* 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.alicloud.env.extension;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.PropertiesPropertySource;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* @author pbting
* @date 2019-01-09 9:00 PM
*/
public class LoadExtraConfigApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
SpringApplication springApplication = event.getSpringApplication();
Class clazz = springApplication.getMainApplicationClass();
if (!clazz.isAnnotationPresent(ImportExtraConfig.class)) {
return;
}
ImportExtraConfig annotation = (ImportExtraConfig) clazz
.getAnnotation(ImportExtraConfig.class);
String[] extraConfig = annotation.name();
if (extraConfig == null || extraConfig.length == 0) {
return;
}
for (String config : extraConfig) {
try {
Properties properties = new Properties();
properties.load(new FileInputStream(config));
event.getEnvironment().getPropertySources()
.addFirst(new PropertiesPropertySource(config, properties));
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}

@ -0,0 +1,2 @@
org.springframework.context.ApplicationListener=\
org.springframework.alicloud.env.extension.LoadExtraConfigApplicationListener

@ -23,6 +23,7 @@
<module>sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api</module>
<module>nacos-example/nacos-discovery-example</module>
<module>nacos-example/nacos-config-example</module>
<module>env-extension</module>
<module>oss-example</module>
<module>ans-example/ans-consumer-feign-example</module>
<module>ans-example/ans-consumer-ribbon-example</module>

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>alibaba.com</groupId>
<artifactId>sms-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sms-example</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR2</spring-cloud.version>
<spring-cloud-alibaba-alicloud.version>0.2.2.BUILD-SNAPSHOT</spring-cloud-alibaba-alicloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba-alicloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-sms</artifactId>
</dependency>
<dependency>
<groupId>alibaba.com</groupId>
<artifactId>env-extension</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -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.example;
import org.springframework.alicloud.env.extension.ImportExtraConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
*/
@SpringBootApplication
@ImportExtraConfig(name = "/Users/toava/sms.properties")
public class SmsApplication {
public static void main(String[] args) throws Exception{
SpringApplication.run(SmsApplication.class, args);
}
}

@ -0,0 +1,139 @@
package org.springframework.cloud.alibaba.cloud.example;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alicloud.sms.ISmsService;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.aliyun.mns.model.Message;
import com.aliyuncs.dysmsapi.model.v20170525.*;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
@RestController
public class SmsController {
@Autowired
private Environment environment;
@Autowired
private ISmsService smsService;
@Autowired
private SmsReportMessageListener smsReportMessageListener;
@GetMapping("/report-queue.do")
public String getSmsReportQueuename(){
return environment.getProperty("spring.cloud.alicloud.sms.up-queue-name");
}
/**
* Example
* @param code
* @return
*/
@RequestMapping("/batch-sms-send.do")
public SendBatchSmsResponse batchsendCheckCode(
@RequestParam(name = "code") String code) {
// 组装请求对象
SendBatchSmsRequest request = new SendBatchSmsRequest();
// 使用post提交
request.setMethod(MethodType.GET);
// 必填:待发送手机号。支持JSON格式的批量调用批量上限为100个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
request.setPhoneNumberJson("[\"177********\",\"130********\"]");
// 必填:短信签名-支持不同的号码发送不同的短信签名
request.setSignNameJson("[\"*******\",\"*******\"]");
// 必填:短信模板-可在短信控制台中找到
request.setTemplateCode("******");
// 必填:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
// 友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
request.setTemplateParamJson(
"[{\"code\":\"" + code + "\"},{\"code\":\"" + code + "\"}]");
// 可选-上行短信扩展码(扩展码字段控制在7位或以下无特殊需求用户请忽略此字段)
// request.setSmsUpExtendCodeJson("[\"90997\",\"90998\"]");
try {
SendBatchSmsResponse sendSmsResponse = smsService
.sendSmsBatchRequest(request);
return sendSmsResponse;
}
catch (ClientException e) {
e.printStackTrace();
}
return new SendBatchSmsResponse();
}
/**
* Example
* @param code
* @return
*/
@RequestMapping("/sms-send.do")
public SendSmsResponse sendCheckCode(@RequestParam(name = "code") String code) {
// 组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
// 必填:待发送手机号
request.setPhoneNumbers("******");
// 必填:短信签名-可在短信控制台中找到
request.setSignName("******");
// 必填:短信模板-可在短信控制台中找到
request.setTemplateCode("******");
// 可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
request.setTemplateParam("{\"code\":\"" + code + "\"}");
// 选填-上行短信扩展码(无特殊需求用户请忽略此字段)
// request.setSmsUpExtendCode("90997");
// 可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
request.setOutId("****TraceId");
try {
SendSmsResponse sendSmsResponse = smsService.sendSmsRequest(request);
return sendSmsResponse;
}
catch (ClientException e) {
e.printStackTrace();
}
return new SendSmsResponse();
}
/**
*
* Example
* @param telephone
* @return
*/
@RequestMapping("/query.do")
public QuerySendDetailsResponse querySendDetailsResponse(
@RequestParam(name = "tel") String telephone) {
// 组装请求对象
QuerySendDetailsRequest request = new QuerySendDetailsRequest();
// 必填-号码
request.setPhoneNumber(telephone);
// 必填-短信发送的日期 支持30天内记录查询可查其中一天的发送数据格式yyyyMMdd
request.setSendDate("20190103");
// 必填-页大小
request.setPageSize(10L);
// 必填-当前页码从1开始计数
request.setCurrentPage(1L);
try {
QuerySendDetailsResponse response = smsService.querySendDetails(request);
return response;
}
catch (ClientException e) {
e.printStackTrace();
}
return new QuerySendDetailsResponse();
}
@RequestMapping("/sms-report.do")
public List<Message> smsReport() {
return smsReportMessageListener.getSmsReportMessageSet();
}
}

@ -0,0 +1,28 @@
package org.springframework.cloud.alibaba.cloud.example;
import com.aliyun.mns.model.Message;
import org.springframework.stereotype.Component;
import java.util.LinkedList;
import java.util.List;
/**
* @author Spring Bean
*/
@Component
public class SmsReportMessageListener
implements org.springframework.cloud.alicloud.sms.SmsReportMessageListener {
private List<Message> smsReportMessageSet = new LinkedList<>();
@Override
public boolean dealMessage(Message message) {
smsReportMessageSet.add(message);
System.err.println(this.getClass().getName() + "; " + message.toString());
return true;
}
public List<Message> getSmsReportMessageSet() {
return smsReportMessageSet;
}
}

@ -0,0 +1,19 @@
package org.springframework.cloud.alibaba.cloud.example;
import org.springframework.stereotype.Component;
import com.aliyun.mns.model.Message;
/**
* @author Spring Bean
*/
@Component
public class SmsUpMessageListener
implements org.springframework.cloud.alicloud.sms.SmsUpMessageListener {
@Override
public boolean dealMessage(Message message) {
System.err.println(this.getClass().getName() + "; " + message.toString());
return true;
}
}

@ -0,0 +1,4 @@
spring.application.name=sca-sms-example
server.port=9051
# config management
management.endpoints.web.exposure.include=*

@ -27,15 +27,16 @@ import org.springframework.context.annotation.Configuration;
@Configuration
public class NacosConfigBootstrapConfiguration {
@Bean
public NacosPropertySourceLocator nacosPropertySourceLocator() {
return new NacosPropertySourceLocator();
}
@Bean
@ConditionalOnMissingBean
public NacosConfigProperties nacosConfigProperties() {
return new NacosConfigProperties();
}
@Bean
public NacosPropertySourceLocator nacosPropertySourceLocator(
NacosConfigProperties nacosConfigProperties) {
return new NacosPropertySourceLocator(nacosConfigProperties);
}
}

@ -18,8 +18,8 @@ package org.springframework.cloud.alibaba.nacos;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
@ -42,10 +42,9 @@ import static com.alibaba.nacos.api.PropertyKeyConst.*;
@ConfigurationProperties(NacosConfigProperties.PREFIX)
public class NacosConfigProperties {
public static final String PREFIX = "spring.cloud.nacos.config";
static final String PREFIX = "spring.cloud.nacos.config";
private static final Logger LOGGER = LoggerFactory
.getLogger(NacosConfigProperties.class);
private static final Log log = LogFactory.getLog(NacosConfigProperties.class);
/**
* nacos config server address
@ -348,7 +347,9 @@ public class NacosConfigProperties {
return configService;
}
catch (Exception e) {
LOGGER.error("create config service error!properties={},e=,", this, e);
log.error(
"create config service error!properties=" + this.toString() + ",e=,",
e);
return null;
}
}

@ -18,8 +18,8 @@ package org.springframework.cloud.alibaba.nacos.client;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.cloud.alibaba.nacos.NacosPropertySourceRepository;
import org.springframework.core.io.ByteArrayResource;
@ -33,8 +33,7 @@ import java.util.*;
* @author pbting
*/
public class NacosPropertySourceBuilder {
private static final Logger LOGGER = LoggerFactory
.getLogger(NacosPropertySourceBuilder.class);
private static final Log log = LogFactory.getLog(NacosPropertySourceBuilder.class);
private static final Properties EMPTY_PROPERTIES = new Properties();
private ConfigService configService;
@ -68,9 +67,6 @@ public class NacosPropertySourceBuilder {
NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
Properties p = loadNacosData(dataId, group, fileExtension);
if (p == null) {
p = EMPTY_PROPERTIES;
}
NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId,
propertiesToMap(p), new Date(), isRefreshable);
NacosPropertySourceRepository.collectNacosPropertySources(nacosPropertySource);
@ -82,7 +78,7 @@ public class NacosPropertySourceBuilder {
try {
data = configService.getConfig(dataId, group, timeout);
if (!StringUtils.isEmpty(data)) {
LOGGER.info(String.format("Loading nacos data, dataId: '%s', group: '%s'",
log.info(String.format("Loading nacos data, dataId: '%s', group: '%s'",
dataId, group));
if (fileExtension.equalsIgnoreCase("properties")) {
@ -101,13 +97,13 @@ public class NacosPropertySourceBuilder {
}
}
catch (NacosException e) {
LOGGER.error("get data from Nacos error,dataId:{}, ", dataId, e);
log.error("get data from Nacos error,dataId:" + dataId + ", ", e);
}
catch (Exception e) {
LOGGER.error("parse data from Nacos error,dataId:{},data:{},", dataId, data,
e);
log.error("parse data from Nacos error,dataId:" + dataId + ",data:" + data
+ ",", e);
}
return null;
return EMPTY_PROPERTIES;
}
@SuppressWarnings("unchecked")

@ -16,10 +16,11 @@
package org.springframework.cloud.alibaba.nacos.client;
import com.alibaba.nacos.api.config.ConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
import org.springframework.cloud.alibaba.nacos.NacosPropertySourceRepository;
import org.springframework.cloud.alibaba.nacos.refresh.NacosContextRefresher;
@ -30,8 +31,7 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;
import com.alibaba.nacos.api.config.ConfigService;
/**
* @author xiaojing
@ -40,8 +40,7 @@ import java.util.List;
@Order(0)
public class NacosPropertySourceLocator implements PropertySourceLocator {
private static final Logger LOGGER = LoggerFactory
.getLogger(NacosPropertySourceLocator.class);
private static final Log log = LogFactory.getLog(NacosPropertySourceLocator.class);
private static final String NACOS_PROPERTY_SOURCE_NAME = "NACOS";
private static final String SEP1 = "-";
private static final String DOT = ".";
@ -49,22 +48,21 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
private static final List<String> SUPPORT_FILE_EXTENSION = Arrays.asList("properties",
"yaml", "yml");
@Autowired
private NacosPropertySourceBuilder nacosPropertySourceBuilder;
private NacosConfigProperties nacosConfigProperties;
public NacosPropertySourceLocator() {
public NacosPropertySourceLocator(NacosConfigProperties nacosConfigProperties) {
this.nacosConfigProperties = nacosConfigProperties;
}
private NacosPropertySourceBuilder nacosPropertySourceBuilder;
@Override
public PropertySource<?> locate(Environment env) {
ConfigService configService = nacosConfigProperties.configServiceInstance();
if (null == configService) {
LOGGER.warn(
"no instance of config service found, can't load config from nacos");
log.warn("no instance of config service found, can't load config from nacos");
return null;
}
long timeout = nacosConfigProperties.getTimeout();
@ -167,7 +165,7 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
private void loadNacosDataIfPresent(final CompositePropertySource composite,
final String dataId, final String group, String fileExtension,
boolean isRefreshable) {
if (NacosContextRefresher.loadCount.get() != 0) {
if (NacosContextRefresher.getRefreshCount() != 0) {
NacosPropertySource ps;
if (!isRefreshable) {
ps = NacosPropertySourceRepository.getNacosPropertySource(dataId);
@ -187,10 +185,10 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
private static void checkDataIdFileExtension(String[] sharedDataIdArry) {
StringBuilder stringBuilder = new StringBuilder();
outline: for (int i = 0; i < sharedDataIdArry.length; i++) {
for (int i = 0; i < sharedDataIdArry.length; i++) {
for (String fileExtension : SUPPORT_FILE_EXTENSION) {
if (sharedDataIdArry[i].indexOf(fileExtension) > 0) {
continue outline;
break;
}
}
stringBuilder.append(sharedDataIdArry[i] + ",");

@ -19,8 +19,8 @@ package org.springframework.cloud.alibaba.nacos.refresh;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.alibaba.nacos.NacosPropertySourceRepository;
import org.springframework.cloud.alibaba.nacos.client.NacosPropertySource;
@ -51,10 +51,9 @@ import java.util.concurrent.atomic.AtomicLong;
public class NacosContextRefresher
implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware {
private final static Logger LOGGER = LoggerFactory
.getLogger(NacosContextRefresher.class);
private final static Log log = LogFactory.getLog(NacosContextRefresher.class);
public static final AtomicLong loadCount = new AtomicLong(0);
private static final AtomicLong REFRESH_COUNT = new AtomicLong(0);
private final NacosRefreshProperties refreshProperties;
@ -108,7 +107,7 @@ public class NacosContextRefresher
Listener listener = listenerMap.computeIfAbsent(dataId, i -> new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
loadCount.incrementAndGet();
refreshCountIncrement();
String md5 = "";
if (!StringUtils.isEmpty(configInfo)) {
try {
@ -117,14 +116,14 @@ public class NacosContextRefresher
.toString(16);
}
catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
LOGGER.warn("[Nacos] unable to get md5 for dataId: " + dataId, e);
log.warn("[Nacos] unable to get md5 for dataId: " + dataId, e);
}
}
refreshHistory.add(dataId, md5);
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Refresh Nacos config group{},dataId{}", group, dataId);
if (log.isDebugEnabled()) {
log.debug("Refresh Nacos config group " + group + ",dataId" + dataId);
}
}
@ -142,4 +141,11 @@ public class NacosContextRefresher
}
}
public static long getRefreshCount() {
return REFRESH_COUNT.get();
}
public static void refreshCountIncrement() {
REFRESH_COUNT.incrementAndGet();
}
}

@ -21,12 +21,16 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alicloud.ans.migrate.MigrateOnConditionMissingClass;
import org.springframework.cloud.alicloud.ans.registry.AnsAutoServiceRegistration;
import org.springframework.cloud.alicloud.ans.registry.AnsRegistration;
import org.springframework.cloud.alicloud.ans.registry.AnsServiceRegistry;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
@ -34,6 +38,7 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
@EnableConfigurationProperties
@Conditional(MigrateOnConditionMissingClass.class)
@ConditionalOnClass(name = "org.springframework.boot.web.context.WebServerInitializedEvent")
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@ConditionalOnAnsEnabled
@ -49,8 +54,9 @@ public class AnsAutoConfiguration {
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public AnsRegistration ansRegistration() {
return new AnsRegistration();
public AnsRegistration ansRegistration(AnsProperties ansProperties,
ApplicationContext applicationContext) {
return new AnsRegistration(ansProperties, applicationContext);
}
@Bean
@ -63,4 +69,5 @@ public class AnsAutoConfiguration {
return new AnsAutoServiceRegistration(registry, autoServiceRegistrationProperties,
registration);
}
}

@ -16,13 +16,12 @@
package org.springframework.cloud.alicloud.ans;
import java.util.*;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import java.util.*;
/**
* @author xiaolongzuo
@ -75,8 +74,9 @@ public class AnsDiscoveryClient implements DiscoveryClient {
@Override
public List<String> getServices() {
Set<String> publishers = NamingService.getPublishes();
Set<String> doms = NamingService.getDomsSubscribed();
doms.addAll(publishers);
List<String> result = new LinkedList<>();
for (String service : doms) {
result.add(service);

@ -19,15 +19,18 @@ package org.springframework.cloud.alicloud.ans;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alicloud.ans.migrate.MigrateOnConditionMissingClass;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* @author xiaolongzuo
*/
@Configuration
@Conditional(MigrateOnConditionMissingClass.class)
@ConditionalOnMissingBean(DiscoveryClient.class)
@EnableConfigurationProperties
@AutoConfigureBefore(SimpleDiscoveryClientAutoConfiguration.class)

@ -16,19 +16,18 @@
package org.springframework.cloud.alicloud.ans.endpoint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author xiaolongzuo
@ -36,7 +35,7 @@ import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
@Endpoint(id = "ans")
public class AnsEndpoint {
private static final Logger LOGGER = LoggerFactory.getLogger(AnsEndpoint.class);
private static final Log log = LogFactory.getLog(AnsEndpoint.class);
private AnsProperties ansProperties;
@ -50,7 +49,7 @@ public class AnsEndpoint {
@ReadOperation
public Map<String, Object> invoke() {
Map<String, Object> ansEndpoint = new HashMap<>();
LOGGER.info("ANS endpoint invoke, ansProperties is {}", ansProperties);
log.info("ANS endpoint invoke, ansProperties is " + ansProperties);
ansEndpoint.put("ansProperties", ansProperties);
Map<String, Object> subscribes = new HashMap<>();
@ -65,7 +64,7 @@ public class AnsEndpoint {
}
}
ansEndpoint.put("subscribes", subscribes);
LOGGER.info("ANS endpoint invoke, subscribes is {}", subscribes);
log.info("ANS endpoint invoke, subscribes is " + subscribes);
return ansEndpoint;
}

@ -0,0 +1,31 @@
package org.springframework.cloud.alicloud.ans.migrate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
@Endpoint(id = "migrate")
public class MigrateEndpoint {
private static final Log log = LogFactory.getLog(MigrateEndpoint.class);
public MigrateEndpoint() {
}
/**
* @return ans endpoint
*/
@ReadOperation
public Map<String, ConcurrentMap<String, ServerWrapper>> invoke() {
Map<String, ConcurrentMap<String, ServerWrapper>> result = ServerListInvocationHandler
.getServerRegistry();
log.info("migrate server list :" + result);
return result;
}
}

@ -0,0 +1,18 @@
package org.springframework.cloud.alicloud.ans.migrate;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@ConditionalOnWebApplication
@ConditionalOnClass(value = Endpoint.class)
@Conditional(MigrateOnConditionClass.class)
public class MigrateEndpointAutoConfiguration {
@Bean
public MigrateEndpoint ansEndpoint() {
return new MigrateEndpoint();
}
}

@ -0,0 +1,49 @@
package org.springframework.cloud.alicloud.ans.migrate;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
/**
* @author pbting
*/
public abstract class MigrateOnCondition implements Condition, BeanClassLoaderAware {
final String[] conditionOnClass = new String[] {
"org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistration",
"org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration" };
ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public abstract boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata);
boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
forName(className, classLoader);
return true;
}
catch (Throwable var3) {
return false;
}
}
Class<?> forName(String className, ClassLoader classLoader)
throws ClassNotFoundException {
return classLoader != null ? classLoader.loadClass(className)
: Class.forName(className);
}
}

@ -0,0 +1,22 @@
package org.springframework.cloud.alicloud.ans.migrate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* @author pbting
*/
public class MigrateOnConditionClass extends MigrateOnCondition {
protected static final Log log = LogFactory.getLog(MigrateOnConditionClass.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean result = isPresent(conditionOnClass[0], classLoader)
|| isPresent(conditionOnClass[1], classLoader);
log.info("the result of match is :" + result);
return result;
}
}

@ -0,0 +1,23 @@
package org.springframework.cloud.alicloud.ans.migrate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* @author pbting
*/
public class MigrateOnConditionMissingClass extends MigrateOnConditionClass {
protected static final Log log = LogFactory
.getLog(MigrateOnConditionMissingClass.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean result = !super.matches(context, metadata);
log.info("the result of match is :" + result);
return result;
}
}

@ -0,0 +1,97 @@
package org.springframework.cloud.alicloud.ans.migrate;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import org.aopalliance.aop.Advice;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.framework.ProxyFactory;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author pbting
*/
final class MigrateProxyManager {
private final static Log log = LogFactory.getLog(MigrateProxyManager.class);
private final static AtomicBoolean IS_PROXY = new AtomicBoolean(true);
private final static Set<String> SERVICES_ID = new ConcurrentSkipListSet<>();
private static Object springProxyFactory(Object target, ClassLoader classLoader,
List<Advice> adviceList, Class... interfaces) {
final ProxyFactory proxyFactory = new ProxyFactory(interfaces);
proxyFactory.setTarget(target);
adviceList.forEach(advice -> proxyFactory.addAdvice(advice));
return proxyFactory.getProxy(classLoader);
}
static Object newServerListProxy(Object bean, ClassLoader classLoader,
IClientConfig clientConfig) {
bean = springProxyFactory(bean, classLoader,
Arrays.asList(new ServerListInvocationHandler(clientConfig)),
new Class[] { ServerList.class });
log.info("[service id]" + clientConfig.getClientName()
+ " new a ServerList proxy instance for spring cloud netflix to spring cloud alibaba ");
collectServiceId(clientConfig.getClientName());
return bean;
}
static Object newLoadBalancerProxy(Object bean, ClassLoader classLoader,
final IClientConfig clientConfig) {
bean = springProxyFactory(bean, classLoader,
Arrays.asList(new AfterReturningAdvice() {
private final IClientConfig iclientConfig = clientConfig;
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) {
String methodName = method.getName();
if ("chooseServer".equals(methodName)) {
String serviceId = iclientConfig.getClientName();
Server server = (Server) returnValue;
server = ServerListInvocationHandler
.checkAndGetServiceServer(serviceId, server);
ServerListInvocationHandler.incrementCallService(serviceId,
server);
}
}
}), new Class[] { ILoadBalancer.class });
log.info("[service id]" + clientConfig.getClientName()
+ " new a ILoadBalancer proxy instance for spring cloud netflix to spring cloud alibaba ");
return bean;
}
static void migrateProxyClose() {
IS_PROXY.set(false);
}
static void migrateProxyUp() {
IS_PROXY.set(true);
}
static boolean isMigrateProxy() {
return IS_PROXY.get();
}
static void collectServiceId(String serviceId) {
SERVICES_ID.add(serviceId);
}
static Set<String> getServicesId() {
return Collections.unmodifiableSet(SERVICES_ID);
}
}

@ -0,0 +1,80 @@
package org.springframework.cloud.alicloud.ans.migrate;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.springframework.cloud.context.named.NamedContextFactory;
import org.springframework.cloud.endpoint.event.RefreshEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import com.netflix.loadbalancer.ILoadBalancer;
/**
* @author pbting
*/
@Component
public class MigrateRefreshEventListener implements ApplicationListener<RefreshEvent> {
private final static int CHECK_INTERVAL = 1;
private final static String MIGRATE_SWITCH = "spring.cloud.alicloud.migrate.ans.switch";
private volatile String lastScaMigrateAnsSwitchValue = "true";
private Environment environment;
private NamedContextFactory namedContextFactory;
public MigrateRefreshEventListener(Environment environment,
NamedContextFactory namedContextFactory) {
this.environment = environment;
this.namedContextFactory = namedContextFactory;
}
@PostConstruct
public void initTimerCheck() {
Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(
() -> onApplicationEvent(null), CHECK_INTERVAL, CHECK_INTERVAL,
TimeUnit.SECONDS);
}
@Override
public void onApplicationEvent(RefreshEvent event) {
String value = environment.getProperty(MIGRATE_SWITCH, "true");
// check 1: check the value
if (value.equals(lastScaMigrateAnsSwitchValue)) {
return;
}
updateLastScaMigrateAnsResetValue(value);
// step 1: migrate up
if ("true".equals(value)) {
MigrateProxyManager.migrateProxyUp();
serviceIdContextInit();
return;
}
// step 2: migrate close
if ("false".equals(value)) {
MigrateProxyManager.migrateProxyClose();
serviceIdContextInit();
return;
}
}
private void serviceIdContextInit() {
namedContextFactory.destroy();
// initializer each spring context for service id
MigrateProxyManager.getServicesId().forEach(serviceId -> namedContextFactory
.getInstance(serviceId, ILoadBalancer.class));
}
private synchronized void updateLastScaMigrateAnsResetValue(String value) {
this.lastScaMigrateAnsSwitchValue = value;
}
}

@ -0,0 +1,53 @@
package org.springframework.cloud.alicloud.ans.migrate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.ServerList;
public class MigrateRibbonBeanPostProcessor
implements BeanPostProcessor, BeanClassLoaderAware {
protected static final Log log = LogFactory.getLog(MigrateOnCondition.class);
private ClassLoader classLoader;
private IClientConfig clientConfig;
public MigrateRibbonBeanPostProcessor(IClientConfig clientConfig) {
this.clientConfig = clientConfig;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// step 1 : check the bean whether proxy or not
if (!MigrateProxyManager.isMigrateProxy()) {
log.info("Migrate proxy is Close.");
return bean;
}
// step 2 : proxy the designated bean
if (bean instanceof ServerList) {
bean = MigrateProxyManager.newServerListProxy(bean, classLoader,
clientConfig);
}
if (bean instanceof ILoadBalancer) {
bean = MigrateProxyManager.newLoadBalancerProxy(bean, classLoader,
clientConfig);
}
return bean;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
}

@ -0,0 +1,51 @@
package org.springframework.cloud.alicloud.ans.migrate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.cloud.alicloud.ans.registry.AnsRegistration;
import org.springframework.cloud.alicloud.ans.registry.AnsServiceRegistry;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author pbting
*/
@Component
public class MigrateServiceRegistry {
private static final Log log = LogFactory.getLog(MigrateServiceRegistry.class);
private AtomicBoolean running = new AtomicBoolean(false);
private ServiceRegistry serviceRegistry;
private AnsRegistration ansRegistration;
public MigrateServiceRegistry(AnsProperties ansProperties,
ApplicationContext context) {
this.ansRegistration = new AnsRegistration(ansProperties, context);
this.ansRegistration.init();
this.serviceRegistry = new AnsServiceRegistry();
}
@EventListener(WebServerInitializedEvent.class)
public void onApplicationEvent(WebServerInitializedEvent event) {
int serverPort = event.getWebServer().getPort();
this.ansRegistration.setPort(serverPort);
log.info("[ Migrate ] change the port to " + serverPort);
if (!this.running.get()) {
long s = System.currentTimeMillis();
log.info("[Migrate] start to registry server to ANS");
this.serviceRegistry.register(this.ansRegistration);
log.info("[migrate] end to registry server to ANS cost time with "
+ (System.currentTimeMillis() - s) + " ms.");
this.running.set(true);
}
}
}

@ -0,0 +1,36 @@
package org.springframework.cloud.alicloud.ans.migrate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alicloud.ans.ConditionalOnAnsEnabled;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
import org.springframework.cloud.context.named.NamedContextFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
@EnableConfigurationProperties
@Conditional(MigrateOnConditionClass.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@ConditionalOnAnsEnabled
public class MigrationAutoconfiguration {
@Bean
public MigrateServiceRegistry migrationManger(AnsProperties ansProperties,
ApplicationContext applicationContext) {
return new MigrateServiceRegistry(ansProperties, applicationContext);
}
@Bean
public MigrateRefreshEventListener migrateRefreshEventListener(
Environment environment,
@Qualifier(value = "springClientFactory") NamedContextFactory namedContextFactory) {
return new MigrateRefreshEventListener(environment, namedContextFactory);
}
}

@ -0,0 +1,164 @@
package org.springframework.cloud.alicloud.ans.migrate;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.Server;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.alicloud.ans.ribbon.AnsServer;
import org.springframework.cloud.alicloud.ans.ribbon.AnsServerList;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
/**
*
*/
class ServerListInvocationHandler implements MethodInterceptor {
private final static Log log = LogFactory.getLog(ServerListInvocationHandler.class);
private final static ConcurrentMap<String, AnsServerList> SERVER_LIST_CONCURRENT_MAP = new ConcurrentHashMap<>();
private final static ConcurrentMap<String, ConcurrentMap<String, ServerWrapper>> CALL_SERVICE_COUNT = new ConcurrentHashMap<>();
private final static Set<String> INTERCEPTOR_METHOD_NAME = new ConcurrentSkipListSet();
private IClientConfig clientConfig;
private AnsServerList ansServerList;
private AtomicBoolean isFirst = new AtomicBoolean(false);
static {
INTERCEPTOR_METHOD_NAME.add("getInitialListOfServers");
INTERCEPTOR_METHOD_NAME.add("getUpdatedListOfServers");
}
ServerListInvocationHandler(IClientConfig clientConfig) {
this.clientConfig = clientConfig;
this.ansServerList = new AnsServerList(clientConfig.getClientName());
SERVER_LIST_CONCURRENT_MAP.put(ansServerList.getDom(), ansServerList);
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
String methodName = invocation.getMethod().getName();
// step 1: check the method is not interceptor
if (!INTERCEPTOR_METHOD_NAME.contains(methodName)) {
return invocation.proceed();
}
// step 2: interceptor the method
List<Server> serverList = (List<Server>) invocation.proceed();
long s = System.currentTimeMillis();
log.info("[ START ] merage server list for " + clientConfig.getClientName());
serverList = mergeAnsServerList(serverList);
log.info("[ END ] merage server list for " + clientConfig.getClientName()
+ ", cost time " + (System.currentTimeMillis() - s) + " ms .");
return serverList;
}
/**
* 线 Eureka Merage List Server. ANS
*/
private List<Server> mergeAnsServerList(final List<Server> source) {
if (isFirst.compareAndSet(false, true)) {
return source;
}
// step 1: get all of server list and filter the alive
List<AnsServer> ansServerList = filterAliveAnsServer(
this.ansServerList.getInitialListOfServers());
if (ansServerList.isEmpty()) {
return source;
}
log.info("[" + this.clientConfig.getClientName() + "] Get Server List from ANS:"
+ ansServerList + "; loadbalancer server list override before:" + source);
// step 2:remove servers of that have in load balancer list
final Iterator<Server> serverIterator = source.iterator();
while (serverIterator.hasNext()) {
final Server server = serverIterator.next();
ansServerList.forEach(ansServer -> {
if (server.getHostPort()
.equals(ansServer.getHealthService().toInetAddr())) {
// by: ZoneAffinityPredicate
serverIterator.remove();
log.info("Source Server is remove " + server.getHostPort()
+ ", and from ANS Server is override"
+ ansServer.toString());
}
// fix bug: mast be set the zone, update server list,will filter
ansServer.setZone(server.getZone());
ansServer.setSchemea(server.getScheme());
ansServer.setId(ansServer.getHealthService().toInetAddr());
ansServer.setReadyToServe(true);
});
}
ansServerList.forEach(ansServer -> source.add(ansServer));
log.info("[" + this.clientConfig.getClientName() + "] "
+ "; loadbalancer server list override after:" + source);
// override
return source;
}
private List<AnsServer> filterAliveAnsServer(List<AnsServer> sourceServerList) {
final List<AnsServer> resultServerList = new LinkedList<>();
sourceServerList.forEach(ansServer -> {
boolean isAlive = ansServer.isAlive(3);
if (isAlive) {
resultServerList.add(ansServer);
}
log.warn(ansServer.toString() + " isAlive :" + isAlive);
});
return resultServerList;
}
static Map<String, ConcurrentMap<String, ServerWrapper>> getServerRegistry() {
return Collections.unmodifiableMap(CALL_SERVICE_COUNT);
}
static Server checkAndGetServiceServer(String serviceId, Server server) {
if (server != null) {
return server;
}
log.warn(String.format("[%s] refers the server is null", server));
List<AnsServer> ansServerList = SERVER_LIST_CONCURRENT_MAP.get(serviceId)
.getInitialListOfServers();
if (!ansServerList.isEmpty()) {
return ansServerList.get(0);
}
return server;
}
static void incrementCallService(String serviceId, Server server) {
ConcurrentMap<String, ServerWrapper> concurrentHashMap = CALL_SERVICE_COUNT
.putIfAbsent(serviceId, new ConcurrentHashMap<>());
if (concurrentHashMap == null) {
concurrentHashMap = CALL_SERVICE_COUNT.get(serviceId);
}
String ipPort = server.getHostPort();
ServerWrapper serverWraper = concurrentHashMap.putIfAbsent(ipPort,
new ServerWrapper(server, new AtomicLong()));
if (serverWraper == null) {
serverWraper = concurrentHashMap.get(ipPort);
}
serverWraper.setServer(server);
serverWraper.getCallCount().incrementAndGet();
}
}

@ -0,0 +1,35 @@
package org.springframework.cloud.alicloud.ans.migrate;
import com.netflix.loadbalancer.Server;
import java.util.concurrent.atomic.AtomicLong;
public class ServerWrapper {
private Server server;
private AtomicLong callCount;
public ServerWrapper() {
}
public ServerWrapper(Server server, AtomicLong callCount) {
this.server = server;
this.callCount = callCount;
}
public Server getServer() {
return server;
}
public void setServer(Server server) {
this.server = server;
}
public AtomicLong getCallCount() {
return callCount;
}
public void setCallCount(AtomicLong callCount) {
this.callCount = callCount;
}
}

@ -16,8 +16,8 @@
package org.springframework.cloud.alicloud.ans.registry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
@ -30,8 +30,7 @@ import org.springframework.util.StringUtils;
*/
public class AnsAutoServiceRegistration
extends AbstractAutoServiceRegistration<AnsRegistration> {
private static final Logger LOGGER = LoggerFactory
.getLogger(AnsAutoServiceRegistration.class);
private static final Log log = LogFactory.getLog(AnsAutoServiceRegistration.class);
@Autowired
private AnsRegistration registration;
@ -65,7 +64,7 @@ public class AnsAutoServiceRegistration
@Override
protected void register() {
if (!this.registration.getAnsProperties().isRegisterEnabled()) {
LOGGER.debug("Registration disabled.");
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() < 0) {

@ -16,12 +16,6 @@
package org.springframework.cloud.alicloud.ans.registry;
import java.net.URI;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
@ -31,6 +25,10 @@ import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.net.URI;
import java.util.Map;
/**
* @author xiaolongzuo
*/
@ -40,12 +38,14 @@ public class AnsRegistration implements Registration, ServiceInstance {
private static final String MANAGEMENT_CONTEXT_PATH = "management.context-path";
private static final String MANAGEMENT_ADDRESS = "management.address";
@Autowired
private AnsProperties ansProperties;
@Autowired
private ApplicationContext context;
public AnsRegistration(AnsProperties ansProperties, ApplicationContext context) {
this.ansProperties = ansProperties;
this.context = context;
}
@PostConstruct
public void init() {
@ -64,6 +64,11 @@ public class AnsRegistration implements Registration, ServiceInstance {
metadata.put(MANAGEMENT_ADDRESS, address);
}
}
String serverPort = env.getProperty("server.port");
if (null != serverPort) {
this.setPort(Integer.valueOf(serverPort));
}
}
@Override

@ -16,24 +16,23 @@
package org.springframework.cloud.alicloud.ans.registry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.ipms.NodeReactor;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.ipms.NodeReactor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author xiaolongzuo
*/
public class AnsServiceRegistry implements ServiceRegistry<AnsRegistration> {
private static Logger logger = LoggerFactory.getLogger(AnsServiceRegistry.class);
private static Log log = LogFactory.getLog(AnsServiceRegistry.class);
private static final String SEPARATOR = ",";
@ -41,11 +40,11 @@ public class AnsServiceRegistry implements ServiceRegistry<AnsRegistration> {
public void register(AnsRegistration registration) {
if (!registration.isRegisterEnabled()) {
logger.info("Registration is disabled...");
log.info("Registration is disabled...");
return;
}
if (StringUtils.isEmpty(registration.getServiceId())) {
logger.info("No service to register for client...");
log.info("No service to register for client...");
return;
}
@ -63,13 +62,14 @@ public class AnsServiceRegistry implements ServiceRegistry<AnsRegistration> {
NamingService.regDom(dom, registration.getHost(), registration.getPort(),
registration.getRegisterWeight(dom), registration.getCluster(),
tags);
logger.info("INFO_ANS_REGISTER, {} {}:{} register finished", dom,
registration.getAnsProperties().getClientIp(),
registration.getAnsProperties().getClientPort());
log.info("INFO_ANS_REGISTER, " + dom + " "
+ registration.getAnsProperties().getClientIp() + ":"
+ registration.getAnsProperties().getClientPort()
+ " register finished");
}
catch (Exception e) {
logger.error("ERR_ANS_REGISTER, {} register failed...{},", dom,
registration.toString(), e);
log.error("ERR_ANS_REGISTER, " + dom + " register failed..."
+ registration.toString() + ",", e);
}
}
}
@ -77,10 +77,10 @@ public class AnsServiceRegistry implements ServiceRegistry<AnsRegistration> {
@Override
public void deregister(AnsRegistration registration) {
logger.info("De-registering from ANSServer now...");
log.info("De-registering from ANSServer now...");
if (StringUtils.isEmpty(registration.getServiceId())) {
logger.info("No dom to de-register for client...");
log.info("No dom to de-register for client...");
return;
}
@ -89,11 +89,11 @@ public class AnsServiceRegistry implements ServiceRegistry<AnsRegistration> {
registration.getPort(), registration.getCluster());
}
catch (Exception e) {
logger.error("ERR_ANS_DEREGISTER, de-register failed...{},",
registration.toString(), e);
log.error("ERR_ANS_DEREGISTER, de-register failed..."
+ registration.toString() + ",", e);
}
logger.info("De-registration finished.");
log.info("De-registration finished.");
}
@Override

@ -17,7 +17,9 @@
package org.springframework.cloud.alicloud.ans.ribbon;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.alicloud.ans.migrate.MigrateOnConditionMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import com.netflix.client.config.IClientConfig;
@ -27,11 +29,12 @@ import com.netflix.loadbalancer.ServerList;
* @author xiaolongzuo
*/
@Configuration
@Conditional(MigrateOnConditionMissingClass.class)
public class AnsRibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ServerList<?> ribbonServerList(IClientConfig config) {
public ServerList<?> ansRibbonServerList(IClientConfig config) {
AnsServerList serverList = new AnsServerList(config.getClientName());
return serverList;
}

@ -16,12 +16,16 @@
package org.springframework.cloud.alicloud.ans.ribbon;
import java.util.Collections;
import java.util.Map;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import com.netflix.loadbalancer.Server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author xiaolongzuo
*/
@ -34,7 +38,8 @@ public class AnsServer extends Server {
public AnsServer(final Host host, final String dom) {
super(host.getIp(), host.getPort());
this.host = host;
this.metadata = Collections.emptyMap();
this.metadata = new HashMap();
this.metadata.put("source", "ANS");
metaInfo = new MetaInfo() {
@Override
public String getAppName() {
@ -48,16 +53,44 @@ public class AnsServer extends Server {
@Override
public String getServiceIdForDiscovery() {
return null;
return dom;
}
@Override
public String getInstanceId() {
return null;
return AnsServer.this.host.getIp() + ":" + dom + ":"
+ AnsServer.this.host.getPort();
}
};
}
@Override
public boolean isAlive() {
return true;
}
/**
*
* @param timeOut Unit: Seconds
* @return
*/
public boolean isAlive(long timeOut) {
try {
String hostName = this.host.getHostname();
hostName = hostName != null && hostName.trim().length() > 0 ? hostName
: this.host.getIp();
Socket socket = new Socket();
socket.connect(new InetSocketAddress(hostName, this.host.getPort()),
(int) TimeUnit.SECONDS.toMillis(timeOut));
socket.close();
return true;
}
catch (IOException e) {
return false;
}
}
@Override
public MetaInfo getMetaInfo() {
return metaInfo;
@ -71,4 +104,9 @@ public class AnsServer extends Server {
return metadata;
}
@Override
public String toString() {
return "AnsServer{" + "metaInfo=" + metaInfo + ", host=" + host + ", metadata="
+ metadata + '}';
}
}

@ -16,19 +16,21 @@
package org.springframework.cloud.alicloud.ans.ribbon;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList;
import java.util.ArrayList;
import java.util.List;
/**
* @author xiaolongzuo
*/
public class AnsServerList extends AbstractServerList<AnsServer> {
private final static int CONNECT_TIME_OUT = 3;
private String dom;
public AnsServerList(String dom) {
@ -60,10 +62,12 @@ public class AnsServerList extends AbstractServerList<AnsServer> {
List<AnsServer> result = new ArrayList<AnsServer>(hosts.size());
for (Host host : hosts) {
if (host.isValid()) {
result.add(hostToServer(host));
AnsServer ansServer = hostToServer(host);
if (ansServer.isAlive(CONNECT_TIME_OUT)) {
result.add(ansServer);
}
}
}
return result;
}

@ -0,0 +1,20 @@
package org.springframework.cloud.alicloud.ans.ribbon;
import org.springframework.cloud.alicloud.ans.migrate.MigrateRibbonBeanPostProcessor;
import org.springframework.cloud.alicloud.ans.migrate.MigrateOnConditionClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import com.netflix.client.config.IClientConfig;
@Configuration
@Conditional(MigrateOnConditionClass.class)
public class MigrateRibbonCofiguration {
@Bean
public MigrateRibbonBeanPostProcessor migrateBeanPostProcessor(IClientConfig clientConfig) {
return new MigrateRibbonBeanPostProcessor(clientConfig);
}
}

@ -34,6 +34,7 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnBean(SpringClientFactory.class)
@ConditionalOnRibbonAns
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = AnsRibbonClientConfiguration.class)
@RibbonClients(defaultConfiguration = { AnsRibbonClientConfiguration.class,
MigrateRibbonCofiguration.class })
public class RibbonAnsAutoConfiguration {
}

@ -1,6 +1,8 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alicloud.ans.endpoint.AnsEndpointAutoConfiguration,\
org.springframework.cloud.alicloud.ans.ribbon.RibbonAnsAutoConfiguration,\
org.springframework.cloud.alicloud.ans.AnsAutoConfiguration
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.AnsDiscoveryClientAutoConfiguration
org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration

@ -50,6 +50,12 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.ans</groupId>
<artifactId>ans-sdk</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>

@ -0,0 +1,107 @@
package org.springframework.cloud.alicloud.context.sms;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.alicloud.context.AliCloudProperties;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import java.io.Serializable;
/**
* @author pbting
*/
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
public class SmsConfigProperties implements Serializable {
// 产品名称:云通信短信API产品,开发者无需替换
public static final String smsProduct = "Dysmsapi";
// 产品域名,开发者无需替换
public static final String smsDomain = "dysmsapi.aliyuncs.com";
private AliCloudProperties aliCloudProperties;
/**
*
*/
private String reportQueueName;
/**
*
*/
private String upQueueName;
/**
*
*/
protected String connnectTimeout = "10000";
/**
*
*/
protected String readTimeout = "10000";
public SmsConfigProperties(AliCloudProperties aliCloudProperties) {
this.aliCloudProperties = aliCloudProperties;
}
public String getConnnectTimeout() {
return connnectTimeout;
}
public void setConnnectTimeout(String connnectTimeout) {
this.connnectTimeout = connnectTimeout;
}
public String getReadTimeout() {
return readTimeout;
}
public void setReadTimeout(String readTimeout) {
this.readTimeout = readTimeout;
}
public void overiideFromEnv(Environment environment) {
overiideCustomFromEnv(environment);
if (StringUtils.isEmpty(connnectTimeout)) {
String resolveResult = environment.resolveRequiredPlaceholders(
"${spring.cloud.alibaba.sms.connect-timeout:}");
this.setConnnectTimeout(
StringUtils.isEmpty(resolveResult) ? "10000" : resolveResult);
}
if (StringUtils.isEmpty(readTimeout)) {
String resolveResult = environment.resolveRequiredPlaceholders(
"${spring.cloud.alibaba.sms.read-timeout:}");
this.setReadTimeout(
StringUtils.isEmpty(resolveResult) ? "10000" : resolveResult);
}
}
public void overiideCustomFromEnv(Environment environment) {
// nothing to do
}
public String getReportQueueName() {
return reportQueueName;
}
public void setReportQueueName(String reportQueueName) {
this.reportQueueName = reportQueueName;
}
public String getUpQueueName() {
return upQueueName;
}
public String getAccessKeyId() {
return aliCloudProperties.getAccessKey();
}
public String getAccessKeySecret() {
return aliCloudProperties.getSecretKey();
}
public void setUpQueueName(String upQueueName) {
this.upQueueName = upQueueName;
}
}

@ -0,0 +1,26 @@
package org.springframework.cloud.alicloud.context.sms;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
/**
* @author pbting
*/
public class SmsConfigRegistration {
private Environment environment;
private SmsConfigProperties smsConfigProperties;
public SmsConfigRegistration(Environment environment,
SmsConfigProperties smsConfigProperties) {
this.environment = environment;
this.smsConfigProperties = smsConfigProperties;
}
@PostConstruct
public void initSmsConfigRegistration() {
smsConfigProperties.overiideFromEnv(environment);
}
}

@ -0,0 +1,30 @@
package org.springframework.cloud.alicloud.context.sms;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alicloud.context.AliCloudProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(name = "com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest")
@ConditionalOnProperty(value = "spring.cloud.alibaba.deshao.enable.sms", matchIfMissing = true)
public class SmsContextAutoConfiguration {
@Bean
public SmsConfigProperties smsConfigProperties(
AliCloudProperties aliCloudProperties) {
return new SmsConfigProperties(aliCloudProperties);
}
@Bean
public SmsConfigRegistration smsConfigRegistration(Environment environment,
SmsConfigProperties smsConfigProperties) {
return new SmsConfigRegistration(environment, smsConfigProperties);
}
}

@ -6,7 +6,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alicloud.context.ans.AnsContextAutoConfiguration,\
org.springframework.cloud.alicloud.context.oss.OssContextAutoConfiguration,\
org.springframework.cloud.alicloud.context.scx.ScxContextAutoConfiguration,\
org.springframework.cloud.alicloud.context.statistics.StatisticsTaskStarter
org.springframework.cloud.alicloud.context.statistics.StatisticsTaskStarter,\
org.springframework.cloud.alicloud.context.sms.SmsContextAutoConfiguration
org.springframework.context.ApplicationListener=\
org.springframework.cloud.alicloud.context.ans.AnsContextApplicationListener,\
org.springframework.cloud.alicloud.context.nacos.NacosParameterInitListener,\

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-sms</artifactId>
<name>Spring Cloud Alibaba Cloud SMS</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-context</artifactId>
<exclusions>
<exclusion>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- sms -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun.mns</groupId>
<artifactId>aliyun-sdk-mns</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -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.alicloud.sms;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* @author pbting
*/
public abstract class AbstractSmsService implements ISmsService {
private ConcurrentHashMap<String, IAcsClient> acsClientConcurrentHashMap = new ConcurrentHashMap<>();
public IAcsClient getHangZhouRegionClientProfile(String accessKeyId,
String accessKeySecret) {
return acsClientConcurrentHashMap.computeIfAbsent(
getKey("cn-hangzhou", accessKeyId, accessKeySecret),
(iacsClient) -> new DefaultAcsClient(DefaultProfile
.getProfile("cn-hangzhou", accessKeyId, accessKeySecret)));
}
private String getKey(String regionId, String accessKeyId, String accessKeySecret) {
return regionId + ":" + accessKeyId + ":" + accessKeySecret;
}
}

@ -0,0 +1,115 @@
/*
* 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.alicloud.sms;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.*;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
/**
* @author pbting
*/
public interface ISmsService {
/**
*
* @param accessKeyId
* @param secret
* @return IAcsClient
*/
IAcsClient getHangZhouRegionClientProfile(String accessKeyId, String secret);
/**
*
* @param sendSmsRequest
* @throws ServerException
* @throws ClientException
* @return SendSmsResponse
*/
SendSmsResponse sendSmsRequest(SendSmsRequest sendSmsRequest)
throws ServerException, ClientException;
/**
*
* @param sendBatchSmsRequest
* @throws ServerException
* @throws ClientException
* @return SendBatchSmsResponse
*/
SendBatchSmsResponse sendSmsBatchRequest(SendBatchSmsRequest sendBatchSmsRequest)
throws ServerException, ClientException;
/**
*
* accessKeyId/accessKeySecret,使accessKeyId/accessKeySecret使
* accessKeyId/accessKeySecret
* @param sendSmsRequest
* @param accessKeyId
* @param accessKeySecret
* @return
* @throws ServerException
* @throws ClientException
*/
SendSmsResponse sendSmsRequest(SendSmsRequest sendSmsRequest, String accessKeyId,
String accessKeySecret) throws ServerException, ClientException;
/**
*
* @param sendSmsRequest
* @param accessKeyId
* @param accessKeySecret
* @throws ServerException
* @throws ClientException
* @return SendBatchSmsResponse
*/
SendBatchSmsResponse sendSmsBatchRequest(SendBatchSmsRequest sendSmsRequest,
String accessKeyId, String accessKeySecret)
throws ServerException, ClientException;
/**
*
* @param smsReportMessageListener
* @return boolean
*/
boolean startSmsReportMessageListener(
SmsReportMessageListener smsReportMessageListener);
/**
*
* @param smsUpMessageListener
* @return boolean
*/
boolean startSmsUpMessageListener(SmsUpMessageListener smsUpMessageListener);
/**
*
* @param request
* @param accessKeyId
* @param accessKeySecret
* @return QuerySendDetailsResponse
*/
QuerySendDetailsResponse querySendDetails(QuerySendDetailsRequest request,
String accessKeyId, String accessKeySecret) throws ClientException;
/**
*
* @param request
* @return QuerySendDetailsResponse
*/
QuerySendDetailsResponse querySendDetails(QuerySendDetailsRequest request)
throws ClientException;
}

@ -0,0 +1,105 @@
/*
* 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.alicloud.sms;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.alicloud.context.sms.SmsConfigProperties;
import org.springframework.cloud.alicloud.sms.base.MessageListener;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author pbting
*/
@Component
public class SmsInitializerEventListener
implements ApplicationListener<ApplicationStartedEvent> {
private final AtomicBoolean isCalled = new AtomicBoolean(false);
private SmsConfigProperties msConfigProperties;
private ISmsService smsService;
public SmsInitializerEventListener(SmsConfigProperties msConfigProperties,
ISmsService smsService) {
this.msConfigProperties = msConfigProperties;
this.smsService = smsService;
}
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
if (!isCalled.compareAndSet(false, true)) {
return;
}
// 整个application context refreshed then do
// 可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout",
msConfigProperties.getConnnectTimeout());
System.setProperty("sun.net.client.defaultReadTimeout",
msConfigProperties.getReadTimeout());
// 初始化acsClient,暂不支持region化
try {
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou",
SmsConfigProperties.smsProduct, SmsConfigProperties.smsDomain);
Collection<MessageListener> messageListeners = event.getApplicationContext()
.getBeansOfType(MessageListener.class).values();
if (messageListeners.isEmpty()) {
return;
}
for (MessageListener messageListener : messageListeners) {
if (SmsReportMessageListener.class.isInstance(messageListener)) {
if (msConfigProperties.getReportQueueName() != null
&& msConfigProperties.getReportQueueName().trim()
.length() > 0) {
smsService.startSmsReportMessageListener(
(SmsReportMessageListener) messageListener);
continue;
}
throw new IllegalArgumentException("the SmsReport queue name for "
+ messageListener.getClass().getCanonicalName()
+ " must be set.");
}
if (SmsUpMessageListener.class.isInstance(messageListener)) {
if (msConfigProperties.getUpQueueName() != null
&& msConfigProperties.getUpQueueName().trim().length() > 0) {
smsService.startSmsUpMessageListener(
(SmsUpMessageListener) messageListener);
continue;
}
throw new IllegalArgumentException("the SmsUp queue name for "
+ messageListener.getClass().getCanonicalName()
+ " must be set.");
}
}
}
catch (ClientException e) {
throw new RuntimeException(
"initialize sms profile end point cause an exception");
}
}
}

@ -0,0 +1,24 @@
/*
* 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.alicloud.sms;
import org.springframework.cloud.alicloud.sms.base.MessageListener;
/**
* @author pbting
*/
public interface SmsMessageListener extends MessageListener {
}

@ -0,0 +1,22 @@
/*
* 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.alicloud.sms;
/**
* @author pbting
*/
public interface SmsReportMessageListener extends SmsMessageListener {
}

@ -0,0 +1,187 @@
/*
* 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.alicloud.sms;
import com.aliyuncs.dysmsapi.model.v20170525.*;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.alicloud.context.sms.SmsConfigProperties;
import org.springframework.cloud.alicloud.sms.base.DefaultAlicomMessagePuller;
import org.springframework.cloud.alicloud.sms.endpoint.EndpointManager;
import org.springframework.cloud.alicloud.sms.endpoint.ReceiveMessageEntity;
import java.text.ParseException;
/**
* @author pbting
*/
public final class SmsServiceImpl extends AbstractSmsService {
private static final Log log = LogFactory.getLog(SmsServiceImpl.class);
/**
* will expose user to call this method send sms message
* @param sendSmsRequest
* @return
*/
private SmsConfigProperties smsConfigProperties;
public SmsServiceImpl(SmsConfigProperties smsConfigProperties) {
this.smsConfigProperties = smsConfigProperties;
}
public SendSmsResponse sendSmsRequest(SendSmsRequest sendSmsRequest)
throws ClientException {
return sendSmsRequest(sendSmsRequest, smsConfigProperties.getAccessKeyId(),
smsConfigProperties.getAccessKeySecret());
}
/**
*
* accessKeyId/accessKeySecret,使accessKeyId/accessKeySecret使
* accessKeyId/accessKeySecret
* @param sendSmsRequest
* @param accessKeyId
* @param accessKeySecret
* @throws ServerException
* @throws ClientException
* @return SendSmsResponse
*/
public SendSmsResponse sendSmsRequest(SendSmsRequest sendSmsRequest,
String accessKeyId, String accessKeySecret)
throws ServerException, ClientException {
EndpointManager.addSendSmsRequest(sendSmsRequest);
// hint 此处可能会抛出异常注意catch
return getHangZhouRegionClientProfile(accessKeyId, accessKeySecret)
.getAcsResponse(sendSmsRequest);
}
/**
*
* @param smsReportMessageListener
* @return boolean
*/
public boolean startSmsReportMessageListener(
SmsReportMessageListener smsReportMessageListener) {
String messageType = "SmsReport";// 短信回执SmsReport短信上行SmsUp
String queueName = smsConfigProperties.getReportQueueName();
return startReceiveMsg(messageType, queueName, smsReportMessageListener);
}
/**
*
* @param smsUpMessageListener
* @return boolean
*/
public boolean startSmsUpMessageListener(SmsUpMessageListener smsUpMessageListener) {
String messageType = "SmsUp";// 短信回执SmsReport短信上行SmsUp
String queueName = smsConfigProperties.getUpQueueName();
return startReceiveMsg(messageType, queueName, smsUpMessageListener);
}
/**
*
* @param messageType
* @param queueName
* @param messageListener
* @return boolean
*/
private boolean startReceiveMsg(String messageType, String queueName,
SmsMessageListener messageListener) {
String accessKeyId = smsConfigProperties.getAccessKeyId();
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
boolean result = true;
try {
new DefaultAlicomMessagePuller().startReceiveMsg(accessKeyId, accessKeySecret,
messageType, queueName, messageListener);
EndpointManager.addReceiveMessageEntity(
new ReceiveMessageEntity(messageType, queueName, messageListener));
}
catch (ClientException e) {
log.error("start sms report message listener cause an exception", e);
result = false;
}
catch (ParseException e) {
log.error("start sms report message listener cause an exception", e);
result = false;
}
return result;
}
/**
*
* @param sendBatchSmsRequest
* @throws ServerException
* @throws ClientException
* @return SendBatchSmsResponse
*/
@Override
public SendBatchSmsResponse sendSmsBatchRequest(
SendBatchSmsRequest sendBatchSmsRequest)
throws ServerException, ClientException {
return sendSmsBatchRequest(sendBatchSmsRequest,
smsConfigProperties.getAccessKeyId(),
smsConfigProperties.getAccessKeySecret());
}
/**
*
* @param sendBatchSmsRequest
* @param accessKeyId
* @param accessKeySecret
* @throws ClientException
* @return SendBatchSmsResponse
*/
@Override
public SendBatchSmsResponse sendSmsBatchRequest(
SendBatchSmsRequest sendBatchSmsRequest, String accessKeyId,
String accessKeySecret) throws ClientException {
EndpointManager.addSendBatchSmsRequest(sendBatchSmsRequest);
return getHangZhouRegionClientProfile(accessKeyId, accessKeySecret)
.getAcsResponse(sendBatchSmsRequest);
}
/**
*
* @param request
* @param accessKeyId
* @param accessKeySecret
* @throws ClientException
* @return QuerySendDetailsResponse
*/
@Override
public QuerySendDetailsResponse querySendDetails(QuerySendDetailsRequest request,
String accessKeyId, String accessKeySecret) throws ClientException {
return getHangZhouRegionClientProfile(accessKeyId, accessKeySecret)
.getAcsResponse(request);
}
/**
*
* @param request
* @throws ClientException
* @return QuerySendDetailsResponse
*/
@Override
public QuerySendDetailsResponse querySendDetails(QuerySendDetailsRequest request)
throws ClientException {
return querySendDetails(request, smsConfigProperties.getAccessKeyId(),
smsConfigProperties.getAccessKeySecret());
}
}

@ -0,0 +1,22 @@
/*
* 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.alicloud.sms;
/**
* @author pbting
*/
public interface SmsUpMessageListener extends SmsMessageListener {
}

@ -0,0 +1,429 @@
/*
* 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.alicloud.sms.base;
import com.aliyun.mns.client.CloudQueue;
import com.aliyun.mns.common.ClientException;
import com.aliyun.mns.common.ServiceException;
import com.aliyun.mns.model.Message;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
/**
*
*/
public class DefaultAlicomMessagePuller {
private Log logger = LogFactory.getLog(DefaultAlicomMessagePuller.class);
private String mnsAccountEndpoint = "https://1943695596114318.mns.cn-hangzhou.aliyuncs.com/";// 阿里通信消息的endpoint,固定。
private String endpointNameForPop = "cn-hangzhou";
private String regionIdForPop = "cn-hangzhou";
private String domainForPop = "dybaseapi.aliyuncs.com";
private TokenGetterForAlicom tokenGetter;
private MessageListener messageListener;
private boolean isRunning = false;
private Integer pullMsgThreadSize = 1;
private boolean debugLogOpen = false;
private Integer sleepSecondWhenNoData = 30;
public void openDebugLog(boolean debugLogOpen) {
this.debugLogOpen = debugLogOpen;
}
public Integer getSleepSecondWhenNoData() {
return sleepSecondWhenNoData;
}
public void setSleepSecondWhenNoData(Integer sleepSecondWhenNoData) {
this.sleepSecondWhenNoData = sleepSecondWhenNoData;
}
public Integer getPullMsgThreadSize() {
return pullMsgThreadSize;
}
public void setPullMsgThreadSize(Integer pullMsgThreadSize) {
if (pullMsgThreadSize != null && pullMsgThreadSize > 1) {
this.pullMsgThreadSize = pullMsgThreadSize;
}
}
private ExecutorService executorService;
public ExecutorService getExecutorService() {
return executorService;
}
public void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}
protected static final Map<String, Object> sLockObjMap = new HashMap<String, Object>();
protected static Map<String, Boolean> sPollingMap = new ConcurrentHashMap<String, Boolean>();
protected Object lockObj;
public boolean setPolling(String queueName) {
synchronized (lockObj) {
Boolean ret = sPollingMap.get(queueName);
if (ret == null || !ret) {
sPollingMap.put(queueName, true);
return true;
}
return false;
}
}
public void clearPolling(String queueName) {
synchronized (lockObj) {
sPollingMap.put(queueName, false);
lockObj.notifyAll();
if (debugLogOpen) {
logger.info("PullMessageTask_WakeUp:Everyone WakeUp and Work!");
}
}
}
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean running) {
isRunning = running;
}
private class PullMessageTask implements Runnable {
private String messageType;
private String queueName;
@Override
public void run() {
boolean polling = false;
while (isRunning) {
try {
synchronized (lockObj) {
Boolean p = sPollingMap.get(queueName);
if (p != null && p) {
try {
if (debugLogOpen) {
logger.info("PullMessageTask_sleep:"
+ Thread.currentThread().getName()
+ " Have a nice sleep!");
}
polling = false;
lockObj.wait();
}
catch (InterruptedException e) {
if (debugLogOpen) {
logger.info("PullMessageTask_Interrupted!"
+ Thread.currentThread().getName()
+ " QueueName is " + queueName);
}
continue;
}
}
}
TokenForAlicom tokenObject = tokenGetter.getTokenByMessageType(
messageType, queueName, mnsAccountEndpoint);
CloudQueue queue = tokenObject.getQueue();
Message popMsg = null;
if (!polling) {
popMsg = queue.popMessage();
if (debugLogOpen) {
SimpleDateFormat format = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
logger.info("PullMessageTask_popMessage:"
+ Thread.currentThread().getName() + "-popDone at "
+ "," + format.format(new Date()) + " msgSize="
+ (popMsg == null ? 0 : popMsg.getMessageId()));
}
if (popMsg == null) {
polling = true;
continue;
}
}
else {
if (setPolling(queueName)) {
if (debugLogOpen) {
logger.info("PullMessageTask_setPolling:"
+ Thread.currentThread().getName() + " Polling!");
}
}
else {
continue;
}
do {
if (debugLogOpen) {
logger.info("PullMessageTask_Keep_Polling"
+ Thread.currentThread().getName()
+ "KEEP Polling!");
}
try {
popMsg = queue.popMessage(sleepSecondWhenNoData);
}
catch (ClientException e) {
if (debugLogOpen) {
logger.info(
"PullMessageTask_Pop_Message:ClientException Refresh accessKey"
+ e);
}
tokenObject = tokenGetter.getTokenByMessageType(
messageType, queueName, mnsAccountEndpoint);
queue = tokenObject.getQueue();
}
catch (ServiceException e) {
if (debugLogOpen) {
logger.info(
"PullMessageTask_Pop_Message:ServiceException Refresh accessKey"
+ e);
}
tokenObject = tokenGetter.getTokenByMessageType(
messageType, queueName, mnsAccountEndpoint);
queue = tokenObject.getQueue();
}
catch (Exception e) {
if (debugLogOpen) {
logger.info(
"PullMessageTask_Pop_Message:Exception Happened when polling popMessage: "
+ e);
}
}
}
while (popMsg == null && isRunning);
clearPolling(queueName);
}
boolean dealResult = messageListener.dealMessage(popMsg);
if (dealResult) {
// remember to delete message when consume message successfully.
if (debugLogOpen) {
logger.info("PullMessageTask_Deal_Message:"
+ Thread.currentThread().getName() + "deleteMessage "
+ popMsg.getMessageId());
}
queue.deleteMessage(popMsg.getReceiptHandle());
}
}
catch (ClientException e) {
logger.error("PullMessageTask_execute_error,messageType:"
+ messageType + ",queueName:" + queueName, e);
break;
}
catch (ServiceException e) {
if (e.getErrorCode().equals("AccessDenied")) {
logger.error("PullMessageTask_execute_error,messageType:"
+ messageType + ",queueName:" + queueName
+ ",please check messageType and queueName", e);
}
else {
logger.error("PullMessageTask_execute_error,messageType:"
+ messageType + ",queueName:" + queueName, e);
}
break;
}
catch (com.aliyuncs.exceptions.ClientException e) {
if (e.getErrCode().equals("InvalidAccessKeyId.NotFound")) {
logger.error("PullMessageTask_execute_error,messageType:"
+ messageType + ",queueName:" + queueName
+ ",please check AccessKeyId", e);
}
if (e.getErrCode().equals("SignatureDoesNotMatch")) {
logger.error("PullMessageTask_execute_error,messageType:"
+ messageType + ",queueName:" + queueName
+ ",please check AccessKeySecret", e);
}
else {
logger.error("PullMessageTask_execute_error,messageType:"
+ messageType + ",queueName:" + queueName, e);
}
break;
}
catch (Exception e) {
logger.error("PullMessageTask_execute_error,messageType:"
+ messageType + ",queueName:" + queueName, e);
try {
Thread.sleep(sleepSecondWhenNoData);
}
catch (InterruptedException e1) {
logger.error("PullMessageTask_execute_error,messageType:"
+ messageType + ",queueName:" + queueName, e);
}
}
}
}
}
/**
* @param accessKeyId accessKeyId
* @param accessKeySecret accessKeySecret
* @param messageType
* @param queueName
* @param messageListener listener,
* @throws com.aliyuncs.exceptions.ClientException
* @throws ParseException
*/
public void startReceiveMsg(String accessKeyId, String accessKeySecret,
String messageType, String queueName, MessageListener messageListener)
throws com.aliyuncs.exceptions.ClientException, ParseException {
tokenGetter = new TokenGetterForAlicom(accessKeyId, accessKeySecret,
endpointNameForPop, regionIdForPop, domainForPop, null);
this.messageListener = messageListener;
isRunning = true;
PullMessageTask task = new PullMessageTask();
task.messageType = messageType;
task.queueName = queueName;
synchronized (sLockObjMap) {
lockObj = sLockObjMap.get(queueName);
if (lockObj == null) {
lockObj = new Object();
sLockObjMap.put(queueName, lockObj);
}
}
if (executorService == null) {
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(
pullMsgThreadSize,
new BasicThreadFactory.Builder()
.namingPattern(
"PullMessageTask-" + messageType + "-thread-pool-%d")
.daemon(true).build());
executorService = scheduledExecutorService;
}
for (int i = 0; i < pullMsgThreadSize; i++) {
executorService.execute(task);
}
}
/**
* @param accessKeyId accessKeyId
* @param accessKeySecret accessKeySecret
* @param messageType
* @param queueName
* @param messageListener listener,
* @throws com.aliyuncs.exceptions.ClientException
* @throws ParseException
*/
public void startReceiveMsgForVPC(String accessKeyId, String accessKeySecret,
String messageType, String queueName, String regionIdForPop,
String endpointNameForPop, String domainForPop, String mnsAccountEndpoint,
MessageListener messageListener)
throws com.aliyuncs.exceptions.ClientException, ParseException {
this.mnsAccountEndpoint = mnsAccountEndpoint;
tokenGetter = new TokenGetterForAlicom(accessKeyId, accessKeySecret,
endpointNameForPop, regionIdForPop, domainForPop, null);
this.messageListener = messageListener;
isRunning = true;
PullMessageTask task = new PullMessageTask();
task.messageType = messageType;
task.queueName = queueName;
synchronized (sLockObjMap) {
lockObj = sLockObjMap.get(queueName);
if (lockObj == null) {
lockObj = new Object();
sLockObjMap.put(queueName, lockObj);
}
}
if (executorService == null) {
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(
pullMsgThreadSize,
new BasicThreadFactory.Builder()
.namingPattern(
"PullMessageTask-" + messageType + "-thread-pool-%d")
.daemon(true).build());
executorService = scheduledExecutorService;
}
for (int i = 0; i < pullMsgThreadSize; i++) {
executorService.execute(task);
}
}
/**
*
* @param accessKeyId accessKeyId
* @param accessKeySecret accessKeySecret
* @param ownerId ownerId
* @param messageType
* @param queueName
* @param messageListener listener
* @throws com.aliyuncs.exceptions.ClientException
* @throws ParseException
*/
public void startReceiveMsgForPartnerUser(String accessKeyId, String accessKeySecret,
Long ownerId, String messageType, String queueName,
MessageListener messageListener)
throws com.aliyuncs.exceptions.ClientException, ParseException {
tokenGetter = new TokenGetterForAlicom(accessKeyId, accessKeySecret,
endpointNameForPop, regionIdForPop, domainForPop, ownerId);
this.messageListener = messageListener;
isRunning = true;
PullMessageTask task = new PullMessageTask();
task.messageType = messageType;
task.queueName = queueName;
synchronized (sLockObjMap) {
lockObj = sLockObjMap.get(queueName);
if (lockObj == null) {
lockObj = new Object();
sLockObjMap.put(queueName, lockObj);
}
}
if (executorService == null) {
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(
pullMsgThreadSize,
new BasicThreadFactory.Builder()
.namingPattern(
"PullMessageTask-" + messageType + "-thread-pool-%d")
.daemon(true).build());
executorService = scheduledExecutorService;
}
for (int i = 0; i < pullMsgThreadSize; i++) {
executorService.execute(task);
}
}
public void stop() {
isRunning = false;
}
}

@ -0,0 +1,24 @@
/*
* 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.alicloud.sms.base;
import com.aliyun.mns.model.Message;
public interface MessageListener {
boolean dealMessage(Message message);
}

@ -0,0 +1,82 @@
/*
* 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.alicloud.sms.base;
import com.aliyuncs.RpcAcsRequest;
public class QueryTokenForMnsQueueRequest
extends RpcAcsRequest<QueryTokenForMnsQueueResponse> {
private String resourceOwnerAccount;
private String messageType;
private Long resourceOwnerId;
private Long ownerId;
public QueryTokenForMnsQueueRequest() {
super("Dybaseapi", "2017-05-25", "QueryTokenForMnsQueue");
}
public String getResourceOwnerAccount() {
return this.resourceOwnerAccount;
}
public void setResourceOwnerAccount(String resourceOwnerAccount) {
this.resourceOwnerAccount = resourceOwnerAccount;
if (resourceOwnerAccount != null) {
this.putQueryParameter("ResourceOwnerAccount", resourceOwnerAccount);
}
}
public String getMessageType() {
return this.messageType;
}
public void setMessageType(String messageType) {
this.messageType = messageType;
if (messageType != null) {
this.putQueryParameter("MessageType", messageType);
}
}
public Long getResourceOwnerId() {
return this.resourceOwnerId;
}
public void setResourceOwnerId(Long resourceOwnerId) {
this.resourceOwnerId = resourceOwnerId;
if (resourceOwnerId != null) {
this.putQueryParameter("ResourceOwnerId", resourceOwnerId.toString());
}
}
public Long getOwnerId() {
return this.ownerId;
}
public void setOwnerId(Long ownerId) {
this.ownerId = ownerId;
if (ownerId != null) {
this.putQueryParameter("OwnerId", ownerId.toString());
}
}
public Class<QueryTokenForMnsQueueResponse> getResponseClass() {
return QueryTokenForMnsQueueResponse.class;
}
}

@ -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.alicloud.sms.base;
import com.aliyuncs.AcsResponse;
import com.aliyuncs.transform.UnmarshallerContext;
public class QueryTokenForMnsQueueResponse extends AcsResponse {
private String requestId;
private String code;
private String message;
private QueryTokenForMnsQueueResponse.MessageTokenDTO messageTokenDTO;
public QueryTokenForMnsQueueResponse() {
}
public String getRequestId() {
return this.requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public String getCode() {
return this.code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
public QueryTokenForMnsQueueResponse.MessageTokenDTO getMessageTokenDTO() {
return this.messageTokenDTO;
}
public void setMessageTokenDTO(
QueryTokenForMnsQueueResponse.MessageTokenDTO messageTokenDTO) {
this.messageTokenDTO = messageTokenDTO;
}
public QueryTokenForMnsQueueResponse getInstance(UnmarshallerContext context) {
return QueryTokenForMnsQueueResponseUnmarshaller.unmarshall(this, context);
}
public static class MessageTokenDTO {
private String accessKeyId;
private String accessKeySecret;
private String securityToken;
private String createTime;
private String expireTime;
public MessageTokenDTO() {
}
public String getAccessKeyId() {
return this.accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessKeySecret() {
return this.accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public String getSecurityToken() {
return this.securityToken;
}
public void setSecurityToken(String securityToken) {
this.securityToken = securityToken;
}
public String getCreateTime() {
return this.createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getExpireTime() {
return this.expireTime;
}
public void setExpireTime(String expireTime) {
this.expireTime = expireTime;
}
}
}

@ -0,0 +1,48 @@
/*
* 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.alicloud.sms.base;
import com.aliyuncs.transform.UnmarshallerContext;
public class QueryTokenForMnsQueueResponseUnmarshaller {
public QueryTokenForMnsQueueResponseUnmarshaller() {
}
public static QueryTokenForMnsQueueResponse unmarshall(
QueryTokenForMnsQueueResponse queryTokenForMnsQueueResponse,
UnmarshallerContext context) {
queryTokenForMnsQueueResponse.setRequestId(
context.stringValue("QueryTokenForMnsQueueResponse.RequestId"));
queryTokenForMnsQueueResponse
.setCode(context.stringValue("QueryTokenForMnsQueueResponse.Code"));
queryTokenForMnsQueueResponse
.setMessage(context.stringValue("QueryTokenForMnsQueueResponse.Message"));
QueryTokenForMnsQueueResponse.MessageTokenDTO messageTokenDTO = new QueryTokenForMnsQueueResponse.MessageTokenDTO();
messageTokenDTO.setAccessKeyId(context.stringValue(
"QueryTokenForMnsQueueResponse.MessageTokenDTO.AccessKeyId"));
messageTokenDTO.setAccessKeySecret(context.stringValue(
"QueryTokenForMnsQueueResponse.MessageTokenDTO.AccessKeySecret"));
messageTokenDTO.setSecurityToken(context.stringValue(
"QueryTokenForMnsQueueResponse.MessageTokenDTO.SecurityToken"));
messageTokenDTO.setCreateTime(context
.stringValue("QueryTokenForMnsQueueResponse.MessageTokenDTO.CreateTime"));
messageTokenDTO.setExpireTime(context
.stringValue("QueryTokenForMnsQueueResponse.MessageTokenDTO.ExpireTime"));
queryTokenForMnsQueueResponse.setMessageTokenDTO(messageTokenDTO);
return queryTokenForMnsQueueResponse;
}
}

@ -0,0 +1,96 @@
/*
* 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.alicloud.sms.base;
import com.aliyun.mns.client.CloudQueue;
import com.aliyun.mns.client.MNSClient;
/**
* token
*
*/
public class TokenForAlicom {
private String messageType;
private String token;
private Long expireTime;
private String tempAccessKeyId;
private String tempAccessKeySecret;
private MNSClient client;
private CloudQueue queue;
public String getMessageType() {
return messageType;
}
public void setMessageType(String messageType) {
this.messageType = messageType;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
public String getTempAccessKeyId() {
return tempAccessKeyId;
}
public void setTempAccessKeyId(String tempAccessKeyId) {
this.tempAccessKeyId = tempAccessKeyId;
}
public String getTempAccessKeySecret() {
return tempAccessKeySecret;
}
public void setTempAccessKeySecret(String tempAccessKeySecret) {
this.tempAccessKeySecret = tempAccessKeySecret;
}
public MNSClient getClient() {
return client;
}
public void setClient(MNSClient client) {
this.client = client;
}
public CloudQueue getQueue() {
return queue;
}
public void setQueue(CloudQueue queue) {
this.queue = queue;
}
public void closeClient() {
if (client != null) {
this.client.close();
}
}
}

@ -0,0 +1,142 @@
/*
* 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.alicloud.sms.base;
import com.aliyun.mns.client.CloudAccount;
import com.aliyun.mns.client.CloudQueue;
import com.aliyun.mns.client.MNSClient;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* token
*
*/
public class TokenGetterForAlicom {
private Log logger = LogFactory.getLog(TokenGetterForAlicom.class);
private String accessKeyId;
private String accessKeySecret;
private String endpointNameForPop;
private String regionIdForPop;
private String domainForPop;
private IAcsClient iAcsClient;
private Long ownerId;
private final static String productName = "Dybaseapi";
private long bufferTime = 1000 * 60 * 2;// 过期时间小于2分钟则重新获取防止服务器时间误差
private final Object lock = new Object();
private ConcurrentMap<String, TokenForAlicom> tokenMap = new ConcurrentHashMap<String, TokenForAlicom>();
public TokenGetterForAlicom(String accessKeyId, String accessKeySecret,
String endpointNameForPop, String regionIdForPop, String domainForPop,
Long ownerId) throws ClientException {
this.accessKeyId = accessKeyId;
this.accessKeySecret = accessKeySecret;
this.endpointNameForPop = endpointNameForPop;
this.regionIdForPop = regionIdForPop;
this.domainForPop = domainForPop;
this.ownerId = ownerId;
init();
}
private void init() throws ClientException {
DefaultProfile.addEndpoint(endpointNameForPop, regionIdForPop, productName,
domainForPop);
IClientProfile profile = DefaultProfile.getProfile(regionIdForPop, accessKeyId,
accessKeySecret);
profile.getHttpClientConfig().setCompatibleMode(true);
iAcsClient = new DefaultAcsClient(profile);
}
private TokenForAlicom getTokenFromRemote(String messageType)
throws ServerException, ClientException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
QueryTokenForMnsQueueRequest request = new QueryTokenForMnsQueueRequest();
request.setAcceptFormat(FormatType.JSON);
request.setMessageType(messageType);
request.setOwnerId(ownerId);
request.setProtocol(ProtocolType.HTTPS);
request.setMethod(MethodType.POST);
QueryTokenForMnsQueueResponse response = iAcsClient.getAcsResponse(request);
String resultCode = response.getCode();
if (resultCode != null && "OK".equals(resultCode)) {
QueryTokenForMnsQueueResponse.MessageTokenDTO dto = response
.getMessageTokenDTO();
TokenForAlicom token = new TokenForAlicom();
String timeStr = dto.getExpireTime();
token.setMessageType(messageType);
token.setExpireTime(df.parse(timeStr).getTime());
token.setToken(dto.getSecurityToken());
token.setTempAccessKeyId(dto.getAccessKeyId());
token.setTempAccessKeySecret(dto.getAccessKeySecret());
return token;
}
else {
logger.error("getTokenFromRemote_error,messageType:" + messageType + ",code:"
+ response.getCode() + ",message:" + response.getMessage());
throw new ServerException(response.getCode(), response.getMessage());
}
}
public TokenForAlicom getTokenByMessageType(String messageType, String queueName,
String mnsAccountEndpoint)
throws ServerException, ClientException, ParseException {
TokenForAlicom token = tokenMap.get(messageType);
Long now = System.currentTimeMillis();
if (token == null || (token.getExpireTime() - now) < bufferTime) {// 过期时间小于2分钟则重新获取防止服务器时间误差
synchronized (lock) {
token = tokenMap.get(messageType);
if (token == null || (token.getExpireTime() - now) < bufferTime) {
TokenForAlicom oldToken = null;
if (token != null) {
oldToken = token;
}
token = getTokenFromRemote(messageType);
// 因为换token时需要重建client和关闭老的client所以创建client的代码和创建token放在一起
CloudAccount account = new CloudAccount(token.getTempAccessKeyId(),
token.getTempAccessKeySecret(), mnsAccountEndpoint,
token.getToken());
// logger.warn("ak:"+token.getTempAccessKey());
// logger.warn("token:"+token.getToken());
MNSClient client = account.getMNSClient();
CloudQueue queue = client.getQueueRef(queueName);
token.setClient(client);
token.setQueue(queue);
tokenMap.put(messageType, token);
if (oldToken != null) {
oldToken.closeClient();
}
}
}
}
return token;
}
}

@ -0,0 +1,49 @@
/*
* 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.alicloud.sms.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alicloud.context.sms.SmsConfigProperties;
import org.springframework.cloud.alicloud.sms.ISmsService;
import org.springframework.cloud.alicloud.sms.SmsInitializerEventListener;
import org.springframework.cloud.alicloud.sms.SmsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
/**
* @author pbting
*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(value = SendSmsRequest.class)
@ConditionalOnProperty(value = "spring.cloud.alicloud.sms.enable", matchIfMissing = true)
public class SmsAutoConfiguration {
@Bean
public SmsServiceImpl smsService(SmsConfigProperties smsConfigProperties) {
return new SmsServiceImpl(smsConfigProperties);
}
@Bean
public SmsInitializerEventListener smsInitializePostListener(
SmsConfigProperties msConfigProperties, ISmsService smsService) {
return new SmsInitializerEventListener(msConfigProperties, smsService);
}
}

@ -0,0 +1,106 @@
/*
* 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.alicloud.sms.endpoint;
import com.aliyuncs.dysmsapi.model.v20170525.SendBatchSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantLock;
/**
*
*/
public final class EndpointManager {
private final static int BACKLOG_SIZE = 20;
private final static ReentrantLock SEND_REENTRANT_LOCK = new ReentrantLock(true);
private final static ReentrantLock SEND_BATCH_REENTRANT_LOCK = new ReentrantLock(
true);
private final static LinkedBlockingQueue<SendSmsRequest> SEND_SMS_REQUESTS = new LinkedBlockingQueue(
BACKLOG_SIZE);
private final static LinkedBlockingQueue<SendBatchSmsRequest> SEND_BATCH_SMS_REQUESTS = new LinkedBlockingQueue(
BACKLOG_SIZE);
private final static LinkedBlockingQueue<ReceiveMessageEntity> RECEIVE_MESSAGE_ENTITIES = new LinkedBlockingQueue(
BACKLOG_SIZE);
public static void addSendSmsRequest(SendSmsRequest sendSmsRequest) {
if (SEND_SMS_REQUESTS.offer(sendSmsRequest)) {
return;
}
try {
SEND_REENTRANT_LOCK.lock();
SEND_SMS_REQUESTS.poll();
SEND_SMS_REQUESTS.offer(sendSmsRequest);
}
finally {
SEND_REENTRANT_LOCK.unlock();
}
}
public static void addSendBatchSmsRequest(SendBatchSmsRequest sendBatchSmsRequest) {
if (SEND_BATCH_SMS_REQUESTS.offer(sendBatchSmsRequest)) {
return;
}
try {
SEND_BATCH_REENTRANT_LOCK.lock();
SEND_BATCH_SMS_REQUESTS.poll();
SEND_BATCH_SMS_REQUESTS.offer(sendBatchSmsRequest);
}
finally {
SEND_BATCH_REENTRANT_LOCK.unlock();
}
}
public static void addReceiveMessageEntity(
ReceiveMessageEntity receiveMessageEntity) {
if (RECEIVE_MESSAGE_ENTITIES.offer(receiveMessageEntity)) {
return;
}
RECEIVE_MESSAGE_ENTITIES.poll();
RECEIVE_MESSAGE_ENTITIES.offer(receiveMessageEntity);
}
public static Map<String, Object> getSmsEndpointMessage() {
List<SendSmsRequest> sendSmsRequests = new LinkedList<>();
List<SendBatchSmsRequest> sendBatchSmsRequests = new LinkedList<>();
List<ReceiveMessageEntity> receiveMessageEntities = new LinkedList<>();
try {
SEND_REENTRANT_LOCK.lock();
SEND_BATCH_REENTRANT_LOCK.lock();
sendSmsRequests.addAll(SEND_SMS_REQUESTS);
sendBatchSmsRequests.addAll(SEND_BATCH_SMS_REQUESTS);
}
finally {
SEND_REENTRANT_LOCK.unlock();
SEND_BATCH_REENTRANT_LOCK.unlock();
}
receiveMessageEntities.addAll(RECEIVE_MESSAGE_ENTITIES);
Map<String, Object> endpointMessages = new HashMap<>();
endpointMessages.put("send-sms-request", sendSmsRequests);
endpointMessages.put("send-batch-sms-request", sendBatchSmsRequests);
endpointMessages.put("message-listener", receiveMessageEntities);
return endpointMessages;
}
}

@ -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.alicloud.sms.endpoint;
import org.springframework.cloud.alicloud.sms.base.MessageListener;
import java.io.Serializable;
/**
* @author pbting
*/
public class ReceiveMessageEntity implements Serializable {
private String messageType;
private String queueName;
private MessageListener messageListener;
public ReceiveMessageEntity(String messageType, String queueName,
MessageListener messageListener) {
this.messageType = messageType;
this.queueName = queueName;
this.messageListener = messageListener;
}
public String getMessageType() {
return messageType;
}
public void setMessageType(String messageType) {
this.messageType = messageType;
}
public String getQueueName() {
return queueName;
}
public void setQueueName(String queueName) {
this.queueName = queueName;
}
public MessageListener getMessageListener() {
return messageListener;
}
public void setMessageListener(MessageListener messageListener) {
this.messageListener = messageListener;
}
}

@ -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.alicloud.sms.endpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import java.util.Map;
@Endpoint(id = "sms-info")
public class SmsEndpoint {
@ReadOperation
public Map<String, Object> invoke() {
return EndpointManager.getSmsEndpointMessage();
}
}

@ -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.alicloud.sms.endpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
@ConditionalOnWebApplication
@ConditionalOnClass(Endpoint.class)
public class SmsEndpointAutoConfiguration {
@Bean
public SmsEndpoint smsEndpoint() {
return new SmsEndpoint();
}
}

@ -0,0 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alicloud.sms.config.SmsAutoConfiguration,\
org.springframework.cloud.alicloud.sms.endpoint.SmsEndpointAutoConfiguration

@ -15,5 +15,6 @@
<module>spring-cloud-starter-alicloud-acm</module>
<module>spring-cloud-starter-alicloud-ans</module>
<module>spring-cloud-starter-alicloud-schedulerx</module>
<module>spring-cloud-starter-alicloud-sms</module>
</modules>
</project>

@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-alicloud-sms</artifactId>
<name>Spring Cloud Starter Alibaba Cloud SMS</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-sms</artifactId>
</dependency>
</dependencies>
</project>
Loading…
Cancel
Save