diff --git a/src/main/java/org/springframework/data/release/build/BuildExecutor.java b/src/main/java/org/springframework/data/release/build/BuildExecutor.java index 5f78df8..eabb5b5 100644 --- a/src/main/java/org/springframework/data/release/build/BuildExecutor.java +++ b/src/main/java/org/springframework/data/release/build/BuildExecutor.java @@ -16,11 +16,16 @@ package org.springframework.data.release.build; import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import java.io.File; +import java.io.FileInputStream; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -35,11 +40,12 @@ import javax.annotation.PreDestroy; import org.apache.commons.io.IOUtils; -import org.springframework.data.release.model.JavaVersionAware; +import org.springframework.data.release.dependency.InfrastructureOperations; +import org.springframework.data.release.io.Workspace; +import org.springframework.data.release.model.JavaVersion; import org.springframework.data.release.model.Project; import org.springframework.data.release.model.ProjectAware; import org.springframework.data.release.utils.ListWrapperCollector; -import org.springframework.data.release.utils.Logger; import org.springframework.data.util.Streamable; import org.springframework.plugin.core.PluginRegistry; import org.springframework.stereotype.Component; @@ -51,19 +57,13 @@ import org.springframework.util.Assert; * @author Mark Paluch */ @Component +@RequiredArgsConstructor class BuildExecutor { private final @NonNull PluginRegistry buildSystems; private final MavenProperties mavenProperties; private final ExecutorService executor; - - public BuildExecutor(PluginRegistry buildSystems, Logger logger, - MavenProperties mavenProperties, ExecutorService buildExecutor) { - - this.buildSystems = buildSystems; - this.mavenProperties = mavenProperties; - this.executor = buildExecutor; - } + private final Workspace workspace; @PreDestroy public void shutdown() { @@ -162,20 +162,14 @@ class BuildExecutor { Supplier exception = () -> new IllegalStateException( String.format("No build system plugin found for project %s!", module.getProject())); - BuildSystem buildSystem = buildSystems.getPluginFor(module.getProject(), exception); - BuildSystem buildSystemToUse; - - if (module instanceof JavaVersionAware) { - buildSystemToUse = buildSystem.withJavaVersion(((JavaVersionAware) module).getJavaVersion()); - } else { - buildSystemToUse = buildSystem; - } + BuildSystem buildSystem = buildSystems.getPluginFor(module.getProject(), exception) + .withJavaVersion(detectJavaVersion(module.getProject())); Runnable runnable = () -> { try { - result.complete(function.apply(buildSystemToUse, module)); + result.complete(function.apply(buildSystem, module)); } catch (Exception e) { result.completeExceptionally(e); } @@ -186,6 +180,24 @@ class BuildExecutor { return result; } + @SneakyThrows + public JavaVersion detectJavaVersion(Project project) { + + File ciProperties = workspace.getFile(InfrastructureOperations.CI_PROPERTIES, project); + + if (!ciProperties.exists()) { + throw new IllegalStateException(String.format("Cannot find %s for project %s", ciProperties, project)); + } + + Properties properties = new Properties(); + + try (FileInputStream fis = new FileInputStream(ciProperties)) { + properties.load(fis); + } + + return JavaVersion.fromDockerTag(properties.getProperty("java.main.tag")); + } + /** * Returns a new collector to toSummaryCollector {@link ExecutionResult} as {@link Summary} using the {@link Stream} * API. diff --git a/src/main/java/org/springframework/data/release/build/BuildOperations.java b/src/main/java/org/springframework/data/release/build/BuildOperations.java index c78a4bf..c09ef30 100644 --- a/src/main/java/org/springframework/data/release/build/BuildOperations.java +++ b/src/main/java/org/springframework/data/release/build/BuildOperations.java @@ -222,7 +222,7 @@ public class BuildOperations { BuildSystem buildSystem = buildSystems.getPluginFor(module.getProject(), exception); - return function.apply(buildSystem.withJavaVersion(module.getJavaVersion()), module); + return function.apply(buildSystem.withJavaVersion(executor.detectJavaVersion(module.getProject())), module); } } diff --git a/src/main/java/org/springframework/data/release/io/JavaRuntimes.java b/src/main/java/org/springframework/data/release/io/JavaRuntimes.java index c92dcea..5841287 100644 --- a/src/main/java/org/springframework/data/release/io/JavaRuntimes.java +++ b/src/main/java/org/springframework/data/release/io/JavaRuntimes.java @@ -91,6 +91,10 @@ public class JavaRuntimes { .orElseThrow(() -> new NoSuchElementException(String.format("%s%nAvailable JDK: %s", message.get(), jdks))); } + public static List getJdks() { + return JDKS.get(); + } + /** * JDK detection strategy. */ @@ -125,7 +129,10 @@ public class JavaRuntimes { } public static Selector from(JavaVersion javaVersion) { - return builder().and(it -> javaVersion.getVersionDetector().test(it.getVersion())) + + return builder() + .and(it -> javaVersion.getVersionDetector().test(it.getVersion()) + && javaVersion.getImplementor().test(it.getImplementor())) .message("Cannot find Java " + javaVersion.getName()); } @@ -184,10 +191,29 @@ public class JavaRuntimes { version = Version.parse(candidateVersion); } - return new JdkInstallation(version, it.getName(), it); + String implementor = normalizeImplementor(parseImplementor(it)); + + return new JdkInstallation(version, toDisplayName(implementor, candidateVersion), implementor, it); }).collect(Collectors.toList()); } + + @SneakyThrows + private String parseImplementor(File candidateHome) { + + List release = FileUtils.readLines(new File(candidateHome, "release")); + + for (String line : release) { + + if (line.startsWith("IMPLEMENTOR=")) { + String substring = line.substring(line.indexOf("=\"")); + substring = substring.substring(2, substring.length() - 1); + return substring; + } + } + + return "?"; + } } /** @@ -208,7 +234,8 @@ public class JavaRuntimes { public List detect() { return Collections - .singletonList(new JdkInstallation(JavaVersion.parse(javaVersion), javaVendor + " " + javaVersion, javaHome)); + .singletonList(new JdkInstallation(JavaVersion.parse(javaVersion), toDisplayName(javaVendor, javaVersion), + normalizeImplementor(javaVendor), javaHome)); } } @@ -252,6 +279,7 @@ public class JavaRuntimes { String jvmHomePath = dict.get("JVMHomePath").toJavaObject(String.class); String name = dict.get("JVMName").toJavaObject(String.class); String version = dict.get("JVMVersion").toJavaObject(String.class); + String vendor = dict.get("JVMVendor").toJavaObject(String.class); Matcher matcher = VERSION.matcher(version); if (!matcher.find()) { @@ -259,7 +287,9 @@ public class JavaRuntimes { + ". This should not happen in an ideal world, check the VERSION regex."); } - return new JdkInstallation(JavaVersion.parse(matcher.group(1)), name, new File(jvmHomePath)); + String implementor = normalizeImplementor(vendor); + return new JdkInstallation(JavaVersion.parse(matcher.group(1)), toDisplayName(implementor, version), + implementor, new File(jvmHomePath)); }).collect(Collectors.toList()); } @@ -270,6 +300,7 @@ public class JavaRuntimes { Version version; String name; + String implementor; File home; @Override @@ -277,4 +308,26 @@ public class JavaRuntimes { return this.version.compareTo(o.version); } } + + static String normalizeImplementor(String implementor) { + + if (implementor.equals("Eclipse Adoptium")) { + return "Eclipse Temurin"; + } + + if (implementor.equals("Eclipse Foundation")) { + return "Eclipse Temurin"; + } + + return implementor; + } + + static String toDisplayName(String implementor, String version) { + + if (implementor.startsWith("Oracle")) { + implementor = "Oracle Java"; + } + + return implementor + " " + version; + } } diff --git a/src/main/java/org/springframework/data/release/model/JavaVersion.java b/src/main/java/org/springframework/data/release/model/JavaVersion.java index 54cecfc..224386b 100644 --- a/src/main/java/org/springframework/data/release/model/JavaVersion.java +++ b/src/main/java/org/springframework/data/release/model/JavaVersion.java @@ -18,6 +18,8 @@ package org.springframework.data.release.model; import lombok.Value; import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Value object representing a Java version. @@ -27,20 +29,49 @@ import java.util.function.Predicate; @Value(staticConstructor = "of") public class JavaVersion { + private static final Pattern DOCKER_TAG_PATTERN = Pattern.compile("((:?\\d+(:?u\\d+)?(:?\\.\\d+)*)).*"); + public static final JavaVersion JAVA_8 = of("1.8.0_312"); - public static final JavaVersion JAVA_17 = of("Java 17", version -> version.getMajor() == 17); + public static final JavaVersion JAVA_17 = of("Java 17", version -> version.getMajor() == 17, it -> true); String name; Predicate versionDetector; + Predicate implementor; public static JavaVersion of(String version) { Version expectedVersion = parse(version); - return of("Java " + version, candidate -> candidate.is(expectedVersion)); + return of("Java " + version, candidate -> candidate.is(expectedVersion), it -> true); } public static Version parse(String version) { return Version.parse(version.replace('_', '.')); } + /** + * Parse a docker tag into a Java version using {@code eclipse-temurin} conventions. + * + * @param tagName + * @return + */ + public static JavaVersion fromDockerTag(String tagName) { + + Pattern versionExtractor = Pattern.compile("(:?\\d+(:?u\\d+)?(:?\\.\\d+)*).*"); + Matcher matcher = versionExtractor.matcher(tagName); + if (!matcher.find()) { + throw new IllegalStateException(String.format("Cannot parse Java version '%s'", tagName)); + } + + String plainVersion = matcher.group(1); + + if (plainVersion.startsWith("8u")) { + return of("1.8.0_" + plainVersion.substring(2)); + } + + return of(plainVersion).withImplementor("Temurin"); + } + + public JavaVersion withImplementor(String implementor) { + return of(String.format("%s (%s)", name, implementor), versionDetector, it -> it.contains(implementor)); + } } diff --git a/src/main/java/org/springframework/data/release/model/JavaVersionAware.java b/src/main/java/org/springframework/data/release/model/JavaVersionAware.java deleted file mode 100644 index b48d9a4..0000000 --- a/src/main/java/org/springframework/data/release/model/JavaVersionAware.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * 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 - * - * https://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 org.springframework.data.release.model; - -/** - * @author Mark Paluch - */ -public interface JavaVersionAware { - - JavaVersion getJavaVersion(); -} diff --git a/src/main/java/org/springframework/data/release/model/Module.java b/src/main/java/org/springframework/data/release/model/Module.java index 15b811b..a382a47 100644 --- a/src/main/java/org/springframework/data/release/model/Module.java +++ b/src/main/java/org/springframework/data/release/model/Module.java @@ -18,7 +18,6 @@ package org.springframework.data.release.model; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Value; -import lombok.With; import org.springframework.util.Assert; @@ -28,31 +27,25 @@ import org.springframework.util.Assert; */ @Value @AllArgsConstructor(access = AccessLevel.PRIVATE) -public class Module implements VersionAware, ProjectAware, JavaVersionAware, Comparable { +public class Module implements VersionAware, ProjectAware, Comparable { Project project; Version version; Iteration customFirstIteration; - @With JavaVersion javaVersion; public Module(Project project, String version) { - this(project, version, null, JavaVersion.JAVA_8); + this(project, version, null); } + Module(Project project, String version, String customFirstIteration) { - this(project, version, customFirstIteration, JavaVersion.JAVA_8); - } - - Module(Project project, String version, String customFirstIteration, JavaVersion javaVersion) { Assert.notNull(project, "Project must not be null!"); - Assert.notNull(javaVersion, "JavaVersion must not be null!"); this.project = project; this.version = Version.parse(version); this.customFirstIteration = customFirstIteration == null ? null : new Iteration(customFirstIteration, Iteration.RC1); - this.javaVersion = javaVersion; } public boolean hasName(String name) { @@ -97,8 +90,4 @@ public class Module implements VersionAware, ProjectAware, JavaVersionAware, Com return String.format("Spring Data %s %s - %s", project.getName(), version, project.getKey()); } - public Module withJavaVersion(JavaVersion javaVersion) { - return this.javaVersion == javaVersion ? this - : new Module(this.project, this.version, this.customFirstIteration, javaVersion); - } } diff --git a/src/main/java/org/springframework/data/release/model/ModuleIteration.java b/src/main/java/org/springframework/data/release/model/ModuleIteration.java index d863dd4..5686aa8 100644 --- a/src/main/java/org/springframework/data/release/model/ModuleIteration.java +++ b/src/main/java/org/springframework/data/release/model/ModuleIteration.java @@ -25,7 +25,7 @@ import lombok.RequiredArgsConstructor; */ @RequiredArgsConstructor @EqualsAndHashCode -public class ModuleIteration implements IterationVersion, ProjectAware, JavaVersionAware { +public class ModuleIteration implements IterationVersion, ProjectAware { private final @Getter Module module; private final @Getter TrainIteration trainIteration; @@ -46,11 +46,6 @@ public class ModuleIteration implements IterationVersion, ProjectAware, JavaVers return module.getProject(); } - @Override - public JavaVersion getJavaVersion() { - return trainIteration.getTrain().getJavaVersion(); - } - /* * (non-Javadoc) * @see org.springframework.data.release.model.IterationVersion#getVersion() diff --git a/src/main/java/org/springframework/data/release/model/ReleaseTrains.java b/src/main/java/org/springframework/data/release/model/ReleaseTrains.java index 0083a52..4d996fb 100644 --- a/src/main/java/org/springframework/data/release/model/ReleaseTrains.java +++ b/src/main/java/org/springframework/data/release/model/ReleaseTrains.java @@ -77,7 +77,6 @@ public class ReleaseTrains { TURING = PASCAL.next("Turing", Transition.MAJOR, // new Module(R2DBC, "3.0")) // .withCalver("2022.0") // - .withJavaVersion(JavaVersion.JAVA_17) .filterModules(module -> !module.getProject().equals(ENVERS)) .withAlwaysUseBranch(true) .withIterations(new Train.Iterations(M1, M2, M3, M4, M5, RC1, RC2, GA, SR1, SR2, SR3, SR4, SR5)); diff --git a/src/main/java/org/springframework/data/release/model/Train.java b/src/main/java/org/springframework/data/release/model/Train.java index 4aac8b6..1903893 100644 --- a/src/main/java/org/springframework/data/release/model/Train.java +++ b/src/main/java/org/springframework/data/release/model/Train.java @@ -185,15 +185,6 @@ public class Train implements Streamable { return new Train(name, Modules.of(modules), calver, iterations, alwaysUseBranch, javaVersion); } - public Train withJavaVersion(JavaVersion javaVersion) { - - Set modules = this.modules.stream().map(it -> { - return it.withJavaVersion(javaVersion); - }).collect(Collectors.toSet()); - - return new Train(name, Modules.of(modules), calver, iterations, alwaysUseBranch, javaVersion); - } - /** * Check whether this train comes before {@link Train other}. * diff --git a/src/test/java/org/springframework/data/release/io/JavaRuntimesUnitTests.java b/src/test/java/org/springframework/data/release/io/JavaRuntimesUnitTests.java index 0b2eaad..7a6e8ad 100644 --- a/src/test/java/org/springframework/data/release/io/JavaRuntimesUnitTests.java +++ b/src/test/java/org/springframework/data/release/io/JavaRuntimesUnitTests.java @@ -37,4 +37,5 @@ class JavaRuntimesUnitTests { assertThat(jdk).isNotNull(); assertThat(System.getProperty("java.home")).contains(jdk.getHome().getPath()); } + } diff --git a/src/test/java/org/springframework/data/release/model/JavaVersionUnitTests.java b/src/test/java/org/springframework/data/release/model/JavaVersionUnitTests.java new file mode 100644 index 0000000..2b895bb --- /dev/null +++ b/src/test/java/org/springframework/data/release/model/JavaVersionUnitTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2022 the original author or authors. + * + * 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 + * + * https://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 org.springframework.data.release.model; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link JavaVersion}. + * + * @author Mark Paluch + */ +class JavaVersionUnitTests { + + @Test + void shouldParseDockerTag() { + + assertThat(JavaVersion.fromDockerTag("8u312-b07-jdk").getName()).isEqualTo("Java 1.8.0_312"); + assertThat(JavaVersion.fromDockerTag("8u322-b06-jre-focal").getName()).isEqualTo("Java 1.8.0_322"); + assertThat(JavaVersion.fromDockerTag("11.0.13_8-jdk").getName()).isEqualTo("Java 11.0.13"); + assertThat(JavaVersion.fromDockerTag("17.0.1_12").getName()).isEqualTo("Java 17.0.1"); + } + + @Test + void shouldConsiderImplementor() { + + assertThat(JavaVersion.fromDockerTag("8u312-b07-jdk").withImplementor("Temurin").getName()) + .isEqualTo("Java 1.8.0_312 (Temurin)"); + } +}