🛠️ Testing, Tools & Ecosystem · Advanced

Packaging & running in Java

JAR, executable JAR, classpath, jlink custom runtimes, jpackage.

🧩 The mysteryYour Docker image is 400 MB, and your app's own code is 2 MB. What is the other 398 MB doing in there?

What a JAR is

A JAR is a ZIP of class files plus **META-INF/MANIFEST.MF**. You can run any class in it by naming it on the classpath.

java -cp app.jar com.acme.Main

Executable JARs

Name the entry point in the manifest with **Main-Class** and the JAR becomes executable: java -jar app.jar. Without that entry, the launcher fails with "no main manifest attribute".

Manifest-Version: 1.0
Main-Class: com.acme.App

Writing Main-Class

✗ Wrong
Main-Class: com/acme/App.class

A file path with .class: the launcher can't find the class.

✓ Right
Main-Class: com.acme.App

Fully qualified class name, with dots and no .class suffix.

🔮 Predict it

Where are my libraries?

You run java -cp "libs/*" -jar app.jar. Are the JARs in libs/ on the classpath?

  1. No: with -jar, -cp and CLASSPATH are ignored
  2. Yes: -cp is always applied
  3. Only if they come first
Show the answer

No. In -jar mode the classpath is **the JAR itself plus the Class-Path entries in its manifest**. -cp and the CLASSPATH variable are ignored. A fat JAR or a manifest Class-Path solves it.

Slim runtimes and installers

jdeps shows which modules your code depends on. jlink builds a custom runtime containing only those modules. jpackage wraps your app and a runtime into a native installer (.msi, .dmg, .deb).

jdeps --print-module-deps app.jar
jlink --add-modules java.base,java.sql,\
java.logging --output runtime
💼 In the real world

Shrinking a Docker image

The image ships a full JDK, but the app only uses java.base, java.sql and java.logging. A jlink runtime with just those modules drops the compiler, tools and unused libraries, usually a fraction of the original size.

Key takeaways

  1. Executable JAR: Main-Class entry in the manifest
  2. java -jar ignores -cp and CLASSPATH
  3. jlink: custom runtime with only the needed modules
  4. jpackage: .msi / .dmg / .deb installers
🤯 Did you know?

A JAR file really is a ZIP file: rename app.jar to app.zip and any unzip tool will open it.

Practice questions

Your Docker image with a full JDK is 400 MB, but the app only uses java.base, java.sql and java.logging. How can you shrink it?

  1. Delete rt.jar
  2. Run with -Xmx64m
  3. Use jlink to build a runtime containing just those modules
  4. Compress the JAR twice
Check your answer

Use jlink to build a runtime containing just those modules. jlink assembles a runtime image from only the modules you list, usually a fraction of a full JDK.

`java -jar app.jar` fails with "no main manifest attribute". What's missing?

  1. A public static void main in every class
  2. The -cp flag
  3. A module-info.java
  4. A Main-Class entry in META-INF/MANIFEST.MF
Check your answer

A Main-Class entry in META-INF/MANIFEST.MF. Without Main-Class the launcher doesn't know which class to start. Build plugins can add it for you.

Your app runs in production. How do you know what it's doing? Next: logging done right.