Worker API classloaderIsolation vs processIsolation in test-kit

Hello,

recently we tried to rewrite a plugin of ours to make use of the Worker API, specifically to make use of the classloaderIsolation feature.

    @TaskAction
    public void executeTask() {
        ConfigurableFileCollection classpath = getClasspath();
        WorkerExecutor executor = getWorkerExecutor();
        WorkQueue queue = executor.classLoaderIsolation(spec -> spec.getClasspath().from(classpath));

        queue.submit(DbWorkAction.class, parameters -> {
            // set db params
        });
        queue.await();
    }

The task does interact with a database and this can be unavailable due to various reasons, so the WorkAction will obviously fail.

public void execute() {
        try {
            // do database stuff
        } catch (Exception e) {
            throw new GradleException("db interaction failed", e);
        }
    }

As we covered that in a test-kit case we experienced that (even though we could see the right stacktrace in the logs) the test was not successful due to the call to buildAndFail crashing before we could even evaluate the builds output.

        when:
        def gradleRunner = GradleRunner.create()
                .forwardOutput()
                .withProjectDir(dir)
                .withEnvironment(env)
                .withArguments(arguments)
                .withPluginClasspath();
        BuildResult result = gradleRunner.buildAndFail() // <-- it fails here!

        then:
        result.task(':updateDB').outcome == TaskOutcome.FAILED
        result.output.contains("Connection to localhost:43432 refused")

The result of the test states:

// at first there are the PostgreSql jdbc driver stacks but then:
* Exception is:
java.lang.NoClassDefFoundError: org/postgresql/util/ServerErrorMessage
	at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
	at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3166)
	at java.base/java.lang.Class.getDeclaredMethods(Class.java:2309)
	at org.gradle.internal.serialize.ExceptionPlaceholder.findCandidateGetCausesMethod(ExceptionPlaceholder.java:296)
	at org.gradle.internal.serialize.ExceptionPlaceholder.tryExtractMultiCauses(ExceptionPlaceholder.java:315)
	at org.gradle.internal.serialize.ExceptionPlaceholder.extractCauses(ExceptionPlaceholder.java:279)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:104)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.TopLevelExceptionPlaceholder.<init>(TopLevelExceptionPlaceholder.java:28)
	at org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream.doReplaceObject(ExceptionReplacingObjectOutputStream.java:67)
	at org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream$1.transform(ExceptionReplacingObjectOutputStream.java:31)
	at org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream.replaceObject(ExceptionReplacingObjectOutputStream.java:62)
	at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1135)
	at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:345)
	at org.gradle.tooling.internal.provider.serialization.PayloadSerializer.serialize(PayloadSerializer.java:53)
	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl.apply(BuildSessionLifecycleBuildActionExecuter.java:105)
	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl.apply(BuildSessionLifecycleBuildActionExecuter.java:80)
	at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:71)
	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter.execute(BuildSessionLifecycleBuildActionExecuter.java:62)
	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter.execute(BuildSessionLifecycleBuildActionExecuter.java:41)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:64)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:51)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:39)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31)
	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75)
	at org.gradle.util.internal.Swapper.swap(Swapper.java:38)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:64)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.ClassNotFoundException: org.postgresql.util.ServerErrorMessage
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:594)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:527)
	... 66 more

// and at the end there's even more:
org.gradle.tooling.GradleConnectionException: Could not execute build using connection to Gradle installation ...
Caused by: java.lang.NullPointerException
	at org.gradle.tooling.internal.provider.serialization.PayloadSerializer.deserialize(PayloadSerializer.java:67)
	at org.gradle.tooling.internal.provider.ProviderConnection.run(ProviderConnection.java:232)
	at org.gradle.tooling.internal.provider.ProviderConnection.run(ProviderConnection.java:152)
	at org.gradle.tooling.internal.provider.DefaultConnection.getModel(DefaultConnection.java:149)
	at org.gradle.tooling.internal.consumer.connection.CancellableModelBuilderBackedModelProducer.produceModel(CancellableModelBuilderBackedModelProducer.java:54)
	at org.gradle.tooling.internal.consumer.connection.PluginClasspathInjectionSupportedCheckModelProducer.produceModel(PluginClasspathInjectionSupportedCheckModelProducer.java:38)
	at org.gradle.tooling.internal.consumer.connection.AbstractConsumerConnection.run(AbstractConsumerConnection.java:64)
	at org.gradle.tooling.internal.consumer.connection.ParameterValidatingConsumerConnection.run(ParameterValidatingConsumerConnection.java:49)
	at org.gradle.tooling.internal.consumer.DefaultBuildLauncher$1.run(DefaultBuildLauncher.java:97)
	at org.gradle.tooling.internal.consumer.DefaultBuildLauncher$1.run(DefaultBuildLauncher.java:89)
	at org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor.run(LazyConsumerActionExecutor.java:143)
	at org.gradle.tooling.internal.consumer.connection.CancellableConsumerActionExecutor.run(CancellableConsumerActionExecutor.java:45)
	at org.gradle.tooling.internal.consumer.connection.ProgressLoggingConsumerActionExecutor.run(ProgressLoggingConsumerActionExecutor.java:61)
	at org.gradle.tooling.internal.consumer.connection.RethrowingErrorsConsumerActionExecutor.run(RethrowingErrorsConsumerActionExecutor.java:38)
	at org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor$1$1.run(DefaultAsyncConsumerActionExecutor.java:67)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)

We found 3 workarounds, but none of them seem very appealing:

  • switching from classloaderIsolation to processIsolation
  • running the test withDebug(true) (which is not possible when using environment variables for the gradleRunner) completes the test successfully but the NoClassDefFoundError is still in the log
  • “stringify” the original Exception and deliver the messages and stacktraces as message of the GradleException

Anyone an Idea if we’re doing something wrong or is it a known issue in test-kit?

Maybe investigate what getClasspath() returns and whether it is the expected result including that PostgreSQL class?

If it works when executing the plugin in a real project but not when using TestKit, often a proper mitigation is to not use withPluginClasspath(), but publishing the plugin to some local maven repository (optimally something dedicated and not mavenLocal()). Then use the plugin in the test from that repository.

The postgresql.jar is compileOnly for the plugin, but it’s a dependency in a configuration created by the plugin which is the classpath you see in my code example.

We specifically wanted to decouple the Tasks runtime dependencies from the plugin classpath, cause we had issues earlier with plugins that where forcing certain library versions that where not compatible with another plugins dependencies (one plugin forcing commons-io:1.2 and the other requesting 1.12 - awful but it really happened).
We also wanted to be able to simply play with the dependencies versions without the need to rebuild the plugins.

In my quick testing as an includedBuild the same error occurred when trying to run the task while no database was up.

FAILURE: Build completed with 2 failures.

1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':PBL_TestEAR:updateDB'.
> There was a failure while executing work items
   > A failure occurred while executing com...UpdateWorkAction
      > db interaction failed

* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':updateDB'.
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:130)
	at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:128)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:116)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:331)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:318)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:314)
	at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:80)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:314)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:303)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:463)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:380)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
Caused by: org.gradle.workers.WorkerExecutionException: There was a failure while executing work items
	at org.gradle.workers.internal.DefaultWorkerExecutor.workerExecutionException(DefaultWorkerExecutor.java:223)
	at org.gradle.workers.internal.DefaultWorkerExecutor.await(DefaultWorkerExecutor.java:217)
	at org.gradle.workers.internal.DefaultWorkerExecutor.access$100(DefaultWorkerExecutor.java:60)
	at org.gradle.workers.internal.DefaultWorkerExecutor$DefaultWorkQueue.await(DefaultWorkerExecutor.java:312)
	at com...UpdateTask.updateDB(UpdateTask.java:42)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
	at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:244)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:229)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:212)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:195)
	at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:162)
        ...
Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing com...UpdateWorkAction
	at org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution.waitForCompletion(DefaultWorkerExecutor.java:287)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.lambda$waitForItemsAndGatherFailures$2(DefaultAsyncWorkTracker.java:130)
	at org.gradle.internal.Factories$1.create(Factories.java:31)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:336)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:319)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLock(DefaultWorkerLeaseService.java:324)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:126)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:88)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForAll(DefaultAsyncWorkTracker.java:78)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:72)
	at org.gradle.workers.internal.DefaultWorkerExecutor.await(DefaultWorkerExecutor.java:215)
	at org.gradle.workers.internal.DefaultWorkerExecutor.access$100(DefaultWorkerExecutor.java:60)
	at org.gradle.workers.internal.DefaultWorkerExecutor$DefaultWorkQueue.await(DefaultWorkerExecutor.java:312)
	at com...UpdateTask.updateDB(UpdateTask.java:42)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
	at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:244)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:229)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:212)
        ...
Caused by: org.gradle.api.GradleException: db interaction failed
	at com...UpdateWorkAction.execute(UpdateWorkAction.java:20)
	at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
	at org.gradle.workers.internal.AbstractClassLoaderWorker$1.create(AbstractClassLoaderWorker.java:54)
	at org.gradle.workers.internal.AbstractClassLoaderWorker$1.create(AbstractClassLoaderWorker.java:48)
	at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
	at org.gradle.workers.internal.AbstractClassLoaderWorker.executeInClassLoader(AbstractClassLoaderWorker.java:48)
	at org.gradle.workers.internal.IsolatedClassloaderWorker.run(IsolatedClassloaderWorker.java:49)
	at org.gradle.workers.internal.IsolatedClassloaderWorker.run(IsolatedClassloaderWorker.java:30)
	at org.gradle.workers.internal.IsolatedClassloaderWorkerFactory$1.lambda$execute$0(IsolatedClassloaderWorkerFactory.java:57)
	at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
	at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
	at org.gradle.workers.internal.IsolatedClassloaderWorkerFactory$1.execute(IsolatedClassloaderWorkerFactory.java:49)
	at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:187)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:120)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:162)
	at org.gradle.internal.Factories$1.create(Factories.java:31)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:264)
	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:128)
	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:133)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:157)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:126)
	... 2 more
Caused by: liquibase.exception.DatabaseException: org.postgresql.util.PSQLException: Connection to localhost:43432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
	at liquibase.database.DatabaseFactory.openConnection(DatabaseFactory.java:247)
	at liquibase.database.DatabaseFactory.openDatabase(DatabaseFactory.java:151)
	at liquibase.database.DatabaseFactory.openDatabase(DatabaseFactory.java:140)
	... 32 more
Caused by: org.postgresql.util.PSQLException: Connection to localhost:43432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
	at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:280)
	at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
	at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195)
	at org.postgresql.Driver.makeConnection(Driver.java:454)
	at org.postgresql.Driver.connect(Driver.java:256)
	at liquibase.database.DatabaseFactory.openConnection(DatabaseFactory.java:239)
	... 39 more
Caused by: java.net.ConnectException: Connection refused: connect
	at org.postgresql.core.PGStream.<init>(PGStream.java:70)
	at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:91)
	at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:192)
	... 44 more

==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
org/postgresql/util/ServerErrorMessage
> org.postgresql.util.ServerErrorMessage

* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

* Exception is:
java.lang.NoClassDefFoundError: org/postgresql/util/ServerErrorMessage
	at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
	at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3166)
	at java.base/java.lang.Class.getDeclaredMethods(Class.java:2309)
	at org.gradle.internal.serialize.ExceptionPlaceholder.findCandidateGetCausesMethod(ExceptionPlaceholder.java:296)
	at org.gradle.internal.serialize.ExceptionPlaceholder.tryExtractMultiCauses(ExceptionPlaceholder.java:315)
	at org.gradle.internal.serialize.ExceptionPlaceholder.extractCauses(ExceptionPlaceholder.java:279)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:104)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.ExceptionPlaceholder.convertToExceptionPlaceholderList(ExceptionPlaceholder.java:174)
	at org.gradle.internal.serialize.ExceptionPlaceholder.<init>(ExceptionPlaceholder.java:141)
	at org.gradle.internal.serialize.TopLevelExceptionPlaceholder.<init>(TopLevelExceptionPlaceholder.java:28)
	at org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream.doReplaceObject(ExceptionReplacingObjectOutputStream.java:67)
	at org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream$1.transform(ExceptionReplacingObjectOutputStream.java:31)
	at org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream.replaceObject(ExceptionReplacingObjectOutputStream.java:62)
	at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1135)
	at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:345)
	at org.gradle.tooling.internal.provider.serialization.PayloadSerializer.serialize(PayloadSerializer.java:53)
        ...
Caused by: java.lang.ClassNotFoundException: org.postgresql.util.ServerErrorMessage
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:594)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:527)
	... 66 more

==============================================================================

BUILD FAILED in 2s
12:26:21: Execution finished 'updateDB --stacktrace'.

So that might answer my previous question if it’s only related to test-kit, as it’s also happening without. But now it’s even more astounding that withDebug(true) did work during testing.

Not an included build, published to a local directory and taken from there.
When using withPluginClasspath iirc the plugin’s classes are in some higher class loader that might then also be available from there in the classloader isolation and then cannot find the PostgreSQL class as it is in a lower classpath.
When using debug, the tests are executed in-process and so classloaders are again different.
The more production-y setup you have if you publish to a local repository and consume from there.

Fails the exact same way (even used a new Version to make sure I’m not mixing up anything).
It’s the exact same behavior like in the includedBuild.

Hm, then I’m out of idea without having an MCVE hands-on I might find time to have a look at.

For the classloaders I am expecting them to behave like you said.
The plugin classloader should not see the tasks Worker classes.
The Worker should not see the plugin classpath.

I am fully aware that throwing an Exception in the Worker, which is an instance of a Class that is only available within that worker is a tricky thing to hand back to the gradle core, but I expected that to be taken care of and don’t remember seeing that issue before.

For the MVCE, I’ll try to squeeze out some time to create one.

1 Like

@Vampire I’ve created a minimal example showcasing the issue, hope that helps to shed some light on the issue I was trying to discuss:

That is using withPluginClasspath(), where is the version that uses instead a locally published version?

Ah, wait, I think I see what you mean now, sorry.
The Gradle serialization tries to replace the exception with some placeholder and there fails due to that no class def found error while trying to get all declared methods.

Looks to me like a Gradle bug you should report.