diff --git a/hippo4j-example/hippo4j-spring-boot-starter-example/src/main/resources/application.properties b/hippo4j-example/hippo4j-spring-boot-starter-example/src/main/resources/application.properties
index 3d283f86..8d3ade56 100644
--- a/hippo4j-example/hippo4j-spring-boot-starter-example/src/main/resources/application.properties
+++ b/hippo4j-example/hippo4j-spring-boot-starter-example/src/main/resources/application.properties
@@ -24,3 +24,20 @@ spring.dynamic.thread-pool.monitor.collect-types=server,micrometer
spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic,web
spring.dynamic.thread-pool.monitor.initial-delay=10000
spring.dynamic.thread-pool.monitor.collect-interval=5000
+
+# for email notify
+## simple example
+spring.mail.host=smtp.qq.com
+spring.mail.username=xxx@qq.com
+spring.mail.password=xxx
+
+## for gmail need ssl
+#spring.mail.host=smtp.gmail.com
+#spring.mail.username=xxx@gmail.com
+#spring.mail.password=xxx
+#spring.mail.protocol=smtp
+#spring.mail.properties.mail.smtp.auth=true
+#spring.mail.properties.mail.smtp.port=465
+#spring.mail.properties.mail.smtp.starttls.enable=true
+#spring.mail.properties.mail.smtp.starttls.required=true
+#spring.mail.properties.mail.smtp.ssl.enable=true
diff --git a/hippo4j-message/pom.xml b/hippo4j-message/pom.xml
index 3a2b349d..02de5617 100644
--- a/hippo4j-message/pom.xml
+++ b/hippo4j-message/pom.xml
@@ -24,6 +24,14 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+ org.freemarker
+ freemarker
+
@@ -33,6 +41,7 @@
**/*.txt
**/*.json
+ **/*.ftl
diff --git a/hippo4j-message/src/main/java/cn/hippo4j/message/config/MessageConfiguration.java b/hippo4j-message/src/main/java/cn/hippo4j/message/config/MessageConfiguration.java
index e495cfe4..63899819 100644
--- a/hippo4j-message/src/main/java/cn/hippo4j/message/config/MessageConfiguration.java
+++ b/hippo4j-message/src/main/java/cn/hippo4j/message/config/MessageConfiguration.java
@@ -19,6 +19,7 @@ package cn.hippo4j.message.config;
import cn.hippo4j.message.api.NotifyConfigBuilder;
import cn.hippo4j.message.platform.DingSendMessageHandler;
+import cn.hippo4j.message.platform.EmailSendMessageHandler;
import cn.hippo4j.message.platform.LarkSendMessageHandler;
import cn.hippo4j.message.platform.WeChatSendMessageHandler;
import cn.hippo4j.message.service.AlarmControlHandler;
@@ -57,4 +58,9 @@ public class MessageConfiguration {
public SendMessageHandler weChatSendMessageHandler() {
return new WeChatSendMessageHandler();
}
+
+ @Bean
+ public EmailSendMessageHandler emailSendMessageHandler() {
+ return new EmailSendMessageHandler();
+ }
}
diff --git a/hippo4j-message/src/main/java/cn/hippo4j/message/enums/NotifyPlatformEnum.java b/hippo4j-message/src/main/java/cn/hippo4j/message/enums/NotifyPlatformEnum.java
index 5ffbad3b..0d5aa7c3 100644
--- a/hippo4j-message/src/main/java/cn/hippo4j/message/enums/NotifyPlatformEnum.java
+++ b/hippo4j-message/src/main/java/cn/hippo4j/message/enums/NotifyPlatformEnum.java
@@ -35,5 +35,10 @@ public enum NotifyPlatformEnum {
/**
* WECHAT
*/
- WECHAT
+ WECHAT,
+
+ /**
+ * EMAIL
+ */
+ EMAIL
}
diff --git a/hippo4j-message/src/main/java/cn/hippo4j/message/platform/EmailSendMessageHandler.java b/hippo4j-message/src/main/java/cn/hippo4j/message/platform/EmailSendMessageHandler.java
new file mode 100644
index 00000000..9edd3646
--- /dev/null
+++ b/hippo4j-message/src/main/java/cn/hippo4j/message/platform/EmailSendMessageHandler.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.message.platform;
+
+import cn.hippo4j.common.toolkit.FileUtil;
+import cn.hippo4j.common.toolkit.Singleton;
+import cn.hippo4j.message.dto.NotifyConfigDTO;
+import cn.hippo4j.message.enums.NotifyPlatformEnum;
+import cn.hippo4j.message.platform.constant.EmailAlarmConstants;
+import cn.hippo4j.message.request.AlarmNotifyRequest;
+import cn.hippo4j.message.request.ChangeParameterNotifyRequest;
+import cn.hippo4j.message.service.SendMessageHandler;
+import freemarker.cache.StringTemplateLoader;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.beanutils.BeanUtils;
+import org.springframework.boot.autoconfigure.mail.MailProperties;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+
+import javax.annotation.Resource;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+
+/**
+ * Send Email notification message.
+ */
+@Slf4j
+public class EmailSendMessageHandler implements SendMessageHandler {
+
+ @Resource
+ private JavaMailSender emailSender;
+
+ @Resource
+ MailProperties mailProperties;
+
+ @Override
+ public String getType() {
+ return NotifyPlatformEnum.EMAIL.name();
+ }
+
+ @Override
+ public void sendAlarmMessage(NotifyConfigDTO notifyConfig, AlarmNotifyRequest alarmNotifyRequest) {
+ try {
+ String emailAlarmTxtKey = "message/robot/dynamic-thread-pool/email-alarm.ftl";
+ Map dataModel = getDataModel(alarmNotifyRequest);
+ dataModel.put("interval", notifyConfig.getInterval().toString());
+ String emailAlarmTxt = Singleton.get(emailAlarmTxtKey, () -> FileUtil.readUtf8String(emailAlarmTxtKey));
+ String renderedEmailAlarmTxt = render(dataModel, emailAlarmTxt);
+ String alarmSubject = render(dataModel, EmailAlarmConstants.Email_ALARM_TITLE);
+ String[] recipients = notifyConfig.getReceives().split(",");
+ execute(recipients, alarmSubject, renderedEmailAlarmTxt);
+ } catch (Exception e) {
+ log.error("Email failed to send message", e);
+ }
+ }
+
+ @Override
+ public void sendChangeMessage(NotifyConfigDTO notifyConfig, ChangeParameterNotifyRequest changeParameterNotifyRequest) {
+ try {
+ String emailConfigTxtKey = "message/robot/dynamic-thread-pool/email-config.ftl";
+ Map dataModel = getDataModel(changeParameterNotifyRequest);
+ String emailConfigTxt = Singleton.get(emailConfigTxtKey, () -> FileUtil.readUtf8String(emailConfigTxtKey));
+ String renderedEmailConfigTxt = render(dataModel, emailConfigTxt);
+ String configSubject = render(dataModel, EmailAlarmConstants.Email_NOTICE_TITLE);
+ String[] recipients = notifyConfig.getReceives().split(",");
+ execute(recipients, configSubject, renderedEmailConfigTxt);
+ } catch (Exception e) {
+ log.error("Email failed to send message", e);
+ }
+ }
+
+ private String render(Map dataModel, String stringTemplate) {
+ Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
+ StringTemplateLoader stringLoader = new StringTemplateLoader();
+ stringLoader.putTemplate("renderTemplate", stringTemplate);
+ cfg.setTemplateLoader(stringLoader);
+ Writer out = new StringWriter(2048);
+ try {
+ Template tpl = cfg.getTemplate("renderTemplate", "UTF-8");
+ tpl.process(dataModel, out);
+ } catch (Exception e) {
+ log.error("failed to render template,dataModel:{},stringTemplate:{}", dataModel, stringTemplate);
+ }
+ return out.toString();
+ }
+
+ @SneakyThrows
+ private Map getDataModel(Object bean) {
+ Map dataModel = BeanUtils.describe(bean);
+ dataModel.put("from", mailProperties.getUsername());
+ dataModel.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+ return dataModel;
+ }
+
+ private void execute(String[] to, String subject, String htmlBody) throws MessagingException {
+ MimeMessage message = emailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message, "UTF-8");
+ helper.setFrom(mailProperties.getUsername());
+ helper.setTo(to);
+ helper.setSubject(subject);
+ helper.setText(htmlBody, true);
+ emailSender.send(message);
+ }
+}
\ No newline at end of file
diff --git a/hippo4j-message/src/main/java/cn/hippo4j/message/platform/constant/EmailAlarmConstants.java b/hippo4j-message/src/main/java/cn/hippo4j/message/platform/constant/EmailAlarmConstants.java
new file mode 100644
index 00000000..b20c45dc
--- /dev/null
+++ b/hippo4j-message/src/main/java/cn/hippo4j/message/platform/constant/EmailAlarmConstants.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.message.platform.constant;
+
+/**
+ * Email alarm constants.
+ */
+public class EmailAlarmConstants {
+
+ /**
+ * Thread Pool Alert Notification Title
+ */
+ public static String Email_ALARM_TITLE = "【Hippo4J】${active}-${threadPoolId} 线程池 ${notifyTypeEnum} 预警";
+
+ /**
+ * Thread pool parameter change notification title
+ */
+ public static String Email_NOTICE_TITLE = "【Hippo4J】${active}-${threadPoolId} 线程池参数变更通知";
+}
\ No newline at end of file
diff --git a/hippo4j-message/src/main/resources/message/robot/dynamic-thread-pool/email-alarm.ftl b/hippo4j-message/src/main/resources/message/robot/dynamic-thread-pool/email-alarm.ftl
new file mode 100644
index 00000000..ef1fd737
--- /dev/null
+++ b/hippo4j-message/src/main/resources/message/robot/dynamic-thread-pool/email-alarm.ftl
@@ -0,0 +1,28 @@
+[警报] ${active} - 动态线程池运行告警(${notifyTypeEnum})
+
+ - 线程池ID:${threadPoolId}
+ - 应用名称:${appName}
+ - 应用实例:${identify}
+ - 核心线程数:${corePoolSize}
+ - 最大线程数:${maximumPoolSize}
+ - 当前线程数:${poolSize}
+ - 活跃线程数:${activeCount}
+ - 同存最大线程数:${largestPoolSize}
+ - 线程池任务总量:${completedTaskCount}
+ - 队列类型:${queueName}
+ - 队列容量:${capacity}
+ - 队列元素个数:${queueSize}
+ - 队列剩余个数:${remainingCapacity}
+ - 拒绝策略:${rejectedExecutionHandlerName}
+ - 拒绝策略执行次数:${rejectCountNum}
+ - OWNER:${from}
+ - 提示:${interval} 分钟内此线程池不会重复告警(可配置)
+
+
+ 播报时间:${date}
+
+
\ No newline at end of file
diff --git a/hippo4j-message/src/main/resources/message/robot/dynamic-thread-pool/email-config.ftl b/hippo4j-message/src/main/resources/message/robot/dynamic-thread-pool/email-config.ftl
new file mode 100644
index 00000000..620b3305
--- /dev/null
+++ b/hippo4j-message/src/main/resources/message/robot/dynamic-thread-pool/email-config.ftl
@@ -0,0 +1,24 @@
+[通知] ${active} - 动态线程池参数变更
+
+ - 线程池ID:${threadPoolId}
+ - 应用名称:${appName}
+ - 应用实例:${identify}
+ - 核心线程数:${beforeCorePoolSize} -> ${nowCorePoolSize}
+ - 核心线程超时:${beforeMaximumPoolSize} -> ${nowMaximumPoolSize}
+ - 线程存活时间:${beforeAllowsCoreThreadTimeOut} -> ${nowAllowsCoreThreadTimeOut}
+ - 执行超时时间:${beforeKeepAliveTime} -> ${nowKeepAliveTime}
+ - 队列类型:${blockingQueueName}
+ - 队列容量:${beforeQueueCapacity} -> ${nowQueueCapacity}
+ - AGO 拒绝策略:${beforeRejectedName}
+ - NOW 拒绝策略:${nowRejectedName}
+ - OWNER: ${from}
+ - 提示:动态线程池配置变更实时通知(无限制)
+
+
+ 播报时间:${date}
+
+
diff --git a/pom.xml b/pom.xml
index f19c94e8..3bc533f3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -95,6 +95,7 @@
3.0
2.22.1
3.1.0
+ 2.3.31
@@ -126,6 +127,11 @@
netty-all
${netty.version}
+
+ org.freemarker
+ freemarker
+ ${freemarker.version}
+