Browse Source

Merge pull request #4537 from CalvinKris/alert_plugin_design

[Feature-#3049] Alert Plugin Design(merge alert_plugin_design to dev
pull/3/MERGE
gaojun2048 4 years ago committed by GitHub
parent
commit
acee4e00ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      LICENSE
  2. 25
      docker/kubernetes/dolphinscheduler/requirements.yaml~HEAD
  3. 25
      docker/kubernetes/dolphinscheduler/requirements.yaml~HEAD_0
  4. 25
      docker/kubernetes/dolphinscheduler/requirements.yaml~dev
  5. 25
      docker/kubernetes/dolphinscheduler/requirements.yaml~dev_0
  6. 82
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/pom.xml
  7. 41
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannel.java
  8. 89
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java
  9. 34
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertPlugin.java
  10. 57
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java
  11. 204
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java
  12. 48
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java
  13. 57
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSenderTest.java
  14. 128
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml
  15. 69
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannel.java
  16. 137
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactory.java
  17. 33
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertPlugin.java
  18. 75
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailConstants.java
  19. 22
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtils.java
  20. 65
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailParamsConstants.java
  21. 429
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailSender.java
  22. 21
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/AlertTemplate.java
  23. 53
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplate.java
  24. 75
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactoryTest.java
  25. 161
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelTest.java
  26. 8
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtilsTest.java
  27. 136
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/MailUtilsTest.java
  28. 105
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplateTest.java
  29. 76
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/pom.xml
  30. 41
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannel.java
  31. 78
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactory.java
  32. 35
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertConstants.java
  33. 34
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPlugin.java
  34. 166
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSender.java
  35. 44
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactoryTest.java
  36. 104
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelTest.java
  37. 38
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPluginTest.java
  38. 46
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSenderTest.java
  39. 79
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/pom.xml
  40. 32
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/OSUtils.java
  41. 62
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtils.java
  42. 40
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannel.java
  43. 71
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactory.java
  44. 35
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertPlugin.java
  45. 40
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptParamsConstants.java
  46. 74
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSender.java
  47. 49
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptType.java
  48. 51
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/StreamGobbler.java
  49. 37
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtilsTest.java
  50. 48
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactoryTest.java
  51. 58
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSenderTest.java
  52. 25
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/example.sh
  53. 13
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/scriptTest.sh
  54. 78
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/pom.xml
  55. 41
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannel.java
  56. 94
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactory.java
  57. 34
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertConstants.java
  58. 56
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertParamsConstants.java
  59. 34
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertPlugin.java
  60. 335
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSender.java
  61. 48
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactoryTest.java
  62. 89
      dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSenderTest.java
  63. 41
      dolphinscheduler-alert-plugin/pom.xml
  64. 79
      dolphinscheduler-alert/pom.xml
  65. 126
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/AlertServer.java
  66. 110
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EmailManager.java
  67. 59
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EnterpriseWeChatManager.java
  68. 10
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/MsgManager.java
  69. 29
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AbstractDolphinPluginManager.java
  70. 99
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AlertPluginManager.java
  71. 139
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginClassLoader.java
  72. 139
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginDiscovery.java
  73. 194
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginLoader.java
  74. 121
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginManagerConfig.java
  75. 147
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/EmailAlertPlugin.java
  76. 67
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/processor/AlertRequestProcessor.java
  77. 174
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/runner/AlertSender.java
  78. 172
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/Constants.java
  79. 141
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/DingTalkUtils.java
  80. 286
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/EnterpriseWeChatUtils.java
  81. 375
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/MailUtils.java
  82. 2
      dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/PropertyUtils.java
  83. 48
      dolphinscheduler-alert/src/main/resources/alert.properties
  84. 91
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/AlertServerTest.java
  85. 65
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/plugin/AlertPluginManagerTest.java
  86. 60
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginLoaderTest.java
  87. 251
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/plugin/EmailAlertPluginTest.java
  88. 61
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/processor/AlertRequestProcessorTest.java
  89. 181
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/runner/AlertSenderTest.java
  90. 65
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactoryTest.java
  91. 96
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/template/impl/DefaultHTMLTemplateTest.java
  92. 120
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/DingTalkUtilsTest.java
  93. 283
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/EnterpriseWeChatUtilsTest.java
  94. 10
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/FuncUtilsTest.java
  95. 190
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/MailUtilsTest.java
  96. 17
      dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/PropertyUtilsTest.java
  97. 113
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertGroupController.java
  98. 240
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceController.java
  99. 113
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ExecutorController.java
  100. 132
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/SchedulerController.java
  101. Some files were not shown because too many files have changed in this diff Show More

4
LICENSE

@ -216,3 +216,7 @@ The text of each license is the standard Apache 2.0 license.
ScriptRunner from https://github.com/mybatis/mybatis-3 Apache 2.0
mvnw files from https://github.com/takari/maven-wrapper Apache 2.0
PropertyPlaceholderHelper from https://github.com/spring-projects/spring-framework Apache 2.0
DolphinPluginClassLoader from https://github.com/prestosql/presto Apache 2.0
DolphinPluginDiscovery from https://github.com/prestosql/presto Apache 2.0
DolphinPluginLoader from https://github.com/prestosql/presto Apache 2.0

25
docker/kubernetes/dolphinscheduler/requirements.yaml~HEAD

@ -0,0 +1,25 @@
#
# 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.
#
dependencies:
- name: postgresql
version: 8.x.x
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: zookeeper
version: 5.x.x
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled

25
docker/kubernetes/dolphinscheduler/requirements.yaml~HEAD_0

@ -0,0 +1,25 @@
#
# 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.
#
dependencies:
- name: postgresql
version: 8.x.x
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: zookeeper
version: 5.x.x
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled

25
docker/kubernetes/dolphinscheduler/requirements.yaml~dev

@ -0,0 +1,25 @@
#
# 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.
#
dependencies:
- name: postgresql
version: 8.x.x
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: zookeeper
version: 5.x.x
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled

25
docker/kubernetes/dolphinscheduler/requirements.yaml~dev_0

@ -0,0 +1,25 @@
#
# 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.
#
dependencies:
- name: postgresql
version: 8.x.x
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: zookeeper
version: 5.x.x
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled

82
dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/pom.xml

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dolphinscheduler-alert-plugin</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<version>1.3.4-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-alert-dingtalk</artifactId>
<packaging>dolphinscheduler-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>dolphinscheduler-alert-dingtalk-${project.version}</finalName>
</build>
</project>

41
dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannel.java

@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.dingtalk;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertData;
import org.apache.dolphinscheduler.spi.alert.AlertInfo;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import java.util.Map;
/**
* DingTalkAlertChannel
*/
public class DingTalkAlertChannel implements AlertChannel {
@Override
public AlertResult process(AlertInfo alertInfo) {
AlertData alertData = alertInfo.getAlertData();
String alertParams = alertInfo.getAlertParams();
Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
return new DingTalkSender(paramsMap).sendDingTalkMsg(alertData.getTitle(), alertData.getContent());
}
}

89
dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java

@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.dingtalk;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import org.apache.dolphinscheduler.spi.params.InputParam;
import org.apache.dolphinscheduler.spi.params.PasswordParam;
import org.apache.dolphinscheduler.spi.params.RadioParam;
import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.params.base.Validate;
import java.util.Arrays;
import java.util.List;
/**
* DingTalkAlertChannelFactory
*/
public class DingTalkAlertChannelFactory implements AlertChannelFactory {
@Override
public String getName() {
return "DingTalk";
}
@Override
public List<PluginParams> getParams() {
InputParam webHookParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK, DingTalkParamsConstants.DING_TALK_WEB_HOOK)
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam keywordParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD, DingTalkParamsConstants.DING_TALK_KEYWORD)
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
RadioParam isEnableProxy =
RadioParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE, DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE)
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.setValue(true)
.addValidate(Validate.newBuilder()
.setRequired(false)
.build())
.build();
InputParam proxyParam =
InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PROXY, DingTalkParamsConstants.DING_TALK_PROXY)
.addValidate(Validate.newBuilder()
.setRequired(false).build())
.build();
InputParam portParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PORT, DingTalkParamsConstants.DING_TALK_PORT)
.addValidate(Validate.newBuilder()
.setRequired(false).build())
.build();
InputParam userParam =
InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_USER, DingTalkParamsConstants.DING_TALK_USER)
.addValidate(Validate.newBuilder()
.setRequired(false).build())
.build();
PasswordParam passwordParam = PasswordParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD, DingTalkParamsConstants.DING_TALK_PASSWORD)
.setPlaceholder("if enable use authentication, you need input password")
.build();
return Arrays.asList(webHookParam, keywordParam, isEnableProxy, proxyParam, portParam, userParam, passwordParam);
}
@Override
public AlertChannel create() {
return new DingTalkAlertChannel();
}
}

34
dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertPlugin.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 org.apache.dolphinscheduler.plugin.alert.dingtalk;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import com.google.common.collect.ImmutableList;
/**
* DingTalkAlertPlugin
*/
public class DingTalkAlertPlugin implements DolphinSchedulerPlugin {
@Override
public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
return ImmutableList.of(new DingTalkAlertChannelFactory());
}
}

57
dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java

@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.dingtalk;
/**
* DingTalkParamsConstants
*/
public class DingTalkParamsConstants {
public DingTalkParamsConstants() {
throw new IllegalStateException("Utility class");
}
static final String DING_TALK_WEB_HOOK = "dingtalk.webhook";
static final String NAME_DING_TALK_WEB_HOOK = "dingTalkWebHook";
static final String DING_TALK_KEYWORD = "dingtalk.keyword";
static final String NAME_DING_TALK_KEYWORD = "dingTalkKeyword";
public static final String DING_TALK_PROXY_ENABLE = "dingtalk.isEnableProxy";
static final String NAME_DING_TALK_PROXY_ENABLE = "dingTalkIsEnableProxy";
static final String DING_TALK_PROXY = "dingtalk.proxy";
static final String NAME_DING_TALK_PROXY = "dingTalkProxy";
static final String DING_TALK_PORT = "dingtalk.port";
static final String NAME_DING_TALK_PORT = "dingTalkPort";
static final String DING_TALK_USER = "dingtalk.user";
static final String NAME_DING_TALK_USER = "dingTalkUser";
static final String DING_TALK_PASSWORD = "dingtalk.password";
static final String NAME_DING_TALK_PASSWORD = "dingTalkPassword";
}

204
dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java

@ -0,0 +1,204 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.dingtalk;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Ding Talk Sender
*/
public class DingTalkSender {
private static final Logger logger = LoggerFactory.getLogger(DingTalkSender.class);
private String url;
private String keyword;
private Boolean enableProxy;
private String proxy;
private Integer port;
private String user;
private String password;
DingTalkSender(Map<String, String> config) {
url = config.get(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK);
keyword = config.get(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD);
enableProxy = Boolean.valueOf(config.get(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE));
if (Boolean.TRUE.equals(enableProxy)) {
port = Integer.parseInt(config.get(DingTalkParamsConstants.NAME_DING_TALK_PORT));
proxy = config.get(DingTalkParamsConstants.NAME_DING_TALK_PROXY);
user = config.get(DingTalkParamsConstants.DING_TALK_USER);
password = config.get(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD);
}
}
public AlertResult sendDingTalkMsg(String msg, String charset) {
AlertResult alertResult;
try {
String resp = sendMsg(msg, charset);
return checkSendDingTalkSendMsgResult(resp);
} catch (Exception e) {
logger.info("send ding talk alert msg exception : {}", e.getMessage());
alertResult = new AlertResult();
alertResult.setStatus("false");
alertResult.setMessage("send ding talk alert fail.");
}
return alertResult;
}
private String sendMsg(String msg, String charset) throws IOException {
String msgToJson = textToJsonString(msg + "#" + keyword);
HttpPost httpPost = constructHttpPost(url, msgToJson, charset);
CloseableHttpClient httpClient;
if (Boolean.TRUE.equals(enableProxy)) {
httpClient = getProxyClient(proxy, port, user, password);
RequestConfig rcf = getProxyConfig(proxy, port);
httpPost.setConfig(rcf);
} else {
httpClient = getDefaultClient();
}
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
String resp;
try {
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, charset);
EntityUtils.consume(entity);
} finally {
response.close();
}
logger.info("Ding Talk send [ %s ], resp:{%s}", msg, resp);
return resp;
} finally {
httpClient.close();
}
}
private static HttpPost constructHttpPost(String url, String msg, String charset) {
HttpPost post = new HttpPost(url);
StringEntity entity = new StringEntity(msg, charset);
post.setEntity(entity);
post.addHeader("Content-Type", "application/json; charset=utf-8");
return post;
}
private static CloseableHttpClient getProxyClient(String proxy, int port, String user, String password) {
HttpHost httpProxy = new HttpHost(proxy, port);
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(new AuthScope(httpProxy), new UsernamePasswordCredentials(user, password));
return HttpClients.custom().setDefaultCredentialsProvider(provider).build();
}
private static CloseableHttpClient getDefaultClient() {
return HttpClients.createDefault();
}
private static RequestConfig getProxyConfig(String proxy, int port) {
HttpHost httpProxy = new HttpHost(proxy, port);
return RequestConfig.custom().setProxy(httpProxy).build();
}
private static String textToJsonString(String text) {
Map<String, Object> items = new HashMap<>();
items.put("msgtype", "text");
Map<String, String> textContent = new HashMap<>();
byte[] byt = StringUtils.getBytesUtf8(text);
String txt = StringUtils.newStringUtf8(byt);
textContent.put("content", txt);
items.put("text", textContent);
return JSONUtils.toJsonString(items);
}
public static class DingTalkSendMsgResponse {
private Integer errcode;
private String errmsg;
public Integer getErrcode() {
return errcode;
}
public void setErrcode(Integer errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}
private static AlertResult checkSendDingTalkSendMsgResult(String result) {
AlertResult alertResult = new AlertResult();
alertResult.setStatus("false");
if (null == result) {
alertResult.setMessage("send ding talk msg error");
logger.info("send ding talk msg error,ding talk server resp is null");
return alertResult;
}
DingTalkSendMsgResponse sendMsgResponse = JSONUtils.parseObject(result, DingTalkSendMsgResponse.class);
if (null == sendMsgResponse) {
alertResult.setMessage("send ding talk msg fail");
logger.info("send ding talk msg error,resp error");
return alertResult;
}
if (sendMsgResponse.errcode == 0) {
alertResult.setStatus("true");
alertResult.setMessage("send ding talk msg success");
return alertResult;
}
alertResult.setMessage(String.format("alert send ding talk msg error : %s", sendMsgResponse.getErrmsg()));
logger.info("alert send ding talk msg error : {}", sendMsgResponse.getErrmsg());
return alertResult;
}
}

48
dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.dingtalk;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
/**
* DingTalkAlertChannelFactoryTest
*/
public class DingTalkAlertChannelFactoryTest {
@Test
public void testGetParams() {
DingTalkAlertChannelFactory dingTalkAlertChannelFactory = new DingTalkAlertChannelFactory();
List<PluginParams> params = dingTalkAlertChannelFactory.getParams();
JSONUtils.toJsonString(params);
Assert.assertEquals(7, params.size());
}
@Test
public void testCreate() {
DingTalkAlertChannelFactory dingTalkAlertChannelFactory = new DingTalkAlertChannelFactory();
AlertChannel alertChannel = dingTalkAlertChannelFactory.create();
Assert.assertNotNull(alertChannel);
}
}

57
dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSenderTest.java

@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.dingtalk;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* DingTalkSenderTest
*/
public class DingTalkSenderTest {
private static Map<String, String> dingTalkConfig = new HashMap<>();
@Before
public void initDingTalkConfig() {
dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD, "keyWord");
dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK, "url");
dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE, "false");
dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD, "password");
dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_PORT, "9988");
dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_USER, "user1,user2");
}
@Test
public void testSend() {
DingTalkSender dingTalkSender = new DingTalkSender(dingTalkConfig);
dingTalkSender.sendDingTalkMsg("keyWord+Welcome", "UTF-8");
dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE, "true");
dingTalkSender = new DingTalkSender(dingTalkConfig);
AlertResult alertResult = dingTalkSender.sendDingTalkMsg("keyWord+Welcome", "UTF-8");
Assert.assertEquals("false",alertResult.getStatus());
}
}

128
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dolphinscheduler-alert-plugin</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<version>1.3.4-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-alert-email</artifactId>
<!-- can be load as a Alert Plugin when development and run server in IDE -->
<packaging>dolphinscheduler-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<finalName>dolphinscheduler-alert-email-${project.version}</finalName>
</build>
</project>

69
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannel.java

@ -0,0 +1,69 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertData;
import org.apache.dolphinscheduler.spi.alert.AlertInfo;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* email alert channel . use email to seed the alertInfo
*/
public class EmailAlertChannel implements AlertChannel {
private static final Logger logger = LoggerFactory.getLogger(EmailAlertChannel.class);
@Override
public AlertResult process(AlertInfo info) {
AlertData alert = info.getAlertData();
String alertParams = info.getAlertParams();
Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
MailSender mailSender = new MailSender(paramsMap);
AlertResult alertResult = mailSender.sendMails(alert.getTitle(), alert.getContent());
//send flag
boolean flag = false;
if (alertResult == null) {
alertResult = new AlertResult();
alertResult.setStatus("false");
alertResult.setMessage("alert send error.");
logger.info("alert send error : {}", alertResult.getMessage());
return alertResult;
}
flag = Boolean.parseBoolean(String.valueOf(alertResult.getStatus()));
if (flag) {
logger.info("alert send success");
alertResult.setMessage("email send success.");
} else {
alertResult.setMessage("alert send error.");
logger.info("alert send error : {}", alertResult.getMessage());
}
return alertResult;
}
}

137
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactory.java

@ -0,0 +1,137 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import org.apache.dolphinscheduler.spi.alert.AlertConstants;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.params.InputParam;
import org.apache.dolphinscheduler.spi.params.PasswordParam;
import org.apache.dolphinscheduler.spi.params.RadioParam;
import org.apache.dolphinscheduler.spi.params.base.DataType;
import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.params.base.Validate;
import java.util.ArrayList;
import java.util.List;
/**
* email alert factory
*/
public class EmailAlertChannelFactory implements AlertChannelFactory {
@Override
public String getName() {
return "Email";
}
@Override
public List<PluginParams> getParams() {
List<PluginParams> paramsList = new ArrayList<>();
InputParam receivesParam = InputParam.newBuilder(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERS, MailParamsConstants.PLUGIN_DEFAULT_EMAIL_RECEIVERS)
.setPlaceholder("please input receives")
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam receiveCcsParam = InputParam.newBuilder(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERCCS, MailParamsConstants.PLUGIN_DEFAULT_EMAIL_RECEIVERCCS)
.build();
InputParam mailSmtpHost = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_HOST, MailParamsConstants.MAIL_SMTP_HOST)
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
InputParam mailSmtpPort = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_PORT, MailParamsConstants.MAIL_SMTP_PORT)
.setValue(25)
.addValidate(Validate.newBuilder()
.setRequired(true)
.setType(DataType.NUMBER.getDataType())
.build())
.build();
InputParam mailSender = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_SENDER, MailParamsConstants.MAIL_SENDER)
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
RadioParam enableSmtpAuth = RadioParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_AUTH, MailParamsConstants.MAIL_SMTP_AUTH)
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.setValue(true)
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
InputParam mailUser = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_USER, MailParamsConstants.MAIL_USER)
.setPlaceholder("if enable use authentication, you need input user")
.build();
PasswordParam mailPassword = PasswordParam.newBuilder(MailParamsConstants.NAME_MAIL_PASSWD, MailParamsConstants.MAIL_PASSWD)
.setPlaceholder("if enable use authentication, you need input password")
.build();
RadioParam enableTls = RadioParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_STARTTLS_ENABLE, MailParamsConstants.MAIL_SMTP_STARTTLS_ENABLE)
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.setValue(false)
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
RadioParam enableSsl = RadioParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_SSL_ENABLE, MailParamsConstants.MAIL_SMTP_SSL_ENABLE)
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.setValue(false)
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
InputParam sslTrust = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_SSL_TRUST, MailParamsConstants.MAIL_SMTP_SSL_TRUST)
.setValue("*")
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
RadioParam showType = RadioParam.newBuilder(AlertConstants.SHOW_TYPE, AlertConstants.SHOW_TYPE)
.addParamsOptions(new ParamsOptions(ShowType.TABLE.getDescp(), ShowType.TABLE.getDescp(), false))
.addParamsOptions(new ParamsOptions(ShowType.TEXT.getDescp(), ShowType.TEXT.getDescp(), false))
.addParamsOptions(new ParamsOptions(ShowType.ATTACHMENT.getDescp(), ShowType.ATTACHMENT.getDescp(), false))
.addParamsOptions(new ParamsOptions(ShowType.TABLEATTACHMENT.getDescp(), ShowType.TABLEATTACHMENT.getDescp(), false))
.setValue(ShowType.TABLE.getDescp())
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
paramsList.add(receivesParam);
paramsList.add(receiveCcsParam);
paramsList.add(mailSmtpHost);
paramsList.add(mailSmtpPort);
paramsList.add(mailSender);
paramsList.add(enableSmtpAuth);
paramsList.add(mailUser);
paramsList.add(mailPassword);
paramsList.add(enableTls);
paramsList.add(enableSsl);
paramsList.add(sslTrust);
paramsList.add(showType);
return paramsList;
}
@Override
public AlertChannel create() {
return new EmailAlertChannel();
}
}

33
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertPlugin.java

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import com.google.common.collect.ImmutableList;
/**
* email alert plugin
*/
public class EmailAlertPlugin implements DolphinSchedulerPlugin {
@Override
public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
return ImmutableList.of(new EmailAlertChannelFactory());
}
}

75
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailConstants.java

@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
public class EmailConstants {
public static final String XLS_FILE_PATH = "xls.file.path";
public static final String MAIL_TRANSPORT_PROTOCOL = "mail.transport.protocol";
public static final String DEFAULT_SMTP_PORT = "25";
public static final String TEXT_HTML_CHARSET_UTF_8 = "text/html;charset=utf-8";
public static final int NUMBER_1000 = 1000;
public static final String TR = "<tr>";
public static final String TD = "<td>";
public static final String TD_END = "</td>";
public static final String TR_END = "</tr>";
public static final String TITLE = "title";
public static final String CONTENT = "content";
public static final String TH = "<th>";
public static final String TH_END = "</th>";
public static final String MARKDOWN_QUOTE = ">";
public static final String MARKDOWN_ENTER = "\n";
public static final String HTML_HEADER_PREFIX = new StringBuilder("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>")
.append("<html>")
.append("<head>")
.append("<title>dolphinscheduler</title>")
.append("<meta name='Keywords' content=''>")
.append("<meta name='Description' content=''>")
.append("<style type=\"text/css\">")
.append("table {margin-top:0px;padding-top:0px;border:1px solid;font-size: 14px;color: #333333;border-width: 1px;border-color: #666666;border-collapse: collapse;}")
.append("table th {border-width: 1px;padding: 8px;border-style: solid;border-color: #666666;background-color: #dedede;text-align: left;}")
.append("table td {border-width: 1px;padding: 8px;border-style: solid;border-color: #666666;background-color: #ffffff;text-align: left;}")
.append("</style>")
.append("</head>")
.append("<body style=\"margin:0;padding:0\"><table border=\"1px\" cellpadding=\"5px\" cellspacing=\"-10px\"> ")
.toString();
public static final String TABLE_BODY_HTML_TAIL = "</table></body></html>";
public static final String UTF_8 = "UTF-8";
public static final String EXCEL_SUFFIX_XLS = ".xls";
public static final String SINGLE_SLASH = "/";
}

22
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/ExcelUtils.java → dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtils.java

@ -15,11 +15,11 @@
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.utils;
package org.apache.dolphinscheduler.plugin.alert.email;
import org.apache.dolphinscheduler.common.utils.CollectionUtils;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
@ -31,6 +31,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -43,12 +44,12 @@ import org.slf4j.LoggerFactory;
*/
public class ExcelUtils {
private static final Logger logger = LoggerFactory.getLogger(ExcelUtils.class);
private ExcelUtils() {
throw new IllegalStateException(ExcelUtils.class.getName());
public ExcelUtils() {
throw new IllegalStateException("Utility class");
}
private static final Logger logger = LoggerFactory.getLogger(ExcelUtils.class);
/**
* generate excel file
*
@ -71,7 +72,9 @@ public class ExcelUtils {
List<String> headerList = new ArrayList<>();
for (Map.Entry<String, Object> en : headerMap.entrySet()) {
Iterator<Map.Entry<String, Object>> iter = headerMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Object> en = iter.next();
headerList.add(en.getKey());
}
@ -122,11 +125,12 @@ public class ExcelUtils {
}
//setting file output
fos = new FileOutputStream(xlsFilePath + Constants.SINGLE_SLASH + title + Constants.EXCEL_SUFFIX_XLS);
fos = new FileOutputStream(xlsFilePath + EmailConstants.SINGLE_SLASH + title + EmailConstants.EXCEL_SUFFIX_XLS);
wb.write(fos);
} catch (Exception e) {
logger.error("generate excel error", e);
throw new RuntimeException("generate excel error", e);
} finally {
if (wb != null) {

65
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailParamsConstants.java

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
/**
* mail plugin params json use
*/
public class MailParamsConstants {
public MailParamsConstants() {
throw new IllegalStateException("Utility class");
}
public static final String PLUGIN_DEFAULT_EMAIL_RECEIVERS = "$t('receivers')";
public static final String NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERS = "receivers";
public static final String PLUGIN_DEFAULT_EMAIL_RECEIVERCCS = "$t('receiverCcs')";
public static final String NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERCCS = "receiverCcs";
public static final String MAIL_PROTOCOL = "transport.protocol";
public static final String NAME_MAIL_PROTOCOL = "protocol";
public static final String MAIL_SMTP_HOST = "smtp.host";
public static final String NAME_MAIL_SMTP_HOST = "serverHost";
public static final String MAIL_SMTP_PORT = "smtp.port";
public static final String NAME_MAIL_SMTP_PORT = "serverPort";
public static final String MAIL_SENDER = "sender";
public static final String NAME_MAIL_SENDER = "sender";
public static final String MAIL_SMTP_AUTH = "smtp.auth";
public static final String NAME_MAIL_SMTP_AUTH = "enableSmtpAuth";
public static final String MAIL_USER = "user";
public static final String NAME_MAIL_USER = "user";
public static final String MAIL_PASSWD = "passwd";
public static final String NAME_MAIL_PASSWD = "passwd";
public static final String MAIL_SMTP_STARTTLS_ENABLE = "smtp.starttls.enable";
public static final String NAME_MAIL_SMTP_STARTTLS_ENABLE = "starttlsEnable";
public static final String MAIL_SMTP_SSL_ENABLE = "smtp.ssl.enable";
public static final String NAME_MAIL_SMTP_SSL_ENABLE = "sslEnable";
public static final String MAIL_SMTP_SSL_TRUST = "smtp.ssl.trust";
public static final String NAME_MAIL_SMTP_SSL_TRUST = "smtpSslTrust";
}

429
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailSender.java

@ -0,0 +1,429 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
import static java.util.Objects.requireNonNull;
import org.apache.dolphinscheduler.plugin.alert.email.template.AlertTemplate;
import org.apache.dolphinscheduler.plugin.alert.email.template.DefaultHTMLTemplate;
import org.apache.dolphinscheduler.spi.alert.AlertConstants;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.utils.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.mail.smtp.SMTPProvider;
/**
* mail utils
*/
public class MailSender {
public static final Logger logger = LoggerFactory.getLogger(MailSender.class);
private List<String> receivers;
private List<String> receiverCcs;
private String mailProtocol = "SMTP";
private String mailSmtpHost;
private String mailSmtpPort;
private String mailSender;
private String enableSmtpAuth;
private String mailUser;
private String mailPasswd;
private String mailUseStartTLS;
private String mailUseSSL;
private String xlsFilePath;
private String sslTrust;
private String showType;
private AlertTemplate alertTemplate;
public MailSender(Map<String, String> config) {
String receiversConfig = config.get(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERS);
if (receiversConfig == null || "".equals(receiversConfig)) {
throw new RuntimeException(MailParamsConstants.PLUGIN_DEFAULT_EMAIL_RECEIVERS + "must not be null");
}
receivers = Arrays.asList(receiversConfig.split(","));
String receiverCcsConfig = config.get(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERCCS);
receiverCcs = new ArrayList<>();
if (receiverCcsConfig != null && !"".equals(receiverCcsConfig)) {
receiverCcs = Arrays.asList(receiverCcsConfig.split(","));
}
mailSmtpHost = config.get(MailParamsConstants.NAME_MAIL_SMTP_HOST);
requireNonNull(mailSmtpHost, MailParamsConstants.MAIL_SMTP_HOST + " must not null");
mailSmtpPort = config.get(MailParamsConstants.NAME_MAIL_SMTP_PORT);
requireNonNull(mailSmtpPort, MailParamsConstants.MAIL_SMTP_PORT + " must not null");
mailSender = config.get(MailParamsConstants.NAME_MAIL_SENDER);
requireNonNull(mailSender, MailParamsConstants.MAIL_SENDER + " must not null");
enableSmtpAuth = config.get(MailParamsConstants.NAME_MAIL_SMTP_AUTH);
mailUser = config.get(MailParamsConstants.NAME_MAIL_USER);
requireNonNull(mailUser, MailParamsConstants.MAIL_USER + " must not null");
mailPasswd = config.get(MailParamsConstants.NAME_MAIL_PASSWD);
requireNonNull(mailPasswd, MailParamsConstants.MAIL_PASSWD + " must not null");
mailUseStartTLS = config.get(MailParamsConstants.NAME_MAIL_SMTP_STARTTLS_ENABLE);
requireNonNull(mailUseStartTLS, MailParamsConstants.MAIL_SMTP_STARTTLS_ENABLE + " must not null");
mailUseSSL = config.get(MailParamsConstants.NAME_MAIL_SMTP_SSL_ENABLE);
requireNonNull(mailUseSSL, MailParamsConstants.MAIL_SMTP_SSL_ENABLE + " must not null");
sslTrust = config.get(MailParamsConstants.NAME_MAIL_SMTP_SSL_TRUST);
requireNonNull(sslTrust, MailParamsConstants.MAIL_SMTP_SSL_TRUST + " must not null");
showType = config.get(AlertConstants.SHOW_TYPE);
requireNonNull(showType, AlertConstants.SHOW_TYPE + " must not null");
xlsFilePath = config.get(EmailConstants.XLS_FILE_PATH);
if (StringUtils.isBlank(xlsFilePath)) {
xlsFilePath = "/tmp/xls";
}
alertTemplate = new DefaultHTMLTemplate();
}
/**
* send mail to receivers
*
* @param title title
* @param content content
* @return
*/
public AlertResult sendMails(String title, String content) {
return sendMails(this.receivers, this.receiverCcs, title, content);
}
/**
* send mail to receivers
*
* @param title email title
* @param content email content
* @return
*/
public AlertResult sendMailsToReceiverOnly(String title, String content) {
return sendMails(this.receivers, null, title, content);
}
/**
* send mail
*
* @param receivers receivers
* @param receiverCcs receiverCcs
* @param title title
* @param content content
* @return
*/
public AlertResult sendMails(List<String> receivers, List<String> receiverCcs, String title, String content) {
AlertResult alertResult = new AlertResult();
alertResult.setStatus("false");
// if there is no receivers && no receiversCc, no need to process
if (CollectionUtils.isEmpty(receivers) && CollectionUtils.isEmpty(receiverCcs)) {
return alertResult;
}
receivers.removeIf(StringUtils::isEmpty);
if (showType.equals(ShowType.TABLE.getDescp()) || showType.equals(ShowType.TEXT.getDescp())) {
// send email
HtmlEmail email = new HtmlEmail();
try {
Session session = getSession();
email.setMailSession(session);
email.setFrom(mailSender);
email.setCharset(EmailConstants.UTF_8);
if (CollectionUtils.isNotEmpty(receivers)) {
// receivers mail
for (String receiver : receivers) {
email.addTo(receiver);
}
}
if (CollectionUtils.isNotEmpty(receiverCcs)) {
//cc
for (String receiverCc : receiverCcs) {
email.addCc(receiverCc);
}
}
// sender mail
return getStringObjectMap(title, content, alertResult, email);
} catch (Exception e) {
handleException(alertResult, e);
}
} else if (showType.equals(ShowType.ATTACHMENT.getDescp()) || showType.equals(ShowType.TABLEATTACHMENT.getDescp())) {
try {
String partContent = (showType.equals(ShowType.ATTACHMENT.getDescp()) ? "Please see the attachment " + title + EmailConstants.EXCEL_SUFFIX_XLS : htmlTable(content, false));
attachment(title, content, partContent);
alertResult.setStatus("true");
return alertResult;
} catch (Exception e) {
handleException(alertResult, e);
return alertResult;
}
}
return alertResult;
}
/**
* html table content
*
* @param content the content
* @param showAll if show the whole content
* @return the html table form
*/
private String htmlTable(String content, boolean showAll) {
return alertTemplate.getMessageFromTemplate(content, ShowType.TABLE, showAll);
}
/**
* html table content
*
* @param content the content
* @return the html table form
*/
private String htmlTable(String content) {
return htmlTable(content, true);
}
/**
* html text content
*
* @param content the content
* @return text in html form
*/
private String htmlText(String content) {
return alertTemplate.getMessageFromTemplate(content, ShowType.TEXT);
}
/**
* send mail as Excel attachment
*
* @param title
* @param content
* @param partContent
* @throws Exception
*/
private void attachment(String title, String content, String partContent) throws Exception {
MimeMessage msg = getMimeMessage();
attachContent(title, content, partContent, msg);
}
/**
* get MimeMessage
*
* @return
* @throws MessagingException
*/
private MimeMessage getMimeMessage() throws MessagingException {
// 1. The first step in creating mail: creating session
Session session = getSession();
// Setting debug mode, can be turned off
session.setDebug(false);
// 2. creating mail: Creating a MimeMessage
MimeMessage msg = new MimeMessage(session);
// 3. set sender
msg.setFrom(new InternetAddress(mailSender));
// 4. set receivers
for (String receiver : receivers) {
msg.addRecipients(Message.RecipientType.TO, InternetAddress.parse(receiver));
}
return msg;
}
/**
* get session
*
* @return the new Session
*/
private Session getSession() {
Properties props = new Properties();
props.setProperty(MailParamsConstants.MAIL_SMTP_HOST, mailSmtpHost);
props.setProperty(MailParamsConstants.MAIL_SMTP_PORT, mailSmtpPort);
props.setProperty(MailParamsConstants.MAIL_SMTP_AUTH, enableSmtpAuth);
props.setProperty(EmailConstants.MAIL_TRANSPORT_PROTOCOL, mailProtocol);
props.setProperty(MailParamsConstants.MAIL_SMTP_STARTTLS_ENABLE, mailUseStartTLS);
props.setProperty(MailParamsConstants.MAIL_SMTP_SSL_ENABLE, mailUseSSL);
props.setProperty(MailParamsConstants.MAIL_SMTP_SSL_TRUST, sslTrust);
Authenticator auth = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
// mail username and password
return new PasswordAuthentication(mailUser, mailPasswd);
}
};
Session session = Session.getInstance(props, auth);
session.addProvider(new SMTPProvider());
return session;
}
/**
* attach content
*
* @param title
* @param content
* @param partContent
* @param msg
* @throws MessagingException
* @throws IOException
*/
private void attachContent(String title, String content, String partContent, MimeMessage msg) throws MessagingException, IOException {
/**
* set receiverCc
*/
if (CollectionUtils.isNotEmpty(receiverCcs)) {
for (String receiverCc : receiverCcs) {
msg.addRecipients(Message.RecipientType.CC, InternetAddress.parse(receiverCc));
}
}
// set subject
msg.setSubject(title);
MimeMultipart partList = new MimeMultipart();
// set signature
MimeBodyPart part1 = new MimeBodyPart();
part1.setContent(partContent, EmailConstants.TEXT_HTML_CHARSET_UTF_8);
// set attach file
MimeBodyPart part2 = new MimeBodyPart();
File file = new File(xlsFilePath + EmailConstants.SINGLE_SLASH + title + EmailConstants.EXCEL_SUFFIX_XLS);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
// make excel file
ExcelUtils.genExcelFile(content, title, xlsFilePath);
part2.attachFile(file);
part2.setFileName(MimeUtility.encodeText(title + EmailConstants.EXCEL_SUFFIX_XLS, EmailConstants.UTF_8, "B"));
// add components to collection
partList.addBodyPart(part1);
partList.addBodyPart(part2);
msg.setContent(partList);
// 5. send Transport
Transport.send(msg);
// 6. delete saved file
deleteFile(file);
}
/**
* the string object map
*
* @param title
* @param content
* @param alertResult
* @param email
* @return
* @throws EmailException
*/
private AlertResult getStringObjectMap(String title, String content, AlertResult alertResult, HtmlEmail email) throws EmailException {
/**
* the subject of the message to be sent
*/
email.setSubject(title);
/**
* to send information, you can use HTML tags in mail content because of the use of HtmlEmail
*/
if (showType.equals(ShowType.TABLE.getDescp())) {
email.setMsg(htmlTable(content));
} else if (showType.equals(ShowType.TEXT.getDescp())) {
email.setMsg(htmlText(content));
}
// send
email.setDebug(true);
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
email.send();
alertResult.setStatus("true");
return alertResult;
}
/**
* file delete
*
* @param file the file to delete
*/
public void deleteFile(File file) {
if (file.exists()) {
if (file.delete()) {
logger.info("delete success: {}", file.getAbsolutePath() + file.getName());
} else {
logger.info("delete fail: {}", file.getAbsolutePath() + file.getName());
}
} else {
logger.info("file not exists: {}", file.getAbsolutePath() + file.getName());
}
}
/**
* handle exception
*
* @param alertResult
* @param e
*/
private void handleException(AlertResult alertResult, Exception e) {
logger.error("Send email to {} failed", receivers, e);
alertResult.setMessage("Send email to {" + String.join(",", receivers) + "} failed," + e.toString());
}
}

21
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplate.java → dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/AlertTemplate.java

@ -14,9 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.template;
import org.apache.dolphinscheduler.common.enums.ShowType;
package org.apache.dolphinscheduler.plugin.alert.email.template;
import org.apache.dolphinscheduler.spi.alert.ShowType;
/**
* alert message template
@ -25,20 +26,22 @@ public interface AlertTemplate {
/**
* get a message from a specified alert template
* @param content alert message content
* @param showType show type
* @param showAll whether to show all
*
* @param content alert message content
* @param showType show type
* @param showAll whether to show all
* @return a message from a specified alert template
*/
String getMessageFromTemplate(String content, ShowType showType,boolean showAll);
String getMessageFromTemplate(String content, ShowType showType, boolean showAll);
/**
* default showAll is true
* @param content alert message content
*
* @param content alert message content
* @param showType show type
* @return a message from a specified alert template
*/
default String getMessageFromTemplate(String content,ShowType showType){
return getMessageFromTemplate(content,showType,true);
default String getMessageFromTemplate(String content, ShowType showType) {
return getMessageFromTemplate(content, showType, true);
}
}

53
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/impl/DefaultHTMLTemplate.java → dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplate.java

@ -15,21 +15,19 @@
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.template.impl;
package org.apache.dolphinscheduler.plugin.alert.email.template;
import static org.apache.dolphinscheduler.common.utils.Preconditions.checkNotNull;
import static java.util.Objects.requireNonNull;
import org.apache.dolphinscheduler.alert.template.AlertTemplate;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.common.enums.ShowType;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.common.utils.StringUtils;
import org.apache.dolphinscheduler.plugin.alert.email.EmailConstants;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import org.apache.dolphinscheduler.spi.utils.StringUtils;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
@ -52,7 +50,7 @@ public class DefaultHTMLTemplate implements AlertTemplate {
case TABLE:
return getTableTypeMessage(content, showAll);
case TEXT:
return getTextTypeMessage(content);
return getTextTypeMessage(content, showAll);
default:
throw new IllegalArgumentException(String.format("not support showType: %s in DefaultHTMLTemplate", showType));
}
@ -70,8 +68,8 @@ public class DefaultHTMLTemplate implements AlertTemplate {
if (StringUtils.isNotEmpty(content)) {
List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
if (!showAll && mapItemsList.size() > Constants.NUMBER_1000) {
mapItemsList = mapItemsList.subList(0, Constants.NUMBER_1000);
if (!showAll && mapItemsList.size() > EmailConstants.NUMBER_1000) {
mapItemsList = mapItemsList.subList(0, EmailConstants.NUMBER_1000);
}
StringBuilder contents = new StringBuilder(200);
@ -81,21 +79,21 @@ public class DefaultHTMLTemplate implements AlertTemplate {
String title = "";
for (LinkedHashMap mapItems : mapItemsList) {
Set<Entry<String, Object>> entries = mapItems.entrySet();
Set<Map.Entry<String, Object>> entries = mapItems.entrySet();
Iterator<Entry<String, Object>> iterator = entries.iterator();
Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
StringBuilder t = new StringBuilder(Constants.TR);
StringBuilder cs = new StringBuilder(Constants.TR);
StringBuilder t = new StringBuilder(EmailConstants.TR);
StringBuilder cs = new StringBuilder(EmailConstants.TR);
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
t.append(Constants.TH).append(entry.getKey()).append(Constants.TH_END);
cs.append(Constants.TD).append(String.valueOf(entry.getValue())).append(Constants.TD_END);
t.append(EmailConstants.TH).append(entry.getKey()).append(EmailConstants.TH_END);
cs.append(EmailConstants.TD).append(String.valueOf(entry.getValue())).append(EmailConstants.TD_END);
}
t.append(Constants.TR_END);
cs.append(Constants.TR_END);
t.append(EmailConstants.TR_END);
cs.append(EmailConstants.TR_END);
if (flag) {
title = t.toString();
}
@ -113,17 +111,18 @@ public class DefaultHTMLTemplate implements AlertTemplate {
* get alert message which type is TEXT
*
* @param content message content
* @param showAll weather to show all
* @return alert message
*/
private String getTextTypeMessage(String content) {
private String getTextTypeMessage(String content, boolean showAll) {
if (StringUtils.isNotEmpty(content)) {
ArrayNode list = JSONUtils.parseArray(content);
StringBuilder contents = new StringBuilder(100);
for (JsonNode jsonNode : list) {
contents.append(Constants.TR);
contents.append(Constants.TD).append(jsonNode.toString()).append(Constants.TD_END);
contents.append(Constants.TR_END);
contents.append(EmailConstants.TR);
contents.append(EmailConstants.TD).append(jsonNode.toString()).append(EmailConstants.TD_END);
contents.append(EmailConstants.TR_END);
}
return getMessageFromHtmlTemplate(null, contents.toString());
@ -136,16 +135,16 @@ public class DefaultHTMLTemplate implements AlertTemplate {
/**
* get alert message from a html template
*
* @param title message title
* @param title message title
* @param content message content
* @return alert message which use html template
*/
private String getMessageFromHtmlTemplate(String title, String content) {
checkNotNull(content);
String htmlTableThead = StringUtils.isEmpty(title) ? "" : String.format("<thead>%s</thead>%n", title);
requireNonNull(content, "content must not null");
String htmlTableThead = StringUtils.isEmpty(title) ? "" : String.format("<thead>%s</thead>\n", title);
return Constants.HTML_HEADER_PREFIX + htmlTableThead + content + Constants.TABLE_BODY_HTML_TAIL;
return EmailConstants.HTML_HEADER_PREFIX + htmlTableThead + content + EmailConstants.TABLE_BODY_HTML_TAIL;
}
}

75
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactoryTest.java

@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.List;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* EmailAlertChannelFactory Tester.
*
* @version 1.0
* @since <pre>Aug 20, 2020</pre>
*/
public class EmailAlertChannelFactoryTest {
@Before
public void before() throws Exception {
}
@After
public void after() throws Exception {
}
/**
* Method: getName()
*/
@Test
public void testGetName() throws Exception {
}
/**
* Method: getParams()
*/
@Test
public void testGetParams() throws Exception {
EmailAlertChannelFactory emailAlertChannelFactory = new EmailAlertChannelFactory();
List<PluginParams> params = emailAlertChannelFactory.getParams();
System.out.println(JSONUtils.toJsonString(params));
Assert.assertEquals(12, params.size());
}
/**
* Method: create()
*/
@Test
public void testCreate() throws Exception {
EmailAlertChannelFactory emailAlertChannelFactory = new EmailAlertChannelFactory();
AlertChannel alertChannel = emailAlertChannelFactory.create();
Assert.assertNotNull(alertChannel);
}
}

161
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelTest.java

@ -0,0 +1,161 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
import org.apache.dolphinscheduler.spi.alert.AlertConstants;
import org.apache.dolphinscheduler.spi.alert.AlertData;
import org.apache.dolphinscheduler.spi.alert.AlertInfo;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.params.InputParam;
import org.apache.dolphinscheduler.spi.params.PasswordParam;
import org.apache.dolphinscheduler.spi.params.RadioParam;
import org.apache.dolphinscheduler.spi.params.base.DataType;
import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.params.base.Validate;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
/**
* EmailAlertChannel Tester.
*/
public class EmailAlertChannelTest {
/**
* Method: process(AlertInfo info)
*/
@Test
public void testProcess() {
EmailAlertChannel emailAlertChannel = new EmailAlertChannel();
AlertData alertData = new AlertData();
LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
map1.put("mysql service name", "mysql200");
map1.put("mysql address", "192.168.xx.xx");
map1.put("port", "3306");
map1.put("no index of number", "80");
map1.put("database client connections", "190");
List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
maps.add(0, map1);
String mapjson = JSONUtils.toJsonString(maps);
alertData.setId(10)
.setContent(mapjson)
.setLog("10")
.setTitle("test");
AlertInfo alertInfo = new AlertInfo();
alertInfo.setAlertData(alertData);
alertInfo.setAlertParams(getEmailAlertParams());
AlertResult alertResult = emailAlertChannel.process(alertInfo);
Assert.assertNotNull(alertResult);
Assert.assertEquals("false", alertResult.getStatus());
}
public String getEmailAlertParams() {
List<PluginParams> paramsList = new ArrayList<>();
InputParam receivesParam = InputParam.newBuilder("receivers", "receivers")
.setValue("540957506@qq.com")
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
InputParam mailSmtpHost = InputParam.newBuilder("serverHost", "smtp.host")
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue("smtp.126.com")
.build();
InputParam mailSmtpPort = InputParam.newBuilder("serverPort", "smtp.port")
.addValidate(Validate.newBuilder()
.setRequired(true)
.setType(DataType.NUMBER.getDataType())
.build())
.setValue(25)
.build();
InputParam mailSender = InputParam.newBuilder("sender", "sender")
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue("dolphinscheduler@126.com")
.build();
RadioParam enableSmtpAuth = RadioParam.newBuilder("enableSmtpAuth", "smtp.auth")
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue(false)
.build();
InputParam mailUser = InputParam.newBuilder("user", "user")
.setPlaceholder("if enable use authentication, you need input user")
.setValue("dolphinscheduler@126.com")
.build();
PasswordParam mailPassword = PasswordParam.newBuilder("passwd", "passwd")
.setPlaceholder("if enable use authentication, you need input password")
.setValue("escheduler123")
.build();
RadioParam enableTls = RadioParam.newBuilder("starttlsEnable", "starttls.enable")
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue(true)
.build();
RadioParam enableSsl = RadioParam.newBuilder("sslEnable", "smtp.ssl.enable")
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue(true)
.build();
InputParam sslTrust = InputParam.newBuilder("smtpSslTrust", "smtp.ssl.trust")
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue("smtp.126.com")
.build();
List<ParamsOptions> emailShowTypeList = new ArrayList<>();
emailShowTypeList.add(new ParamsOptions(ShowType.TABLE.getDescp(), ShowType.TABLE.getDescp(), false));
emailShowTypeList.add(new ParamsOptions(ShowType.TEXT.getDescp(), ShowType.TEXT.getDescp(), false));
emailShowTypeList.add(new ParamsOptions(ShowType.ATTACHMENT.getDescp(), ShowType.ATTACHMENT.getDescp(), false));
emailShowTypeList.add(new ParamsOptions(ShowType.TABLEATTACHMENT.getDescp(), ShowType.TABLEATTACHMENT.getDescp(), false));
RadioParam showType = RadioParam.newBuilder(AlertConstants.SHOW_TYPE, "showType")
.setParamsOptionsList(emailShowTypeList)
.setValue(ShowType.TABLE.getDescp())
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
paramsList.add(receivesParam);
paramsList.add(mailSmtpHost);
paramsList.add(mailSmtpPort);
paramsList.add(mailSender);
paramsList.add(enableSmtpAuth);
paramsList.add(mailUser);
paramsList.add(mailPassword);
paramsList.add(enableTls);
paramsList.add(enableSsl);
paramsList.add(sslTrust);
paramsList.add(showType);
return JSONUtils.toJsonString(paramsList);
}
}

8
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/ExcelUtilsTest.java → dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtilsTest.java

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.utils;
package org.apache.dolphinscheduler.plugin.alert.email;
import static org.junit.Assert.assertTrue;
@ -63,7 +63,7 @@ public class ExcelUtilsTest {
//Define dest file path
String xlsFilePath = rootPath + System.getProperty("file.separator");
logger.info("XLS_FILE_PATH: " + xlsFilePath);
logger.info("xlsFilePath: " + xlsFilePath);
//Define correctContent
String correctContent = "[{\"name\":\"ds name\",\"value\":\"ds value\"}]";
@ -78,7 +78,7 @@ public class ExcelUtilsTest {
ExcelUtils.genExcelFile(correctContent, title, xlsFilePath);
//Test file exists
File xlsFile = new File(xlsFilePath + Constants.SINGLE_SLASH + title + Constants.EXCEL_SUFFIX_XLS);
File xlsFile = new File(xlsFilePath + EmailConstants.SINGLE_SLASH + title + EmailConstants.EXCEL_SUFFIX_XLS);
assertTrue(xlsFile.exists());
//Expected RuntimeException
@ -98,7 +98,7 @@ public class ExcelUtilsTest {
@Test
public void testGenExcelFileByCheckDir() {
ExcelUtils.genExcelFile("[{\"a\": \"a\"},{\"a\": \"a\"}]", "t", "/tmp/xls");
File file = new File("/tmp/xls" + Constants.SINGLE_SLASH + "t" + Constants.EXCEL_SUFFIX_XLS);
File file = new File("/tmp/xls" + EmailConstants.SINGLE_SLASH + "t" + EmailConstants.EXCEL_SUFFIX_XLS);
file.delete();
}
}

136
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/MailUtilsTest.java

@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email;
import org.apache.dolphinscheduler.plugin.alert.email.template.AlertTemplate;
import org.apache.dolphinscheduler.plugin.alert.email.template.DefaultHTMLTemplate;
import org.apache.dolphinscheduler.spi.alert.AlertConstants;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*/
public class MailUtilsTest {
private static final Logger logger = LoggerFactory.getLogger(MailUtilsTest.class);
private static Map<String, String> emailConfig = new HashMap<>();
private static AlertTemplate alertTemplate;
static MailSender mailSender;
@BeforeClass
public static void initEmailConfig() {
emailConfig.put(MailParamsConstants.NAME_MAIL_PROTOCOL, "smtp");
emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_HOST, "xxx.xxx.com");
emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_PORT, "25");
emailConfig.put(MailParamsConstants.NAME_MAIL_SENDER, "xxx1.xxx.com");
emailConfig.put(MailParamsConstants.NAME_MAIL_USER, "xxx2.xxx.com");
emailConfig.put(MailParamsConstants.NAME_MAIL_PASSWD, "111111");
emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_STARTTLS_ENABLE, "true");
emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_SSL_ENABLE, "false");
emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_SSL_TRUST, "false");
emailConfig.put(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERS, "347801120@qq.com");
emailConfig.put(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERCCS, "347801120@qq.com");
emailConfig.put(AlertConstants.SHOW_TYPE, ShowType.TEXT.getDescp());
alertTemplate = new DefaultHTMLTemplate();
mailSender = new MailSender(emailConfig);
}
@Test
public void testSendMails() {
String content = "[\"id:69\","
+ "\"name:UserBehavior-0--1193959466\","
+ "\"Job name: Start workflow\","
+ "\"State: SUCCESS\","
+ "\"Recovery:NO\","
+ "\"Run time: 1\","
+ "\"Start time: 2018-08-06 10:31:34.0\","
+ "\"End time: 2018-08-06 10:31:49.0\","
+ "\"Host: 192.168.xx.xx\","
+ "\"Notify group :4\"]";
mailSender.sendMails(
"Mysql Exception",
content);
}
public String list2String() {
LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
map1.put("mysql service name", "mysql200");
map1.put("mysql address", "192.168.xx.xx");
map1.put("port", "3306");
map1.put("no index of number", "80");
map1.put("database client connections", "190");
LinkedHashMap<String, Object> map2 = new LinkedHashMap<>();
map2.put("mysql service name", "mysql210");
map2.put("mysql address", "192.168.xx.xx");
map2.put("port", "3306");
map2.put("no index of number", "10");
map2.put("database client connections", "90");
List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
maps.add(0, map1);
maps.add(1, map2);
String mapjson = JSONUtils.toJsonString(maps);
logger.info(mapjson);
return mapjson;
}
@Test
public void testSendTableMail() {
String title = "Mysql Exception";
String content = list2String();
emailConfig.put(AlertConstants.SHOW_TYPE, ShowType.TABLE.getDescp());
mailSender = new MailSender(emailConfig);
mailSender.sendMails(title, content);
}
@Test
public void testAttachmentFile() throws Exception {
String content = list2String();
emailConfig.put(AlertConstants.SHOW_TYPE, ShowType.ATTACHMENT.getDescp());
mailSender = new MailSender(emailConfig);
mailSender.sendMails("gaojing", content);
}
@Test
public void testTableAttachmentFile() throws Exception {
String content = list2String();
emailConfig.put(AlertConstants.SHOW_TYPE, ShowType.TABLEATTACHMENT.getDescp());
mailSender = new MailSender(emailConfig);
mailSender.sendMails("gaojing", content);
}
}

105
dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplateTest.java

@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.email.template;
import static org.junit.Assert.assertEquals;
import org.apache.dolphinscheduler.plugin.alert.email.EmailConstants;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* test class for DefaultHTMLTemplate
*/
public class DefaultHTMLTemplateTest {
private static final Logger logger = LoggerFactory.getLogger(DefaultHTMLTemplateTest.class);
/**
* only need test method GetMessageFromTemplate
*/
@Test
public void testGetMessageFromTemplate() {
DefaultHTMLTemplate template = new DefaultHTMLTemplate();
String tableTypeMessage = template.getMessageFromTemplate(list2String(), ShowType.TABLE, true);
assertEquals(tableTypeMessage, generateMockTableTypeResultByHand());
String textTypeMessage = template.getMessageFromTemplate(list2String(), ShowType.TEXT, true);
assertEquals(textTypeMessage, generateMockTextTypeResultByHand());
}
/**
* generate some simulation data
*/
private String list2String() {
LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
map1.put("mysql service name", "mysql200");
map1.put("mysql address", "192.168.xx.xx");
map1.put("database client connections", "190");
map1.put("port", "3306");
map1.put("no index of number", "80");
LinkedHashMap<String, Object> map2 = new LinkedHashMap<>();
map2.put("mysql service name", "mysql210");
map2.put("mysql address", "192.168.xx.xx");
map2.put("database client connections", "90");
map2.put("port", "3306");
map2.put("no index of number", "10");
List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
maps.add(0, map1);
maps.add(1, map2);
String mapjson = JSONUtils.toJsonString(maps);
logger.info(mapjson);
return mapjson;
}
private String generateMockTableTypeResultByHand() {
return EmailConstants.HTML_HEADER_PREFIX
+ "<thead>"
+ "<tr><th>mysql service name</th><th>mysql address</th><th>database client connections</th><th>port</th><th>no index of number</th></tr>"
+ "</thead>\n"
+ "<tr><td>mysql200</td><td>192.168.xx.xx</td><td>190</td><td>3306</td><td>80</td></tr>"
+ "<tr><td>mysql210</td><td>192.168.xx.xx</td><td>90</td><td>3306</td><td>10</td></tr>"
+ EmailConstants.TABLE_BODY_HTML_TAIL;
}
private String generateMockTextTypeResultByHand() {
return EmailConstants.HTML_HEADER_PREFIX
+ "<tr><td>{\"mysql service name\":\"mysql200\",\"mysql address\":\"192.168.xx.xx\",\"database client connections\":\"190\",\"port\":\"3306\",\"no index of number\":\"80\"}</td></tr>"
+ "<tr><td>{\"mysql service name\":\"mysql210\",\"mysql address\":\"192.168.xx.xx\",\"database client connections\":\"90\",\"port\":\"3306\",\"no index of number\":\"10\"}</td></tr>"
+ EmailConstants.TABLE_BODY_HTML_TAIL;
}
}

76
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/pom.xml

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dolphinscheduler-alert-plugin</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<version>1.3.4-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dolphinscheduler-alert-http</artifactId>
<packaging>dolphinscheduler-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>dolphinscheduler-alert-http-${project.version}</finalName>
</build>
</project>

41
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannel.java

@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.http;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertData;
import org.apache.dolphinscheduler.spi.alert.AlertInfo;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import java.util.Map;
/**
* http alert channel,use sms message to seed the alertInfo
*/
public class HttpAlertChannel implements AlertChannel {
@Override
public AlertResult process(AlertInfo alertInfo) {
AlertData alertData = alertInfo.getAlertData();
String alertParams = alertInfo.getAlertParams();
Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
return new HttpSender(paramsMap).send(alertData.getContent());
}
}

78
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactory.java

@ -0,0 +1,78 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.http;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import org.apache.dolphinscheduler.spi.params.InputParam;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.params.base.Validate;
import java.util.Arrays;
import java.util.List;
/**
* http alert factory
*/
public class HttpAlertChannelFactory implements AlertChannelFactory {
@Override
public String getName() {
return "Http";
}
@Override
public List<PluginParams> getParams() {
InputParam url = InputParam.newBuilder(HttpAlertConstants.URL, HttpAlertConstants.URL)
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam headerParams = InputParam.newBuilder(HttpAlertConstants.HEADER_PARAMS, HttpAlertConstants.HEADER_PARAMS)
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam bodyParams = InputParam.newBuilder(HttpAlertConstants.BODY_PARAMS, HttpAlertConstants.BODY_PARAMS)
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam contentField = InputParam.newBuilder(HttpAlertConstants.CONTENT_FIELD, HttpAlertConstants.CONTENT_FIELD)
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam requestType = InputParam.newBuilder(HttpAlertConstants.REQUEST_TYPE, HttpAlertConstants.REQUEST_TYPE)
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
return Arrays.asList(url, requestType, headerParams, bodyParams, contentField);
}
@Override
public AlertChannel create() {
return new HttpAlertChannel();
}
}

35
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertConstants.java

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.http;
public class HttpAlertConstants {
private HttpAlertConstants() {
}
public static final String URL = "url";
public static final String HEADER_PARAMS = "headerParams";
public static final String BODY_PARAMS = "bodyParams";
public static final String CONTENT_FIELD = "contentField";
public static final String REQUEST_TYPE = "requestType";
}

34
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPlugin.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 org.apache.dolphinscheduler.plugin.alert.http;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import com.google.common.collect.ImmutableList;
/**
* http alertPlugins
*/
public class HttpAlertPlugin implements DolphinSchedulerPlugin {
@Override
public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
return ImmutableList.of(new HttpAlertChannelFactory());
}
}

166
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSender.java

@ -0,0 +1,166 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.http;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import org.apache.dolphinscheduler.spi.utils.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* http send message
*/
public class HttpSender {
public static final Logger logger = LoggerFactory.getLogger(HttpSender.class);
private String url;
private final String headerParams;
private final String bodyParams;
private final String contentField;
private final String requestType;
private HttpRequestBase httpRequest;
private static final String URL_SPLICE_CHAR = "?";
/**
* request type post
*/
private static final String REQUEST_TYPE_POST = "POST";
/**
* request type get
*/
private static final String REQUEST_TYPE_GET = "GET";
private static final String DEFAULT_CHARSET = "utf-8";
public HttpSender(Map<String, String> paramsMap) {
url = paramsMap.get(HttpAlertConstants.URL);
headerParams = paramsMap.get(HttpAlertConstants.HEADER_PARAMS);
bodyParams = paramsMap.get(HttpAlertConstants.BODY_PARAMS);
contentField = paramsMap.get(HttpAlertConstants.CONTENT_FIELD);
requestType = paramsMap.get(HttpAlertConstants.REQUEST_TYPE);
}
public AlertResult send(String msg) {
AlertResult alertResult = new AlertResult();
createHttpRequest(msg);
if (httpRequest == null) {
alertResult.setStatus("false");
alertResult.setMessage("Request types are not supported");
return alertResult;
}
try {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
CloseableHttpResponse response = httpClient.execute(httpRequest);
HttpEntity entity = response.getEntity();
String resp = EntityUtils.toString(entity, DEFAULT_CHARSET);
alertResult.setStatus("true");
alertResult.setMessage(resp);
} catch (Exception e) {
logger.error("send http alert msg exception : {}", e.getMessage());
alertResult.setStatus("false");
alertResult.setMessage("send http request alert fail.");
}
return alertResult;
}
private void createHttpRequest(String msg) {
if (REQUEST_TYPE_POST.equals(requestType)) {
httpRequest = new HttpPost(url);
//POST request add param in request body
setMsgInRequestBody(msg);
} else if (REQUEST_TYPE_GET.equals(requestType)) {
//GET request add param in url
setMsgInUrl(msg);
httpRequest = new HttpGet(url);
}
setHeader();
}
/**
* add msg param in url
*/
private void setMsgInUrl(String msg) {
if (StringUtils.isNotBlank(contentField)) {
String type = "&";
//check splice char is & or ?
if (!url.contains(URL_SPLICE_CHAR)) {
type = URL_SPLICE_CHAR;
}
url = String.format("%s%s%s=%s", url, type, contentField, msg);
}
}
/**
* set header params
*/
private void setHeader() {
if (httpRequest == null) {
return;
}
HashMap<String, Object> map = JSONUtils.parseObject(headerParams, HashMap.class);
for (Map.Entry<String, Object> entry : map.entrySet()) {
httpRequest.setHeader(entry.getKey(), String.valueOf(entry.getValue()));
}
}
/**
* set body params
*/
private String setMsgInRequestBody(String msg) {
ObjectNode objectNode = JSONUtils.parseObject(bodyParams);
//set msg content field
objectNode.put(contentField, msg);
return objectNode.toString();
}
}

44
dolphinscheduler-plugin-api/src/test/java/org/apache/dolphinscheduler/plugin/model/AlertInfoTest.java → dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactoryTest.java

@ -14,41 +14,41 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.model;
import org.junit.Before;
import org.junit.Test;
package org.apache.dolphinscheduler.plugin.alert.http;
import java.util.HashMap;
import java.util.Map;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import static org.junit.Assert.*;
import java.util.List;
public class AlertInfoTest {
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
private AlertInfo alertInfo;
/**
* HttpAlertChannelFactory UT
*/
public class HttpAlertChannelFactoryTest {
private HttpAlertChannelFactory httpAlertChannelFactory;
@Before
public void before() {
alertInfo = new AlertInfo();
public void init() {
httpAlertChannelFactory = new HttpAlertChannelFactory();
}
@Test
public void getAlertProps() {
Map<String, Object> map = new HashMap<>();
alertInfo.setAlertProps(map);
assertNotNull(alertInfo.getAlertProps());
}
public void getParamsTest() {
@Test
public void getProp() {
alertInfo.addProp("k", "v");
assertEquals("v", alertInfo.getProp("k"));
List<PluginParams> pluginParamsList = httpAlertChannelFactory.getParams();
Assert.assertEquals(5, pluginParamsList.size());
}
@Test
public void getAlertData() {
alertInfo.setAlertData(new AlertData());
assertNotNull(alertInfo.getAlertData());
public void createTest() {
AlertChannel alertChannel = httpAlertChannelFactory.create();
Assert.assertNotNull(alertChannel);
}
}

104
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelTest.java

@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.http;
import org.apache.dolphinscheduler.spi.alert.AlertData;
import org.apache.dolphinscheduler.spi.alert.AlertInfo;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.params.InputParam;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.params.base.Validate;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
/**
* HttpAlertChannel UT
*/
public class HttpAlertChannelTest {
@Test
public void processTest() {
HttpAlertChannel alertChannel = new HttpAlertChannel();
AlertInfo alertInfo = new AlertInfo();
AlertData alertData = new AlertData();
alertData.setContent("Fault tolerance warning");
alertInfo.setAlertData(alertData);
AlertResult alertResult = alertChannel.process(alertInfo);
Assert.assertEquals("Request types are not supported", alertResult.getMessage());
}
@Test
public void processTest2() {
HttpAlertChannel alertChannel = new HttpAlertChannel();
AlertInfo alertInfo = new AlertInfo();
AlertData alertData = new AlertData();
alertData.setContent("Fault tolerance warning");
alertInfo.setAlertData(alertData);
alertInfo.setAlertParams(getParams());
AlertResult alertResult = alertChannel.process(alertInfo);
Assert.assertEquals("true", alertResult.getStatus());
}
/**
* create params
*/
private String getParams() {
List<PluginParams> paramsList = new ArrayList<>();
InputParam urlParam = InputParam.newBuilder("url", "url")
.setValue("http://www.baidu.com")
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
InputParam headerParams = InputParam.newBuilder("headerParams", "headerParams")
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue("{\"Content-Type\":\"application/json\"}")
.build();
InputParam bodyParams = InputParam.newBuilder("bodyParams", "bodyParams")
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue("{\"number\":\"13457654323\"}")
.build();
InputParam content = InputParam.newBuilder("contentField", "contentField")
.setValue("content")
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
InputParam requestType = InputParam.newBuilder("requestType", "requestType")
.setValue("POST")
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
paramsList.add(urlParam);
paramsList.add(headerParams);
paramsList.add(bodyParams);
paramsList.add(content);
paramsList.add(requestType);
return JSONUtils.toJsonString(paramsList);
}
}

38
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPluginTest.java

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.http;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import org.junit.Assert;
import org.junit.Test;
/**
* HttpAlertPlugin UT
*/
public class HttpAlertPluginTest {
@Test
public void getAlertChannelFactorysTest() {
HttpAlertPlugin httpAlertPlugin = new HttpAlertPlugin();
Iterable<AlertChannelFactory> alertChannelFactorys = httpAlertPlugin.getAlertChannelFactorys();
Assert.assertNotNull(alertChannelFactorys);
}
}

46
dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSenderTest.java

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.http;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
/**
* HttpSender UT
*/
public class HttpSenderTest {
@Test
public void sendTest() {
Map<String, String> paramsMap = new HashMap<>();
paramsMap.put(HttpAlertConstants.URL, "http://www.baidu.com");
paramsMap.put(HttpAlertConstants.REQUEST_TYPE, "POST");
paramsMap.put(HttpAlertConstants.HEADER_PARAMS, "{\"Content-Type\":\"application/json\"}");
paramsMap.put(HttpAlertConstants.BODY_PARAMS, "{\"number\":\"13457654323\"}");
paramsMap.put(HttpAlertConstants.CONTENT_FIELD, "content");
HttpSender httpSender = new HttpSender(paramsMap);
AlertResult alertResult = httpSender.send("Fault tolerance warning");
Assert.assertEquals("true", alertResult.getStatus());
}
}

79
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/pom.xml

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dolphinscheduler-alert-plugin</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<version>1.3.4-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-alert-script</artifactId>
<packaging>dolphinscheduler-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>dolphinscheduler-alert-script-${project.version}</finalName>
</build>
</project>

32
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/OSUtils.java

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
/**
* OSUtils
*/
public class OSUtils {
public OSUtils() {
throw new UnsupportedOperationException("Construct OSUtils");
}
static Boolean isWindows() {
return System.getProperty("os.name").startsWith("Windows");
}
}

62
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtils.java

@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ProcessUtils
*/
public class ProcessUtils {
private static final Logger logger = LoggerFactory.getLogger(ProcessUtils.class);
private ProcessUtils() {
throw new IllegalStateException("Utility class");
}
/**
* executeScript
*
* @param cmd cmd params
* @return exit code
*/
static Integer executeScript(String... cmd) {
int exitCode = -1;
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
try {
Process process = processBuilder.start();
StreamGobbler inputStreamGobbler = new StreamGobbler(process.getInputStream());
StreamGobbler errorStreamGobbler = new StreamGobbler(process.getErrorStream());
inputStreamGobbler.start();
errorStreamGobbler.start();
return process.waitFor();
} catch (IOException | InterruptedException e) {
logger.error("execute alert script error {}", e.getMessage());
Thread.currentThread().interrupt();
}
return exitCode;
}
}

40
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannel.java

@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertData;
import org.apache.dolphinscheduler.spi.alert.AlertInfo;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import java.util.Map;
/**
* ScriptAlertChannel
*/
public class ScriptAlertChannel implements AlertChannel {
@Override
public AlertResult process(AlertInfo alertinfo) {
AlertData alertData = alertinfo.getAlertData();
String alertParams = alertinfo.getAlertParams();
Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
return new ScriptSender(paramsMap).sendScriptAlert(alertData.getTitle());
}
}

71
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactory.java

@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import org.apache.dolphinscheduler.spi.params.InputParam;
import org.apache.dolphinscheduler.spi.params.RadioParam;
import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.params.base.Validate;
import java.util.Arrays;
import java.util.List;
/**
* ScriptAlertChannelFactory
*/
public class ScriptAlertChannelFactory implements AlertChannelFactory {
@Override
public String getName() {
return "Script";
}
@Override
public List<PluginParams> getParams() {
InputParam scriptUserParam = InputParam.newBuilder(ScriptParamsConstants.NAME_SCRIPT_USER_PARAMS, ScriptParamsConstants.SCRIPT_USER_PARAMS)
.addValidate(Validate.newBuilder()
.setRequired(false)
.build())
.setPlaceholder("please enter your custom parameters, which will be passed to you when calling your script")
.build();
// need check file type and file exist
InputParam scriptPathParam = InputParam.newBuilder(ScriptParamsConstants.NAME_SCRIPT_PATH, ScriptParamsConstants.SCRIPT_PATH)
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.setPlaceholder("please upload the file to the disk directory of the alert server, and ensure that the path is absolute and has the corresponding access rights")
.build();
RadioParam scriptTypeParams = RadioParam.newBuilder(ScriptParamsConstants.NAME_SCRIPT_TYPE, ScriptParamsConstants.SCRIPT_TYPE)
.addParamsOptions(new ParamsOptions(ScriptType.SHELL.getDescp(), ScriptType.SHELL.getCode(), false))
.setValue(ScriptType.SHELL.getCode())
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
return Arrays.asList(scriptUserParam, scriptPathParam, scriptTypeParams);
}
@Override
public AlertChannel create() {
return new ScriptAlertChannel();
}
}

35
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertPlugin.java

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import com.google.common.collect.ImmutableList;
/**
* ScriptAlertPlugin
*/
public class ScriptAlertPlugin implements DolphinSchedulerPlugin {
@Override
public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
return ImmutableList.of(new ScriptAlertChannelFactory());
}
}

40
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptParamsConstants.java

@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
/**
* ScriptParamsConstants
*/
public class ScriptParamsConstants {
public ScriptParamsConstants() {
throw new IllegalStateException("Utility class");
}
static final String SCRIPT_TYPE = "type";
static final String NAME_SCRIPT_TYPE = "type";
static final String SCRIPT_PATH = "path";
static final String NAME_SCRIPT_PATH = "path";
static final String SCRIPT_USER_PARAMS = "user.params";
static final String NAME_SCRIPT_USER_PARAMS = "userParams";
}

74
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSender.java

@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ScriptSender
*/
public class ScriptSender {
private static final Logger logger = LoggerFactory.getLogger(ScriptSender.class);
private String scriptPath;
private Integer scriptType;
private String userParams;
ScriptSender(Map<String, String> config) {
scriptPath = config.get(ScriptParamsConstants.NAME_SCRIPT_PATH);
scriptType = Integer.parseInt(config.get(ScriptParamsConstants.NAME_SCRIPT_TYPE));
userParams = config.get(ScriptParamsConstants.NAME_SCRIPT_USER_PARAMS);
}
AlertResult sendScriptAlert(String msg) {
AlertResult alertResult = new AlertResult();
if (ScriptType.of(scriptType).equals(ScriptType.SHELL)) {
return executeShellScript(msg);
}
return alertResult;
}
private AlertResult executeShellScript(String msg) {
AlertResult alertResult = new AlertResult();
alertResult.setStatus("false");
if (Boolean.TRUE.equals(OSUtils.isWindows())) {
alertResult.setMessage("shell script not support windows os");
return alertResult;
}
String[] cmd = {"/bin/sh", "-c", scriptPath + " " + msg + " " + userParams};
int exitCode = ProcessUtils.executeScript(cmd);
if (exitCode == 0) {
alertResult.setStatus("true");
alertResult.setMessage("send script alert msg success");
return alertResult;
}
alertResult.setMessage("send script alert msg error,exitCode is " + exitCode);
logger.info("send script alert msg error,exitCode is {}", exitCode);
return alertResult;
}
}

49
dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/AlertInfo.java → dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptType.java

@ -14,48 +14,49 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.model;
package org.apache.dolphinscheduler.plugin.alert.script;
import java.util.HashMap;
import java.util.Map;
/**
* AlertInfo
* ScriptType
*/
public class AlertInfo {
public enum ScriptType {
private Map<String, Object> alertProps;
private AlertData alertData;
SHELL(0, "SHELL"),
;
public AlertInfo() {
this.alertProps = new HashMap<>();
ScriptType(int code, String descp) {
this.code = code;
this.descp = descp;
}
public Map<String, Object> getAlertProps() {
return alertProps;
}
private final int code;
private final String descp;
public AlertInfo setAlertProps(Map<String, Object> alertProps) {
this.alertProps = alertProps;
return this;
public int getCode() {
return code;
}
public AlertInfo addProp(String key, Object value) {
this.alertProps.put(key, value);
return this;
public String getDescp() {
return descp;
}
public Object getProp(String key) {
return this.alertProps.get(key);
}
private static final Map<Integer, ScriptType> SCRIPT_TYPE_MAP = new HashMap<>();
public AlertData getAlertData() {
return alertData;
static {
for (ScriptType scriptType : ScriptType.values()) {
SCRIPT_TYPE_MAP.put(scriptType.code, scriptType);
}
}
public AlertInfo setAlertData(AlertData alertData) {
this.alertData = alertData;
return this;
public static ScriptType of(Integer code) {
if (SCRIPT_TYPE_MAP.containsKey(code)) {
return SCRIPT_TYPE_MAP.get(code);
}
throw new IllegalArgumentException("invalid code : " + code);
}
}

51
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/DingTalkManager.java → dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/StreamGobbler.java

@ -15,40 +15,47 @@
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.manager;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.alert.utils.DingTalkUtils;
import org.apache.dolphinscheduler.plugin.model.AlertInfo;
package org.apache.dolphinscheduler.plugin.alert.script;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Ding Talk Manager
* StreamGobbler
*/
public class DingTalkManager {
private static final Logger logger = LoggerFactory.getLogger(DingTalkManager.class);
public class StreamGobbler extends Thread {
private static final Logger logger = LoggerFactory.getLogger(StreamGobbler.class);
private InputStream inputStream;
StreamGobbler(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public void run() {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader inputBufferReader = new BufferedReader(inputStreamReader);
public Map<String, Object> send(AlertInfo alert) {
Map<String, Object> retMap = new HashMap<>();
retMap.put(Constants.STATUS, false);
logger.info("send message {}", alert.getAlertData().getTitle());
try {
String msg = buildMessage(alert);
DingTalkUtils.sendDingTalkMsg(msg, Constants.UTF_8);
String line;
StringBuilder output = new StringBuilder();
while ((line = inputBufferReader.readLine()) != null) {
output.append(line);
output.append(System.getProperty("line.separator"));
}
if (output.length() > 0) {
logger.info("out put msg is{}",output.toString());
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
logger.error("I/O error occurs {}", e.getMessage());
}
retMap.put(Constants.STATUS, true);
return retMap;
}
private String buildMessage(AlertInfo alert) {
return alert.getAlertData().getContent();
}
}

37
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtilsTest.java

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
import org.junit.Test;
/**
* ProcessUtilsTest
*/
public class ProcessUtilsTest {
private static final String rootPath = System.getProperty("user.dir");
private static final String shellFilPath = rootPath + "/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/example.sh";
private String[] cmd = {"/bin/sh", "-c", shellFilPath + " " + "testMsg" + " " + "userParams"};
@Test
public void testExecuteScript() {
ProcessUtils.executeScript(cmd);
}
}

48
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactoryTest.java

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
/**
* ScriptAlertChannelFactoryTest
*/
public class ScriptAlertChannelFactoryTest {
@Test
public void testGetParams() {
ScriptAlertChannelFactory scriptAlertChannelFactory = new ScriptAlertChannelFactory();
List<PluginParams> params = scriptAlertChannelFactory.getParams();
JSONUtils.toJsonString(params);
Assert.assertEquals(3, params.size());
}
@Test
public void testCreate() {
ScriptAlertChannelFactory scriptAlertChannelFactory = new ScriptAlertChannelFactory();
AlertChannel alertChannel = scriptAlertChannelFactory.create();
Assert.assertNotNull(alertChannel);
}
}

58
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSenderTest.java

@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.script;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* ScriptSenderTest
*/
public class ScriptSenderTest {
private static Map<String, String> scriptConfig = new HashMap<>();
private static final String rootPath = System.getProperty("user.dir");
private static final String shellFilPath = rootPath + "/src/test/script/shell/scriptTest.sh";
@Before
public void initScriptConfig() {
scriptConfig.put(ScriptParamsConstants.NAME_SCRIPT_TYPE, String.valueOf(ScriptType.SHELL.getCode()));
scriptConfig.put(ScriptParamsConstants.NAME_SCRIPT_USER_PARAMS, "userParams");
scriptConfig.put(ScriptParamsConstants.NAME_SCRIPT_PATH, shellFilPath);
}
@Test
public void testScriptSenderTest() {
ScriptSender scriptSender = new ScriptSender(scriptConfig);
AlertResult alertResult;
alertResult = scriptSender.sendScriptAlert("success");
Assert.assertEquals("true", alertResult.getStatus());
alertResult = scriptSender.sendScriptAlert("errorMsg");
Assert.assertEquals("false", alertResult.getStatus());
}
}

25
dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/example.sh

@ -0,0 +1,25 @@
#
# 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.
#
msg=$1
content=$2
# Write your specific logic here
# Set the exit code according to your execution result, and alert needs to use it to judge the status of this alarm result
exit 0

13
dolphinscheduler-plugin-api/src/test/resources/plugin.properties → dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/scriptTest.sh

@ -15,8 +15,11 @@
# limitations under the License.
#
test.string=teststring
test.false=false
test.true=true
cba=3.1
test.long=100
msg=$1
content=$2
if [ $msg = errorMsg ]
then
exit 12
fi
exit 0

78
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/pom.xml

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dolphinscheduler-alert-plugin</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<version>1.3.4-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-alert-wechat</artifactId>
<packaging>dolphinscheduler-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>dolphinscheduler-alert-wechat-${project.version}</finalName>
</build>
</project>

41
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannel.java

@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.wechat;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertData;
import org.apache.dolphinscheduler.spi.alert.AlertInfo;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import java.util.Map;
/**
* WeChatAlertChannel
*/
public class WeChatAlertChannel implements AlertChannel {
@Override
public AlertResult process(AlertInfo info) {
AlertData alertData = info.getAlertData();
String alertParams = info.getAlertParams();
Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
return new WeChatSender(paramsMap).sendEnterpriseWeChat(alertData.getTitle(), alertData.getContent());
}
}

94
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactory.java

@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.wechat;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import org.apache.dolphinscheduler.spi.alert.AlertConstants;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.params.InputParam;
import org.apache.dolphinscheduler.spi.params.RadioParam;
import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.params.base.Validate;
import java.util.Arrays;
import java.util.List;
/**
* WeChatAlertChannelFactory
*/
public class WeChatAlertChannelFactory implements AlertChannelFactory {
@Override
public String getName() {
return "WeChat";
}
@Override
public List<PluginParams> getParams() {
InputParam corpIdParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_CORP_ID, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_CORP_ID)
.setPlaceholder("please input corp id ")
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam secretParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_SECRET, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_SECRET)
.setPlaceholder("please input secret ")
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam usersParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USERS, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_USERS)
.setPlaceholder("please input users ")
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam userSendMsgParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USER_SEND_MSG, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_USER_SEND_MSG)
.setPlaceholder("please input corp id ")
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
InputParam agentIdParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_AGENT_ID, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_AGENT_ID)
.setPlaceholder("please input agent id ")
.addValidate(Validate.newBuilder()
.setRequired(true)
.build())
.build();
RadioParam showType = RadioParam.newBuilder(AlertConstants.SHOW_TYPE, AlertConstants.SHOW_TYPE)
.addParamsOptions(new ParamsOptions(ShowType.TABLE.getDescp(), ShowType.TABLE.getDescp(), false))
.addParamsOptions(new ParamsOptions(ShowType.TEXT.getDescp(), ShowType.TEXT.getDescp(), false))
.setValue(ShowType.TABLE.getDescp())
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
return Arrays.asList(corpIdParam, secretParam, usersParam, userSendMsgParam, agentIdParam, showType);
}
@Override
public AlertChannel create() {
return new WeChatAlertChannel();
}
}

34
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertConstants.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 org.apache.dolphinscheduler.plugin.alert.wechat;
/**
* WeChatAlertConstants
*/
public class WeChatAlertConstants {
static final String MARKDOWN_QUOTE = ">";
static final String MARKDOWN_ENTER = "\n";
static final String CHARSET = "UTF-8";
static final String WE_CHAT_PUSH_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}";
static final String WE_CHAT_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={secret}";
}

56
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertParamsConstants.java

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.wechat;
/**
* WeChatAlertParamsConstants
*/
public class WeChatAlertParamsConstants {
static final String ENTERPRISE_WE_CHAT_CORP_ID = "corp.id";
static final String NAME_ENTERPRISE_WE_CHAT_CORP_ID = "corpId";
static final String ENTERPRISE_WE_CHAT_SECRET = "secret";
static final String NAME_ENTERPRISE_WE_CHAT_SECRET = "secret";
static final String ENTERPRISE_WE_CHAT_TEAM_SEND_MSG = "team.send.msg";
static final String NAME_ENTERPRISE_WE_CHAT_TEAM_SEND_MSG = "teamSendMsg";
static final String ENTERPRISE_WE_CHAT_USER_SEND_MSG = "user.send.msg";
static final String NAME_ENTERPRISE_WE_CHAT_USER_SEND_MSG = "userSendMsg";
static final String ENTERPRISE_WE_CHAT_AGENT_ID = "agent.id";
static final String NAME_ENTERPRISE_WE_CHAT_AGENT_ID = "agentId";
static final String ENTERPRISE_WE_CHAT_USERS = "users";
static final String NAME_ENTERPRISE_WE_CHAT_USERS = "users";
}

34
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertPlugin.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 org.apache.dolphinscheduler.plugin.alert.wechat;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import com.google.common.collect.ImmutableList;
/**
* WeChatAlertPlugin
*/
public class WeChatAlertPlugin implements DolphinSchedulerPlugin {
@Override
public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
return ImmutableList.of(new WeChatAlertChannelFactory());
}
}

335
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSender.java

@ -0,0 +1,335 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.wechat;
import static java.util.Objects.requireNonNull;
import org.apache.dolphinscheduler.spi.alert.AlertConstants;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import org.apache.dolphinscheduler.spi.utils.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* WeChatSender
*/
public class WeChatSender {
private static Logger logger = LoggerFactory.getLogger(WeChatSender.class);
private String weChatAgentId;
private String weChatUsers;
private String weChatTeamSendMsg;
private String weChatUserSendMsg;
private String weChatTokenUrlReplace;
private String weChatToken;
private String showType;
private static final String agentIdRegExp = "{agentId}";
private static final String msgRegExp = "{msg}";
private static final String userRegExp = "{toUser}";
private static final String corpIdRegex = "{corpId}";
private static final String secretRegex = "{secret}";
private static final String toPartyRegex = "{toParty}";
private static final String toUserRegex = "{toUser}";
private static final String tokenRegex = "{token}";
WeChatSender(Map<String, String> config) {
weChatAgentId = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_AGENT_ID);
weChatUsers = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USERS);
String weChatCorpId = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_CORP_ID);
String weChatSecret = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_SECRET);
String weChatTokenUrl = WeChatAlertConstants.WE_CHAT_TOKEN_URL;
weChatTeamSendMsg = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_TEAM_SEND_MSG);
weChatUserSendMsg = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USER_SEND_MSG);
showType = config.get(AlertConstants.SHOW_TYPE);
requireNonNull(showType, AlertConstants.SHOW_TYPE + " must not null");
weChatTokenUrlReplace = weChatTokenUrl
.replace(corpIdRegex, weChatCorpId)
.replace(secretRegex, weChatSecret);
weChatToken = getToken();
}
/**
* make user multi user message
*
* @param toUser the toUser
* @param agentId the agentId
* @param msg the msg
* @return Enterprise WeChat send message
*/
private String makeUserSendMsg(Collection<String> toUser, String agentId, String msg) {
String listUser = mkString(toUser);
return weChatUserSendMsg.replace(userRegExp, listUser)
.replace(agentIdRegExp, agentId)
.replace(msgRegExp, msg);
}
/**
* send Enterprise WeChat
*
* @return Enterprise WeChat resp, demo: {"errcode":0,"errmsg":"ok","invaliduser":""}
* @throws Exception the Exception
*/
public AlertResult sendEnterpriseWeChat(String title, String content) {
AlertResult alertResult;
List<String> userList = Arrays.asList(weChatUsers.split(","));
String data = markdownByAlert(title, content);
String msg = makeUserSendMsg(userList, weChatAgentId, data);
if (null == weChatToken) {
alertResult = new AlertResult();
alertResult.setMessage("send we chat alert fail,get weChat token error");
alertResult.setStatus("false");
return alertResult;
}
String enterpriseWeChatPushUrlReplace = WeChatAlertConstants.WE_CHAT_PUSH_URL.replace(tokenRegex, weChatToken);
try {
return checkWeChatSendMsgResult(post(enterpriseWeChatPushUrlReplace, msg));
} catch (Exception e) {
logger.info("send we chat alert msg exception : {}", e.getMessage());
alertResult = new AlertResult();
alertResult.setMessage("send we chat alert fail");
alertResult.setStatus("false");
}
return alertResult;
}
private static String post(String url, String data) throws IOException {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(data, WeChatAlertConstants.CHARSET));
CloseableHttpResponse response = httpClient.execute(httpPost);
String resp;
try {
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, WeChatAlertConstants.CHARSET);
EntityUtils.consume(entity);
} finally {
response.close();
}
logger.info("Enterprise WeChat send [{}], param:{}, resp:{}",
url, data, resp);
return resp;
}
}
/**
* convert table to markdown style
*
* @param title the title
* @param content the content
* @return markdown table content
*/
private static String markdownTable(String title, String content) {
List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
if (null == mapItemsList || mapItemsList.isEmpty()) {
logger.error("itemsList is null");
throw new RuntimeException("itemsList is null");
}
StringBuilder contents = new StringBuilder(200);
for (LinkedHashMap mapItems : mapItemsList) {
Set<Entry<String, Object>> entries = mapItems.entrySet();
Iterator<Entry<String, Object>> iterator = entries.iterator();
StringBuilder t = new StringBuilder(String.format("`%s`%s", title, WeChatAlertConstants.MARKDOWN_ENTER));
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
t.append(WeChatAlertConstants.MARKDOWN_QUOTE);
t.append(entry.getKey()).append(":").append(entry.getValue());
t.append(WeChatAlertConstants.MARKDOWN_ENTER);
}
contents.append(t);
}
return contents.toString();
}
/**
* convert text to markdown style
*
* @param title the title
* @param content the content
* @return markdown text
*/
private static String markdownText(String title, String content) {
if (StringUtils.isNotEmpty(content)) {
List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
if (null == mapItemsList || mapItemsList.isEmpty()) {
logger.error("itemsList is null");
throw new RuntimeException("itemsList is null");
}
StringBuilder contents = new StringBuilder(100);
contents.append(String.format("`%s`%n", title));
for (LinkedHashMap mapItems : mapItemsList) {
Set<Map.Entry<String, Object>> entries = mapItems.entrySet();
for (Entry<String, Object> entry : entries) {
contents.append(WeChatAlertConstants.MARKDOWN_QUOTE);
contents.append(entry.getKey()).append(":").append(entry.getValue());
contents.append(WeChatAlertConstants.MARKDOWN_ENTER);
}
}
return contents.toString();
}
return null;
}
/**
* Determine the mardown style based on the show type of the alert
*
* @return the markdown alert table/text
*/
private String markdownByAlert(String title, String content) {
String result = "";
if (showType.equals(ShowType.TABLE.getDescp())) {
result = markdownTable(title, content);
} else if (showType.equals(ShowType.TEXT.getDescp())) {
result = markdownText(title, content);
}
return result;
}
private String getToken() {
try {
return get(weChatTokenUrlReplace);
} catch (IOException e) {
logger.info("we chat alert get token error{}", e.getMessage());
}
return null;
}
private static String get(String url) throws IOException {
String resp;
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet httpGet = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, WeChatAlertConstants.CHARSET);
EntityUtils.consume(entity);
}
HashMap map = JSONUtils.parseObject(resp, HashMap.class);
if (map != null && null != map.get("access_token")) {
return map.get("access_token").toString();
} else {
return null;
}
}
}
private static String mkString(Iterable<String> list) {
if (null == list || StringUtils.isEmpty("|")) {
return null;
}
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String item : list) {
if (first) {
first = false;
} else {
sb.append("|");
}
sb.append(item);
}
return sb.toString();
}
public static class WeChatSendMsgResponse {
private Integer errcode;
private String errmsg;
public Integer getErrcode() {
return errcode;
}
public void setErrcode(Integer errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}
private static AlertResult checkWeChatSendMsgResult(String result) {
AlertResult alertResult = new AlertResult();
alertResult.setStatus("false");
if (null == result) {
alertResult.setMessage("we chat send fail");
logger.info("send we chat msg error,resp is null");
return alertResult;
}
WeChatSendMsgResponse sendMsgResponse = JSONUtils.parseObject(result, WeChatSendMsgResponse.class);
if (null == sendMsgResponse) {
alertResult.setMessage("we chat send fail");
logger.info("send we chat msg error,resp error");
return alertResult;
}
if (sendMsgResponse.errcode == 0) {
alertResult.setStatus("true");
alertResult.setMessage("we chat alert send success");
return alertResult;
}
alertResult.setStatus("false");
alertResult.setMessage(sendMsgResponse.getErrmsg());
return alertResult;
}
}

48
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactoryTest.java

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.wechat;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
/**
* WeChatAlertChannelFactoryTest
*/
public class WeChatAlertChannelFactoryTest {
@Test
public void testGetParams() {
WeChatAlertChannelFactory weChatAlertChannelFactory = new WeChatAlertChannelFactory();
List<PluginParams> params = weChatAlertChannelFactory.getParams();
JSONUtils.toJsonString(params);
Assert.assertEquals(6, params.size());
}
@Test
public void testCreate() {
WeChatAlertChannelFactory dingTalkAlertChannelFactory = new WeChatAlertChannelFactory();
AlertChannel alertChannel = dingTalkAlertChannelFactory.create();
Assert.assertNotNull(alertChannel);
}
}

89
dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSenderTest.java

@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.wechat;
import org.apache.dolphinscheduler.spi.alert.AlertConstants;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* WeChatSenderTest
*/
public class WeChatSenderTest {
private static Map<String, String> weChatConfig = new HashMap<>();
private String content = "[{\"id\":\"69\","
+
"\"name\":\"UserBehavior-0--1193959466\","
+
"\"Job name\":\"Start workflow\","
+
"\"State\":\"SUCCESS\","
+
"\"Recovery\":\"NO\","
+
"\"Run time\":\"1\","
+
"\"Start time\": \"2018-08-06 10:31:34.0\","
+
"\"End time\": \"2018-08-06 10:31:49.0\","
+
"\"Host\": \"192.168.xx.xx\","
+
"\"Notify group\" :\"4\"}]";
@Before
public void initDingTalkConfig() {
// Just for this test, I will delete these configurations before this PR is merged
weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_AGENT_ID, "100000");
weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_CORP_ID, "NAME_ENTERPRISE_WE_CHAT_CORP_ID");
weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_SECRET, "NAME_ENTERPRISE_WE_CHAT_SECRET");
weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USER_SEND_MSG, "{\"touser\":\"{toUser}\",\"agentid\":{agentId}"
+
",\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"{msg}\"}}"
);
weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USERS, "Kris");
weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_TEAM_SEND_MSG, "msg");
weChatConfig.put(AlertConstants.SHOW_TYPE, ShowType.TABLE.getDescp());
}
@Test
public void testSendWeChatTableMsg() {
WeChatSender weChatSender = new WeChatSender(weChatConfig);
AlertResult alertResult = weChatSender.sendEnterpriseWeChat("test", content);
Assert.assertEquals("false", alertResult.getStatus());
}
@Test
public void testSendWeChatTextMsg() {
weChatConfig.put(AlertConstants.SHOW_TYPE, ShowType.TEXT.getDescp());
WeChatSender weChatSender = new WeChatSender(weChatConfig);
AlertResult alertResult = weChatSender.sendEnterpriseWeChat("test", content);
Assert.assertEquals("false", alertResult.getStatus());
}
}

41
dolphinscheduler-alert-plugin/pom.xml

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dolphinscheduler</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<version>1.3.4-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-alert-plugin</artifactId>
<packaging>pom</packaging>
<modules>
<module>dolphinscheduler-alert-email</module>
<module>dolphinscheduler-alert-wechat</module>
<module>dolphinscheduler-alert-dingtalk</module>
<module>dolphinscheduler-alert-script</module>
<module>dolphinscheduler-alert-http</module>
</modules>
</project>

79
dolphinscheduler-alert/pom.xml

@ -32,38 +32,21 @@
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-spi</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<type>jar</type>
<scope>test</scope>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-remote</artifactId>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<scope>test</scope>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-common</artifactId>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
</dependency>
<dependency>
@ -108,6 +91,52 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.sonatype.aether</groupId>
<artifactId>aether-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.airlift.resolver</groupId>
<artifactId>resolver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

126
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/AlertServer.java

@ -17,21 +17,32 @@
package org.apache.dolphinscheduler.alert;
import org.apache.dolphinscheduler.alert.plugin.EmailAlertPlugin;
import static org.apache.dolphinscheduler.common.Constants.ALERT_RPC_PORT;
import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager;
import org.apache.dolphinscheduler.alert.plugin.DolphinPluginLoader;
import org.apache.dolphinscheduler.alert.plugin.DolphinPluginManagerConfig;
import org.apache.dolphinscheduler.alert.processor.AlertRequestProcessor;
import org.apache.dolphinscheduler.alert.runner.AlertSender;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.alert.utils.PropertyUtils;
import org.apache.dolphinscheduler.common.plugin.FilePluginManager;
import org.apache.dolphinscheduler.common.thread.Stopper;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.DaoFactory;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.dao.entity.Alert;
import org.apache.dolphinscheduler.remote.NettyRemotingServer;
import org.apache.dolphinscheduler.remote.command.CommandType;
import org.apache.dolphinscheduler.remote.config.NettyServerConfig;
import org.apache.dolphinscheduler.spi.utils.StringUtils;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
/**
* alert of start
*/
@ -42,34 +53,75 @@ public class AlertServer {
*/
private AlertDao alertDao = DaoFactory.getDaoInstance(AlertDao.class);
private PluginDao pluginDao = DaoFactory.getDaoInstance(PluginDao.class);
private AlertSender alertSender;
private static AlertServer instance;
private FilePluginManager alertPluginManager;
private AlertPluginManager alertPluginManager;
private DolphinPluginManagerConfig alertPluginManagerConfig;
public static final String ALERT_PLUGIN_BINDING = "alert.plugin.binding";
public static final String ALERT_PLUGIN_DIR = "alert.plugin.dir";
public static final String MAVEN_LOCAL_REPOSITORY = "maven.local.repository";
/**
* netty server
*/
private NettyRemotingServer server;
private static class AlertServerHolder {
private static final AlertServer INSTANCE = new AlertServer();
}
private static final String[] whitePrefixes = new String[]{"org.apache.dolphinscheduler.plugin.utils."};
public static final AlertServer getInstance() {
return AlertServerHolder.INSTANCE;
}
private static final String[] excludePrefixes = new String[]{
"org.apache.dolphinscheduler.plugin.",
"ch.qos.logback.",
"org.slf4j."
};
private AlertServer() {
public AlertServer() {
alertPluginManager =
new FilePluginManager(PropertyUtils.getString(Constants.PLUGIN_DIR), whitePrefixes, excludePrefixes);
// add default alert plugins
alertPluginManager.addPlugin(new EmailAlertPlugin());
}
public static synchronized AlertServer getInstance() {
if (null == instance) {
instance = new AlertServer();
private void initPlugin() {
alertPluginManager = new AlertPluginManager();
alertPluginManagerConfig = new DolphinPluginManagerConfig();
alertPluginManagerConfig.setPlugins(PropertyUtils.getString(ALERT_PLUGIN_BINDING));
if (StringUtils.isNotBlank(PropertyUtils.getString(ALERT_PLUGIN_DIR))) {
alertPluginManagerConfig.setInstalledPluginsDir(PropertyUtils.getString(ALERT_PLUGIN_DIR, Constants.ALERT_PLUGIN_PATH).trim());
}
if (StringUtils.isNotBlank(PropertyUtils.getString(MAVEN_LOCAL_REPOSITORY))) {
alertPluginManagerConfig.setMavenLocalRepository(PropertyUtils.getString(MAVEN_LOCAL_REPOSITORY).trim());
}
DolphinPluginLoader alertPluginLoader = new DolphinPluginLoader(alertPluginManagerConfig, ImmutableList.of(alertPluginManager));
try {
alertPluginLoader.loadPlugins();
} catch (Exception e) {
throw new RuntimeException("load Alert Plugin Failed !", e);
}
return instance;
}
public void start() {
logger.info("alert server ready start ");
/**
* init netty remoting server
*/
private void initRemoteServer() {
NettyServerConfig serverConfig = new NettyServerConfig();
serverConfig.setListenPort(ALERT_RPC_PORT);
this.server = new NettyRemotingServer(serverConfig);
this.server.registerProcessor(CommandType.ALERT_SEND_REQUEST, new AlertRequestProcessor(alertDao, alertPluginManager, pluginDao));
this.server.start();
}
/**
* Cyclic alert info sending alert
*/
private void runSender() {
while (Stopper.isRunning()) {
try {
Thread.sleep(Constants.ALERT_SCAN_INTERVAL);
@ -77,15 +129,43 @@ public class AlertServer {
logger.error(e.getMessage(), e);
Thread.currentThread().interrupt();
}
List<Alert> alerts = alertDao.listWaitExecutionAlert();
AlertSender alertSender = new AlertSender(alerts, alertDao, alertPluginManager);
alertSender.run();
if (alertPluginManager == null || alertPluginManager.getAlertChannelMap().size() == 0) {
logger.warn("No Alert Plugin . Can not send alert info. ");
} else {
List<Alert> alerts = alertDao.listWaitExecutionAlert();
alertSender = new AlertSender(alerts, alertDao, alertPluginManager, pluginDao);
alertSender.run();
}
}
}
/**
* start
*/
public void start() {
initPlugin();
initRemoteServer();
logger.info("alert server ready start ");
runSender();
}
/**
* stop
*/
public void stop() {
this.server.close();
logger.info("alert server shut down");
}
public static void main(String[] args) {
AlertServer alertServer = AlertServer.getInstance();
alertServer.start();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
alertServer.stop();
}
});
}
}

110
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EmailManager.java

@ -1,55 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.manager;
import org.apache.dolphinscheduler.alert.utils.MailUtils;
import java.util.List;
import java.util.Map;
/**
* email send manager
*/
public class EmailManager {
/**
* email send
* @param receiversList the receiver list
* @param receiversCcList the cc List
* @param title the title
* @param content the content
* @param showType the showType
* @return the send result
*/
public Map<String,Object> send(List<String> receiversList,List<String> receiversCcList,String title,String content,String showType){
return MailUtils.sendMails(receiversList, receiversCcList, title, content, showType);
}
/**
* msg send
* @param receiversList the receiver list
* @param title the title
* @param content the content
* @param showType the showType
* @return the send result
*/
public Map<String,Object> send(List<String> receiversList,String title,String content,String showType){
return MailUtils.sendMails(receiversList,title, content, showType);
}
}
///*
// * Licensed to the Apache Software Foundation (ASF) under one or more
// * contributor license agreements. See the NOTICE file distributed with
// * this work for additional information regarding copyright ownership.
// * The ASF licenses this file to You under the Apache License, Version 2.0
// * (the "License"); you may not use this file except in compliance with
// * the License. You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//package org.apache.dolphinscheduler.alert.manager;
//
//import org.apache.dolphinscheduler.alert.utils.MailUtils;
//
//import java.util.List;
//import java.util.Map;
//
///**
// * email send manager
// */
//public class EmailManager {
// /**
// * email send
// * @param receiversList the receiver list
// * @param receiversCcList the cc List
// * @param title the title
// * @param content the content
// * @param showType the showType
// * @return the send result
// */
// public Map<String,Object> send(List<String> receiversList,List<String> receiversCcList,String title,String content,String showType){
//
// return MailUtils.sendMails(receiversList, receiversCcList, title, content, showType);
// }
//
// /**
// * msg send
// * @param receiversList the receiver list
// * @param title the title
// * @param content the content
// * @param showType the showType
// * @return the send result
// */
// public Map<String,Object> send(List<String> receiversList,String title,String content,String showType){
//
// return MailUtils.sendMails(receiversList,title, content, showType);
// }
//
//}

59
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EnterpriseWeChatManager.java

@ -1,59 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.manager;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.alert.utils.EnterpriseWeChatUtils;
import org.apache.dolphinscheduler.plugin.model.AlertInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Enterprise WeChat Manager
*/
public class EnterpriseWeChatManager {
private static final Logger logger = LoggerFactory.getLogger(EnterpriseWeChatManager.class);
/**
* Enterprise We Chat send
* @param alertInfo the alert info
* @param token the token
* @return the send result
*/
public Map<String,Object> send(AlertInfo alertInfo, String token){
Map<String,Object> retMap = new HashMap<>();
retMap.put(Constants.STATUS, false);
String agentId = EnterpriseWeChatUtils.ENTERPRISE_WE_CHAT_AGENT_ID;
String users = EnterpriseWeChatUtils.ENTERPRISE_WE_CHAT_USERS;
List<String> userList = Arrays.asList(users.split(","));
logger.info("send message {}", alertInfo.getAlertData().getTitle());
String msg = EnterpriseWeChatUtils.makeUserSendMsg(userList, agentId,EnterpriseWeChatUtils.markdownByAlert(alertInfo.getAlertData()));
try {
EnterpriseWeChatUtils.sendEnterpriseWeChat(Constants.UTF_8, msg, token);
} catch (IOException e) {
logger.error(e.getMessage(),e);
}
retMap.put(Constants.STATUS, true);
return retMap;
}
}

10
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/MsgManager.java

@ -14,23 +14,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.manager;
import org.apache.dolphinscheduler.dao.entity.Alert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* SMS send manager
*/
public class MsgManager {
public class MsgManager {
private static final Logger logger = LoggerFactory.getLogger(MsgManager.class);
/**
* SMS send
*
* @param alert the alert
*/
public void send(Alert alert){
logger.info("send message {}",alert);
public void send(Alert alert) {
logger.info("send message {}", alert);
}
}

29
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AbstractDolphinPluginManager.java

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import org.apache.dolphinscheduler.dao.DaoFactory;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
public abstract class AbstractDolphinPluginManager {
protected PluginDao pluginDao = DaoFactory.getDaoInstance(PluginDao.class);
public abstract void installPlugin(DolphinSchedulerPlugin dolphinSchedulerPlugin);
}

99
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AlertPluginManager.java

@ -0,0 +1,99 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static com.google.common.base.Preconditions.checkState;
import org.apache.dolphinscheduler.common.enums.PluginType;
import org.apache.dolphinscheduler.dao.entity.PluginDefine;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
import org.apache.dolphinscheduler.spi.classloader.ThreadContextClassLoader;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* load the configured alert plugin and manager them
*/
public class AlertPluginManager extends AbstractDolphinPluginManager {
private static final Logger logger = LoggerFactory.getLogger(AlertPluginManager.class);
private final Map<String, AlertChannelFactory> alertChannelFactoryMap = new ConcurrentHashMap<>();
private final Map<String, AlertChannel> alertChannelMap = new ConcurrentHashMap<>();
public void addAlertChannelFactory(AlertChannelFactory alertChannelFactory) {
requireNonNull(alertChannelFactory, "alertChannelFactory is null");
if (alertChannelFactoryMap.putIfAbsent(alertChannelFactory.getName(), alertChannelFactory) != null) {
throw new IllegalArgumentException(format("Alert Plugin '{}' is already registered", alertChannelFactory.getName()));
}
try {
loadAlertChannel(alertChannelFactory.getName());
} catch (Exception e) {
throw new IllegalArgumentException(format("Alert Plugin '{}' is can not load .", alertChannelFactory.getName()));
}
}
protected void loadAlertChannel(String name) {
requireNonNull(name, "name is null");
AlertChannelFactory alertChannelFactory = alertChannelFactoryMap.get(name);
checkState(alertChannelFactory != null, "Alert Plugin {} is not registered", name);
try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(alertChannelFactory.getClass().getClassLoader())) {
AlertChannel alertChannel = alertChannelFactory.create();
this.alertChannelMap.put(name, alertChannel);
}
logger.info("-- Loaded Alert Plugin {} --", name);
}
public Map<String, AlertChannelFactory> getAlertChannelFactoryMap() {
return alertChannelFactoryMap;
}
public Map<String, AlertChannel> getAlertChannelMap() {
return alertChannelMap;
}
@Override
public void installPlugin(DolphinSchedulerPlugin dolphinSchedulerPlugin) {
for (AlertChannelFactory alertChannelFactory : dolphinSchedulerPlugin.getAlertChannelFactorys()) {
logger.info("Registering Alert Plugin '{}'", alertChannelFactory.getName());
this.addAlertChannelFactory(alertChannelFactory);
List<PluginParams> params = alertChannelFactory.getParams();
String nameEn = alertChannelFactory.getName();
String paramsJson = PluginParamsTransfer.transferParamsToJson(params);
PluginDefine pluginDefine = new PluginDefine(nameEn, PluginType.ALERT.getDesc(), paramsJson);
pluginDao.addOrUpdatePluginDefine(pluginDefine);
}
}
}

139
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginClassLoader.java

@ -0,0 +1,139 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import static java.util.Objects.requireNonNull;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
class DolphinPluginClassLoader
extends URLClassLoader {
private static final ClassLoader PLATFORM_CLASS_LOADER = findPlatformClassLoader();
private final ClassLoader spiClassLoader;
private final List<String> spiPackages;
private final List<String> spiResources;
public DolphinPluginClassLoader(
List<URL> urls,
ClassLoader spiClassLoader,
Iterable<String> spiPackages) {
this(urls,
spiClassLoader,
spiPackages,
Iterables.transform(spiPackages, DolphinPluginClassLoader::classNameToResource));
}
private DolphinPluginClassLoader(
List<URL> urls,
ClassLoader spiClassLoader,
Iterable<String> spiPackages,
Iterable<String> spiResources) {
// plugins should not have access to the system (application) class loader
super(urls.toArray(new URL[urls.size()]), PLATFORM_CLASS_LOADER);
this.spiClassLoader = requireNonNull(spiClassLoader, "spiClassLoader is null");
this.spiPackages = ImmutableList.copyOf(spiPackages);
this.spiResources = ImmutableList.copyOf(spiResources);
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// grab the magic lock
synchronized (getClassLoadingLock(name)) {
// Check if class is in the loaded classes cache
Class<?> cachedClass = findLoadedClass(name);
if (cachedClass != null) {
return resolveClass(cachedClass, resolve);
}
// If this is an SPI class, only check SPI class loader
if (isSpiClass(name)) {
return resolveClass(spiClassLoader.loadClass(name), resolve);
}
// Look for class locally
return super.loadClass(name, resolve);
}
}
private Class<?> resolveClass(Class<?> clazz, boolean resolve) {
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
@Override
public URL getResource(String name) {
// If this is an SPI resource, only check SPI class loader
if (isSpiResource(name)) {
return spiClassLoader.getResource(name);
}
// Look for resource locally
return super.getResource(name);
}
@Override
public Enumeration<URL> getResources(String name)
throws IOException {
// If this is an SPI resource, use SPI resources
if (isSpiClass(name)) {
return spiClassLoader.getResources(name);
}
// Use local resources
return super.getResources(name);
}
private boolean isSpiClass(String name) {
return spiPackages.stream().anyMatch(name::startsWith);
}
private boolean isSpiResource(String name) {
return spiResources.stream().anyMatch(name::startsWith);
}
private static String classNameToResource(String className) {
return className.replace('.', '/');
}
@SuppressWarnings("JavaReflectionMemberAccess")
private static ClassLoader findPlatformClassLoader() {
try {
// use platform class loader on Java 9
Method method = ClassLoader.class.getMethod("getPlatformClassLoader");
return (ClassLoader) method.invoke(null);
} catch (NoSuchMethodException ignored) {
// use null class loader on Java 8
return null;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new AssertionError(e);
}
}
}

139
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginDiscovery.java

@ -0,0 +1,139 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.createDirectories;
import static java.nio.file.Files.walkFileTree;
import static com.google.common.io.ByteStreams.toByteArray;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.objectweb.asm.ClassReader;
import org.sonatype.aether.artifact.Artifact;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* The role of this class is to load the plugin class during development
*/
final class DolphinPluginDiscovery {
private static final String JAVA_CLASS_FILE_SUFFIX = ".class";
private static final String PLUGIN_SERVICES_FILE = "META-INF/services/" + DolphinSchedulerPlugin.class.getName();
private DolphinPluginDiscovery() {
}
public static Set<String> discoverPluginsFromArtifact(Artifact artifact, ClassLoader classLoader)
throws IOException {
if (!artifact.getExtension().equals("dolphinscheduler-plugin")) {
throw new RuntimeException("Unexpected extension for main artifact: " + artifact);
}
File file = artifact.getFile();
if (!file.getPath().endsWith("/target/classes")) {
throw new RuntimeException("Unexpected file for main artifact: " + file);
}
if (!file.isDirectory()) {
throw new RuntimeException("Main artifact file is not a directory: " + file);
}
if (new File(file, PLUGIN_SERVICES_FILE).exists()) {
return ImmutableSet.of();
}
return listClasses(file.toPath()).stream()
.filter(name -> classInterfaces(name, classLoader).contains(DolphinSchedulerPlugin.class.getName()))
.collect(Collectors.toSet());
}
public static void writePluginServices(Iterable<String> plugins, File root)
throws IOException {
Path path = root.toPath().resolve(PLUGIN_SERVICES_FILE);
createDirectories(path.getParent());
try (Writer out = new OutputStreamWriter(new FileOutputStream(path.toFile()), UTF_8)) {
for (String plugin : plugins) {
out.write(plugin + "\n");
}
}
}
private static List<String> listClasses(Path base)
throws IOException {
ImmutableList.Builder<String> list = ImmutableList.builder();
walkFileTree(base, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
if (file.getFileName().toString().endsWith(JAVA_CLASS_FILE_SUFFIX)) {
String name = file.subpath(base.getNameCount(), file.getNameCount()).toString();
list.add(javaName(name.substring(0, name.length() - JAVA_CLASS_FILE_SUFFIX.length())));
}
return FileVisitResult.CONTINUE;
}
});
return list.build();
}
private static List<String> classInterfaces(String name, ClassLoader classLoader) {
ImmutableList.Builder<String> list = ImmutableList.builder();
ClassReader reader = readClass(name, classLoader);
for (String binaryName : reader.getInterfaces()) {
list.add(javaName(binaryName));
}
if (reader.getSuperName() != null) {
list.addAll(classInterfaces(javaName(reader.getSuperName()), classLoader));
}
return list.build();
}
private static ClassReader readClass(String name, ClassLoader classLoader) {
try (InputStream in = classLoader.getResourceAsStream(binaryName(name) + JAVA_CLASS_FILE_SUFFIX)) {
if (in == null) {
throw new RuntimeException("Failed to read class: " + name);
}
return new ClassReader(toByteArray(in));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static String binaryName(String javaName) {
return javaName.replace('.', '/');
}
private static String javaName(String binaryName) {
return binaryName.replace('/', '.');
}
}

194
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginLoader.java

@ -0,0 +1,194 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static com.google.common.base.Preconditions.checkState;
import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
import org.apache.dolphinscheduler.spi.classloader.ThreadContextClassLoader;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.aether.artifact.Artifact;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import io.airlift.resolver.ArtifactResolver;
/**
* Plugin Loader
* Load Plugin from pom when development and run server in IDE
* Load Plugin from the plugin directory when running on the server
*/
public class DolphinPluginLoader {
private static final Logger logger = LoggerFactory.getLogger(DolphinPluginLoader.class);
/**
* All third-party jar packages used in the classes which in spi package need to be add
*/
private static final ImmutableList<String> DOLPHIN_SPI_PACKAGES = ImmutableList.<String>builder()
.add("org.apache.dolphinscheduler.spi.")
.add("com.fasterxml.jackson.")
.build();
private final File installedPluginsDir;
private final List<String> configPlugins;
private ArtifactResolver resolver = null;
private final List<AbstractDolphinPluginManager> dolphinPluginManagerList;
public DolphinPluginLoader(DolphinPluginManagerConfig config, List<AbstractDolphinPluginManager> dolphinPluginManagerList) {
installedPluginsDir = config.getInstalledPluginsDir();
if (config.getPlugins() == null) {
this.configPlugins = ImmutableList.of();
} else {
this.configPlugins = ImmutableList.copyOf(config.getPlugins());
}
this.dolphinPluginManagerList = requireNonNull(dolphinPluginManagerList, "dolphinPluginManagerList is null");
if (configPlugins != null && configPlugins.size() > 0) {
this.resolver = new ArtifactResolver(config.getMavenLocalRepository(), config.getMavenRemoteRepository());
}
}
public void loadPlugins()
throws Exception {
for (File file : listPluginDirs(installedPluginsDir)) {
if (file.isDirectory()) {
loadPlugin(file.getAbsolutePath());
}
}
for (String plugin : configPlugins) {
loadPlugin(plugin);
}
}
private void loadPlugin(String plugin)
throws Exception {
logger.info("-- Loading Alert plugin {} --", plugin);
URLClassLoader pluginClassLoader = buildPluginClassLoader(plugin);
try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(pluginClassLoader)) {
loadPlugin(pluginClassLoader);
}
logger.info("-- Finished loading Alert plugin {} --", plugin);
}
private void loadPlugin(URLClassLoader pluginClassLoader) {
ServiceLoader<DolphinSchedulerPlugin> serviceLoader = ServiceLoader.load(DolphinSchedulerPlugin.class, pluginClassLoader);
List<DolphinSchedulerPlugin> plugins = ImmutableList.copyOf(serviceLoader);
checkState(!plugins.isEmpty(), "No service providers the plugin {}", DolphinSchedulerPlugin.class.getName());
for (DolphinSchedulerPlugin plugin : plugins) {
logger.info("Installing {}", plugin.getClass().getName());
for (AbstractDolphinPluginManager dolphinPluginManager : dolphinPluginManagerList) {
dolphinPluginManager.installPlugin(plugin);
}
}
}
private URLClassLoader buildPluginClassLoader(String plugin)
throws Exception {
File file = new File(plugin);
if (!file.isDirectory() && (file.getName().equals("pom.xml") || file.getName().endsWith(".pom"))) {
return buildPluginClassLoaderFromPom(file);
}
if (file.isDirectory()) {
return buildPluginClassLoaderFromDirectory(file);
} else {
throw new IllegalArgumentException(format("plugin must be a pom file or directory {} .", plugin));
}
}
private URLClassLoader buildPluginClassLoaderFromPom(File pomFile)
throws Exception {
List<Artifact> artifacts = resolver.resolvePom(pomFile);
URLClassLoader classLoader = createClassLoader(artifacts, pomFile.getPath());
Artifact artifact = artifacts.get(0);
Set<String> plugins = DolphinPluginDiscovery.discoverPluginsFromArtifact(artifact, classLoader);
if (!plugins.isEmpty()) {
DolphinPluginDiscovery.writePluginServices(plugins, artifact.getFile());
}
return classLoader;
}
private URLClassLoader buildPluginClassLoaderFromDirectory(File dir)
throws Exception {
logger.info("Classpath for {}:", dir.getName());
List<URL> urls = new ArrayList<>();
for (File file : listPluginDirs(dir)) {
logger.info(" {}", file);
urls.add(file.toURI().toURL());
}
return createClassLoader(urls);
}
private URLClassLoader createClassLoader(List<Artifact> artifacts, String name)
throws IOException {
logger.info("Classpath for {}:", name);
List<URL> urls = new ArrayList<>();
for (Artifact artifact : sortArtifacts(artifacts)) {
if (artifact.getFile() == null) {
throw new RuntimeException("Could not resolve artifact: " + artifact);
}
File file = artifact.getFile().getCanonicalFile();
logger.info(" {}", file);
urls.add(file.toURI().toURL());
}
return createClassLoader(urls);
}
private URLClassLoader createClassLoader(List<URL> urls) {
ClassLoader parent = getClass().getClassLoader();
return new DolphinPluginClassLoader(urls, parent, DOLPHIN_SPI_PACKAGES);
}
private static List<File> listPluginDirs(File installedPluginsDir) {
if (installedPluginsDir != null && installedPluginsDir.isDirectory()) {
File[] files = installedPluginsDir.listFiles();
if (files != null) {
Arrays.sort(files);
return ImmutableList.copyOf(files);
}
}
return ImmutableList.of();
}
private static List<Artifact> sortArtifacts(List<Artifact> artifacts) {
List<Artifact> list = new ArrayList<>(artifacts);
Collections.sort(list, Ordering.natural().nullsLast().onResultOf(Artifact::getFile));
return list;
}
}

121
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginManagerConfig.java

@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import java.io.File;
import java.util.List;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
/**
* Dolphin Scheduler Plugin Manager Config
*/
public class DolphinPluginManagerConfig {
/**
* The dir of the Alert Plugin in.
* When AlertServer is running on the server, it will load the Alert Plugin from this directory.
*/
private File installedPluginsDir;
/**
* The plugin should be load.
* The installedPluginsDir is empty when we development and run server in IDEA. Then we can config which plugin should be load by param name alert.plugin.binding in the alert.properties file
*/
private List<String> plugins;
/**
* Development, When AlertServer is running on IDE, AlertPluginLoad can load Alert Plugin from local Repository.
*/
private String mavenLocalRepository = System.getProperty("user.home") + "/.m2/repository";
private List<String> mavenRemoteRepository = ImmutableList.of("http://repo1.maven.org/maven2/");
public File getInstalledPluginsDir() {
return installedPluginsDir;
}
/**
* @param pluginDir
*/
public DolphinPluginManagerConfig setInstalledPluginsDir(String pluginDir) {
requireNonNull(pluginDir, "pluginDir can not be null");
File pluginDirFile = new File(pluginDir);
if (!pluginDirFile.exists()) {
throw new IllegalArgumentException(format("plugin dir not exists ! {}", pluginDirFile.getPath()));
}
this.installedPluginsDir = pluginDirFile;
return this;
}
public List<String> getPlugins() {
return plugins;
}
public DolphinPluginManagerConfig setPlugins(List<String> plugins) {
this.plugins = plugins;
return this;
}
/**
* When development and run server in IDE, this method can set plugins in alert.properties .
* Then when you start AlertServer in IDE, the plugin can be load.
* eg:
* file: alert.properties
* alert.plugin=\
* ../dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml, \
* ../dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/pom.xml
*
* @param plugins
* @return
*/
public DolphinPluginManagerConfig setPlugins(String plugins) {
if (plugins == null) {
this.plugins = null;
} else {
this.plugins = ImmutableList.copyOf(Splitter.on(',').omitEmptyStrings().trimResults().split(plugins));
}
return this;
}
public String getMavenLocalRepository() {
return mavenLocalRepository;
}
public DolphinPluginManagerConfig setMavenLocalRepository(String mavenLocalRepository) {
this.mavenLocalRepository = mavenLocalRepository;
return this;
}
public List<String> getMavenRemoteRepository() {
return mavenRemoteRepository;
}
public DolphinPluginManagerConfig setMavenRemoteRepository(List<String> mavenRemoteRepository) {
this.mavenRemoteRepository = mavenRemoteRepository;
return this;
}
public DolphinPluginManagerConfig setMavenRemoteRepository(String mavenRemoteRepository) {
this.mavenRemoteRepository = ImmutableList.copyOf(Splitter.on(',').omitEmptyStrings().trimResults().split(mavenRemoteRepository));
return this;
}
}

147
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/EmailAlertPlugin.java

@ -1,147 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import org.apache.dolphinscheduler.alert.manager.DingTalkManager;
import org.apache.dolphinscheduler.alert.manager.EmailManager;
import org.apache.dolphinscheduler.alert.manager.EnterpriseWeChatManager;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.alert.utils.DingTalkUtils;
import org.apache.dolphinscheduler.alert.utils.EnterpriseWeChatUtils;
import org.apache.dolphinscheduler.common.utils.CollectionUtils;
import org.apache.dolphinscheduler.common.utils.StringUtils;
import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
import org.apache.dolphinscheduler.plugin.model.AlertData;
import org.apache.dolphinscheduler.plugin.model.AlertInfo;
import org.apache.dolphinscheduler.plugin.model.PluginName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EmailAlertPlugin
* <p>
* This plugin is a default plugin, and mix up email and enterprise wechat, because adapt with former alert behavior
*/
public class EmailAlertPlugin implements AlertPlugin {
private static final Logger logger = LoggerFactory.getLogger(EmailAlertPlugin.class);
private PluginName pluginName;
private static final EmailManager emailManager = new EmailManager();
private static final EnterpriseWeChatManager weChatManager = new EnterpriseWeChatManager();
private static final DingTalkManager dingTalkManager = new DingTalkManager();
public EmailAlertPlugin() {
this.pluginName = new PluginName();
this.pluginName.setEnglish(Constants.PLUGIN_DEFAULT_EMAIL_EN);
this.pluginName.setChinese(Constants.PLUGIN_DEFAULT_EMAIL_CH);
}
@Override
public String getId() {
return Constants.PLUGIN_DEFAULT_EMAIL_ID;
}
@Override
public PluginName getName() {
return pluginName;
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> process(AlertInfo info) {
Map<String, Object> retMaps = new HashMap<>();
AlertData alert = info.getAlertData();
List<String> receiversList = (List<String>) info.getProp(Constants.PLUGIN_DEFAULT_EMAIL_RECEIVERS);
// receiving group list
// custom receiver
String receivers = alert.getReceivers();
if (StringUtils.isNotEmpty(receivers)) {
String[] splits = receivers.split(",");
receiversList.addAll(Arrays.asList(splits));
}
List<String> receiversCcList = new ArrayList<>();
// Custom Copier
String receiversCc = alert.getReceiversCc();
if (StringUtils.isNotEmpty(receiversCc)) {
String[] splits = receiversCc.split(",");
receiversCcList.addAll(Arrays.asList(splits));
}
if (CollectionUtils.isEmpty(receiversList) && CollectionUtils.isEmpty(receiversCcList)) {
logger.warn("alert send error : At least one receiver address required");
retMaps.put(Constants.STATUS, "false");
retMaps.put(Constants.MESSAGE, "execution failure,At least one receiver address required.");
return retMaps;
}
retMaps = emailManager.send(receiversList, receiversCcList, alert.getTitle(), alert.getContent(),
alert.getShowType());
//send flag
boolean flag = false;
if (retMaps == null) {
retMaps = new HashMap<>();
retMaps.put(Constants.MESSAGE, "alert send error.");
retMaps.put(Constants.STATUS, "false");
logger.info("alert send error : {}", retMaps.get(Constants.MESSAGE));
return retMaps;
}
flag = Boolean.parseBoolean(String.valueOf(retMaps.get(Constants.STATUS)));
if (flag) {
logger.info("alert send success");
retMaps.put(Constants.MESSAGE, "email send success.");
if (EnterpriseWeChatUtils.isEnable()) {
logger.info("Enterprise WeChat is enable!");
try {
String token = EnterpriseWeChatUtils.getToken();
weChatManager.send(info, token);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
if (DingTalkUtils.IS_ENABLE_DING_TALK) {
logger.info("Ding Talk is enable.");
dingTalkManager.send(info);
}
} else {
retMaps.put(Constants.MESSAGE, "alert send error.");
logger.info("alert send error : {}", retMaps.get(Constants.MESSAGE));
}
return retMaps;
}
}

67
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/processor/AlertRequestProcessor.java

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.processor;
import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager;
import org.apache.dolphinscheduler.alert.runner.AlertSender;
import org.apache.dolphinscheduler.common.utils.Preconditions;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.remote.command.Command;
import org.apache.dolphinscheduler.remote.command.CommandType;
import org.apache.dolphinscheduler.remote.command.alert.AlertSendRequestCommand;
import org.apache.dolphinscheduler.remote.command.alert.AlertSendResponseCommand;
import org.apache.dolphinscheduler.remote.processor.NettyRequestProcessor;
import org.apache.dolphinscheduler.remote.utils.JsonSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.channel.Channel;
/**
* alert request processor
*/
public class AlertRequestProcessor implements NettyRequestProcessor {
private final Logger logger = LoggerFactory.getLogger(AlertRequestProcessor.class);
private AlertDao alertDao;
private PluginDao pluginDao;
private AlertPluginManager alertPluginManager;
public AlertRequestProcessor(AlertDao alertDao, AlertPluginManager alertPluginManager, PluginDao pluginDao) {
this.alertDao = alertDao;
this.pluginDao = pluginDao;
this.alertPluginManager = alertPluginManager;
}
@Override
public void process(Channel channel, Command command) {
Preconditions.checkArgument(CommandType.ALERT_SEND_REQUEST == command.getType(),
String.format("invalid command type : %s", command.getType()));
AlertSendRequestCommand alertSendRequestCommand = JsonSerializer.deserialize(
command.getBody(), AlertSendRequestCommand.class);
logger.info("received command : {}", alertSendRequestCommand);
AlertSender alertSender = new AlertSender(alertDao, alertPluginManager, pluginDao);
AlertSendResponseCommand alertSendResponseCommand = alertSender.syncHandler(alertSendRequestCommand.getGroupId(), alertSendRequestCommand.getTitle(), alertSendRequestCommand.getContent());
channel.writeAndFlush(alertSendResponseCommand.convert2Command(command.getOpaque()));
}
}

174
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/runner/AlertSender.java

@ -14,23 +14,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.runner;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager;
import org.apache.dolphinscheduler.common.enums.AlertStatus;
import org.apache.dolphinscheduler.common.plugin.PluginManager;
import org.apache.dolphinscheduler.common.utils.CollectionUtils;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.dao.entity.Alert;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
import org.apache.dolphinscheduler.plugin.model.AlertData;
import org.apache.dolphinscheduler.plugin.model.AlertInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance;
import org.apache.dolphinscheduler.remote.command.alert.AlertSendResponseCommand;
import org.apache.dolphinscheduler.remote.command.alert.AlertSendResponseResult;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertData;
import org.apache.dolphinscheduler.spi.alert.AlertInfo;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* alert sender
@ -41,60 +46,135 @@ public class AlertSender {
private List<Alert> alertList;
private AlertDao alertDao;
private PluginManager pluginManager;
private PluginDao pluginDao;
private AlertPluginManager alertPluginManager;
public AlertSender() {
public AlertSender(AlertPluginManager alertPluginManager) {
this.alertPluginManager = alertPluginManager;
}
public AlertSender(List<Alert> alertList, AlertDao alertDao, PluginManager pluginManager) {
public AlertSender(AlertDao alertDao, AlertPluginManager alertPluginManager, PluginDao pluginDao) {
super();
this.alertDao = alertDao;
this.pluginDao = pluginDao;
this.alertPluginManager = alertPluginManager;
}
public AlertSender(List<Alert> alertList, AlertDao alertDao, AlertPluginManager alertPluginManager, PluginDao pluginDao) {
super();
this.alertList = alertList;
this.alertDao = alertDao;
this.pluginManager = pluginManager;
this.pluginDao = pluginDao;
this.alertPluginManager = alertPluginManager;
}
public void run() {
List<User> users;
Map<String, Object> retMaps = null;
for (Alert alert : alertList) {
users = alertDao.listUserByAlertgroupId(alert.getAlertGroupId());
// receiving group list
List<String> receiversList = new ArrayList<>();
for (User user : users) {
receiversList.add(user.getEmail());
//get alert group from alert
int alertGroupId = alert.getAlertGroupId();
List<AlertPluginInstance> alertInstanceList = alertDao.listInstanceByAlertGroupId(alertGroupId);
if (CollectionUtils.isEmpty(alertInstanceList)) {
logger.error("send alert msg fail,no bind plugin instance.");
return;
}
AlertData alertData = new AlertData();
alertData.setId(alert.getId())
.setAlertGroupId(alert.getAlertGroupId())
.setContent(alert.getContent())
.setLog(alert.getLog())
.setReceivers(alert.getReceivers())
.setReceiversCc(alert.getReceiversCc())
.setShowType(alert.getShowType().getDescp())
.setTitle(alert.getTitle());
AlertInfo alertInfo = new AlertInfo();
alertInfo.setAlertData(alertData);
alertInfo.addProp("receivers", receiversList);
AlertPlugin emailPlugin = pluginManager.findOne(Constants.PLUGIN_DEFAULT_EMAIL_ID);
retMaps = emailPlugin.process(alertInfo);
if (retMaps == null) {
alertDao.updateAlert(AlertStatus.EXECUTION_FAILURE, "alert send error", alert.getId());
logger.error("alert send error : return value is null");
} else if (!Boolean.parseBoolean(String.valueOf(retMaps.get(Constants.STATUS)))) {
alertDao.updateAlert(AlertStatus.EXECUTION_FAILURE, String.valueOf(retMaps.get(Constants.MESSAGE)), alert.getId());
logger.error("alert send error : {}", retMaps.get(Constants.MESSAGE));
} else {
alertDao.updateAlert(AlertStatus.EXECUTION_SUCCESS, (String) retMaps.get(Constants.MESSAGE), alert.getId());
logger.info("alert send success");
.setContent(alert.getContent())
.setLog(alert.getLog())
.setTitle(alert.getTitle());
for (AlertPluginInstance instance : alertInstanceList) {
AlertResult alertResult = this.alertResultHandler(instance, alertData);
AlertStatus alertStatus = Boolean.parseBoolean(String.valueOf(alertResult.getStatus())) ? AlertStatus.EXECUTION_SUCCESS : AlertStatus.EXECUTION_FAILURE;
alertDao.updateAlert(alertStatus, alertResult.getMessage(), alert.getId());
}
}
}
/**
* sync send alert handler
*
* @param alertGroupId alertGroupId
* @param title title
* @param content content
* @return AlertSendResponseCommand
*/
public AlertSendResponseCommand syncHandler(int alertGroupId, String title, String content) {
List<AlertPluginInstance> alertInstanceList = alertDao.listInstanceByAlertGroupId(alertGroupId);
AlertData alertData = new AlertData();
alertData.setContent(title)
.setTitle(content);
boolean sendResponseStatus = true;
List<AlertSendResponseResult> sendResponseResults = new ArrayList<>();
if (CollectionUtils.isEmpty(alertInstanceList)) {
sendResponseStatus = false;
AlertSendResponseResult alertSendResponseResult = new AlertSendResponseResult();
String message = String.format("Alert GroupId %s send error : not found alert instance", alertGroupId);
alertSendResponseResult.setStatus(sendResponseStatus);
alertSendResponseResult.setMessage(message);
sendResponseResults.add(alertSendResponseResult);
logger.error("Alert GroupId {} send error : not found alert instance", alertGroupId);
return new AlertSendResponseCommand(sendResponseStatus, sendResponseResults);
}
for (AlertPluginInstance instance : alertInstanceList) {
AlertResult alertResult = this.alertResultHandler(instance, alertData);
AlertSendResponseResult alertSendResponseResult = new AlertSendResponseResult(
Boolean.parseBoolean(String.valueOf(alertResult.getStatus())), alertResult.getMessage());
sendResponseStatus = sendResponseStatus && alertSendResponseResult.getStatus();
sendResponseResults.add(alertSendResponseResult);
}
return new AlertSendResponseCommand(sendResponseStatus, sendResponseResults);
}
/**
* alert result handler
*
* @param instance instance
* @param alertData alertData
* @return AlertResult
*/
private AlertResult alertResultHandler(AlertPluginInstance instance, AlertData alertData) {
String pluginName = pluginDao.getPluginDefineById(instance.getPluginDefineId()).getPluginName();
AlertChannel alertChannel = alertPluginManager.getAlertChannelMap().get(pluginName);
AlertResult alertResultExtend = new AlertResult();
String pluginInstanceName = instance.getInstanceName();
if (alertChannel == null) {
String message = String.format("Alert Plugin %s send error : return value is null", pluginInstanceName);
alertResultExtend.setStatus(String.valueOf(false));
alertResultExtend.setMessage(message);
logger.error("Alert Plugin {} send error : not found plugin {}", pluginInstanceName, pluginName);
return alertResultExtend;
}
AlertInfo alertInfo = new AlertInfo();
alertInfo.setAlertData(alertData);
alertInfo.setAlertParams(instance.getPluginInstanceParams());
AlertResult alertResult = alertChannel.process(alertInfo);
if (alertResult == null) {
String message = String.format("Alert Plugin %s send error : return alertResult value is null", pluginInstanceName);
alertResultExtend.setStatus(String.valueOf(false));
alertResultExtend.setMessage(message);
logger.info("Alert Plugin {} send error : return alertResult value is null", pluginInstanceName);
} else if (!Boolean.parseBoolean(String.valueOf(alertResult.getStatus()))) {
alertResultExtend.setStatus(String.valueOf(false));
alertResultExtend.setMessage(alertResult.getMessage());
logger.info("Alert Plugin {} send error : {}", pluginInstanceName, alertResult.getMessage());
} else {
String message = String.format("Alert Plugin %s send success", pluginInstanceName);
alertResultExtend.setStatus(String.valueOf(true));
alertResultExtend.setMessage(message);
logger.info("Alert Plugin {} send success", pluginInstanceName);
}
return alertResultExtend;
}
}

172
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/Constants.java

@ -30,177 +30,9 @@ public class Constants {
*/
public static final String ALERT_PROPERTIES_PATH = "/alert.properties";
public static final String DATA_SOURCE_PROPERTIES_PATH = "/dao/data_source.properties";
public static final String SINGLE_SLASH = "/";
/**
* UTF-8
*/
public static final String UTF_8 = "UTF-8";
public static final String STATUS = "status";
public static final String MESSAGE = "message";
public static final String MAIL_PROTOCOL = "mail.protocol";
public static final String MAIL_SERVER_HOST = "mail.server.host";
public static final String MAIL_SERVER_PORT = "mail.server.port";
public static final String MAIL_SENDER = "mail.sender";
public static final String MAIL_USER = "mail.user";
public static final String MAIL_PASSWD = "mail.passwd";
public static final String XLS_FILE_PATH = "xls.file.path";
public static final String MAIL_HOST = "mail.smtp.host";
public static final String MAIL_PORT = "mail.smtp.port";
public static final String MAIL_SMTP_AUTH = "mail.smtp.auth";
public static final String MAIL_TRANSPORT_PROTOCOL = "mail.transport.protocol";
public static final String MAIL_SMTP_STARTTLS_ENABLE = "mail.smtp.starttls.enable";
public static final String MAIL_SMTP_SSL_ENABLE = "mail.smtp.ssl.enable";
public static final String MAIL_SMTP_SSL_TRUST = "mail.smtp.ssl.trust";
public static final String TEXT_HTML_CHARSET_UTF_8 = "text/html;charset=utf-8";
public static final String STRING_TRUE = "true";
public static final String EXCEL_SUFFIX_XLS = ".xls";
public static final int NUMBER_1000 = 1000;
public static final String SPRING_DATASOURCE_DRIVER_CLASS_NAME = "spring.datasource.driver-class-name";
public static final String SPRING_DATASOURCE_URL = "spring.datasource.url";
public static final String SPRING_DATASOURCE_USERNAME = "spring.datasource.username";
public static final String SPRING_DATASOURCE_PASSWORD = "spring.datasource.password";
public static final String SPRING_DATASOURCE_VALIDATION_QUERY_TIMEOUT = "spring.datasource.validationQueryTimeout";
public static final String SPRING_DATASOURCE_INITIAL_SIZE = "spring.datasource.initialSize";
public static final String SPRING_DATASOURCE_MIN_IDLE = "spring.datasource.minIdle";
public static final String SPRING_DATASOURCE_MAX_ACTIVE = "spring.datasource.maxActive";
public static final String SPRING_DATASOURCE_MAX_WAIT = "spring.datasource.maxWait";
public static final String SPRING_DATASOURCE_TIME_BETWEEN_EVICTION_RUNS_MILLIS = "spring.datasource.timeBetweenEvictionRunsMillis";
public static final String SPRING_DATASOURCE_MIN_EVICTABLE_IDLE_TIME_MILLIS = "spring.datasource.minEvictableIdleTimeMillis";
public static final String SPRING_DATASOURCE_VALIDATION_QUERY = "spring.datasource.validationQuery";
public static final String SPRING_DATASOURCE_TEST_WHILE_IDLE = "spring.datasource.testWhileIdle";
public static final String SPRING_DATASOURCE_TEST_ON_BORROW = "spring.datasource.testOnBorrow";
public static final String SPRING_DATASOURCE_TEST_ON_RETURN = "spring.datasource.testOnReturn";
public static final String SPRING_DATASOURCE_POOL_PREPARED_STATEMENTS = "spring.datasource.poolPreparedStatements";
public static final String SPRING_DATASOURCE_DEFAULT_AUTO_COMMIT = "spring.datasource.defaultAutoCommit";
public static final String SPRING_DATASOURCE_KEEP_ALIVE = "spring.datasource.keepAlive";
public static final String SPRING_DATASOURCE_MAX_POOL_PREPARED_STATEMENT_PER_CONNECTION_SIZE = "spring.datasource.maxPoolPreparedStatementPerConnectionSize";
public static final String DEVELOPMENT = "development";
public static final String TR = "<tr>";
public static final String TD = "<td>";
public static final String TD_END = "</td>";
public static final String TR_END = "</tr>";
public static final String TITLE = "title";
public static final String CONTENT = "content";
public static final String TH = "<th>";
public static final String TH_END = "</th>";
/** default alert plugin dir **/
public static final String ALERT_PLUGIN_PATH = "./lib/plugin/alert";
public static final int ALERT_SCAN_INTERVAL = 5000;
public static final String MARKDOWN_QUOTE = ">";
public static final String MARKDOWN_ENTER = "\n";
public static final String ENTERPRISE_WECHAT_ENABLE = "enterprise.wechat.enable";
public static final String ENTERPRISE_WECHAT_CORP_ID = "enterprise.wechat.corp.id";
public static final String ENTERPRISE_WECHAT_SECRET = "enterprise.wechat.secret";
public static final String ENTERPRISE_WECHAT_TOKEN_URL = "enterprise.wechat.token.url";
public static final String ENTERPRISE_WECHAT_PUSH_URL = "enterprise.wechat.push.url";
public static final String ENTERPRISE_WECHAT_TEAM_SEND_MSG = "enterprise.wechat.team.send.msg";
public static final String ENTERPRISE_WECHAT_USER_SEND_MSG = "enterprise.wechat.user.send.msg";
public static final String ENTERPRISE_WECHAT_AGENT_ID = "enterprise.wechat.agent.id";
public static final String ENTERPRISE_WECHAT_USERS = "enterprise.wechat.users";
public static final String DINGTALK_WEBHOOK = "dingtalk.webhook";
public static final String DINGTALK_KEYWORD = "dingtalk.keyword";
public static final String DINGTALK_PROXY_ENABLE = "dingtalk.isEnableProxy";
public static final String DINGTALK_PROXY = "dingtalk.proxy";
public static final String DINGTALK_PORT = "dingtalk.port";
public static final String DINGTALK_USER = "dingtalk.user";
public static final String DINGTALK_PASSWORD = "dingtalk.password";
public static final String DINGTALK_ENABLE = "dingtalk.isEnable";
public static final String HTML_HEADER_PREFIX = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>"
+ "<html><head><title>dolphinscheduler</title><meta name='Keywords' content=''>"
+ "<meta name='Description' content=''>"
+ "<style type=\"text/css\">table {margin-top:0px;padding-top:0px;border:1px solid;font-size: 14px;color: #333333;border-width: 1px;border-color: #666666;border-collapse: collapse;}"
+ "table th {border-width: 1px;padding: 8px;border-style: solid;border-color: #666666;background-color: #dedede;text-align: left;}"
+ "table td {border-width: 1px;padding: 8px;border-style: solid;border-color: #666666;background-color: #ffffff;text-align: left;}</style>"
+ "/head><body style=\"margin:0;padding:0\"><table border=\"1px\" cellpadding=\"5px\" cellspacing=\"-10px\"> ";
public static final String TABLE_BODY_HTML_TAIL = "</table></body></html>";
/**
* plugin config
*/
public static final String PLUGIN_DIR = "plugin.dir";
public static final String PLUGIN_DEFAULT_EMAIL_ID = "email";
public static final String PLUGIN_DEFAULT_EMAIL_CH = "邮件";
public static final String PLUGIN_DEFAULT_EMAIL_EN = "email";
public static final String PLUGIN_DEFAULT_EMAIL_RECEIVERS = "receivers";
public static final String PLUGIN_DEFAULT_EMAIL_RECEIVERCCS = "receiverCcs";
public static final String RETMAP_MSG = "msg";
}

141
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/DingTalkUtils.java

@ -1,141 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.utils;
import org.apache.dolphinscheduler.common.utils.*;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DingTalkUtils utils
* support send msg to ding talk by robot message push function.
* support PROXY setting
*/
public class DingTalkUtils {
public static final Logger logger = LoggerFactory.getLogger(DingTalkUtils.class);
public static final boolean IS_ENABLE_DING_TALK = PropertyUtils.getBoolean(Constants.DINGTALK_ENABLE);
private static final String DING_TASK_URL = PropertyUtils.getString(Constants.DINGTALK_WEBHOOK);
private static final String KEYWORD = PropertyUtils.getString(Constants.DINGTALK_KEYWORD);
private static final Boolean IS_ENABLE_PROXY = PropertyUtils.getBoolean(Constants.DINGTALK_PROXY_ENABLE);
private static final String PROXY = PropertyUtils.getString(Constants.DINGTALK_PROXY);
private static final String USER = PropertyUtils.getString(Constants.DINGTALK_USER);
private static final String PASSWD = PropertyUtils.getString(Constants.DINGTALK_PASSWORD);
private static final Integer PORT = PropertyUtils.getInt(Constants.DINGTALK_PORT);
private DingTalkUtils() {
throw new IllegalStateException(DingTalkUtils.class.getName());
}
/**
* send message interface
* only support text message format now.
*
* @param msg message context to send
* @param charset charset type
* @return result of sending msg
* @throws IOException the IOException
*/
public static String sendDingTalkMsg(String msg, String charset) throws IOException {
String msgToJson = textToJsonString(msg + "#" + KEYWORD);
HttpPost httpPost = constructHttpPost(msgToJson, charset);
CloseableHttpClient httpClient;
if (Boolean.TRUE.equals(IS_ENABLE_PROXY)) {
httpClient = getProxyClient();
RequestConfig rcf = getProxyConfig();
httpPost.setConfig(rcf);
} else {
httpClient = getDefaultClient();
}
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
String resp;
try {
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, charset);
EntityUtils.consume(entity);
} finally {
response.close();
}
logger.info("Ding Talk send [{}], resp:{%s}", msg);
return resp;
} finally {
httpClient.close();
}
}
public static HttpPost constructHttpPost(String msg, String charset) {
HttpPost post = new HttpPost(DING_TASK_URL);
StringEntity entity = new StringEntity(msg, charset);
post.setEntity(entity);
post.addHeader("Content-Type", "application/json; charset=utf-8");
return post;
}
public static CloseableHttpClient getProxyClient() {
HttpHost httpProxy = new HttpHost(PROXY, PORT);
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(new AuthScope(httpProxy), new UsernamePasswordCredentials(USER, PASSWD));
return HttpClients.custom().setDefaultCredentialsProvider(provider).build();
}
public static CloseableHttpClient getDefaultClient() {
return HttpClients.createDefault();
}
public static RequestConfig getProxyConfig() {
HttpHost httpProxy = new HttpHost(PROXY, PORT);
return RequestConfig.custom().setProxy(httpProxy).build();
}
public static String textToJsonString(String text) {
Map<String, Object> items = new HashMap<>();
items.put("msgtype", "text");
Map<String, String> textContent = new HashMap<>();
byte[] byt = StringUtils.getBytesUtf8(text);
String txt = StringUtils.newStringUtf8(byt);
textContent.put("content", txt);
items.put("text", textContent);
return JSONUtils.toJsonString(items);
}
}

286
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/EnterpriseWeChatUtils.java

@ -1,286 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.utils;
import org.apache.dolphinscheduler.common.enums.ShowType;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.common.utils.StringUtils;
import org.apache.dolphinscheduler.plugin.model.AlertData;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Enterprise WeChat utils
*/
public class EnterpriseWeChatUtils {
public static final Logger logger = LoggerFactory.getLogger(EnterpriseWeChatUtils.class);
public static final String ENTERPRISE_WE_CHAT_AGENT_ID = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_AGENT_ID);
public static final String ENTERPRISE_WE_CHAT_USERS = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_USERS);
private static final String ENTERPRISE_WE_CHAT_CORP_ID = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_CORP_ID);
private static final String ENTERPRISE_WE_CHAT_SECRET = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_SECRET);
private static final String ENTERPRISE_WE_CHAT_TOKEN_URL = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_TOKEN_URL);
private static final String ENTERPRISE_WE_CHAT_TOKEN_URL_REPLACE = ENTERPRISE_WE_CHAT_TOKEN_URL == null ? null : ENTERPRISE_WE_CHAT_TOKEN_URL
.replaceAll("\\{corpId}", ENTERPRISE_WE_CHAT_CORP_ID)
.replaceAll("\\{secret}", ENTERPRISE_WE_CHAT_SECRET);
private static final String ENTERPRISE_WE_CHAT_PUSH_URL = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_PUSH_URL);
private static final String ENTERPRISE_WE_CHAT_TEAM_SEND_MSG = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_TEAM_SEND_MSG);
private static final String ENTERPRISE_WE_CHAT_USER_SEND_MSG = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_USER_SEND_MSG);
private static final String AGENT_ID_REG_EXP = "\\{agentId}";
private static final String MSG_REG_EXP = "\\{msg}";
private static final String USER_REG_EXP = "\\{toUser}";
private EnterpriseWeChatUtils() {
throw new IllegalStateException(EnterpriseWeChatUtils.class.getName());
}
/**
* get Enterprise WeChat is enable
*
* @return isEnable
*/
public static boolean isEnable() {
Boolean isEnable = null;
try {
isEnable = PropertyUtils.getBoolean(Constants.ENTERPRISE_WECHAT_ENABLE);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (isEnable == null) {
return false;
}
return isEnable;
}
/**
* get Enterprise WeChat token info
*
* @return token string info
* @throws IOException the IOException
*/
public static String getToken() throws IOException {
String resp;
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
HttpGet httpGet = new HttpGet(ENTERPRISE_WE_CHAT_TOKEN_URL_REPLACE);
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, Constants.UTF_8);
EntityUtils.consume(entity);
} finally {
response.close();
}
Map<String, String> map = JSONUtils.toMap(resp);
return map == null ? null : map.get("access_token");
} finally {
httpClient.close();
}
}
/**
* make team single Enterprise WeChat message
*
* @param toParty the toParty
* @param agentId the agentId
* @param msg the msg
* @return Enterprise WeChat send message
*/
public static String makeTeamSendMsg(String toParty, String agentId, String msg) {
return ENTERPRISE_WE_CHAT_TEAM_SEND_MSG.replaceAll("\\{toParty}", toParty)
.replaceAll(AGENT_ID_REG_EXP, agentId)
.replaceAll(MSG_REG_EXP, msg);
}
/**
* make team multi Enterprise WeChat message
*
* @param toParty the toParty
* @param agentId the agentId
* @param msg the msg
* @return Enterprise WeChat send message
*/
public static String makeTeamSendMsg(Collection<String> toParty, String agentId, String msg) {
String listParty = FuncUtils.mkString(toParty, "|");
return ENTERPRISE_WE_CHAT_TEAM_SEND_MSG.replaceAll("\\{toParty}", listParty)
.replaceAll(AGENT_ID_REG_EXP, agentId)
.replaceAll(MSG_REG_EXP, msg);
}
/**
* make team single user message
*
* @param toUser the toUser
* @param agentId the agentId
* @param msg the msg
* @return Enterprise WeChat send message
*/
public static String makeUserSendMsg(String toUser, String agentId, String msg) {
return ENTERPRISE_WE_CHAT_USER_SEND_MSG.replaceAll(USER_REG_EXP, toUser)
.replaceAll(AGENT_ID_REG_EXP, agentId)
.replaceAll(MSG_REG_EXP, msg);
}
/**
* make team multi user message
*
* @param toUser the toUser
* @param agentId the agentId
* @param msg the msg
* @return Enterprise WeChat send message
*/
public static String makeUserSendMsg(Collection<String> toUser, String agentId, String msg) {
String listUser = FuncUtils.mkString(toUser, "|");
return ENTERPRISE_WE_CHAT_USER_SEND_MSG.replaceAll(USER_REG_EXP, listUser)
.replaceAll(AGENT_ID_REG_EXP, agentId)
.replaceAll(MSG_REG_EXP, msg);
}
/**
* send Enterprise WeChat
*
* @param charset the charset
* @param data the data
* @param token the token
* @return Enterprise WeChat resp, demo: {"errcode":0,"errmsg":"ok","invaliduser":""}
* @throws IOException the IOException
*/
public static String sendEnterpriseWeChat(String charset, String data, String token) throws IOException {
String enterpriseWeChatPushUrlReplace = ENTERPRISE_WE_CHAT_PUSH_URL.replaceAll("\\{token}", token);
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
HttpPost httpPost = new HttpPost(enterpriseWeChatPushUrlReplace);
httpPost.setEntity(new StringEntity(data, charset));
CloseableHttpResponse response = httpClient.execute(httpPost);
String resp;
try {
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, charset);
EntityUtils.consume(entity);
} finally {
response.close();
}
logger.info("Enterprise WeChat send [{}], param:{}, resp:{}",
ENTERPRISE_WE_CHAT_PUSH_URL, data, resp);
return resp;
} finally {
httpClient.close();
}
}
/**
* convert table to markdown style
*
* @param title the title
* @param content the content
* @return markdown table content
*/
public static String markdownTable(String title, String content) {
List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
StringBuilder contents = new StringBuilder(200);
if (null != mapItemsList) {
for (LinkedHashMap mapItems : mapItemsList) {
Set<Map.Entry<String, Object>> entries = mapItems.entrySet();
Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
StringBuilder t = new StringBuilder(String.format("`%s`%s", title, Constants.MARKDOWN_ENTER));
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
t.append(Constants.MARKDOWN_QUOTE);
t.append(entry.getKey()).append(":").append(entry.getValue());
t.append(Constants.MARKDOWN_ENTER);
}
contents.append(t);
}
}
return contents.toString();
}
/**
* convert text to markdown style
*
* @param title the title
* @param content the content
* @return markdown text
*/
public static String markdownText(String title, String content) {
if (StringUtils.isNotEmpty(content)) {
List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
if (null != mapItemsList) {
StringBuilder contents = new StringBuilder(100);
contents.append(String.format("`%s`%n", title));
for (LinkedHashMap mapItems : mapItemsList) {
Set<Map.Entry<String, Object>> entries = mapItems.entrySet();
Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
contents.append(Constants.MARKDOWN_QUOTE);
contents.append(entry.getKey()).append(":").append(entry.getValue());
contents.append(Constants.MARKDOWN_ENTER);
}
}
return contents.toString();
}
}
return null;
}
/**
* Determine the mardown style based on the show type of the alert
*
* @return the markdown alert table/text
*/
public static String markdownByAlert(AlertData alert) {
String result = "";
if (alert.getShowType().equals(ShowType.TABLE.getDescp())) {
result = markdownTable(alert.getTitle(), alert.getContent());
} else if (alert.getShowType().equals(ShowType.TEXT.getDescp())) {
result = markdownText(alert.getTitle(), alert.getContent());
}
return result;
}
}

375
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/MailUtils.java

@ -1,375 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.utils;
import org.apache.dolphinscheduler.alert.template.AlertTemplate;
import org.apache.dolphinscheduler.alert.template.AlertTemplateFactory;
import org.apache.dolphinscheduler.common.enums.ShowType;
import org.apache.dolphinscheduler.common.utils.CollectionUtils;
import org.apache.dolphinscheduler.common.utils.StringUtils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* mail utils
*/
public class MailUtils {
public static final Logger logger = LoggerFactory.getLogger(MailUtils.class);
public static final String MAIL_PROTOCOL = PropertyUtils.getString(Constants.MAIL_PROTOCOL);
public static final String MAIL_SERVER_HOST = PropertyUtils.getString(Constants.MAIL_SERVER_HOST);
public static final Integer MAIL_SERVER_PORT = PropertyUtils.getInt(Constants.MAIL_SERVER_PORT);
public static final String MAIL_SENDER = PropertyUtils.getString(Constants.MAIL_SENDER);
public static final String MAIL_USER = PropertyUtils.getString(Constants.MAIL_USER);
public static final String MAIL_PASSWD = PropertyUtils.getString(Constants.MAIL_PASSWD);
public static final Boolean MAIL_USE_START_TLS = PropertyUtils.getBoolean(Constants.MAIL_SMTP_STARTTLS_ENABLE);
public static final Boolean MAIL_USE_SSL = PropertyUtils.getBoolean(Constants.MAIL_SMTP_SSL_ENABLE);
public static final String XLS_FILE_PATH = PropertyUtils.getString(Constants.XLS_FILE_PATH, "/tmp/xls");
public static final String STARTTLS_ENABLE = PropertyUtils.getString(Constants.MAIL_SMTP_STARTTLS_ENABLE);
public static final Boolean SSL_ENABLE = PropertyUtils.getBoolean(Constants.MAIL_SMTP_SSL_ENABLE);
public static final String SSL_TRUST = PropertyUtils.getString(Constants.MAIL_SMTP_SSL_TRUST);
public static final AlertTemplate alertTemplate = AlertTemplateFactory.getMessageTemplate();
//Solve the problem of messy Chinese name in excel attachment
static {
System.setProperty("mail.mime.splitlongparameters", "false");
}
private MailUtils() {
throw new IllegalStateException(MailUtils.class.getName());
}
/**
* send mail to receivers
*
* @param receivers the receiver list
* @param title the title
* @param content the content
* @param showType the show type
* @return the result map
*/
public static Map<String, Object> sendMails(Collection<String> receivers, String title, String content, String showType) {
return sendMails(receivers, null, title, content, showType);
}
/**
* send mail
*
* @param receivers the receiver list
* @param receiversCc cc list
* @param title the title
* @param content the content
* @param showType the show type
* @return the send result
*/
public static Map<String, Object> sendMails(Collection<String> receivers, Collection<String> receiversCc, String title, String content, String showType) {
Map<String, Object> retMap = new HashMap<>();
retMap.put(Constants.STATUS, false);
// if there is no receivers && no receiversCc, no need to process
if (CollectionUtils.isEmpty(receivers) && CollectionUtils.isEmpty(receiversCc)) {
return retMap;
}
receivers.removeIf(StringUtils::isEmpty);
if (showType.equals(ShowType.TABLE.getDescp()) || showType.equals(ShowType.TEXT.getDescp())) {
// send email
HtmlEmail email = new HtmlEmail();
try {
Session session = getSession();
email.setMailSession(session);
email.setFrom(MAIL_SENDER);
email.setCharset(Constants.UTF_8);
if (CollectionUtils.isNotEmpty(receivers)) {
// receivers mail
for (String receiver : receivers) {
email.addTo(receiver);
}
}
if (CollectionUtils.isNotEmpty(receiversCc)) {
//cc
for (String receiverCc : receiversCc) {
email.addCc(receiverCc);
}
}
// sender mail
return getStringObjectMap(title, content, showType, retMap, email);
} catch (Exception e) {
handleException(receivers, retMap, e);
}
} else if (showType.equals(ShowType.ATTACHMENT.getDescp()) || showType.equals(ShowType.TABLEATTACHMENT.getDescp())) {
try {
String partContent = (showType.equals(ShowType.ATTACHMENT.getDescp()) ? "Please see the attachment " + title + Constants.EXCEL_SUFFIX_XLS : htmlTable(content, false));
attachment(receivers, receiversCc, title, content, partContent);
retMap.put(Constants.STATUS, true);
return retMap;
} catch (Exception e) {
handleException(receivers, retMap, e);
return retMap;
}
}
return retMap;
}
/**
* html table content
*
* @param content the content
* @param showAll if show the whole content
* @return the html table form
*/
private static String htmlTable(String content, boolean showAll) {
return alertTemplate.getMessageFromTemplate(content, ShowType.TABLE, showAll);
}
/**
* html table content
*
* @param content the content
* @return the html table form
*/
private static String htmlTable(String content) {
return htmlTable(content, true);
}
/**
* html text content
*
* @param content the content
* @return text in html form
*/
private static String htmlText(String content) {
return alertTemplate.getMessageFromTemplate(content, ShowType.TEXT);
}
/**
* send mail as Excel attachment
*
* @param receivers the receiver list
* @param title the title
*/
private static void attachment(Collection<String> receivers, Collection<String> receiversCc, String title, String content, String partContent) throws Exception {
MimeMessage msg = getMimeMessage(receivers);
attachContent(receiversCc, title, content, partContent, msg);
}
/**
* get MimeMessage
*
* @param receivers receivers
* @return the MimeMessage
*/
private static MimeMessage getMimeMessage(Collection<String> receivers) throws MessagingException {
// 1. The first step in creating mail: creating session
Session session = getSession();
// Setting debug mode, can be turned off
session.setDebug(false);
// 2. creating mail: Creating a MimeMessage
MimeMessage msg = new MimeMessage(session);
// 3. set sender
msg.setFrom(new InternetAddress(MAIL_SENDER));
// 4. set receivers
for (String receiver : receivers) {
msg.addRecipients(Message.RecipientType.TO, InternetAddress.parse(receiver));
}
return msg;
}
/**
* get session
*
* @return the new Session
*/
private static Session getSession() {
Properties props = new Properties();
props.setProperty(Constants.MAIL_HOST, MAIL_SERVER_HOST);
props.setProperty(Constants.MAIL_PORT, String.valueOf(MAIL_SERVER_PORT));
props.setProperty(Constants.MAIL_SMTP_AUTH, Constants.STRING_TRUE);
props.setProperty(Constants.MAIL_TRANSPORT_PROTOCOL, MAIL_PROTOCOL);
props.setProperty(Constants.MAIL_SMTP_STARTTLS_ENABLE, STARTTLS_ENABLE);
if (Boolean.TRUE.equals(SSL_ENABLE)) {
props.setProperty(Constants.MAIL_SMTP_SSL_ENABLE, "true");
props.setProperty(Constants.MAIL_SMTP_SSL_TRUST, SSL_TRUST);
}
Authenticator auth = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
// mail username and password
return new PasswordAuthentication(MAIL_USER, MAIL_PASSWD);
}
};
return Session.getInstance(props, auth);
}
/**
* attach content
*
* @param receiversCc the cc list
* @param title the title
* @param content the content
* @param partContent the partContent
* @param msg the message
*/
private static void attachContent(Collection<String> receiversCc, String title, String content, String partContent, MimeMessage msg) throws MessagingException, IOException {
/*
* set receiverCc
*/
if (CollectionUtils.isNotEmpty(receiversCc)) {
for (String receiverCc : receiversCc) {
msg.addRecipients(Message.RecipientType.CC, InternetAddress.parse(receiverCc));
}
}
// set subject
msg.setSubject(title);
MimeMultipart partList = new MimeMultipart();
// set signature
MimeBodyPart part1 = new MimeBodyPart();
part1.setContent(partContent, Constants.TEXT_HTML_CHARSET_UTF_8);
// set attach file
MimeBodyPart part2 = new MimeBodyPart();
File file = new File(XLS_FILE_PATH + Constants.SINGLE_SLASH + title + Constants.EXCEL_SUFFIX_XLS);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
// make excel file
ExcelUtils.genExcelFile(content, title, XLS_FILE_PATH);
part2.attachFile(file);
part2.setFileName(MimeUtility.encodeText(title + Constants.EXCEL_SUFFIX_XLS, Constants.UTF_8, "B"));
// add components to collection
partList.addBodyPart(part1);
partList.addBodyPart(part2);
msg.setContent(partList);
// 5. send Transport
Transport.send(msg);
// 6. delete saved file
deleteFile(file);
}
/**
* the string object map
*
* @param title the title
* @param content the content
* @param showType the showType
* @param retMap the result map
* @param email the email
* @return the result map
*/
private static Map<String, Object> getStringObjectMap(String title, String content, String showType, Map<String, Object> retMap, HtmlEmail email) throws EmailException {
/*
* the subject of the message to be sent
*/
email.setSubject(title);
/*
* to send information, you can use HTML tags in mail content because of the use of HtmlEmail
*/
if (showType.equals(ShowType.TABLE.getDescp())) {
email.setMsg(htmlTable(content));
} else if (showType.equals(ShowType.TEXT.getDescp())) {
email.setMsg(htmlText(content));
}
// send
email.send();
retMap.put(Constants.STATUS, true);
return retMap;
}
/**
* file delete
*
* @param file the file to delete
*/
public static void deleteFile(File file) {
if (file.exists()) {
if (file.delete()) {
logger.info("delete success: {}", file.getAbsolutePath() + file.getName());
} else {
logger.info("delete fail: {}", file.getAbsolutePath() + file.getName());
}
} else {
logger.info("file not exists: {}", file.getAbsolutePath() + file.getName());
}
}
/**
* handle exception
*
* @param receivers the receiver list
* @param retMap the result map
* @param e the exception
*/
private static void handleException(Collection<String> receivers, Map<String, Object> retMap, Exception e) {
logger.error("Send email to {} failed", receivers, e);
retMap.put(Constants.MESSAGE, "Send email to {" + String.join(",", receivers) + "} failed," + e.toString());
}
}

2
dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/PropertyUtils.java

@ -88,7 +88,7 @@ public class PropertyUtils {
/**
* get property value
*
* @param key property name
* @param key property name
* @param defaultVal default value
* @return property value
*/

48
dolphinscheduler-alert/src/main/resources/alert.properties

@ -15,46 +15,18 @@
# limitations under the License.
#
#alert type is EMAIL/SMS
alert.type=EMAIL
#This configuration file configures the configuration parameters related to the AlertServer.
#These parameters are only related to the AlertServer, and it has nothing to do with the specific Alert Plugin.
#eg : max retry num.
#eg : Alert Server Listener port
# mail server configuration
mail.protocol=SMTP
mail.server.host=xxx.xxx.com
mail.server.port=25
mail.sender=xxx@xxx.com
mail.user=xxx@xxx.com
mail.passwd=111111
# TLS
mail.smtp.starttls.enable=true
# SSL
mail.smtp.ssl.enable=false
mail.smtp.ssl.trust=xxx.xxx.com
#alert.plugin.dir config the Alert Plugin dir . AlertServer while find and load the Alert Plugin Jar from this dir when deploy and start AlertServer on the server .
#eg :alert.plugin.dir=/opt/soft/spi/lib/plugin/alert
#xls file path,need create if not exist
#xls.file.path=/tmp/xls
#maven.local.repository=/Users/gaojun/Documents/jianguoyun/localRepository
# Enterprise WeChat configuration
enterprise.wechat.enable=false
#alert.plugin.binding config the Alert Plugin need be load when development and run in IDE
#alert.plugin.binding=\
# ./dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml
#enterprise.wechat.corp.id=xxxxxxx
#enterprise.wechat.secret=xxxxxxx
#enterprise.wechat.agent.id=xxxxxxx
#enterprise.wechat.users=xxxxxxx
#enterprise.wechat.token.url=https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={secret}
#enterprise.wechat.push.url=https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}
#enterprise.wechat.team.send.msg={\"toparty\":\"{toParty}\",\"agentid\":\"{agentId}\",\"msgtype\":\"text\",\"text\":{\"content\":\"{msg}\"},\"safe\":\"0\"}
#enterprise.wechat.user.send.msg={\"touser\":\"{toUser}\",\"agentid\":\"{agentId}\",\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"{msg}\"}}
plugin.dir=/Users/xx/your/path/to/plugin/dir
#ding talk configuration
dingtalk.isEnable=flase
dingtalk.webhook=https://oapi.dingtalk.com/robot/send?access_token=xxxxx
dingtalk.keyword=
dingtalk.proxy=
dingtalk.port=80
dingtalk.user=
dingtalk.password=
dingtalk.isEnableProxy=false

91
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/AlertServerTest.java

@ -0,0 +1,91 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert;
import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager;
import org.apache.dolphinscheduler.alert.plugin.DolphinPluginLoader;
import org.apache.dolphinscheduler.alert.plugin.DolphinPluginManagerConfig;
import org.apache.dolphinscheduler.alert.runner.AlertSender;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.DaoFactory;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.remote.NettyRemotingServer;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({AlertServer.class,DaoFactory.class})
public class AlertServerTest {
@Before
public void before() {
}
@Test
public void testMain() throws Exception {
AlertDao alertDao = PowerMockito.mock(AlertDao.class);
PowerMockito.mockStatic(DaoFactory.class);
PowerMockito.when(DaoFactory.getDaoInstance(AlertDao.class)).thenReturn(alertDao);
PluginDao pluginDao = PowerMockito.mock(PluginDao.class);
PowerMockito.when(DaoFactory.getDaoInstance(PluginDao.class)).thenReturn(pluginDao);
AlertChannel alertChannelMock = PowerMockito.mock(AlertChannel.class);
AlertPluginManager alertPluginManager = PowerMockito.mock(AlertPluginManager.class);
PowerMockito.whenNew(AlertPluginManager.class).withNoArguments().thenReturn(alertPluginManager);
ConcurrentHashMap alertChannelMap = new ConcurrentHashMap<>();
alertChannelMap.put("pluginName",alertChannelMock);
PowerMockito.when(alertPluginManager.getAlertChannelMap()).thenReturn(alertChannelMap);
DolphinPluginManagerConfig alertPluginManagerConfig = PowerMockito.mock(DolphinPluginManagerConfig.class);
PowerMockito.whenNew(DolphinPluginManagerConfig.class).withNoArguments().thenReturn(alertPluginManagerConfig);
NettyRemotingServer nettyRemotingServer = PowerMockito.mock(NettyRemotingServer.class);
PowerMockito.whenNew(NettyRemotingServer.class).withAnyArguments().thenReturn(nettyRemotingServer);
AlertSender alertSender = PowerMockito.mock(AlertSender.class);
PowerMockito.whenNew(AlertSender.class).withAnyArguments().thenReturn(alertSender);
DolphinPluginLoader dolphinPluginLoader = PowerMockito.mock(DolphinPluginLoader.class);
PowerMockito.whenNew(DolphinPluginLoader.class).withAnyArguments().thenReturn(dolphinPluginLoader);
AlertServer alertServer = AlertServer.getInstance();
Assert.assertNotNull(alertServer);
new Thread(() -> {
alertServer.start(); })
.start();
Thread.sleep(5 * Constants.ALERT_SCAN_INTERVAL);
alertServer.stop();
}
}

65
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/plugin/AlertPluginManagerTest.java

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import org.apache.dolphinscheduler.alert.AlertServer;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.alert.utils.PropertyUtils;
import org.apache.dolphinscheduler.spi.utils.StringUtils;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
/**
* AlertPluginManager Tester.
*/
@Ignore
public class AlertPluginManagerTest {
private static final Logger logger = LoggerFactory.getLogger(AlertPluginManagerTest.class);
@Test
public void testLoadPlugins() throws Exception {
logger.info("begin test AlertPluginManagerTest");
AlertPluginManager alertPluginManager = new AlertPluginManager();
DolphinPluginManagerConfig alertPluginManagerConfig = new DolphinPluginManagerConfig();
String path = DolphinPluginLoader.class.getClassLoader().getResource("").getPath();
alertPluginManagerConfig.setPlugins(path + "../../../dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml");
if (StringUtils.isNotBlank(PropertyUtils.getString(AlertServer.ALERT_PLUGIN_DIR))) {
alertPluginManagerConfig.setInstalledPluginsDir(org.apache.dolphinscheduler.alert.utils.PropertyUtils.getString(AlertServer.ALERT_PLUGIN_DIR, Constants.ALERT_PLUGIN_PATH).trim());
}
if (StringUtils.isNotBlank(PropertyUtils.getString(AlertServer.MAVEN_LOCAL_REPOSITORY))) {
alertPluginManagerConfig.setMavenLocalRepository(PropertyUtils.getString(AlertServer.MAVEN_LOCAL_REPOSITORY).trim());
}
DolphinPluginLoader alertPluginLoader = new DolphinPluginLoader(alertPluginManagerConfig, ImmutableList.of(alertPluginManager));
try {
alertPluginLoader.loadPlugins();
} catch (Exception e) {
throw new RuntimeException("load Alert Plugin Failed !", e);
}
Assert.assertNotNull(alertPluginManager.getAlertChannelFactoryMap().get("email alert"));
}
}

60
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginLoaderTest.java

@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
/**
* DolphinPluginLoader Tester.
*/
@Ignore
public class DolphinPluginLoaderTest {
@Before
public void before() throws Exception {
}
@After
public void after() throws Exception {
}
/**
* Method: loadPlugins()
*/
@Test
public void testLoadPlugins() throws Exception {
AlertPluginManager alertPluginManager = new AlertPluginManager();
DolphinPluginManagerConfig alertPluginManagerConfig = new DolphinPluginManagerConfig();
String path = DolphinPluginLoader.class.getClassLoader().getResource("").getPath();
alertPluginManagerConfig.setPlugins(path + "../../../dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml");
DolphinPluginLoader alertPluginLoader = new DolphinPluginLoader(alertPluginManagerConfig, ImmutableList.of(alertPluginManager));
try {
alertPluginLoader.loadPlugins();
} catch (Exception e) {
throw new RuntimeException("load Alert Plugin Failed !", e);
}
Assert.assertNotNull(alertPluginManager.getAlertChannelFactoryMap().get("email alert"));
}
}

251
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/plugin/EmailAlertPluginTest.java

@ -14,67 +14,226 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.plugin;
import org.apache.dolphinscheduler.alert.AlertServer;
import org.apache.dolphinscheduler.alert.runner.AlertSender;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.common.enums.ShowType;
import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
import org.apache.dolphinscheduler.plugin.model.AlertData;
import org.apache.dolphinscheduler.plugin.model.AlertInfo;
import org.apache.dolphinscheduler.plugin.model.PluginName;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.dolphinscheduler.alert.utils.PropertyUtils;
import org.apache.dolphinscheduler.common.enums.AlertStatus;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.DaoFactory;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.dao.entity.Alert;
import org.apache.dolphinscheduler.dao.entity.AlertGroup;
import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance;
import org.apache.dolphinscheduler.dao.entity.PluginDefine;
import org.apache.dolphinscheduler.spi.alert.AlertConstants;
import org.apache.dolphinscheduler.spi.alert.ShowType;
import org.apache.dolphinscheduler.spi.params.InputParam;
import org.apache.dolphinscheduler.spi.params.PasswordParam;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import org.apache.dolphinscheduler.spi.params.RadioParam;
import org.apache.dolphinscheduler.spi.params.base.DataType;
import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import org.apache.dolphinscheduler.spi.params.base.Validate;
import org.apache.dolphinscheduler.spi.utils.StringUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
/**
* test load and use alert plugin
*/
public class EmailAlertPluginTest {
private static final Logger logger = LoggerFactory.getLogger(EmailAlertPluginTest.class);
AlertDao alertDao = DaoFactory.getDaoInstance(AlertDao.class);
PluginDao pluginDao = DaoFactory.getDaoInstance(PluginDao.class);
private AlertPlugin plugin;
@Test
@Ignore
public void testRunSend() throws Exception {
@Before
public void before() {
plugin = new EmailAlertPlugin();
}
//create alert group
AlertGroup alertGroup = new AlertGroup();
alertGroup.setDescription("test alert group 1");
alertGroup.setGroupName("testalertg1");
alertDao.getAlertGroupMapper().insert(alertGroup);
@Test
public void getId() {
String id = plugin.getId();
assertEquals(Constants.PLUGIN_DEFAULT_EMAIL_ID, id);
}
//add alert
Alert alert1 = new Alert();
alert1.setTitle("test alert");
LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
map1.put("mysql service name", "mysql200");
map1.put("mysql address", "192.168.xx.xx");
map1.put("port", "3306");
map1.put(AlertConstants.SHOW_TYPE, ShowType.TEXT.getDescp());
map1.put("no index of number", "80");
map1.put("database client connections", "190");
LinkedHashMap<String, Object> map2 = new LinkedHashMap<>();
map2.put("mysql service name", "mysql210");
map2.put("mysql address", "192.168.xx.xx");
map2.put("port", "3306");
map2.put("no index of number", "10");
map1.put(AlertConstants.SHOW_TYPE, ShowType.TABLE.getDescp());
map2.put("database client connections", "90");
List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
maps.add(0, map1);
maps.add(1, map2);
String mapjson = JSONUtils.toJsonString(maps);
alert1.setContent(mapjson);
alert1.setLog("log log");
alert1.setAlertGroupId(alertGroup.getId());
alertDao.addAlert(alert1);
List<Alert> alertList = new ArrayList<>();
alertList.add(alert1);
//load email alert plugin
AlertPluginManager alertPluginManager = new AlertPluginManager();
DolphinPluginManagerConfig alertPluginManagerConfig = new DolphinPluginManagerConfig();
String path = DolphinPluginLoader.class.getClassLoader().getResource("").getPath();
alertPluginManagerConfig.setPlugins(path + "../../../dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml");
if (StringUtils.isNotBlank(PropertyUtils.getString(AlertServer.ALERT_PLUGIN_DIR))) {
alertPluginManagerConfig.setInstalledPluginsDir(PropertyUtils.getString(AlertServer.ALERT_PLUGIN_DIR, Constants.ALERT_PLUGIN_PATH).trim());
}
if (StringUtils.isNotBlank(PropertyUtils.getString(AlertServer.MAVEN_LOCAL_REPOSITORY))) {
alertPluginManagerConfig.setMavenLocalRepository(PropertyUtils.getString(AlertServer.MAVEN_LOCAL_REPOSITORY).trim());
}
DolphinPluginLoader alertPluginLoader = new DolphinPluginLoader(alertPluginManagerConfig, ImmutableList.of(alertPluginManager));
try {
alertPluginLoader.loadPlugins();
} catch (Exception e) {
throw new RuntimeException("load Alert Plugin Failed !", e);
}
//create email alert plugin instance
AlertPluginInstance alertPluginInstance = new AlertPluginInstance();
alertPluginInstance.setCreateTime(new Date());
alertPluginInstance.setInstanceName("test email alert");
List<PluginDefine> pluginDefineList = pluginDao.getPluginDefineMapper().queryByNameAndType("email alert", "alert");
if (pluginDefineList == null || pluginDefineList.size() == 0) {
throw new RuntimeException("no alert plugin be load");
}
PluginDefine pluginDefine = pluginDefineList.get(0);
alertPluginInstance.setPluginDefineId(pluginDefine.getId());
alertPluginInstance.setPluginInstanceParams(getEmailAlertParams());
alertDao.getAlertPluginInstanceMapper().insert(alertPluginInstance);
AlertSender alertSender = new AlertSender(alertList, alertDao, alertPluginManager, pluginDao);
alertSender.run();
Alert alertResult = alertDao.getAlertMapper().selectById(alert1.getId());
Assert.assertNotNull(alertResult);
Assert.assertEquals(alertResult.getAlertStatus(), AlertStatus.EXECUTION_FAILURE);
alertDao.getAlertGroupMapper().deleteById(alertGroup.getId());
alertDao.getAlertPluginInstanceMapper().deleteById(alertPluginInstance.getId());
alertDao.getAlertMapper().deleteById(alert1.getId());
@Test
public void getName() {
PluginName pluginName = plugin.getName();
assertEquals(Constants.PLUGIN_DEFAULT_EMAIL_CH, pluginName.getChinese());
assertEquals(Constants.PLUGIN_DEFAULT_EMAIL_EN, pluginName.getEnglish());
}
@Test
public void process() {
AlertInfo alertInfo = new AlertInfo();
AlertData alertData = new AlertData();
alertData.setId(1)
.setAlertGroupId(1)
.setContent("[\"alarm time:2018-02-05\", \"service name:MYSQL_ALTER\", \"alarm name:MYSQL_ALTER_DUMP\", " +
"\"get the alarm exception.!,interface error,exception information:timed out\", \"request address:http://blog.csdn.net/dreamInTheWorld/article/details/78539286\"]")
.setLog("test log")
.setReceivers("xx@xx.com")
.setReceiversCc("xx@xx.com")
.setShowType(ShowType.TEXT.getDescp())
.setTitle("test title");
alertInfo.setAlertData(alertData);
List<String> list = new ArrayList<String>(){{ add("xx@xx.com"); }};
alertInfo.addProp("receivers", list);
// Map<String, Object> ret = plugin.process(alertInfo);
// assertFalse(Boolean.parseBoolean(String.valueOf(ret.get(Constants.STATUS))));
public String getEmailAlertParams() {
List<PluginParams> paramsList = new ArrayList<>();
InputParam receivesParam = InputParam.newBuilder("receivers", "receivers")
.setValue("540957506@qq.com")
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
InputParam mailSmtpHost = InputParam.newBuilder("mailServerHost", "mail.smtp.host")
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue("smtp.exmail.qq.com")
.build();
InputParam mailSmtpPort = InputParam.newBuilder("mailServerPort", "mail.smtp.port")
.addValidate(Validate.newBuilder()
.setRequired(true)
.setType(DataType.NUMBER.getDataType())
.build())
.setValue(25)
.build();
InputParam mailSender = InputParam.newBuilder("mailSender", "mail.sender")
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue("easyscheduler@analysys.com.cn")
.build();
RadioParam enableSmtpAuth = RadioParam.newBuilder("enableSmtpAuth", "mail.smtp.auth")
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue(true)
.build();
InputParam mailUser = InputParam.newBuilder("mailUser", "mail.user")
.setPlaceholder("if enable use authentication, you need input user")
.setValue("easyscheduler@analysys.com.cn")
.build();
PasswordParam mailPassword = PasswordParam.newBuilder("mailPasswd", "mail.passwd")
.setPlaceholder("if enable use authentication, you need input password")
.setValue("xxxxxxx")
.build();
RadioParam enableTls = RadioParam.newBuilder("starttlsEnable", "mail.smtp.starttls.enable")
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue(true)
.build();
RadioParam enableSsl = RadioParam.newBuilder("sslEnable", "mail.smtp.ssl.enable")
.addParamsOptions(new ParamsOptions("YES", true, false))
.addParamsOptions(new ParamsOptions("NO", false, false))
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue(false)
.build();
InputParam sslTrust = InputParam.newBuilder("mailSmtpSslTrust", "mail.smtp.ssl.trust")
.addValidate(Validate.newBuilder().setRequired(true).build())
.setValue("smtp.exmail.qq.com")
.build();
List<ParamsOptions> emailShowTypeList = new ArrayList<>();
emailShowTypeList.add(new ParamsOptions(ShowType.TABLE.getDescp(), ShowType.TABLE.getDescp(), false));
emailShowTypeList.add(new ParamsOptions(ShowType.TEXT.getDescp(), ShowType.TEXT.getDescp(), false));
emailShowTypeList.add(new ParamsOptions(ShowType.ATTACHMENT.getDescp(), ShowType.ATTACHMENT.getDescp(), false));
emailShowTypeList.add(new ParamsOptions(ShowType.TABLEATTACHMENT.getDescp(), ShowType.TABLEATTACHMENT.getDescp(), false));
RadioParam showType = RadioParam.newBuilder(AlertConstants.SHOW_TYPE, "showType")
.setParamsOptionsList(emailShowTypeList)
.setValue(ShowType.TABLE.getDescp())
.addValidate(Validate.newBuilder().setRequired(true).build())
.build();
paramsList.add(receivesParam);
paramsList.add(mailSmtpHost);
paramsList.add(mailSmtpPort);
paramsList.add(mailSender);
paramsList.add(enableSmtpAuth);
paramsList.add(mailUser);
paramsList.add(mailPassword);
paramsList.add(enableTls);
paramsList.add(enableSsl);
paramsList.add(sslTrust);
paramsList.add(showType);
return PluginParamsTransfer.transferParamsToJson(paramsList);
}
}

61
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/processor/AlertRequestProcessorTest.java

@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.processor;
import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.remote.command.Command;
import org.apache.dolphinscheduler.remote.command.CommandType;
import org.apache.dolphinscheduler.remote.command.alert.AlertSendRequestCommand;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.powermock.api.mockito.PowerMockito;
import io.netty.channel.Channel;
/**
* alert request processor test
*/
public class AlertRequestProcessorTest {
private AlertDao alertDao;
private PluginDao pluginDao;
private AlertPluginManager alertPluginManager;
private AlertRequestProcessor alertRequestProcessor;
@Before
public void before() {
alertDao = PowerMockito.mock(AlertDao.class);
pluginDao = PowerMockito.mock(PluginDao.class);
alertPluginManager = PowerMockito.mock(AlertPluginManager.class);
alertRequestProcessor = new AlertRequestProcessor(alertDao,alertPluginManager,pluginDao);
}
@Test
public void testProcess() {
Channel channel = PowerMockito.mock(Channel.class);
AlertSendRequestCommand alertSendRequestCommand = new AlertSendRequestCommand(1,"title","content");
Command reqCommand = alertSendRequestCommand.convert2Command();
Assert.assertEquals(CommandType.ALERT_SEND_REQUEST,reqCommand.getType());
alertRequestProcessor.process(channel,reqCommand);
}
}

181
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/runner/AlertSenderTest.java

@ -0,0 +1,181 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.runner;
import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.dao.entity.Alert;
import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance;
import org.apache.dolphinscheduler.dao.entity.PluginDefine;
import org.apache.dolphinscheduler.remote.command.alert.AlertSendResponseCommand;
import org.apache.dolphinscheduler.spi.alert.AlertChannel;
import org.apache.dolphinscheduler.spi.alert.AlertResult;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* alert sender test
*/
public class AlertSenderTest {
private static final Logger logger = LoggerFactory.getLogger(AlertSenderTest.class);
private AlertDao alertDao;
private PluginDao pluginDao;
private AlertPluginManager alertPluginManager;
private AlertSender alertSender;
@Before
public void before() {
alertDao = PowerMockito.mock(AlertDao.class);
pluginDao = PowerMockito.mock(PluginDao.class);
alertPluginManager = PowerMockito.mock(AlertPluginManager.class);
}
@Test
public void testSyncHandler() {
int alertGroupId = 1;
String title = "alert mail test title";
String content = "alert mail test content";
alertSender = new AlertSender(alertDao,alertPluginManager,pluginDao);
//1.alert instance does not exist
PowerMockito.when(alertDao.listInstanceByAlertGroupId(alertGroupId)).thenReturn(null);
AlertSendResponseCommand alertSendResponseCommand = alertSender.syncHandler(alertGroupId, title, content);
Assert.assertFalse(alertSendResponseCommand.getResStatus());
alertSendResponseCommand.getResResults().forEach(result ->
logger.info("alert send response result, status:{}, message:{}",result.getStatus(),result.getMessage()));
//2.alert plugin does not exist
int pluginDefineId = 1;
String pluginInstanceParams = "alert-instance-mail-params";
String pluginInstanceName = "alert-instance-mail";
List<AlertPluginInstance> alertInstanceList = new ArrayList<>();
AlertPluginInstance alertPluginInstance = new AlertPluginInstance(
pluginDefineId,pluginInstanceParams,pluginInstanceName);
alertInstanceList.add(alertPluginInstance);
PowerMockito.when(alertDao.listInstanceByAlertGroupId(1)).thenReturn(alertInstanceList);
String pluginName = "alert-plugin-mail";
PluginDefine pluginDefine = new PluginDefine(pluginName,"1",null);
PowerMockito.when(pluginDao.getPluginDefineById(pluginDefineId)).thenReturn(pluginDefine);
alertSendResponseCommand = alertSender.syncHandler(alertGroupId, title, content);
Assert.assertFalse(alertSendResponseCommand.getResStatus());
alertSendResponseCommand.getResResults().forEach(result ->
logger.info("alert send response result, status:{}, message:{}",result.getStatus(),result.getMessage()));
//3.alert result value is null
AlertChannel alertChannelMock = PowerMockito.mock(AlertChannel.class);
PowerMockito.when(alertChannelMock.process(Mockito.any())).thenReturn(null);
Map<String, AlertChannel> alertChannelMap = new ConcurrentHashMap<>();
alertChannelMap.put(pluginName,alertChannelMock);
PowerMockito.when(alertPluginManager.getAlertChannelMap()).thenReturn(alertChannelMap);
alertSendResponseCommand = alertSender.syncHandler(alertGroupId, title, content);
Assert.assertFalse(alertSendResponseCommand.getResStatus());
alertSendResponseCommand.getResResults().forEach(result ->
logger.info("alert send response result, status:{}, message:{}",result.getStatus(),result.getMessage()));
//4.abnormal information inside the alert plug-in code
AlertResult alertResult = new AlertResult();
alertResult.setStatus(String.valueOf(false));
alertResult.setMessage("Abnormal information inside the alert plug-in code");
PowerMockito.when(alertChannelMock.process(Mockito.any())).thenReturn(alertResult);
alertChannelMap = new ConcurrentHashMap<>();
alertChannelMap.put(pluginName,alertChannelMock);
PowerMockito.when(alertPluginManager.getAlertChannelMap()).thenReturn(alertChannelMap);
alertSendResponseCommand = alertSender.syncHandler(alertGroupId, title, content);
Assert.assertFalse(alertSendResponseCommand.getResStatus());
alertSendResponseCommand.getResResults().forEach(result ->
logger.info("alert send response result, status:{}, message:{}",result.getStatus(),result.getMessage()));
//5.alert plugin send success
alertResult = new AlertResult();
alertResult.setStatus(String.valueOf(true));
alertResult.setMessage(String.format("Alert Plugin %s send success",pluginInstanceName));
PowerMockito.when(alertChannelMock.process(Mockito.any())).thenReturn(alertResult);
alertChannelMap = new ConcurrentHashMap<>();
alertChannelMap.put(pluginName,alertChannelMock);
PowerMockito.when(alertPluginManager.getAlertChannelMap()).thenReturn(alertChannelMap);
alertSendResponseCommand = alertSender.syncHandler(alertGroupId, title, content);
Assert.assertTrue(alertSendResponseCommand.getResStatus());
alertSendResponseCommand.getResResults().forEach(result ->
logger.info("alert send response result, status:{}, message:{}",result.getStatus(),result.getMessage()));
}
@Test
public void testRun() {
int alertGroupId = 1;
String title = "alert mail test title";
String content = "alert mail test content";
List<Alert> alertList = new ArrayList<>();
Alert alert = new Alert();
alert.setAlertGroupId(alertGroupId);
alert.setTitle(title);
alert.setContent(content);
alertList.add(alert);
alertSender = new AlertSender(alertList,alertDao,alertPluginManager,pluginDao);
int pluginDefineId = 1;
String pluginInstanceParams = "alert-instance-mail-params";
String pluginInstanceName = "alert-instance-mail";
List<AlertPluginInstance> alertInstanceList = new ArrayList<>();
AlertPluginInstance alertPluginInstance = new AlertPluginInstance(
pluginDefineId,pluginInstanceParams,pluginInstanceName);
alertInstanceList.add(alertPluginInstance);
PowerMockito.when(alertDao.listInstanceByAlertGroupId(alertGroupId)).thenReturn(alertInstanceList);
String pluginName = "alert-plugin-mail";
PluginDefine pluginDefine = new PluginDefine(pluginName,"1",null);
PowerMockito.when(pluginDao.getPluginDefineById(pluginDefineId)).thenReturn(pluginDefine);
AlertResult alertResult = new AlertResult();
alertResult.setStatus(String.valueOf(true));
alertResult.setMessage(String.format("Alert Plugin %s send success",pluginInstanceName));
AlertChannel alertChannelMock = PowerMockito.mock(AlertChannel.class);
PowerMockito.when(alertChannelMock.process(Mockito.any())).thenReturn(alertResult);
ConcurrentHashMap alertChannelMap = new ConcurrentHashMap<>();
alertChannelMap.put(pluginName,alertChannelMock);
PowerMockito.when(alertPluginManager.getAlertChannelMap()).thenReturn(alertChannelMap);
Assert.assertTrue(Boolean.parseBoolean(alertResult.getStatus()));
alertSender.run();
}
}

65
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactoryTest.java

@ -1,65 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.template;
import org.apache.dolphinscheduler.alert.template.impl.DefaultHTMLTemplate;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.alert.utils.PropertyUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
/**
* test class for AlertTemplateFactory
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest(PropertyUtils.class)
public class AlertTemplateFactoryTest {
private static final Logger logger = LoggerFactory.getLogger(AlertTemplateFactoryTest.class);
/**
* GetMessageTemplate method test
*/
@Test
public void testGetMessageTemplate(){
PowerMockito.mockStatic(PropertyUtils.class);
AlertTemplate defaultTemplate = AlertTemplateFactory.getMessageTemplate();
assertTrue(defaultTemplate instanceof DefaultHTMLTemplate);
}
/**
* GetMessageTemplate method throw Exception test
*/
@Test
public void testGetMessageTemplateException(){
AlertTemplate defaultTemplate = AlertTemplateFactory.getMessageTemplate();
assertTrue(defaultTemplate instanceof DefaultHTMLTemplate);
}
}

96
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/template/impl/DefaultHTMLTemplateTest.java

@ -1,96 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.template.impl;
import org.apache.dolphinscheduler.common.utils.*;
import org.apache.dolphinscheduler.alert.utils.Constants;
import org.apache.dolphinscheduler.common.enums.ShowType;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import static org.junit.Assert.*;
/**
* test class for DefaultHTMLTemplate
*/
public class DefaultHTMLTemplateTest{
private static final Logger logger = LoggerFactory.getLogger(DefaultHTMLTemplateTest.class);
/**
* only need test method GetMessageFromTemplate
*/
@Test
public void testGetMessageFromTemplate(){
DefaultHTMLTemplate template = new DefaultHTMLTemplate();
String tableTypeMessage = template.getMessageFromTemplate(list2String(), ShowType.TABLE,true);
assertEquals(tableTypeMessage,generateMockTableTypeResultByHand());
String textTypeMessage = template.getMessageFromTemplate(list2String(), ShowType.TEXT,true);
assertEquals(textTypeMessage,generateMockTextTypeResultByHand());
}
/**
* generate some simulation data
*/
private String list2String(){
LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
map1.put("mysql service name","mysql200");
map1.put("mysql address","192.168.xx.xx");
map1.put("database client connections","190");
map1.put("port","3306");
map1.put("no index of number","80");
LinkedHashMap<String, Object> map2 = new LinkedHashMap<>();
map2.put("mysql service name","mysql210");
map2.put("mysql address","192.168.xx.xx");
map2.put("database client connections","90");
map2.put("port","3306");
map2.put("no index of number","10");
List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
maps.add(0,map1);
maps.add(1,map2);
String mapjson = JSONUtils.toJsonString(maps);
logger.info(mapjson);
return mapjson;
}
private String generateMockTableTypeResultByHand(){
return Constants.HTML_HEADER_PREFIX +
"<thead><tr><th>mysql service name</th><th>mysql address</th><th>database client connections</th><th>port</th><th>no index of number</th></tr></thead>\n" +
"<tr><td>mysql200</td><td>192.168.xx.xx</td><td>190</td><td>3306</td><td>80</td></tr><tr><td>mysql210</td><td>192.168.xx.xx</td><td>90</td><td>3306</td><td>10</td></tr>" + Constants.TABLE_BODY_HTML_TAIL;
}
private String generateMockTextTypeResultByHand(){
return Constants.HTML_HEADER_PREFIX + "<tr><td>{\"mysql service name\":\"mysql200\",\"mysql address\":\"192.168.xx.xx\",\"database client connections\":\"190\",\"port\":\"3306\",\"no index of number\":\"80\"}</td></tr><tr><td>{\"mysql service name\":\"mysql210\",\"mysql address\":\"192.168.xx.xx\",\"database client connections\":\"90\",\"port\":\"3306\",\"no index of number\":\"10\"}</td></tr>" + Constants.TABLE_BODY_HTML_TAIL;
}
}

120
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/DingTalkUtilsTest.java

@ -1,120 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.utils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
@PrepareForTest(PropertyUtils.class)
@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.net.ssl.*")
public class DingTalkUtilsTest {
Logger logger = LoggerFactory.getLogger(DingTalkUtilsTest.class);
private static final String mockUrl = "https://oapi.dingtalk.com/robot/send?access_token=test";
private static final String mockKeyWords = "onway";
private static final String msg = "ding talk test";
@Before
public void init(){
PowerMockito.mockStatic(PropertyUtils.class);
Mockito.when(PropertyUtils.getString(Constants.DINGTALK_WEBHOOK)).thenReturn(mockUrl);
Mockito.when(PropertyUtils.getString(Constants.DINGTALK_KEYWORD)).thenReturn(mockKeyWords);
Mockito.when(PropertyUtils.getBoolean(Constants.DINGTALK_PROXY_ENABLE)).thenReturn(true);
Mockito.when(PropertyUtils.getString(Constants.DINGTALK_PROXY)).thenReturn("proxy.com.cn");
Mockito.when(PropertyUtils.getString(Constants.DINGTALK_USER)).thenReturn("user");
Mockito.when(PropertyUtils.getString(Constants.DINGTALK_PASSWORD)).thenReturn("pswd");
Mockito.when(PropertyUtils.getInt(Constants.DINGTALK_PORT)).thenReturn(80);
}
// @Test
// @Ignore
// public void testSendMsg() {
// try {
// String msgTosend = "msg to send";
// logger.info(PropertyUtils.getString(Constants.DINGTALK_WEBHOOK));
// String rsp = DingTalkUtils.sendDingTalkMsg(msgTosend, Constants.UTF_8);
// logger.info("send msg result:{}",rsp);
// String errmsg = JSONUtils.parseObject(rsp).getString("errmsg");
// Assert.assertEquals("ok", errmsg);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
@Test
public void testCreateDefaultClient() {
CloseableHttpClient client = DingTalkUtils.getDefaultClient();;
try {
Assert.assertNotNull(client);
client.close();
} catch (IOException ex) {
logger.info("close exception",ex.getMessage());
new Throwable();
}
}
@Test
public void testCreateProxyClient() {
CloseableHttpClient client = DingTalkUtils.getProxyClient();
try {
Assert.assertNotNull(client);
client.close();
} catch (IOException ex) {
logger.info("close exception",ex.getMessage());
new Throwable();
}
}
@Test
public void testProxyConfig() {
RequestConfig rc = DingTalkUtils.getProxyConfig();
Assert.assertEquals(80, rc.getProxy().getPort());
Assert.assertEquals("proxy.com.cn", rc.getProxy().getHostName());
}
@Test
public void testDingTalkMsgToJson() {
String jsonString = DingTalkUtils.textToJsonString("this is test");
logger.info(jsonString);
String expect = "{\"text\":{\"content\":\"this is test\"},\"msgtype\":\"text\"}";
Assert.assertEquals(expect, jsonString);
}
@Test
public void testDingTalkMsgUtf8() {
String msg = DingTalkUtils.textToJsonString("this is test:中文");
logger.info("test support utf8, actual:" + msg);
logger.info("test support utf8, actual:" + DingTalkUtils.IS_ENABLE_DING_TALK);
String expect = "{\"text\":{\"content\":\"this is test:中文\"},\"msgtype\":\"text\"}";
Assert.assertEquals(expect, msg);
}
}

283
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/EnterpriseWeChatUtilsTest.java

@ -1,283 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.utils;
import org.apache.dolphinscheduler.common.enums.AlertType;
import org.apache.dolphinscheduler.common.enums.ShowType;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.dao.entity.Alert;
import org.apache.dolphinscheduler.plugin.model.AlertData;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
/**
* Please manually modify the configuration file before testing.
* file: alert.properties
* enterprise.wechat.corp.id
* enterprise.wechat.secret
* enterprise.wechat.token.url
* enterprise.wechat.push.url
* enterprise.wechat.send.msg
* enterprise.wechat.agent.id
* enterprise.wechat.users
*/
@PrepareForTest(PropertyUtils.class)
@RunWith(PowerMockRunner.class)
public class EnterpriseWeChatUtilsTest {
private static final String toParty = "wwc99134b6fc1edb6";
private static final String enterpriseWechatSecret = "Uuv2KFrkdf7SeKOsTDCpsTkpawXBMNRhFy6VKX5FV";
private static final String enterpriseWechatAgentId = "1000004";
private static final String enterpriseWechatUsers = "LiGang,journey";
private static final String msg = "hello world";
private static final String enterpriseWechatTeamSendMsg = "{\\\"toparty\\\":\\\"{toParty}\\\",\\\"agentid\\\":\\\"{agentId}\\\""
+
",\\\"msgtype\\\":\\\"text\\\",\\\"text\\\":{\\\"content\\\":\\\"{msg}\\\"},\\\"safe\\\":\\\"0\\\"}";
private static final String enterpriseWechatUserSendMsg = "{\\\"touser\\\":\\\"{toUser}\\\",\\\"agentid\\\":\\\"{agentId}\\\""
+
",\\\"msgtype\\\":\\\"markdown\\\",\\\"markdown\\\":{\\\"content\\\":\\\"{msg}\\\"}}";
@Before
public void init() {
PowerMockito.mockStatic(PropertyUtils.class);
Mockito.when(PropertyUtils.getBoolean(Constants.ENTERPRISE_WECHAT_ENABLE)).thenReturn(true);
Mockito.when(PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_USER_SEND_MSG)).thenReturn(enterpriseWechatUserSendMsg);
Mockito.when(PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_TEAM_SEND_MSG)).thenReturn(enterpriseWechatTeamSendMsg);
}
@Test
public void testIsEnable() {
Boolean weChartEnable = EnterpriseWeChatUtils.isEnable();
Assert.assertTrue(weChartEnable);
}
@Test
public void testMakeTeamSendMsg1() {
String sendMsg = EnterpriseWeChatUtils.makeTeamSendMsg(toParty, enterpriseWechatSecret, msg);
Assert.assertTrue(sendMsg.contains(toParty));
Assert.assertTrue(sendMsg.contains(enterpriseWechatSecret));
Assert.assertTrue(sendMsg.contains(msg));
}
@Test
public void testMakeTeamSendMsg2() {
List<String> parties = new ArrayList<>();
parties.add(toParty);
parties.add("test1");
String sendMsg = EnterpriseWeChatUtils.makeTeamSendMsg(parties, enterpriseWechatSecret, msg);
Assert.assertTrue(sendMsg.contains(toParty));
Assert.assertTrue(sendMsg.contains(enterpriseWechatSecret));
Assert.assertTrue(sendMsg.contains(msg));
}
@Test
public void tesMakeUserSendMsg1() {
String sendMsg = EnterpriseWeChatUtils.makeUserSendMsg(enterpriseWechatUsers, enterpriseWechatAgentId, msg);
Assert.assertTrue(sendMsg.contains(enterpriseWechatUsers));
Assert.assertTrue(sendMsg.contains(enterpriseWechatAgentId));
Assert.assertTrue(sendMsg.contains(msg));
}
@Test
public void tesMakeUserSendMsg2() {
List<String> users = new ArrayList<>();
users.add("user1");
users.add("user2");
String sendMsg = EnterpriseWeChatUtils.makeUserSendMsg(users, enterpriseWechatAgentId, msg);
Assert.assertTrue(sendMsg.contains(users.get(0)));
Assert.assertTrue(sendMsg.contains(users.get(1)));
Assert.assertTrue(sendMsg.contains(enterpriseWechatAgentId));
Assert.assertTrue(sendMsg.contains(msg));
}
@Test
public void testMarkdownByAlertForText() {
Alert alertForText = createAlertForText();
AlertData alertData = new AlertData();
alertData.setTitle(alertForText.getTitle())
.setShowType(alertForText.getShowType().getDescp())
.setContent(alertForText.getContent());
String result = EnterpriseWeChatUtils.markdownByAlert(alertData);
Assert.assertNotNull(result);
}
@Test
public void testMarkdownByAlertForTable() {
Alert alertForText = createAlertForTable();
AlertData alertData = new AlertData();
alertData.setTitle(alertForText.getTitle())
.setShowType(alertForText.getShowType().getDescp())
.setContent(alertForText.getContent());
String result = EnterpriseWeChatUtils.markdownByAlert(alertData);
Assert.assertNotNull(result);
}
private Alert createAlertForText() {
String content = "[{\"id\":\"69\","
+
"\"name\":\"UserBehavior-0--1193959466\","
+
"\"Job name\":\"Start workflow\","
+
"\"State\":\"SUCCESS\","
+
"\"Recovery\":\"NO\","
+
"\"Run time\":\"1\","
+
"\"Start time\": \"2018-08-06 10:31:34.0\","
+
"\"End time\": \"2018-08-06 10:31:49.0\","
+
"\"Host\": \"192.168.xx.xx\","
+
"\"Notify group\" :\"4\"}]";
Alert alert = new Alert();
alert.setTitle("Mysql Exception");
alert.setShowType(ShowType.TEXT);
alert.setContent(content);
alert.setAlertType(AlertType.EMAIL);
alert.setAlertGroupId(4);
return alert;
}
private String list2String() {
LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
map1.put("mysql service name", "mysql200");
map1.put("mysql address", "192.168.xx.xx");
map1.put("port", "3306");
map1.put("no index of number", "80");
map1.put("database client connections", "190");
LinkedHashMap<String, Object> map2 = new LinkedHashMap<>();
map2.put("mysql service name", "mysql210");
map2.put("mysql address", "192.168.xx.xx");
map2.put("port", "3306");
map2.put("no index of number", "10");
map2.put("database client connections", "90");
List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
maps.add(0, map1);
maps.add(1, map2);
String mapjson = JSONUtils.toJsonString(maps);
return mapjson;
}
private Alert createAlertForTable() {
Alert alert = new Alert();
alert.setTitle("Mysql Exception");
alert.setShowType(ShowType.TABLE);
String content = list2String();
alert.setContent(content);
alert.setAlertType(AlertType.EMAIL);
alert.setAlertGroupId(1);
return alert;
}
// @Test
// public void testSendSingleTeamWeChat() {
// try {
// String token = EnterpriseWeChatUtils.getToken();
// String msg = EnterpriseWeChatUtils.makeTeamSendMsg(partyId, agentId, "hello world");
// String resp = EnterpriseWeChatUtils.sendEnterpriseWeChat("utf-8", msg, token);
//
// String errmsg = JSONUtils.parseObject(resp).getString("errmsg");
// Assert.assertEquals("ok",errmsg);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
//
// @Test
// public void testSendMultiTeamWeChat() {
//
// try {
// String token = EnterpriseWeChatUtils.getToken();
// String msg = EnterpriseWeChatUtils.makeTeamSendMsg(listPartyId, agentId, "hello world");
// String resp = EnterpriseWeChatUtils.sendEnterpriseWeChat("utf-8", msg, token);
//
// String errmsg = JSONUtils.parseObject(resp).getString("errmsg");
// Assert.assertEquals("ok",errmsg);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
//
// @Test
// public void testSendSingleUserWeChat() {
// try {
// String token = EnterpriseWeChatUtils.getToken();
// String msg = EnterpriseWeChatUtils.makeUserSendMsg(listUserId.stream().findFirst().get(), agentId, "your meeting room has been booked and will be synced to the 'mailbox' later \n" +
// ">**matter details** \n" +
// ">matter:<font color='info'>meeting</font> <br>" +
// ">organizer:@miglioguan \n" +
// ">participant:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" +
// "> \n" +
// ">meeting room:<font color='info'>Guangzhou TIT 1st Floor 301</font> \n" +
// ">date:<font color='warning'>May 18, 2018</font> \n" +
// ">time:<font color='comment'>9:00-11:00 am</font> \n" +
// "> \n" +
// ">please attend the meeting on time\n" +
// "> \n" +
// ">to modify the meeting information, please click: [Modify Meeting Information](https://work.weixin.qq.com)\"");
//
// String resp = EnterpriseWeChatUtils.sendEnterpriseWeChat("utf-8", msg, token);
//
// String errmsg = JSONUtils.parseObject(resp).getString("errmsg");
// Assert.assertEquals("ok",errmsg);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
//
// @Test
// public void testSendMultiUserWeChat() {
// try {
// String token = EnterpriseWeChatUtils.getToken();
//
// String msg = EnterpriseWeChatUtils.makeUserSendMsg(listUserId, agentId, "hello world");
// String resp = EnterpriseWeChatUtils.sendEnterpriseWeChat("utf-8", msg, token);
//
// String errmsg = JSONUtils.parseObject(resp).getString("errmsg");
// Assert.assertEquals("ok",errmsg);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}

10
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/FuncUtilsTest.java

@ -17,14 +17,14 @@
package org.apache.dolphinscheduler.alert.utils;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FuncUtilsTest {

190
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/MailUtilsTest.java

@ -1,190 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.alert.utils;
import org.apache.dolphinscheduler.common.enums.AlertType;
import org.apache.dolphinscheduler.common.enums.ShowType;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.DaoFactory;
import org.apache.dolphinscheduler.dao.entity.Alert;
import org.apache.dolphinscheduler.dao.entity.User;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import org.apache.dolphinscheduler.common.utils.*;
/**
*/
public class MailUtilsTest {
private static final Logger logger = LoggerFactory.getLogger(MailUtilsTest.class);
@Test
public void testSendMails() {
String[] receivers = new String[]{"347801120@qq.com"};
String[] receiversCc = new String[]{"347801120@qq.com"};
String content ="[\"id:69\"," +
"\"name:UserBehavior-0--1193959466\"," +
"\"Job name: Start workflow\"," +
"\"State: SUCCESS\"," +
"\"Recovery:NO\"," +
"\"Run time: 1\"," +
"\"Start time: 2018-08-06 10:31:34.0\"," +
"\"End time: 2018-08-06 10:31:49.0\"," +
"\"Host: 192.168.xx.xx\"," +
"\"Notify group :4\"]";
Alert alert = new Alert();
alert.setTitle("Mysql Exception");
alert.setShowType(ShowType.TEXT);
alert.setContent(content);
alert.setAlertType(AlertType.EMAIL);
alert.setAlertGroupId(4);
MailUtils.sendMails(Arrays.asList(receivers),Arrays.asList(receiversCc),alert.getTitle(),alert.getContent(), ShowType.TEXT.getDescp());
}
@Test
public void testQuery(){
AlertDao alertDao = DaoFactory.getDaoInstance(AlertDao.class);
List<Alert> alerts = alertDao.listWaitExecutionAlert();
String[] mails = new String[]{"xx@xx.com"};
for(Alert alert : alerts){
MailUtils.sendMails(Arrays.asList(mails),"gaojing", alert.getContent(), ShowType.TABLE.getDescp());
}
}
public String list2String(){
LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
map1.put("mysql service name","mysql200");
map1.put("mysql address","192.168.xx.xx");
map1.put("port","3306");
map1.put("no index of number","80");
map1.put("database client connections","190");
LinkedHashMap<String, Object> map2 = new LinkedHashMap<>();
map2.put("mysql service name","mysql210");
map2.put("mysql address","192.168.xx.xx");
map2.put("port","3306");
map2.put("no index of number","10");
map2.put("database client connections","90");
List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
maps.add(0,map1);
maps.add(1,map2);
String mapjson = JSONUtils.toJsonString(maps);
logger.info(mapjson);
return mapjson;
}
@Test
public void testSendTableMail(){
String[] mails = new String[]{"347801120@qq.com"};
Alert alert = new Alert();
alert.setTitle("Mysql Exception");
alert.setShowType(ShowType.TABLE);
String content= list2String();
alert.setContent(content);
alert.setAlertType(AlertType.EMAIL);
alert.setAlertGroupId(1);
MailUtils.sendMails(Arrays.asList(mails),"gaojing", alert.getContent(), ShowType.TABLE.getDescp());
}
/**
* Used to test add alarm information, mail sent
* Text
*/
@Test
public void addAlertText(){
AlertDao alertDao = DaoFactory.getDaoInstance(AlertDao.class);
Alert alert = new Alert();
alert.setTitle("Mysql Exception");
alert.setShowType(ShowType.TEXT);
alert.setContent("[\"alarm time:2018-02-05\", \"service name:MYSQL_ALTER\", \"alarm name:MYSQL_ALTER_DUMP\", " +
"\"get the alarm exception.!,interface error,exception information:timed out\", \"request address:http://blog.csdn.net/dreamInTheWorld/article/details/78539286\"]");
alert.setAlertType(AlertType.EMAIL);
alert.setAlertGroupId(1);
alertDao.addAlert(alert);
}
/**
* Used to test add alarm information, mail sent
* Table
*/
@Test
public void testAddAlertTable(){
logger.info("testAddAlertTable");
AlertDao alertDao = DaoFactory.getDaoInstance(AlertDao.class);
Assert.assertNotNull(alertDao);
Alert alert = new Alert();
alert.setTitle("Mysql Exception");
alert.setShowType(ShowType.TABLE);
String content = list2String();
alert.setContent(content);
alert.setAlertType(AlertType.EMAIL);
alert.setAlertGroupId(1);
alertDao.addAlert(alert);
logger.info("" +alert);
}
@Test
public void testAlertDao(){
AlertDao alertDao = DaoFactory.getDaoInstance(AlertDao.class);
List<User> users = alertDao.listUserByAlertgroupId(3);
logger.info(users.toString());
}
@Test
public void testAttachmentFile()throws Exception{
String[] mails = new String[]{"xx@xx.com"};
Alert alert = new Alert();
alert.setTitle("Mysql Exception");
alert.setShowType(ShowType.ATTACHMENT);
String content = list2String();
alert.setContent(content);
alert.setAlertType(AlertType.EMAIL);
alert.setAlertGroupId(1);
MailUtils.sendMails(Arrays.asList(mails),"gaojing",alert.getContent(),ShowType.ATTACHMENT.getDescp());
}
@Test
public void testTableAttachmentFile()throws Exception{
String[] mails = new String[]{"xx@xx.com"};
Alert alert = new Alert();
alert.setTitle("Mysql Exception");
alert.setShowType(ShowType.TABLEATTACHMENT);
String content = list2String();
alert.setContent(content);
alert.setAlertType(AlertType.EMAIL);
alert.setAlertGroupId(1);
MailUtils.sendMails(Arrays.asList(mails),"gaojing",alert.getContent(),ShowType.TABLEATTACHMENT.getDescp());
}
}

17
dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/PropertyUtilsTest.java

@ -17,13 +17,18 @@
package org.apache.dolphinscheduler.alert.utils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import org.apache.dolphinscheduler.common.enums.ZKNodeType;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.*;
/**
* Test PropertyUtils
* and the resource path is src/test/resources/alert.properties.
@ -196,19 +201,19 @@ public class PropertyUtilsTest {
public void testGetEnum() {
//Expected MASTER
ZKNodeType zkNodeType = PropertyUtils.getEnum("test.server.enum1", ZKNodeType.class,ZKNodeType.WORKER);
ZKNodeType zkNodeType = PropertyUtils.getEnum("test.server.enum1", ZKNodeType.class, ZKNodeType.WORKER);
assertEquals(ZKNodeType.MASTER, zkNodeType);
//Expected DEAD_SERVER
zkNodeType = PropertyUtils.getEnum("test.server.enum2", ZKNodeType.class,ZKNodeType.WORKER);
zkNodeType = PropertyUtils.getEnum("test.server.enum2", ZKNodeType.class, ZKNodeType.WORKER);
assertEquals(ZKNodeType.DEAD_SERVER, zkNodeType);
//If key is null, then return defaultval
zkNodeType = PropertyUtils.getEnum(null, ZKNodeType.class,ZKNodeType.WORKER);
zkNodeType = PropertyUtils.getEnum(null, ZKNodeType.class, ZKNodeType.WORKER);
assertEquals(ZKNodeType.WORKER, zkNodeType);
//If the value doesn't define in enum ,it will log the error and return -1
zkNodeType = PropertyUtils.getEnum("test.server.enum3", ZKNodeType.class,ZKNodeType.WORKER);
zkNodeType = PropertyUtils.getEnum("test.server.enum3", ZKNodeType.class, ZKNodeType.WORKER);
assertEquals(ZKNodeType.WORKER, zkNodeType);
}

113
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertGroupController.java

@ -14,31 +14,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.controller;
import static org.apache.dolphinscheduler.api.enums.Status.CREATE_ALERT_GROUP_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.DELETE_ALERT_GROUP_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.LIST_PAGING_ALERT_GROUP_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_ALL_ALERTGROUP_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_ALERT_GROUP_ERROR;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.exceptions.ApiException;
import org.apache.dolphinscheduler.api.service.AlertGroupService;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.common.enums.AlertType;
import org.apache.dolphinscheduler.common.utils.ParameterUtils;
import org.apache.dolphinscheduler.common.utils.StringUtils;
import org.apache.dolphinscheduler.dao.entity.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.dolphinscheduler.api.enums.Status;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import static org.apache.dolphinscheduler.api.enums.Status.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.annotations.ApiIgnore;
/**
* alert group controller
@ -57,28 +70,31 @@ public class AlertGroupController extends BaseController {
/**
* create alert group
*
* @param loginUser login user
* @param groupName group name
* @param groupType group type
* @param loginUser login user
* @param groupName group name
* @param description description
* @return create result code
*/
@ApiOperation(value = "createAlertgroup", notes = "CREATE_ALERT_GROUP_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "groupName", value = "GROUP_NAME", required = true, dataType = "String"),
@ApiImplicitParam(name = "groupType", value = "GROUP_TYPE", required = true, dataType = "AlertType"),
@ApiImplicitParam(name = "description", value = "DESC", dataType = "String")
@ApiImplicitParam(name = "description", value = "DESC", dataType = "String"),
@ApiImplicitParam(name = "alertInstanceIds", value = "alertInstanceIds", dataType = "String")
})
@PostMapping(value = "/create")
@ResponseStatus(HttpStatus.CREATED)
@ApiException(CREATE_ALERT_GROUP_ERROR)
public Result createAlertgroup(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "groupName") String groupName,
@RequestParam(value = "groupType") AlertType groupType,
@RequestParam(value = "description", required = false) String description) {
logger.info("loginUser user {}, create alertgroup, groupName: {}, groupType: {}, desc: {}",
loginUser.getUserName(), groupName, groupType, description);
Map<String, Object> result = alertGroupService.createAlertgroup(loginUser, groupName, groupType, description);
@RequestParam(value = "description", required = false) String description,
@RequestParam(value = "alertInstanceIds") String alertInstanceIds) {
String strUserName = StringUtils.replaceNRTtoUnderline(loginUser.getUserName());
String strGroupName = StringUtils.replaceNRTtoUnderline(groupName);
String strDescription = StringUtils.replaceNRTtoUnderline(description);
String strAlertInstanceIds = StringUtils.replaceNRTtoUnderline(alertInstanceIds);
logger.info("loginUser user {}, create alert group, groupName: {}, desc: {},alertInstanceIds:{}",
strUserName, strGroupName, strDescription, strAlertInstanceIds);
Map<String, Object> result = alertGroupService.createAlertgroup(loginUser, groupName, description, alertInstanceIds);
return returnDataList(result);
}
@ -103,9 +119,9 @@ public class AlertGroupController extends BaseController {
* paging query alarm group list
*
* @param loginUser login user
* @param pageNo page number
* @param pageNo page number
* @param searchVal search value
* @param pageSize page size
* @param pageSize page size
* @return alert group list page
*/
@ApiOperation(value = "queryAlertGroupListPaging", notes = "QUERY_ALERT_GROUP_LIST_PAGING_NOTES")
@ -136,10 +152,9 @@ public class AlertGroupController extends BaseController {
/**
* updateProcessInstance alert group
*
* @param loginUser login user
* @param id alert group id
* @param groupName group name
* @param groupType group type
* @param loginUser login user
* @param id alert group id
* @param groupName group name
* @param description description
* @return update result code
*/
@ -147,8 +162,8 @@ public class AlertGroupController extends BaseController {
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "ALERT_GROUP_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "groupName", value = "GROUP_NAME", required = true, dataType = "String"),
@ApiImplicitParam(name = "groupType", value = "GROUP_TYPE", required = true, dataType = "AlertType"),
@ApiImplicitParam(name = "description", value = "DESC", dataType = "String")
@ApiImplicitParam(name = "description", value = "DESC", dataType = "String"),
@ApiImplicitParam(name = "alertInstanceIds", value = "alertInstanceIds", dataType = "String")
})
@PostMapping(value = "/update")
@ResponseStatus(HttpStatus.OK)
@ -156,11 +171,13 @@ public class AlertGroupController extends BaseController {
public Result updateAlertgroup(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "id") int id,
@RequestParam(value = "groupName") String groupName,
@RequestParam(value = "groupType") AlertType groupType,
@RequestParam(value = "description", required = false) String description) {
logger.info("login user {}, updateProcessInstance alertgroup, groupName: {}, groupType: {}, desc: {}",
loginUser.getUserName(), groupName, groupType, description);
Map<String, Object> result = alertGroupService.updateAlertgroup(loginUser, id, groupName, groupType, description);
@RequestParam(value = "description", required = false) String description,
@RequestParam(value = "alertInstanceIds") String alertInstanceIds) {
logger.info("login user {}, updateProcessInstance alert group, groupName: {}, desc: {}",
StringUtils.replaceNRTtoUnderline(loginUser.getUserName()),
StringUtils.replaceNRTtoUnderline(groupName),
StringUtils.replaceNRTtoUnderline(description));
Map<String, Object> result = alertGroupService.updateAlertgroup(loginUser, id, groupName, description, alertInstanceIds);
return returnDataList(result);
}
@ -168,7 +185,7 @@ public class AlertGroupController extends BaseController {
* delete alert group by id
*
* @param loginUser login user
* @param id alert group id
* @param id alert group id
* @return delete result code
*/
@ApiOperation(value = "delAlertgroupById", notes = "DELETE_ALERT_GROUP_BY_ID_NOTES")
@ -215,28 +232,4 @@ public class AlertGroupController extends BaseController {
}
return result;
}
/**
* grant user
*
* @param loginUser login user
* @param userIds user ids in the group
* @param alertgroupId alert group id
* @return grant result code
*/
@ApiOperation(value = "grantUser", notes = "GRANT_ALERT_GROUP_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "ALERT_GROUP_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "userIds", value = "USER_IDS", required = true, dataType = "String")
})
@PostMapping(value = "/grant-user")
@ResponseStatus(HttpStatus.OK)
@ApiException(ALERT_GROUP_GRANT_USER_ERROR)
public Result grantUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "alertgroupId") int alertgroupId,
@RequestParam(value = "userIds") String userIds) {
logger.info("login user {}, grant user, alertGroupId: {},userIds : {}", loginUser.getUserName(), alertgroupId, userIds);
Map<String, Object> result = alertGroupService.grantUser(loginUser, alertgroupId, userIds);
return returnDataList(result);
}
}

240
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceController.java

@ -0,0 +1,240 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.controller;
import static org.apache.dolphinscheduler.api.enums.Status.CREATE_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.DELETE_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.GET_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.LIST_PAGING_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_ALL_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_ALERT_PLUGIN_INSTANCE_ERROR;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.exceptions.ApiException;
import org.apache.dolphinscheduler.api.service.AlertPluginInstanceService;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.common.utils.StringUtils;
import org.apache.dolphinscheduler.dao.entity.User;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.annotations.ApiIgnore;
/**
* alert plugin instance controller
*/
@Api(tags = "ALERT_PLUGIN_INSTANCE_TAG", position = 1)
@RestController
@RequestMapping("alert-plugin-instance")
public class AlertPluginInstanceController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(AlertPluginInstanceController.class);
@Autowired
private AlertPluginInstanceService alertPluginInstanceService;
/**
* create alert plugin instance
*
* @param loginUser login user
* @param pluginDefineId alert plugin define id
* @param instanceName instance name
* @param pluginInstanceParams instance params
* @return result
*/
@ApiOperation(value = "createAlertPluginInstance", notes = "CREATE_ALERT_PLUGIN_INSTANCE_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "pluginDefineId", value = "ALERT_PLUGIN_DEFINE_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "instanceName", value = "ALERT_PLUGIN_INSTANCE_NAME", required = true, dataType = "String", example = "DING TALK"),
@ApiImplicitParam(name = "pluginInstanceParams", value = "ALERT_PLUGIN_INSTANCE_PARAMS", required = true, dataType = "String", example = "ALERT_PLUGIN_INSTANCE_PARAMS")
})
@PostMapping(value = "/create")
@ResponseStatus(HttpStatus.CREATED)
@ApiException(CREATE_ALERT_PLUGIN_INSTANCE_ERROR)
public Result createAlertPluginInstance(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "pluginDefineId") int pluginDefineId,
@RequestParam(value = "instanceName") String instanceName,
@RequestParam(value = "pluginInstanceParams") String pluginInstanceParams) {
logger.info("login user {},create alert plugin instance, instanceName:{} ",
StringUtils.replaceNRTtoUnderline(loginUser.getUserName()),
StringUtils.replaceNRTtoUnderline(instanceName));
Map<String, Object> result = alertPluginInstanceService.create(loginUser, pluginDefineId, instanceName, pluginInstanceParams);
return returnDataList(result);
}
/**
* updateAlertPluginInstance
*
* @param loginUser login user
* @param alertPluginInstanceId alert plugin instance id
* @param instanceName instance name
* @param pluginInstanceParams instance params
* @return result
*/
@ApiOperation(value = "update", notes = "UPDATE_ALERT_PLUGIN_INSTANCE_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "alertPluginInstanceId", value = "ALERT_PLUGIN_INSTANCE_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "instanceName", value = "ALERT_PLUGIN_INSTANCE_NAME", required = true, dataType = "String", example = "DING TALK"),
@ApiImplicitParam(name = "pluginInstanceParams", value = "ALERT_PLUGIN_INSTANCE_PARAMS", required = true, dataType = "String", example = "ALERT_PLUGIN_INSTANCE_PARAMS")
})
@GetMapping(value = "/update")
@ResponseStatus(HttpStatus.OK)
@ApiException(UPDATE_ALERT_PLUGIN_INSTANCE_ERROR)
public Result updateAlertPluginInstance(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "alertPluginInstanceId") int alertPluginInstanceId,
@RequestParam(value = "instanceName") String instanceName,
@RequestParam(value = "pluginInstanceParams") String pluginInstanceParams) {
logger.info("login user {},update alert plugin instance id {}", StringUtils.replaceNRTtoUnderline(loginUser.getUserName()), alertPluginInstanceId);
Map<String, Object> result = alertPluginInstanceService.update(loginUser, alertPluginInstanceId, instanceName, pluginInstanceParams);
return returnDataList(result);
}
/**
* deleteAlertPluginInstance
*
* @param loginUser login user
* @param id id
* @return result
*/
@ApiOperation(value = "delete", notes = "DELETE_ALERT_PLUGIN_INSTANCE_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "ALERT_PLUGIN_ID", required = true, dataType = "Int", example = "100")
})
@GetMapping(value = "/delete")
@ResponseStatus(HttpStatus.OK)
@ApiException(DELETE_ALERT_PLUGIN_INSTANCE_ERROR)
public Result deleteAlertPluginInstance(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "id") int id) {
logger.info("login user {},delete alert plugin instance id {}", StringUtils.replaceNRTtoUnderline(loginUser.getUserName()), id);
Map<String, Object> result = alertPluginInstanceService.delete(loginUser, id);
return returnDataList(result);
}
/**
* getAlertPluginInstance
*
* @param loginUser login user
* @param id alert plugin instance id
* @return result
*/
@ApiOperation(value = "get", notes = "GET_ALERT_PLUGIN_INSTANCE_NOTES")
@PostMapping(value = "/get")
@ResponseStatus(HttpStatus.OK)
@ApiException(GET_ALERT_PLUGIN_INSTANCE_ERROR)
public Result getAlertPluginInstance(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "id") int id) {
logger.info("login user {},get alert plugin instance, id {}", StringUtils.replaceNRTtoUnderline(loginUser.getUserName()), id);
Map<String, Object> result = alertPluginInstanceService.get(loginUser, id);
return returnDataList(result);
}
/**
* getAlertPluginInstance
*
* @param loginUser login user
* @return result
*/
@ApiOperation(value = "/queryAll", notes = "QUERY_ALL_ALERT_PLUGIN_INSTANCE_NOTES")
@PostMapping(value = "/queryAll")
@ResponseStatus(HttpStatus.OK)
@ApiException(QUERY_ALL_ALERT_PLUGIN_INSTANCE_ERROR)
public Result getAlertPluginInstance(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
logger.info("login user {}, query all alert plugin instance", StringUtils.replaceNRTtoUnderline(loginUser.getUserName()));
Map<String, Object> result = alertPluginInstanceService.queryAll();
return returnDataList(result);
}
/**
* check alert group exist
*
* @param loginUser login user
* @param alertInstanceName alert instance name
* @return check result code
*/
@ApiOperation(value = "verifyAlertInstanceName", notes = "VERIFY_ALERT_INSTANCE_NAME_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "groupName", value = "GROUP_NAME", required = true, dataType = "String"),
})
@GetMapping(value = "/verify-alert-instance-name")
@ResponseStatus(HttpStatus.OK)
public Result verifyGroupName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "alertInstanceName") String alertInstanceName) {
logger.info("login user {},verify alert instance name: {}", StringUtils.replaceNRTtoUnderline(loginUser.getUserName()), StringUtils.replaceNRTtoUnderline(alertInstanceName));
boolean exist = alertPluginInstanceService.checkExistPluginInstanceName(alertInstanceName);
Result result = new Result();
if (exist) {
logger.error("alert plugin instance {} has exist, can't create again.", alertInstanceName);
result.setCode(Status.PLUGIN_INSTANCE_ALREADY_EXIT.getCode());
result.setMsg(Status.PLUGIN_INSTANCE_ALREADY_EXIT.getMsg());
} else {
result.setCode(Status.SUCCESS.getCode());
result.setMsg(Status.SUCCESS.getMsg());
}
return result;
}
/**
* paging query alert plugin instance group list
*
* @param loginUser login user
* @param pageNo page number
* @param pageSize page size
* @return alert plugin instance list page
*/
@ApiOperation(value = "queryAlertPluginInstanceListPaging", notes = "QUERY_ALERT_PLUGIN_INSTANCE_LIST_PAGING_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
@ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType = "Int", example = "20")
})
@GetMapping(value = "/list-paging")
@ResponseStatus(HttpStatus.OK)
@ApiException(LIST_PAGING_ALERT_PLUGIN_INSTANCE_ERROR)
public Result listPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam("pageNo") Integer pageNo,
@RequestParam("pageSize") Integer pageSize) {
logger.info("login user {}, list paging, pageNo: {}, pageSize: {}",StringUtils.replaceNRTtoUnderline(loginUser.getUserName()), pageNo, pageSize);
Map<String, Object> result = checkPageParams(pageNo, pageSize);
if (result.get(Constants.STATUS) != Status.SUCCESS) {
return returnDataListPaging(result);
}
result = alertPluginInstanceService.queryPluginPage(pageNo, pageSize);
return returnDataListPaging(result);
}
}

113
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ExecutorController.java

@ -14,31 +14,49 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.controller;
import static org.apache.dolphinscheduler.api.enums.Status.CHECK_PROCESS_DEFINITION_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.EXECUTE_PROCESS_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.START_PROCESS_INSTANCE_ERROR;
import org.apache.dolphinscheduler.api.enums.ExecuteType;
import org.apache.dolphinscheduler.api.exceptions.ApiException;
import org.apache.dolphinscheduler.api.service.ExecutorService;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.common.enums.CommandType;
import org.apache.dolphinscheduler.common.enums.FailureStrategy;
import org.apache.dolphinscheduler.common.enums.Priority;
import org.apache.dolphinscheduler.common.enums.RunMode;
import org.apache.dolphinscheduler.common.enums.TaskDependType;
import org.apache.dolphinscheduler.common.enums.WarningType;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.dao.entity.User;
import io.swagger.annotations.*;
import org.apache.dolphinscheduler.common.enums.*;
import java.text.ParseException;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import springfox.documentation.annotations.ApiIgnore;
import java.text.ParseException;
import java.util.Map;
import static org.apache.dolphinscheduler.api.enums.Status.*;
/**
* execute process controller
*/
@ -55,22 +73,20 @@ public class ExecutorController extends BaseController {
/**
* execute process instance
*
* @param loginUser login user
* @param projectName project name
* @param processDefinitionId process definition id
* @param scheduleTime schedule time
* @param failureStrategy failure strategy
* @param startNodeList start nodes list
* @param taskDependType task depend type
* @param execType execute type
* @param warningType warning type
* @param warningGroupId warning group id
* @param receivers receivers
* @param receiversCc receivers cc
* @param runMode run mode
* @param loginUser login user
* @param projectName project name
* @param processDefinitionId process definition id
* @param scheduleTime schedule time
* @param failureStrategy failure strategy
* @param startNodeList start nodes list
* @param taskDependType task depend type
* @param execType execute type
* @param warningType warning type
* @param warningGroupId warning group id
* @param runMode run mode
* @param processInstancePriority process instance priority
* @param workerGroup worker group
* @param timeout timeout
* @param workerGroup worker group
* @param timeout timeout
* @return start process result code
*/
@ApiOperation(value = "startProcessInstance", notes = "RUN_PROCESS_INSTANCE_NOTES")
@ -83,8 +99,6 @@ public class ExecutorController extends BaseController {
@ApiImplicitParam(name = "execType", value = "COMMAND_TYPE", dataType = "CommandType"),
@ApiImplicitParam(name = "warningType", value = "WARNING_TYPE", required = true, dataType = "WarningType"),
@ApiImplicitParam(name = "warningGroupId", value = "WARNING_GROUP_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "receivers", value = "RECEIVERS", dataType = "String"),
@ApiImplicitParam(name = "receiversCc", value = "RECEIVERS_CC", dataType = "String"),
@ApiImplicitParam(name = "runMode", value = "RUN_MODE", dataType = "RunMode"),
@ApiImplicitParam(name = "processInstancePriority", value = "PROCESS_INSTANCE_PRIORITY", required = true, dataType = "Priority"),
@ApiImplicitParam(name = "workerGroup", value = "WORKER_GROUP", dataType = "String", example = "default"),
@ -103,19 +117,16 @@ public class ExecutorController extends BaseController {
@RequestParam(value = "execType", required = false) CommandType execType,
@RequestParam(value = "warningType", required = true) WarningType warningType,
@RequestParam(value = "warningGroupId", required = false) int warningGroupId,
@RequestParam(value = "receivers", required = false) String receivers,
@RequestParam(value = "receiversCc", required = false) String receiversCc,
@RequestParam(value = "runMode", required = false) RunMode runMode,
@RequestParam(value = "processInstancePriority", required = false) Priority processInstancePriority,
@RequestParam(value = "workerGroup", required = false, defaultValue = "default") String workerGroup,
@RequestParam(value = "timeout", required = false) Integer timeout,
@RequestParam(value = "startParams", required = false) String startParams) throws ParseException {
logger.info("login user {}, start process instance, project name: {}, process definition id: {}, schedule time: {}, "
logger.info("login user {}, start process instance, project name: {}, process definition id: {}, schedule time: {}, "
+ "failure policy: {}, node name: {}, node dep: {}, notify type: {}, "
+ "notify group id: {},receivers:{},receiversCc:{}, run mode: {},process instance priority:{}, workerGroup: {}, timeout: {}, "
+ "startParams: {}",
+ "notify group id: {}, run mode: {},process instance priority:{}, workerGroup: {}, timeout: {}, startParams: {} ",
loginUser.getUserName(), projectName, processDefinitionId, scheduleTime,
failureStrategy, startNodeList, taskDependType, warningType, workerGroup, receivers, receiversCc, runMode, processInstancePriority,
failureStrategy, startNodeList, taskDependType, warningType, workerGroup, runMode, processInstancePriority,
workerGroup, timeout, startParams);
if (timeout == null) {
@ -127,7 +138,7 @@ public class ExecutorController extends BaseController {
}
Map<String, Object> result = execService.execProcessInstance(loginUser, projectName, processDefinitionId, scheduleTime, execType, failureStrategy,
startNodeList, taskDependType, warningType,
warningGroupId, receivers, receiversCc, runMode, processInstancePriority, workerGroup, timeout, startParamMap);
warningGroupId, runMode, processInstancePriority, workerGroup, timeout, startParamMap);
return returnDataList(result);
}
@ -135,10 +146,10 @@ public class ExecutorController extends BaseController {
/**
* do action to process instancepause, stop, repeat, recover from pause, recover from stop
*
* @param loginUser login user
* @param projectName project name
* @param loginUser login user
* @param projectName project name
* @param processInstanceId process instance id
* @param executeType execute type
* @param executeType execute type
* @return execute result code
*/
@ApiOperation(value = "execute", notes = "EXECUTE_ACTION_TO_PROCESS_INSTANCE_NOTES")
@ -163,7 +174,7 @@ public class ExecutorController extends BaseController {
/**
* check process definition and all of the son process definitions is on line.
*
* @param loginUser login user
* @param loginUser login user
* @param processDefinitionId process definition id
* @return check result code
*/
@ -180,32 +191,4 @@ public class ExecutorController extends BaseController {
Map<String, Object> result = execService.startCheckByProcessDefinedId(processDefinitionId);
return returnDataList(result);
}
/**
* query recipients and copyers by process definition ID
*
* @param loginUser login user
* @param processDefinitionId process definition id
* @param processInstanceId process instance id
* @return receivers cc list
*/
@ApiIgnore
@ApiOperation(value = "getReceiverCc", notes = "GET_RECEIVER_CC_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", required = true, dataType = "Int", example = "100")
})
@GetMapping(value = "/get-receiver-cc")
@ResponseStatus(HttpStatus.OK)
@ApiException(QUERY_RECIPIENTS_AND_COPYERS_BY_PROCESS_DEFINITION_ERROR)
public Result getReceiverCc(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "processDefinitionId", required = false) Integer processDefinitionId,
@RequestParam(value = "processInstanceId", required = false) Integer processInstanceId) {
logger.info("login user {}, get process definition receiver and cc", loginUser.getUserName());
Map<String, Object> result = execService.getReceiverCc(processDefinitionId, processInstanceId);
return returnDataList(result);
}
}

132
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/SchedulerController.java

@ -14,8 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.controller;
import static org.apache.dolphinscheduler.api.enums.Status.CREATE_SCHEDULE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.DELETE_SCHEDULE_CRON_BY_ID_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.OFFLINE_SCHEDULE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.PREVIEW_SCHEDULE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.PUBLISH_SCHEDULE_ONLINE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_SCHEDULE_LIST_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_SCHEDULE_LIST_PAGING_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_SCHEDULE_ERROR;
import static org.apache.dolphinscheduler.common.Constants.SESSION_USER;
import org.apache.dolphinscheduler.api.exceptions.ApiException;
import org.apache.dolphinscheduler.api.service.SchedulerService;
@ -25,20 +35,30 @@ import org.apache.dolphinscheduler.common.enums.Priority;
import org.apache.dolphinscheduler.common.enums.ReleaseState;
import org.apache.dolphinscheduler.common.enums.WarningType;
import org.apache.dolphinscheduler.common.utils.ParameterUtils;
import org.apache.dolphinscheduler.common.utils.StringUtils;
import org.apache.dolphinscheduler.dao.entity.User;
import io.swagger.annotations.*;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import java.io.IOException;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import static org.apache.dolphinscheduler.api.enums.Status.*;
import static org.apache.dolphinscheduler.common.Constants.SESSION_USER;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import springfox.documentation.annotations.ApiIgnore;
/**
* schedule controller
@ -61,17 +81,15 @@ public class SchedulerController extends BaseController {
/**
* create schedule
*
* @param loginUser login user
* @param projectName project name
* @param processDefinitionId process definition id
* @param schedule scheduler
* @param warningType warning type
* @param warningGroupId warning group id
* @param failureStrategy failure strategy
* @param loginUser login user
* @param projectName project name
* @param processDefinitionId process definition id
* @param schedule scheduler
* @param warningType warning type
* @param warningGroupId warning group id
* @param failureStrategy failure strategy
* @param processInstancePriority process instance priority
* @param receivers receivers
* @param receiversCc receivers cc
* @param workerGroup worker group
* @param workerGroup worker group
* @return create result code
*/
@ApiOperation(value = "createSchedule", notes = "CREATE_SCHEDULE_NOTES")
@ -81,8 +99,6 @@ public class SchedulerController extends BaseController {
@ApiImplicitParam(name = "warningType", value = "WARNING_TYPE", type = "WarningType"),
@ApiImplicitParam(name = "warningGroupId", value = "WARNING_GROUP_ID", dataType = "Int", example = "100"),
@ApiImplicitParam(name = "failureStrategy", value = "FAILURE_STRATEGY", type = "FailureStrategy"),
@ApiImplicitParam(name = "receivers", value = "RECEIVERS", type = "String"),
@ApiImplicitParam(name = "receiversCc", value = "RECEIVERS_CC", type = "String"),
@ApiImplicitParam(name = "workerGroupId", value = "WORKER_GROUP_ID", dataType = "Int", example = "100"),
@ApiImplicitParam(name = "processInstancePriority", value = "PROCESS_INSTANCE_PRIORITY", type = "Priority"),
})
@ -96,16 +112,14 @@ public class SchedulerController extends BaseController {
@RequestParam(value = "warningType", required = false, defaultValue = DEFAULT_WARNING_TYPE) WarningType warningType,
@RequestParam(value = "warningGroupId", required = false, defaultValue = DEFAULT_NOTIFY_GROUP_ID) int warningGroupId,
@RequestParam(value = "failureStrategy", required = false, defaultValue = DEFAULT_FAILURE_POLICY) FailureStrategy failureStrategy,
@RequestParam(value = "receivers", required = false) String receivers,
@RequestParam(value = "receiversCc", required = false) String receiversCc,
@RequestParam(value = "workerGroup", required = false, defaultValue = "default") String workerGroup,
@RequestParam(value = "processInstancePriority", required = false, defaultValue = DEFAULT_PROCESS_INSTANCE_PRIORITY) Priority processInstancePriority) throws IOException {
logger.info("login user {}, project name: {}, process name: {}, create schedule: {}, warning type: {}, warning group id: {}," +
"failure policy: {},receivers : {},receiversCc : {},processInstancePriority : {}, workGroupId:{}",
loginUser.getUserName(), projectName, processDefinitionId, schedule, warningType, warningGroupId,
failureStrategy, receivers, receiversCc, processInstancePriority, workerGroup);
@RequestParam(value = "processInstancePriority", required = false, defaultValue = DEFAULT_PROCESS_INSTANCE_PRIORITY) Priority processInstancePriority) {
logger.info("login user {},project name: {}, process name: {}, create schedule: {}, warning type: {}, warning group id: {},"
+ "failure policy: {},processInstancePriority : {}, workGroupId:{}",
StringUtils.replaceNRTtoUnderline(loginUser.getUserName()), StringUtils.replaceNRTtoUnderline(projectName), processDefinitionId, schedule, warningType, warningGroupId,
failureStrategy, processInstancePriority, workerGroup);
Map<String, Object> result = schedulerService.insertSchedule(loginUser, projectName, processDefinitionId, schedule,
warningType, warningGroupId, failureStrategy, receivers, receiversCc, processInstancePriority, workerGroup);
warningType, warningGroupId, failureStrategy, processInstancePriority, workerGroup);
return returnDataList(result);
}
@ -113,17 +127,15 @@ public class SchedulerController extends BaseController {
/**
* updateProcessInstance schedule
*
* @param loginUser login user
* @param projectName project name
* @param id scheduler id
* @param schedule scheduler
* @param warningType warning type
* @param warningGroupId warning group id
* @param failureStrategy failure strategy
* @param receivers receivers
* @param workerGroup worker group
* @param loginUser login user
* @param projectName project name
* @param id scheduler id
* @param schedule scheduler
* @param warningType warning type
* @param warningGroupId warning group id
* @param failureStrategy failure strategy
* @param workerGroup worker group
* @param processInstancePriority process instance priority
* @param receiversCc receivers cc
* @return update result code
*/
@ApiOperation(value = "updateSchedule", notes = "UPDATE_SCHEDULE_NOTES")
@ -133,8 +145,6 @@ public class SchedulerController extends BaseController {
@ApiImplicitParam(name = "warningType", value = "WARNING_TYPE", type = "WarningType"),
@ApiImplicitParam(name = "warningGroupId", value = "WARNING_GROUP_ID", dataType = "Int", example = "100"),
@ApiImplicitParam(name = "failureStrategy", value = "FAILURE_STRATEGY", type = "FailureStrategy"),
@ApiImplicitParam(name = "receivers", value = "RECEIVERS", type = "String"),
@ApiImplicitParam(name = "receiversCc", value = "RECEIVERS_CC", type = "String"),
@ApiImplicitParam(name = "workerGroupId", value = "WORKER_GROUP_ID", dataType = "Int", example = "100"),
@ApiImplicitParam(name = "processInstancePriority", value = "PROCESS_INSTANCE_PRIORITY", type = "Priority"),
})
@ -147,26 +157,24 @@ public class SchedulerController extends BaseController {
@RequestParam(value = "warningType", required = false, defaultValue = DEFAULT_WARNING_TYPE) WarningType warningType,
@RequestParam(value = "warningGroupId", required = false) int warningGroupId,
@RequestParam(value = "failureStrategy", required = false, defaultValue = "END") FailureStrategy failureStrategy,
@RequestParam(value = "receivers", required = false) String receivers,
@RequestParam(value = "receiversCc", required = false) String receiversCc,
@RequestParam(value = "workerGroup", required = false, defaultValue = "default") String workerGroup,
@RequestParam(value = "processInstancePriority", required = false) Priority processInstancePriority) throws IOException {
logger.info("login user {}, project name: {},id: {}, updateProcessInstance schedule: {}, notify type: {}, notify mails: {}, " +
"failure policy: {},receivers : {},receiversCc : {},processInstancePriority : {},workerGroupId:{}",
loginUser.getUserName(), projectName, id, schedule, warningType, warningGroupId, failureStrategy,
receivers, receiversCc, processInstancePriority, workerGroup);
@RequestParam(value = "processInstancePriority", required = false) Priority processInstancePriority) {
logger.info("login user {},project name: {},id: {}, updateProcessInstance schedule: {}, notify type: {}, notify mails: {}, "
+ "failure policy: {},processInstancePriority : {},workerGroupId:{}",
StringUtils.replaceNRTtoUnderline(loginUser.getUserName()), StringUtils.replaceNRTtoUnderline(projectName), id, schedule, warningType, warningGroupId, failureStrategy,
processInstancePriority, workerGroup);
Map<String, Object> result = schedulerService.updateSchedule(loginUser, projectName, id, schedule,
warningType, warningGroupId, failureStrategy, receivers, receiversCc, null, processInstancePriority, workerGroup);
warningType, warningGroupId, failureStrategy, null, processInstancePriority, workerGroup);
return returnDataList(result);
}
/**
* publish schedule setScheduleState
*
* @param loginUser login user
* @param loginUser login user
* @param projectName project name
* @param id scheduler id
* @param id scheduler id
* @return publish result code
*/
@ApiOperation(value = "online", notes = "ONLINE_SCHEDULE_NOTES")
@ -187,9 +195,9 @@ public class SchedulerController extends BaseController {
/**
* offline schedule
*
* @param loginUser login user
* @param loginUser login user
* @param projectName project name
* @param id schedule id
* @param id schedule id
* @return operation result code
*/
@ApiOperation(value = "offline", notes = "OFFLINE_SCHEDULE_NOTES")
@ -211,12 +219,12 @@ public class SchedulerController extends BaseController {
/**
* query schedule list paging
*
* @param loginUser login user
* @param projectName project name
* @param loginUser login user
* @param projectName project name
* @param processDefinitionId process definition id
* @param pageNo page number
* @param pageSize page size
* @param searchVal search value
* @param pageNo page number
* @param pageSize page size
* @param searchVal search value
* @return schedule list page
*/
@ApiOperation(value = "queryScheduleListPaging", notes = "QUERY_SCHEDULE_LIST_PAGING_NOTES")
@ -245,9 +253,9 @@ public class SchedulerController extends BaseController {
/**
* delete schedule by id
*
* @param loginUser login user
* @param loginUser login user
* @param projectName project name
* @param scheduleId scheule id
* @param scheduleId scheule id
* @return delete result code
*/
@ApiOperation(value = "deleteScheduleById", notes = "OFFLINE_SCHEDULE_NOTES")
@ -270,7 +278,7 @@ public class SchedulerController extends BaseController {
/**
* query schedule list
*
* @param loginUser login user
* @param loginUser login user
* @param projectName project name
* @return schedule list
*/
@ -288,9 +296,9 @@ public class SchedulerController extends BaseController {
/**
* preview schedule
*
* @param loginUser login user
* @param loginUser login user
* @param projectName project name
* @param schedule schedule expression
* @param schedule schedule expression
* @return the next five fire time
*/
@ApiOperation(value = "previewSchedule", notes = "PREVIEW_SCHEDULE_NOTES")

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

Loading…
Cancel
Save