Programming and So

Tips and tricks in Java

Spring Batch 2.0: Current Resource of a MultiResourceItemReader

with one comment

[UPDATE. This feature is available on Spring Batch 2.0.1. Thanks for your interest, Dan. So, following code it's just only an exercise on reflection and dirty access to private methods and fields]

MultiResourceItemReader class reads items from multiple resources sequentially.

Below a classic job configuration using this feature is exposed.

< ?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:batch="http://www.springframework.org/schema/batch"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/batch
                      http://www.springframework.org/schema/batch/spring-batch-2.0.xsd">

    <job id="sampleJob" job-repository="jobRepository">
        <step id="stepWSDB">
            <tasklet>
                <chunk processor="processor"
                    reader="multifileReader"
                    writer="itemWriter"
                    commit-interval="100" />
            </tasklet>
        </step>
    </job>

    <bean id="processor" class="foo.bar.CustomProcessor" />

    <bean id="multifileReader"
          class="org.springframework.batch.item.file.MultiResourceItemReader"
          scope="step"
          lazy-init="true">
<property name="resources" value="file://${loc.working.directory}/*.#{jobParameters[day]}" />
<property name="delegate" ref="itemReader" />
  </bean>

  <bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
     <!-- Configuration omitted -->
  </bean>

  <bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
     <!-- Configuration omitted -->
  </bean>

</beans>

Note. “job”, “step”, “tasklet” and “chunk” must appear as “batch:job”,… in the listing above, but WordPress sourcecode plugin does not want to show them so.

In some scenarios should be useful to know resource file name at processing time in order to perform some mappings or writings depending on this file name. However, there is no path to access this information using Spring Batch API.

So, MultiResourceItemReader must be modified (in a some dirty way) to share resource name across the Step Context.

import java.lang.reflect.Field;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.file.MultiResourceItemReader;
import org.springframework.core.io.Resource;

public class CustomMultiResourceItemReader extends MultiResourceItemReader
	<list <String>> {

  private static final Logger log = Logger.getLogger(CustomMultiResourceItemReader.class.getName());

  @Override
  public void open(ExecutionContext executionContext) throws ItemStreamException {

    super.open(executionContext);

    try {

      Field resourcesField = MultiResourceItemReader.class.getDeclaredField("resources");
      resourcesField.setAccessible(true);
      Resource[] resources = (Resource[]) resourcesField.get(this);

      executionContext.put("current.resource.name", resources[0].getFilename());

    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
  }

  @Override
  public void update(ExecutionContext executionContext) throws ItemStreamException {

    super.update(executionContext);

    try {

      Field indexField = MultiResourceItemReader.class.getDeclaredField("index");
      indexField.setAccessible(true);
      Object index = indexField.get(this);

      Field currentResourceField = index.getClass().getDeclaredField("currentResource");
      currentResourceField.setAccessible(true);
      Integer currentResource = (Integer)currentResourceField.get(index);

      Field resourcesField = MultiResourceItemReader.class.getDeclaredField("resources");
      resourcesField.setAccessible(true);
      Resource[] resources = (Resource[]) resourcesField.get(this);

      if (currentResource < resources.length) {
          executionContext.put("current.resource.name", resources[currentResource].getFile().getPath());
      } else {
          executionContext.put("current.resource.name", null);
      }

    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
  }

}

Note that this modification may only work with Spring Batch 2.0 because it’s accessing private fields and methods within core classes.

Written by angelborroy

May 15, 2009 at 1:13 pm

Posted in java

Tagged with

One Response

Subscribe to comments with RSS.

  1. Thanks for bringing this up.

    I logged an issue for this feature: http://jira.springframework.org/browse/BATCH-1243. The MultiResourceItemReader now has a method getCurrentResource() that returns the current Resource.

    Feel free to log issues yourself for future improvements. You can also post questions to the Spring Batch forum.

    Dan Garrette

    May 15, 2009 at 7:33 pm


Leave a Reply