google-site-verification: google61fe8ba583a51912.html
JLink for Custom Runtime Creation in Java

Comprehensive guide to using jlink for creating custom Java runtime images with only the necessary modules.

1. JLink Overview and Basic Usage

What is JLink?

# JLink creates customized runtime images containing only required modules # Benefits: # - Smaller deployment size # - Improved startup time # - Reduced attack surface # - Self-contained applications

Basic JLink Command

# Basic syntax jlink --module-path <path> --add-modules <modules> --output <runtime-image> # Example: Create minimal runtime with java.base only jlink --module-path $JAVA_HOME/jmods \ --add-modules java.base \ --output minimal-runtime # Check the created runtime ./minimal-runtime/bin/java --version

2. Module System Setup for JLink

Sample Modular Application Structure

// module-info.java for main application module com.enterprise.app { requires java.base; requires java.sql; requires java.logging; requires java.net.http; requires com.utils; exports com.enterprise.app.api; uses com.enterprise.app.spi.ServiceProvider; } // Main application class package com.enterprise.app; public class MainApp { public static void main(String[] args) { System.out.println("Enterprise Application Started"); // Application logic here } } // Utility module module com.utils { requires java.base; requires java.logging; exports com.utils.string; exports com.utils.validation; } // Utility classes package com.utils.string; public class StringUtils { public static boolean isEmpty(String str) { return str == null || str.trim().isEmpty(); } public static String capitalize(String str) { if (isEmpty(str)) return str; return str.substring(0, 1).toUpperCase() + str.substring(1); } }

Service Provider Module

// Service provider interface module module com.enterprise.app.spi { exports com.enterprise.app.spi; } // Service interface package com.enterprise.app.spi; public interface ServiceProvider { String getName(); void execute(); } // Service implementation module module com.enterprise.app.providers { requires com.enterprise.app.spi; requires java.logging; provides com.enterprise.app.spi.ServiceProvider with com.enterprise.app.providers.DefaultServiceProvider; } // Service implementation package com.enterprise.app.providers; import com.enterprise.app.spi.ServiceProvider; import java.util.logging.Logger; public class DefaultServiceProvider implements ServiceProvider { private static final Logger logger = Logger.getLogger(DefaultServiceProvider.class.getName()); @Override public String getName() { return "DefaultServiceProvider"; } @Override public void execute() { logger.info("Default service provider executing..."); // Implementation logic } }

3. Basic JLink Examples

Simple Application Runtime

# Create runtime for simple application jlink --module-path "target/modules:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --output enterprise-runtime # The generated runtime structure: # enterprise-runtime/ # ├── bin/ # │ ├── java # │ └── keytool # ├── conf/ # ├── include/ # ├── legal/ # ├── lib/ # └── release

Including Multiple Modules

# Explicitly list all required modules jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app,com.utils,java.sql,java.logging,java.net.http \ --output custom-runtime # Run the application with custom runtime ./custom-runtime/bin/java -m com.enterprise.app/com.enterprise.app.MainApp

4. Advanced JLink Features

Compression Options

# Different compression levels jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --compress=2 \ # 0=no compression, 1=constant string sharing, 2=ZIP --output compressed-runtime # Compare sizes du -sh minimal-runtime compressed-runtime

Stripping Debug Information

# Remove debug info to reduce size jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --strip-debug \ --no-header-files \ --no-man-pages \ --output stripped-runtime

Vendor Information

# Add custom vendor information jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --vendor "Acme Corporation" \ --version "1.0.0" \ --output branded-runtime

5. Plugin System with JLink

Plugin Architecture Setup

// Plugin module definition module com.enterprise.plugin.api { exports com.enterprise.plugin.api; exports com.enterprise.plugin.api.spi; } // Plugin interface package com.enterprise.plugin.api.spi; public interface Plugin { String getName(); void initialize(); void execute(); void shutdown(); } // Plugin manager in main app module com.enterprise.app { requires java.base; requires java.logging; requires com.enterprise.plugin.api; uses com.enterprise.plugin.api.spi.Plugin; exports com.enterprise.app; } // Plugin manager implementation package com.enterprise.app; import com.enterprise.plugin.api.spi.Plugin; import java.util.ServiceLoader; import java.util.logging.Logger; public class PluginManager { private static final Logger logger = Logger.getLogger(PluginManager.class.getName()); public void loadAndExecutePlugins() { ServiceLoader<Plugin> plugins = ServiceLoader.load(Plugin.class); for (Plugin plugin : plugins) { logger.info("Loading plugin: " + plugin.getName()); try { plugin.initialize(); plugin.execute(); } catch (Exception e) { logger.severe("Plugin failed: " + plugin.getName() + " - " + e.getMessage()); } } } public void shutdownPlugins() { ServiceLoader<Plugin> plugins = ServiceLoader.load(Plugin.class); for (Plugin plugin : plugins) { try { plugin.shutdown(); } catch (Exception e) { logger.warning("Plugin shutdown failed: " + plugin.getName()); } } } }

Sample Plugins

// Database plugin module com.enterprise.plugin.database { requires com.enterprise.plugin.api; requires java.sql; requires java.logging; provides com.enterprise.plugin.api.spi.Plugin with com.enterprise.plugin.database.DatabasePlugin; } package com.enterprise.plugin.database; import com.enterprise.plugin.api.spi.Plugin; import java.sql.*; import java.util.logging.Logger; public class DatabasePlugin implements Plugin { private static final Logger logger = Logger.getLogger(DatabasePlugin.class.getName()); private Connection connection; @Override public String getName() { return "DatabasePlugin"; } @Override public void initialize() { logger.info("Initializing database plugin"); // Initialize database connection } @Override public void execute() { logger.info("Executing database operations"); // Perform database operations } @Override public void shutdown() { logger.info("Shutting down database plugin"); if (connection != null) { try { connection.close(); } catch (SQLException e) { /* ignore */ } } } } // Reporting plugin module com.enterprise.plugin.reporting { requires com.enterprise.plugin.api; requires java.logging; provides com.enterprise.plugin.api.spi.Plugin with com.enterprise.plugin.reporting.ReportingPlugin; } package com.enterprise.plugin.reporting; import com.enterprise.plugin.api.spi.Plugin; import java.util.logging.Logger; public class ReportingPlugin implements Plugin { private static final Logger logger = Logger.getLogger(ReportingPlugin.class.getName()); @Override public String getName() { return "ReportingPlugin"; } @Override public void initialize() { logger.info("Initializing reporting plugin"); } @Override public void execute() { logger.info("Generating reports"); // Generate reports } @Override public void shutdown() { logger.info("Shutting down reporting plugin"); } }

6. JLink with Service Binding

Service-Based Runtime Creation

# JLink automatically includes service providers jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --bind-services \ # Automatically add all service provider modules --output service-runtime

Manual Service Inclusion

# Manually specify service provider modules jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app,com.enterprise.plugin.database,com.enterprise.plugin.reporting \ --output manual-service-runtime

7. Platform-Specific Customization

Windows-Specific Runtime

# Windows-specific optimizations jlink --module-path "mods;$JAVA_HOME/jmods" \ --add-modules com.enterprise.app,java.desktop,java.sql \ --launcher enterprise-app=com.enterprise.app/com.enterprise.app.MainApp \ --output windows-runtime

Linux-Specific Runtime

# Linux-specific configuration jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app,java.management,java.logging \ --launcher enterprise-app=com.enterprise.app/com.enterprise.app.MainApp \ --output linux-runtime

8. Launcher Creation

Custom Launchers

# Create custom launcher command jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --launcher start-app=com.enterprise.app/com.enterprise.app.MainApp \ --launcher admin-tool=com.enterprise.app/com.enterprise.app.AdminTool \ --output launcher-runtime # Usage: ./launcher-runtime/bin/start-app ./launcher-runtime/bin/admin-tool

Launcher with Default Arguments

# Create launcher with default JVM arguments jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --launcher "enterprise-app=com.enterprise.app/com.enterprise.app.MainApp -Xms128m -Xmx512m -Dapp.mode=production" \ --output production-runtime

9. Advanced JLink Scenarios

Multi-Module Complex Application

// Complex application with multiple modules module com.enterprise.core { requires java.base; requires java.logging; requires transitive com.utils; exports com.enterprise.core.api; exports com.enterprise.core.spi; uses com.enterprise.core.spi.ExtensionPoint; } module com.enterprise.persistence { requires transitive com.enterprise.core; requires java.sql; requires java.naming; exports com.enterprise.persistence.dao; provides com.enterprise.core.spi.ExtensionPoint with com.enterprise.persistence.DatabaseExtension; } module com.enterprise.web { requires transitive com.enterprise.core; requires java.net.http; requires jdk.httpserver; exports com.enterprise.web.controller; provides com.enterprise.core.spi.ExtensionPoint with com.enterprise.web.WebExtension; } module com.enterprise.main { requires com.enterprise.core; requires com.enterprise.persistence; requires com.enterprise.web; exports com.enterprise.main; }

Building Complex Runtime

# Build runtime for complex application jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.main \ --bind-services \ --launcher enterprise=com.enterprise.main/com.enterprise.main.Main \ --compress=2 \ --strip-debug \ --no-header-files \ --no-man-pages \ --vendor "Enterprise Solutions Inc." \ --version "2.1.0" \ --output enterprise-runtime

10. JLink with Third-Party Dependencies

Including External JARs as Automatic Modules

# Convert JARs to automatic modules and include in runtime # Step 1: Copy JARs to mods directory cp lib/*.jar mods/ # Step 2: Build with automatic modules jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app,com.fasterxml.jackson.databind,org.slf4j \ --output with-deps-runtime

Module Path Configuration

# Complex module path with multiple locations jlink --module-path "application-mods:library-mods:automatic-mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --output complex-runtime

11. Optimization Techniques

Module Reduction

# Use jdeps to find required modules jdeps --print-module-deps --ignore-missing-deps \ -m com.enterprise.app \ target/modules/com.enterprise.app.jar # Output: java.base,java.logging,java.sql # Use this output with jlink jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules java.base,java.logging,java.sql,com.enterprise.app \ --output optimized-runtime

Class Data Sharing

# Generate class data sharing archive for faster startup jlink --module-path "mods:$JAVA_HOME/jmods" \ --add-modules com.enterprise.app \ --class-sharing \ --output cds-runtime

12. Verification and Testing

Runtime Verification

# Verify the created runtime ./enterprise-runtime/bin/java --list-modules # Check module dependencies ./enterprise-runtime/bin/java -m com.enterprise.app/com.enterprise.app.MainApp --validate-modules # Test service loading ./enterprise-runtime/bin/java -m com.enterprise.app/com.enterprise.app.PluginManager

Size Analysis

# Analyze runtime size du -sh enterprise-runtime/ find enterprise-runtime/ -name "*.so" -o -name "*.dll" | wc -l find enterprise-runtime/ -name "*.class" | wc -l # Compare with full JDK du -sh $JAVA_HOME

13. Maven Integration

Maven Configuration for JLink

<!-- pom.xml - Maven JLink plugin configuration --> <project> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jlink-plugin</artifactId> <version>3.1.0</version> <configuration> <modulePath>${project.build.directory}/modules</modulePath> <addModules>com.enterprise.app</addModules> <output>${project.build.directory}/runtime</output> <launcher>enterprise-app=com.enterprise.app/com.enterprise.app.MainApp</launcher> <compress>2</compress> <stripDebug>true</stripDebug> <noHeaderFiles>true</noHeaderFiles> <noManPages>true</noManPages> <vendor>Acme Corporation</vendor> <version>${project.version}</version> <jlinkVerbose>true</jlinkVerbose> </configuration> <executions> <execution> <id>create-runtime</id> <phase>package</phase> <goals> <goal>jlink</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> </project>

Multi-Module Maven Project

<!-- Parent POM for multi-module project --> <project> <modules> <module>core</module> <module>persistence</module> <module>web</module> <module>main-app</module> </modules> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jlink-plugin</artifactId> <version>3.1.0</version> <configuration> <addModules>com.enterprise.main</addModules> <launcher>app=com.enterprise.main/com.enterprise.main.Main</launcher> </configuration> </plugin> </plugins> </build> </project>

14. Docker Integration

Dockerfile for JLink Runtime

# Dockerfile for JLink-based application FROM debian:11-slim # Install minimal dependencies RUN apt-get update && apt-get install -y \ libfreetype6 \ fontconfig \ && rm -rf /var/lib/apt/lists/* # Create non-root user RUN groupadd -r appuser && useradd -r -g appuser appuser # Copy custom runtime COPY --chown=appuser:appuser enterprise-runtime /opt/app/runtime # Switch to non-root user USER appuser # Set working directory WORKDIR /home/appuser # Copy application data COPY --chown=appuser:appuser app-data ./data # Use custom runtime ENV PATH="/opt/app/runtime/bin:${PATH}" # Launch application CMD ["enterprise-app"]

Multi-stage Docker Build

# Multi-stage Docker build FROM maven:3.8-openjdk-17 as builder # Copy source code WORKDIR /app COPY . . # Build application and create runtime RUN mvn clean package jlink:jlink # Runtime stage FROM debian:11-slim # Install minimal dependencies RUN apt-get update && apt-get install -y \ libfreetype6 \ && rm -rf /var/lib/apt/lists/* # Copy runtime from builder stage COPY --from=builder /app/target/runtime /opt/app/runtime # Set entry point ENTRYPOINT ["/opt/app/runtime/bin/enterprise-app"]

Key Benefits Summary

FeatureBenefitExample
Smaller SizeReduced deployment size40MB vs 200MB+
Faster StartupOptimized class loading2x faster startup
Improved SecurityReduced attack surfaceOnly required modules
Self-containedNo external JDK neededSingle deployment unit
Custom LaunchersSimplified executionCustom command names

JLink enables creating highly optimized, minimal Java runtimes tailored to specific applications, significantly improving deployment efficiency and runtime performance.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper