2015-10-27 23 views
10

Ho un'applicazione Spring-batch che memorizza diversi oggetti time Java 8 in JobExecutionContext. Sto usando il serializzatore predefinito per il mio JobRespository. Sto affrontando delle eccezioni quando analizzo i dati che vengono scritti nella tabella BATCH_STEP_EXECUTION_CONTEXT. Ho un LocalDateTime che viene memorizzata come:Problemi di serializzazione Spring Batch con il time package Java 8

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

Questo porta ad un'eccezione quando provo a leggere dai dati JobExecution precedente:

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 

Sto usando Primavera-batch 3.0.5. PUBBLICAZIONE. Ho anche provato ad aggiornare alle ultime versioni di xstream (1.4.8) e Jettison (1.3.7), ma ottengo la stessa eccezione.

Questo sembra essere un problema noto con XStream (link). Il suggerimento era di registrare un convertitore personalizzato all'interno di XStream. Tuttavia, spring-batch non espone l'oggetto XStream effettivo per registrare un convertitore. Qualche suggerimento su come procedere?

+0

hai aggiunto l'annotazione @EnableBatchProcessing su una classe di configurazione? –

risposta

3

Spring Batch consente di configurare il proprio serializzatore per ExecutionContext implementando l'interfaccia ExecutionContextSerializer e immettendolo nello JobRepositoryFactoryBean.

Sei corretto nel fatto che non ti permettiamo di iniettare la tua istanza XStream attualmente (anche se sembra un ragionevole punto di estensione dato questo problema). Per ora, dovresti estendere o copiare XStreamExecutionContextStringSerializer e utilizzare la tua istanza XStream.

+0

Ho aggiunto il mio serializzatore cliente. Tuttavia, 'JobRepositoryFactoryBean' utilizza ancora il predefinito' XStreamExecutionContextStringSerializer', che sta portando a eccezioni come questa quando provo a leggere i dati del lavoro da 'JobExplorer': –

+0

' Causato da: com.thoughtworks.xstream.converters.ConversionException: Impossibile deserializzare l'oggetto con nuovi metodi readObject()/writeObject() ' ' ---- Informazioni di debug ---- ' ' classe: java.time.LocalDate' 'required-type: java.time.LocalDate' 'tipo-convertitore: com.thoughtworks.xstream.converters.reflection.SerializableConverter' –

+0

Sembra anche che premendo il pulsante di riavvio sull'interfaccia utente di Spring Batch Admin si attivi' JobExecutionController.restart', che sta tentando di serializzare con il predefinito 'XStreamExecutionContextStringSerializer'. . Ottengo le stesse eccezioni in quello scenario (e la traccia dello stack include XStreamExecutionContextStringSerializer piuttosto che il mio serializzatore personalizzato). –

1

Ho avuto lo stesso problema durante la deserializzazione di LocalDate dal contesto di esecuzione passo.

quindi devo fare il mio corretta convertitore:

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; 
    } 
} 

Dopo che devo creare il mio corretta XStreamExecutionContextStringSerializer per usare il mio convertitore

/** 
* 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()); 
    } 
} 

L'ultima fase consiste nella registrazione MyXStreamExecutionContextStringSerializer nel file execution-context.xml che registra il bean jobRepository

<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> 
Problemi correlati