JPPF Issue Tracker
star_faded.png
Please log in to bookmark issues
bug_report_small.png
CLOSED  Bug report JPPF-606  -  ClassNotFoundException when submitting a Callable to an ExecutorService from a JPPF task
Posted Oct 08, 2019 - updated Oct 08, 2019
icon_info.png This issue has been closed with status "Closed" and resolution "RESOLVED".
Issue details
  • Type of issue
    Bug report
  • Status
     
    Closed
  • Assigned to
     lolo4j
  • Progress
       
  • Type of bug
    Not triaged
  • Likelihood
    Not triaged
  • Effect
    Not triaged
  • Posted by
     lolo4j
  • Owned by
    Not owned by anyone
  • Category
    Node
  • Resolution
    RESOLVED
  • Priority
    High
  • Reproducability
    Always
  • Severity
    Normal
  • Targetted for
    icon_milestones.png JPPF 6.1.3
Issue description
When submitting a Callable to an ExecutorService from a JPPF task, where the Callable requires a class that has not yet been loaded by the task, I get the following exception:
java.util.concurrent.ExecutionException: java.lang.NoClassDefFoundError: test/MyTask$MyClass
  at java.util.concurrent.FutureTask.report(FutureTask.java:122)
  at java.util.concurrent.FutureTask.get(FutureTask.java:192)
  at test.MyTask.run(MyTask.java:13)
  at org.jppf.execute.NodeTaskWrapper.run(NodeTaskWrapper.java:191)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoClassDefFoundError: test/MyTask$MyClass
  at test.MyTask.lambda$0(MyTask.java:12)
  ... 4 more
Caused by: java.lang.ClassNotFoundException: Could not load class 'test.MyTask$MyClass'
  at org.jppf.classloader.AbstractJPPFClassLoader.findClass(AbstractJPPFClassLoader.java:156)
  at org.jppf.classloader.AbstractJPPFClassLoader.findClass(AbstractJPPFClassLoader.java:111)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
  at org.jppf.classloader.AbstractJPPFClassLoader.loadClass(AbstractJPPFClassLoader.java:331)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
  ... 5 more
When invoking Callable.call() directly, instead of submitting the Callable via an executor, then no error occurs
Steps to reproduce this issue
Submit the following JPPF task:
public class MyTask extends AbstractTask<String> {
  @Override
  public void run() {
    final ExecutorService executor = Executors.newFixedThreadPool(1);
    try {
      final Callable<String> callable = () -> "hello from " + new MyClass().getName();
      final String msg = executor.submit(callable).get();
      setResult(msg);
    } catch (final Exception e) {
      setThrowable(e);
    } finally {
      executor.shutdownNow();
    }
  }
 
  public static class MyClass {
    public String getName() {
      return "task";
    }
  }
}
using ths client-side code:
public class Runner {
  public static void main(final String...args) {
    try (final JPPFClient client = new JPPFClient()) {
      final JPPFJob job = new JPPFJob().setName("my job");
      for (int i=1; i<=1; i++) {
        job.add(new MyTask()).setId(String.format("task-%03d", i));
      }
      final List<Task<?>> results = client.submit(job);
      for (final Task<?> task: results) {
        if (task.getThrowable() != null) {
          System.out.printf("%s got exception: %s\n", task.getId(), ExceptionUtils.getStackTrace(task.getThrowable()));
        } else {
          System.out.printf("%s got result: %s\n", task.getId(), task.getResult());
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

#4
Comment posted by
 lolo4j
Oct 08, 05:11
I have tried the same code in JPPF 6.0 (using an anonymous class instead of a lambda, since it uses Java 7) and the error does not occur there.
#5
Comment posted by
 lolo4j
Oct 08, 06:06
The cause of the problem is that the job uuid for a class loading request is stored in a ThreadLocal variable. It is thus available to the processing thread that executes the JPPF task, but not to any thread created by that task. Because of that, the class loading request has a null job uuid, which prevents the driver from dispatching the request to the client that submitted the job, or from finding the class in its class definitions cache.
#6
Comment posted by
 lolo4j
Oct 08, 06:21
Using InheritableThreadLocal instead of ThreadLocal fixes the problem.