0

I want to substitute a TimerTask in a Spring MVC webapp with a Quartz scheduler. My spring-quartz.xml:

<bean id="manifestTask" class="it.dhl.wla.quartz.ManifestTask"  />
<bean id="manifestJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="manifestTask" />
    <property name="targetMethod" value="go" />
</bean>
<bean id="manifestTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="manifestJob" />
    <property name="cronExpression" value="59 15 * * * ?" />     
</bean>

It works with an empty go() method (with just a log output) The problem is with a ManifstTask dependence that i try to @Autowire:

public class ManifestTask {
@Autowired
ManifestService manifestService;

private static final Logger LOG = Logger.getLogger(ManifestTask.class);

public void go() {
    LOG.info("Manifest quartz task start...");
    boolean res=manifestService.doManifest();
    LOG.info("Manifest  quartz task end: "+res);
}
}

This is the error on startup:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [it.dhl.wla.service.ManifestService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

ManifestService is autowired in other classes and works, seems a problem with bean instantiation order in this specific case of quartz task.

Spring version is 3.0.5, Quartz 1.8.6.

EDIT:

Added a reference to ApplicationContext in ManifestTask :

@Autowired
ApplicationContext appctx=null;

Well, when method go() is called by quartz I explore the appctx but can't find any of the @Components declared with annotation. I find instead the beans declared in spring-quartz.xml.

Concrete class for appctx is XmlWebApplicationContext. Are xml-declared beans in a separated context? How can I access to annotated @Components?

  • do you have more that one spring configuration xml? Do you have another spring configuration besides the spring-quartz.xml? If you do you need to use the in you main configuration xml so that both will be used. – zpontikas Jun 24 '15 at 10:05
  • I have some xml defined in web.xml: contextConfigLocation classpath:applicationContext.xml classpath:applicationContext-security.xml classpath:spring-quartz.xml My problem is with annotated components... – Roberto Girelli Jun 24 '15 at 10:16

4 Answers4

0

You should annotate your ManifestTask with @Component so that is will be picked up by spring :D

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ManifestTask {
    @Autowired
    ManifestService manifestService;

    private static final Logger LOG = Logger.getLogger(ManifestTask.class);

    public void go() {
        LOG.info("Manifest quartz task start...");
        boolean res=manifestService.doManifest();
        LOG.info("Manifest  quartz task end: "+res);
    }
}

Also after looking at your edited post I think you should read this answer:
How to import spring-config.xml of one project into spring-config.xml of another project?

Community
  • 1
  • 1
zpontikas
  • 5,445
  • 2
  • 37
  • 41
0

I found a workaround, but i don't like it.

spring-quartz.xml:

<!-- MANIFEST -->
<bean id="theManifestService" class="it.dhl.wla.service.ManifestService"/>
<bean id="manifestTask" class="it.dhl.wla.quartz.ManifestTask" >
    <property name="manifestService" ref="theManifestService"/>        
</bean>
<bean id="manifestJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="manifestTask" />
    <property name="targetMethod" value="go" />
</bean>
<bean id="manifestTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="manifestJob" />
    <property name="cronExpression" value="59 15 * * * ?" />   
</bean>

and in ManifestTask.java:

private ManifestService manifestService;

public ManifestService getManifestService() {
    return manifestService;
}

public void setManifestService(ManifestService manifestService) {
    this.manifestService = manifestService;
}

Now the ManifestService bean and the dependence are defined in xml. But my ManifestService class is still annotated as @Component because i use it in some controllers.

I think that now I have 2 separated instances of ManifestService ...

0

Follow below steps to integrate Quartz with Spring boot

1 - add below dependencies in pom.xml file it needs spring-context-support as well

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.3.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>

2 - create separate xml and add below code in existing one

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="quartzJob" class="com.mightyjava.quartz.ExecuteUsingQuartz" />

    <bean id="jobFactoryBean" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
        p:targetObject-ref="quartzJob" p:targetMethod="run" />

    <bean id="triggerFactoryBean"
        class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"
        p:jobDetail-ref="jobFactoryBean" p:cronExpression="0/5 * * * * ?" />

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
        p:triggers-ref="triggerFactoryBean" />

</beans>

3 - create a class which will contains execution logic

import java.util.Date;

public class ExecuteUsingQuartz {
    public void run() { 
        System.out.println("Executing using quartz in every 15 seconds " + new Date());
    }
}

if still have some doubt then click on below link and see live demo

How to integrate Quartz scheduler in Spring MVC ?

0

What I suspect happened is that ManifestService bean is scanned and instantiated in one of the xxxx-servlet.xml Spring content files. Beans created this way are not available when processing / instantiating beans in yyyy-servlet.xml.

For a bean to be globally available to all Spring contexts, you would need to scan the package it belongs to or define it in an applicationContext.xml file associated to the Spring context loader listener. Something like:

web.xml

...
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> 
...
ootero
  • 3,235
  • 2
  • 16
  • 22