Skip to content
Permalink
Browse files
Merge branch 'master' into BATCHEE-65
  • Loading branch information
rsandtner committed Nov 9, 2015
2 parents 7592acb + 8d89ac8 commit 420c4fc6535a9bc61ccf65bc7f871cdcd3c306c4
Showing 4 changed files with 197 additions and 17 deletions.
@@ -84,6 +84,10 @@
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-core</artifactId>
@@ -20,6 +20,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
@@ -44,13 +45,14 @@ public class DefaultDataRepresentationService implements DataRepresentationServi


public static final String BATCHEE_DATA_PREFIX = "BatchEE_data" + BATCHEE_SPLIT_TOKEN;
private Charset UTF8_CHARSET = StandardCharsets.UTF_8;

private static final Charset UTF8_CHARSET = StandardCharsets.UTF_8;

private static final Logger LOGGER = Logger.getLogger(DefaultDataRepresentationService.class.getName());


@Override
public void init(Properties batchConfig) {
// nothing to do
}

@Override
@@ -63,6 +65,18 @@ public <T> byte[] toInternalRepresentation(T dataObject) {
if (serialValue == null) {
serialValue = convertJava7DateTypes(dataObject);
}
if (serialValue == null) {
serialValue = convertJava8DateTypes(dataObject);
}
if (serialValue == null) {
serialValue = convertJodaDateTypes(dataObject);
}
if (serialValue == null) {
serialValue = convertCustomEnumTypes(dataObject);
}
if (serialValue == null) {
serialValue = convertCustomTypes(dataObject);
}
if (serialValue == null) {
// as last resort we do a simple java serialisation
serialValue = convertSerializableObjectTypes(dataObject);
@@ -89,6 +103,18 @@ public <T> T toJavaRepresentation(byte[] internalRepresentation) {
if (data == null) {
data = convertBackJava7DateTypes(typeVal, valueVal);
}
if (data == null) {
data = convertBackJava8DateTypes(typeVal, valueVal);
}
if (data == null) {
data = convertBackJodaDateTypes(typeVal, valueVal);
}
if (data == null) {
data = convertBackCustomEnumTypes(typeVal, valueVal);
}
if (data == null) {
data = convertBackCustomTypes(typeVal, valueVal);
}
}


@@ -106,12 +132,45 @@ public <T> T toJavaRepresentation(byte[] internalRepresentation) {
return data;
}

private <T> byte[] convertCustomEnumTypes(T dataObject) {
if (dataObject instanceof Enum) {
return toBatchEeData(dataObject.getClass(), ((Enum) dataObject).name());
}
return null;
}

private <T> T convertBackCustomEnumTypes(String typeVal, String valueVal) {
try {
Class typeClass = getClassLoader().loadClass(typeVal);
if (typeClass.isEnum()) {
return (T) Enum.valueOf(typeClass, valueVal);
}
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Cannot convert back BatchEE data: " + valueVal + " of Enum type " + typeVal);
}
return null;
}

/**
* This is an extension point for other serialisation algorithms.
*/
protected <T> byte[] convertCustomTypes(T dataObject) {
return null;
}

/**
* This is an extension point for other serialsation algorithms.
*/
private <T> T convertBackCustomTypes(String typeVal, String valueVal) {
return null;
}

/**
* This method converts java native types to a 'nice' string representation
* which will be stored in the database.
* @return the String representation or {@code null} if the dataObject was not a native Java type
*/
private <T> byte[] convertJavaNativeTypes(T dataObject) {
protected <T> byte[] convertJavaNativeTypes(T dataObject) {
// convert int, Integer, etc
if (dataObject instanceof Integer ||
dataObject instanceof String ||
@@ -125,7 +184,7 @@ private <T> byte[] convertJavaNativeTypes(T dataObject) {
return null;
}

private <T> T convertBackJavaNativeTypes(String typeVal, String value) {
protected <T> T convertBackJavaNativeTypes(String typeVal, String value) {
if (Integer.class.getName().equals(typeVal)) {
return (T) Integer.valueOf(value);
}
@@ -156,7 +215,7 @@ private <T> T convertBackJavaNativeTypes(String typeVal, String value) {
* <li>java.sql.Timestamp</li>
* </ul>
*/
private <T> byte[] convertJava7DateTypes(T dataObject) {
protected <T> byte[] convertJava7DateTypes(T dataObject) {
if (dataObject.getClass().equals(Date.class)) {
return toBatchEeData(Date.class, getSimpleDateFormat().format((Date) dataObject));
}
@@ -170,7 +229,7 @@ private <T> byte[] convertJava7DateTypes(T dataObject) {
return null;
}

private <T> T convertBackJava7DateTypes(String typeVal, String valueVal) {
protected <T> T convertBackJava7DateTypes(String typeVal, String valueVal) {
if (Date.class.getName().equals(typeVal)) {
try {
return (T) getSimpleDateFormat().parse(valueVal);
@@ -194,49 +253,123 @@ private <T> T convertBackJava7DateTypes(String typeVal, String valueVal) {
return null;
}

private <T> byte[] convertJodaDateTypes(T dataObject) {
String className = dataObject.getClass().getName();
if (className.equals("org.joda.time.LocalDate") ||
className.equals("org.joda.time.LocalDateTime") ||
className.equals("org.joda.time.LocalTime")) {
// joda date API does the right thing on toString
return toBatchEeData(dataObject.getClass(), dataObject.toString());
}
return null;
}

private <T> T convertBackJodaDateTypes(String typeVal, String valueVal) {
if (typeVal.equals("org.joda.time.LocalDate") ||
typeVal.equals("org.joda.time.LocalDateTime") ||
typeVal.equals("org.joda.time.LocalTime")) {
return (T) invokeStaticMethod(typeVal, "parse", String.class, valueVal);
}
return null;
}

private <T> byte[] convertJava8DateTypes(T dataObject) {
String className = dataObject.getClass().getName();
if (className.equals("java.time.LocalDate") ||
className.equals("java.time.LocalDateTime") ||
className.equals("java.time.LocalTime")) {
// joda date API does the right thing on toString
return toBatchEeData(dataObject.getClass(), dataObject.toString());
}
return null;
}

private <T> T convertBackJava8DateTypes(String typeVal, String valueVal) {
if (typeVal.equals("java.time.LocalDate") ||
typeVal.equals("java.time.LocalDateTime") ||
typeVal.equals("java.time.LocalTime")) {
return (T) invokeStaticMethod(typeVal, "parse", CharSequence.class, valueVal);
}
return null;
}

protected Object invokeStaticMethod(String typeVal, String methodName, Class paramType, String valueVal) {
try {
Class<?> typeClass = getClassLoader().loadClass(typeVal);
Method method = typeClass.getMethod(methodName, paramType);
return method.invoke(null, valueVal);
} catch (ReflectiveOperationException e) {
throw new BatchContainerServiceException("Cannot convert data [" + valueVal + "] of type [" + typeVal + "]", e );
}
}

/**
* This is the default operation if no other way to serialise the data was used
*/
private <T> byte[] convertSerializableObjectTypes(T dataObject) {
protected <T> byte[] convertSerializableObjectTypes(T dataObject) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(baos);
oos.writeObject(dataObject);
oos.close();
} catch (IOException e) {
throw new BatchContainerServiceException("Cannot convert data for [" + dataObject.toString() + "]");
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
// meh give up...
}
}
}
return baos.toByteArray();
}

private <T> T convertBackSerializableObjectTypes(byte[] internalRepresentation) {

protected <T> T convertBackSerializableObjectTypes(byte[] internalRepresentation) {
final ByteArrayInputStream readerChkptBA = new ByteArrayInputStream(internalRepresentation);
TCCLObjectInputStream readerOIS;
TCCLObjectInputStream readerOIS = null;
try {
// need to use the TCCL in case the batch is in a webapp but jbatch runtime is provided in a parent ClassLoader
readerOIS = new TCCLObjectInputStream(readerChkptBA);
T instance = (T) readerOIS.readObject();
readerOIS.close();
return instance;
} catch (final Exception ex) {
return null;
} finally {
if (readerOIS != null) {
try {
readerOIS.close();
} catch (IOException e) {
// meh give up...
}
}
}
}

private byte[] toBatchEeData(Class<?> type, String stringRepresentation) {
protected byte[] toBatchEeData(Class<?> type, String stringRepresentation) {
return (BATCHEE_DATA_PREFIX + type.getName() + BATCHEE_SPLIT_TOKEN + stringRepresentation).getBytes(UTF8_CHARSET);
}

private SimpleDateFormat getSimpleDateFormat() {
protected ClassLoader getClassLoader() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = this.getClass().getClassLoader();
}
return cl;
}

protected SimpleDateFormat getSimpleDateFormat() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
return sdf;
}

private SimpleDateFormat getTimestampDateFormat() {
/**
* Attention: The nanos must get concatenated separately as there is no formatter for nanos!
*/
protected SimpleDateFormat getTimestampDateFormat() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
return sdf;
@@ -17,6 +17,7 @@
package org.apache.batchee.test.data;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
@@ -32,6 +33,9 @@
import org.apache.batchee.container.services.data.DefaultDataRepresentationService;
import org.apache.batchee.spi.DataRepresentationService;
import org.apache.batchee.util.Batches;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.junit.Assert;
import org.junit.Test;
import static org.testng.Assert.assertEquals;
@@ -72,7 +76,15 @@ public void testJavaNatives() {

@Test
public void testJavaCustomEnum() {
//X TODO
assertRoundTripEquals(MySampleEnum.VALUE1, true);
assertRoundTripEquals(MySampleEnum.VALUE2, true);
assertRoundTripEquals(MySampleEnum.ANOTHER_VALUE, true);
}

public enum MySampleEnum {
VALUE1,
VALUE2,
ANOTHER_VALUE
}

@Test
@@ -102,12 +114,32 @@ public void testJavaCustomObjects() {

@Test
public void testJava8DateTimeViaReflection() {
//X TODO
Object java8LocalDate = createJava8datetype("java.time.LocalDate");
Object java8LocalTime = createJava8datetype("java.time.LocalTime");
Object java8LocalDateTime = createJava8datetype("java.time.LocalDateTime");
if (java8LocalDate != null) {
assertRoundTripEquals(java8LocalDate, true);
assertRoundTripEquals(java8LocalTime, true);
assertRoundTripEquals(java8LocalDateTime, true);
}
}

private Object createJava8datetype(String className) {
try {
Class<?> clazz = Class.forName(className);
Method now = clazz.getMethod("now");
return now.invoke(null);
} catch (ReflectiveOperationException e) {
// all fine, we are just not running on java8
}
return null;
}

@Test
public void testJodaDateTimeViaReflection() {
//X TODO
assertRoundTripEquals(LocalDate.now(), true);
assertRoundTripEquals(LocalDateTime.now(), true);
assertRoundTripEquals(LocalTime.now(), true);
}

@Test
@@ -190,6 +222,7 @@ public void setI(int i) {
}


@SuppressWarnings("unused")
public static class DummyReaderWithCheckpoint extends AbstractItemReader {


@@ -213,6 +246,7 @@ public Serializable checkpointInfo() throws Exception {
}
}

@SuppressWarnings("unused")
public static class DummyWriterWithCheckpoint extends AbstractItemWriter {

private Integer lastCount = null;
@@ -42,6 +42,7 @@
<atinject.version>1.0</atinject.version>
<batch-api.version>1.0</batch-api.version>
<jackson.version>2.2.2</jackson.version>
<jodatime.version>2.8.1</jodatime.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<batchee.scmPubUrl>https://svn.apache.org/repos/asf/incubator/batchee/site</batchee.scmPubUrl>
<batchee.scmPubCheckoutDirectory>${basedir}/.site-content</batchee.scmPubCheckoutDirectory>
@@ -164,6 +165,14 @@
<scope>test</scope>
</dependency>

<dependency>
<!-- We have optional support for joda time IF it is available on the classpath -->
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
<scope>test</scope>
</dependency>

<!-- Testing libraries -->
<dependency> <!-- TCK and default tests -->
<groupId>org.testng</groupId>

0 comments on commit 420c4fc

Please sign in to comment.