diff --git a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/SpringBootUtil.java b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/SpringBootUtil.java index c98a407f6f..6c5dc789bd 100644 --- a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/SpringBootUtil.java +++ b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/SpringBootUtil.java @@ -66,25 +66,11 @@ public static Properties getSpringBootApplicationProperties(String springActiveP URL ymlResource = compileClassLoader.findResource("application.yml"); URL propertiesResource = compileClassLoader.findResource("application.properties"); - Properties props = getPropertiesFromApplicationYamlResource(springActiveProfile, ymlResource); + Properties props = YamlUtil.getPropertiesFromYamlResource(springActiveProfile, ymlResource); props.putAll(getPropertiesFromResource(propertiesResource)); return props; } - public static Properties getPropertiesFromApplicationYamlResource(String springActiveProfile, URL ymlResource) { - return YamlUtil.getPropertiesFromYamlResource(springActiveProfile, ymlResource); - } - - /** - * Determine the spring-boot devtools version for the current project - * - * @param mavenProject Maven project - * @return devtools version or null - */ - public static Optional getSpringBootDevToolsVersion(JavaProject mavenProject) { - return getSpringBootVersion(mavenProject); - } - /** * Determine the spring-boot major version for the current project * @@ -95,6 +81,11 @@ public static Optional getSpringBootVersion(JavaProject javaProject) { return Optional.ofNullable(JKubeProjectUtil.getAnyDependencyVersionWithGroupId(javaProject, SPRING_BOOT_GROUP_ID)); } + /** + * Returns the currently active spring-boot profile or null if not found. + * @param project the JavaProject for which to search the active profile. + * @return the currently active spring-boot profile or null if not found. + */ public static String getSpringBootActiveProfile(JavaProject project) { if (project != null && project.getProperties() != null && project.getProperties().get("spring.profiles.active") != null) { @@ -103,6 +94,11 @@ public static String getSpringBootActiveProfile(JavaProject project) { return null; } + /** + * Returns a Map containing the Spring Boot configuration for the applicable plugin (Maven or Gradle). + * @param javaProject the JavaProject for which to search the Spring Boot plugin configuration. + * @return a Map containing the Spring Boot configuration or an empty Map if no plugin is found. + */ public static Map getSpringBootPluginConfiguration(JavaProject javaProject) { Plugin mavenPlugin = JKubeProjectUtil.getPlugin(javaProject, SPRING_BOOT_MAVEN_PLUGIN_ARTIFACT_ID); if (mavenPlugin != null) { @@ -139,6 +135,12 @@ public static Plugin getNativePlugin(JavaProject project) { return JKubeProjectUtil.getPlugin(project, "org.graalvm.buildtools.native", "org.graalvm.buildtools.native.gradle.plugin"); } + /** + * Returns the native executable artifact produced file by the Spring Boot build or null if not found. + * @param project the JavaProject for which to search the native executable artifact + * @return the native executable artifact produced file by the Spring Boot build or null if not found + * @throws IllegalStateException if more than one native executable artifact is found + */ public static File findNativeArtifactFile(JavaProject project) { for (String location : new String[] {"", "native/nativeCompile/"}) { File nativeArtifactDir = new File(project.getBuildDirectory(), location); diff --git a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/ProjectClassLoadersTest.java b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/ProjectClassLoadersTest.java index 089441ff9b..73509be111 100644 --- a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/ProjectClassLoadersTest.java +++ b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/ProjectClassLoadersTest.java @@ -13,14 +13,13 @@ */ package org.eclipse.jkube.kit.common.util; -import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import java.io.File; import java.net.URLClassLoader; -import java.util.Arrays; +import java.util.Collections; import static org.assertj.core.api.Assertions.assertThat; @@ -30,61 +29,58 @@ class ProjectClassLoadersTest { @BeforeEach void setUp(@TempDir File temporaryFolder) throws Exception { - File applicationProp = new File(getClass().getResource("/util/spring-boot-application.properties").getPath()); - File targetFolder = new File(temporaryFolder, "target"); - File classesInTarget = new File(targetFolder, "classes"); - File applicationPropertiesInsideTarget = new File(classesInTarget, "application.properties"); - FileUtils.copyFile(applicationProp, applicationPropertiesInsideTarget); - compileClassLoader = ClassUtil.createClassLoader(Arrays.asList(classesInTarget.getAbsolutePath(), applicationProp.getAbsolutePath()), classesInTarget.getAbsolutePath()); + File classesInTarget = new File(new File(temporaryFolder, "target"), "classes"); + FileUtil.createDirectory(classesInTarget); + compileClassLoader = ClassUtil.createClassLoader(Collections.singletonList(classesInTarget.getAbsolutePath())); } @Test - void testIsClassInCompileClasspathWhenTrue() { + void isClassInCompileClasspathWhenTrue() { //Given boolean all = true; ProjectClassLoaders obj = new ProjectClassLoaders(compileClassLoader); //When - boolean result = obj.isClassInCompileClasspath(all); + boolean result = obj.isClassInCompileClasspath(all); //Then assertThat(result).isTrue(); } @Test - void testIsClassInCompileClasspathWhenFalse() { + void isClassInCompileClasspathWhenFalse() { //Given boolean all = false; ProjectClassLoaders obj = new ProjectClassLoaders(compileClassLoader); //When - boolean result = obj.isClassInCompileClasspath(all,"ProjectClassLoadersTest","UserConfigurationCompare"); + boolean result = obj.isClassInCompileClasspath(all,"ProjectClassLoadersTest", "UserConfigurationCompare"); //Then assertThat(result).isFalse(); } @Test - void testIsClassInCompileClasspathWhenHasAllClassesTrue() { + void isClassInCompileClasspathWhenHasAllClassesTrue() { //Given boolean all = true; ProjectClassLoaders obj = new ProjectClassLoaders(compileClassLoader); //When - boolean result = obj.isClassInCompileClasspath(all,"ProjectClassLoadersTest","UserConfigurationCompare"); + boolean result = obj.isClassInCompileClasspath(all,"ProjectClassLoadersTest", "UserConfigurationCompare"); //Then assertThat(result).isFalse(); } @Test - void testIsClassInCompileClasspathWhenHasAllClassesFalse() { + void isClassInCompileClasspathWhenHasAllClassesFalse() { //Given boolean all = false; ProjectClassLoaders obj = new ProjectClassLoaders(compileClassLoader); //When - boolean result = obj.isClassInCompileClasspath(all); + boolean result = obj.isClassInCompileClasspath(all); //Then assertThat(result).isFalse(); diff --git a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/SpringBootUtilTest.java b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/SpringBootUtilTest.java index e19749f0a4..59cee10f1e 100644 --- a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/SpringBootUtilTest.java +++ b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/SpringBootUtilTest.java @@ -54,42 +54,18 @@ public void setUp() { } @Test - void testGetSpringBootApplicationProperties(@TempDir File temporaryFolder) throws IOException { + void getSpringBootApplicationPropertiesLoadsStandardProperties(@TempDir File tempDir) throws IOException { //Given - File applicationProp = new File(Objects.requireNonNull(getClass().getResource("/util/spring-boot-application.properties")).getPath()); - String springActiveProfile = null; - File targetFolder = new File(temporaryFolder, "target"); - File classesInTarget = new File(targetFolder, "classes"); - boolean isTargetClassesCreated = classesInTarget.mkdirs(); - File applicationPropertiesInsideTarget = new File(classesInTarget, "application.properties"); - FileUtils.copyFile(applicationProp, applicationPropertiesInsideTarget); - URLClassLoader urlClassLoader = ClassUtil.createClassLoader(Arrays.asList(classesInTarget.getAbsolutePath(), applicationProp.getAbsolutePath()), classesInTarget.getAbsolutePath()); - + URLClassLoader cl = createClassLoader(tempDir, "/util/springboot/spring-boot-application.properties"); //When - Properties result = SpringBootUtil.getSpringBootApplicationProperties(springActiveProfile ,urlClassLoader); - + Properties result = SpringBootUtil.getSpringBootApplicationProperties(cl); //Then - assertThat(isTargetClassesCreated).isTrue(); assertThat(result).containsOnly( entry("spring.application.name", "demoservice"), entry("server.port", "9090") ); } - @Test - void testGetSpringBootDevToolsVersion() { - //Given - Dependency p = Dependency.builder().groupId("org.springframework.boot").version("1.6.3").build(); - when(mavenProject.getDependencies()).thenReturn(Collections.singletonList(p)); - - //when - Optional result = SpringBootUtil.getSpringBootDevToolsVersion(mavenProject); - - //Then - assertThat(result).isPresent().contains("1.6.3"); - } - - @Test void testGetSpringBootVersion() { //Given @@ -168,18 +144,21 @@ void testNonExistentYamlToPropertiesParsing() { } @Test - void testMultipleProfilesParsing() { - Properties props = SpringBootUtil.getPropertiesFromApplicationYamlResource(null, getClass().getResource("/util/springboot/test-application-with-multiple-profiles.yml")); - + void getSpringBootApplicationProperties_withMultipleProfilesAndDefault(@TempDir File tempDir) throws IOException { + URLClassLoader cl = createClassLoader(tempDir, "/util/springboot/test-application-with-multiple-profiles.yml"); + Properties props = SpringBootUtil.getSpringBootApplicationProperties(cl); assertThat(props).isNotEmpty() .contains( entry("spring.application.name", "spring-boot-k8-recipes"), entry("management.endpoints.enabled-by-default", "false"), entry("management.endpoint.health.enabled", "true")) .doesNotContainEntry("cloud.kubernetes.reload.enabled", null); + } - props = SpringBootUtil.getPropertiesFromApplicationYamlResource("kubernetes", getClass().getResource("/util/springboot/test-application-with-multiple-profiles.yml")); - + @Test + void getSpringBootApplicationProperties_withMultipleProfilesAndSpecificProfile(@TempDir File tempDir) throws IOException { + URLClassLoader cl = createClassLoader(tempDir, "/util/springboot/test-application-with-multiple-profiles.yml"); + Properties props = SpringBootUtil.getSpringBootApplicationProperties("kubernetes", cl); assertThat(props) .containsEntry("cloud.kubernetes.reload.enabled", "true") .doesNotContain( @@ -440,4 +419,14 @@ void findNativeArtifactFile_whenNativeExecutableInStandardGradleNativeDirectory_ // Then assertThat(nativeArtifactFound).hasName("sample"); } + + private URLClassLoader createClassLoader(File temporaryFolder, String resource) throws IOException { + File applicationProp = new File(Objects.requireNonNull(SpringBootUtilTest.class.getResource(resource)).getPath()); + File classesInTarget = new File(new File(temporaryFolder, "target"), "classes"); + FileUtil.createDirectory(classesInTarget); + File applicationPropertiesInsideTarget = new File(classesInTarget, "application." + + applicationProp.getName().substring(applicationProp.getName().lastIndexOf(".") + 1)); + FileUtils.copyFile(applicationProp, applicationPropertiesInsideTarget); + return ClassUtil.createClassLoader(Arrays.asList(classesInTarget.getAbsolutePath(), applicationProp.getAbsolutePath())); + } } diff --git a/jkube-kit/common/src/test/resources/util/spring-boot-application.properties b/jkube-kit/common/src/test/resources/util/springboot/spring-boot-application.properties similarity index 100% rename from jkube-kit/common/src/test/resources/util/spring-boot-application.properties rename to jkube-kit/common/src/test/resources/util/springboot/spring-boot-application.properties diff --git a/jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/SpringBootDevtoolsUtils.java b/jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/SpringBootDevtoolsUtils.java index dd541f2175..5a4e1b084c 100644 --- a/jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/SpringBootDevtoolsUtils.java +++ b/jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/SpringBootDevtoolsUtils.java @@ -180,8 +180,8 @@ private static File getFatJarFile(FatJarDetector.Result fatJarDetectResult) { return fatJarDetectResult.getArchiveFile(); } - private static File getSpringBootDevToolsJar(JavaProject project) { - String version = SpringBootUtil.getSpringBootDevToolsVersion(project) + public static File getSpringBootDevToolsJar(JavaProject project) { + String version = SpringBootUtil.getSpringBootVersion(project) .orElseThrow(() -> new IllegalStateException("Unable to find the spring-boot version")); final File devToolsJar = JKubeProjectUtil.resolveArtifact(project, SPRING_BOOT_GROUP_ID, SPRING_BOOT_DEVTOOLS_ARTIFACT_ID, version, "jar"); if (!devToolsJar.exists()) { diff --git a/jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/watcher/SpringBootWatcher.java b/jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/watcher/SpringBootWatcher.java index 8777409c3a..44e961223b 100644 --- a/jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/watcher/SpringBootWatcher.java +++ b/jkube-kit/jkube-kit-spring-boot/src/main/java/org/eclipse/jkube/springboot/watcher/SpringBootWatcher.java @@ -30,7 +30,6 @@ import java.util.stream.Stream; import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import org.eclipse.jkube.kit.common.JavaProject; import org.eclipse.jkube.kit.common.KitLogger; import org.eclipse.jkube.kit.common.PrefixedLogger; import org.eclipse.jkube.kit.common.util.ClassUtil; @@ -54,6 +53,7 @@ import static org.eclipse.jkube.kit.common.util.EnvUtil.isWindows; import static org.eclipse.jkube.kit.common.util.SpringBootUtil.DEV_TOOLS_REMOTE_SECRET; import static org.eclipse.jkube.kit.common.util.SpringBootUtil.getSpringBootPluginConfiguration; +import static org.eclipse.jkube.springboot.SpringBootDevtoolsUtils.getSpringBootDevToolsJar; public class SpringBootWatcher extends BaseWatcher { @@ -138,13 +138,8 @@ private void runRemoteSpringApplication(String url) { ) { classLoaders.add(projectClassLoader); - final String devToolsPath; - try { - devToolsPath = getSpringBootDevToolsJar(getContext().getBuildContext().getProject()) + final String devToolsPath = getSpringBootDevToolsJar(getContext().getBuildContext().getProject()) .getCanonicalPath(); - } catch (Exception e) { - throw new IllegalStateException("Failed to include devtools in the classpath: " + e, e); - } final String classPath = classLoaders.stream().flatMap(cl -> Stream.of(cl.getURLs())).map(u -> { try { URI uri = u.toURI(); @@ -228,11 +223,6 @@ public void run() { return printer; } - private File getSpringBootDevToolsJar(JavaProject project) { - String version = SpringBootUtil.getSpringBootDevToolsVersion(project).orElseThrow(() -> new IllegalStateException("Unable to find the spring-boot version")); - return JKubeProjectUtil.resolveArtifact(getContext().getBuildContext().getProject(), SpringBootUtil.SPRING_BOOT_GROUP_ID, SpringBootUtil.SPRING_BOOT_DEVTOOLS_ARTIFACT_ID, version, "jar"); - } - private String validateSpringBootDevtoolsSettings() { final Map configuration = getSpringBootPluginConfiguration(getContext().getBuildContext().getProject()); if(configuration != null && (!configuration.containsKey("excludeDevtools") || !configuration.get("excludeDevtools").equals("false"))) { diff --git a/jkube-kit/jkube-kit-spring-boot/src/test/java/org/eclipse/jkube/springboot/watcher/SpringBootWatcherIntegrationTest.java b/jkube-kit/jkube-kit-spring-boot/src/test/java/org/eclipse/jkube/springboot/watcher/SpringBootWatcherIntegrationTest.java index d0ca04bf21..ae5f377491 100644 --- a/jkube-kit/jkube-kit-spring-boot/src/test/java/org/eclipse/jkube/springboot/watcher/SpringBootWatcherIntegrationTest.java +++ b/jkube-kit/jkube-kit-spring-boot/src/test/java/org/eclipse/jkube/springboot/watcher/SpringBootWatcherIntegrationTest.java @@ -53,13 +53,12 @@ class SpringBootWatcherIntegrationTest { Path project; KubernetesClient kubernetesClient; private WatcherContext watcherContext; - private File applicationProperties; + private Path target; private Deployment deployment; @BeforeEach void setUp() throws IOException { - final Path target = Files.createDirectory(project.resolve("target")); - applicationProperties = target.resolve("application.properties").toFile(); + target = Files.createDirectory(project.resolve("target")); deployment = new DeploymentBuilder() .withNewSpec() .withNewSelector().addToMatchLabels("app", "spring-boot-test").endSelector() @@ -121,7 +120,8 @@ void withNoRemoteSecretThrowsException() { @Test void withAllRequirementsShouldStartWatcherProcess() throws Exception { final Runtime runtime = mock(Runtime.class, RETURNS_DEEP_STUBS); - FileUtils.write(applicationProperties, "spring.devtools.remote.secret=this-is-a-test", StandardCharsets.UTF_8); + target.resolve("spring-boot-devtools.jar").toFile().createNewFile(); + FileUtils.write(target.resolve("application.properties").toFile(), "spring.devtools.remote.secret=this-is-a-test", StandardCharsets.UTF_8); new SpringBootWatcher(runtime, watcherContext) .watch(Collections.emptyList(), null, Collections.singletonList(deployment), PlatformMode.kubernetes); verify(runtime).exec(ArgumentMatchers.argThat(command -> command.length == 6