2012-02-21 3 views
1

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

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

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

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 protected]"); 

     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 никогда не умирает?

+0

Я могу только предположить, что это тасклет работает в отдельном потоке, а 'main' поток уже останавливая контекст приложения, вызов' BasicDataSource.close() '. Можете ли вы поставить точку останова на этот метод и посмотреть, кто отвечает за его вызов? Я уверен, что это 'ApplicationContext.close()' ... Кроме того, как выглядит ваш интеграционный тест? Как начать контекст Spring? –

+0

Пожалуйста, см. ** ОБНОВЛЕНИЕ ** в нижней части моего оригинального сообщения. – rapt

+0

@Tomasz Nurkiewicz - как вы видите, задание определено для запуска в другом потоке, кроме потока контроллера (и теста junit). Поэтому, я думаю, когда заканчивается поток контроллера, junit закрывает все открытые ресурсы (например, открытые источники данных). Как сообщить junit ждать с этим закрытием ресурсов до тех пор, пока не закончится задание? – rapt

ответ

0

У меня есть аналогичная проблема в моем тесте.

Дело в том, что ваше пакетное задание запускает асинхронно и тест завершен, но работа выполняется (или собирается запустить). И контекст уже закрыт (и все бобы тоже закрыты, включая 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>