How do you get java.util.concurrent.Executor or CompletionService to work on Google AppEngine? The classes are all officially white-listed, but I get a runtime security error when trying to submit asynchronous tasks.
Code:
// uses the async API but this factory makes it so that tasks really
// happen sequentially
Executor executor = java.util.concurrent.Executors.newSingleThreadExecutor();
// wrap Executor in CompletionService
CompletionService<String> completionService =
new ExecutorCompletionService<String>(executor);
final SomeTask someTask = new SomeTask();
// this line throws exception
completionService.submit(new Callable<String>(){
public String call() {
return someTask.doNothing("blah");
}
});
// alternately, send Runnable task directly to Executor,
// which also throws an exception
executor.execute(new Runnable(){
public void run() {
someTask.doNothing("blah");
}
});
}
private class SomeTask{
public String doNothing(String message){
return message;
}
}
Exception:
java.security.AccessControlException:
access denied
(java.lang.RuntimePermission
modifyThreadGroup) at
java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at
java.security.AccessController.checkPermission(AccessController.java:546)
at
java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at
com.google.appengine.tools.development.DevAppServerFactory$CustomSecurityManager.checkPermission(DevAppServerFactory.java:166)
at
com.google.appengine.tools.development.DevAppServerFactory$CustomSecurityManager.checkAccess(DevAppServerFactory.java:191)
at
java.lang.ThreadGroup.checkAccess(ThreadGroup.java:288)
at
java.lang.Thread.init(Thread.java:332)
at
java.lang.Thread.(Thread.java:565)
at
java.util.concurrent.Executors$DefaultThreadFactory.newThread(Executors.java:542)
at
java.util.concurrent.ThreadPoolExecutor.addThread(ThreadPoolExecutor.java:672)
at
java.util.concurrent.ThreadPoolExecutor.addIfUnderCorePoolSize(ThreadPoolExecutor.java:697)
at
java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:652)
at
java.util.concurrent.Executors$DelegatedExecutorService.execute(Executors.java:590)
at
java.util.concurrent.ExecutorCompletionService.submit(ExecutorCompletionService.java:152)
This code works fine when run on Tomcat or via command-line JVM. However, it chokes in the AppEngine SDK Jetty container (tried with Eclipse plugin and the maven-gae-plugin).
AppEngine is likely designed to not allow potentially dangerous programs to run, so I could see them completely disabling thread creation. However, why would Google allow you to create a class, but not allow you to call methods on it? White-listing java.util.concurrent is misleading.
Is there some other way to do parallel/simultaneous/concurrent tasks on GAE?