Improving the Spring TaskExecutor

October 28, 2006

Abstract

In this blogpost I try to explain how the Spring TaskExecutor should be improved.

Introduction

I’m interested in concurrency control and also in the Spring Framework. I have used the new concurrency library of Java 5 in quite a few Spring based projects and it works like a dream. One of the interfaces I use quite often is the Executor from Java 5. It is great for separating the submission of tasks from the execution mechanism for running the task. The consequence is that you don’t have to fiddle around with creating threads yourself and this makes your components a lot cleaner, more customizable and better testable.

It is interesting to see that Spring 2 also has support for this concept, in the form of the TaskExecutor. The advantage of using the TaskExecutor instead of the Java 5 specific Executor is that it doesn’t make code dependant of a specific implementation, eg:

  1. JSR 166: part of Java 5.
  2. backport of JSR 166: great if you still need to work with Java 1.4.
  3. CommonJ WorkManager: if you work in an environment where it is not allowed or acceptable to create unmanaged threads. There are plans to make it possible to use JSR-166 just in just these kind of environments.
  4. one of the Spring 2 implementations.

In most cases I prefer using the Executor (or one of the subinterfaces like the ExecutorService) because I often need more control, like waiting for the completion of a task. Often I have the freedom to use the concurrency library that suits my needs, so why not make use of the library directly. But if you don’t have this freedom, the TaskExecutor is a good solution.

What is the problem?

The problem is that there is no clearly defined way to detect if a task is not accepted by a TaskExecutor implementation. There are various reasons why a task can’t be accepted:

  1. if a spring executor implementation uses a workqueue, for example the ThreadPoolTaskExecutor, it could decide to rejects a task when the workqueue is full. If there is no limit on the size of the workqueue (SimpleThreadPoolTaskExecutor or a ThreadPoolExecutor with an unbound workqueue), it could lead to resource problems like running out of memory. This means that the system doesn’t degrade gracefully under heavy load.
  2. if a spring executor is shutting down, is not started or even when it is momentarily pausing.

That is why the execute method of the Executor throws a java.util.concurrent.RejectedExecutionException to signal when a task is rejected. However the execute method of the TaskExecutor, doesn’t define an exception that is thrown when a task is rejected.

You might wonder if is a good thing to deal with this exception, instead of relying on a some kind of general exception handler. I think it is good to create the possibility to deal with the exception immediately, because it is not a situation where all bets are off, like a programming or database exception. When a task can’t be executed, you give the client (another java object) the option to catch it and deal with it. And in most cases the client is aware of threading because it is dealing with an asynchronous call.

Tip

Try to make the number of components that are aware of threading, as small as possible. This makes the system a lot easier to deal with because you don’t have to worry about it all the time. In most cases, isolation (confinement) and immutability are your biggest friends to reduce concurrency control related complexity.

Solution

The solution is letting the execute method of the TaskExecutor throw a Spring specific unchecked exception, eg the : RejectedTaskExecutionException. This solution is quite simple and they already do it in other parts of the system like:

  1. DataAccessExcepton for the access to datasources
  2. RemoteAccessException for remoting.

Unification of different exception hierarchies into a single one, makes it more reliable to switch between implementations because code does not depend on implementation specific exceptions. And because the exception is not checked:

  • clients are not forced to deal with it if they don’t need to
  • it doesn’t break any existing implementations of the TaskExecutor interface

So I wonder why they have not added it. I placed a JIRA issue for it some time ago, but it hasn’t been fixed although Spring 2 has been released.


String is not a good universal datatype

October 11, 2006

Abstract

In this post I want to make clear that using Strings as a datatype is terrible for design of objects. The development may be faster on the short run, but on the long run, a price has to be paid.

Introduction

I’m working on a EJB 2.1/Struts project where a lot fields of objects are String. These fields are used to store different kinds of information: accountnumbers, phonenumbers, status information (an enumeration of values) etc.

The problems

The problem with using Strings as datatypes:

  1.   operations on the strings are scattered all over the place. If you need a function to format a phonenumber, it often is created adhoc instead of in a suitable class. This makes logic very hard to find back and the consequence is that you get more code-duplication.  
  2.   it is easy to get inconsistent data: a phonenumber of 20 digits for example is not possible, but you can store anything in a String. This makes it very difficult to control the consistency of the data.  
  3.   you don’t have any typesafeness: you could pass a telephonenumber to a bankaccount format function. Using a specific datatype instead of a String, makes it possible to get compiletie typesafeness and this is a good way to decrease developmenttime.  
  4.   it is more difficult to understand what a field means (especially if the documentation is not that great): if you have a field of type String and name imsi, how can you tell it contains a phonenumber? If the field is of type Phonenumber, you know at least it is a phonenumber.  

And I expect there are more, but these were the first that came to mind.

Conclusion

Ofcourse it is easier on the short run (you don’t have to create any special classes), but on the long run development will slowdown. I thought most developers knew this, but it appears that not all developers share the same knowledge.