Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defend against a missing WindowsRegistry service #29068

Merged
merged 2 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -405,29 +405,29 @@ protected WindowsRegistry createWindowsRegistry(OperatingSystem operatingSystem)
if (useNativeIntegrations && operatingSystem.isWindows()) {
return net.rubygrapefruit.platform.Native.get(WindowsRegistry.class);
}
return notAvailable(WindowsRegistry.class);
return notAvailable(WindowsRegistry.class, operatingSystem);
}

public SystemInfo createSystemInfo() {
public SystemInfo createSystemInfo(OperatingSystem operatingSystem) {
if (useNativeIntegrations) {
try {
return net.rubygrapefruit.platform.Native.get(SystemInfo.class);
} catch (NativeIntegrationUnavailableException e) {
LOGGER.debug("Native-platform system info is not available. Continuing with fallback.");
}
}
return notAvailable(SystemInfo.class);
return notAvailable(SystemInfo.class, operatingSystem);
}

protected Memory createMemory() {
protected Memory createMemory(OperatingSystem operatingSystem) {
if (useNativeIntegrations) {
try {
return net.rubygrapefruit.platform.Native.get(Memory.class);
} catch (NativeIntegrationUnavailableException e) {
LOGGER.debug("Native-platform memory integration is not available. Continuing with fallback.");
}
}
return notAvailable(Memory.class);
return notAvailable(Memory.class, operatingSystem);
}

protected ProcessLauncher createProcessLauncher() {
Expand All @@ -441,15 +441,15 @@ protected ProcessLauncher createProcessLauncher() {
return new DefaultProcessLauncher();
}

protected PosixFiles createPosixFiles() {
protected PosixFiles createPosixFiles(OperatingSystem operatingSystem) {
if (useNativeIntegrations) {
try {
return net.rubygrapefruit.platform.Native.get(PosixFiles.class);
} catch (NativeIntegrationUnavailableException e) {
LOGGER.debug("Native-platform posix files integration is not available. Continuing with fallback.");
}
}
return notAvailable(UnavailablePosixFiles.class);
return notAvailable(UnavailablePosixFiles.class, operatingSystem);
}

protected HostnameLookup createHostnameLookup() {
Expand Down Expand Up @@ -505,19 +505,19 @@ public boolean useFileSystemWatching() {
};
}

protected FileSystems createFileSystems() {
protected FileSystems createFileSystems(OperatingSystem operatingSystem) {
if (useNativeIntegrations) {
try {
return net.rubygrapefruit.platform.Native.get(FileSystems.class);
} catch (NativeIntegrationUnavailableException e) {
LOGGER.debug("Native-platform file systems information is not available. Continuing with fallback.");
}
}
return notAvailable(FileSystems.class);
return notAvailable(FileSystems.class, operatingSystem);
}

private <T> T notAvailable(Class<T> type) {
return Cast.uncheckedNonnullCast(Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, new BrokenService(type.getSimpleName())));
private <T> T notAvailable(Class<T> type, OperatingSystem operatingSystem) {
return Cast.uncheckedNonnullCast(Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, new BrokenService(type.getSimpleName(), useNativeIntegrations, operatingSystem)));
}

private static String format(Throwable throwable) {
Expand All @@ -533,14 +533,18 @@ private static String format(Throwable throwable) {

private static class BrokenService implements InvocationHandler {
private final String type;
private final boolean useNativeIntegrations;
private final OperatingSystem operatingSystem;

private BrokenService(String type) {
private BrokenService(String type, boolean useNativeIntegrations, OperatingSystem operatingSystem) {
this.type = type;
this.useNativeIntegrations = useNativeIntegrations;
this.operatingSystem = operatingSystem;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
throw new org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException(String.format("%s is not supported on this operating system.", type));
throw new org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException(String.format("Service '%s' is not available (os=%s, enabled=%s).", type, operatingSystem, useNativeIntegrations));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import net.rubygrapefruit.platform.WindowsRegistry
import org.gradle.api.internal.file.temp.TemporaryFileProvider
import org.gradle.internal.file.Chmod
import org.gradle.internal.file.Stat
import org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException
import org.gradle.internal.nativeintegration.ProcessEnvironment
import org.gradle.internal.nativeintegration.console.ConsoleDetector
import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer
import org.gradle.internal.nativeintegration.filesystem.FileSystem
import org.gradle.internal.os.OperatingSystem
import org.gradle.test.precondition.Requires
import org.gradle.test.preconditions.UnitTestPreconditions
import org.gradle.util.UsesNativeServices
import spock.lang.Specification

Expand Down Expand Up @@ -83,4 +86,14 @@ class NativeServicesTest extends Specification {
expect:
services.get(TemporaryFileProvider) != null
}

@Requires(UnitTestPreconditions.NotWindows)
def "try using a WindowsRegistry on a non-Windows OS"() {
def service = services.get(WindowsRegistry)
when:
service.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, "SOFTWARE\\AdoptOpenJDK\\JDK")
then:
def e = thrown(NativeIntegrationUnavailableException)
e.message == "Service 'WindowsRegistry' is not available (os=${OperatingSystem.current()}, enabled=true)."
}
}
1 change: 1 addition & 0 deletions platforms/jvm/jvm-services/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies {
api(libs.nativePlatform)

implementation(project(":functional"))
implementation(project(":native"))

implementation(libs.guava)
implementation(libs.asm)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.common.collect.Lists;
import net.rubygrapefruit.platform.MissingRegistryEntryException;
import net.rubygrapefruit.platform.WindowsRegistry;
import org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException;
import org.gradle.internal.os.OperatingSystem;

import java.io.File;
Expand Down Expand Up @@ -67,9 +68,9 @@ private Set<InstallationLocation> findInstallationsInRegistry() {

private List<String> find(String sdkSubkey, String path, String value) {
try {
return getVersions(sdkSubkey).stream()
.map(version -> getValue(sdkSubkey, path, value, version)).collect(Collectors.toList());
} catch (MissingRegistryEntryException e) {
List<String> versions = getVersions(sdkSubkey);
return versions.stream().map(version -> getValue(sdkSubkey, path, value, version)).collect(Collectors.toList());
} catch (MissingRegistryEntryException | NativeIntegrationUnavailableException e) {
// Ignore
return Collections.emptyList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ package org.gradle.jvm.toolchain.internal

import net.rubygrapefruit.platform.MissingRegistryEntryException
import net.rubygrapefruit.platform.WindowsRegistry
import org.gradle.api.internal.provider.Providers
import org.gradle.api.provider.ProviderFactory
import org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException
import org.gradle.internal.os.OperatingSystem
import spock.lang.Specification

Expand Down Expand Up @@ -74,8 +73,20 @@ class WindowsInstallationSupplierTest extends Specification {

def "handles absent adoptopenjdk keys"() {
given:
registry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, "SOFTWARE\\AdoptOpenJDK\\JDK") >> { throw new MissingRegistryEntryException() }
def supplier = createSupplier(OperatingSystem.MAC_OS)
registry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, _) >> { throw new MissingRegistryEntryException() }
def supplier = createSupplier()

when:
def locations = supplier.get()

then:
locations.isEmpty()
}

def "gracefully handles unavailable native integration"() {
given:
registry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, _) >> { throw new NativeIntegrationUnavailableException() }
def supplier = createSupplier()

when:
def locations = supplier.get()
Expand Down Expand Up @@ -114,11 +125,4 @@ class WindowsInstallationSupplierTest extends Specification {
WindowsInstallationSupplier createSupplier(OperatingSystem os = OperatingSystem.WINDOWS) {
new WindowsInstallationSupplier(registry, os)
}

ProviderFactory createProviderFactory(String propertyValue) {
def providerFactory = Mock(ProviderFactory)
providerFactory.gradleProperty("org.gradle.java.installations.auto-detect") >> Providers.notDefined()
providerFactory
}

}