Java Modules (JPMS) & Modern Packaging

Java Platform Module System ও আধুনিক packaging

Read: ~35 min Advanced 5 practice problems Packaging tools

1. The Problem JPMS Solves

Before Java 9, the JDK itself was one giant rt.jar — every Android app, every Hello-World, shipped the whole thing. Worse, packages could be accessed from anywhere. Java 9 introduced the Java Platform Module System (JPMS): a formal way to split the JDK and your own code into modules that declare exactly what they export and what they require.

Java 9-এর আগে পুরো JDK একটি বিশাল rt.jar ছিল — ছোট Hello-World অ্যাপেও পুরোটাই যেতো। Package যেকোনো জায়গা থেকে access করা যেতো। Java 9-এ এলো Java Platform Module System (JPMS) — JDK এবং আপনার কোড module-এ ভাগ করা, যেখানে প্রতিটি module স্পষ্টভাবে বলে দেয় কী export করে এবং কী require করে।

2. Anatomy of module-info.java

Every JPMS module is identified by a single file in its root: module-info.java. The file is short and declarative — it lists what the module exports, what it requires, and the services it uses or provides.

প্রতিটি JPMS module-এর root-এ একটি ফাইল থাকে: module-info.java। এটি ছোট ও declarative — কোন package export করবে, কোন module require করবে, এবং কোন service use বা provide করবে।
module-info.java
// Module: com.abcltech.payments
module com.abcltech.payments {

    // Depends on two other modules
    requires java.net.http;
    requires transitive com.abcltech.core;

    // Public API — only these packages are visible to others
    exports com.abcltech.payments.api;

    // Internal — not exported (but opened for reflection)
    opens com.abcltech.payments.internal to com.fasterxml.jackson.databind;

    // Service consumer / producer
    uses     com.abcltech.payments.spi.PaymentProvider;
    provides com.abcltech.payments.spi.PaymentProvider
        with  com.abcltech.payments.bkash.BkashProvider;
}
KeywordMeansবাংলায়
requiresThis module depends on that module.এই module ঐ module-এর উপর নির্ভরশীল।
requires transitivePasses the dep through to consumers.পরবর্তী consumer-এও dependency পৌঁছায়।
exportsMakes a package public API.একটি package-কে public API করে।
opensAllows deep reflection (for frameworks).Reflection access দেয় (framework-এর জন্য)।
uses / providesService-loader SPI consumer / producer.ServiceLoader-এর SPI consumer / producer।

3. Modules in a Real App

App Modularity · requires & exports com.abcltech.core exports: util, model com.abcltech.payments requires core · exports api com.abcltech.web requires payments JDK platform modules · java.base · java.net.http · … Figure 46.1 — তিনটি module এবং JDK platform module-এর মধ্যে requires সম্পর্ক।
একটি real application-এ আপনার module-গুলো ছোট-ছোট অংশে ভাগ — core, payments, web। প্রতিটি module-এর dependency স্পষ্ট; কোনো অজ্ঝাত package classpath থেকে লিক করতে পারে না।

4. jlink — Ship Just the JDK You Need

Once your app is modular, jlink can generate a custom JDK runtime containing only the platform modules you actually use. A Hello-World runtime can drop from ~180 MB down to ~40 MB.

আপনার app modular হলে jlink একটি custom JDK runtime বানিয়ে দেয় — শুধু যা যা লাগছে। Hello-World-এর জন্য পুরো JDK (~১৮০ MB) না নিয়ে ছোট ~৪০ MB runtime-ই যথেষ্ট।
build.sh
# 1. Compile into a modular JAR
javac -d out --module-source-path src $(find src -name "*.java")
jar --create --file payments.jar -C out/com.abcltech.payments .

# 2. jlink — slim, custom runtime containing only our modules
jlink \
  --module-path $JAVA_HOME/jmods:. \
  --add-modules com.abcltech.payments \
  --launcher run=com.abcltech.payments/com.abcltech.payments.Main \
  --compress=2 --no-header-files --no-man-pages \
  --output build/slim-runtime

# 3. Run it — no separate JDK needed on target
./build/slim-runtime/bin/run

4.1 jpackage — Native Installers

jpackage (since Java 14) wraps a jlink runtime + your app into a native installer: .msi on Windows, .dmg on macOS, .deb/.rpm on Linux. Users do not need a pre-installed JDK.

jpackage (Java 14+) jlink-এর runtime এবং আপনার app একসাথে করে native installer তৈরি করে — Windows-এ .msi, macOS-এ .dmg, Linux-এ .deb/.rpm। User-এর আলাদা JDK install করতে হয় না।

5. A Runnable Peek at Module Info

Even in a non-modular sandbox, the Module API works. Every class belongs to a module — when you are not using JPMS, it is the "unnamed module". Run this to see which modules your classes live in.

JPMS ব্যবহার না করলেও Java 9+ runtime-এ প্রতিটি class-এর একটি module থাকে — সাধারণ সময়ে unnamed module। নিচের কোডটি module inspect করে দেখায়।
Main.java
class Main {
    public static void main(String[] args) {
        Module m1 = String.class.getModule();
        Module m2 = Main.class.getModule();
        Module m3 = java.util.ArrayList.class.getModule();

        System.out.println("String lives in:    " + m1);
        System.out.println("Main lives in:      " + (m2.isNamed() ? m2 : "(unnamed module)"));
        System.out.println("ArrayList lives in: " + m3);

        System.out.println("\nJDK reports " + ModuleLayer.boot().modules().size() + " boot-layer modules.");
    }
}

6. The Honest State of Modules

✅ Where JPMS Shines

  • Desktop apps shipped with jlink/jpackage
  • Large corporate apps that want strict encapsulation
  • Custom embedded JDK builds (IoT, edge)
  • Publishing libraries with a clear public surface

⚠️ Where It Lags

  • Many older libraries still aren't modular
  • Automatic-module naming can be fragile
  • Spring Boot ecosystem leans on classpath, not modulepath
  • Team ergonomics — steeper learning curve
Many production Java shops — especially in Bangladesh's Spring-heavy fintech scene — still build on the classpath and reach for JPMS only when they want jlink/jpackage benefits for a client-facing installer. That's a pragmatic choice, not a failure of the module system.

বাংলাদেশের Spring-নির্ভর fintech-এ অনেকেই এখনো classpath-এ কাজ করে, আর শুধু jlink/jpackage লাগলেই JPMS-এ যায়। এটা একটি বাস্তব সিদ্ধান্ত।

7. Vocabulary

TermMeaningবাংলায়
JPMSJava Platform Module System, since Java 9.Java 9-এ আসা module system।
Module descriptorThe module-info.java file.module-info.java ফাইল।
Automatic moduleA plain JAR used as a module — name derived from filename.Non-modular JAR-কে auto module হিসেবে গণ্য।
Unnamed moduleEverything on the classpath.Classpath-এ থাকা সব কোড।
jlinkCreates a custom JDK runtime image.Custom JDK runtime তৈরি করে।
jpackageCreates native installers.Native installer বানায় (.msi/.dmg/.deb)।

8. Practice Problems

  1. Write a module-info.java for a hypothetical com.abcltech.utils module that depends on java.logging and exports a package com.abcltech.utils.text.
    একটি module-info.java লিখুন — com.abcltech.utils module, java.logging-এর উপর নির্ভরশীল, com.abcltech.utils.text export করে।
    ✨ Show Answer
    module-info.java
    module com.abcltech.utils {
        requires java.logging;
        exports com.abcltech.utils.text;
    }
  2. Write a Java program that prints java.base, java.sql, and your own class's module names.
    একটি Java program লিখুন যা java.base, java.sql ও আপনার নিজের class-এর module print করবে।
    ✨ Show Answer
    Main.java
    class Main {
        public static void main(String[] args) throws Exception {
            System.out.println("String     -> " + String.class.getModule());
            System.out.println("Connection -> " + Class.forName("java.sql.Connection").getModule());
            System.out.println("Main       -> " + Main.class.getModule());
        }
    }
  3. Explain the difference between exports and opens.
    exports এবং opens-এর পার্থক্য ব্যাখ্যা করুন।
    ✨ Show Answer

    Answer: exports makes the package visible at compile time AND for normal runtime access — other modules can import and call its public types. opens additionally permits deep reflection (including private members) at runtime, but does not permit compile-time imports. Frameworks like Hibernate and Jackson need opens to read your private fields via reflection; ordinary consumers only need exports.

  4. You're shipping a small JavaFX desktop app to Bangladeshi users who do not have Java installed. Which tool creates the .msi installer?
    একটি JavaFX desktop app ব্যবহারকারীদের কাছে পাঠাতে চান — তাদের কাছে Java নেই। কোন টুল দিয়ে .msi installer বানাবেন?
    ✨ Show Answer

    Answer: jpackage. It bundles your application, the JavaFX modules, and a slimmed-down JDK produced by jlink into a single native installer. The end user just double-clicks the .msi — no separate JDK, no setup steps.

  5. Write a runnable program that lists every "java.*" module currently loaded in the boot layer and counts them.
    একটি program লিখুন যা boot layer-এ লোড হওয়া সব java.* module তালিকা করবে এবং সংখ্যা গুণে দেখাবে।
    ✨ Show Answer
    Main.java
    class Main {
        public static void main(String[] args) {
            long n = ModuleLayer.boot().modules().stream()
                .map(Module::getName)
                .filter(name -> name.startsWith("java."))
                .peek(System.out::println)
                .count();
            System.out.println("\nTotal java.* modules: " + n);
        }
    }

Summary — Module 46

JPMS gives Java a real module system — explicit requires, explicit exports, and strong encapsulation. It pairs beautifully with jlink (custom runtimes) and jpackage (native installers). Adoption is uneven, but for desktop apps, libraries, and embedded JDK builds the story is excellent.

JPMS Java-কে সত্যিকারের module system দিয়েছে — স্পষ্ট requires, স্পষ্ট exports ও কঠোর encapsulation। jlink (custom runtime) ও jpackage (native installer)-এর সাথে চমৎকার। Adoption অসম হলেও, desktop app, library ও embedded JDK build-এ দারুণ কাজ করে।

Next Module → Spring Boot Basics — DI, Controllers, Services।