Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ExecutionContextSerializer implementation based on Gson #4185

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions spring-batch-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.batch.core.repository.dao;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import org.springframework.batch.core.repository.ExecutionContextSerializer;
import org.springframework.util.Assert;

/**
* This class is an implementation of {@link ExecutionContextSerializer}
* based on <a href="https://github.com/google/gson">Google Gson</a> library.
*
* @author Mahmoud Ben Hassine
* @since 5.0
*/
public class GsonExecutionContextStringSerializer implements ExecutionContextSerializer {

private Gson gson;

/**
* Create a new {@link GsonExecutionContextStringSerializer}.
*
* @param gson the Gson instance to use
*/
public GsonExecutionContextStringSerializer(Gson gson) {
this.gson = gson;
}

@Override
public Map<String, Object> deserialize(InputStream inputStream) throws IOException {
Assert.notNull(inputStream, "An InputStream is required");
Type mapType = new TypeToken<Map<String, Object>>(){}.getType();
try (JsonReader jsonReader = new JsonReader(new InputStreamReader(inputStream))) {
return this.gson.fromJson(jsonReader, mapType);
}
}

@Override
public void serialize(Map<String, Object> context, OutputStream outputStream) throws IOException {
Assert.notNull(context, "A context is required");
Assert.notNull(outputStream, "An OutputStream is required");
Type mapType = new TypeToken<Map<String, Object>>(){}.getType();
try (JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(outputStream))) {
this.gson.toJson(context, mapType, jsonWriter);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.batch.core.repository;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.ToNumberPolicy;

import org.springframework.batch.core.repository.dao.AbstractExecutionContextSerializerTests;
import org.springframework.batch.core.repository.dao.GsonExecutionContextStringSerializer;

/**
* Test class for {@link GsonExecutionContextStringSerializer}.
*
* @author Mahmoud Ben Hassine
*/
public class GsonExecutionContextStringSerializerTests extends AbstractExecutionContextSerializerTests {

@Override
protected ExecutionContextSerializer getSerializer() {
Gson gson = new GsonBuilder()
// TODO correctly configure gson to pass the test suite
.create();
return new GsonExecutionContextStringSerializer(gson);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,24 @@
*/
package org.springframework.batch.core.repository.dao;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.junit.jupiter.api.Test;

import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.repository.ExecutionContextSerializer;

import java.io.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

/**
Expand All @@ -54,7 +58,7 @@ void testSerializeAMap() throws Exception {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -64,7 +68,7 @@ void testSerializeStringJobParameter() throws Exception {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -74,7 +78,7 @@ void testSerializeDateJobParameter() throws Exception {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -84,7 +88,7 @@ void testSerializeDoubleJobParameter() throws Exception {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -94,7 +98,7 @@ void testSerializeLongJobParameter() throws Exception {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -104,7 +108,7 @@ void testSerializeNonIdentifyingJobParameter() throws Exception {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -117,7 +121,7 @@ void testSerializeJobParameters() throws Exception {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -127,7 +131,7 @@ void testSerializeEmptyJobParameters() throws IOException {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -149,7 +153,7 @@ void testComplexObject() throws Exception {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -160,7 +164,7 @@ void testSerializeRecords() throws IOException {

Map<String, Object> m2 = serializationRoundTrip(m1);

compareContexts(m1, m2);
assertEquals(m1.entrySet(), m2.entrySet());
}

@Test
Expand All @@ -169,13 +173,6 @@ void testNullSerialization() {
assertThrows(IllegalArgumentException.class, () -> serializer.serialize(null, null));
}

protected void compareContexts(Map<String, Object> m1, Map<String, Object> m2) {

for (Map.Entry<String, Object> entry : m1.entrySet()) {
assertThat(m2, hasEntry(entry.getKey(), entry.getValue()));
}
}

protected Map<String, Object> serializationRoundTrip(Map<String, Object> m1) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
getSerializer().serialize(m1, out);
Expand Down