[appendix] [[executable-jar]] == The executable jar format The `spring-boot-loader` modules allows Spring Boot to support executable jar and war files. If you're using the Maven or Gradle plugin, executable jars are automatically generated and you generally won't need to know the details of how they work. If you need to create executable jars from a different build system, or if you are just curious about the underlying technology, this section provides some background. [[executable-jar-nested-jars]] === Nested JARs Java does not provide any standard way to load nested jar files (i.e. jar files that are themselves contained within a jar). This can be problematic if you are looking to distribute a self-contained application that you can just run from the command line without unpacking. To solve this problem, many developers use "`shaded`" jars. A shaded jar simply packages all classes, from all jars, into a single 'uber jar'. The problem with shaded jars is that it becomes hard to see which libraries you are actually using in your application. It can also be problematic if the same filename is used (but with different content) in multiple jars. Spring Boot takes a different approach and allows you to actually nest jars directly. [[executable-jar-jar-file-structure]] ==== The executable jar file structure Spring Boot Loader compatible jar files should be structured in the following way: [indent=0] ---- example.jar | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +- +-com | +-mycompany | + project | +-YouClasses.class +-lib +-dependency1.jar +-dependency2.jar ---- Dependencies should be placed in a nested `lib` directory. [[executable-jar-war-file-structure]] ==== The executable war file structure Spring Boot Loader compatible war files should be structured in the following way: [indent=0] ---- example.war | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +- +-WEB-INF +-classes | +-com | +-mycompany | +-project | +-YouClasses.class +-lib | +-dependency1.jar | +-dependency2.jar +-lib-provided +-servlet-api.jar +-dependency3.jar ---- Dependencies should be placed in a nested `WEB-INF/lib` directory. Any dependencies that are required when running embedded but are not required when deploying to a traditional web container should be placed in `WEB-INF/lib-provided`. [[executable-jar-jarfile]] === Spring Boot's "`JarFile`" class The core class used to support loading nested jars is `org.springframework.boot.loader.jar.JarFile`. It allows you load jar content from a standard jar file, or from nested child jar data. When first loaded, the location of each `JarEntry` is mapped to a physical file offset of the outer jar: [indent=0] ---- myapp.jar +---------+---------------------+ | | /lib/mylib.jar | | A.class |+---------+---------+| | || B.class | B.class || | |+---------+---------+| +---------+---------------------+ ^ ^ ^ 0063 3452 3980 ---- The example above shows how `A.class` can be found in `myapp.jar` position `0063`. `B.class` from the nested jar can actually be found in `myapp.jar` position `3452` and `B.class` is at position `3980`. Armed with this information, we can load specific nested entries by simply seeking to appropriate part if the outer jar. We don't need to unpack the archive and we don't need to read all entry data into memory. [[executable-jar-jarfile-compatibility]] ==== Compatibility with the standard Java "`JarFile`" Spring Boot Loader strives to remain compatible with existing code and libraries. `org.springframework.boot.loader.jar.JarFile` extends from `java.util.jar.JarFile` and should work as a drop-in replacement. The `getURL()` method will return a `URL` that opens a `java.net.JarURLConnection` compatible connection and can be used with Java's `URLClassLoader`. [[executable-jar-launching]] === Launching executable jars The `org.springframework.boot.loader.Launcher` class is a special bootstrap class that is used as an executable jars main entry point. It is the actual `Main-Class` in your jar file and it's used to setup an appropriate `URLClassLoader` and ultimately call your `main()` method. There are 3 launcher subclasses (`JarLauncher`, `WarLauncher` and `PropertiesLauncher`). Their purpose is to load resources (`.class` files etc.) from nested jar files or war files in directories (as opposed to explicitly on the classpath). In the case of the `[Jar|War]Launcher` the nested paths are fixed (`+lib/*.jar+` and `+lib-provided/*.jar+` for the war case) so you just add extra jars in those locations if you want more. The `PropertiesLauncher` looks in `lib/` in your application archive by default, but you can add additional locations by setting an environment variable `LOADER_PATH` or `loader.path` in `application.properties` (comma-separated list of directories or archives). [[executable-jar-launcher-manifest]] ==== Launcher manifest You need to specify an appropriate `Launcher` as the `Main-Class` attribute of `META-INF/MANIFEST.MF`. The actual class that you want to launch (i.e. the class that you wrote that contains a `main` method) should be specified in the `Start-Class` attribute. For example, here is a typical `MANIFEST.MF` for an executable jar file: [indent=0] ---- Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: com.mycompany.project.MyApplication ---- For a war file, it would be: [indent=0] ---- Main-Class: org.springframework.boot.loader.WarLauncher Start-Class: com.mycompany.project.MyApplication ---- NOTE: You do not need to specify `Class-Path` entries in your manifest file, the classpath will be deduced from the nested jars. [[executable-jar-exploded-archives]] ==== Exploded archives Certain PaaS implementations may choose to unpack archives before they run. For example, Cloud Foundry operates in this way. You can run an unpacked archive by simply starting the appropriate launcher: [indent=0] ---- $ unzip -q myapp.jar $ java org.springframework.boot.loader.JarLauncher ---- [[executable-jar-property-launcher-features]] === PropertiesLauncher Features `PropertiesLauncher` has a few special features that can be enabled with external properties (System properties, environment variables, manifest entries or `application.properties`). [cols="2,4"] |=== |Key |Purpose |`loader.path` |Comma-separated Classpath, e.g. `lib,${HOME}/app/lib`. Earlier entries take precedence, just like a regular `-classpath` on the `javac` command line. |`loader.home` |Location of additional properties file, e.g. `file:///opt/app` (defaults to `${user.dir}`) |`loader.args` |Default arguments for the main method (space separated) |`loader.main` |Name of main class to launch, e.g. `com.app.Application`. |`loader.config.name` |Name of properties file, e.g. `loader` (defaults to `application`). |`loader.config.location` |Path to properties file, e.g. `classpath:loader.properties` (defaults to `application.properties`). |`loader.system` |Boolean flag to indicate that all properties should be added to System properties (defaults to `false`) |=== Manifest entry keys are formed by capitalizing initial letters of words and changing the separator to "`-`" from "`.`" (e.g. `Loader-Path`). The exception is `loader.main` which is looked up as `Start-Class` in the manifest for compatibility with `JarLauncher`). TIP: Build plugins automatically move the `Main-Class` attribute to `Start-Class` when the fat jar is built. If you are using that, specify the name of the class to launch using the `Main-Class` attribute and leave out `Start-Class`. Environment variables can be capitalized with underscore separators instead of periods. * `loader.home` is the directory location of an additional properties file (overriding the default) as long as `loader.config.location` is not specified. * `loader.path` can contain directories (scanned recursively for jar and zip files), archive paths, or wildcard patterns (for the default JVM behavior). * `loader.path` (if empty) defaults to `lib` (meaning a local directory or a nested one if running from an archive). Because of this `PropertiesLauncher` behaves the same as `JarLauncher` when no additional configuration is provided. * Placeholder replacement is done from System and environment variables plus the properties file itself on all values before use. [[executable-jar-restrictions]] === Executable jar restrictions There are a number of restrictions that you need to consider when working with a Spring Boot Loader packaged application. [[executable-jar-zip-entry-compression]] ==== Zip entry compression The `ZipEntry` for a nested jar must be saved using the `ZipEntry.STORED` method. This is required so that we can seek directly to individual content within the nested jar. The content of the nested jar file itself can still be compressed, as can any other entries in the outer jar. [[executable-jar-system-classloader]] ==== System ClassLoader Launched applications should use `Thread.getContextClassLoader()` when loading classes (most libraries and frameworks will do this by default). Trying to load nested jar classes via `ClassLoader.getSystemClassLoader()` will fail. Please be aware that `java.util.Logging` always uses the system classloader, for this reason you should consider a different logging implementation. [[executable-jar-alternatives]] === Alternative single jar solutions If the above restrictions mean that you cannot use Spring Boot Loader the following alternatives could be considered: * https://maven.apache.org/plugins/maven-shade-plugin/[Maven Shade Plugin] * http://www.jdotsoft.com/JarClassLoader.php[JarClassLoader] * https://sourceforge.net/projects/one-jar/[OneJar]