Skip to content
This repository was archived by the owner on May 14, 2025. It is now read-only.

Commit e17b570

Browse files
committed
Need to select the serialization method for JobParameters for the commandline
User needs ability to set the default serialization technique for SCDF when restarting a job User needs ability to select a serialization technique for a specific job restart When user restarts a job repository from the list that is derived from thinjobexecutions it should use default technique Add restful documentation. Add support to allow user to set useJsonJobParameters for job relaunch via the shell. Note: there are not tests for the shell update in this commit. This is because the current set of tests rely on @EnableDataflowServer which does not work. But before we fix @EnableDataflowServer we need to make sure we want to carry it forward per Issue #1040 Polish PR before push to repo JobCommand should ignore identifyingParameters when deserializing JobParameters This is a generated list and and will cause deserialization to fail if not skipped SCDF needs to support any type of class for JobParameters However, if the class is not a base java type or one provided by dataflow the user has to build dataflow with their class included. Add tests for JobParameterJacksonDeserializer Remove Disabled annotation from JobExecutionController tests that are no longer needed Update per code review request. Added test for JobParamterMixin via the JobTemplateTests Removed the duration parameter from getDurationBasedEstimate method Rebased Reset the duration calculation from nanos back to millis. Optimized restartExecutionArgs routine that removes duplicates. This was per a comment in code review Remove unnecessary exclusions from AbstractShellIntegrationTest The changes are code review requests. Add warn log message when job restart id is invalid. * Initialize ObjectMapper with the tools provided by DataFlowTemplate when testing * These changes were identified during code review
1 parent 50f1e20 commit e17b570

File tree

25 files changed

+395
-106
lines changed

25 files changed

+395
-106
lines changed

spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/JobExecutionsDocumentation.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,13 +334,17 @@ public void jobStop() throws Exception {
334334
public void jobRestart() throws Exception {
335335
this.mockMvc.perform(put("/jobs/executions/{id}", "2")
336336
.queryParam("restart", "true")
337+
.queryParam("useJsonJobParameters", "true")
337338
)
338339
.andDo(print())
339340
.andExpect(status().isOk())
340341
.andDo(this.documentationHandler.document(
341342
pathParameters(parameterWithName("id")
342343
.description("The id of an existing job execution (required)"))
343344
, queryParameters(
345+
parameterWithName("useJsonJobParameters").description("If true dataflow will " +
346+
"serialize job parameters as JSON. Default is null, and the default " +
347+
"configuration will be used to determine serialization method.").optional(),
344348
parameterWithName("restart")
345349
.description("Sends signal to restart the job if set to true")
346350
)

spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2666,7 +2666,7 @@ include::{snippets}/job-executions-documentation/job-restart/path-parameters.ado
26662666
[[api-guide-resources-job-executions-restart-request-parameters]]
26672667
===== Request Parameters
26682668

2669-
include::{snippets}/job-executions-documentation/job-restart/request-parameters.adoc[]
2669+
include::{snippets}/job-executions-documentation/job-restart/query-parameters.adoc[]
26702670

26712671

26722672

spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/JobOperations.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ public interface JobOperations {
4242
*/
4343
void executionRestart(long id);
4444

45+
/**
46+
* Restarts a job by id
47+
*
48+
* @param id job execution id
49+
* @param useJsonJobParameters if true {@link org.springframework.batch.core.JobParameters} will be serialized to JSON.
50+
* Default is {@code Null} which will serialize the {@link org.springframework.batch.core.JobParameters}
51+
* to the default specified in SCDF's configuration.
52+
*/
53+
void executionRestart(long id, Boolean useJsonJobParameters);
54+
4555
/**
4656
* @return the list job executions without step executions known to the system.
4757
*/

spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/JobTemplate.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ public void executionRestart(long id) {
117117
restTemplate.put(builder.toUriString(), null);
118118
}
119119

120+
@Override
121+
public void executionRestart(long id, Boolean useJsonJobParameters) {
122+
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(executionLink.expand(id).getHref()).queryParam("restart", "true")
123+
.queryParam("useJsonJobParameters", useJsonJobParameters);
124+
125+
restTemplate.put(builder.toUriString(), null);
126+
}
127+
120128
@Override
121129
public PagedModel<JobExecutionThinResource> executionThinList() {
122130
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(thinExecutionsLink.getHref()).queryParam("size", "2000");

spring-cloud-dataflow-rest-client/src/test/java/org/springframework/cloud/dataflow/rest/client/DataflowTemplateTests.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222
import java.util.List;
2323
import java.util.Optional;
2424

25+
import com.fasterxml.jackson.core.JsonProcessingException;
2526
import com.fasterxml.jackson.databind.ObjectMapper;
26-
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
27-
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
2827
import org.junit.After;
2928
import org.junit.Before;
3029
import org.junit.Test;
@@ -34,15 +33,14 @@
3433
import org.springframework.batch.core.JobInstance;
3534
import org.springframework.batch.core.JobParameter;
3635
import org.springframework.batch.core.JobParameters;
36+
import org.springframework.batch.core.JobParametersBuilder;
3737
import org.springframework.batch.core.StepExecution;
3838
import org.springframework.batch.item.ExecutionContext;
3939
import org.springframework.cloud.dataflow.rest.Version;
4040
import org.springframework.cloud.dataflow.rest.job.StepExecutionHistory;
4141
import org.springframework.cloud.dataflow.rest.resource.RootResource;
42-
import org.springframework.cloud.dataflow.rest.support.jackson.Jackson2DataflowModule;
4342
import org.springframework.hateoas.Link;
4443
import org.springframework.hateoas.LinkRelation;
45-
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
4644
import org.springframework.http.converter.HttpMessageConverter;
4745
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
4846
import org.springframework.web.client.ResourceAccessException;
@@ -69,10 +67,7 @@ public class DataflowTemplateTests {
6967
@Before
7068
public void setup() {
7169
mapper = new ObjectMapper();
72-
mapper.registerModule(new Jdk8Module());
73-
mapper.registerModule(new Jackson2HalModule());
74-
mapper.registerModule(new JavaTimeModule());
75-
mapper.registerModule(new Jackson2DataflowModule());
70+
DataFlowTemplate.prepareObjectMapper(mapper);
7671
System.setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(100));
7772
}
7873

@@ -102,9 +97,22 @@ public void testDataFlowTemplateContructorWithNonExistingUri() throws URISyntaxE
10297

10398
@Test
10499
public void testThatObjectMapperGetsPrepared() {
105-
final ObjectMapper objectMapper = new ObjectMapper();
106-
DataFlowTemplate.prepareObjectMapper(objectMapper);
107-
assertCorrectMixins(objectMapper);
100+
assertCorrectMixins(this.mapper);
101+
}
102+
103+
@Test
104+
public void testJobParameters() throws JsonProcessingException {
105+
JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
106+
jobParametersBuilder.addString("foo", "foo");
107+
jobParametersBuilder.addString("bar", "bar");
108+
109+
JobParameters jobParameters = jobParametersBuilder.toJobParameters();
110+
assertCorrectMixins(this.mapper);
111+
String jobParametersSerialized = this.mapper.writeValueAsString(jobParameters);
112+
jobParameters = this.mapper.readValue(jobParametersSerialized, JobParameters.class);
113+
assertEquals(jobParameters.getParameter("foo").getValue(), "foo");
114+
assertEquals(jobParameters.getParameter("bar").getValue(), "bar");
115+
assertEquals(jobParameters.getParameters().size(), 2);
108116
}
109117

110118
@Test

spring-cloud-dataflow-rest-resource/src/main/java/org/springframework/cloud/dataflow/rest/support/jackson/JobParameterJacksonDeserializer.java

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,16 @@ public JobParameter deserialize(JsonParser jsonParser, DeserializationContext de
5050
String type = node.get("type").asText();
5151

5252
JobParameter jobParameter;
53-
//TODO: Boot3x followup Verify that Job Parameters setup properly for Batch 5
54-
if (!type.isEmpty() && !type.equalsIgnoreCase("STRING")) {
55-
if ("DATE".equalsIgnoreCase(type)) {
56-
jobParameter = new JobParameter(LocalDateTime.parse(value), LocalDateTime.class, identifying);
57-
}
58-
else if ("DOUBLE".equalsIgnoreCase(type)) {
59-
jobParameter = new JobParameter(Double.valueOf(value), Double.class, identifying);
60-
}
61-
else if ("LONG".equalsIgnoreCase(type)) {
62-
jobParameter = new JobParameter(Long.valueOf(value), Long.class, identifying);
63-
}
64-
else {
65-
throw new IllegalStateException("Unsupported JobParameter type: " + type);
53+
if (!type.isEmpty()) {
54+
try {
55+
jobParameter = new JobParameter(value, Class.forName(type), identifying);
56+
} catch (ClassNotFoundException e) {
57+
throw new IllegalArgumentException("JobParameter type %s is not supported by DataFlow".formatted(type), e);
6658
}
6759
}
6860
else {
69-
jobParameter = new JobParameter(value, String.class, identifying);
70-
}
61+
jobParameter = new JobParameter(value, String.class, identifying);
62+
}
7163

7264
if (logger.isDebugEnabled()) {
7365
logger.debug("jobParameter - value: {} (type: {}, isIdentifying: {})",

spring-cloud-dataflow-rest-resource/src/main/java/org/springframework/cloud/dataflow/rest/support/jackson/JobParametersJacksonMixIn.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616

1717
package org.springframework.cloud.dataflow.rest.support.jackson;
1818

19+
import java.util.Map;
20+
1921
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
2022
import com.fasterxml.jackson.annotation.JsonProperty;
2123

24+
import org.springframework.batch.core.JobParameter;
2225
import org.springframework.batch.core.JobParameters;
2326

2427
/**
@@ -27,9 +30,12 @@
2730
* @author Gunnar Hillert
2831
* @since 1.0
2932
*/
30-
@JsonIgnoreProperties("empty")
33+
@JsonIgnoreProperties({"empty", "identifyingParameters"})
3134
public abstract class JobParametersJacksonMixIn {
3235

3336
@JsonProperty
3437
abstract boolean isEmpty();
38+
39+
@JsonProperty
40+
abstract Map<String, JobParameter<?>> getIdentifyingParameters();
3541
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.dataflow.rest.support.jackson;
18+
19+
import java.io.ByteArrayInputStream;
20+
import java.io.IOException;
21+
22+
import com.fasterxml.jackson.core.JsonFactory;
23+
import com.fasterxml.jackson.core.JsonParser;
24+
import com.fasterxml.jackson.core.json.UTF8StreamJsonParser;
25+
import com.fasterxml.jackson.databind.ObjectMapper;
26+
import org.junit.jupiter.api.Test;
27+
import org.springframework.batch.core.JobParameter;
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
30+
31+
public class JobParameterJacksonDeserializerTests {
32+
33+
@Test
34+
public void validJobParameter() throws IOException {
35+
JobParameterJacksonDeserializer jobParameterJacksonDeserializer = new JobParameterJacksonDeserializer();
36+
String json = "{\"value\":\"BAR\",\"type\":\"java.lang.String\",\"identifying\":true}";
37+
JobParameter jobParameter = jobParameterJacksonDeserializer.deserialize(getJsonParser(json), null);
38+
assertThat(jobParameter.getType()).isEqualTo(String.class);
39+
assertThat(jobParameter.getValue()).isEqualTo("BAR");
40+
assertThat(jobParameter.isIdentifying()).isTrue();
41+
}
42+
43+
@Test
44+
public void inValidJobParameter() throws IOException {
45+
JobParameterJacksonDeserializer jobParameterJacksonDeserializer = new JobParameterJacksonDeserializer();
46+
String json = "{\"value\":\"BAR\",\"type\":\"java.lang.FOO\",\"identifying\":true}";
47+
assertThatExceptionOfType(IllegalArgumentException.class)
48+
.isThrownBy(() -> {
49+
jobParameterJacksonDeserializer.deserialize(getJsonParser(json), null);
50+
})
51+
.withMessage("JobParameter type java.lang.FOO is not supported by DataFlow");
52+
}
53+
54+
private JsonParser getJsonParser(String json) throws IOException {
55+
JsonFactory factory = new JsonFactory();
56+
byte[] jsonData = json.getBytes();
57+
ByteArrayInputStream inputStream = new ByteArrayInputStream(jsonData);
58+
UTF8StreamJsonParser jsonParser = (UTF8StreamJsonParser) factory.createParser(inputStream);
59+
jsonParser.setCodec(new ObjectMapper());
60+
return jsonParser;
61+
}
62+
}

spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/batch/JdbcSearchableStepExecutionDao.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.sql.ResultSet;
1919
import java.sql.SQLException;
20+
import java.sql.Timestamp;
2021
import java.util.Collection;
2122
import java.util.Collections;
2223
import java.util.HashMap;
@@ -201,8 +202,11 @@ private static class StepExecutionRowMapper implements RowMapper<StepExecution>
201202
public StepExecution mapRow(ResultSet rs, int rowNum) throws SQLException {
202203
StepExecution stepExecution = new StepExecution(rs.getString(2), null);
203204
stepExecution.setId(rs.getLong(1));
204-
stepExecution.setStartTime(rs.getTimestamp(3).toLocalDateTime());
205-
stepExecution.setEndTime(rs.getTimestamp(4).toLocalDateTime());
205+
Timestamp startTimeStamp = rs.getTimestamp(3);
206+
Timestamp endTimeStamp = rs.getTimestamp(4);
207+
208+
stepExecution.setStartTime((startTimeStamp == null) ? null : startTimeStamp.toLocalDateTime());
209+
stepExecution.setEndTime((endTimeStamp == null) ? null : endTimeStamp.toLocalDateTime());
206210
stepExecution.setStatus(BatchStatus.valueOf(rs.getString(5)));
207211
stepExecution.setCommitCount(rs.getInt(6));
208212
stepExecution.setReadCount(rs.getInt(7));

spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/features/TaskConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,13 +272,14 @@ public TaskJobService taskJobExecutionRepository(
272272
DataflowTaskExplorer taskExplorer,
273273
TaskDefinitionRepository taskDefinitionRepository,
274274
TaskExecutionService taskExecutionService,
275-
LauncherRepository launcherRepository) {
275+
LauncherRepository launcherRepository, TaskConfigurationProperties taskConfigurationProperties) {
276276
return new DefaultTaskJobService(
277277
service,
278278
taskExplorer,
279279
taskDefinitionRepository,
280280
taskExecutionService,
281-
launcherRepository
281+
launcherRepository,
282+
taskConfigurationProperties
282283
);
283284
}
284285
}

0 commit comments

Comments
 (0)