Does your application have working brakes?
June 28, 2012 | 3 min ReadSuppose your friend got a new motorcycle. The first questions are probably like the following. What horsepower does it have? How fast can it go? How long does it take from 0 to 100km/h? How about 0 to 200km/h? Rarely will you ask how fast it can stop. Does it have brakes at all?
Of course every motor vehicle is supposed to have working brakes. In software things are different.
In any application many thoughts are given to the when and how things start up. Not as often are thoughts given to application shutdown. Of course, there are always these stop()
methods, i.e. in the IApplication
in Eclipse RCP or RAP and in every OSGi bundles activator. But what are you supposed to do with it? You’re supposed to shut down your connections, release your resources and stop your threads. Wait until this has happened, then exit stop()
. Otherwise you risk ugly log entries in the best case, but also memory, UI handle or file handle leakages.
Cleaning up your resources is especially important in environments where the application lifecycle is not bound to a user interaction, i.e. on the server side or in RCP applications that just keep running.
In the following example, I present a small OSGi bundle with an activator and a class TimeIndicator
. The Activator has the default start()
and stop()
methods, and at this moment the stop()
method is implemented somewhat carelessly.
public void start(BundleContext bundleContext) throws Exception {
Activator.context = bundleContext;
clock = new TimeIndicator();
clock.start();
}
public void stop(BundleContext bundleContext) throws Exception {
// TODO free up resources
Activator.context = null;
}
The TimeIndicator
is a Thread that gets the OSGi LogService and logs a message “Tick” every second until it is shut down. For the purposes of this example the TimeIndicator
assumes that it will always get the LogService
via the activators bundle context.
@Override
public void run() {
running = true;
while (running) {
BundleContext context = Activator.getContext();
ServiceReference reference = context.getServiceReference(LogService.class);
LogService logService = context.getService(reference);
logService.log(LogService.LOG_ERROR, "Tick");
waitASecond();
}
}
Lets start this bundle. I’m starting this bundle as OSGi application. In the OSGi console, I stop my bundle, and then inspect the status:
osgi> stop com.eclipsesource.examples.cleanup osgi> ss
Framework is launched.
id State Bundle 0 ACTIVE org.eclipse.osgi_3.7.2.v20120110-1415 1 ACTIVE org.eclipse.core.contenttype_3.4.100.v20110423-0524 2 RESOLVED com.eclipsesource.examples.cleanup_1.0.0.qualifier 3 ACTIVE org.eclipse.equinox.app_1.3.100.v20110321 4 ACTIVE org.eclipse.equinox.common_3.6.0.v20110523
!ENTRY com.eclipsesource.examples.cleanup 4 0 2012-06-05 09:25:52.036 !MESSAGE Tick
This is a dangerous state. The bundle stopped, but the thread is still running. It will soon fail because the context will be null
in the next iteration, but in some bigger jobs it is not even sure that required classes would be loaded.
But the solution is straightforward. Here is what should be done in the stop()
method:
public void stop(BundleContext bundleContext) throws Exception {
if( clock != null ) {
clock.stopTicTac();
clock.join();
}
Activator.context = null;
}
Checking your brakes is not hard, but just as a vehicle needs an inspection from time to time you should have a look now and then at cleaning up your resources properly.
Credit: Motorcycle photo by Richard Taylor via CC BY 2.0