Tools for generating executable JAR/WAR files with embedded containers for Spring Boot applications
—
Comprehensive system for handling dependency libraries including scope classification, coordinate tracking, inclusion/exclusion logic, and integration with build tools. The library management system ensures proper dependency resolution and packaging for executable archives.
Core class representing a single library dependency with metadata and packaging instructions.
public class Library {
/**
* Create a library with basic file and scope information.
*
* @param file the library JAR file
* @param scope the library scope (COMPILE, RUNTIME, PROVIDED, CUSTOM)
*/
public Library(File file, LibraryScope scope);
/**
* Create a library with complete metadata and packaging options.
*
* @param name the library name (derived from file if null)
* @param file the library JAR file
* @param scope the library scope
* @param coordinates the Maven/Gradle coordinates
* @param unpackRequired whether the library needs to be unpacked
* @param local whether this is a local/project library
* @param included whether to include this library in the archive
*/
public Library(String name, File file, LibraryScope scope, LibraryCoordinates coordinates,
boolean unpackRequired, boolean local, boolean included);
/**
* Get the library name (typically the JAR filename without extension).
*
* @return the library name
*/
public String getName();
/**
* Get the library file.
*
* @return the JAR file for this library
*/
public File getFile();
/**
* Get the library scope.
*
* @return the scope (COMPILE, RUNTIME, PROVIDED, CUSTOM)
*/
public LibraryScope getScope();
/**
* Get the library coordinates (group:artifact:version).
*
* @return the coordinates, or null if not available
*/
public LibraryCoordinates getCoordinates();
/**
* Check if this library requires unpacking.
* Some libraries need to be unpacked rather than included as nested JARs.
*
* @return true if unpacking is required
*/
public boolean isUnpackRequired();
/**
* Check if this is a local library (part of the project).
*
* @return true if this is a local/project library
*/
public boolean isLocal();
/**
* Check if this library should be included in the archive.
*
* @return true if the library should be included
*/
public boolean isIncluded();
}Interface for providing collections of libraries to the packaging process.
public interface Libraries {
/**
* Process all libraries using the provided callback.
* The callback is invoked once for each library that should be included.
*
* @param callback the callback to process each library
* @throws IOException if library processing fails
*/
void doWithLibraries(LibraryCallback callback) throws IOException;
/**
* Empty libraries collection.
*/
Libraries NONE = callback -> {
// No libraries to process
};
}Callback interface for processing individual libraries during packaging.
@FunctionalInterface
public interface LibraryCallback {
/**
* Process a single library.
* Called once for each library that should be included in the archive.
*
* @param library the library to process
* @throws IOException if library processing fails
*/
void library(Library library) throws IOException;
}Interface defining the different scopes for library dependencies.
public interface LibraryScope {
/**
* Compile-time and runtime dependency.
* Included in the final archive.
*/
LibraryScope COMPILE = /* implementation */;
/**
* Runtime-only dependency.
* Included in the final archive but not needed for compilation.
*/
LibraryScope RUNTIME = /* implementation */;
/**
* Provided by the runtime environment.
* Not included in the final archive.
*/
LibraryScope PROVIDED = /* implementation */;
/**
* Custom scope with user-defined behavior.
*/
LibraryScope CUSTOM = /* implementation */;
}Interface for Maven/Gradle artifact coordinates providing metadata about library origins.
public interface LibraryCoordinates {
/**
* Get the group ID (Maven) or organization (Gradle).
*
* @return the group ID
*/
String getGroupId();
/**
* Get the artifact ID or module name.
*
* @return the artifact ID
*/
String getArtifactId();
/**
* Get the version string.
*
* @return the version
*/
String getVersion();
/**
* Create coordinates from individual components.
*
* @param groupId the group ID
* @param artifactId the artifact ID
* @param version the version
* @return new coordinates instance
*/
static LibraryCoordinates of(String groupId, String artifactId, String version);
/**
* Convert coordinates to standard notation string (group:artifact:version).
*
* @param coordinates the coordinates to convert
* @return the standard notation string
*/
static String toStandardNotationString(LibraryCoordinates coordinates);
}public class DefaultLibraryCoordinates implements LibraryCoordinates {
/**
* Create coordinates with group, artifact, and version.
*
* @param groupId the group ID
* @param artifactId the artifact ID
* @param version the version
*/
public DefaultLibraryCoordinates(String groupId, String artifactId, String version);
}public class JarModeLibrary extends Library {
/**
* Special library for JAR mode support.
* Provides tools functionality for JAR introspection and manipulation.
*/
public static final JarModeLibrary TOOLS = /* implementation */;
}import org.springframework.boot.loader.tools.*;
import java.io.File;
import java.util.List;
// Create a simple library collection
List<File> libraryFiles = List.of(
new File("lib/spring-boot-3.2.0.jar"),
new File("lib/spring-context-6.0.0.jar"),
new File("lib/logback-classic-1.4.5.jar"),
new File("lib/jackson-core-2.15.0.jar")
);
Libraries libraries = callback -> {
for (File libFile : libraryFiles) {
// Determine scope based on naming convention
LibraryScope scope = libFile.getName().contains("test")
? LibraryScope.PROVIDED
: LibraryScope.COMPILE;
callback.library(new Library(libFile, scope));
}
};
// Use with repackager
Repackager repackager = new Repackager(new File("myapp.jar"));
repackager.repackage(libraries);import org.springframework.boot.loader.tools.*;
import java.io.File;
// Create libraries with full metadata
Libraries libraries = callback -> {
// Runtime dependency with coordinates
LibraryCoordinates springCoords = LibraryCoordinates.of(
"org.springframework", "spring-core", "6.0.0"
);
callback.library(new Library(
"spring-core-6.0.0.jar",
new File("lib/spring-core-6.0.0.jar"),
LibraryScope.RUNTIME,
springCoords,
false, // no unpacking required
false, // not local
true // include in archive
));
// Local project dependency that needs unpacking
callback.library(new Library(
"my-custom-lib.jar",
new File("build/libs/my-custom-lib.jar"),
LibraryScope.COMPILE,
LibraryCoordinates.of("com.example", "my-custom-lib", "1.0.0"),
true, // requires unpacking
true, // local library
true // include in archive
));
// Provided dependency (excluded from final archive)
callback.library(new Library(
"servlet-api.jar",
new File("provided/servlet-api.jar"),
LibraryScope.PROVIDED,
LibraryCoordinates.of("javax.servlet", "servlet-api", "3.1.0"),
false, // no unpacking needed
false, // not local
false // exclude from archive (provided by container)
));
};import org.springframework.boot.loader.tools.*;
import java.io.File;
import java.util.Set;
// Example integration with Gradle dependency resolution
public class GradleLibraries implements Libraries {
private final Set<File> compileClasspath;
private final Set<File> runtimeClasspath;
private final Set<File> providedClasspath;
public GradleLibraries(Set<File> compileClasspath, Set<File> runtimeClasspath, Set<File> providedClasspath) {
this.compileClasspath = compileClasspath;
this.runtimeClasspath = runtimeClasspath;
this.providedClasspath = providedClasspath;
}
@Override
public void doWithLibraries(LibraryCallback callback) throws IOException {
// Process compile dependencies
for (File file : compileClasspath) {
callback.library(new Library(file, LibraryScope.COMPILE));
}
// Process runtime-only dependencies
for (File file : runtimeClasspath) {
if (!compileClasspath.contains(file)) {
callback.library(new Library(file, LibraryScope.RUNTIME));
}
}
// Process provided dependencies (marked as excluded)
for (File file : providedClasspath) {
callback.library(new Library(
file.getName(),
file,
LibraryScope.PROVIDED,
null, // no coordinates
false, false, false // exclude from archive
));
}
}
}import org.springframework.boot.loader.tools.*;
import java.io.File;
import java.util.List;
// Example integration with Maven dependency resolution
public class MavenLibraries implements Libraries {
private final List<MavenArtifact> artifacts;
public MavenLibraries(List<MavenArtifact> artifacts) {
this.artifacts = artifacts;
}
@Override
public void doWithLibraries(LibraryCallback callback) throws IOException {
for (MavenArtifact artifact : artifacts) {
LibraryScope scope = mapMavenScope(artifact.getScope());
LibraryCoordinates coords = LibraryCoordinates.of(
artifact.getGroupId(),
artifact.getArtifactId(),
artifact.getVersion()
);
boolean include = !LibraryScope.PROVIDED.equals(scope);
callback.library(new Library(
artifact.getFile().getName(),
artifact.getFile(),
scope,
coords,
artifact.requiresUnpacking(),
artifact.isLocal(),
include
));
}
}
private LibraryScope mapMavenScope(String mavenScope) {
switch (mavenScope) {
case "compile": return LibraryScope.COMPILE;
case "runtime": return LibraryScope.RUNTIME;
case "provided": return LibraryScope.PROVIDED;
case "test": return LibraryScope.PROVIDED; // exclude test deps
default: return LibraryScope.CUSTOM;
}
}
}import org.springframework.boot.loader.tools.*;
import java.io.File;
import java.util.function.Predicate;
// Wrapper for filtering libraries
public class FilteredLibraries implements Libraries {
private final Libraries delegate;
private final Predicate<Library> filter;
public FilteredLibraries(Libraries delegate, Predicate<Library> filter) {
this.delegate = delegate;
this.filter = filter;
}
@Override
public void doWithLibraries(LibraryCallback callback) throws IOException {
delegate.doWithLibraries(library -> {
if (filter.test(library)) {
callback.library(library);
}
});
}
}
// Usage examples
Libraries allLibraries = /* ... */;
// Only include compile and runtime libraries
Libraries filtered = new FilteredLibraries(allLibraries, library ->
LibraryScope.COMPILE.equals(library.getScope()) ||
LibraryScope.RUNTIME.equals(library.getScope())
);
// Exclude snapshot versions
Libraries noSnapshots = new FilteredLibraries(allLibraries, library -> {
LibraryCoordinates coords = library.getCoordinates();
return coords == null || !coords.getVersion().contains("SNAPSHOT");
});
// Only local libraries
Libraries localOnly = new FilteredLibraries(allLibraries, Library::isLocal);import org.springframework.boot.loader.tools.*;
import java.io.IOException;
// Inspect libraries before packaging
public void inspectLibraries(Libraries libraries) throws IOException {
System.out.println("Library Analysis:");
System.out.println("==================");
libraries.doWithLibraries(library -> {
System.out.printf("Name: %s%n", library.getName());
System.out.printf(" File: %s%n", library.getFile().getAbsolutePath());
System.out.printf(" Scope: %s%n", library.getScope());
System.out.printf(" Size: %,d bytes%n", library.getFile().length());
System.out.printf(" Local: %s%n", library.isLocal());
System.out.printf(" Included: %s%n", library.isIncluded());
System.out.printf(" Unpack Required: %s%n", library.isUnpackRequired());
LibraryCoordinates coords = library.getCoordinates();
if (coords != null) {
System.out.printf(" Coordinates: %s%n",
LibraryCoordinates.toStandardNotationString(coords));
}
System.out.println();
});
}import org.springframework.boot.loader.tools.*;
// Include JAR mode tools for runtime introspection
Libraries librariesWithTools = callback -> {
// Add your regular libraries
// ... regular library processing ...
// Add JAR mode tools for runtime capabilities
callback.library(JarModeLibrary.TOOLS);
};
// Enable JAR mode in repackager
Repackager repackager = new Repackager(new File("myapp.jar"));
repackager.setIncludeRelevantJarModeJars(true);
repackager.repackage(librariesWithTools);
// The resulting JAR can be inspected at runtime:
// java -Djarmode=tools -jar myapp.jarThe library management system processes dependencies through these stages:
This pipeline ensures that only necessary dependencies are included while maintaining proper classpath organization for runtime execution.
Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-boot--spring-boot-loader-tools