«SQLException: источник данных закрыт» при модульном тестировании контроллера, вызывающего Spring-Batch. - PullRequest
1 голос
/ 22 февраля 2012

Я получаю следующее исключение при запуске теста JUnit, в котором я тестирую контроллер Spring-MVC, который вызывает задание Spring-Batch.Задание включает в себя два тасклета: сначала чтение из файла и запись в БД, а затем обновление БД.Оба тасклета используют одну и ту же БД.Насколько я вижу, исключение говорит мне, что источник данных закрыт, но в БД я вижу, что первый тасклет выполнен, а второй - нет.

Не могли бы вы прийти?Можно ли предположить, почему источник данных закрыт (???) во время второго тасклета (обновление БД)?

Задание выполняет оба тасклета, когда я вызываю контроллер из браузера.

ERROR [taskExecutor-1] (AbstractJob.java:306) - Encountered fatal error executing job
org.springframework.batch.core.JobExecutionException: Flow execution ended unexpectedly
    at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:141)
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:281)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:120)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
Caused by: org.springframework.batch.core.job.flow.FlowExecutionException: Ended flow=writeProductsJob at state=writeProductsJob.readWrite with exception
    at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:152)
    at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:124)
    at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:135)
    ... 5 more
Caused by: org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: Data source is closed
    at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:240)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy27.getStepExecutionCount(Unknown Source)
    at org.springframework.batch.core.job.SimpleStepHandler.shouldStart(SimpleStepHandler.java:210)
    at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:117)
    at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:61)
    at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:60)
    at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:144)
    ... 7 more
Caused by: java.sql.SQLException: Data source is closed
    at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1362)
    at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
    at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:202)
    ... 18 more
Exception in thread "taskExecutor-1" org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: Data source is closed
    at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:240)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy27.update(Unknown Source)
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:329)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:120)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
Caused by: java.sql.SQLException: Data source is closed
    at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1362)
    at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
    at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:202)
    ... 11 more
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.628 sec

ОБНОВЛЕНИЕ:

Класс тестирования:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext-test.xml"})
public class UploadFileValidatorTest {

    @Autowired
    private ApplicationContext applicationContext;

    private MockMultipartHttpServletRequest request;
    private MockHttpServletResponse response;

    @Autowired
    private MyController controller;

    private MessageSource messageSource;

    @Before
    public void setUp() {
       request = new MockMultipartHttpServletRequest();
       response = new MockHttpServletResponse();
    }

    @Test
    @DirtiesContext
    public void testDoSomething() throws Exception {

        DiskFileItem fileItem = null;

        final File TEST_FILE = applicationContext.getResource("classpath:csv_example.txt").getFile();

        try
        {
            fileItem = (DiskFileItem) new DiskFileItemFactory().createItem("fileData", "text/plain", true, TEST_FILE.getName());
            InputStream input =  new FileInputStream(TEST_FILE);
            OutputStream os = fileItem.getOutputStream();
            int ret = input.read();
            while ( ret != -1 )
            {
                os.write(ret);
                ret = input.read();
            }
            os.flush();
            System.out.println("diskFileItem.getString() = " + fileItem.getString());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        /////////////////////////////////////////////////////////////////////

        request.addFile(multipartFile);

        request.addParameter("email", "email@email.com");

        request.setRequestURI("/book/upload.html");

        final ModelAndView mav = new AnnotationMethodHandlerAdapter().handle(request, response, controller);

        BindingResult bindException = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + "uploadFile");
        for (Object object : bindException.getAllErrors()) {
            if(object instanceof FieldError) {
                FieldError fieldError = (FieldError) object;

                assertEquals(fieldError.getField(), "fileData");

                System.out.println(messageSource.getMessage((FieldError) object, null));
            }
        }

    }

}

Пакетная конфигурация:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
    <property name="transactionManager" ref="transactionManager" />
</bean>

<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
    <property name="taskExecutor" ref="taskExecutor"/>
</bean>

<bean id="taskExecutor"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="5" />
    <property name="maxPoolSize" value="5" />
</bean>

ОБНОВЛЕНИЕ 2:

Я попытался добавить следующий код в конце метода теста testDoSomething():

ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();

int activeCount = threadGroup.activeCount();

Thread[] list = new Thread[activeCount];

threadGroup.enumerate(list);

for (Thread thread : list) {
    if (thread.getName().startsWith("taskExecutor")) {
        System.out.println("WAITING FOR THREAD: " + thread.getName());
        try {
            thread.join();
        }
        catch (InterruptedException ignore) {}
    }
}

Теперь все задание выполнено, но testDoSomething() никогда не кончается, кажется, что он вечно ждет смерти taskExecutor.Есть идеи, почему taskExecutor никогда не умирает?

1 Ответ

0 голосов
/ 09 ноября 2012

У меня похожая проблема в моем тесте.

Дело в том, что ваше пакетное задание запускает асинхронно и тест завершен, но задание выполняется (или готовится к выполнению).И контекст уже закрыт (и все компоненты тоже закрыты, включая DataSource).Поэтому вам нужно использовать синхронный JobLauhcher в своем тесте.Пример определения двух пусковых установок:

  <beans:bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">    
        <beans:property name="jobRepository" ref="jobRepository"/>
    </beans:bean>

    <beans:bean id="jobLauncher_asynch"
          class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <beans:property name="jobRepository" ref="jobRepository" />
        <beans:property name="taskExecutor" ref="taskExecutor"/>
    </beans:bean>
...