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?
The App Engine Java Overview states
an app cannot spawn threads
This is even more clearly stated in their docs on The Java Servlet Environment
Threads
A Java application cannot create a new
java.lang.ThreadGroup
nor a newjava.lang.Thread
. These restrictions also apply to JRE classes that make use of threads. For example, an application cannot create a newjava.util.concurrent.ThreadPoolExecutor
, or ajava.util.Timer
. An application can perform operations against the current thread, such asThread.currentThread().dumpStack()
.
Perhaps the white-listing is so that you can work with libraries that accept Executors
, and you can supply your own Executor
that performs the work in the current thread.
You could try the experimental Task Queues
You can start thread in the request context
https://developers.google.com/appengine/docs/java/?csw=1#Java_The_sandbox
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With