These probably don't qualify as canonical use cases, but they are applications of executors that I have come across in real life.
I have a service interface with a method that blocks, possibly for a long time, for example, the client side of an HTTP-based service that blocks while waiting for a response.
public interface Request ...
public interface Response ...
interface BlockingService {
Response serve (Request req) throws ServiceException;
}
I want an adapter that converts this interface into one that
returns a Future<Response> without blocking.
interface NonBlockingService {
Future serve (Request req);
}
Here's how I might do it under the currently checked in proposal.
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
class NonBlockingServiceAdapter implements NonBlockingService {
public NonBlockingServiceAdapter (BlockingService svc) {
this(svc, null);
}
public NonBlockingServiceAdapter (BlockingService svc, Executor executor) {
this.blockingService = svc;
if (executor == null) {
this.executor = Executors.newFixedThreadPool(3);
}
else {
this.executor = executor;
}
}
public Future serve (final Request req) {
Callable task = new Callable() {
public Response call () {
return blockingService.serve(req);
}
};
FutureTask ftask = new FutureTask(task);
executor.execute(ftask);
return ftask;
}
private final BlockingService blockingService;
private final Executor executor;
}
If I change my mind about using a single thread executor as the
default and want to use, say, a thread pool, I don't have to change
the imports and I don't have to learn any new types; I just
consult the Executors javadocs and change one line to something
like:
this.executor = Executors.newCachedThreadPool();
In this case it is irrelevant that the factory method returns a more specific type.
If there were no Executor factory methods or if those factory
methods were part of AbstractExecutor/ThreadExecutor, I'd have to
learn about and import a type just so I could get access to its
factory methods or public constructors.
If I have more complicated requirements on the internal
executor, I can pass in an instance that has been configured in
advance with Callbacks/Hooks/Intercepts, possibly an extension of an
existing class or even my own custom implementation of Executor.
If a later redesign requires lifecycle management for the
internal executor, under the current proposal I would change its
type to ExecutorService and provide methods on
NonBlockingServiceAdapter that ultimately call into the
ExecutorService instance. The executor and the executor
service are the same object under the current proposal, but this is not
a requirement for this example.
An annoying design flaw in one version of the Borland C++ (for Windows) runtime libraries causes a severe crash.if a C++ exception is thrown in DLL code within a thread other than the one in which the DLL was loaded. For JNI users, there are several ways around this problem, including changing the compiler and avoiding the use of C++ exceptions. However, if none of these workarounds are available, the only solution is to make sure that alls calls to native methods implemented by such a DLL are made in the same thread as the one that loaded the DLL.
If you run into this situation repeatedly, as I did, you might
find yourself wanting an adapter class to do the heavy lifting for
you using a dynamic proxy, as follows: For a class NativeImpl with
native methods implemented by a Borland C++ DLL, define an interface
NativeInterface consisting of all of the native methods
of NativeImpl,
and make NativeImpl extend NativeInterface. The
adapter uses Proxy.newProxyInstance to create a new instance of this
interface by implementing the invoke to execute each method
call as a Callable on a single thread executor that delegates to an
underlying NativeImpl.
class SingleThreadAdapter implements InvocationHandler {
public SingleThreadAdapter (String library) {
this.executor = Executors.newSingleThreadExecutor();
try {
FutureTask ftask = new FutureTask(new Runnable () {
public void run () {
System.loadLibrary(library);
}
});
executor.executor(ftask);
}
catch (ExecutionException e) {
if (e.getCause() == null) {
throw new RuntimeException(e);
}
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
}
if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
throw new RuntimeException(e.getCause());
}
// ... other invocation or interruption related catch clauses ...
}
public Object newInstance (Callable creator) throws Exception {
try {
FutureTask createTask = new FutureTask(creator);
executor.execute(createTask);
Object instance = createTask.get();
return Proxy.newProxyInstance(
instance.getClass().getClassLoader(),
instance.getClass().getInterfaces(),
new Handler(instance));
}
catch (ExecutionException e) {
if (e.getCause() == null) {
throw e;
}
if (e.getCause() instanceof Exception) {
throw e.getCause();
}
else {
throw new RuntimeException(e.getCause());
}
}
// ... other invocation or interruption related catch clauses ...
}
private class Handler implements InvocationHandler {
public Object invoke (Object proxy,
final Method method,
final Object[] args) throws Throwable {
Callable task = new Callable () {
public Object call () throws Exception {
method.invoke(instance, args);
}
};
try {
FutureTask ftask = new FutureTask(task)
executor.execute(ftask);
return ftask.get();
}
catch (ExecutionException e) {
throw e.getCause() == null ? e : e.getCause();
}
// ... other invocation or interruption related catch clauses ...
}
Handler (Object instance) {
this.instance = instance;
}
private final Object instance;
}
private final Executor executor;
}
Typical use of this class would be:
try {
SingleThreadAdapter adapter = new SingleThreadAdapter("LegacyEngine.dll");
NativeInterface ni = (NativeInterface)
adapter.newInstance(new Callable () {
public NativeInterface call () {
return new NativeImpl();
}
});
int i = ni.nativeIntMethod();
}
// catch clauses for adapter creation, instance creation, invocation,
// interruption...
Even though it is clear that SingleThreadExecutor and no other kind of Executor will be used in the implementation, there is no reason to expose this anywhere except in the SingleThreadAdapter constructor.
The exception handling is delicate and long winded. I punted on most of it, and I'm not sure I got the rest right either.