Slide 1

Slide 1 text

Javaアプリケーションの配布とパッケージング STORES 株式会社 VPoE @hogelog (Sunao Komuro) JJUG CCC 2025 Spring (2025/6/7)

Slide 2

Slide 2 text

自己紹介 @hogelog, 小室 直 (Komuro Sunao) STORES 株式会社 VP of Engineering 組織や技術を考えたり決めたり開発に手を出したり。 一番続けている趣味は結局プログラミング。

Slide 3

Slide 3 text

アジェンダ 1. アプリケーション配布の課題 2. CLIツールのパッケージング 3. GUIアプリケーションのパッケージング 4. パッケージングとリリースの自動化

Slide 4

Slide 4 text

サンプルリポジトリ サンプルコード ビルド設定 GitHub Actions設定 https://github.com/hogelog/java-app-packaging

Slide 5

Slide 5 text

アプリケーション配布の課題

Slide 6

Slide 6 text

アプリケーション配布の課題 どの言語でも直面する困難 ランタイム依存: ユーザーのマシンに必要なランタイムがインストールされている か? プラットフォーム対応: Windows、macOS、Linuxで異なるビルドが必要 インストール体験: ユーザーにとって簡単で安全なインストール方法は?

Slide 7

Slide 7 text

Javaのパッケージングツール GraalVM Native Image 対象: CLIツール 特徴: 単一実行ファイル、高速起動、省メモリ jpackage 対象: GUIアプリケーション 特徴: ネイティブインストーラー、JRE同梱

Slide 8

Slide 8 text

CLIツールのパッケージング GraalVM Native Imageを使ったCLIツールのパッケージング

Slide 9

Slide 9 text

GraalVM Native Imageとは Javaアプリケーションをネイティブバイナリにコンパイル 起動が高速 メモリ使用量が少ない 依存関係が少ない

Slide 10

Slide 10 text

実践例: jlsコマンド ※ 例外処理は省略 native-image/src/main/java/com/example/LsCommand.java package com.example; import java.io.File; public class LsCommand { public static void main(String[] args) { String path = args.length > 0 ? args[0] : "."; File directory = new File(path); File[] files = directory.listFiles(); for (File file : files) { System.out.println(file.getName()); } } }

Slide 11

Slide 11 text

Gradleでの設定 ./gradlew nativeCompile でビルド native-image/build.gradle plugins { id 'java' id 'org.graalvm.buildtools.native' version '0.10.6' } graalvmNative { binaries { main { imageName = 'jls' mainClass = 'com.example.LsCommand' buildArgs.addAll('--no-fallback') javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(24) vendor = JvmVendorSpec.matching("Oracle") } } } }

Slide 12

Slide 12 text

パフォーマンス比較 Native Image JVM 実行時間 9ms ⚡️ 290ms バイナリサイズ 6.3MB 📦 - JVM依存 なし ✅ あり 比較コマンド hyperfine -- './build/native/nativeCompile/jls'` hyperfine -- 'java -cp build/libs/native-image-1.0-SNAPSHOT.jar com.example.LsCommand'

Slide 13

Slide 13 text

GUIアプリケーションのパッケージング jpackageを使ったGUIアプリケーションのパッケージング

Slide 14

Slide 14 text

実践例: SimpleNotepad jpackage/src/main/java/com/example/SimpleNotepad.java public class SimpleNotepad extends JFrame { private JTextArea textArea; public SimpleNotepad() { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); setTitle("Text Viewer"); setSize(600, 400); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); textArea = new JTextArea(); textArea.setEditable(false); add(new JScrollPane(textArea)); ... } ... }

Slide 15

Slide 15 text

Java Platform Module System (JPMS) jpackage/src/main/java/module-info.java module com.example.simplenotepad { requires java.desktop; // Swing/AWT 用 exports com.example; // メインクラスをエクスポート }

Slide 16

Slide 16 text

jpackageの使い方 直接コマンドで実行 jpackage \ --module-path build/libs \ --module com.example.simplenotepad/com.example.SimpleNotepad \ --name "SimpleNotepad" \ --app-version 1.0.0 \ --vendor "Example Inc."

Slide 17

Slide 17 text

Gradleでの設定 ./gradlew jpackage でビルド jpackage/build.gradle plugins { id 'java' id 'application' id 'org.beryx.jlink' version '3.1.1' } application { mainClass = 'com.example.SimpleNotepad' mainModule = 'com.example.simplenotepad' } jlink { jpackage { imageName = 'SimpleNotepad' installerName = 'SimpleNotepad' appVersion = '1.0.0' vendor = 'Example Inc.' } }

Slide 18

Slide 18 text

生成されるインストーラー ファイル形式 特徴 Windows 🪟 SimpleNotepad.msi ショートカット自動作成 macOS 🍎 SimpleNotepad.dmg ドラッグ&ドロップ Linux 🐧 simplenotepad.deb dpkgでインストール 外部依存: なし (JRE同梱でJavaインストール不要 ✅)

Slide 19

Slide 19 text

パッケージングとリリースの自動化 GitHub Actionsを使ったパッケージングとリリース

Slide 20

Slide 20 text

自動化のメリット クロスプラットフォームビルド 効率性 ⚡: タグ一つで全プラットフォーム 可植性 📦: ローカル環境に依存しない 今回例示するリリースフロー # 1. タグ作成・プッシュ git tag v1.0.0 && git push origin v1.0.0 # 2. 自動ビルド開始 # GitHub Actions が起動 # 3. 成果物配布 # GitHub Releases に自動アップロード

Slide 21

Slide 21 text

Native Image用GitHub Actions .github/workflows/release-native-image.yml on: push: branches: ['main'] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # v1.3.3 with: java-version: '24' distribution: 'graalvm' - uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - run: ./gradlew nativeCompile working-directory: native-image

Slide 22

Slide 22 text

jpackage用GitHub Actions .github/workflows/release-jpackage.yml on: push: branches: ['main'] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, windows-latest] steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'oracle' java-version: '24' - uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - run: ./gradlew jpackage working-directory: jpackage

Slide 23

Slide 23 text

自動ビルドの成果物 CLI jls-linux , jls-macos , jls-windows.exe 単体実行ファイル GUI SimpleNotepad.msi , SimpleNotepad.dmg インストーラー形式

Slide 24

Slide 24 text

⚠️ セキュリティの課題と対策 各OSのセキュリティ機能 macOS: Gatekeeperが「開発元が未確認」でブロック Windows: SmartScreenが「このアプリは実行できません」でブロック 解決策 それぞれ適切な方法でコード署名を行うのが一番の正攻法 ただし費用はかかる 💰️💰️💰️️️ macOS向けCLIならば工夫で警告回避可能 (Homebrewなどと同様に)quarantine 属性がつかないインストール方法

Slide 25

Slide 25 text

まとめ CLIツール: GraalVM Native Imageで高速起動・低メモリ GUIアプリ: jpackageでネイティブインストーラー GitHub Actions: クロスプラットフォーム自動ビルド コード署名: 安全な配布のため検討を サンプルリポジトリ: https://github.com/hogelog/java-app-packaging