Configuring Spring’s Scheduler through Docker

Spring (Spring Framework) is such a joy to use. So often when I find a problem with what I am doing the guys at Spring have built some feature into their product that solves it and all I had to do was find it.

Recently I had two problems to solve. The first was I wanted to externalise some configuration settings. I thought it might be tricky but actually it is almost obvious. I was already using the @Value annotation to inject values. I just add something like this into a class that is being instantiated as a Spring Bean:

@Value("${nz.co.senanque.demo.ViewScopedView.identifier:not-set}") private transient String m_identifier;

And in the declaration my @Configuration class I add:

@PropertySource("classpath:config.properties")

Finally I add a config.properties class in the top of the classpath, ie in my src/main/resources directory (or somewhere else if I want to change the @PropertySource to point there instead).

In config.properties I put something like this:

nz.co.senanque.demo.ViewScopedView.identifier=whatever

Spring will fetch ‘whatever’ and inject it into m_identifier. I like to use names like nz.co.senanque.demo.ViewScopedView.identifier because it helps me remember where I used that value, but you can use any convention you like, and mine doesn’t work too well if I use the value in more than one place. Notice the ‘not-set’ value in the @Value declaration. This is optional, and if you omit the value from your properties file that value, if present will be used. If there is no value available m_identifier will be left at null.

If you want to do this in XML just use

<context:property-placeholder location="classpath:config.properties"/>

All this is easily findable, by the way. It is the next bit that gets a little tricky.

What if you want to override one of your config values? Easy. Just launch your JVM with the -D option and define the property you want. Interesting. Okay, what if you want to bundle your application into Docker as a war file and run it under Tomcat. Easy again. Just build an image with a Dockerfile like this:

# Pull base image
From tomcat:8-jre8

# Maintainer
MAINTAINER "Roger Parkinson <roger.parkinson35@gmail.com">

# Copy to images tomcat path
ADD target/*.war /usr/local/tomcat/webapps/

You build it with this command:

docker build -t demo .

That gives you an image called demo and you run it like this:

docker run -it --rm -p 8080:8080 demo:latest

But if you want to you can run it like this:

docker run -e "nz.co.senanque.demo.ViewScopedView.identifier=from -e" -it --rm -p 8080:8080 demo:latest

That will override the value in the config.properties file, if there is one, or it will override the value in the default if there is one. Whatever is there you will end up with the value “from -e” in the m_identifier. But that looks a bit bothersome if you have a lot of those to set so you can put them in a file and refer to it in the run command with the –env-file option.

So this is all just fine for what I want, but I said I had a second problem to solve. This one was about scheduling. I use the Spring scheduler to repeatedly run several methods, each of which might need a different interval. I want those intervals to be customised externally but with sensible defaults. I also want to be able to turn the scheduler off in some variants of the image. That means I want the scheduler to be turned off by something external.

Now, we already know how to do external configuration so it is just a matter of coding things right. Here’s my scheduler code:

@Component
@Conditional(ScheduleCondition.class)
public class Scheduler {

@Autowired Executor m_executor;

@Scheduled(fixedDelayString="${nz.co.senanque.myui.Scheduler.activeProcesses:10000}")
public void activeProcesses() {
m_executor.activeProcesses();
}
@Scheduled(fixedDelayString="${nz.co.senanque.myui.Scheduler.deferredEvents:10000}")
public void deferredEvents() {
m_executor.deferredEvents();
}
@Scheduled(fixedDelayString="${nz.co.senanque.myui.Scheduler.clearDeferredEvents:60000}")
public void clearDeferredEvents() {
m_executor.clearDeferredEvents();
}
}

It is annotated as a Spring Component and it is in a package that I’ve told Spring to scan, so this class will be instantiated as a bean. Another bean, my executor, which contains the code I want run repeatedly, is injected automatically by Spring as well. Finally the @Scheduled annotations tell Spring to run each of those methods with the configured delay between each run. Notice that the actual delay is specified similar to the @Value approach and, yes, these values can be added to the config.properties file and/or added using the -e option in Docker. But the values we have in the defaults are probably about right for most systems so if people don’t want to do anything to them they can forget about them.

I also said I needed to be able to turn all the scheduling off. That’s what the @Conditional is for. It refers to a class called ScheduleCondition. That class implements org.springframework.context.annotation.Condition which means it has a matches() method that looks like this:

public boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String p = context.getEnvironment().getProperty("nz.co.senanque.ScheduleCondition");
if ("off".equals(p)) {
return false;
}
return true;
}

So what the method does is check for a property and if it exists and contains ‘off’ returns false. Otherwise it returns true. Spring understands that if it returns false it will not instantiate that SchedulerBean. If it doesn’t instantiate the bean it won’t schedule the methods either. So when I want to suppress the scheduler all I need to do is supply the property with value ‘off’ and the job is done. But there is a slight catch.

I tried adding nz.co.senanque.ScheduleCondition=off to my config.properties file and…no effect. I haven’t tracked down just why this is because it doesn’t fit my use case. When I specify it through Docker or through a JVM parameter it works just fine.

References:
http://ryannickel.com/html/playing_with_docker_enviornment_variables.html
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html
http://tuhrig.de/why-using-springs-value-annotation-is-bad/ (which shows how to use @Value, and possibly a better way to use it)

Leave a Reply

Your email address will not be published. Required fields are marked *