Workaround pattern for autowiring jobs

Autowiring is supported for Job plugin modules in Confluence 4.0.1 and later.

As described in CONF-20162, there is no autowiring for Job Modules prior to Confluence 4.0.1. In plugins 1 dependencies could be easily fetched from the ContainerManager. In plugins 2 this is not always the case. There is a workaround however. Instead of using a job module, use a regular component module that extends JobDetail.

Module descriptors

In your atlassian-plugin.xml declare the trigger as usual and make it point to a JobDetail that is a component module, rather than a job module.

<atlassian-plugin>
    <!-- ... -->

    <component key="sampleJobDetail" name="A sample Job Detail" class="com.example.SampleJobDetail">
        <description>This SampleJobDetail is a component module that will be autowired by Srping.</description>
        <interface>org.quartz.JobDetail</interface>
    </component>

    <trigger key="sampleJobTrigger" name="Sample Job Trigger">
        <job key="sampleJobDetail"/>
        <schedule cron-expression="0/2 * * * * ?"/>
    </trigger>

    <!-- ... -->
</atlassian-plugin>

JobDetail

The Detail object itself is, or can be, fairly trivial. It needs to be autowired with the dependencies you need, and it needs to call the super constructor with the class of the actual job to run.

/**
 * This class allows Spring dependencies to be injected into {@link SampleJob}.
 * A bug in Confluence's auto-wiring prevents  Job components from being auto-wired.
 */
public class SampleJobDetail extends JobDetail
{
    private final SampleDependency sampleDependency;

    /**
     * Constructs a new SampleJobDetail. Calling the parent constructor is what registers the {@link SampleJob}
     * as a job type to be run.
     *
     * @param sampleDependency the dependency required to perform the job.  Will be autowired.
     */
    public SampleJobDetail(SampleDependency sampleDependency)
    {
        super();
        setName(SampleJobDetail.class.getSimpleName());
        setJobClass(SampleJob.class);
        this.sampleDependency = sampleDependency;
    }

    public SampleDependency getSampleDependency()
    {
        return sampleDependency;
    }
}

The Job

Finally the Job itself can now retrieve anything it wants from the Detail which is retrieved from the jobExecutionContext. It does have to cast the the appropriate Detail class first.

/**
 * Job for doing something on a regular basis.
 */
public class SampleJob extends AbstractJob
{
    public void doExecute(JobExecutionContext jobExecutionContext) throws JobExecutionException
    {
        SampleJobDetail jobDetail = (SampleJobDetail) jobExecutionContext.getJobDetail();
        jobDetail.getSampleDependency().doTheThingYouNeedThisComponentFor();
    }
}

Working example

You can see an example of this in the WebDAV plugin. Look at ConfluenceDavSessionInvalidatorJob and ConfluenceDavSessionInvalidatorJobDetail.

Was this page helpful?

Have a question about this article?

See questions about this article

Powered by Confluence and Scroll Viewport