Add initial reference manual documentation
See gh-295
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
[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 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
|
||||
| +-<spring boot loader classes>
|
||||
+-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.jar
|
||||
|
|
||||
+-META-INF
|
||||
| +-MANIFEST.MF
|
||||
+-org
|
||||
| +-springframework
|
||||
| +-boot
|
||||
| +-loader
|
||||
| +-<spring boot loader classes>
|
||||
+-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 `RandomAccessJarFile.getURL()` method will
|
||||
return a `URL` that opens a `java.net.JarURLConnection` compatible connection.
|
||||
`RandomAccessJarFile` URLs 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/` by default, but you can add additional locations by
|
||||
setting an environment variable `LOADER_PATH` or `loader.path` in `application.properties`
|
||||
(colon-separated list of directories or archives).
|
||||
|
||||
|
||||
|
||||
[[executable-jar-launcher-manifest]]
|
||||
==== Launcher manifest
|
||||
You need 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 a 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`
|
||||
|Colon-separated Classpath, e.g. `lib:${HOME}/app/lib`.
|
||||
|
||||
|`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`).
|
||||
|
||||
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).
|
||||
* 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:
|
||||
|
||||
* http://maven.apache.org/plugins/maven-shade-plugin/[Maven Shade Plugin]
|
||||
* http://www.jdotsoft.com/JarClassLoader.php[JarClassLoader]
|
||||
* http://one-jar.sourceforge.net[OneJar]
|
||||
Reference in New Issue
Block a user