Skip to content

Commit

Permalink
fix: skip executable libs when searching Spring Boot native binary
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <[email protected]>
  • Loading branch information
manusa authored Jan 15, 2025
1 parent 339a9af commit d470786
Show file tree
Hide file tree
Showing 3 changed files with 438 additions and 433 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Usage:
* Fix #2667: Add new helm test goal task (`k8s:helm-test` for maven and `k8sHelmTest` for gradle)
* Fix #3326: Micronaut healthcheck enricher infers overridden server port in application.properties
* Fix #3354: Build fails with `imageStream` for `buildRecreate` value
* Fix #3578: Skip executable libs when searching Spring Boot native binary
* Fix #3623: Updated jkube-images to 0.0.25

### 1.17.0 (2024-08-13)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors;

import org.eclipse.jkube.kit.common.JavaProject;
import org.eclipse.jkube.kit.common.Plugin;
Expand All @@ -32,133 +35,143 @@
*/
public class SpringBootUtil {

public static final String SPRING_BOOT_GROUP_ID = "org.springframework.boot";
// public static final String SPRING_BOOT_ARTIFACT_ID = "spring-boot";
public static final String SPRING_BOOT_DEVTOOLS_ARTIFACT_ID = "spring-boot-devtools";
public static final String SPRING_BOOT_MAVEN_PLUGIN_ARTIFACT_ID = "spring-boot-maven-plugin";
public static final String SPRING_BOOT_GRADLE_PLUGIN_ARTIFACT_ID = "org.springframework.boot.gradle.plugin";
public static final String DEV_TOOLS_REMOTE_SECRET = "spring.devtools.remote.secret";
public static final String DEV_TOOLS_REMOTE_SECRET_ENV = "SPRING_DEVTOOLS_REMOTE_SECRET";

private static final String SPRING_WEB_FLUX_ARTIFACT_ID = "spring-boot-starter-webflux";
private static final String PLACEHOLDER_PREFIX = "${";
private static final String PLACEHOLDER_SUFFIX = "}";
private static final String VALUE_SEPARATOR = ":";

private SpringBootUtil() {}

/**
* Returns the spring boot configuration (supports `application.properties` and `application.yml`)
* or an empty properties object if not found, it assumes first profile as default profile.
*
* @param compileClassLoader compile class loader
* @return properties object
*/
public static Properties getSpringBootApplicationProperties(URLClassLoader compileClassLoader) {
return getSpringBootApplicationProperties(null, compileClassLoader);
public static final String SPRING_BOOT_GROUP_ID = "org.springframework.boot";
public static final String SPRING_BOOT_DEVTOOLS_ARTIFACT_ID = "spring-boot-devtools";
public static final String SPRING_BOOT_MAVEN_PLUGIN_ARTIFACT_ID = "spring-boot-maven-plugin";
public static final String SPRING_BOOT_GRADLE_PLUGIN_ARTIFACT_ID = "org.springframework.boot.gradle.plugin";
public static final String DEV_TOOLS_REMOTE_SECRET = "spring.devtools.remote.secret";
public static final String DEV_TOOLS_REMOTE_SECRET_ENV = "SPRING_DEVTOOLS_REMOTE_SECRET";

private static final String SPRING_WEB_FLUX_ARTIFACT_ID = "spring-boot-starter-webflux";
private static final String PLACEHOLDER_PREFIX = "${";
private static final String PLACEHOLDER_SUFFIX = "}";
private static final String VALUE_SEPARATOR = ":";

private SpringBootUtil() {}

/**
* Returns the spring boot configuration (supports `application.properties` and `application.yml`)
* or an empty properties object if not found, it assumes first profile as default profile.
*
* @param compileClassLoader compile class loader
* @return properties object
*/
public static Properties getSpringBootApplicationProperties(URLClassLoader compileClassLoader) {
return getSpringBootApplicationProperties(null, compileClassLoader);
}

/**
* Returns the spring boot configuration (supports `application.properties` and `application.yml`)
* or an empty properties object if not found
*
* @param springActiveProfile currently active spring-boot profile
* @param compileClassLoader compile class loader
* @return properties object
*/
public static Properties getSpringBootApplicationProperties(String springActiveProfile, URLClassLoader compileClassLoader) {
URL ymlResource = compileClassLoader.findResource("application.yml");
URL propertiesResource = compileClassLoader.findResource("application.properties");

Properties props = YamlUtil.getPropertiesFromYamlResource(springActiveProfile, ymlResource);
props.putAll(getPropertiesFromResource(propertiesResource));
if (ymlResource != null) {
props.put(JKUBE_INTERNAL_APP_CONFIG_FILE_LOCATION, ymlResource.toString());
} else if (propertiesResource != null) {
props.put(JKUBE_INTERNAL_APP_CONFIG_FILE_LOCATION, propertiesResource.toString());
}

/**
* Returns the spring boot configuration (supports `application.properties` and `application.yml`)
* or an empty properties object if not found
*
* @param springActiveProfile currently active spring-boot profile
* @param compileClassLoader compile class loader
* @return properties object
*/
public static Properties getSpringBootApplicationProperties(String springActiveProfile, URLClassLoader compileClassLoader) {
URL ymlResource = compileClassLoader.findResource("application.yml");
URL propertiesResource = compileClassLoader.findResource("application.properties");

Properties props = YamlUtil.getPropertiesFromYamlResource(springActiveProfile, ymlResource);
props.putAll(getPropertiesFromResource(propertiesResource));
if (ymlResource != null) {
props.put(JKUBE_INTERNAL_APP_CONFIG_FILE_LOCATION, ymlResource.toString());
} else if (propertiesResource != null) {
props.put(JKUBE_INTERNAL_APP_CONFIG_FILE_LOCATION, propertiesResource.toString());
}
return new SpringBootPropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true)
.replaceAllPlaceholders(props);
return new SpringBootPropertyPlaceholderHelper(PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true)
.replaceAllPlaceholders(props);
}

/**
* Determine the spring-boot major version for the current project
*
* @param javaProject project
* @return spring boot version or null
*/
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) {
return project.getProperties().get("spring.profiles.active").toString();
}

/**
* Determine the spring-boot major version for the current project
*
* @param javaProject project
* @return spring boot version or null
*/
public static Optional<String> getSpringBootVersion(JavaProject javaProject) {
return Optional.ofNullable(JKubeProjectUtil.getAnyDependencyVersionWithGroupId(javaProject, SPRING_BOOT_GROUP_ID));
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) {
return mavenPlugin.getConfiguration();
}

/**
* 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) {
return project.getProperties().get("spring.profiles.active").toString();
}
return null;
Plugin gradlePlugin = JKubeProjectUtil.getPlugin(javaProject, SPRING_BOOT_GRADLE_PLUGIN_ARTIFACT_ID);
if (gradlePlugin != null) {
return gradlePlugin.getConfiguration();
}

/**
* 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) {
return mavenPlugin.getConfiguration();
}
Plugin gradlePlugin = JKubeProjectUtil.getPlugin(javaProject, SPRING_BOOT_GRADLE_PLUGIN_ARTIFACT_ID);
if (gradlePlugin != null) {
return gradlePlugin.getConfiguration();
}
return Collections.emptyMap();
return Collections.emptyMap();
}

public static boolean isSpringBootRepackage(JavaProject project) {
Plugin plugin = JKubeProjectUtil.getPlugin(project, SPRING_BOOT_MAVEN_PLUGIN_ARTIFACT_ID);
return Optional.ofNullable(plugin)
.map(Plugin::getExecutions)
.map(e -> e.contains("repackage"))
.orElse(false);
}

public static Plugin getNativePlugin(JavaProject project) {
Plugin plugin = JKubeProjectUtil.getPlugin(project, "org.graalvm.buildtools", "native-maven-plugin");
if (plugin != null) {
return plugin;
}

public static boolean isSpringBootRepackage(JavaProject project) {
Plugin plugin = JKubeProjectUtil.getPlugin(project, SPRING_BOOT_MAVEN_PLUGIN_ARTIFACT_ID);
return Optional.ofNullable(plugin)
.map(Plugin::getExecutions)
.map(e -> e.contains("repackage"))
.orElse(false);
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) {
File[] nativeExecutableArtifacts = null;
for (String location : new String[] {"", "native/nativeCompile/"}) {
nativeExecutableArtifacts = new File(project.getBuildDirectory(), location)
.listFiles(f -> f.isFile() && f.canExecute());
if (nativeExecutableArtifacts != null && nativeExecutableArtifacts.length > 0) {
break;
}
}

public static Plugin getNativePlugin(JavaProject project) {
Plugin plugin = JKubeProjectUtil.getPlugin(project, "org.graalvm.buildtools", "native-maven-plugin");
if (plugin != null) {
return plugin;
}
return JKubeProjectUtil.getPlugin(project, "org.graalvm.buildtools.native", "org.graalvm.buildtools.native.gradle.plugin");
if (nativeExecutableArtifacts == null) {
return null;
}

/**
* 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);
File[] nativeExecutableArtifacts = nativeArtifactDir.listFiles(f -> f.isFile() && f.canExecute());
if (nativeExecutableArtifacts != null && nativeExecutableArtifacts.length > 0) {
if (nativeExecutableArtifacts.length == 1) {
return nativeExecutableArtifacts[0];
}
throw new IllegalStateException("More than one native executable file found in " + nativeArtifactDir.getAbsolutePath());
}
}
return null;
if (nativeExecutableArtifacts.length == 1) {
return nativeExecutableArtifacts[0];
}

public static boolean hasSpringWebFluxDependency(JavaProject javaProject) {
return JKubeProjectUtil.hasDependency(javaProject, SPRING_BOOT_GROUP_ID, SPRING_WEB_FLUX_ARTIFACT_ID);
// TODO: find better way to distinguish between native executables
final List<File> filteredBinaries = Arrays.stream(nativeExecutableArtifacts)
.filter(f -> !f.getName().endsWith(".so"))
.collect(Collectors.toList());
if (filteredBinaries.size() == 1) {
return filteredBinaries.iterator().next();
}
throw new IllegalStateException("More than one native executable file found in " + project.getBuildDirectory().getAbsolutePath());
}

public static boolean hasSpringWebFluxDependency(JavaProject javaProject) {
return JKubeProjectUtil.hasDependency(javaProject, SPRING_BOOT_GROUP_ID, SPRING_WEB_FLUX_ARTIFACT_ID);
}
}

Loading

0 comments on commit d470786

Please sign in to comment.