de.langmi.spring.batch.examples.readers.support.CompositeCursorItemReaderTest.java Source code

Java tutorial

Introduction

Here is the source code for de.langmi.spring.batch.examples.readers.support.CompositeCursorItemReaderTest.java

Source

/**
 * Copyright 2011 Michael R. Lange <michael.r.lange@langmi.de>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.langmi.spring.batch.examples.readers.support;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.dbcp.BasicDataSource;
import org.junit.After;
import java.util.ArrayList;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.batch.item.ExecutionContext;
import static org.junit.Assert.*;
import org.junit.Before;
import org.springframework.batch.item.database.AbstractCursorItemReader;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.batch.test.MetaDataInstanceFactory;
import org.springframework.jdbc.core.RowMapper;

/**
 * CompositeItemStreamReaderTest with JdbcCursorItemReaders.
 *
 * @author Michael R. Lange <michael.r.lange@langmi.de>
 */
public class CompositeCursorItemReaderTest {

    private final CompositeCursorItemReader<String> reader = new CompositeCursorItemReader<String>();
    private static final String CREATE_TEST_TABLE = "CREATE TABLE TEST (ID INTEGER GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR (100))";
    private static final String SELECT = "SELECT NAME as name FROM TEST";
    private static final String INSERT = "INSERT INTO TEST (NAME) VALUES (?)";
    private static final int EXPECTED_COUNT = 20;
    private DataSource dataSource1;
    private DataSource dataSource2;

    @Test
    public void testRestart() throws Exception {
        // reader names
        final String readerName1 = "reader1";
        final String readerName2 = "reader2";

        // setup composite reader
        reader.setUnifyingMapper(new DefaultUnifyingStringItemsMapper());
        reader.setCursorItemReaders(new ArrayList<AbstractCursorItemReader<?>>() {

            {
                add(createJdbcCursorItemReader(dataSource1, readerName1));
                add(createJdbcCursorItemReader(dataSource2, readerName2));
            }
        });

        // open, provide "mock" ExecutionContext and fake restart scenario, 
        // it works because the name of reader is made distinct
        int alreadyRead = 2;
        ExecutionContext ec = new ExecutionContext();
        ec.put(readerName1 + "." + "read.count", alreadyRead);
        ec.put(readerName2 + "." + "read.count", alreadyRead);
        reader.open(ec);
        // read
        try {
            // this makes sure we test a restart scenario, first read item
            // should be alreadyRead, for my example files this is a "2" and
            // not the first line a "0"
            int count = alreadyRead;
            String line;
            while ((line = reader.read()) != null) {
                assertEquals(String.valueOf(count) + String.valueOf(count), line);
                count++;
            }
            // read count includes the alreadyRead items too
            assertEquals(EXPECTED_COUNT, count);
        } catch (Exception e) {
            throw e;
        } finally {
            reader.close();
        }
    }

    @Test
    public void testRead() throws Exception {
        // setup composite reader
        reader.setUnifyingMapper(new DefaultUnifyingStringItemsMapper());
        reader.setCursorItemReaders(new ArrayList<AbstractCursorItemReader<?>>() {

            {
                add(createJdbcCursorItemReader(dataSource1, "readerName1"));
                add(createJdbcCursorItemReader(dataSource2, "readerName2"));
            }
        });

        // open, provide "mock" ExecutionContext
        reader.open(MetaDataInstanceFactory.createStepExecution().getExecutionContext());
        // read
        try {
            int count = 0;
            String line;
            while ((line = reader.read()) != null) {
                assertEquals(String.valueOf(count) + String.valueOf(count), line);
                count++;
            }
            assertEquals(EXPECTED_COUNT, count);
        } catch (Exception e) {
            throw e;
        } finally {
            reader.close();
        }
    }

    /**
     * Helpermethod to create a JdbcCursorItemReader, sets the name too, to make restart
     * scenario possible - otherwise one reader would overwrite the restart data from
     * the other.
     * 
     * @param dataSource
     * @param name for restart
     * @return
     * @throws Exception 
     */
    private AbstractCursorItemReader<String> createJdbcCursorItemReader(DataSource dataSource, String name)
            throws Exception {
        JdbcCursorItemReader<String> jcir = new JdbcCursorItemReader<String>();
        jcir.setDataSource(dataSource);
        jcir.setSql(SELECT);
        jcir.setRowMapper(new RowMapper() {

            @Override
            public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
                return rs.getString(1);
            }
        });
        jcir.setName(name);
        jcir.setSaveState(true);
        // jcir.afterPropertiesSet(); not mandatory yet, just checks dataSource

        return jcir;
    }

    /**
     * Setup Datasources and create table for test.
     *
     * @throws Exception 
     */
    @Before
    public void setUp() throws Exception {
        dataSource1 = createDataSource("jdbc:hsqldb:mem:foodb");
        createTableWithTestData(dataSource1);

        dataSource2 = createDataSource("jdbc:hsqldb:mem:bardb");
        createTableWithTestData(dataSource2);
    }

    /**
     * Shutdown HSQLDB properly.
     *
     * @throws Exception 
     */
    @After
    public void tearDown() throws Exception {
        tearDownDataSource(dataSource1);
        tearDownDataSource(dataSource2);
    }

    /**
     * Create a HSQLDB in-memory based datasource.
     *
     * @param url
     * @return 
     */
    private DataSource createDataSource(final String url) {
        // DataSource Setup with apache commons 
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
        dataSource.setUrl(url);
        dataSource.setUsername("sa");
        dataSource.setPassword("");

        return dataSource;
    }

    /**
     * Create a table and fill with some test data.
     *
     * @param dataSource
     * @throws Exception 
     */
    private void createTableWithTestData(final DataSource dataSource) throws Exception {
        // create table
        Connection conn = dataSource.getConnection();
        Statement st = conn.createStatement();
        st.execute(CREATE_TEST_TABLE);
        conn.commit();
        st.close();
        conn.close();

        // fill with values
        conn = dataSource.getConnection();
        // prevent auto commit for batching
        conn.setAutoCommit(false);
        PreparedStatement ps = conn.prepareStatement(INSERT);
        // fill with values
        for (int i = 0; i < EXPECTED_COUNT; i++) {
            ps.setString(1, String.valueOf(i));
            ps.addBatch();
        }
        ps.executeBatch();
        conn.commit();
        ps.close();
        conn.close();
    }

    /**
     * Properly tears down a HSQLDB in-memory database.
     *
     * @param dataSource
     * @throws Exception 
     */
    private void tearDownDataSource(final DataSource dataSource) throws Exception {
        Connection conn = dataSource.getConnection();
        Statement st = conn.createStatement();
        st.execute("SHUTDOWN");
        conn.commit();
        st.close();
    }
}