1

I have created a simple job in Spring Batch with Spring Boot to be executed as a task with Spring Cloud Task (all in STS4). If I execute it as a Spring Boot App, te execution is correct and without problems, but if I compile the project or try to launch the test, the execution of the job is correct:

o.s.c.t.b.l.TaskBatchExecutionListener   : The job execution id 19 was run within the task execution 56
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
com.example.demo.MyTasklet               : Executing Tasklet: STEP 1
o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 16ms
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step2]
com.example.demo.MyTasklet               : Executing Tasklet: STEP 2
o.s.batch.core.step.AbstractStep         : Step: [step2] executed in 7ms
o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job2]] completed with the following parameters: [{run.id=7, -spring.output.ansi.enabled=always}] and the following status: [COMPLETED] in 52ms
com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown initiated...
com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown completed.

But after that, it happends an exception:

o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@576d5deb] to prepare test instance [com.example.demo.DemoJobApplicationTests@173ed316]

java.lang.IllegalStateException: The ApplicationContext loaded for [[MergedContextConfiguration@e350b40 testClass = DemoJobApplicationTests, locations = '{}', classes = '{class com.example.demo.DemoJobApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@5e82df6a, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@50a638b5, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@130161f7, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@479d31f3], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]] is not active. This may be due to one of the following reasons: 1) the context was closed programmatically by user code; 2) the context was closed during parallel test execution either according to @DirtiesContext semantics or due to automatic eviction from the ContextCache due to a maximum cache size policy.
    at org.springframework.util.Assert.state(Assert.java:94) ~[spring-core-5.2.3.RELEASE.jar:5.2.3.RELEASE]
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:127) ~[spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]
[...]

This makes that the compilation ends in fail, but the execution in the database is recorded as COMPLETED. Reviewing the code where the error is generated, in the Spring class called DefaultTestContext:

@Override
    public ApplicationContext getApplicationContext() {
        ApplicationContext context = this.cacheAwareContextLoaderDelegate.loadContext(this.mergedContextConfiguration);
        if (context instanceof ConfigurableApplicationContext) {
            @SuppressWarnings("resource")
            ConfigurableApplicationContext cac = (ConfigurableApplicationContext) context;
            Assert.state(cac.isActive(), () ->
                    "The ApplicationContext loaded for [" + this.mergedContextConfiguration +
                    "] is not active. This may be due to one of the following reasons: " +
                    "1) the context was closed programmatically by user code; " +
                    "2) the context was closed during parallel test execution either " +
                    "according to @DirtiesContext semantics or due to automatic eviction " +
                    "from the ContextCache due to a maximum cache size policy.");
        }
        return context;
    }

... the problem is that "context" is not active.

These are my files: ArqDatasourceConfiguration.java

package com.example.demo;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class ArqDatasourceConfiguration {

    @Bean
    @ConfigurationProperties("spring.batch.datasource")
    public DataSource dataSource() throws SQLException {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public JdbcTemplate jdbcTemplateBatch(final DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean(name = "taskDataSource")
    @ConfigurationProperties("spring.task.datasource")
    public DataSource taskDataSource() throws SQLException {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public JdbcTemplate jdbcTemplateTask(@Qualifier("taskDataSource") final DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

CustomTaskConfigurer.java

package com.example.demo;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.task.configuration.DefaultTaskConfigurer;
import org.springframework.stereotype.Component;

@Component
public class CustomTaskConfigurer extends DefaultTaskConfigurer {

    @Autowired
    public CustomTaskConfigurer(@Qualifier("taskDataSource") DataSource dataSource) {
        super(dataSource);
    }
}

DemoJobApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.task.configuration.EnableTask;

@SpringBootApplication
@EnableTask
public class DemoJobApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoJobApplication.class, args);
    }

}

MyJob.java

package com.example.demo;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class MyJob {

    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job job2(Step step1, Step step2) {
        return jobBuilderFactory.get("job2")
                .incrementer(new RunIdIncrementer())
                .flow(step1)
                .next(step2)
                .end()
                .build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                .tasklet(new MyTasklet("STEP 1"))
                .build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get("step2")
                .tasklet(new MyTasklet("STEP 2"))
                .build();
    }
}

MyTasklet.java

package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class MyTasklet implements Tasklet {

    private static final Logger log = LoggerFactory.getLogger(MyTasklet.class);

    private String msg;

    public MyTasklet(String msg) {
        this.msg = msg;
    }

    @Override
    public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) {
        log.info("Executing Tasklet: " + msg);
        return RepeatStatus.FINISHED;
    }
}

application.properties

spring.batch.initialize-schema=always
spring.main.allow-bean-definition-overriding=true

spring.batch.datasource.jdbc-url=jdbc:postgresql://localhost:5432/postgres
spring.batch.datasource.username=postgres
spring.batch.datasource.password=*****
spring.batch.datasource.driver-class=org.postgresql.Driver
spring.batch.datasource.schema=springbatch

spring.task.datasource.jdbc-url=jdbc:postgresql://localhost:5432/postgres
spring.task.datasource.username=postgres
spring.task.datasource.password=*****
spring.task.datasource.driver-class=org.postgresql.Driver
spring.task.datasource.schema=springtask

DemoJobApplicationTests.java

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoJobApplicationTests {

    @Test
    void contextLoads() {
    }

}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demoJob</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demoJob</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Cloud Task -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-task</artifactId>
            <version>1.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-task-core</artifactId>
            <version>1.2.2.RELEASE</version>
        </dependency>
        <!-- ================================== -->
        <!-- Database -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- ================================== -->
        <!-- Warning @ConfigurationProperties -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- ================================== -->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

I have tried several annotations like @DirtiesContext, @EnableAutoConfiguration, @SpringBatchTest... but nothing seems to solve the issue, so I would appreciate any idea or help.

RLS
  • 65
  • 2
  • 10

1 Answers1

0

Well, after some head banging we have found the solution, ant it is really stu...simply. It is necessary to update the version of the dependencies from Spring Cloud Task in the pom.xml, changing 1.2.2.RELEASE with 2.2.2.RELEASE

RLS
  • 65
  • 2
  • 10