333 changed files with 15312 additions and 7749 deletions
@ -0,0 +1,43 @@ |
|||||||
|
# Licensed to 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. Apache Software Foundation (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. |
||||||
|
|
||||||
|
header: |
||||||
|
license: |
||||||
|
spdx-id: Apache-2.0 |
||||||
|
copyright-owner: Apache Software Foundation |
||||||
|
|
||||||
|
paths-ignore: |
||||||
|
- dist |
||||||
|
- NOTICE |
||||||
|
- LICENSE |
||||||
|
- DISCLAIMER |
||||||
|
- dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/ScriptRunner.java |
||||||
|
- mvnw.cmd |
||||||
|
- sql/soft_version |
||||||
|
- .mvn |
||||||
|
- .gitattributes |
||||||
|
- '**/licenses/**/LICENSE-*' |
||||||
|
- '**/*.md' |
||||||
|
- '**/*.json' |
||||||
|
- '**/*.iml' |
||||||
|
- '**/.babelrc' |
||||||
|
- '**/.eslintignore' |
||||||
|
- '**/.gitignore' |
||||||
|
- '**/LICENSE' |
||||||
|
- '**/NOTICE' |
||||||
|
|
||||||
|
comment: on-failure |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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> |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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"; |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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> |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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 = "/"; |
||||||
|
} |
@ -0,0 +1,153 @@ |
|||||||
|
/* |
||||||
|
* 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.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; |
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook; |
||||||
|
import org.apache.poi.ss.usermodel.CellStyle; |
||||||
|
import org.apache.poi.ss.usermodel.HorizontalAlignment; |
||||||
|
|
||||||
|
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; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* excel utils |
||||||
|
*/ |
||||||
|
public class ExcelUtils { |
||||||
|
|
||||||
|
public ExcelUtils() { |
||||||
|
throw new IllegalStateException("Utility class"); |
||||||
|
} |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ExcelUtils.class); |
||||||
|
|
||||||
|
/** |
||||||
|
* generate excel file |
||||||
|
* |
||||||
|
* @param content the content |
||||||
|
* @param title the title |
||||||
|
* @param xlsFilePath the xls path |
||||||
|
*/ |
||||||
|
public static void genExcelFile(String content, String title, String xlsFilePath) { |
||||||
|
List<LinkedHashMap> itemsList; |
||||||
|
|
||||||
|
//The JSONUtils.toList has been try catch ex
|
||||||
|
itemsList = JSONUtils.toList(content, LinkedHashMap.class); |
||||||
|
|
||||||
|
if (CollectionUtils.isEmpty(itemsList)) { |
||||||
|
logger.error("itemsList is null"); |
||||||
|
throw new RuntimeException("itemsList is null"); |
||||||
|
} |
||||||
|
|
||||||
|
LinkedHashMap<String, Object> headerMap = itemsList.get(0); |
||||||
|
|
||||||
|
List<String> headerList = new ArrayList<>(); |
||||||
|
|
||||||
|
Iterator<Map.Entry<String, Object>> iter = headerMap.entrySet().iterator(); |
||||||
|
while (iter.hasNext()) { |
||||||
|
Map.Entry<String, Object> en = iter.next(); |
||||||
|
headerList.add(en.getKey()); |
||||||
|
} |
||||||
|
|
||||||
|
HSSFWorkbook wb = null; |
||||||
|
FileOutputStream fos = null; |
||||||
|
try { |
||||||
|
// declare a workbook
|
||||||
|
wb = new HSSFWorkbook(); |
||||||
|
// generate a table
|
||||||
|
HSSFSheet sheet = wb.createSheet(); |
||||||
|
HSSFRow row = sheet.createRow(0); |
||||||
|
//set the height of the first line
|
||||||
|
row.setHeight((short) 500); |
||||||
|
|
||||||
|
//set Horizontal right
|
||||||
|
CellStyle cellStyle = wb.createCellStyle(); |
||||||
|
cellStyle.setAlignment(HorizontalAlignment.RIGHT); |
||||||
|
|
||||||
|
//setting excel headers
|
||||||
|
for (int i = 0; i < headerList.size(); i++) { |
||||||
|
HSSFCell cell = row.createCell(i); |
||||||
|
cell.setCellStyle(cellStyle); |
||||||
|
cell.setCellValue(headerList.get(i)); |
||||||
|
} |
||||||
|
|
||||||
|
//setting excel body
|
||||||
|
int rowIndex = 1; |
||||||
|
for (LinkedHashMap<String, Object> itemsMap : itemsList) { |
||||||
|
Object[] values = itemsMap.values().toArray(); |
||||||
|
row = sheet.createRow(rowIndex); |
||||||
|
//setting excel body height
|
||||||
|
row.setHeight((short) 500); |
||||||
|
rowIndex++; |
||||||
|
for (int j = 0; j < values.length; j++) { |
||||||
|
HSSFCell cell1 = row.createCell(j); |
||||||
|
cell1.setCellStyle(cellStyle); |
||||||
|
cell1.setCellValue(String.valueOf(values[j])); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < headerList.size(); i++) { |
||||||
|
sheet.setColumnWidth(i, headerList.get(i).length() * 800); |
||||||
|
} |
||||||
|
|
||||||
|
File file = new File(xlsFilePath); |
||||||
|
if (!file.exists()) { |
||||||
|
file.mkdirs(); |
||||||
|
} |
||||||
|
|
||||||
|
//setting file output
|
||||||
|
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) { |
||||||
|
try { |
||||||
|
wb.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
if (fos != null) { |
||||||
|
try { |
||||||
|
fos.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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"; |
||||||
|
|
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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> |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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"; |
||||||
|
|
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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> |
@ -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"); |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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"; |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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 |
@ -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> |
@ -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()); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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}"; |
||||||
|
} |
@ -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"; |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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> |
@ -1,55 +1,55 @@ |
|||||||
/* |
///*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
// * Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with |
// * contributor license agreements. See the NOTICE file distributed with
|
||||||
* this work for additional information regarding copyright ownership. |
// * this work for additional information regarding copyright ownership.
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
// * 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 not use this file except in compliance with
|
||||||
* the License. You may obtain a copy of the License at |
// * the License. You may obtain a copy of the License at
|
||||||
* |
// *
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||||
* |
// *
|
||||||
* Unless required by applicable law or agreed to in writing, software |
// * Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and |
// * See the License for the specific language governing permissions and
|
||||||
* limitations under the License. |
// * limitations under the License.
|
||||||
*/ |
// */
|
||||||
package org.apache.dolphinscheduler.alert.manager; |
//package org.apache.dolphinscheduler.alert.manager;
|
||||||
|
//
|
||||||
import org.apache.dolphinscheduler.alert.utils.MailUtils; |
//import org.apache.dolphinscheduler.alert.utils.MailUtils;
|
||||||
|
//
|
||||||
import java.util.List; |
//import java.util.List;
|
||||||
import java.util.Map; |
//import java.util.Map;
|
||||||
|
//
|
||||||
/** |
///**
|
||||||
* email send manager |
// * email send manager
|
||||||
*/ |
// */
|
||||||
public class EmailManager { |
//public class EmailManager {
|
||||||
/** |
// /**
|
||||||
* email send |
// * email send
|
||||||
* @param receiversList the receiver list |
// * @param receiversList the receiver list
|
||||||
* @param receiversCcList the cc List |
// * @param receiversCcList the cc List
|
||||||
* @param title the title |
// * @param title the title
|
||||||
* @param content the content |
// * @param content the content
|
||||||
* @param showType the showType |
// * @param showType the showType
|
||||||
* @return the send result |
// * @return the send result
|
||||||
*/ |
// */
|
||||||
public Map<String,Object> send(List<String> receiversList,List<String> receiversCcList,String title,String content,String showType){ |
// 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); |
// return MailUtils.sendMails(receiversList, receiversCcList, title, content, showType);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
/** |
// /**
|
||||||
* msg send |
// * msg send
|
||||||
* @param receiversList the receiver list |
// * @param receiversList the receiver list
|
||||||
* @param title the title |
// * @param title the title
|
||||||
* @param content the content |
// * @param content the content
|
||||||
* @param showType the showType |
// * @param showType the showType
|
||||||
* @return the send result |
// * @return the send result
|
||||||
*/ |
// */
|
||||||
public Map<String,Object> send(List<String> receiversList,String title,String content,String showType){ |
// public Map<String,Object> send(List<String> receiversList,String title,String content,String showType){
|
||||||
|
//
|
||||||
return MailUtils.sendMails(receiversList,title, content, showType); |
// return MailUtils.sendMails(receiversList,title, content, showType);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
} |
//}
|
||||||
|
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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('/', '.'); |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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.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 org.slf4j.Logger; |
|
||||||
import org.slf4j.LoggerFactory; |
|
||||||
|
|
||||||
import java.util.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* EmailAlertPlugin |
|
||||||
* |
|
||||||
* 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.isEnableDingTalk) { |
|
||||||
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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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())); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -1,136 +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 org.slf4j.Logger; |
|
||||||
import org.slf4j.LoggerFactory; |
|
||||||
|
|
||||||
import java.io.IOException; |
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
/** |
|
||||||
* 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 isEnableDingTalk = PropertyUtils.getBoolean(Constants.DINGTALK_ENABLE); |
|
||||||
private static final String dingTaskUrl = PropertyUtils.getString(Constants.DINGTALK_WEBHOOK); |
|
||||||
private static final String keyword = PropertyUtils.getString(Constants.DINGTALK_KEYWORD); |
|
||||||
private static final Boolean isEnableProxy = 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); |
|
||||||
|
|
||||||
/** |
|
||||||
* 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 (isEnableProxy) { |
|
||||||
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, resp); |
|
||||||
return resp; |
|
||||||
} finally { |
|
||||||
httpClient.close(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static HttpPost constructHttpPost(String msg, String charset) { |
|
||||||
HttpPost post = new HttpPost(dingTaskUrl); |
|
||||||
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)); |
|
||||||
CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(provider).build(); |
|
||||||
return httpClient; |
|
||||||
} |
|
||||||
|
|
||||||
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<String, Object>(); |
|
||||||
items.put("msgtype", "text"); |
|
||||||
Map<String, String> textContent = new HashMap<String, String>(); |
|
||||||
byte[] byt = StringUtils.getBytesUtf8(text); |
|
||||||
String txt = StringUtils.newStringUtf8(byt); |
|
||||||
textContent.put("content", txt); |
|
||||||
items.put("text", textContent); |
|
||||||
|
|
||||||
return JSONUtils.toJsonString(items); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,282 +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 agentIdRegExp = "\\{agentId}"; |
|
||||||
private static final String msgRegExp = "\\{msg}"; |
|
||||||
private static final String userRegExp = "\\{toUser}"; |
|
||||||
|
|
||||||
/** |
|
||||||
* 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(agentIdRegExp, agentId) |
|
||||||
.replaceAll(msgRegExp, 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(agentIdRegExp, agentId) |
|
||||||
.replaceAll(msgRegExp, 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("\\{toUser}", toUser) |
|
||||||
.replaceAll(agentIdRegExp, agentId) |
|
||||||
.replaceAll(msgRegExp, 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(userRegExp, listUser) |
|
||||||
.replaceAll(agentIdRegExp, agentId) |
|
||||||
.replaceAll(msgRegExp, 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; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,140 +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.CollectionUtils; |
|
||||||
import org.apache.poi.hssf.usermodel.HSSFCell; |
|
||||||
import org.apache.poi.hssf.usermodel.HSSFRow; |
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet; |
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; |
|
||||||
import org.apache.poi.ss.usermodel.CellStyle; |
|
||||||
import org.apache.poi.ss.usermodel.HorizontalAlignment; |
|
||||||
import org.slf4j.Logger; |
|
||||||
import org.slf4j.LoggerFactory; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.io.FileOutputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.util.*; |
|
||||||
import org.apache.dolphinscheduler.common.utils.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* excel utils |
|
||||||
*/ |
|
||||||
public class ExcelUtils { |
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ExcelUtils.class); |
|
||||||
/** |
|
||||||
* generate excel file |
|
||||||
* @param content the content |
|
||||||
* @param title the title |
|
||||||
* @param xlsFilePath the xls path |
|
||||||
*/ |
|
||||||
public static void genExcelFile(String content,String title,String xlsFilePath){ |
|
||||||
List<LinkedHashMap> itemsList; |
|
||||||
|
|
||||||
//The JSONUtils.toList has been try catch ex
|
|
||||||
itemsList = JSONUtils.toList(content, LinkedHashMap.class); |
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(itemsList)){ |
|
||||||
logger.error("itemsList is null"); |
|
||||||
throw new RuntimeException("itemsList is null"); |
|
||||||
} |
|
||||||
|
|
||||||
LinkedHashMap<String, Object> headerMap = itemsList.get(0); |
|
||||||
|
|
||||||
List<String> headerList = new ArrayList<>(); |
|
||||||
|
|
||||||
Iterator<Map.Entry<String, Object>> iter = headerMap.entrySet().iterator(); |
|
||||||
while (iter.hasNext()){ |
|
||||||
Map.Entry<String, Object> en = iter.next(); |
|
||||||
headerList.add(en.getKey()); |
|
||||||
} |
|
||||||
|
|
||||||
HSSFWorkbook wb = null; |
|
||||||
FileOutputStream fos = null; |
|
||||||
try { |
|
||||||
// declare a workbook
|
|
||||||
wb = new HSSFWorkbook(); |
|
||||||
// generate a table
|
|
||||||
HSSFSheet sheet = wb.createSheet(); |
|
||||||
HSSFRow row = sheet.createRow(0); |
|
||||||
//set the height of the first line
|
|
||||||
row.setHeight((short)500); |
|
||||||
|
|
||||||
//set Horizontal right
|
|
||||||
CellStyle cellStyle = wb.createCellStyle(); |
|
||||||
cellStyle.setAlignment(HorizontalAlignment.RIGHT); |
|
||||||
|
|
||||||
//setting excel headers
|
|
||||||
for (int i = 0; i < headerList.size(); i++) { |
|
||||||
HSSFCell cell = row.createCell(i); |
|
||||||
cell.setCellStyle(cellStyle); |
|
||||||
cell.setCellValue(headerList.get(i)); |
|
||||||
} |
|
||||||
|
|
||||||
//setting excel body
|
|
||||||
int rowIndex = 1; |
|
||||||
for (LinkedHashMap<String, Object> itemsMap : itemsList){ |
|
||||||
Object[] values = itemsMap.values().toArray(); |
|
||||||
row = sheet.createRow(rowIndex); |
|
||||||
//setting excel body height
|
|
||||||
row.setHeight((short)500); |
|
||||||
rowIndex++; |
|
||||||
for (int j = 0 ; j < values.length ; j++){ |
|
||||||
HSSFCell cell1 = row.createCell(j); |
|
||||||
cell1.setCellStyle(cellStyle); |
|
||||||
cell1.setCellValue(String.valueOf(values[j])); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
for (int i = 0; i < headerList.size(); i++) { |
|
||||||
sheet.setColumnWidth(i, headerList.get(i).length() * 800); |
|
||||||
} |
|
||||||
|
|
||||||
File file = new File(xlsFilePath); |
|
||||||
if (!file.exists()) { |
|
||||||
file.mkdirs(); |
|
||||||
} |
|
||||||
|
|
||||||
//setting file output
|
|
||||||
fos = new FileOutputStream(xlsFilePath + Constants.SINGLE_SLASH + title + Constants.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){ |
|
||||||
try { |
|
||||||
wb.close(); |
|
||||||
} catch (IOException e) { |
|
||||||
logger.error(e.getMessage(),e); |
|
||||||
} |
|
||||||
} |
|
||||||
if (fos != null){ |
|
||||||
try { |
|
||||||
fos.close(); |
|
||||||
} catch (IOException e) { |
|
||||||
logger.error(e.getMessage(),e); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,352 +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.commons.mail.EmailException; |
|
||||||
import org.apache.commons.mail.HtmlEmail; |
|
||||||
import org.apache.dolphinscheduler.common.utils.CollectionUtils; |
|
||||||
import org.apache.dolphinscheduler.common.utils.StringUtils; |
|
||||||
import org.slf4j.Logger; |
|
||||||
import org.slf4j.LoggerFactory; |
|
||||||
|
|
||||||
import javax.mail.*; |
|
||||||
import javax.mail.internet.*; |
|
||||||
import java.io.*; |
|
||||||
import java.util.*; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 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 xlsFilePath = 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"); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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 |
|
||||||
* @throws Exception |
|
||||||
*/ |
|
||||||
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 |
|
||||||
* @throws MessagingException |
|
||||||
*/ |
|
||||||
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 (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 |
|
||||||
* @throws MessagingException |
|
||||||
* @throws IOException |
|
||||||
*/ |
|
||||||
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(xlsFilePath + Constants.SINGLE_SLASH + title + Constants.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 + 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 |
|
||||||
* @throws EmailException |
|
||||||
*/ |
|
||||||
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()); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
@ -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(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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")); |
||||||
|
} |
||||||
|
} |
@ -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")); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue