Spring Integration
Though Obsidian works great on its own, Obsidian also supports seamless integration with Spring. This page outlines how to set up Spring integration and execute jobs which are configured as Spring components.
Dependency Injection via Spring
As of Obsidian 2.2.0, Obsidian has greatly improved the integration with your Spring dependency injection container, and now supports loading job implementations from the Spring context. Obsidian does not include any Spring libraries and will defer to the version and libraries you include with your application. It supports versions of Spring from 2.5 onward.
Obsidian uses the @Component and any Spring or custom extensions of those annotations (such as @Service and @Repository) as a basis for knowing which job implementations should be retrieved from the Spring context.
Job classes which can be loaded from Spring can be either SchedulableJob implementations or annotated jobs. Even if you are not using Spring annotations to wire your classes, you will need to add the annotation to your job classes to serve as a Marker to Obsidian. See Implementing Jobs for details on how to write and configure executable jobs.
Regardless of the method you use to configure your Spring components, Obsidian will load them directly from the container which means the actual singleton or prototype object obtained from Spring is executed by Obsidian. This makes creating jobs which use configured services very easy and convenient.
To allow Obsidian to locate your application's Spring context, you will need to ensure Obsidian's ApplicationContextAware implementation is configured by Spring. If you are using Spring's classpath scanning, you can use the following approach to add an additional package to your configuration:
<context:component-scan base-package="com.my.package.scan.base, com.carfey.ops.job.di"/>
Another option is to extend our com.carfey.ops.job.di.SpringContextAware class with an empty extension class in one of your currently scanned packages. When this object is configured by Spring, it will automatically initialize Obsidian's Spring integration.
If you are not using scanning, simply ensure you wire in our com.carfey.ops.job.di.SpringContextAware class via XML.
If you have multiple instances of a given bean type in your Spring context, please ensure you use the value="" when you annotate your job with @Component or its specializations as this is how Obsidian will uniquely identify the job within the Spring context.
Best Practices
- Ensure Spring is initialized before Obsidian is started via
com.carfey.ops.job.SchedulerStarterorcom.carfey.ops.servlet.StartupShutdownListener. This ensures the Spring context is fully available when Obsidian starts running jobs. - If your job has internal state (i.e. members) which are used during job execution, Spring prototypes should be used to avoid concurrency issues.
- In many cases, the majority of job logic can be left in your existing Spring components.
- Use Obsidian to configure parameters and store results, but leave application-level configuration in Spring.
Sample Jobs
As a prototype @Component:
@Component
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Configuration(knownParameters={
@Parameter(name=SleepJob.SLEEP_SECONDS, type=Type.INTEGER, defaultValue="60", required=true)
})
@Description("This job can simulate jobs with various running times.")
public class SpringComponentJob implements SchedulableJob {
@Autowired private TestService mTestService;
@Override
public void execute(Context context) throws Exception {
context.saveJobResult("counterIncrement", mTestService.incrementCounter());
}
}
As a singleton @Service:
@Service
@Configuration(knownParameters={
@Parameter(name=SleepJob.SLEEP_SECONDS, type=Type.INTEGER, defaultValue="60", required=true)
})
@Description("This job can simulate jobs with various running times.")
public class SpringServiceJob implements SchedulableJob {
@Autowired private TestService mTestService;
@Override
public void execute(Context context) throws Exception {
context.saveJobResult("currentCounter", mTestService.currentValue());
}
}