Comparing Quartz, cron4j and Obsidian Scheduler

We’ve all worked on projects that required us to do very basic tasks at periodic intervals. Perhaps we chose a basic ScheduledThreadPoolExecutor. If we’re already using Spring, maybe we tried their TaskExecutor/TaskScheduler support.

But once we encounter any number of situations such as an increased quantity of tasks, new interdependencies between tasks, unexpected problems in task execution or the like, we will likely start to consider a more extensive scheduling solution.

Our website has a fairly exhaustive feature comparison of the most commonly used Java schedulers, so we won’t go into that in this post, but we do encourage you to take a look.

Features aside, are there other criteria that should come into play? Factors such as development team responsiveness to feature requests and bug reports certainly can be critical for many organizations. If you head over to the Quartz Download page, you’ll see that they haven’t had a release in over a year, despite there being many active unresolved issues. Cron4j hasn’t had a release in over 2 years. While Spring has made some changes to the design of their TaskExecutor/TaskScheduler support in recent releases, their true priorities lie elsewhere as they have not really done much to expand the feature set.

Obsidian Scheduler on the other hand is actively maintained, actively supported (with free online support!) and responsive to our user community. In the past year, we’ve averaged a release per month delivering a blend of features, enhancements and fixes, proof that we’re a nimble and responsive organization. We encourage you to give Obsidian a try today!

Swapping out Spring Bean Configuration at Runtime

Most Java developers these days deal with Spring on a regular basis and there are lots of us out there that have become familiar with its abilities as well as its limitations.

I recently came across a problem that I hadn’t hit before: introducing the ability to rewire a bean’s internals based on configuration introduced at runtime. This is valuable for simple configuration changes or perhaps swapping out something like a Strategy or Factory class, rather than rebuilding of a complex part of the application context.

I was able to find some notes about how to do this, but I thought that some might find my notes and code samples useful, especially since I can confirm this technique works on versions of Spring back to 1.2.6. Unfortunately, not all of us are lucky enough to be on the latest and greatest of every library.

Scope of the Problem

The approach I’m going to outline is meant primarily to target changes to a single bean, though this code could easily be extended to change multiple beans. It could be invoked through JMX or some other UI exposed to administrators.

One thing it does not cover is rewiring a singleton all across an application – this could conceivably be done via some reflection and inspection of the current application context, but is likely to be unsafe in most applications unless they have some way of temporarily shutting down or blocking all processing for a period while the changes are made all over the application.

The Code

Here’s the sample code. It will take a list of Strings which contains bean definitions, and wire them into a new temporary Spring context. You’ll see a parent context can be provided, which is useful in case your new bean definitions need to refer to beans already configured in the application.

public static <T> Map<String, T> extractBeans(Class<T> beanType, 
   List<String> contextXmls, ApplicationContext parentContext) throws Exception {

   List<String> paths = new ArrayList<String>();
   try {
      for (String xml : contextXmls) {
         File file = File.createTempFile("spring", "xml");
         // ... write the file using a utility method
         FileUtils.writeStringToFile(file, xml, "UTF-8");
         paths.add(file.getAbsolutePath());
      }

      String[] pathArray = paths.toArray(new String[0]);
      return buildContextAndGetBeans(beanType, pathArray, parentContext);

   } finally {
      // ... clean up temp files immediately if desired
   }
}

private static <T> Map<String, T> buildContextAndGetBeans(Class<T> beanType, 
               String[] paths, ApplicationContext parentContext) throws Exception {

   FileSystemXmlApplicationContext context = 
      new FileSystemXmlApplicationContext(paths, false, parentContext) {
         @Override  // suppress refresh events bubbling to parent context
         public void publishEvent(ApplicationEvent event) { }

         @Override 
         protected Resource getResourceByPath(String path) {
            return new FileSystemResource(path); // support absolute paths
         }
      };

   try {
      // avoid classloader errors in some environments      
      context.setClassLoader(beanType.getClassLoader());
      context.refresh(); // parse and load context
      Map<String, T> beanMap = context.getBeansOfType(beanType);

      return beanMap;
   } finally {
      try {
         context.close();
      } catch (Exception e) {
         // ... log this
      }
   }
}

If you look at buildContextAndGetBeans(), you’ll see it does the bulk of the work by building up a Spring context with the supplied XML bean definition files. It then returns a map of the constructed beans of the type requested.

Note: Since the temporary Spring context is destroyed, ensure your beans do not have lifecycle methods that cause them to be put into an invalid state when stopped or destroyed.

Here’s an example of a Spring context that might be used to rewire a component. Imagine we have an e-commerce system that does fraud checks, but various strategies for checking for fraud. We may wish to swap these from our service class without having to stop and reconfigure the application, since we lose business when we do so. Perhaps we are finding a specific abuse of the system that would be better dealt with by changing the strategy used to locate fraudulent orders.

Here’s a sample XML definition that could be used to rewire our FraudService.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <bean id="fraudStrategy" class="com.example.SomeFraudStategory">
      <!-- example of a bean defined in the parent application context that we can reference -->
      <property name="fraudRuleFactory" ref="fraudRuleFactory"/>
   </bean>
</beans>

And here is the code you could use to rewire your bean with a reference to the defined fraudStrategy, assuming you have it in a utility class called SpringUtils:

public class FraudService implements ApplicationContextAware {

   private ApplicationContext context;
   // volatile for thread safety (in Java 1.5 and up only)
   private volatile FraudStrategy fraudStrategy;
   
   @Override // get a handle on the the parent context 
   public void setApplicationContext(ApplicationContext context) {
      this.context = context;
   }

   public void swapFraudStategy(String xmlDefinition) throws Exception {
      List<Sting> definitions = Arrays.asList(xmlDefinition);
      Map<String, FraudStrategy> beans = 
         SpringUtils.extractBeans(FraudStrategy.class, definitions, context);
      if (beans.size() != 1) {
         throw new RuntimeException("Invalid number of beans: " + beans .size());
      }
      this.fraudStrategy = beans.values().iterator().next();
   }
   
}

And there you have it! This example could be extended a fair bit to meet your needs, but I think it shows the fundamentals of how to create a Spring context on the fly, and use its beans to reconfigure your application without any need for downtime.