Add nullability annotations to loader/spring-boot-jarmode-tools

See gh-46587
This commit is contained in:
Moritz Halbritter
2025-08-11 15:50:49 +02:00
parent f00dc9753b
commit d1d35d03c8
11 changed files with 57 additions and 38 deletions
@@ -28,6 +28,8 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
/**
* A command that can be launched.
*
@@ -132,7 +134,7 @@ abstract class Command {
* Returns the deprecation message.
* @return the deprecation message
*/
String getDeprecationMessage() {
@Nullable String getDeprecationMessage() {
return null;
}
@@ -142,7 +144,7 @@ abstract class Command {
* @param name the name of the command to find
* @return a {@link Command} instance or {@code null}.
*/
static Command find(Collection<? extends Command> commands, String name) {
static @Nullable Command find(Collection<? extends Command> commands, String name) {
for (Command command : commands) {
if (command.getName().equals(name)) {
return command;
@@ -206,7 +208,7 @@ abstract class Command {
this.values = values;
}
private Option find(String arg) {
private @Nullable Option find(String arg) {
if (arg.startsWith("--")) {
String name = arg.substring(2);
for (Option candidate : this.values) {
@@ -264,13 +266,13 @@ abstract class Command {
private final String name;
private final String valueDescription;
private final @Nullable String valueDescription;
private final String description;
private final boolean optionalValue;
private Option(String name, String valueDescription, String description, boolean optionalValue) {
private Option(String name, @Nullable String valueDescription, String description, boolean optionalValue) {
this.name = name;
this.description = description;
this.valueDescription = valueDescription;
@@ -290,7 +292,7 @@ abstract class Command {
* option is a flag/switch.
* @return the option value description
*/
String getValueDescription() {
@Nullable String getValueDescription() {
return this.valueDescription;
}
@@ -310,7 +312,7 @@ abstract class Command {
return this.description;
}
private String claimArg(Deque<String> args) {
private @Nullable String claimArg(Deque<String> args) {
if (this.valueDescription == null) {
return null;
}
@@ -28,6 +28,8 @@ import java.security.ProtectionDomain;
import java.util.Locale;
import java.util.jar.JarFile;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -41,7 +43,7 @@ class Context {
private final File workingDir;
private final String relativeDir;
private final @Nullable String relativeDir;
/**
* Create a new {@link Context} instance.
@@ -63,7 +65,7 @@ class Context {
this.relativeDir = deduceRelativeDir(archiveFile.getParentFile(), this.workingDir);
}
private boolean isExistingFile(File archiveFile) {
private boolean isExistingFile(@Nullable File archiveFile) {
return archiveFile != null && archiveFile.isFile() && archiveFile.exists();
}
@@ -81,10 +83,10 @@ class Context {
if (source != null && source.exists()) {
return source.getAbsoluteFile();
}
return null;
throw new IllegalStateException("Unable to find source archive");
}
catch (Exception ex) {
return null;
throw new IllegalStateException("Unable to find source archive", ex);
}
}
@@ -105,7 +107,7 @@ class Context {
return new File(name);
}
private String deduceRelativeDir(File sourceDirectory, File workingDir) {
private @Nullable String deduceRelativeDir(File sourceDirectory, File workingDir) {
String sourcePath = sourceDirectory.getAbsolutePath();
String workingPath = workingDir.getAbsolutePath();
if (sourcePath.equals(workingPath) || !sourcePath.startsWith(workingPath)) {
@@ -136,7 +138,7 @@ class Context {
* or {@code null} if none relative directory can be deduced.
* @return the relative dir ending in {@code /} or {@code null}
*/
String getRelativeArchiveDir() {
@Nullable String getRelativeArchiveDir() {
return this.relativeDir;
}
@@ -41,6 +41,8 @@ import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.jarmode.tools.JarStructure.Entry;
import org.springframework.boot.jarmode.tools.JarStructure.Entry.Type;
import org.springframework.boot.loader.jarmode.JarModeErrorException;
@@ -84,13 +86,13 @@ class ExtractCommand extends Command {
private final Context context;
private final Layers layers;
private final @Nullable Layers layers;
ExtractCommand(Context context) {
this(context, null);
}
ExtractCommand(Context context, Layers layers) {
ExtractCommand(Context context, @Nullable Layers layers) {
super("extract", "Extract the contents from the jar", Options.of(LAUNCHER_OPTION, LAYERS_OPTION,
DESTINATION_OPTION, LIBRARIES_DIRECTORY_OPTION, APPLICATION_FILENAME_OPTION, FORCE_OPTION),
Parameters.none());
@@ -153,7 +155,7 @@ class ExtractCommand extends Command {
String librariesDirectory = getLibrariesDirectory(options);
extractArchive(fileResolver, (jarEntry) -> {
Entry entry = jarStructure.resolve(jarEntry);
if (isType(entry, Type.LIBRARY)) {
if (entry != null && entry.type() == Type.LIBRARY) {
return librariesDirectory + entry.location();
}
return null;
@@ -269,10 +271,6 @@ class ExtractCommand extends Command {
return this.context.getArchiveFile().getName();
}
private static boolean isType(Entry entry, Type type) {
return (entry != null) && entry.type() == type;
}
private static void extractEntry(InputStream stream, JarEntry entry, File file) throws IOException {
mkdirs(file.getParentFile());
try (OutputStream out = new FileOutputStream(file)) {
@@ -287,15 +285,15 @@ class ExtractCommand extends Command {
}
}
private static FileTime getCreationTime(JarEntry entry) {
private static @Nullable FileTime getCreationTime(JarEntry entry) {
return (entry.getCreationTime() != null) ? entry.getCreationTime() : entry.getLastModifiedTime();
}
private static FileTime getLastAccessTime(JarEntry entry) {
private static @Nullable FileTime getLastAccessTime(JarEntry entry) {
return (entry.getLastAccessTime() != null) ? entry.getLastAccessTime() : getLastModifiedTime(entry);
}
private static FileTime getLastModifiedTime(JarEntry entry) {
private static @Nullable FileTime getLastModifiedTime(JarEntry entry) {
return (entry.getLastModifiedTime() != null) ? entry.getLastModifiedTime() : entry.getCreationTime();
}
@@ -348,7 +346,7 @@ class ExtractCommand extends Command {
@FunctionalInterface
private interface EntryNameTransformer {
String getName(JarEntry entry);
@Nullable String getName(JarEntry entry);
}
@@ -375,7 +373,7 @@ class ExtractCommand extends Command {
* should be skipped
* @throws IOException if something went wrong
*/
default File resolve(JarEntry entry, String newName) throws IOException {
default @Nullable File resolve(JarEntry entry, String newName) throws IOException {
return resolve(entry.getName(), newName);
}
@@ -387,7 +385,7 @@ class ExtractCommand extends Command {
* should be skipped
* @throws IOException if something went wrong
*/
File resolve(String originalName, String newName) throws IOException;
@Nullable File resolve(String originalName, String newName) throws IOException;
/**
* Resolves the file for the application.
@@ -395,7 +393,7 @@ class ExtractCommand extends Command {
* be skipped
* @throws IOException if something went wrong
*/
File resolveApplication() throws IOException;
@Nullable File resolveApplication() throws IOException;
}
@@ -453,7 +451,7 @@ class ExtractCommand extends Command {
}
@Override
public File resolve(String originalName, String newName) throws IOException {
public @Nullable File resolve(String originalName, String newName) throws IOException {
String layer = this.layers.getLayer(originalName);
if (shouldExtractLayer(layer)) {
File directory = getLayerDirectory(layer);
@@ -463,7 +461,7 @@ class ExtractCommand extends Command {
}
@Override
public File resolveApplication() throws IOException {
public @Nullable File resolveApplication() throws IOException {
String layer = this.layers.getApplicationLayerName();
if (shouldExtractLayer(layer)) {
File directory = getLayerDirectory(layer);
@@ -22,6 +22,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.StringUtils;
/**
@@ -39,7 +41,7 @@ class ExtractLayersCommand extends Command {
this(context, null);
}
ExtractLayersCommand(Context context, Layers layers) {
ExtractLayersCommand(Context context, @Nullable Layers layers) {
super("extract", "Extracts layers from the jar for image creation", Options.of(DESTINATION_OPTION),
Parameters.of("[<layer>...]"));
this.delegate = new ExtractCommand(context, layers);
@@ -21,6 +21,8 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
/**
* Implicit {@code 'help'} command.
*
@@ -39,7 +41,7 @@ class HelpCommand extends Command {
this(context, commands, System.getProperty("jarmode"));
}
HelpCommand(Context context, List<Command> commands, String jarMode) {
HelpCommand(Context context, List<Command> commands, @Nullable String jarMode) {
super("help", "Help about any command", Options.none(), Parameters.of("[<command>]"));
this.context = context;
this.commands = commands;
@@ -33,6 +33,8 @@ import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.jarmode.tools.JarStructure.Entry.Type;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
@@ -92,7 +94,7 @@ class IndexedJarStructure implements JarStructure {
}
@Override
public Entry resolve(String name) {
public @Nullable Entry resolve(String name) {
if (ENTRY_IGNORE_LIST.contains(name)) {
return null;
}
@@ -138,7 +140,7 @@ class IndexedJarStructure implements JarStructure {
return value;
}
static IndexedJarStructure get(File file) {
static @Nullable IndexedJarStructure get(File file) {
try {
try (JarFile jarFile = new JarFile(file)) {
Manifest manifest = jarFile.getManifest();
@@ -30,6 +30,8 @@ import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
@@ -98,7 +100,7 @@ class IndexedLayers implements Layers {
* @return an {@link IndexedLayers} instance or {@code null} if this not a layered
* jar.
*/
static IndexedLayers get(Context context) {
static @Nullable IndexedLayers get(Context context) {
try (JarFile jarFile = new JarFile(context.getArchiveFile())) {
Manifest manifest = jarFile.getManifest();
if (manifest == null) {
@@ -20,6 +20,8 @@ import java.util.function.UnaryOperator;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import org.jspecify.annotations.Nullable;
/**
* Provide information about a fat jar structure that is meant to be extracted.
*
@@ -34,7 +36,7 @@ interface JarStructure {
* @param entry the entry to handle
* @return the resolved {@link Entry}
*/
default Entry resolve(ZipEntry entry) {
default @Nullable Entry resolve(ZipEntry entry) {
return resolve(entry.getName());
}
@@ -44,7 +46,7 @@ interface JarStructure {
* @param name the name of the entry to handle
* @return the resolved {@link Entry}
*/
Entry resolve(String name);
@Nullable Entry resolve(String name);
/**
* Create the {@link Manifest} for the launcher jar, applying the specified operator
@@ -18,6 +18,8 @@ package org.springframework.boot.jarmode.tools;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.loader.jarmode.JarMode;
/**
@@ -29,7 +31,7 @@ import org.springframework.boot.loader.jarmode.JarMode;
*/
public class LayerToolsJarMode implements JarMode {
static Context contextOverride;
static @Nullable Context contextOverride;
@Override
public boolean accepts(String mode) {
@@ -19,6 +19,8 @@ package org.springframework.boot.jarmode.tools;
import java.io.PrintStream;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.loader.jarmode.JarMode;
/**
@@ -37,7 +39,7 @@ public class ToolsJarMode implements JarMode {
this(null, null);
}
public ToolsJarMode(Context context, PrintStream out) {
public ToolsJarMode(@Nullable Context context, @Nullable PrintStream out) {
this.context = (context != null) ? context : new Context();
this.out = (out != null) ? out : System.out;
}
@@ -17,4 +17,7 @@
/**
* JarMode support for layertools and tools.
*/
@NullMarked
package org.springframework.boot.jarmode.tools;
import org.jspecify.annotations.NullMarked;