MaVVamaldo
MaVVamaldo

Reputation: 2535

@Autowired doesn't work if applied to a bean shared between two threads

I have some troubles with a very basic scenario: I have a java web app (spring 3) and I want to store some object to a stack. Then I want to manage the objects in the stack with a scheduler and so I implemented a quartz job. Plain and simple. I inject the stack in the first service class and in the job class with @Autowired annotation. For the service class the table is succesfully injected but for the job class the table is null. here's the code:

the class to share

package it.unifi.det.telemat.vr.data;
@Component
public class IndexedNodesStack extends HashMap<IndexedObject, Boolean>{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Override
    public synchronized int size() {
        // TODO Auto-generated method stub
        return super.size();
    }

    //all the HashMap methods are implemented in a synchronized wrapper method

}

the first class (@Autowired is successful)

package it.unifi.det.telemat.vr.service;
@Service
public class InnerNodeManager extends ConcreteNodeManager{

    @Autowired
    private IndexedNodesStack indexedNodesStack; //<--- it is actually autowired!


    private void manageIndexedNodes(Boolean isPut, String lri, String features)
    {
        IndexedObject indexedObject = new IndexedObject();
        indexedObject.setId(lri);
        if(features != null && isPut)
            indexedObject.generateFeatures(features);

        indexedNodesStack.put(indexedObject, isPut);
    }

}

the job class (@Autowired fails)

package it.unifi.det.telemat.vr.service.scheduler;
@Component
public class QuartzJSearchJob extends QuartzJobBean{

    @Autowired
    private IndexedNodesStack indexedNodesStack; //<--- this variable is null :-(

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException
    {
        //do work       
    }
}

EDIT: here's the servlet.xml

<context:component-scan base-package="it.unifi.det.telemat.vr" />

<bean name="searchJob"
class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="it.unifi.det.telemat.vr.service.scheduler.QuartzJSearchJob" />
</bean>

<bean id="searchJobTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="searchJob" />
    <property name="cronExpression" value="0/50 * * * * ?" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
    <list>
        <ref bean="searchJobTrigger" />
        </list>
    </property>
</bean>

maybe I lack some knowledge about the resource sharing between threads since it is my first attempt in this field. What am I missing?

Upvotes: 2

Views: 3238

Answers (6)

Riadh ben
Riadh ben

Reputation: 1

I have same problem, I resolved this by adding SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); as first line of my Job.execute(JobExecutionContext context) method.

Upvotes: 0

Saad Benbouzid
Saad Benbouzid

Reputation: 531

You forget the "jobDetails" property :

<property name="jobDetails">
    <list>
        <ref bean="searchJob" />
    </list>
</property>

You also need to use org.springframework.scheduling.quartz.JobDetailFactoryBean instead of org.springframework.scheduling.quartz.JobDetailBean.

Insert it like this :

<bean name="searchJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="it.unifi.det.telemat.vr.service.scheduler.QuartzJSearchJob" />
</bean>

<bean id="searchJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="searchJob" />
    <property name="cronExpression" value="0/50 * * * * ?" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="searchJobTrigger" />
        </list>
    </property>
    <property name="jobDetails">
        <list>
            <ref bean="searchJob" />
        </list>
    </property>
</bean>

Upvotes: 0

JetQin
JetQin

Reputation: 95

I think you must assign a value in scheduleFactoryBean, setSchedulerContextAsMap(), you put your object in this map, then it will work.

@Bean(name = "scheduler")
  public SchedulerFactoryBean schedulerFactory()
  {
    SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
    schedulerFactory.setDataSource(dataSource);
    schedulerFactory.setAutoStartup(true);
    schedulerFactory.setGlobalJobListeners(globalJobListeners);
    schedulerFactory.setSchedulerContextAsMap(contextMap());
    schedulerFactory.setQuartzProperties(schedulerProperties());
    return schedulerFactory;
  }

Upvotes: -2

MaVVamaldo
MaVVamaldo

Reputation: 2535

ok, finally I get the point. It doesn't works becaouse spring does not instantiate the quartzJSearchJob, quartz does. The beans to inject inside the job have to be passed via SchedulerFacoryBean. Here's the configuration that makes the things work.

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="searchJobTrigger" />
        </list>
    </property>
    <property name="schedulerContextAsMap">
    <map>
       <entry key="indexedNodesStack" value-ref="indexedNodesStack" />
    </map>
    </property>
</bean>

Upvotes: 2

gigadot
gigadot

Reputation: 8969

Autowiring only works if the bean is created from Spring bean factory. Did you create the object without using bean factory, i.e. create it using new QuartzJSearchJob() syntax?

Upvotes: 2

idbentley
idbentley

Reputation: 4218

QuartzJSearchJob isn't recognized as a bean, because it doesn't have a class level annotation. Try @Component

Upvotes: 0

Related Questions