2015-10-27 26 views
10

私は、いくつかのJava 8時間オブジェクトをJobExecutionContextに格納するSpringバッチアプリケーションを持っています。私は自分のJobRespositoryにデフォルトのシリアライザを使用しています。私は、BATCH_STEP_EXECUTION_CONTEXTテーブルに書き込まれているデータを解析する際に例外に直面しています。私は以前JobExecutionデータから読み取るしようとすると、これは例外につながるバグのシリアル化の問題Java 8のタイムパッケージの問題

{ 
    "@resolves-to": "java.time.Ser", 
    "byte": [5, 
    8, 
    18, 
    8, 
    45, 
    50], 
    "int": [2015, 
    10000000] 
} 

Caused by: java.lang.ClassCastException: java.lang.Byte cannot be cast to java.lang.Integer 
at com.thoughtworks.xstream.core.util.CustomObjectInputStream.readInt(CustomObjectInputStream.java:144) ~[xstream-1.4.8.jar:1.4.8] 
at java.time.LocalDate.readExternal(LocalDate.java:2070) ~[na:1.8.0_45] 
at java.time.LocalDateTime.readExternal(LocalDateTime.java:2002) ~[na:1.8.0_45] 
at java.time.Ser.readInternal(Ser.java:259) ~[na:1.8.0_45] 
at java.time.Ser.readExternal(Ser.java:246) ~[na:1.8.0_45] 
at com.thoughtworks.xstream.converters.reflection.ExternalizableConverter.unmarshal(ExternalizableConverter.java:167) ~[xstream-1.4.8.jar:1.4.8] 
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72) ~[xstream-1.4.8.jar:na] 
... 97 common frames omitted 

私は春バッチ3.0.5を使用しています私はとして格納されているのLocalDateTimeを持っています。リリース。私はxstream(1.4.8)とJettison(1.3.7)の最新バージョンへのアップグレードも試みましたが、同じ例外があります。

これはXStream(link)の既知の問題のようです。提案はXStream内にカスタムコンバータを登録することでした。ただし、spring-batchはコンバータを登録するために実際のXStreamオブジェクトを公開しません。どのように進むべきかについての提案はありますか?

+0

@EnableBatchProcessingアノテーションを設定クラスに追加しましたか? –

答えて

3

ExecutionContextの独自のシリアライザをコンフィグレーションして、ExecutionContextSerializerインターフェイスを実装し、JobRepositoryFactoryBeanに注入することができます。

現在のところ、独自のXStreamインスタンスを挿入することは許可されていません(ただし、この問題では合理的な拡張ポイントのようです)。今のところ、XStreamExecutionContextStringSerializerを拡張またはコピーし、独自のXStreamインスタンスを使用する必要があります。

+0

私は自分の顧客シリアライザを追加しました。しかし、 'JobRepositoryFactoryBean'はデフォルトの' XStreamExecutionContextStringSerializer'を使用しています。これは 'JobExplorer'からジョブデータを読み込もうとしたときに例外を発生させます: –

+0

'原因:com.thoughtworks.xstream.converters.ConversionException: '----デバッグ情報----' 'class:java.time.LocalDate' ' required-type:java.time.LocalDate' 新しいreadObject()/ writeObject()メソッドでオブジェクトを逆シリアル化できません。 'convert-type:com.thoughtworks.xstream.converters.reflection.SerializableConverter' –

+0

また、Spring Batch Admin UIの再起動ボタンを押すと、デフォルトの' XStreamExecutionContextStringSerializer'でシリアル化しようとしている 'JobExecutionController.restart' 。私はそのシナリオで同じ例外を受け取ります(そして私のスタックトレースには私のカスタムシリアライザではなくXStreamExecutionContextStringSerializerが含まれています)。 –

1

ステップ実行コンテキストからLocalDateをデシリアライズするときに同じ問題が発生しました。

だから私は私の適切なコンバーターを行う必要があります。その後

public class DateConverter implements Converter { 

    private static final String   DEFAULT_DATE_PATTERN = "yyyy-MM-dd"; 
    private static final DateTimeFormatter DEFAULT_DATE_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN); 

    public DateConverter() { 
     super(); 
    } 

    public boolean canConvert(Class clazz) { 
     return LocalDate.class.isAssignableFrom(clazz); 
    } 

    /** 
    * Convert LocalDate to String 
    */ 
    public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { 
     LocalDate date = (LocalDate) value; 
     String result = date.format(DEFAULT_DATE_FORMATTER); 
     writer.setValue(result); 
    } 

    /** 
    * convert Xml to LocalDate 
    */ 
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { 
     LocalDate result = LocalDate.parse(reader.getValue(), DEFAULT_DATE_FORMATTER); 
     return result; 
    } 
} 

私は最後のステップは、ファイル内のMyXStreamExecutionContextStringSerializerを登録することである

/** 
* My XStreamExecutionContextStringSerializer 
* @since 1.0 
*/ 
public class MyXStreamExecutionContextStringSerializer implements ExecutionContextSerializer, InitializingBean { 

    private ReflectionProvider reflectionProvider = null; 

    private HierarchicalStreamDriver hierarchicalStreamDriver; 

    private XStream xstream; 

    public void setReflectionProvider(ReflectionProvider reflectionProvider) { 
     this.reflectionProvider = reflectionProvider; 
    } 

    public void setHierarchicalStreamDriver(HierarchicalStreamDriver hierarchicalStreamDriver) { 
     this.hierarchicalStreamDriver = hierarchicalStreamDriver; 
    } 

    @Override 
    public void afterPropertiesSet() throws Exception { 
     init(); 
    } 

    public synchronized void init() throws Exception { 
     if (hierarchicalStreamDriver == null) { 
      this.hierarchicalStreamDriver = new JettisonMappedXmlDriver(); 
     } 
     if (reflectionProvider == null) { 
      xstream = new XStream(hierarchicalStreamDriver); 
     } 
     else { 
      xstream = new XStream(reflectionProvider, hierarchicalStreamDriver); 
     } 

     // Convert LocalDate 
     xstream.registerConverter(new DateConverter()); 
    } 

    /** 
    * Serializes the passed execution context to the supplied OutputStream. 
    * 
    * @param context 
    * @param out 
    * @see Serializer#serialize(Object, OutputStream) 
    */ 
    @Override 
    public void serialize(Map<String, Object> context, OutputStream out) throws IOException { 
     Assert.notNull(context); 
     Assert.notNull(out); 

     out.write(xstream.toXML(context).getBytes()); 
    } 

    /** 
    * Deserializes the supplied input stream into a new execution context. 
    * 
    * @param in 
    * @return a reconstructed execution context 
    * @see Deserializer#deserialize(InputStream) 
    */ 
    @SuppressWarnings("unchecked") 
    @Override 
    public Map<String, Object> deserialize(InputStream in) throws IOException { 
     BufferedReader br = new BufferedReader(new InputStreamReader(in)); 

     StringBuilder sb = new StringBuilder(); 

     String line; 
     while ((line = br.readLine()) != null) { 
      sb.append(line); 
     } 

     return (Map<String, Object>) xstream.fromXML(sb.toString()); 
    } 
} 

私のコンバータを使用するためのXStreamExecutionContextStringSerializer私の適切なを作成する必要がありますBeanジョブを登録するexecution-context.xml

<bean id="jobRepository" 
    class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="transactionManager" ref="transactionManager" /> 
    <property name="tablePrefix" value="${batch.table.prefix:BATCH.BATCH_}" /> 
    <property name="serializer"> <bean class="com.batch.config.MyXStreamExecutionContextStringSerializer"/> </property> 
</bean>