user1020455
user1020455

Reputation: 186

Spring Boot 2.0 - Quartz & Spring Integration - Not working

I am using Quartz & Spring Integration modules in spring boot application. After I added Spring Integration in pom, getting below exception. It was working before adding Spring Integration modules in pom. It looks like some conflicts between Quartz and Spring Integration. Please help.

Exception

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'quartzScheduler' defined in class path resource [org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.class]: Invocation of init method failed; nested exception is org.quartz.SchedulerException: ThreadPool class 'org.springframework.scheduling.quartz.LocalTaskExecutorThreadPool' props could not be configured. [See nested exception: java.lang.NoSuchMethodException: No setter for property 'threadCount']
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1710) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:583) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:502) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:312) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:741) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:868) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at com.xxx.t3.starter.Application.main(Application.java:31) [classes/:na]
Caused by: org.quartz.SchedulerException: ThreadPool class 'org.springframework.scheduling.quartz.LocalTaskExecutorThreadPool' props could not be configured.
    at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:849) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1559) ~[quartz-2.3.0.jar:na]
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.createScheduler(SchedulerFactoryBean.java:636) ~[spring-context-support-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:510) ~[spring-context-support-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1769) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1706) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    ... 15 common frames omitted
Caused by: java.lang.NoSuchMethodException: No setter for property 'threadCount'
    at org.quartz.impl.StdSchedulerFactory.setBeanProps(StdSchedulerFactory.java:1447) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:847) ~[quartz-2.3.0.jar:na]
    ... 20 common frames omitted

pom.xml

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.xxx</groupId>
    <artifactId>etl</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.activemq</groupId>
                    <artifactId>activemq-broker</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-sftp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>wlthint3client</artifactId>
            <version>12.2.1.2</version>
            <scope>system</scope>
            <systemPath>C:/Oracle/products/mw_home/wlserver/server/lib/wlthint3client.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc</artifactId>
            <version>7</version>
            <scope>system</scope>
            <systemPath>C:/Oracle/products/mw_home/oracle_common/modules/oracle.jdbc/ojdbc7.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.sandp</groupId>
            <artifactId>rsadecryptor</artifactId>
            <version>1.0</version>
            <scope>system</scope>
            <systemPath>C:/DB/rsadecryptor.jar</systemPath>
        </dependency>
    </dependencies>

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

application.yml

spring:
  mail:
    host: XXXXXXXXXX
    port: 25
    protocol: smtp
    defaultEncoding: UTF-8
  quartz:
    jdbc:
      initialize-schema: never
    properties:
      org:
        quartz:
          scheduler:
            instanceId: AUTO
            #instanceName: ETL
            batchTriggerAcquisitionMaxCount: 25
            skipUpdateCheck: true
          threadPool:
            threadCount: 50
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
            tablePrefix: QRTZ_ 
            useProperties: true
            acquireTriggersWithinLock: true

email:
  from: [email protected]

scheduler:
  datasource:
    url: jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=XXXXXXXXXX)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XXXXXX)))
    username: scheduler
    password: XXXXXX
    driver-class-name: oracle.jdbc.OracleDriver

t3:
  datasource:
    url: jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=XXXXXXXXXXXXX)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XXXXXXXXXXXX)))
    username: app
    password: XXXXXXXXX
    driver-class-name: oracle.jdbc.OracleDriver

Application.java

@ComponentScan("com.xxx.t3")
@IntegrationComponentScan
@EnableIntegration
@SpringBootApplication(exclude = { ActiveMQAutoConfiguration.class, JmxAutoConfiguration.class })
public class Application
{
    public static void main(String[] args) 
    {
        ApplicationContext context = SpringApplication.run(Application.class, args);        
    }
}

SchedulerConfig.java

@Configuration
public class SchedulerConfig
{
    private DataSource dataSource;

    private PlatformTransactionManager transactionManager;

    @Autowired
    public SchedulerConfig(@Qualifier("schedulerDataSource") DataSource dataSource, @Qualifier("schedulerTransactionManager") PlatformTransactionManager transactionManager) 
    {
        this.dataSource = dataSource;
        this.transactionManager = transactionManager;
    }

    @Bean
    public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer() 
    {
        return bean -> 
        {
            bean.setDataSource(dataSource);
            bean.setTransactionManager(transactionManager);
            bean.setSchedulerName("ETL");
        };
    }
}

SftpConfig.java

@ConfigurationProperties("sftp")
@Configuration
public class SftpConfig 
{
    private String host;
    private int port;
    private String user;
    private String password;
    private String remoteDirectory;
    private String localDirectory;

    @Bean
    public SessionFactory<LsEntry> sftpSessionFactory()
    {
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost(host);
        factory.setPort(port);
        factory.setUser(user);
        factory.setPassword(password);
        factory.setAllowUnknownKeys(true);

        return new CachingSessionFactory<LsEntry>(factory);
    }

    @Bean
    public SftpInboundFileSynchronizer sftpInboundFileSynchronizer() 
    {
        SftpInboundFileSynchronizer fileSynchronizer = new SftpInboundFileSynchronizer(sftpSessionFactory());
        fileSynchronizer.setDeleteRemoteFiles(true);
        fileSynchronizer.setRemoteDirectory(remoteDirectory);

        return fileSynchronizer;
    }

    @Bean
    @InboundChannelAdapter(channel = "fromSftpChannel", poller = @Poller(cron = "0/5 * * * * *"))
    public MessageSource<File> sftpMessageSource() 
    {
        SftpInboundFileSynchronizingMessageSource source = new SftpInboundFileSynchronizingMessageSource(sftpInboundFileSynchronizer());
        source.setAutoCreateLocalDirectory(true);
        source.setLocalDirectory(new File(localDirectory));

        return source;
    }

    @Bean
    @ServiceActivator(inputChannel = "fromSftpChannel")
    public MessageHandler resultFileHandler()
    {
        return new MessageHandler() 
        {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException 
            {
                System.out.println("File added - " + message.getPayload());
            }
        };
    }           
}

Upvotes: 2

Views: 6272

Answers (1)

Gary Russell
Gary Russell

Reputation: 174739

Spring Integration auto-configures a TaskScheduler, which is a TaskExecutor.

I just looked at boot's auto configuration and if it finds exactly one task executor in the application context, it wires it into the quartz factory bean:

public QuartzAutoConfiguration(QuartzProperties properties,
        ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers,
        ObjectProvider<Executor> taskExecutor, ObjectProvider<JobDetail[]> jobDetails,
        ObjectProvider<Map<String, Calendar>> calendars,
        ObjectProvider<Trigger[]> triggers, ApplicationContext applicationContext) {
    this.properties = properties;
    this.customizers = customizers.getIfAvailable();
    this.taskExecutor = taskExecutor.getIfUnique();
    this.jobDetails = jobDetails.getIfAvailable();
    this.calendars = calendars.getIfAvailable();
    this.triggers = triggers.getIfAvailable();
    this.applicationContext = applicationContext;
}

and

if (this.taskExecutor != null) {
    schedulerFactoryBean.setTaskExecutor(this.taskExecutor);
}

Then, the quartz factory bean configures a LocalTaskExecutorThreadPool which doesn't have a thread count property.

You could add a second task executor so the getIfUnique() call will return null.

I suggest you open a github issue against spring-boot to see if they can somehow avoid this issue.

EDIT

You should also be able to add bean.setTaskExecutor(null); to your SchedulerFactoryBeanCustomizer to revert the previous behavior.

Upvotes: 2

Related Questions