Drew
Drew

Reputation: 1937

Using Java Executor on AppEngine causes AccessControlException

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?

Upvotes: 4

Views: 2954

Answers (2)

Fran&#231;ois Wauquier
Fran&#231;ois Wauquier

Reputation: 568

You can start thread in the request context

https://developers.google.com/appengine/docs/java/?csw=1#Java_The_sandbox

Upvotes: 1

Stephen Denne
Stephen Denne

Reputation: 37027

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 new java.lang.Thread. These restrictions also apply to JRE classes that make use of threads. For example, an application cannot create a new java.util.concurrent.ThreadPoolExecutor, or a java.util.Timer. An application can perform operations against the current thread, such as Thread.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

Upvotes: 11

Related Questions