Skip to content

Commit

Permalink
refactor: document and simplify the Spring Boot utils class
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <[email protected]>
  • Loading branch information
manusa committed Nov 18, 2023
1 parent a8983ae commit 32b9c78
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> getSpringBootDevToolsVersion(JavaProject mavenProject) {
return getSpringBootVersion(mavenProject);
}

/**
* Determine the spring-boot major version for the current project
*
Expand All @@ -95,6 +81,11 @@ public static Optional<String> 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) {
Expand All @@ -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<String, Object> getSpringBootPluginConfiguration(JavaProject javaProject) {
Plugin mavenPlugin = JKubeProjectUtil.getPlugin(javaProject, SPRING_BOOT_MAVEN_PLUGIN_ARTIFACT_ID);
if (mavenPlugin != null) {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> result = SpringBootUtil.getSpringBootDevToolsVersion(mavenProject);

//Then
assertThat(result).isPresent().contains("1.6.3");
}


@Test
void testGetSpringBootVersion() {
//Given
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<String, Object> configuration = getSpringBootPluginConfiguration(getContext().getBuildContext().getProject());
if(configuration != null && (!configuration.containsKey("excludeDevtools") || !configuration.get("excludeDevtools").equals("false"))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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.<String[]>argThat(command -> command.length == 6
Expand Down

0 comments on commit 32b9c78

Please sign in to comment.