You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2020/10/18 12:42:11 UTC

[shardingsphere-elasticjob] branch master updated: Polish elasticjob-error-handler-dingtalk module (#1571)

This is an automated email from the ASF dual-hosted git repository.

zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere-elasticjob.git


The following commit(s) were added to refs/heads/master by this push:
     new 3a795fc  Polish elasticjob-error-handler-dingtalk module (#1571)
3a795fc is described below

commit 3a795fc39a997a6634f60d5ce6872228bfb82d0e
Author: Liang Zhang <te...@163.com>
AuthorDate: Sun Oct 18 20:42:02 2020 +0800

    Polish elasticjob-error-handler-dingtalk module (#1571)
---
 .../handler/dingtalk/DingtalkConfiguration.java    |  23 ++---
 .../handler/dingtalk/DingtalkJobErrorHandler.java  | 112 ++++++++++-----------
 ...tants.java => DingtalkPropertiesConstants.java} |  18 ++--
 .../dingtalk/DingtalkJobErrorHandlerTest.java      |  70 ++++++-------
 .../src/test/resources/logback-test.xml            |  38 +++++++
 .../error/handler/JobErrorHandlerFactory.java      |   2 +-
 .../elasticjob/lite/example/JavaMain.java          |  64 ++++++------
 .../main/resources/META-INF/applicationContext.xml |  31 +++---
 8 files changed, 189 insertions(+), 169 deletions(-)

diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkConfiguration.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkConfiguration.java
index 64ef17c..cefa409 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkConfiguration.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkConfiguration.java
@@ -18,7 +18,6 @@
 package org.apache.shardingsphere.elasticjob.error.handler.dingtalk;
 
 import lombok.Getter;
-import lombok.RequiredArgsConstructor;
 
 import java.util.Properties;
 
@@ -26,7 +25,6 @@ import java.util.Properties;
  * Dingtalk configuration.
  */
 @Getter
-@RequiredArgsConstructor
 public final class DingtalkConfiguration {
     
     private final String webhook;
@@ -35,20 +33,15 @@ public final class DingtalkConfiguration {
     
     private final String secret;
     
-    private final Integer connectTimeout;
+    private final int connectTimeoutMillisecond;
     
-    private final Integer readTimeout;
+    private final int readTimeoutMillisecond;
     
-    /**
-     * Get dingtalk configuration.
-     *
-     * @param props properties
-     * @return dingtalk configuration.
-     */
-    public static DingtalkConfiguration getByProps(final Properties props) {
-        return new DingtalkConfiguration(props.getProperty(DingtalkConstants.DINGTALK_WEBHOOK),
-                props.getProperty(DingtalkConstants.DINGTALK_KEYWORD), props.getProperty(DingtalkConstants.DINGTALK_SECRET),
-                Integer.valueOf(props.getOrDefault(DingtalkConstants.DINGTALK_CONNECT_TIMEOUT, DingtalkConstants.DEFAULT_DINGTALK_CONNECT_TIMEOUT).toString()),
-                Integer.valueOf(props.getOrDefault(DingtalkConstants.DINGTALK_READ_TIMEOUT, DingtalkConstants.DEFAULT_DINGTALK_READ_TIMEOUT).toString()));
+    public DingtalkConfiguration(final Properties props) {
+        webhook = props.getProperty(DingtalkPropertiesConstants.WEBHOOK);
+        keyword = props.getProperty(DingtalkPropertiesConstants.KEYWORD);
+        secret = props.getProperty(DingtalkPropertiesConstants.SECRET);
+        connectTimeoutMillisecond = Integer.parseInt(props.getProperty(DingtalkPropertiesConstants.CONNECT_TIMEOUT_MILLISECOND, DingtalkPropertiesConstants.DEFAULT_CONNECT_TIMEOUT_MILLISECOND));
+        readTimeoutMillisecond = Integer.parseInt(props.getProperty(DingtalkPropertiesConstants.READ_TIMEOUT_MILLISECOND, DingtalkPropertiesConstants.DEFAULT_READ_TIMEOUT_MILLISECOND));
     }
 }
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkJobErrorHandler.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkJobErrorHandler.java
index bcebd00..e094967 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkJobErrorHandler.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkJobErrorHandler.java
@@ -31,7 +31,6 @@ import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
 import org.apache.shardingsphere.elasticjob.api.JobConfiguration;
 import org.apache.shardingsphere.elasticjob.error.handler.JobErrorHandler;
-import org.apache.shardingsphere.elasticjob.infra.exception.JobConfigurationException;
 import org.apache.shardingsphere.elasticjob.infra.json.GsonFactory;
 
 import javax.crypto.Mac;
@@ -49,7 +48,7 @@ import java.util.Base64;
 import java.util.Collections;
 
 /**
- * Job error handler for dingtalk error message.
+ * Job error handler for send error message via dingtalk.
  */
 @Slf4j
 public final class DingtalkJobErrorHandler implements JobErrorHandler {
@@ -60,90 +59,85 @@ public final class DingtalkJobErrorHandler implements JobErrorHandler {
         registerShutdownHook();
     }
     
+    private void registerShutdownHook() {
+        Runtime.getRuntime().addShutdownHook(new Thread("DingtalkJobErrorHandler Shutdown-Hook") {
+            
+            @SneakyThrows
+            @Override
+            public void run() {
+                log.info("Shutting down HTTP client...");
+                httpclient.close();
+            }
+        });
+    }
+    
     @Override
     public void handleException(final JobConfiguration jobConfig, final Throwable cause) {
-        DingtalkConfiguration dingtalkConfiguration = DingtalkConfiguration.getByProps(jobConfig.getProps());
-        HttpPost httpPost = new HttpPost(getUrl(dingtalkConfiguration));
-        RequestConfig requestConfig = RequestConfig.custom()
-                .setConnectTimeout(dingtalkConfiguration.getConnectTimeout())
-                .setSocketTimeout(dingtalkConfiguration.getReadTimeout()).build();
-        httpPost.setConfig(requestConfig);
-        String paramJson = getParamJson(getMessage(jobConfig.getJobName(), dingtalkConfiguration, cause));
-        StringEntity entity = new StringEntity(paramJson, StandardCharsets.UTF_8);
-        entity.setContentEncoding(StandardCharsets.UTF_8.name());
-        entity.setContentType("application/json");
-        httpPost.setEntity(entity);
+        DingtalkConfiguration dingtalkConfig = new DingtalkConfiguration(jobConfig.getProps());
+        HttpPost httpPost = createHTTPPostMethod(jobConfig.getJobName(), cause, dingtalkConfig);
         try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
             int status = response.getStatusLine().getStatusCode();
             if (HttpURLConnection.HTTP_OK == status) {
-                JsonObject resp = GsonFactory.getGson().fromJson(EntityUtils.toString(response.getEntity()), JsonObject.class);
-                if (!"0".equals(resp.get("errcode").getAsString())) {
-                    log.error("An exception has occurred in Job '{}', But failed to send alert by Dingtalk because of: {}", jobConfig.getJobName(), resp.get("errmsg").getAsString(), cause);
+                JsonObject responseMessage = GsonFactory.getGson().fromJson(EntityUtils.toString(response.getEntity()), JsonObject.class);
+                if (!"0".equals(responseMessage.get("errcode").getAsString())) {
+                    log.info("An exception has occurred in Job '{}', But failed to send alert by Dingtalk because of: {}", jobConfig.getJobName(), responseMessage.get("errmsg").getAsString(), cause);
                 } else {
-                    log.error("An exception has occurred in Job '{}', Notification to Dingtalk was successful.", jobConfig.getJobName(), cause);
+                    log.info("An exception has occurred in Job '{}', Notification to Dingtalk was successful.", jobConfig.getJobName(), cause);
                 }
             } else {
                 log.error("An exception has occurred in Job '{}', But failed to send alert by Dingtalk because of: Unexpected response status: {}", jobConfig.getJobName(), status, cause);
             }
-        } catch (IOException ex) {
+        } catch (final IOException ex) {
             cause.addSuppressed(ex);
             log.error("An exception has occurred in Job '{}', But failed to send alert by Dingtalk because of", jobConfig.getJobName(), cause);
         }
     }
     
-    private String getParamJson(final String msg) {
-        return GsonFactory.getGson().toJson(ImmutableMap.of("msgtype", "text", "text", Collections.singletonMap("content", msg)));
-    }
-    
-    private String getMessage(final String jobName, final DingtalkConfiguration dingtalkConfiguration, final Throwable cause) {
-        StringWriter sw = new StringWriter();
-        cause.printStackTrace(new PrintWriter(sw, true));
-        String result = String.format("Job '%s' exception occur in job processing, caused by %s", jobName, sw.toString());
-        if (!Strings.isNullOrEmpty(dingtalkConfiguration.getKeyword())) {
-            result = dingtalkConfiguration.getKeyword().concat(result);
-        }
+    private HttpPost createHTTPPostMethod(final String jobName, final Throwable cause, final DingtalkConfiguration dingtalkConfig) {
+        HttpPost result = new HttpPost(getURL(dingtalkConfig));
+        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(dingtalkConfig.getConnectTimeoutMillisecond()).setSocketTimeout(dingtalkConfig.getReadTimeoutMillisecond()).build();
+        result.setConfig(requestConfig);
+        StringEntity entity = new StringEntity(getJsonParameter(getErrorMessage(jobName, dingtalkConfig, cause)), StandardCharsets.UTF_8);
+        entity.setContentEncoding(StandardCharsets.UTF_8.name());
+        entity.setContentType("application/json");
+        result.setEntity(entity);
         return result;
     }
     
-    private String getUrl(final DingtalkConfiguration dingtalkConfiguration) {
-        if (Strings.isNullOrEmpty(dingtalkConfiguration.getSecret())) {
-            return dingtalkConfiguration.getWebhook();
-        } else {
-            return getSignUrl(dingtalkConfiguration);
-        }
+    private String getURL(final DingtalkConfiguration dingtalkConfig) {
+        return Strings.isNullOrEmpty(dingtalkConfig.getSecret()) ? dingtalkConfig.getWebhook() : getSignedURL(dingtalkConfig);
     }
     
-    private String getSignUrl(final DingtalkConfiguration dingtalkConfiguration) {
-        try {
-            Long timestamp = System.currentTimeMillis();
-            return String.format("%s&timestamp=%s&sign=%s", dingtalkConfiguration.getWebhook(), timestamp, sign(timestamp, dingtalkConfiguration));
-        } catch (NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeyException ex) {
-            throw new JobConfigurationException(ex);
-        }
+    private String getSignedURL(final DingtalkConfiguration dingtalkConfig) {
+        long timestamp = System.currentTimeMillis();
+        return String.format("%s&timestamp=%s&sign=%s", dingtalkConfig.getWebhook(), timestamp, generateSignature(timestamp, dingtalkConfig.getSecret()));
     }
     
-    private String sign(final Long timestamp, final DingtalkConfiguration dingtalkConfiguration) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
-        String stringToSign = timestamp + "\n" + dingtalkConfiguration.getSecret();
-        Mac mac = Mac.getInstance("HmacSHA256");
-        mac.init(new SecretKeySpec(dingtalkConfiguration.getSecret().getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
-        byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
+    @SneakyThrows({NoSuchAlgorithmException.class, UnsupportedEncodingException.class, InvalidKeyException.class})
+    private String generateSignature(final long timestamp, final String secret) {
+        String algorithmName = "HmacSHA256";
+        Mac mac = Mac.getInstance(algorithmName);
+        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), algorithmName));
+        byte[] signData = mac.doFinal((timestamp + "\n" + secret).getBytes(StandardCharsets.UTF_8));
         return URLEncoder.encode(new String(Base64.getEncoder().encode(signData)), StandardCharsets.UTF_8.name());
     }
     
+    private String getJsonParameter(final String message) {
+        return GsonFactory.getGson().toJson(ImmutableMap.of("msgtype", "text", "text", Collections.singletonMap("content", message)));
+    }
+    
+    private String getErrorMessage(final String jobName, final DingtalkConfiguration dingtalkConfig, final Throwable cause) {
+        StringWriter writer = new StringWriter();
+        cause.printStackTrace(new PrintWriter(writer, true));
+        String result = String.format("Job '%s' exception occur in job processing, caused by %s", jobName, writer.toString());
+        if (!Strings.isNullOrEmpty(dingtalkConfig.getKeyword())) {
+            result = dingtalkConfig.getKeyword().concat(result);
+        }
+        return result;
+    }
+    
     @Override
     public String getType() {
         return "DINGTALK";
     }
-    
-    private void registerShutdownHook() {
-        Thread t = new Thread("DingtalkJobErrorHandler Shutdown-Hook") {
-            @SneakyThrows
-            @Override
-            public void run() {
-                log.info("Shutting down httpclient...");
-                httpclient.close();
-            }
-        };
-        Runtime.getRuntime().addShutdownHook(t);
-    }
 }
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkConstants.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkPropertiesConstants.java
similarity index 61%
rename from elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkConstants.java
rename to elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkPropertiesConstants.java
index 9cc9b74..9a2f0a5 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkConstants.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkPropertiesConstants.java
@@ -18,23 +18,23 @@
 package org.apache.shardingsphere.elasticjob.error.handler.dingtalk;
 
 /**
- * Dingtalk constants.
+ * Dingtalk properties constants.
  */
-public final class DingtalkConstants {
+public final class DingtalkPropertiesConstants {
     
     public static final String PREFIX = "dingtalk.";
     
-    public static final String DINGTALK_WEBHOOK = PREFIX + "webhook";
+    public static final String WEBHOOK = PREFIX + "webhook";
     
-    public static final String DINGTALK_KEYWORD = PREFIX + "keyword";
+    public static final String KEYWORD = PREFIX + "keyword";
     
-    public static final String DINGTALK_SECRET = PREFIX + "secret";
+    public static final String SECRET = PREFIX + "secret";
     
-    public static final String DINGTALK_CONNECT_TIMEOUT = PREFIX + "connectTimeout";
+    public static final String CONNECT_TIMEOUT_MILLISECOND = PREFIX + "connectTimeoutMillisecond";
     
-    public static final String DINGTALK_READ_TIMEOUT = PREFIX + "readTimeout";
+    public static final String READ_TIMEOUT_MILLISECOND = PREFIX + "readTimeoutMillisecond";
     
-    public static final Integer DEFAULT_DINGTALK_CONNECT_TIMEOUT = 3000;
+    public static final String DEFAULT_CONNECT_TIMEOUT_MILLISECOND = "3000";
     
-    public static final Integer DEFAULT_DINGTALK_READ_TIMEOUT = 5000;
+    public static final String DEFAULT_READ_TIMEOUT_MILLISECOND = "5000";
 }
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkJobErrorHandlerTest.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkJobErrorHandlerTest.java
index 3a9301b..4342f47 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkJobErrorHandlerTest.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/dingtalk/DingtalkJobErrorHandlerTest.java
@@ -36,8 +36,6 @@ import org.slf4j.Logger;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.verify;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -54,20 +52,27 @@ public final class DingtalkJobErrorHandlerTest {
     
     @BeforeClass
     public static void init() {
-        NettyRestfulServiceConfiguration configuration = new NettyRestfulServiceConfiguration(PORT);
-        configuration.setHost(HOST);
-        configuration.addControllerInstance(new DingtalkInternalController());
-        restfulService = new NettyRestfulService(configuration);
+        NettyRestfulServiceConfiguration config = new NettyRestfulServiceConfiguration(PORT);
+        config.setHost(HOST);
+        config.addControllerInstance(new DingtalkInternalController());
+        restfulService = new NettyRestfulService(config);
         restfulService.startup();
     }
     
+    @AfterClass
+    public static void close() {
+        if (null != restfulService) {
+            restfulService.shutdown();
+        }
+    }
+    
     @Test
     public void assertHandleExceptionWithNotifySuccessful() {
         DingtalkJobErrorHandler actual = getDingtalkJobErrorHandler();
         setStaticFieldValue(actual);
         Throwable cause = new RuntimeException("test");
         actual.handleException(getJobConfiguration("http://localhost:9875/send?access_token=42eead064e81ce81fc6af2c107fbe10a4339a3d40a7db8abf5b34d8261527a3f"), cause);
-        verify(log).error("An exception has occurred in Job '{}', Notification to Dingtalk was successful.", "test_job", cause);
+        verify(log).info("An exception has occurred in Job '{}', Notification to Dingtalk was successful.", "test_job", cause);
     }
     
     @Test
@@ -76,7 +81,7 @@ public final class DingtalkJobErrorHandlerTest {
         setStaticFieldValue(actual);
         Throwable cause = new RuntimeException("test");
         actual.handleException(getJobConfiguration("http://localhost:9875/send?access_token=wrongToken"), cause);
-        verify(log).error("An exception has occurred in Job '{}', But failed to send alert by Dingtalk because of: {}", "test_job", "token is not exist", cause);
+        verify(log).info("An exception has occurred in Job '{}', But failed to send alert by Dingtalk because of: {}", "test_job", "token is not exist", cause);
     }
     
     @Test
@@ -103,26 +108,11 @@ public final class DingtalkJobErrorHandlerTest {
         setStaticFieldValue(actual);
         Throwable cause = new RuntimeException("test");
         actual.handleException(getNoSignJobConfiguration("http://localhost:9875/send?access_token=42eead064e81ce81fc6af2c107fbe10a4339a3d40a7db8abf5b34d8261527a3f"), cause);
-        verify(log).error("An exception has occurred in Job '{}', Notification to Dingtalk was successful.", "test_job", cause);
+        verify(log).info("An exception has occurred in Job '{}', Notification to Dingtalk was successful.", "test_job", cause);
     }
     
-    private JobConfiguration getJobConfiguration(final String webhook) {
-        return JobConfiguration.newBuilder("test_job", 3)
-                .setProperty(DingtalkConstants.DINGTALK_WEBHOOK, webhook)
-                .setProperty(DingtalkConstants.DINGTALK_KEYWORD, "keyword")
-                .setProperty(DingtalkConstants.DINGTALK_SECRET, "SEC0b0a6b13b6823b95737dd83491c23adee5d8a7a649899a12217e038eddc84ff4")
-                .setProperty(DingtalkConstants.DINGTALK_CONNECT_TIMEOUT, "4000")
-                .setProperty(DingtalkConstants.DINGTALK_READ_TIMEOUT, "6000")
-                .build();
-    }
-    
-    private JobConfiguration getNoSignJobConfiguration(final String webhook) {
-        return JobConfiguration.newBuilder("test_job", 3)
-                .setProperty(DingtalkConstants.DINGTALK_WEBHOOK, webhook)
-                .setProperty(DingtalkConstants.DINGTALK_KEYWORD, "keyword")
-                .setProperty(DingtalkConstants.DINGTALK_CONNECT_TIMEOUT, "4000")
-                .setProperty(DingtalkConstants.DINGTALK_READ_TIMEOUT, "6000")
-                .build();
+    private DingtalkJobErrorHandler getDingtalkJobErrorHandler() {
+        return (DingtalkJobErrorHandler) JobErrorHandlerFactory.createHandler("DINGTALK").orElseThrow(() -> new JobConfigurationException("DINGTALK error handler not found."));
     }
     
     @SneakyThrows
@@ -135,20 +125,22 @@ public final class DingtalkJobErrorHandlerTest {
         field.set(dingtalkJobErrorHandler, log);
     }
     
-    @Test
-    public void assertGetType() {
-        DingtalkJobErrorHandler actual = new DingtalkJobErrorHandler();
-        assertThat(actual.getType(), is("DINGTALK"));
-    }
-    
-    @AfterClass
-    public static void close() {
-        if (null != restfulService) {
-            restfulService.shutdown();
-        }
+    private JobConfiguration getJobConfiguration(final String webhook) {
+        return JobConfiguration.newBuilder("test_job", 3)
+                .setProperty(DingtalkPropertiesConstants.WEBHOOK, webhook)
+                .setProperty(DingtalkPropertiesConstants.KEYWORD, "keyword")
+                .setProperty(DingtalkPropertiesConstants.SECRET, "SEC0b0a6b13b6823b95737dd83491c23adee5d8a7a649899a12217e038eddc84ff4")
+                .setProperty(DingtalkPropertiesConstants.CONNECT_TIMEOUT_MILLISECOND, "4000")
+                .setProperty(DingtalkPropertiesConstants.READ_TIMEOUT_MILLISECOND, "6000")
+                .build();
     }
     
-    private DingtalkJobErrorHandler getDingtalkJobErrorHandler() {
-        return (DingtalkJobErrorHandler) JobErrorHandlerFactory.createHandler("DINGTALK").orElseThrow(() -> new JobConfigurationException("DINGTALK error handler not found."));
+    private JobConfiguration getNoSignJobConfiguration(final String webhook) {
+        return JobConfiguration.newBuilder("test_job", 3)
+                .setProperty(DingtalkPropertiesConstants.WEBHOOK, webhook)
+                .setProperty(DingtalkPropertiesConstants.KEYWORD, "keyword")
+                .setProperty(DingtalkPropertiesConstants.CONNECT_TIMEOUT_MILLISECOND, "4000")
+                .setProperty(DingtalkPropertiesConstants.READ_TIMEOUT_MILLISECOND, "6000")
+                .build();
     }
 }
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/test/resources/logback-test.xml b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..0af2535
--- /dev/null
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-dingtalk/src/test/resources/logback-test.xml
@@ -0,0 +1,38 @@
+<?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.
+  -->
+
+<configuration>
+    <property name="log.context.name" value="elasticjob-lite-core-test" />
+    <property name="log.charset" value="UTF-8" />
+    <property name="log.pattern" value="[%-5level] %date --%thread-- [%logger] %msg %n" />
+    
+    <contextName>${log.context.name}</contextName>
+    
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>ERROR</level>
+        </filter>
+        <encoder charset="${log.charset}">
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+    
+    <root>
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-general/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/JobErrorHandlerFactory.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-general/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/JobErrorHandlerFactory.java
index 5e90e27..b58699c 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-general/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/JobErrorHandlerFactory.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-general/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/JobErrorHandlerFactory.java
@@ -50,6 +50,6 @@ public final class JobErrorHandlerFactory {
     }
     
     private static Optional<JobErrorHandler> newHandlerInstance(final String type) {
-        return Optional.ofNullable(ElasticJobServiceLoader.newTypedServiceInstance(JobErrorHandler.class, type));
+        return Optional.of(ElasticJobServiceLoader.newTypedServiceInstance(JobErrorHandler.class, type));
     }
 }
diff --git a/examples/elasticjob-example-lite-java/src/main/java/org/apache/shardingsphere/elasticjob/lite/example/JavaMain.java b/examples/elasticjob-example-lite-java/src/main/java/org/apache/shardingsphere/elasticjob/lite/example/JavaMain.java
index f4e5857..f177a18 100644
--- a/examples/elasticjob-example-lite-java/src/main/java/org/apache/shardingsphere/elasticjob/lite/example/JavaMain.java
+++ b/examples/elasticjob-example-lite-java/src/main/java/org/apache/shardingsphere/elasticjob/lite/example/JavaMain.java
@@ -18,7 +18,7 @@
 package org.apache.shardingsphere.elasticjob.lite.example;
 
 import org.apache.commons.dbcp.BasicDataSource;
-import org.apache.shardingsphere.elasticjob.error.handler.dingtalk.DingtalkConstants;
+import org.apache.shardingsphere.elasticjob.error.handler.dingtalk.DingtalkPropertiesConstants;
 import org.apache.shardingsphere.elasticjob.error.handler.email.EmailConstants;
 import org.apache.shardingsphere.elasticjob.error.handler.wechat.WechatConstants;
 import org.apache.shardingsphere.elasticjob.http.props.HttpJobProperties;
@@ -124,52 +124,52 @@ public final class JavaMain {
     }
     
     private static void setUpOneOffJobWithDingtalk(final CoordinatorRegistryCenter regCenter, final TracingConfiguration<DataSource> tracingConfig) {
-        JobConfiguration jobConfiguration= JobConfiguration.newBuilder("javaOccurErrorOfDingtalkJob", 3)
+        JobConfiguration jobConfig = JobConfiguration.newBuilder("javaOccurErrorOfDingtalkJob", 3)
                 .shardingItemParameters("0=Beijing,1=Shanghai,2=Guangzhou").jobErrorHandlerType("DINGTALK").build();
-        setDingtalkConfig(jobConfiguration);
-        new OneOffJobBootstrap(regCenter, new JavaOccurErrorJob(), jobConfiguration, tracingConfig).execute();
+        setDingtalkConfiguration(jobConfig);
+        new OneOffJobBootstrap(regCenter, new JavaOccurErrorJob(), jobConfig, tracingConfig).execute();
     }
     
     private static void setUpOneOffJobWithWechat(final CoordinatorRegistryCenter regCenter, final TracingConfiguration<DataSource> tracingConfig) {
-        JobConfiguration jobConfiguration= JobConfiguration.newBuilder("javaOccurErrorOfWechatJob", 3)
+        JobConfiguration jobConfig = JobConfiguration.newBuilder("javaOccurErrorOfWechatJob", 3)
                 .shardingItemParameters("0=Beijing,1=Shanghai,2=Guangzhou").jobErrorHandlerType("WECHAT").build();
-        setWechatConfig(jobConfiguration);
-        new OneOffJobBootstrap(regCenter, new JavaOccurErrorJob(), jobConfiguration, tracingConfig).execute();
+        setWechatConfiguration(jobConfig);
+        new OneOffJobBootstrap(regCenter, new JavaOccurErrorJob(), jobConfig, tracingConfig).execute();
     }
     
     private static void setUpOneOffJobWithEmail(final CoordinatorRegistryCenter regCenter, final TracingConfiguration<DataSource> tracingConfig) {
-        JobConfiguration jobConfiguration= JobConfiguration.newBuilder("javaOccurErrorOfEmailJob", 3)
+        JobConfiguration jobConfig = JobConfiguration.newBuilder("javaOccurErrorOfEmailJob", 3)
                 .shardingItemParameters("0=Beijing,1=Shanghai,2=Guangzhou").jobErrorHandlerType("EMAIL").build();
-        setEmailConfig(jobConfiguration);
-        new OneOffJobBootstrap(regCenter, new JavaOccurErrorJob(), jobConfiguration, tracingConfig).execute();
+        setEmailConfiguration(jobConfig);
+        new OneOffJobBootstrap(regCenter, new JavaOccurErrorJob(), jobConfig, tracingConfig).execute();
     }
     
-    private static void setDingtalkConfig(JobConfiguration jobConfiguration) {
-        jobConfiguration.getProps().setProperty(DingtalkConstants.DINGTALK_WEBHOOK, "https://oapi.dingtalk.com/robot/send?access_token=42eead064e81ce81fc6af2c107fbe10a4339a3d40a7db8abf5b34d8261527a3f");
-        jobConfiguration.getProps().setProperty(DingtalkConstants.DINGTALK_KEYWORD, "keyword");
-        jobConfiguration.getProps().setProperty(DingtalkConstants.DINGTALK_SECRET, "SEC0b0a6b13b6823b95737dd83491c23adee5d8a7a649899a12217e038eddc84ff4");
-        jobConfiguration.getProps().setProperty(DingtalkConstants.DINGTALK_CONNECT_TIMEOUT, "7000");
-        jobConfiguration.getProps().setProperty(DingtalkConstants.DINGTALK_READ_TIMEOUT, "8000");
+    private static void setDingtalkConfiguration(final JobConfiguration jobConfig) {
+        jobConfig.getProps().setProperty(DingtalkPropertiesConstants.WEBHOOK, "https://oapi.dingtalk.com/robot/send?access_token=42eead064e81ce81fc6af2c107fbe10a4339a3d40a7db8abf5b34d8261527a3f");
+        jobConfig.getProps().setProperty(DingtalkPropertiesConstants.KEYWORD, "keyword");
+        jobConfig.getProps().setProperty(DingtalkPropertiesConstants.SECRET, "SEC0b0a6b13b6823b95737dd83491c23adee5d8a7a649899a12217e038eddc84ff4");
+        jobConfig.getProps().setProperty(DingtalkPropertiesConstants.CONNECT_TIMEOUT_MILLISECOND, "7000");
+        jobConfig.getProps().setProperty(DingtalkPropertiesConstants.READ_TIMEOUT_MILLISECOND, "8000");
     }
     
-    private static void setWechatConfig(JobConfiguration jobConfiguration) {
-        jobConfiguration.getProps().setProperty(WechatConstants.WECHAT_WEBHOOK, "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=5308e20a-2900-484b-a332-b5bb701ade04");
-        jobConfiguration.getProps().setProperty(WechatConstants.WECHAT_CONNECT_TIMEOUT, "9000");
-        jobConfiguration.getProps().setProperty(WechatConstants.WECHAT_READ_TIMEOUT, "5000");
+    private static void setWechatConfiguration(final JobConfiguration jobConfig) {
+        jobConfig.getProps().setProperty(WechatConstants.WECHAT_WEBHOOK, "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=5308e20a-2900-484b-a332-b5bb701ade04");
+        jobConfig.getProps().setProperty(WechatConstants.WECHAT_CONNECT_TIMEOUT, "9000");
+        jobConfig.getProps().setProperty(WechatConstants.WECHAT_READ_TIMEOUT, "5000");
     }
     
-    private static void setEmailConfig(JobConfiguration jobConfiguration) {
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_HOST, "host");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_PORT, "465");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_USERNAME, "username");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_PASSWORD, "password");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_USE_SSL, "true");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_SUBJECT, "Test elasticJob error message");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_FROM, "from@xxx.com");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_TO, "to1@xxx.com,to2xxx.com");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_CC, "cc@xxx.com");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_BCC, "bcc@xxx.com");
-        jobConfiguration.getProps().setProperty(EmailConstants.EMAIL_DEBUG, "false");
+    private static void setEmailConfiguration(final JobConfiguration jobConfig) {
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_HOST, "host");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_PORT, "465");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_USERNAME, "username");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_PASSWORD, "password");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_USE_SSL, "true");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_SUBJECT, "Test elasticJob error message");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_FROM, "from@xxx.com");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_TO, "to1@xxx.com,to2xxx.com");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_CC, "cc@xxx.com");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_BCC, "bcc@xxx.com");
+        jobConfig.getProps().setProperty(EmailConstants.EMAIL_DEBUG, "false");
     }
     
     private static String buildScriptCommandLine() throws IOException {
diff --git a/examples/elasticjob-example-lite-spring/src/main/resources/META-INF/applicationContext.xml b/examples/elasticjob-example-lite-spring/src/main/resources/META-INF/applicationContext.xml
index 2c6add8..a765879 100644
--- a/examples/elasticjob-example-lite-spring/src/main/resources/META-INF/applicationContext.xml
+++ b/examples/elasticjob-example-lite-spring/src/main/resources/META-INF/applicationContext.xml
@@ -31,9 +31,9 @@
     <context:property-placeholder location="classpath:conf/*.properties" />
     
     <elasticjob:zookeeper id="regCenter" server-lists="${serverLists}" namespace="${namespace}" base-sleep-time-milliseconds="${baseSleepTimeMilliseconds}" max-sleep-time-milliseconds="${maxSleepTimeMilliseconds}" max-retries="${maxRetries}" />
-
+    
     <elasticjob:snapshot id="jobSnapshot" registry-center-ref="regCenter" dump-port="9999"/>
-
+    
     <bean id="elasticJobTracingDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
         <property name="driverClassName" value="${event.rdb.driver}" />
         <property name="url" value="${event.rdb.url}" />
@@ -65,26 +65,28 @@
             <!--<prop key="script.command.line">${script.scriptCommandLine}</prop>-->
         <!--</props>-->
     <!--</elasticjob:job>-->
-
+    
     <bean id="occurErrorNoticeDingtlakJob" class="org.apache.shardingsphere.elasticjob.lite.example.job.simple.JavaOccurErrorJob" />
-
+    
     <elasticjob:job id="${occurErrorNoticeDingtlakJob.id}" job-ref="occurErrorNoticeDingtlakJob" registry-center-ref="regCenter" tracing-ref="elasticJobTrace"
-                    sharding-total-count="${occurErrorNoticeDingtlakJob.shardingTotalCount}" cron="${occurErrorNoticeDingtlakJob.cron}" sharding-item-parameters="${occurErrorNoticeDingtlakJob.shardingItemParameters}" monitor-execution="${occurErrorNoticeDingtlakJob.monitorExecution}" failover="${occurErrorNoticeDingtlakJob.failover}" description="${occurErrorNoticeDingtlakJob.description}"
+                    sharding-total-count="${occurErrorNoticeDingtlakJob.shardingTotalCount}" cron="${occurErrorNoticeDingtlakJob.cron}" sharding-item-parameters="${occurErrorNoticeDingtlakJob.shardingItemParameters}" 
+                    monitor-execution="${occurErrorNoticeDingtlakJob.monitorExecution}" failover="${occurErrorNoticeDingtlakJob.failover}" description="${occurErrorNoticeDingtlakJob.description}"
                     job-error-handler-type="${occurErrorNoticeDingtlakJob.jobErrorHandlerType}"
                     disabled="${occurErrorNoticeDingtlakJob.disabled}" overwrite="${occurErrorNoticeDingtlakJob.overwrite}" >
         <props>
             <prop key="dingtalk.webhook">https://oapi.dingtalk.com/robot/send?access_token=42eead064e81ce81fc6af2c107fbe10a4339a3d40a7db8abf5b34d8261527a3f</prop>
             <prop key="dingtalk.keyword">keyword</prop>
             <prop key="dingtalk.secret">SEC0b0a6b13b6823b95737dd83491c23adee5d8a7a649899a12217e038eddc84ff4</prop>
-            <prop key="dingtalk.connectTimeout">3000</prop>
-            <prop key="dingtalk.readTimeout">5000</prop>
+            <prop key="dingtalk.connectTimeoutMillisecond">3000</prop>
+            <prop key="dingtalk.readTimeoutMillisecond">5000</prop>
         </props>
     </elasticjob:job>
-
+    
     <bean id="occurErrorNoticeWechatJob" class="org.apache.shardingsphere.elasticjob.lite.example.job.simple.JavaOccurErrorJob" />
-
+    
     <elasticjob:job id="${occurErrorNoticeWechatJob.id}" job-ref="occurErrorNoticeWechatJob" registry-center-ref="regCenter" tracing-ref="elasticJobTrace"
-                    sharding-total-count="${occurErrorNoticeWechatJob.shardingTotalCount}" cron="${occurErrorNoticeWechatJob.cron}" sharding-item-parameters="${occurErrorNoticeWechatJob.shardingItemParameters}" monitor-execution="${occurErrorNoticeWechatJob.monitorExecution}" failover="${occurErrorNoticeWechatJob.failover}" description="${occurErrorNoticeWechatJob.description}"
+                    sharding-total-count="${occurErrorNoticeWechatJob.shardingTotalCount}" cron="${occurErrorNoticeWechatJob.cron}" sharding-item-parameters="${occurErrorNoticeWechatJob.shardingItemParameters}" 
+                    monitor-execution="${occurErrorNoticeWechatJob.monitorExecution}" failover="${occurErrorNoticeWechatJob.failover}" description="${occurErrorNoticeWechatJob.description}"
                     job-error-handler-type="${occurErrorNoticeWechatJob.jobErrorHandlerType}"
                     disabled="${occurErrorNoticeWechatJob.disabled}" overwrite="${occurErrorNoticeWechatJob.overwrite}" >
         <props>
@@ -93,11 +95,12 @@
             <prop key="wechat.readTimeout">5000</prop>
         </props>
     </elasticjob:job>
-
-<!--    <bean id="occurErrorNoticeEmailJob" class="org.apache.shardingsphere.elasticjob.lite.example.job.simple.JavaOccurErrorJob" />-->
-
+    
+    <bean id="occurErrorNoticeEmailJob" class="org.apache.shardingsphere.elasticjob.lite.example.job.simple.JavaOccurErrorJob" />
+    
 <!--    <elasticjob:job id="${occurErrorNoticeEmailJob.id}" job-ref="occurErrorNoticeEmailJob" registry-center-ref="regCenter" tracing-ref="elasticJobTrace"-->
-<!--                    sharding-total-count="${occurErrorNoticeEmailJob.shardingTotalCount}" cron="${occurErrorNoticeEmailJob.cron}" sharding-item-parameters="${occurErrorNoticeEmailJob.shardingItemParameters}" monitor-execution="${occurErrorNoticeEmailJob.monitorExecution}" failover="${occurErrorNoticeEmailJob.failover}" description="${occurErrorNoticeEmailJob.description}"-->
+<!--                    sharding-total-count="${occurErrorNoticeEmailJob.shardingTotalCount}" cron="${occurErrorNoticeEmailJob.cron}" sharding-item-parameters="${occurErrorNoticeEmailJob.shardingItemParameters}" -->
+<!--                    monitor-execution="${occurErrorNoticeEmailJob.monitorExecution}" failover="${occurErrorNoticeEmailJob.failover}" description="${occurErrorNoticeEmailJob.description}"-->
 <!--                    job-error-handler-type="${occurErrorNoticeEmailJob.jobErrorHandlerType}"-->
 <!--                    disabled="${occurErrorNoticeEmailJob.disabled}" overwrite="${occurErrorNoticeEmailJob.overwrite}" >-->
 <!--        <props>-->