Skip to content

Commit

Permalink
More laziness to prevent Stackoverflow exceptions. (#2260)
Browse files Browse the repository at this point in the history
Make interactions with gradle java toolchains more lazy, so `StackOverflowError`s do not occur
  • Loading branch information
CRogers authored May 9, 2022
1 parent 3438fa3 commit 7346e88
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 62 deletions.
6 changes: 6 additions & 0 deletions changelog/@unreleased/pr-2260.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type: fix
fix:
description: Make interactions with gradle java toolchains more lazy, so `StackOverflowError`s
do not occur
links:
- https://github.com/palantir/gradle-baseline/pull/2260
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* (c) Copyright 2022 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.palantir.baseline.plugins.javaversions;

// CHECKSTYLE:OFF

import javax.inject.Inject;
import org.gradle.api.file.RegularFile;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.tasks.javadoc.internal.JavadocToolAdapter;
import org.gradle.jvm.toolchain.JavaInstallationMetadata;
import org.gradle.process.internal.ExecActionFactory;
// CHECKSTYLE:ON

final class BaselineJavadocToolAdapter extends JavadocToolAdapter {
private final BaselineJavadocTool javadocTool;

BaselineJavadocToolAdapter(ExecActionFactory execActionFactory, BaselineJavadocTool javadocTool) {
super(execActionFactory, null);
this.javadocTool = javadocTool;
}

@Override
public JavaInstallationMetadata getMetadata() {
return javadocTool.getMetadata();
}

@Override
public RegularFile getExecutablePath() {
return javadocTool.getExecutablePath();
}

public static BaselineJavadocToolAdapter create(ObjectFactory objectFactory, BaselineJavadocTool javadocTool) {
return new BaselineJavadocToolAdapter(
objectFactory.newInstance(ExecActionFactoryGrabber.class).getExecActionFactory(), javadocTool);
}

interface ExecActionFactoryGrabber {
@Inject
ExecActionFactory getExecActionFactory();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@

package com.palantir.baseline.plugins.javaversions;

import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Provider;
import org.gradle.jvm.toolchain.JavaCompiler;
import org.gradle.jvm.toolchain.JavaInstallationMetadata;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.jvm.toolchain.JavadocTool;

final class ConfiguredJavaToolchain implements BaselineJavaToolchain {
private final ObjectFactory objectFactory;
private final Provider<JavaInstallationMetadata> javaInstallationMetadata;

ConfiguredJavaToolchain(Provider<JavaInstallationMetadata> javaInstallationMetadata) {
ConfiguredJavaToolchain(ObjectFactory objectFactory, Provider<JavaInstallationMetadata> javaInstallationMetadata) {
this.objectFactory = objectFactory;
this.javaInstallationMetadata = javaInstallationMetadata;
}

Expand All @@ -36,7 +39,11 @@ public Provider<JavaCompiler> javaCompiler() {

@Override
public Provider<JavadocTool> javadocTool() {
return javaInstallationMetadata.map(BaselineJavadocTool::new);
return javaInstallationMetadata
.map(BaselineJavadocTool::new)
// Gradle casts to the internal type JavadocToolAdapter in the Javadoc class - so unfortunately we have
// to use that instead of just returning the interface.
.map(javadocTool -> BaselineJavadocToolAdapter.create(objectFactory, javadocTool));
}

@Override
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* (c) Copyright 2022 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.baseline.plugins.javaversions;

import org.gradle.api.file.Directory;
import org.gradle.api.provider.Provider;
import org.gradle.jvm.toolchain.JavaInstallationMetadata;
import org.gradle.jvm.toolchain.JavaLanguageVersion;

/**
* The purpose of this class is to provide the JavaLanguageVersion, which we know upfront, without having to resolve an
* entire JDK, which often involves resolution, which causes Gradle to resolve the JavaCompiler/JavaLauncher etc to
* check if toolchains are enabled. That can cause a dependency cycle, which causes a StackOverflowException.
* This class breaks that cycle by immediately providing the JavaLanguageVersion without possibly causing a resolution.
*/
final class JavaInstallationMetadataWrapper implements JavaInstallationMetadata {
private final JavaLanguageVersion javaLanguageVersion;
private final Provider<JavaInstallationMetadata> delegate;

JavaInstallationMetadataWrapper(
JavaLanguageVersion javaLanguageVersion, Provider<JavaInstallationMetadata> delegate) {
this.javaLanguageVersion = javaLanguageVersion;
this.delegate = delegate;
}

@Override
public JavaLanguageVersion getLanguageVersion() {
return javaLanguageVersion;
}

@Override
public String getJavaRuntimeVersion() {
return delegate.get().getJavaRuntimeVersion();
}

@Override
public String getJvmVersion() {
return delegate.get().getJvmVersion();
}

@Override
public String getVendor() {
return delegate.get().getVendor();
}

@Override
public Directory getInstallationPath() {
return delegate.get().getInstallationPath();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,21 @@ public JavaToolchains(Project project, BaselineJavaVersionsExtension baselineJav
}

public Provider<BaselineJavaToolchain> forVersion(Provider<JavaLanguageVersion> javaLanguageVersionProvider) {
return javaLanguageVersionProvider.flatMap(javaLanguageVersion -> {
Provider<JavaInstallationMetadata> configuredJdk =
baselineJavaVersionsExtension.getJdks().getting(javaLanguageVersion);
return javaLanguageVersionProvider.map(javaLanguageVersion -> {
Provider<JavaInstallationMetadata> configuredJdkMetadata = baselineJavaVersionsExtension
.getJdks()
.getting(javaLanguageVersion)
.orElse(project.provider(() -> project.getExtensions()
.getByType(JavaToolchainService.class)
.launcherFor(javaToolchainSpec ->
javaToolchainSpec.getLanguageVersion().set(javaLanguageVersion))
.get()
.getMetadata()));

return configuredJdk
.<BaselineJavaToolchain>map(_ignored -> new ConfiguredJavaToolchain(configuredJdk))
.orElse(project.provider(() -> new FallbackGradleJavaToolchain(
project.getExtensions().getByType(JavaToolchainService.class),
javaToolchainSpec ->
javaToolchainSpec.getLanguageVersion().set(javaLanguageVersion))));
return new ConfiguredJavaToolchain(
project.getObjects(),
project.provider(
() -> new JavaInstallationMetadataWrapper(javaLanguageVersion, configuredJdkMetadata)));
});
}
}

0 comments on commit 7346e88

Please sign in to comment.