A Sample Usage of Java’s Future and Spring’s @Async

Yesterday, a colleague asked about Java Threading. We have this sending-of-files-via-FTP requirement that is already working. Our users wanted us to add functionality that will allow them to do the sending asynchronously. The initial plan was to put the sending of the files in another Thread manually. This means that we have to create a Runnable implementation and manage the Threads ourselves.

My initial thought when my colleague asked about this is that this is something that JavaScript’s Promises can solve very easily. And then it hit me that I read something about this in Java 7. In Java 7, this is called Future. Futures allow the result of an asynchronous operation to be retrieved at a later time. Vanilla Java has implementations, such as FutureTask, that allow the execution of the task to be run in a separate thread. Spring made this easier and cleaner by making it declarable via the @Async annotation.

Here’s a very simple code that uses Java’s Future and Spring’s @Async feature:

After downloading the required Spring dependencies (it’s in spring-context for Maven users), we have to enable Spring’s asynchronous tasks by using the @EnableAsync annotation.

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class FtpAsyncConfig {
}

We can then use the @Async annotation to tell Spring to execute the method asynchronously when called.

import java.io.File;
import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

@Component
public class DefaultFtpApp implements FtpApp {
	@Async
	public Future<Boolean> sendAsync(final File file, final String host)
			throws InterruptedException {
		boolean result = false;

		//call the long running task here
		// this is in place of the actual sending of the file via FTP
		Thread.sleep(1000);

		// say sending succeeds and returns true
		result = true;

		System.out.println("Done running...");

		// Wrap the result in an AsyncResult
		return new AsyncResult<>(result);
	}
}

The other things to note above is the return type and the return value. Spring’s asynchronous task allows a void return value but since we want the result of the operation to be queried by the users, we’re returning a Future object. The return type must be specified inside the angle brackets. The actual return value is wrapped in an implementation of the Future interface, the AsyncResult class. The actual return value must be passed into its constructor.

We can now test this.

import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = {
		FtpAsyncConfig.class, DefaultFtpApp.class })
public class FtpAppTest {

	@Autowired
	private FtpApp ftp;

	@Test
	public void test() throws InterruptedException, ExecutionException {

		System.out.println("About to run...");

		// Call to sendAsync
		// this will execute in another thread. The sysout below this line will
		// execute before the sysout within sendAsync
		Future<Boolean> ftpResult = ftp.sendAsync(new File("somefile"),
				"somehost");

		System.out.println("This will run immediately.");

		// Using get without a timeout will wait for the async task to finish.
		// If you want to wait for only a certain time for the result, you may
		// use the get(long timeout, TimeUnit unit) instead. This flavor of
		// get() will throw a TimeoutException if the result is not yet given
		// after the specified amount of time.
		Boolean result = ftpResult.get();

		System.out.println("And the result of get() is " + result);

		Assert.assertTrue(result);

	}
}

The get() method can be used on the Future interface to retrieve the result of the asynchronous operation. This method also has another flavor where a timeout can be specified.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s