B4J Tutorial Integrated B4JPackager11 - The simple way to distribute standalone UI apps

Status
Not open for further replies.
B4JPackager11 is a utility written in B4J that uses the underlying Java tools to create a standalone package that doesn't depend on any other software being installed.
It works with OpenJDK 11 and OpenJDK 14.
Starting from B4J v8.30 it is integrated in the IDE and available under Project - Build Standalone Package.
External tool: https://www.b4x.com/android/forum/t...lest-way-to-distribute-ui-apps.99835/#content

Example:

Shw8yVDjRi.gif


The output of this tool looks like this:

explorer_fa8Z3lAQP3.png


You need to distribute the executable together with the 4 folders.
The run_debug.bat batch file is useful to test the program and see the logs.

An Inno Script template is created in the parent folder. You can use it together with Inno Script to build a single file installer.

The integrated packager creates a Windows package. You can however use the external tool with the generated json file (in the project folder) to create Linux and Mac packages.

The packager supports all kinds of settings. You can set them with the new #PackagerProperty attribute.

For example to set the icon file, assuming that the ico file is in the Files tab and is named turtle.ico:
B4X:
#PackagerProperty: IconFile = ..\Files\turtle.ico
Also set the executable name:
B4X:
#PackagerProperty: IconFile = ..\Files\turtle.ico
#PackagerProperty: ExeName = Turtle
Note that this will set the executable icon. Set the form icon with the designer. The form icon should be a regular image file (png, jpg, ...).

Tips and special cases

  1. If using jPOI library then you must reference XLUtils.
  2. If using WebView or HtmlEditor add:
    B4X:
    #PackagerProperty: IncludedModules = javafx.web
  3. If using jGoogleMaps add:
    B4X:
    #PackagerProperty: IncludedModules = javafx.web
    #PackagerProperty: AdditionalModuleInfoString = exports com.lynden.gmapsfx.javascript.event;
    There is an issue with Java 14 and Google Maps. Use Java 11 for now if using jGoogleMaps.
  4. You can use #CustomBuildAction with the new After Packager step to copy files after the package is built. The default target folder should be: temp\build\bin\
  5. If using jSerial put the attached jssc.dll file in the project folder and add:
    B4X:
    #CustomBuildAction: After Packager, %WINDIR%\System32\robocopy.exe, ..\ temp\build\bin\ jssc.dll
    Note that it is a Windows 64 bit dll.
    MacSigner instructions: https://www.b4x.com/android/forum/threads/mac-and-jssc.135823/post-859095
  6. Each PackagerProperty key should appear at most once. So for example if using both WebView and jPOI add: #PackagerProperty: IncludedModules = jdk.charsets, javafx.web
    Starting from B4J 9.00, the same property can appear multiple times and the values will be appended.
  7. If you get an error that looks like: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure, add:
    B4X:
    #PackagerProperty: IncludedModules = jdk.crypto.ec
  8. If using jWebSocketClient then you need to add:
    B4X:
    #PackagerProperty: AdditionalModuleInfoString = uses org.eclipse.jetty.websocket.common.RemoteEndpointFactory;
  9. If you want to copy the generated build to a different folder (C:\Users\H\Downloads\test for example):
    B4X:
    #CustomBuildAction: After Packager, %WINDIR%\System32\robocopy.exe, /MIR temp\build\ C:\Users\H\Downloads\test
    Warning : the destination folder will be deleted recursively.
  10. A json file named packager.json is created in the Objects folder. You can use it to run the external packager tool, for example to build Mac or Linux packages.
  11. Steps to automatically build the setup file with Inno Script:
    • Move the generated InstallerScript-template.iss file to the project folder and rename it to InstallerScript.iss.
    • Edit the script, change the app name, publisher and other fields as needed. You also need to update two paths as explained in post #3.
    • Put the icon file in the project folder.
    • Add this attributes:
      B4X:
      #PackagerProperty: IconFile = ..\turtle.ico
      #CustomBuildAction: After Packager, C:\Program Files (x86)\Inno Setup 6\Compil32.exe, /cc ../InstallerScript.iss
    • Output after choosing Project - Build standalone package:

      explorer_ZR0Ey83790.png

    • Note that the AppId field shouldn't be changed after you distribute your app.
  12. By default, not all of the localization data is added to the package. This can cause for example, the dates to appear in English even when the default locale is non-English. You can add the missing data with:
    B4X:
    #PackagerProperty: IncludedModules = jdk.localedata
    It will add about 9mb to the built package.
    (Don't miss tip #6 if you need to add multiple modules.)
 

Attachments

  • jssc.zip
    38 KB · Views: 973
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User

NikB4x

Member
Licensed User
Longtime User
Hi All,
I used the integrated B4JPackager and also the standalone both for Windows and Linux and it works very well! Thanks to Erel another time for his job!
Just a question, if someone found a solution... how can I deploy only the (few) changes made in the app avoiding to re-deploy the full java runtime everytime?
What are the involved files in the folder tree?
Thank you
 

NikB4x

Member
Licensed User
Longtime User
Hi Erel,
no it does not work, I copied the exe in the bin folder (and also in the root folder of the app) but it does not work in both cases.
 

NikB4x

Member
Licensed User
Longtime User
It seems that only the replacement of the file modules in the lib folder does the job.
It works also on Linux, I haven't tried in OSX yet.
 

NikB4x

Member
Licensed User
Longtime User
Hi,
it seems that only the file modules in the folder build/lib is requested, as in the attached screenshot.
It is useful for the online update of the application, for example.
You only need to download that file from a server prepared for the upgrade.
In my case only 36 MB instead of 113 MB of the deployment of the whole app.

sshot.png
 

CR95

Active Member
Licensed User
Hello,
I would like to create applications with B4J under WINDOWS in order to execute them under LINUX (Ubuntu and Debian).
1°) I understood that this was possible.
I also understood that ORACLE Java was NOT mandatory on the LINUX system. Is that true ?

2°)I followed your instructions for creating my LINUX package but I don't understand
You can however use the external tool with the generated json file (in the project folder) to create Linux and Mac packages
What is the external tool ?
I tried to "execute" the json file with the WIndows "cmd" program but nothing happened.
Could you describe in detail the package creation process for each system (Windows and Linux) ?

3°) In order to understand, what is the main purpose of this json file ? To create an independant package ?

4°) I tried to install my application with the following tutorial :
I follow the process which consist of copying all files of the "build" WINDOWS directory under LINUX. But when I execute (under LINUX) the .exe program, I get an error message saying that there was an error during the load of the archive (translated from French).
After some Web research, I found that this message was sent when we try to install windows executables under Linux.
This last comment only for justifying my first question !

Thanks in advance
 

NoNickName

Member
Licensed User
I followed the instructions, but got the following:

B4X:
C:\Users\Admin\source\Repos\ESP8266\<...>\Objects\temp\build\bin>java.exe @release_java_modules.txt  -m b4j/b4j.example.main
main._appstart (java line: -1)
java.lang.NoClassDefFoundError: com/sun/javafx/scene/control/skin/BehaviorSkinBase
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(Unknown Source)
        at java.base/java.lang.ClassLoader.defineClass(Unknown Source)
        at java.base/java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(Unknown Source)
        at java.base/jdk.internal.loader.BuiltinClassLoader.findClassInModuleOrNull(Unknown Source)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(Unknown Source)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
        at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
        at b4j/org.controlsfx.control.PropertySheet.createDefaultSkin(Unknown Source)
        at javafx.controls/javafx.scene.control.Control.doProcessCSS(Unknown Source)
        at javafx.controls/javafx.scene.control.Control.access$000(Unknown Source)
        at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Unknown Source)
        at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(Unknown Source)
        at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(Unknown Source)
        at javafx.graphics/javafx.scene.Parent.doProcessCSS(Unknown Source)
        at javafx.graphics/javafx.scene.Parent.access$400(Unknown Source)
        at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Unknown Source)
        at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(Unknown Source)
        at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(Unknown Source)
        at javafx.graphics/javafx.scene.Parent.doProcessCSS(Unknown Source)
        at javafx.graphics/javafx.scene.Parent.access$400(Unknown Source)
        at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Unknown Source)
        at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(Unknown Source)
        at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(Unknown Source)
        at javafx.graphics/javafx.scene.Node.processCSS(Unknown Source)
        at javafx.graphics/javafx.scene.Scene.doCSSPass(Unknown Source)
        at javafx.graphics/javafx.scene.Scene.preferredSize(Unknown Source)
        at javafx.graphics/javafx.scene.Scene$2.preferredSize(Unknown Source)
        at javafx.graphics/com.sun.javafx.scene.SceneHelper.preferredSize(Unknown Source)
        at javafx.graphics/javafx.stage.Window$12.invalidated(Unknown Source)
        at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(Unknown Source)
        at javafx.base/javafx.beans.property.BooleanPropertyBase.set(Unknown Source)
        at javafx.graphics/javafx.stage.Window.setShowing(Unknown Source)
        at javafx.graphics/javafx.stage.Window.show(Unknown Source)
        at javafx.graphics/javafx.stage.Stage.show(Unknown Source)
        at b4j/anywheresoftware.b4j.objects.Form.Show(Unknown Source)
        at b4j/b4j.example.main._appstart(Unknown Source)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at b4j/anywheresoftware.b4a.BA.raiseEvent2(Unknown Source)
        at b4j/anywheresoftware.b4a.BA.raiseEvent(Unknown Source)
        at b4j/b4j.example.main.start(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(Unknown Source)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(Unknown Source)
        at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(Unknown Source)
        at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.sun.javafx.scene.control.skin.BehaviorSkinBase
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
        at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
        ... 55 more


C:\Users\Admin\source\Repos\ESP8266\<...>\Objects\temp\build\bin>pause
Premere un tasto per continuare . . .

help, please
 

NikB4x

Member
Licensed User
Longtime User
@CR95

Hi, you have to do the build on Linux, you can't copy all the files made from windows.
You have to copy only the file .jar of the app you compiled under windows in a unix dir.
In the same dir you have to copy also the Java runtime FOR LINUX, you can find it here: https://b4xfiles-4c17.kxcdn.com/b4j/linux_jdk-11.0.1.zip
Each SO has the right Java runtime.
So, for example, you can create a dir under Linux (it's better if it is in your home dir) and copy inside of it:

B4JPackager11.jar ---> the tool that makes the build
jdk-11.0.1 ---> a dir containing the java runtime unzipped from linux_jdk-11.0.1.zip
packager.json ---> json file that contains some options (if you need it)
yourapp.jar ---> the .jar file of your application made from B4J
myicon.ico ---> the icon of your app (if you want to have one)

The file packager.json may be a text file more or less like this:

{
"IncludedModules": "javafx.web",
"IconFile": "myicon.ico",
"InputJar": "yourapp.jar"
}

Then, simply from linux you can send this command staying in the dir you created:

jdk-11.0.1/bin/java -jar B4JPackager11.jar packager.json

If all goes well you can find a new dir called temp inside the created dir and under the dir temp you can find another dir called build.
This dir (build) it's all you need to deploy the app.
You can copy it (for example) under /opt/myapp in Linux and you can execute it with:

/opt/myapp/run.command

Hope this help,
Bye
 
Last edited:
Status
Not open for further replies.
Top