B4J Tutorial Creating Linux DEB-package for GUI B4J app

This tutorial is made for Linux JDK14 set Java + JavaFX for B4J practically, AI suggestions were partially OK only.
The .jar app file is compiled at Windows developer host.


Very important part is the starting line of the GUI app under Linux, here the correct starting line after the DEB-package is already installed:

/usr/lib/myb4japp/jdk14/bin/java -jar --module-path /usr/lib/myb4japp/jdk14/javafx/lib --add-modules ALL-MODULE-PATH -Djdk.gtk.version=2 --add-opens javafx.controls/com.sun.javafx.scene.control.skin=ALL-UNNAMED /usr/bin/myb4japp.jar
But more correctly to include into the starting line all the modules that are generated into "release_java_modules.txt" file by building the standalone package (it depends on the libs of your project) on the target platform Linux via B4J-bridge, like:




----------BONUS:------------
If to modify steps 3, 4, 6, 7 - it's possible to use default Java (downloading during .deb installation) with only distributed needed for you JavaFX.

3. Use as lib only javafx folder
4. Use "Depends: default-jre"
6-7. Use short simpler starting line with just "java" and path to the javafx:

But this variant must be installed by apt command as dpkg command - cannot resolve the dependencies:

sudo apt-get install myb4japp.deb
And the .deb file is 3 times smaller without built-in Java for Linux.
 

Attachments

  • TempDownload.png
    10.5 KB · Views: 358
Last edited:

Theera

Expert
Licensed User
Longtime User
I use jdk 19.0.2 , I can do as the same, or not?
 

peacemaker

Expert
Licensed User
Longtime User
Depends on JavaFX version, as i know the GTK2 driver was removed from JavaFX 18, so setting "-Djdk.gtk.version=2" will not help to avoid critical error.
JavaFX 17 maybe OK, it needs to test.
 
Last edited:

peacemaker

Expert
Licensed User
Longtime User
JavaFX 17 maybe OK
I have tested:

1) with OpenJDK23 + JavaFX23 messages:

WARNING: A command line option tried to select the GTK 2 library, which was removed from JavaFX.
WARNING: The GTK 3 library will be used instead.
(java:18590): Gtk-CRITICAL **: 22:44:11.478: gtk_window_resize: assertion 'height > 0' failed

2) with OpenJDK23 + JavaFX17 is just:

(java:28299): Gtk-CRITICAL **: 22:47:10.416: IA__gtk_window_resize: assertion 'height > 0' failed

But at both variants - app works OK, no crash !
Just at WSL2 of Win10 the sizable windows are not resizable, at separate Linux Mint21 - all is OK as usual, sizeable.
 
Last edited:

peacemaker

Expert
Licensed User
Longtime User
DEB-package for Linux of B4J GIU app can be developed by using ... default Java and any needed for you JavaFX !
I have walked this way with creating .DEB-package with default JRE (v11 for Mint 21) + distributing inside .deb JavaFX17 - and can confirm that B4J app under Linux can work OK.
 
Last edited:

Theera

Expert
Licensed User
Longtime User
DEB-package for Linux of B4J GIU app can be developed by using ... default Java and any needed for you JavaFX !
I have walked this way and confirm that B4J app under Linux can work OK.
Many thanks for your replies.
 

peacemaker

Expert
Licensed User
Longtime User
Сreating a `.deb` package for a server-side non-GUI Java application, using `default-jre-headless` as a dependency and including a systemd service file.

## Preparing the Environment and Package Structure

Install the necessary build tools:

Bash:
sudo apt update
sudo apt install build-essential devscripts debhelper

Create a working directory for your package. Let's name the application `myapp`:

Bash:
mkdir -p ~/dev/myapp/debian
cd ~/dev/myapp

We will build the package structure inside the `debian/` directory. It will be simpler than for a GUI app, but we will add a folder for systemd.

## Creating the Directory Structure

Create the following directories inside `~/dev/myapp/debian`:

Bash:
mkdir -p debian/DEBIAN
mkdir -p debian/usr/bin
mkdir -p debian/usr/share/myapp
mkdir -p debian/lib/systemd/system

* `DEBIAN/` — for package metadata and scripts.
* `usr/bin/` — for the executable launch script.
* `usr/share/myapp/` — for your application's JAR file.
* `lib/systemd/system/` — for the systemd service file.

## Metadata File: `control`

Create the file `~/dev/myapp/debian/DEBIAN/control`:

Bash:
nano debian/DEBIAN/control

Add the following content. Pay attention to the `Depends` field — it specifies the need for a JRE without graphics (`headless`), which is optimal for servers.

INI:
Package: myapp
Version: 1.0.0
Section: utils
Priority: optional
Architecture: all
Depends: default-jre-headless (>= 11)
Maintainer: Your Name <your.email@example.com>
Description: My server-side Java application
 A non-GUI Java application that runs as a system service.
 It processes data and provides API endpoints.

* `default-jre-headless` is a meta-package that always points to the current version of the OpenJDK JRE (without X11) used in the distribution. This guarantees that when your package is installed, the necessary runtime environment will be installed as well.

## Placing Application Files

1. **Copy your JAR file** to the target directory:
Bash:
    cp ~/path/to/your/compiled-app.jar debian/usr/share/myapp/myapp.jar
Replace `~/path/to/your/compiled-app.jar` with the actual path.

2. **Create an executable script** for convenient command-line launching (while the service is the main focus, this is useful for debugging):
Bash:
    nano debian/usr/bin/myapp
Script content:
Bash:
    #!/bin/sh
    # Using exec is important for correct signal handling by systemd
    exec java -jar /usr/share/myapp/myapp.jar "$@"
* This script simply calls the JAR with the system Java.
* The `exec` flag replaces the shell process with the Java process, which is crucial for proper signal handling from systemd.

3. **Make the script executable**:
Bash:
    chmod +x debian/usr/bin/myapp

## Creating the systemd Unit File

This is the key difference for a server application. Create the service file `~/dev/myapp/debian/lib/systemd/system/myapp.service`:

Bash:
nano debian/lib/systemd/system/myapp.service

Add the following content (adapt it to your needs):

INI:
[Unit]
Description=My Java Server Application
After=network.target syslog.target
Wants=network.target

[Service]
Type=simple
User=myapp
Group=myapp

# Successful exit code for SIGTERM (143 = 128 + 15)
SuccessExitStatus=143

WorkingDirectory=/usr/share/myapp
Environment="JAVA_HOME=/usr/lib/jvm/default-java"
ExecStart=/usr/bin/myapp
ExecStop=/bin/kill -15 $MAINPID
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

* `After=network.target` — ensures the network is available before startup.
* `Type=simple` — suitable as the process runs directly.
* `User/Group` — **highly recommended** to create a dedicated system user to run the application (not as root). We will add its creation later.
* `SuccessExitStatus=143` — tells systemd that exit code 143 (SIGTERM) is a normal termination, not an error.
* `ExecStart` — points to our script in `/usr/bin`.
* `Restart=on-failure` — automatically restart the application in case of a crash.

## `postinst` and `prerm` Scripts

For systemd to work correctly and to create a user, we need scripts that run during package installation and removal.

1. **Create `postinst` (actions after installation)**:
Bash:
    nano debian/DEBIAN/postinst
Content:
Bash:
    #!/bin/sh
    set -e

    # Create system user if it doesn't exist
    if ! getent passwd myapp > /dev/null; then
        adduser --system --group --no-create-home --home /usr/share/myapp myapp
    fi

    # Set ownership of the application directory
    chown -R myapp:myapp /usr/share/myapp

    # Reload systemd so it knows about the new unit
    systemctl daemon-reload || true

    # Automatic service start (recommended, but can be left for the admin to decide)
    # systemctl enable myapp.service || true
    # systemctl start myapp.service || true

    # Display information for the user
    echo "---------------------------------------------------------------------"
    echo "myapp service installed."
    echo "To start it manually: sudo systemctl start myapp"
    echo "To enable auto-start: sudo systemctl enable myapp"
    echo "---------------------------------------------------------------------"

    exit 0
* The script creates a system user `myapp`, which improves security.
* It performs `daemon-reload` so systemd sees the new or updated unit file.
* In this example, we do not start the service automatically, but instead give instructions to the administrator. This is a safer approach.

2. **Create `prerm` (actions before removal)**:
Bash:
    nano debian/DEBIAN/prerm
Content:
Bash:
    #!/bin/sh
    set -e

    # Stop the service when the package is removed
    if [ -f /lib/systemd/system/myapp.service ]; then
        systemctl stop myapp.service || true
        systemctl disable myapp.service || true
    fi

    exit 0

3. **Make both scripts executable**:
Bash:
    chmod +x debian/DEBIAN/postinst debian/DEBIAN/prerm

## Building the Package

Go to the parent directory (`~/dev/myapp`) and run the build command:

Bash:
cd ~/dev/myapp
dpkg-deb --build debian .

After execution, the file `myapp_1.0.0_all.deb` will appear in the current directory.

Check the package contents:
Bash:
dpkg --contents myapp_1.0.0_all.deb
And its metadata:
Bash:
dpkg --info myapp_1.0.0_all.deb

## Installation and Verification

1. **Install the package** (root privileges required):
Bash:
    sudo dpkg -i myapp_1.0.0_all.deb
If there are errors about missing dependencies (e.g., `default-jre-headless` not installed), run:
Bash:
    sudo apt-get install -f
This will install any required dependencies.

2. **Check the service status**:
Bash:
    sudo systemctl status myapp.service
(It should be inactive at this point, as we didn't start it automatically).

3. **Start the service**:
Bash:
    sudo systemctl start myapp.service
    sudo systemctl status myapp.service

4. **Enable auto-start** (if needed):
Bash:
    sudo systemctl enable myapp.service

## Final Package Structure

For clarity, the complete structure of your build directory:

 
Last edited:
Cookies are required to use this site. You must accept them to continue using the site. Learn more…