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!

Java Enterprise Software Versus What it Should Be

A lot of developers end up in the Java “enterprise” world at some point in their careers. I know the term alone conjures up all kinds of reactions, and rightly so. Often environments where lots of interesting technical challenges exist end up being those that nobody wants to work on because they are brittle, difficult to work on and just no fun. Often the problems that crop up in big projects are due to management, but I’ve seen developers make lots of bad decisions that lead to awful pieces of software, all in the name of “enterprise”.

What is Enterprise?

You could argue that the term can mean just about anything, and that’s true, but for the sake of this article I’m going to define it in a way that I think lines up with the common usage. The average enterprise project has these attributes:

  • typically in a large corporate environment
  • multiple layers of management/direction involved
  • preference for solutions from large vendors like Red Hat, IBM or Microsoft
  • preference for well-known, established (though sometimes deficient) products and standards
  • concerns about scaling and performance

Now that I’ve defined what type of project we are talking about, let’s see what they usually end up looking like.

The Typical Enterprise Java Project

Most of us have seen the hallmarks of enterprise projects. It would help if we had an example, so let’s pretend it’s an e-commerce platform with some B2B capabilities. Here’s what it might look like:

  • EJB3 plus JPA and JSF – these fit a “standard” and everyone use them, so it’s safe choice.
  • SOAP – it’s standard and defines how things like security should work, so there’s less to worry about.
  • JMS Message-Driven Beans – it fits into the platform and offers reliability and load-balancing.
  • Quartz for job scheduling – a “safe” choice (better the enemy you know than the devil you don’t).
  • Deployed on JBoss – it has the backing of a large company and paid support channels.

Now, the problem with a project like this isn’t necessarily the individual pieces of technology selected. I definitely have issues with some of the ones in my example, but the real issue is how the choices are made and what the motivations are for using certain technologies.

The stack of software above is notoriously more difficult to manage and work with compared to other choices. Development will take longer to get off the ground, changes will be more difficult to make as requirements evolve, and the project will ultimately end up more complicated than other possible solutions.

Enterprise Decision-Making

The goals that enterprise project usually fixate on when making choices are:

  • Low-risk technology – make a “safe” choice that won’t result in blow-back, even if it is known to have serious drawbacks.
  • Standards obsession – worrying more about having well-defined specifications like EJB3 or SOAP than offering the simplest solution that does the job effectively.
  • Need for paid support with an SLA, often without concern to the quality or timeliness of responses.
  • Designing out of fear of unknown future requirements.

These goals aren’t bad ones, with the exception of the last one, but they tend to overshadow the real goals of every software project. The primary objective of all software projects is to deliver a project that:

  • is on-time;
  • meets requirements;
  • is reliable;
  • performs well; and
  • is easy to maintain and extend.

These should be what decision-makers focus on in software projects, whether small or large. It’s obvious that sometimes special organizational needs factor into the choices that are made, but fundamentally good choices generally apply in all types of organizations.

So what if we were to reimagine our project with those goals in mind instead?

A Reimagined Enterprise Project

First, a little disclaimer: there are many ways one can go in any project, and I won’t claim that the technologies below are better that the ones mentioned previously. Tools need to be evaluated according to your needs, and everyone’s are different.

What I will try to do is demonstrate an example technology stack along with the reasoning for each choice. This will show how well designed systems can be built which survive in the enterprise world without succumbing to the bad choices that are often made.

Here’s the suggested stack:

  • Spring MVC using Thymeleaf – stable history, lots of development resources, quick development and flexibility. Don’t be afraid of using platforms or libraries, but try to avoid too much “buy-in” to their stack that you might regret.
  • Simple database layer using jOOQ for persistence where useful. This lets us manage performance in a more fine-grained way, while still getting easy database interaction and avoiding ORM pitfalls.
  • REST using Jackson JSON processor – REST and JSON are both popular because they are easy-to-use and understand, cheap to develop, use simple standards and are familiar to developers. Lock-in isn’t much of a problem either – we could easily switch to another JSON processor without much difficulty, unlike SOAP. This can be easily secured with SSL and basic authentication.
  • JMS messaging using JSON-encoded messages on ActiveMQ – loose coupling, reliability and load-balancing, without the nastiness of being stuck with Message-Driven Beans.
  • Obsidian Scheduler – simple-to-use, offers excellent monitoring and reduces burden on developers. Once again, the goal is to simplify and reduce costs where possible.
  • Deployed on Tomcat – no proprietary features used. This helps us follow standards, avoid upgrade issues and keep things working in the future. Who needs support with SLAs when things aren’t inexplicably breaking all the time?

I think the stack and corresponding explanations above help give an idea to what an enterprise project can be if it’s approached from the right angle. The idea is to show that even enterprise projects can be simple and be nimbly built – bloated frameworks and platforms aren’t a necessary part, and rarely offer any significant tangible benefit.

In Closing

Recent trends in development to technologies like REST have been very encouraging and inroads are being made into the enterprise world. Development groups are realizing that simplicity leads to reliability and cost-effective solutions long as the underlying technology choices meets the performance, security, etc. needs of the project.

The software world moves quickly, and is showing promising signs that it’s moving in the right direction. I can only hope that one day the memories of bloated enterprise platforms fade into obscurity.

Integrating Spring and Obsidian Scheduler

The use of Spring framework has woven its way into a large percentage of Java projects of all sorts. It has grown well beyond a dependency injection framework to a large interconnected technology stack providing “plumbing” in a large number of areas. Since it’s so often a part of our lives, we’ll discuss Obsidian’s support of Spring and how to quickly integrate your use of Spring into Obsidian Scheduler.

Version Indifferent

First, Obsidian does not include any Spring libraries and is not dependent on Spring to run. Given that Spring is a very active project and has frequent releases, and at times projects have constraints that require using older versions of certain libraries, we felt it would be best if Obsidian just worked with whatever version of Spring you’re using. Obsidian supports versions 2.5 and later of Spring.

Configuring Job Components

The first step in integrating Obsidian Scheduler is often already in place in your project. Obsidian expects to find @Component annotations or any Spring or custom extensions of those annotations (such as @Service and @Repository) on your job classes. If you’re using these annotations to wire your classes, you likely don’t have to do anything else to complete this step. If you’re not using these annotations to wire your classes, you will need to add the annotation to serve as a Marker to Obsidian. If you happen to have multiple instances of a given bean class in your Spring context, Obsidian will need to know how to uniquely identify the job within the Spring context. In these cases, use value="" in the annotation to provide the bean name. For example:

@Component("mySpringComponent")
public class SpringComponent implements SchedulableJob {

Providing the Spring Context to Obsidian

The next step is to let Obsidian know about your Spring context. Included with Obsidian is an implementation of ApplicationContextAware. If you’re using classpath scanning in Spring, you can just add the Obsidian package to your configuration. Here is a sample:

<context:component-scan base-package="com.my.package, com.carfey.ops.job.di"/>

If you’re wiring explicitly in a configuration XML file, here is a sample configuration:

<bean id="obsidianAware" class="com.carfey.ops.job.di.SpringContextAware"/>

Startup & Shutdown Options

The third step, which is optional and depends on your desired usage, relates to starting and stopping Obsidian. If you’re configuring Spring integration for use in the Obsidian standalone Web Administration application (with no scheduler running), you would not include this configuration. In all other cases, such as embedded scheduler or Web Administration application with bundled scheduler, you can use this sample:

<bean id="obsidianStarter" class="com.carfey.ops.job.di.SpringSchedulerStarter"/>

This bean should wired as late as possible in the Spring context startup, using depends-on or other appropriate strategies. This class does not possess a @Component annotation since it cannot be auto-wired via Spring classpath scanning.

Now your Add/Edit Job list in the Web Administration UI, will include all implementations of com.carfey.ops.job.SchedulableJob that you’ve wired through Spring. If you’re using Spring 3.0 or later, even jobs that use Obsidian’s annotations com.carfey.ops.job.SchedulableJob.Schedulable and com.carfey.ops.job.SchedulableJob.ScheduledRun are included. See our wiki for more information on implementing jobs using the interface or the annotations.

Good to Go!

You now have complete Spring integration with Obsidian. All job instances that loaded via Spring annotations will be loaded from the Spring container when performing validation and execution. Note that these could be either prototypes or singletons according to your needs, though we recommend prototypes if you have any kind of internal state within the job.

Of course Spring is not the only dependency injection framework available in Java. On the roadmap for Obsidian is similar integration with Google Guice. Stay tuned! Get in touch with us if you have any questions on Spring integration or any other Obsidian Scheduler features.

Feature Comparison of Java Job Schedulers

At Carfey Software, we love our flagship product, Obsidian Scheduler. We believe that Obsidian is the best choice for most scheduling needs. Why? Because Obsidian is carefully designed to meet both simple and complex requirements. We think it stacks up well whether you are struggling with an existing scheduler or investigating if you should once again use one of the de facto scheduler solutions on your new project, or perhaps are just curious about alternatives.

So we decided to compare Obsidian to Quartz, cron4j and Spring. And if the technology you’re considering isn’t listed here, why not use these items as a guide to consider what is important for your upcoming project? For a brief overview, check out our feature comparison.

Real-time Schedule Changes / Real-time Job Configuration

Obsidian Quartz cron4j Spring
Yes No native interactivity No

Initially, these may not seem very important, but we’ve all likely dealt with situations where we had to temporarily disable a job or change when it runs due to changes in requirements, unexpected technical problems or simply unanticipated behaviour. Obsidian provides both a UI and a REST API to make these changes and they can be effective at the very next minute. Quartz and cron4j are able to make these changes, but they are done via an API or via configuration, so it’s up to you as the developer to find a way to expose this functionality in real-time.

  Obsidian Quartz cron4j Spring
Ad-hoc Job Submission Yes No No native interactivity No
Configurable Job Conflicts Yes No No No

As you can see, this means supporting something like ad-hoc job submission is also not easily done with these other technologies, when the library even supports it.

When it comes to configurable job conflicts, these too can be configured in real-time. So, if it turns out that two jobs that are executing concurrently are colliding with each other and this is while they are executing in your production environment, you can actually adjust to the circumstance with Obsidian, whereas with other schedulers, you may not have any recourse but shutting down, changing code or configuration, and then starting up again. With Obsidian’s conflict support, you could even choose the conflict configuration as a medium- or long-term solution.

Code- and XML-Free Job Configuration

Obsidian Quartz cron4j Spring
Yes No No No

Obsidian provides you with a rich administration UI exposed via a standard web application. We even support job parameterization that can be validated and enforced via the UI if your job is so designated. Quartz and cron4j are essentially just libraries, so they require code and/or configuration as their means of job configuration.

Since we want to be able to make these types of dynamic changes, Obsidian provides a write access user role which corresponds to scheduler operators who can access the UI and perform the necessary changes. All these changes are audited in Obsidian and these audit logs are searchable from the UI, giving you insight into what changes have been made by your team members.

Job Event Subscription/Notification

Obsidian Quartz cron4j Spring
Yes No No No

Quartz and cron4j can handle event notifications via custom listeners. But again, if you want to send out emails on certain events, you have to write that code. If you want to change who receives which notifications, you either expose the mechanism to make those changes, or push new configuration files or possibly even new code. Obsidian chooses not to use custom listeners since we have provided natively the means to do the things these listeners would be used for. Custom listeners would otherwise be needed to handle something like job chaining, but Obsidian supports that natively, even allowing for configuration of conditional chaining decisions. For all events, items can be subscribed to generally or by specific entity, e.g. subscribe to all job failures or just a specific job’s failures.

  Obsidian Quartz cron4j Spring
Custom Listeners No Yes Yes No
Job Chaining Yes Implement yourself using custom listeners No

Obsidian goes one step further and even allows you to be subscribe and be notified to a broader set of events. For example, you can be notified when an Obsidian node is shut down, when someone changes a job configuration item, when someone changes a system configuration item, and so on. And all notifications are logged in the system for review.

Monitoring & Management UI

Obsidian Quartz cron4j Spring
Yes No No No

Obsidian’s monitoring and management UI is powerful, yet very easy to use. You can even play around with it at our live, functional and interactive demo site to see for yourself. Or download Obsidian and have a local version running against an in-memory DB and bundled servlet container within minutes. Quartz does have an add-on pay product that provides some UI. But Obsidian’s UI is free to use even if you use Obsidian’s free single-node.

We’ve discussed management already, but monitoring and investigating is another key part of keeping software running smoothly. If a job fails or a job seems to have run with unexpected criteria, having to gain access to log files and then pore over them to try to find the problem is inefficient, unproductive and a frustrating process for support staff and developers alike. Obsidian’s UI can grant read-only access to support and developer staff so they can review the details of job executions (both success and failures). Filtering and custom search criteria can be used to drill down and find the relevant detail all without ever having to share or transfer files around.

Zero Configuration Clustering and Load Sharing

Obsidian Quartz cron4j Spring
Yes No No No

If Obsidian is running, it natively has the ability to be clustered providing you with load sharing, reliability and failover. Every Obsidian Scheduler instance of any type automatically joins the existing pool/cluster or establishes it if it is the first one on the scene. No extra configuration required. No communication between servers necessary. No multicast, no replication of data between servers. This means that you can easily swap out hardware in case of failure or add a new member for load sharing with ease. Of the comparison technologies, only Quartz supports clustering, but it requires special advanced configuration. Also, to change from non-clustered mode to clustered mode would require taking the existing Quartz instance down.

  Obsidian Quartz cron4j Spring
Job Execution Host Affinity Yes No Not Applicable

Obsidian in its pooling also supports host specificity so that within a cluster, specific nodes can be designated as the allowable execution nodes for a given job.

Scripting Language Support in Jobs

Obsidian Quartz cron4j Spring
Yes No No No

Obsidian allows you to use Groovy, JavaScript, Python and BeanShell as script languages, in addition to standard Java jobs. It’s been implemented such that you can edit the scripts right in Obsidian’s UI console. One of the biggest benefits this scripting support provides that we and our customers have found is the ability to quickly write new jobs without redeploying. For example, operators can react quickly to situations and configure a simple Python script to run in certain job failure conditions.

Scheduling Precision

Obsidian Quartz cron4j Spring
Minute Second Minute Millisecond

No Java scheduler can really guarantee with fine precision when a job will fire. Busy hardware could easily lead to pauses or delays in any strategy to fire any activity at an expected time. As such, and due to the performance degradations that would be associated with more aggressive scheduling, we made a decision with Obsidian to support only minute-level precision for job scheduling. If you absolutely require more aggressive and precise scheduling knowing there are no assurances, consider the alternatives above.

Job Scheduling & Management REST API

Obsidian Quartz cron4j Spring
Yes No No No

Obsidian introduced a REST API in version 1.5 to ease integration into other applications and software environments, regardless of the technology used. A complete range of job, scheduling and host management features are exposed via the API. This allows you to integrate Obsidian into external monitoring systems or perhaps even writing Obsidian jobs to react to specific situations. For example, if a job that runs hourly has been failing continually over a period of many hours, perhaps you would want automatically disable it. The API can also be used to retrieve the available execution and logging data in Obsidian and could be used for generating reports or informing interested parties of pertinent activity.

Custom Calendar Support

Obsidian Quartz cron4j Spring
No Yes No No

Quartz does have a feature to support custom calendars. This allows you to reference custom scheduling options in your job’s configured schedule. For example, perhaps you would want to run a job on every weekday, skipping certain business holidays. You can do so with Quartz, but not so with any of these other schedulers unless you were to put custom code in the job itself.

Conclusion

Obsidian has many additional features that haven’t been detailed here, such as configurable recovery options, resubmission of failed jobs, parameterized job support, job configuration validation, job results storage/retrieval and so on. In practice, many developers and even project managers gravitate toward these de facto solutions, but for too long we in the developer community been fighting with these scheduling technologies and contending with the inferior results. Try our live, functional and interactive demo site to see for yourself. If you like what you see, download Obsidian and be refreshed with this easy-to-use and feature-rich scheduler.

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.