7

I am creating a Spring Batch job with folowing xml:

<batch:job id="simulatorJob" restartable="false">
    <batch:step id="step1">
        <batch:tasklet transaction-manager="transactionManager">
            <batch:chunk reader="stockListner" writer="customWriter"
                commit-interval="5">
            </batch:chunk>
        </batch:tasklet>
    </batch:step>
</batch:job>

<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />

<bean id="stockListner" class="com.XXX.stock.java.StockReader" scope="step">
    <property name="URL" value="NTPC,TCS" />
    <!-- <property name="URL" value="NTPC" /> -->
    <!-- <property name="URL" value="TCS" /> -->
</bean>

<bean id="customWriter" class="com.XXX.stock.java.FlatFileWriter" />

This is my reader class:

private String URL;

public String getURL() {
    return URL;
}

public void setURL(String uRL) {
    URL = uRL;
}

public ArrayList<StockData> read() throws Exception, UnexpectedInputException,ParseException, NonTransientResourceException {
    ArrayList<StockData> list = new ArrayList<StockData>();
    String[] splitStocks = URL.split(",");      

    for(int i=0; i < splitStocks.length;i++){
        list.add(stockReader.getStockData(splitStocks[i]));     
    }
    return list;        
}  

But the reader class keeps on running. How do I stop at first run?

I tried this aswell:

public StockData read() throws Exception, UnexpectedInputException,ParseException, NonTransientResourceException {
    ArrayList<StockData> list = new ArrayList<StockData>();
    String[] splitStocks = URL.split(",");

    for(int i=0; i < splitStocks.length;i++)
    {  
           return stockReader.getStockData(splitStocks[i]);
    }

    return null;
}  

The read() function goes into loop..

Amaresh
  • 3,231
  • 7
  • 37
  • 60

3 Answers3

4

Quoting ItemReader.read() Javadoc:

Reads a piece of input data and advance to the next one. Implementations must return null at the end of the input data set.

As such, you need to return null after the first read to indicate to Spring Batch that there is nothing more to read.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • Your method `read()` must return only one `StockData`, not a list. Keep track of what parts of `splitStocks ` you already sent and return only one `splitStocks[i]` until there is no more and return `null` (or use a list like you started to do). – Gaël J Oct 27 '15 at 12:29
  • @Gael If not arraylist then how do i send multiple values of – Amaresh Oct 27 '15 at 12:50
  • @Gael I have tried these as per ur comments..but no luck – Amaresh Oct 27 '15 at 13:23
  • See my answer using `ListIteamReader` – Gaël J Oct 27 '15 at 14:00
4

This simple delegate encapsulate one-time reading.

class OneItemReader<T> implements ItemReader<T> {
  boolean read = false;
  ItemReader<T> delegate;

  @Override
  public T read() {
    if(read) {
      return null;
    }
    T item = delegate.read();
    read = true;
    return item;
  }
}

You can create you own reader without think about one-time reading and wrap it using this small delegate.

Your StockURLReader may be defined as

class StockURLReader implements ItemReader<StockReader> {
  String[] tokens = new String[0];
  int index = 0;
  StockDAO stockReader;

  void setURL(String URL) {
    this.tokens = URL.split(",");
    index = 0;
  }

  StockData read() {
    if(index < tokens.length) {
      return stockReader.getStockData(tokens[index++]);
    }
    return null;
  }
}

Create OneTimeReader and set StockURLReader as delegate and you have separate StockData reading from one-time reading logic.
If you want to read a group of StockData the best solution is to create a StockDataListBean where you store all StockData read from a splitted URL.

class StockDataListBean {
  List<StockData> data = new LinkedList<StockData>();
}

and modify StockURLReader as:

class StockURLReader implements ItemReader<StockDataListBean> {
  String[] URLs = new String[0];
  int index;
  StockDAO stockReader;

  void setURLs(String[] URL) {
    this.URLs = URL;
    index = 0;
  }

  StockDataListBean read() {
    StockDataListBean item = null;
    if(index < URLs.length)
    {
      item = new StockDataListBean();
      for(String token : URLs[index].split(",").length)
      {
        item.data.add(stockReader.getStockData(token));
      }
    }
    return item;
  }
}
Luca Basso Ricci
  • 17,829
  • 2
  • 47
  • 69
  • I believe @Aman has more than one `StockData` item to read unlike what its first try suggested (i.e. declare `ArrayList read()`). – Gaël J Oct 27 '15 at 16:15
1

You could use a ListIteamReader like this (example, not tested) :

public class StockReader extends ListItemReader<StockData> {

    public StockReader(String URL) {
         super(createListFromUrl(URL));
    }

    public List<StockData> createListFromUrl(String URL) {
        // create your ArrayList<StockData> list from URL
    }

}
Gaël J
  • 11,274
  • 4
  • 17
  • 32