You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by te...@apache.org on 2020/09/23 07:50:30 UTC

[shardingsphere-elasticjob] branch master updated: Add EmailJobErrorHandler to support sending email when job error (#1481)

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

technoboy 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 9f146a7  Add EmailJobErrorHandler to support sending email when job error (#1481)
9f146a7 is described below

commit 9f146a71149852cebc36b7f2a8a1368a45a4e3ae
Author: luky116 <38...@users.noreply.github.com>
AuthorDate: Wed Sep 23 15:50:19 2020 +0800

    Add EmailJobErrorHandler to support sending email when job error (#1481)
---
 .../elasticjob-error-handler-email/pom.xml         |  34 ++++++
 .../error/handler/email/ConfigurationLoader.java   |  31 +++--
 .../error/handler/email/EmailConfiguration.java    |  49 ++++++++
 .../error/handler/email/EmailJobErrorHandler.java  | 126 +++++++++++++++++++++
 ...sphere.elasticjob.error.handler.JobErrorHandler |  18 +++
 .../src/main/resources/error-handler-email.yaml    |  26 +++++
 .../handler/email/EmailJobErrorHandlerTest.java    |  75 ++++++++++++
 .../src/test/resources/error-handler-email.yaml    |  26 +++++
 .../elasticjob/infra/yaml/YamlEngine.java          |  35 ++++++
 .../elasticjob/infra/yaml/YamlEngineTest.java      |  22 ++++
 .../src/test/resources/yaml-test.yaml              |  25 ++++
 11 files changed, 449 insertions(+), 18 deletions(-)

diff --git a/elasticjob-error-handler/elasticjob-error-handler-email/pom.xml b/elasticjob-error-handler/elasticjob-error-handler-email/pom.xml
index dc65635..4ec9157 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-email/pom.xml
+++ b/elasticjob-error-handler/elasticjob-error-handler-email/pom.xml
@@ -27,4 +27,38 @@
     </parent>
     
     <artifactId>elasticjob-error-handler-email</artifactId>
+    
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.shardingsphere.elasticjob</groupId>
+            <artifactId>elasticjob-error-handler-general</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>javax.mail</artifactId>
+            <version>1.6.0</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+        </dependency>
+    </dependencies>
 </project>
diff --git a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngine.java b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/ConfigurationLoader.java
similarity index 60%
copy from elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngine.java
copy to elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/ConfigurationLoader.java
index 0f4dd9e..a6dc3bd 100644
--- a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngine.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/ConfigurationLoader.java
@@ -7,7 +7,7 @@
  * 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.
@@ -15,37 +15,32 @@
  * limitations under the License.
  */
 
-package org.apache.shardingsphere.elasticjob.infra.yaml;
+package org.apache.shardingsphere.elasticjob.error.handler.email;
 
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
-import org.yaml.snakeyaml.Yaml;
+import org.apache.shardingsphere.elasticjob.infra.yaml.YamlEngine;
+
+import java.io.InputStream;
 
 /**
- * YAML engine.
+ * Job error configuration loader.
  */
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
-public final class YamlEngine {
+public class ConfigurationLoader {
     
-    /**
-     * Marshal YAML.
-     *
-     * @param value object to be marshaled
-     * @return YAML content
-     */
-    public static String marshal(final Object value) {
-        return new Yaml(new ElasticJobYamlRepresenter()).dumpAsMap(value);
-    }
+    private static final String ERROR_HANDLER_CONFIG = "error-handler-email.yaml";
     
     /**
      * Unmarshal YAML.
      *
-     * @param yamlContent YAML content
+     * @param prefix    config prefix
      * @param classType class type
-     * @param <T> type of class
+     * @param <T>       type of class
      * @return object from YAML
      */
-    public static <T> T unmarshal(final String yamlContent, final Class<T> classType) {
-        return new Yaml().loadAs(yamlContent, classType);
+    public static <T> T buildConfigByYaml(final String prefix, final Class<T> classType) {
+        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(ERROR_HANDLER_CONFIG);
+        return YamlEngine.unmarshal(prefix, inputStream, classType);
     }
 }
diff --git a/elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailConfiguration.java b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailConfiguration.java
new file mode 100644
index 0000000..515b95e
--- /dev/null
+++ b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailConfiguration.java
@@ -0,0 +1,49 @@
+/*
+ * 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.shardingsphere.elasticjob.error.handler.email;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Email configuration POJO.
+ */
+@Getter
+@Setter
+public final class EmailConfiguration {
+    
+    private String host;
+    
+    private Integer port;
+    
+    private String username;
+    
+    private String password;
+    
+    private String protocol = "smtp";
+    
+    private String subject = "ElasticJob error message";
+    
+    private String from;
+    
+    private String to;
+    
+    private String cc;
+    
+    private String bcc;
+}
diff --git a/elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailJobErrorHandler.java b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailJobErrorHandler.java
new file mode 100644
index 0000000..554e558
--- /dev/null
+++ b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailJobErrorHandler.java
@@ -0,0 +1,126 @@
+/*
+ * 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.shardingsphere.elasticjob.error.handler.email;
+
+import com.google.common.base.Preconditions;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shardingsphere.elasticjob.error.handler.JobErrorHandler;
+
+import javax.mail.Authenticator;
+import javax.mail.BodyPart;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+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 java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Date;
+import java.util.Properties;
+
+/**
+ * Job error handler for sending error message by email.
+ */
+@Slf4j
+public final class EmailJobErrorHandler implements JobErrorHandler {
+    
+    public static final String CONFIG_PREFIX = "email";
+    
+    private EmailConfiguration emailConfiguration;
+    
+    private Session session;
+    
+    public EmailJobErrorHandler() {
+        loadConfiguration();
+    }
+    
+    @Override
+    public void handleException(final String jobName, final Throwable cause) {
+        try {
+            Preconditions.checkNotNull(emailConfiguration);
+            String content = buildContent(jobName, cause);
+            Message message = buildMessage(content);
+            sendMessage(message);
+        } catch (final NullPointerException | MessagingException ex) {
+            log.error("Elastic job: email job handler error", ex);
+        }
+    }
+    
+    private void loadConfiguration() {
+        emailConfiguration = ConfigurationLoader.buildConfigByYaml(CONFIG_PREFIX, EmailConfiguration.class);
+    }
+    
+    @Override
+    public String getType() {
+        return "EMAIL";
+    }
+    
+    private Session buildSession() {
+        if (null == session) {
+            Properties props = new Properties();
+            props.put("mail.smtp.host", emailConfiguration.getHost());
+            props.put("mail.smtp.port", emailConfiguration.getPort());
+            props.put("mail.smtp.auth", "true");
+            props.put("mail.transport.protocol", emailConfiguration.getProtocol());
+            session = Session.getDefaultInstance(props, new Authenticator() {
+                @Override
+                public PasswordAuthentication getPasswordAuthentication() {
+                    return new PasswordAuthentication(emailConfiguration.getUsername(), emailConfiguration.getPassword());
+                }
+            });
+        }
+        return session;
+    }
+    
+    private Message buildMessage(final String content) throws MessagingException {
+        MimeMessage message = new MimeMessage(buildSession());
+        message.setFrom(new InternetAddress(emailConfiguration.getFrom()));
+        message.setSubject(emailConfiguration.getSubject());
+        message.setSentDate(new Date());
+        Multipart multipart = new MimeMultipart();
+        BodyPart mailBody = new MimeBodyPart();
+        mailBody.setContent(content, "text/html; charset=utf-8");
+        multipart.addBodyPart(mailBody);
+        message.setContent(multipart);
+        if (StringUtils.isNotBlank(emailConfiguration.getTo())) {
+            message.addRecipient(Message.RecipientType.TO, new InternetAddress(emailConfiguration.getTo()));
+        }
+        if (StringUtils.isNotBlank(emailConfiguration.getCc())) {
+            message.addRecipient(Message.RecipientType.CC, new InternetAddress(emailConfiguration.getCc()));
+        }
+        message.saveChanges();
+        return message;
+    }
+    
+    private String buildContent(final String jobName, final Throwable cause) {
+        StringWriter sw = new StringWriter();
+        cause.printStackTrace(new PrintWriter(sw, true));
+        String causeString = sw.toString();
+        return String.format("Job '%s' exception occur in job processing, caused by %s", jobName, causeString);
+    }
+    
+    private void sendMessage(final Message message) throws MessagingException {
+        Transport.send(message);
+    }
+}
diff --git a/elasticjob-error-handler/elasticjob-error-handler-email/src/main/resources/META-INF/services/org.apache.shardingsphere.elasticjob.error.handler.JobErrorHandler b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/resources/META-INF/services/org.apache.shardingsphere.elasticjob.error.handler.JobErrorHandler
new file mode 100644
index 0000000..3096707
--- /dev/null
+++ b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/resources/META-INF/services/org.apache.shardingsphere.elasticjob.error.handler.JobErrorHandler
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+org.apache.shardingsphere.elasticjob.error.handler.email.EmailJobErrorHandler
diff --git a/elasticjob-error-handler/elasticjob-error-handler-email/src/main/resources/error-handler-email.yaml b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/resources/error-handler-email.yaml
new file mode 100644
index 0000000..404bb4e
--- /dev/null
+++ b/elasticjob-error-handler/elasticjob-error-handler-email/src/main/resources/error-handler-email.yaml
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+email:
+  host: xxx
+  port: xxx
+  username: username
+  password: password
+  protocol: smtp
+  from: xxx
+  to: xxx
+  cc: xxx
diff --git a/elasticjob-error-handler/elasticjob-error-handler-email/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailJobErrorHandlerTest.java b/elasticjob-error-handler/elasticjob-error-handler-email/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailJobErrorHandlerTest.java
new file mode 100644
index 0000000..62c6cdf
--- /dev/null
+++ b/elasticjob-error-handler/elasticjob-error-handler-email/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/email/EmailJobErrorHandlerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.error.handler.email;
+
+import lombok.SneakyThrows;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.slf4j.Logger;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class EmailJobErrorHandlerTest {
+        
+    @Mock
+    private Logger log;
+       
+    @Test
+    public void assertHandleExceptionFor() {
+        EmailJobErrorHandler emailJobErrorHandler = new EmailJobErrorHandler();
+        emailJobErrorHandler.handleException("test job name", new RuntimeException("test exception"));
+    }
+        
+    @Test
+    @SneakyThrows
+    public void assertHandleExceptionForNullConfiguration() {
+        EmailJobErrorHandler emailJobErrorHandler = new EmailJobErrorHandler();
+        Field emailConfigurationField = EmailJobErrorHandler.class.getDeclaredField("emailConfiguration");
+        emailConfigurationField.setAccessible(true);
+        emailConfigurationField.set(emailJobErrorHandler, null);
+        
+        setStaticFieldValue(emailJobErrorHandler);
+        
+        Throwable cause = new RuntimeException("test exception");
+        emailJobErrorHandler.handleException("test job name", cause);
+        verify(log).error(ArgumentMatchers.any(String.class), ArgumentMatchers.any(NullPointerException.class));
+    }
+        
+    @SneakyThrows
+    private void setStaticFieldValue(final EmailJobErrorHandler emailJobErrorHandler) {
+        Field field = emailJobErrorHandler.getClass().getDeclaredField("log");
+        field.setAccessible(true);
+        Field modifiers = field.getClass().getDeclaredField("modifiers");
+        modifiers.setAccessible(true);
+        modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
+        field.set(emailJobErrorHandler, log);
+    }
+        
+    @Test
+    public void assertType() {
+        EmailJobErrorHandler emailJobErrorHandler = new EmailJobErrorHandler();
+        assertThat(emailJobErrorHandler.getType(), equalTo("EMAIL"));
+    }
+}
diff --git a/elasticjob-error-handler/elasticjob-error-handler-email/src/test/resources/error-handler-email.yaml b/elasticjob-error-handler/elasticjob-error-handler-email/src/test/resources/error-handler-email.yaml
new file mode 100644
index 0000000..be04b98
--- /dev/null
+++ b/elasticjob-error-handler/elasticjob-error-handler-email/src/test/resources/error-handler-email.yaml
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+email:
+  host: test.mail.com
+  port: 123
+  username: username
+  password: password
+  protocol: smtp
+  from: testmail@qiyi.com
+  to: xxx1@ejob.com
+  cc: xxx2@ejob.com
diff --git a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngine.java b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngine.java
index 0f4dd9e..0ee19a5 100644
--- a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngine.java
+++ b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngine.java
@@ -17,9 +17,15 @@
 
 package org.apache.shardingsphere.elasticjob.infra.yaml;
 
+import com.google.common.base.Splitter;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shardingsphere.elasticjob.infra.json.GsonFactory;
 import org.yaml.snakeyaml.Yaml;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
 
 /**
  * YAML engine.
@@ -48,4 +54,33 @@ public final class YamlEngine {
     public static <T> T unmarshal(final String yamlContent, final Class<T> classType) {
         return new Yaml().loadAs(yamlContent, classType);
     }
+    
+    /**
+     * Unmarshal YAML.
+     *
+     * @param prefix config prefix name
+     * @param configFileInput YAML file input stream
+     * @param classType class type
+     * @param <T> type of class
+     * @return object from YAML
+     */
+    public static <T> T unmarshal(final String prefix, final InputStream configFileInput, final Class<T> classType) {
+        Map<String, Object> configDataMap = new Yaml().loadAs(configFileInput, Map.class);
+        if (null != configDataMap && StringUtils.isNotBlank(prefix)) {
+            List<String> prefixStrList = Splitter.on(".").trimResults().omitEmptyStrings().splitToList(prefix);
+            for (String prefixStr : prefixStrList) {
+                Object configData = configDataMap.get(prefixStr);
+                if (configData instanceof Map) {
+                    configDataMap = (Map) configData;
+                } else {
+                    configDataMap = null;
+                    break;
+                }
+            }
+        }
+        if (null != configDataMap) {
+            return GsonFactory.getGson().fromJson(GsonFactory.getGson().toJson(configDataMap), classType);
+        }
+        return null;
+    }
 }
diff --git a/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngineTest.java b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngineTest.java
index 429d043..3a3c2f5 100644
--- a/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngineTest.java
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/yaml/YamlEngineTest.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.elasticjob.infra.yaml;
 
 import org.apache.shardingsphere.elasticjob.infra.yaml.fixture.FooYamlConfiguration;
 import org.junit.Test;
+import java.io.InputStream;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertNull;
@@ -34,6 +35,10 @@ public final class YamlEngineTest {
     
     private static final String YAML_WITH_NULL = "foo: foo\n";
     
+    private static final String PREFIX = "nest";
+    
+    private static final String PREFIX2 = "nest.bar";
+    
     @Test
     public void assertMarshal() {
         FooYamlConfiguration actual = new FooYamlConfiguration();
@@ -69,4 +74,21 @@ public final class YamlEngineTest {
         assertNull(actual.getBar());
         assertNull(actual.getNest());
     }
+    
+    @Test
+    public void assertUnmarshalWithPrefix() {
+        InputStream configFileInput = Thread.currentThread().getContextClassLoader().getResourceAsStream("yaml-test.yaml");         
+        FooYamlConfiguration actual = YamlEngine.unmarshal(PREFIX, configFileInput, FooYamlConfiguration.class);
+        assertThat(actual.getFoo(), is("nest_foo"));
+        assertThat(actual.getBar(), is("nest_bar"));
+        assertThat(actual.getNest().getFoo(), is("nest_foo2"));
+        assertThat(actual.getNest().getBar(), is("nest_bar2"));
+    }
+    
+    @Test
+    public void assertUnmarshalWithPrefixAndNullValue() {
+        InputStream configFileInput = Thread.currentThread().getContextClassLoader().getResourceAsStream("yaml-test.yaml");
+        FooYamlConfiguration actual = YamlEngine.unmarshal(PREFIX2, configFileInput, FooYamlConfiguration.class);
+        assertNull(actual);
+    }
 }
diff --git a/elasticjob-infra/elasticjob-infra-common/src/test/resources/yaml-test.yaml b/elasticjob-infra/elasticjob-infra-common/src/test/resources/yaml-test.yaml
new file mode 100644
index 0000000..02cb163
--- /dev/null
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/resources/yaml-test.yaml
@@ -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.
+#
+
+bar: bar
+foo: foo
+nest:
+  bar: nest_bar
+  foo: nest_foo
+  nest:
+    bar: nest_bar2
+    foo: nest_foo2