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 13:08:10 UTC
[shardingsphere-elasticjob] branch master updated: Polish
elasticjob-error-handler-wechat (#1576)
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 59b16f4 Polish elasticjob-error-handler-wechat (#1576)
59b16f4 is described below
commit 59b16f4919e5affc19f1465ded2e7fa090ba6f7b
Author: Liang Zhang <te...@163.com>
AuthorDate: Sun Oct 18 21:08:02 2020 +0800
Polish elasticjob-error-handler-wechat (#1576)
---
.../handler/wechat/WechatJobErrorHandler.java | 71 +++++++++++-----------
.../{ => configuration}/WechatConfiguration.java | 24 +++-----
.../WechatPropertiesConstants.java} | 20 +++---
.../handler/wechat/WechatJobErrorHandlerTest.java | 25 ++++----
.../test/resources/conf/error-handler-wechat.yaml | 21 -------
.../src/test/resources/logback-test.xml | 38 ++++++++++++
.../elasticjob/lite/example/JavaMain.java | 8 +--
7 files changed, 109 insertions(+), 98 deletions(-)
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatJobErrorHandler.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatJobErrorHandler.java
index 224c1b4..3902894 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatJobErrorHandler.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatJobErrorHandler.java
@@ -30,6 +30,7 @@ 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.error.handler.wechat.configuration.WechatConfiguration;
import org.apache.shardingsphere.elasticjob.infra.json.GsonFactory;
import java.io.IOException;
@@ -51,63 +52,63 @@ public final class WechatJobErrorHandler implements JobErrorHandler {
registerShutdownHook();
}
+ private void registerShutdownHook() {
+ Runtime.getRuntime().addShutdownHook(new Thread("WechatJobErrorHandler 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) {
- WechatConfiguration wechatConfiguration = WechatConfiguration.getByProps(jobConfig.getProps());
- HttpPost httpPost = new HttpPost(wechatConfiguration.getWebhook());
- RequestConfig requestConfig = RequestConfig.custom()
- .setConnectTimeout(wechatConfiguration.getConnectTimeout())
- .setSocketTimeout(wechatConfiguration.getReadTimeout())
- .build();
- httpPost.setConfig(requestConfig);
- StringEntity entity = new StringEntity(getParamJson(getMsg(jobConfig.getJobName(), cause)), StandardCharsets.UTF_8);
- entity.setContentEncoding(StandardCharsets.UTF_8.name());
- entity.setContentType("application/json");
- httpPost.setEntity(entity);
+ WechatConfiguration wechatConfig = new WechatConfiguration(jobConfig.getProps());
+ HttpPost httpPost = createHTTPPostMethod(jobConfig.getJobName(), cause, wechatConfig);
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 wechat because of: {}", jobConfig.getJobName(), resp.get("errmsg").getAsString(), cause);
+ log.info("An exception has occurred in Job '{}', But failed to send alert by wechat because of: {}", jobConfig.getJobName(), resp.get("errmsg").getAsString(), cause);
} else {
- log.error("An exception has occurred in Job '{}', Notification to wechat was successful.", jobConfig.getJobName(), cause);
+ log.info("An exception has occurred in Job '{}', Notification to wechat was successful.", jobConfig.getJobName(), cause);
}
} else {
log.error("An exception has occurred in Job '{}', But failed to send alert by wechat 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 wechat 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 HttpPost createHTTPPostMethod(final String jobName, final Throwable cause, final WechatConfiguration wechatConfig) {
+ HttpPost result = new HttpPost(wechatConfig.getWebhook());
+ RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(wechatConfig.getConnectTimeoutMillisecond()).setSocketTimeout(wechatConfig.getReadTimeoutMillisecond()).build();
+ result.setConfig(requestConfig);
+ StringEntity entity = new StringEntity(getJsonParameter(getErrorMessage(jobName, cause)), StandardCharsets.UTF_8);
+ entity.setContentEncoding(StandardCharsets.UTF_8.name());
+ entity.setContentType("application/json");
+ result.setEntity(entity);
+ return result;
+ }
+
+ private String getJsonParameter(final String message) {
+ return GsonFactory.getGson().toJson(ImmutableMap.of("msgtype", "text", "text", Collections.singletonMap("content", message)));
}
- private String getMsg(final String jobName, final Throwable cause) {
- StringWriter sw = new StringWriter();
- cause.printStackTrace(new PrintWriter(sw, true));
- return String.format("Job '%s' exception occur in job processing, caused by %s", jobName, sw.toString());
+ private String getErrorMessage(final String jobName, final Throwable cause) {
+ StringWriter stringWriter = new StringWriter();
+ cause.printStackTrace(new PrintWriter(stringWriter, true));
+ return String.format("Job '%s' exception occur in job processing, caused by %s", jobName, stringWriter.toString());
}
@Override
public String getType() {
return "WECHAT";
}
-
- private void registerShutdownHook() {
- Thread t = new Thread("WechatJobErrorHandler 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-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatConfiguration.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/configuration/WechatConfiguration.java
similarity index 59%
rename from elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatConfiguration.java
rename to elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/configuration/WechatConfiguration.java
index a1cfb59..54b3d34 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatConfiguration.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/configuration/WechatConfiguration.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,10 +15,9 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.elasticjob.error.handler.wechat;
+package org.apache.shardingsphere.elasticjob.error.handler.wechat.configuration;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
import java.util.Properties;
@@ -26,24 +25,17 @@ import java.util.Properties;
* Wechat configuration.
*/
@Getter
-@RequiredArgsConstructor
public final class WechatConfiguration {
private final String webhook;
- private final Integer connectTimeout;
+ private final Integer connectTimeoutMillisecond;
- private final Integer readTimeout;
+ private final Integer readTimeoutMillisecond;
- /**
- * Get wechat configuration.
- *
- * @param props properties
- * @return wechat configuration
- */
- public static WechatConfiguration getByProps(final Properties props) {
- return new WechatConfiguration(props.getProperty(WechatConstants.WECHAT_WEBHOOK),
- Integer.valueOf(props.getOrDefault(WechatConstants.WECHAT_CONNECT_TIMEOUT, WechatConstants.DEFAULT_WECHAT_CONNECT_TIMEOUT).toString()),
- Integer.valueOf(props.getOrDefault(WechatConstants.WECHAT_READ_TIMEOUT, WechatConstants.DEFAULT_WECHAT_READ_TIMEOUT).toString()));
+ public WechatConfiguration(final Properties props) {
+ webhook = props.getProperty(WechatPropertiesConstants.WEBHOOK);
+ connectTimeoutMillisecond = Integer.parseInt(props.getProperty(WechatPropertiesConstants.CONNECT_TIMEOUT_MILLISECOND, WechatPropertiesConstants.DEFAULT_CONNECT_TIMEOUT_MILLISECOND));
+ readTimeoutMillisecond = Integer.parseInt(props.getProperty(WechatPropertiesConstants.READ_TIMEOUT_MILLISECOND, WechatPropertiesConstants.DEFAULT_READ_TIMEOUT_MILLISECOND));
}
}
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatConstants.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/configuration/WechatPropertiesConstants.java
similarity index 61%
rename from elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatConstants.java
rename to elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/configuration/WechatPropertiesConstants.java
index bb1318b..e99bcf6 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatConstants.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/configuration/WechatPropertiesConstants.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,22 +15,22 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.elasticjob.error.handler.wechat;
+package org.apache.shardingsphere.elasticjob.error.handler.wechat.configuration;
/**
- * Wechat constants.
+ * Wechat properties constants.
*/
-public final class WechatConstants {
+public final class WechatPropertiesConstants {
- public static final String PREFIX = "wechat.";
+ public static final String DEFAULT_CONNECT_TIMEOUT_MILLISECOND = "3000";
- public static final String WECHAT_WEBHOOK = PREFIX + "webhook";
+ public static final String DEFAULT_READ_TIMEOUT_MILLISECOND = "5000";
- public static final String WECHAT_CONNECT_TIMEOUT = PREFIX + "connectTimeout";
+ private static final String PREFIX = "wechat.";
- public static final String WECHAT_READ_TIMEOUT = PREFIX + "readTimeout";
+ public static final String WEBHOOK = PREFIX + "webhook";
- public static final Integer DEFAULT_WECHAT_CONNECT_TIMEOUT = 3000;
+ public static final String CONNECT_TIMEOUT_MILLISECOND = PREFIX + "connectTimeoutMillisecond";
- public static final Integer DEFAULT_WECHAT_READ_TIMEOUT = 5000;
+ public static final String READ_TIMEOUT_MILLISECOND = PREFIX + "readTimeoutMillisecond";
}
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatJobErrorHandlerTest.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatJobErrorHandlerTest.java
index 6a4eb1b..2b04ee3 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatJobErrorHandlerTest.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/test/java/org/apache/shardingsphere/elasticjob/error/handler/wechat/WechatJobErrorHandlerTest.java
@@ -20,6 +20,7 @@ package org.apache.shardingsphere.elasticjob.error.handler.wechat;
import lombok.SneakyThrows;
import org.apache.shardingsphere.elasticjob.api.JobConfiguration;
import org.apache.shardingsphere.elasticjob.error.handler.JobErrorHandlerFactory;
+import org.apache.shardingsphere.elasticjob.error.handler.wechat.configuration.WechatPropertiesConstants;
import org.apache.shardingsphere.elasticjob.error.handler.wechat.fixture.WechatInternalController;
import org.apache.shardingsphere.elasticjob.infra.exception.JobConfigurationException;
import org.apache.shardingsphere.elasticjob.restful.NettyRestfulService;
@@ -61,13 +62,20 @@ public final class WechatJobErrorHandlerTest {
restfulService.startup();
}
+ @AfterClass
+ public static void close() {
+ if (null != restfulService) {
+ restfulService.shutdown();
+ }
+ }
+
@Test
public void assertHandleExceptionWithNotifySuccessful() {
WechatJobErrorHandler actual = getWechatJobErrorHandler();
setStaticFieldValue(actual);
Throwable cause = new RuntimeException("test");
actual.handleException(getJobConfiguration("http://localhost:9872/send?key=TLQEC0cPivqV1MkT0IPMtzunTBBVyIV3"), cause);
- verify(log).error("An exception has occurred in Job '{}', Notification to wechat was successful.", "test_job", cause);
+ verify(log).info("An exception has occurred in Job '{}', Notification to wechat was successful.", "test_job", cause);
}
@Test
@@ -76,7 +84,7 @@ public final class WechatJobErrorHandlerTest {
setStaticFieldValue(actual);
Throwable cause = new RuntimeException("test");
actual.handleException(getJobConfiguration("http://localhost:9872/send?key=wrongToken"), cause);
- verify(log).error("An exception has occurred in Job '{}', But failed to send alert by wechat because of: {}", "test_job", "token is invalid", cause);
+ verify(log).info("An exception has occurred in Job '{}', But failed to send alert by wechat because of: {}", "test_job", "token is invalid", cause);
}
@Test
@@ -115,20 +123,13 @@ public final class WechatJobErrorHandlerTest {
private JobConfiguration getJobConfiguration(final String webhook) {
return JobConfiguration.newBuilder("test_job", 3)
- .setProperty(WechatConstants.WECHAT_WEBHOOK, webhook)
- .setProperty(WechatConstants.WECHAT_CONNECT_TIMEOUT, "1000")
- .setProperty(WechatConstants.WECHAT_READ_TIMEOUT, "2000")
+ .setProperty(WechatPropertiesConstants.WEBHOOK, webhook)
+ .setProperty(WechatPropertiesConstants.CONNECT_TIMEOUT_MILLISECOND, "1000")
+ .setProperty(WechatPropertiesConstants.READ_TIMEOUT_MILLISECOND, "2000")
.build();
}
private WechatJobErrorHandler getWechatJobErrorHandler() {
return (WechatJobErrorHandler) JobErrorHandlerFactory.createHandler("WECHAT").orElseThrow(() -> new JobConfigurationException("WECHAT error handler not found."));
}
-
- @AfterClass
- public static void close() {
- if (null != restfulService) {
- restfulService.shutdown();
- }
- }
}
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/test/resources/conf/error-handler-wechat.yaml b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/test/resources/conf/error-handler-wechat.yaml
deleted file mode 100644
index 17bd40d..0000000
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/test/resources/conf/error-handler-wechat.yaml
+++ /dev/null
@@ -1,21 +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.
-#
-
-wechat:
- webhook: http://localhost:9876/send?key=TLQEC0cPivqV1MkT0IPMtzunTBBVyIV3
- connectTimeout: 5000
- readTimeout: 3000
diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/src/test/resources/logback-test.xml b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-wechat/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-wechat/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/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 1a25b5c..55c5840 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
@@ -20,7 +20,7 @@ package org.apache.shardingsphere.elasticjob.lite.example;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.shardingsphere.elasticjob.error.handler.dingtalk.configuration.DingtalkPropertiesConstants;
import org.apache.shardingsphere.elasticjob.error.handler.email.EmailConstants;
-import org.apache.shardingsphere.elasticjob.error.handler.wechat.WechatConstants;
+import org.apache.shardingsphere.elasticjob.error.handler.wechat.configuration.WechatPropertiesConstants;
import org.apache.shardingsphere.elasticjob.http.props.HttpJobProperties;
import org.apache.shardingsphere.elasticjob.lite.api.bootstrap.impl.OneOffJobBootstrap;
import org.apache.shardingsphere.elasticjob.lite.api.bootstrap.impl.ScheduleJobBootstrap;
@@ -153,9 +153,9 @@ public final class JavaMain {
}
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");
+ jobConfig.getProps().setProperty(WechatPropertiesConstants.WEBHOOK, "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=5308e20a-2900-484b-a332-b5bb701ade04");
+ jobConfig.getProps().setProperty(WechatPropertiesConstants.CONNECT_TIMEOUT_MILLISECOND, "9000");
+ jobConfig.getProps().setProperty(WechatPropertiesConstants.READ_TIMEOUT_MILLISECOND, "5000");
}
private static void setEmailConfiguration(final JobConfiguration jobConfig) {