Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
315: Packaging a JavaFX Application for Distribution
You've spent weeks polishing your Library Catalog Manager. The UI is snappy, the database integration is solid, and it works perfectly in your IDE. Now comes the part where most developers start sweating: distribution. You want to send this to a client or a friend, but you're about to hit the "it works on my machine" wall if you aren't careful.
The "Just Send the JAR" Trap
The most intuitive move—and the one I see juniors do constantly—is to build a "fat JAR." You use a plugin like Maven Shade or Gradle Shadow to cram every single dependency into one giant ZIP file and tell your user, "Just make sure you have Java 17 installed and run java -jar library-manager.jar."
In the old days of Java 8, this was mostly fine. But since Java 11, JavaFX is no longer bundled with the JDK. If you go the fat JAR route, your user doesn't just need a JRE; they need the specific JavaFX modules installed and configured on their system path. I can guarantee you that your user will not do this. They'll send you a screenshot of a ClassNotFoundException: javafx.application.Application and a very frustrated email. Even if they do install the right JDK, you're still relying on the user to manage their environment variables. That's a losing game. We want the user to double-click an icon, not open a terminal.
Bundling the World with jpackage
The professional way to handle this is to stop thinking of your app as a "Java program" and start thinking of it as a "native application." We do this using jlink and jpackage. Instead of asking the user to provide the runtime, we bundle a stripped-down version of the Java Runtime Environment (JRE) directly with the app.
First, jlink allows us to create a custom runtime image. If your Library Catalog Manager only uses java.base, java.sql, and the JavaFX modules, jlink throws away the rest of the JDK (like the compiler and unused standard libraries). This keeps the footprint smaller. Then, jpackage takes that custom runtime and your code and wraps them in a platform-specific installer—an .msi for Windows, a .dmg for macOS, or a .deb for Linux.
# A simplified look at what the jpackage command is actually doing under the hood
jpackage --name "LibraryManager" \
--input lib/ \
--main-jar library-manager.jar \
--main-class com.catalog.Main \
--type exe \
--win-shortcut \
--win-menu
Now, when the user runs that installer, they get a real executable. They don't even need to know Java is running the show. It just works.
Trading Disk Space for Sanity
There is a trade-off here, and it's purely about file size. A fat JAR might be 5MB. A jpackage distribution will be 40MB to 100MB because it includes the runtime. To some, that feels wasteful. To me, that's a tiny price to pay for eliminating 100% of the "Java not found" support tickets.
One detail I've learned the hard way: jpackage is platform-specific. You cannot build a Windows .exe from a Mac. You have to run the packaging command on the OS you are targeting. I usually handle this by setting up a simple GitHub Action or a Jenkins pipeline that triggers builds on different OS runners, so I don't have to keep three different laptops on my desk just to ship a release.
📋 Practical Task
Packaging the Library Catalog Manager for Windows/macOS
Using the Library Catalog Manager project you've been developing, move beyond the JAR file and create a native system installer. Follow these requirements:
- Use
jlinkto create a minimized runtime image containing only the necessary modules (java.base,java.sql, and the requiredjavafx.*modules). - Run the
jpackagetool to bundle your JARs and the custom runtime into a native installer (.msior.dmg). - Configure the packaging tool to create a desktop shortcut and an application icon.
- Verify the distribution by moving the resulting installer to a different directory (or a different machine) that does not have a JDK installed, and ensure the application launches successfully.
There are no comments for now.