BATCH-1684 - Allow serializer to be injected into JobRepository
* Updated the job-repository tag to allow for a serializer and deserializer to be injected * Updated the XStreamExecutionContextStringSerializer to implement Serializer and Deserializer * Replaced the ExecutionContextStringSerializer with the ExecutionContextSerializer interface * Updated the XSD and parser to accept the injection of an implementation of the ExecutionContextSerializer from the job-repository tag * Updated the JdbcExecutionContextDao to use injected ExecutionContextSerializer implementation Reference: https://jira.springsource.org/browse/BATCH-1684
This commit is contained in:
committed by
Gunnar Hillert
parent
38ef37b882
commit
64ca2608ad
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.springframework.batch.core.repository.dao;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
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 org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Michael Minella
|
||||
*
|
||||
*/
|
||||
public class DefaultExecutionContextSerializerTests {
|
||||
|
||||
private DefaultExecutionContextSerializer serializer;
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
serializer = new DefaultExecutionContextSerializer();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerializeAMap() throws Exception {
|
||||
Map<String, Object> m1 = new HashMap<String, Object>();
|
||||
m1.put("object1", Long.valueOf(12345L));
|
||||
m1.put("object2", "OBJECT TWO");
|
||||
// Use a date after 1971 (otherwise daylight saving screws up)...
|
||||
m1.put("object3", new Date(123456790123L));
|
||||
m1.put("object4", new Double(1234567.1234D));
|
||||
|
||||
Map<String, Object> m2 = serializationRoundTrip(m1);
|
||||
|
||||
compareContexts(m1, m2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexObject() throws Exception {
|
||||
Map<String, Object> m1 = new HashMap<String, Object>();
|
||||
ComplexObject o1 = new ComplexObject();
|
||||
o1.setName("02345");
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
m.put("object1", Long.valueOf(12345L));
|
||||
m.put("object2", "OBJECT TWO");
|
||||
o1.setMap(m);
|
||||
o1.setNumber(new BigDecimal("12345.67"));
|
||||
ComplexObject o2 = new ComplexObject();
|
||||
o2.setName("Inner Object");
|
||||
o2.setMap(m);
|
||||
o2.setNumber(new BigDecimal("98765.43"));
|
||||
o1.setObj(o2);
|
||||
m1.put("co", o1);
|
||||
|
||||
Map<String, Object> m2 = serializationRoundTrip(m1);
|
||||
|
||||
compareContexts(m1, m2);
|
||||
}
|
||||
|
||||
@Test (expected=IllegalArgumentException.class)
|
||||
public void testNullSerialization() throws Exception {
|
||||
serializer.serialize(null, null);
|
||||
}
|
||||
|
||||
private void compareContexts(Map<String, Object> m1, Map<String, Object> m2) {
|
||||
for (String key : m1.keySet()) {
|
||||
System.out.println("m1 = " + m1 + " m2 = " + m2);
|
||||
assertEquals("Bad key/value for " + key, m1.get(key), m2.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> serializationRoundTrip(Map<String, Object> m1) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
serializer.serialize(m1, out);
|
||||
|
||||
String s = out.toString();
|
||||
|
||||
InputStream in = new ByteArrayInputStream(s.getBytes());
|
||||
Map<String, Object> m2 = (Map<String, Object>) serializer.deserialize(in);
|
||||
return m2;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class ComplexObject implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String name;
|
||||
private BigDecimal number;
|
||||
private ComplexObject obj;
|
||||
private Map<String,Object> map;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public BigDecimal getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(BigDecimal number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public ComplexObject getObj() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
public void setObj(ComplexObject obj) {
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
public Map<String,Object> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public void setMap(Map<String,Object> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ComplexObject that = (ComplexObject) o;
|
||||
|
||||
if (map != null ? !map.equals(that.map) : that.map != null) return false;
|
||||
if (name != null ? !name.equals(that.name) : that.name != null) return false;
|
||||
if (number != null ? !number.equals(that.number) : that.number != null) return false;
|
||||
if (obj != null ? !obj.equals(that.obj) : that.obj != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = (name != null ? name.hashCode() : 0);
|
||||
result = 31 * result + (number != null ? number.hashCode() : 0);
|
||||
result = 31 * result + (obj != null ? obj.hashCode() : 0);
|
||||
result = 31 * result + (map != null ? map.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ComplexObject [name=" + name + ", number=" + number + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@ package org.springframework.batch.core.repository.dao;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@@ -9,22 +12,26 @@ import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.repository.ExecutionContextSerializer;
|
||||
|
||||
/**
|
||||
* @author Thomas Risberg
|
||||
* @author Michael Minella
|
||||
*/
|
||||
public class XStreamExecutionContextStringSerializerTests {
|
||||
|
||||
ExecutionContextStringSerializer serializer;
|
||||
ExecutionContextSerializer serializer;
|
||||
|
||||
@Before
|
||||
public void onSetUp() throws Exception {
|
||||
serializer = new XStreamExecutionContextStringSerializer();
|
||||
((XStreamExecutionContextStringSerializer)serializer).afterPropertiesSet();
|
||||
XStreamExecutionContextStringSerializer serializerDeserializer = new XStreamExecutionContextStringSerializer();
|
||||
(serializerDeserializer).afterPropertiesSet();
|
||||
|
||||
serializer = serializerDeserializer;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSerializeAMap() {
|
||||
public void testSerializeAMap() throws Exception {
|
||||
Map<String, Object> m1 = new HashMap<String, Object>();
|
||||
m1.put("object1", Long.valueOf(12345L));
|
||||
m1.put("object2", "OBJECT TWO");
|
||||
@@ -32,15 +39,13 @@ public class XStreamExecutionContextStringSerializerTests {
|
||||
m1.put("object3", new Date(123456790123L));
|
||||
m1.put("object4", new Double(1234567.1234D));
|
||||
|
||||
String s = serializer.serialize(m1);
|
||||
|
||||
Map<String, Object> m2 = serializer.deserialize(s);
|
||||
Map<String, Object> m2 = serializationRoundTrip(m1);
|
||||
|
||||
compareContexts(m1, m2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexObject() {
|
||||
public void testComplexObject() throws Exception {
|
||||
Map<String, Object> m1 = new HashMap<String, Object>();
|
||||
ComplexObject o1 = new ComplexObject();
|
||||
o1.setName("02345");
|
||||
@@ -56,19 +61,36 @@ public class XStreamExecutionContextStringSerializerTests {
|
||||
o1.setObj(o2);
|
||||
m1.put("co", o1);
|
||||
|
||||
String s = serializer.serialize(m1);
|
||||
|
||||
Map<String, Object> m2 = serializer.deserialize(s);
|
||||
Map<String, Object> m2 = serializationRoundTrip(m1);
|
||||
|
||||
compareContexts(m1, m2);
|
||||
}
|
||||
|
||||
@Test (expected=IllegalArgumentException.class)
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testNullSerialization() throws Exception {
|
||||
serializer.serialize(null, null);
|
||||
}
|
||||
|
||||
private void compareContexts(Map<String, Object> m1, Map<String, Object> m2) {
|
||||
for (String key : m1.keySet()) {
|
||||
System.out.println("m1 = " + m1 + " m2 = " + m2);
|
||||
assertEquals("Bad key/value for " + key, m1.get(key), m2.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> serializationRoundTrip(Map<String, Object> m1) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
serializer.serialize(m1, out);
|
||||
|
||||
String s = out.toString();
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
|
||||
Map<String, Object> m2 = (Map<String, Object>) serializer.deserialize(in);
|
||||
return m2;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class ComplexObject {
|
||||
private String name;
|
||||
@@ -109,6 +131,7 @@ public class XStreamExecutionContextStringSerializerTests {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
@@ -123,6 +146,7 @@ public class XStreamExecutionContextStringSerializerTests {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = (name != null ? name.hashCode() : 0);
|
||||
@@ -136,6 +160,6 @@ public class XStreamExecutionContextStringSerializerTests {
|
||||
public String toString() {
|
||||
return "ComplexObject [name=" + name + ", number=" + number + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,19 @@ import static org.easymock.EasyMock.verify;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.repository.ExecutionContextSerializer;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.dao.DefaultExecutionContextSerializer;
|
||||
import org.springframework.batch.core.repository.dao.XStreamExecutionContextStringSerializer;
|
||||
import org.springframework.batch.item.database.support.DataFieldMaxValueIncrementerFactory;
|
||||
import org.springframework.core.serializer.Serializer;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
|
||||
import org.springframework.jdbc.support.lob.DefaultLobHandler;
|
||||
@@ -45,7 +50,7 @@ import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class JobRepositoryFactoryBeanTests {
|
||||
|
||||
@@ -81,26 +86,26 @@ public class JobRepositoryFactoryBeanTests {
|
||||
expect(dataSource.getConnection()).andReturn(con);
|
||||
expect(con.getMetaData()).andReturn(dmd);
|
||||
expect(dmd.getDatabaseProductName()).andReturn("Oracle");
|
||||
|
||||
|
||||
expect(incrementerFactory.isSupportedIncrementerType("ORACLE")).andReturn(true);
|
||||
expect(incrementerFactory.getSupportedIncrementerTypes()).andReturn(new String[0]);
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_SEQ")).andReturn(new StubIncrementer());
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_EXECUTION_SEQ")).andReturn(new StubIncrementer());
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "STEP_EXECUTION_SEQ")).andReturn(new StubIncrementer());
|
||||
replay(dataSource,con,dmd, incrementerFactory);
|
||||
|
||||
|
||||
factory.afterPropertiesSet();
|
||||
factory.getObject();
|
||||
|
||||
verify(incrementerFactory);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOracleLobHandler() throws Exception {
|
||||
|
||||
factory.setDatabaseType("ORACLE");
|
||||
|
||||
|
||||
incrementerFactory = createNiceMock(DataFieldMaxValueIncrementerFactory.class);
|
||||
expect(incrementerFactory.isSupportedIncrementerType("ORACLE")).andReturn(true);
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_SEQ")).andReturn(new StubIncrementer());
|
||||
@@ -112,14 +117,14 @@ public class JobRepositoryFactoryBeanTests {
|
||||
factory.afterPropertiesSet();
|
||||
LobHandler lobHandler = (LobHandler) ReflectionTestUtils.getField(factory, "lobHandler");
|
||||
assertTrue(lobHandler instanceof OracleLobHandler);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomLobHandler() throws Exception {
|
||||
|
||||
factory.setDatabaseType("ORACLE");
|
||||
|
||||
|
||||
incrementerFactory = createNiceMock(DataFieldMaxValueIncrementerFactory.class);
|
||||
expect(incrementerFactory.isSupportedIncrementerType("ORACLE")).andReturn(true);
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_SEQ")).andReturn(new StubIncrementer());
|
||||
@@ -127,13 +132,52 @@ public class JobRepositoryFactoryBeanTests {
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "STEP_EXECUTION_SEQ")).andReturn(new StubIncrementer());
|
||||
replay(dataSource,incrementerFactory);
|
||||
factory.setIncrementerFactory(incrementerFactory);
|
||||
|
||||
|
||||
LobHandler lobHandler = new DefaultLobHandler();
|
||||
factory.setLobHandler(lobHandler);
|
||||
|
||||
factory.afterPropertiesSet();
|
||||
assertEquals(lobHandler, ReflectionTestUtils.getField(factory, "lobHandler"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void tesDefaultSerializer() throws Exception {
|
||||
|
||||
factory.setDatabaseType("ORACLE");
|
||||
|
||||
incrementerFactory = createNiceMock(DataFieldMaxValueIncrementerFactory.class);
|
||||
expect(incrementerFactory.isSupportedIncrementerType("ORACLE")).andReturn(true);
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_SEQ")).andReturn(new StubIncrementer());
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_EXECUTION_SEQ")).andReturn(new StubIncrementer());
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "STEP_EXECUTION_SEQ")).andReturn(new StubIncrementer());
|
||||
replay(dataSource,incrementerFactory);
|
||||
factory.setIncrementerFactory(incrementerFactory);
|
||||
|
||||
factory.afterPropertiesSet();
|
||||
Serializer<Map<String, Object>> serializer = (Serializer<Map<String,Object>>) ReflectionTestUtils.getField(factory, "serializer");
|
||||
assertTrue(serializer instanceof XStreamExecutionContextStringSerializer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomSerializer() throws Exception {
|
||||
|
||||
factory.setDatabaseType("ORACLE");
|
||||
|
||||
incrementerFactory = createNiceMock(DataFieldMaxValueIncrementerFactory.class);
|
||||
expect(incrementerFactory.isSupportedIncrementerType("ORACLE")).andReturn(true);
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_SEQ")).andReturn(new StubIncrementer());
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_EXECUTION_SEQ")).andReturn(new StubIncrementer());
|
||||
expect(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "STEP_EXECUTION_SEQ")).andReturn(new StubIncrementer());
|
||||
replay(dataSource,incrementerFactory);
|
||||
factory.setIncrementerFactory(incrementerFactory);
|
||||
|
||||
ExecutionContextSerializer customSerializer = new DefaultExecutionContextSerializer();
|
||||
factory.setSerializer(customSerializer);
|
||||
|
||||
factory.afterPropertiesSet();
|
||||
assertEquals(customSerializer, ReflectionTestUtils.getField(factory, "serializer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -161,7 +205,7 @@ public class JobRepositoryFactoryBeanTests {
|
||||
expect(incrementerFactory.isSupportedIncrementerType("mockDb")).andReturn(true);
|
||||
expect(incrementerFactory.getSupportedIncrementerTypes()).andReturn(new String[0]);
|
||||
replay(incrementerFactory);
|
||||
|
||||
|
||||
factory.afterPropertiesSet();
|
||||
fail();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user