diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml
new file mode 100644
index 0000000..300a7b4
--- /dev/null
+++ b/.github/workflows/compile.yml
@@ -0,0 +1,149 @@
+name: Compile and release
+
+# trigger the workflow manually
+on: workflow_dispatch
+
+jobs:
+
+  draft_release:
+    name: Create draft release
+    runs-on: ubuntu-latest
+    outputs:
+      release_id: ${{ steps.create_draft_release.outputs.id }}
+      upload_url: ${{ steps.create_draft_release.outputs.upload_url }}
+    steps:
+      - name: Get version from tag
+        id: tag_name
+        run: echo "RELEASE_VERSION=$(date +'%Y%m%d.%H%M%S')" >> $GITHUB_ENV
+      - uses: actions/checkout@v2
+      - name: Create draft release
+        id: create_draft_release
+        uses: actions/create-release@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          tag_name: ${{ env.RELEASE_VERSION }}
+          release_name: ${{ env.RELEASE_VERSION }}
+          draft: true
+          prerelease: ${{ steps.changelog_reader.outputs.status == 'prereleased' }}
+          body: ${{ steps.changelog_reader.outputs.changes }}
+      - name: Upload universalJavaApplicationStub.sh asset
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ steps.create_draft_release.outputs.upload_url }}
+          asset_name: universalJavaApplicationStub.sh
+          asset_path: ./src/universalJavaApplicationStub
+          asset_content_type: text/x-shellscript
+
+  compile:
+    name: Compile the stub
+    needs: draft_release # we need to know the upload URL
+    runs-on: macos-latest
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Install shc via HomeBrew
+        run: |
+          brew install shc
+          shc -h
+
+      - name: Compile universalJavaApplicationStub
+        run: |
+          echo "Running shc..."
+          shc -r -f src/universalJavaApplicationStub
+
+      - name: Build universalJavaApplicationStub x86_64
+        run: |
+          echo "Running clang for universalJavaApplicationStub.x86_64"
+          clang -o universalJavaApplicationStub.x86_64 -target x86_64-apple-macos10.10 src/universalJavaApplicationStub.x.c 
+          strip universalJavaApplicationStub.x86_64
+
+      - name: Build universalJavaApplicationStub arm64
+        run: |
+          echo "Running clang for universalJavaApplicationStub.arm64"
+          clang -o universalJavaApplicationStub.arm64 -target arm64-apple-macos11 src/universalJavaApplicationStub.x.c
+          strip universalJavaApplicationStub.arm64
+
+      - name: Build universal universalJavaApplicationStub
+        run: |
+          echo "Runnning lipo"
+          lipo -create -output universalJavaApplicationStub universalJavaApplicationStub.x86_64 universalJavaApplicationStub.arm64
+          strip universalJavaApplicationStub
+          chmod ug=rwx,o=rx universalJavaApplicationStub
+
+      - name: Build nativeJavaApplicationStub
+        run: |
+          make universal
+
+      - name: Upload universalJavaApplicationStub.x86_64 asset
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ needs.draft_release.outputs.upload_url }}
+          asset_name: universalJavaApplicationStub.x86_64
+          asset_path: ./universalJavaApplicationStub.x86_64
+          asset_content_type: application/octet-stream
+
+      - name: Upload universalJavaApplicationStub.arm64 asset
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ needs.draft_release.outputs.upload_url }}
+          asset_name: universalJavaApplicationStub.arm64
+          asset_path: ./universalJavaApplicationStub.arm64
+          asset_content_type: application/octet-stream
+
+      - name: Upload universalJavaApplicationStub asset
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ needs.draft_release.outputs.upload_url }}
+          asset_name: universalJavaApplicationStub
+          asset_path: ./universalJavaApplicationStub
+          asset_content_type: application/octet-stream
+
+      - name: Upload nativeJavaApplicationStub.x86_64 asset
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ needs.draft_release.outputs.upload_url }}
+          asset_name: nativeJavaApplicationStub.x86_64
+          asset_path: ./build/x86_64/nativeJavaApplicationStub
+          asset_content_type: application/octet-stream
+
+      - name: Upload nativeJavaApplicationStub.arm64 asset
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ needs.draft_release.outputs.upload_url }}
+          asset_name: nativeJavaApplicationStub.arm64
+          asset_path: ./build/arm64/nativeJavaApplicationStub
+          asset_content_type: application/octet-stream
+
+      - name: Upload nativeJavaApplicationStub asset
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ needs.draft_release.outputs.upload_url }}
+          asset_name: nativeJavaApplicationStub
+          asset_path: ./build/universal/nativeJavaApplicationStub
+          asset_content_type: application/octet-stream
+
+  publish_release:
+    name: Publish drafted release
+    needs: [ draft_release, compile ]
+    runs-on: ubuntu-latest
+    steps:
+      - uses: eregon/publish-release@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          release_id: ${{ needs.draft_release.outputs.release_id }}
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 6b26b45..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,114 +0,0 @@
-name: Release
-
-# Trigger the workflow only on tags
-on:
-  push:
-    tags:
-      - 'v[0-9]+.[0-9]+.[0-9]+'
-
-
-jobs:
-
-  draft_release:
-    name: Create draft release
-    runs-on: ubuntu-latest
-
-    outputs:
-      release_id: ${{ steps.create_draft_release.outputs.id }}
-      upload_url: ${{ steps.create_draft_release.outputs.upload_url }}
-
-    steps:
-      - name: Get version from tag
-        id: tag_name
-        run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
-
-      - uses: actions/checkout@v2
-
-      - name: Get Changelog Entry
-        id: changelog_reader
-        uses: mindsers/changelog-reader-action@v2
-        with:
-          version: ${{ env.RELEASE_VERSION }}
-          path: ./CHANGELOG.md
-
-      - name: Create draft release
-        id: create_draft_release
-        uses: actions/create-release@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GH_API_PERSONAL_ACCESS_TOKEN }}
-        with:
-          tag_name: ${{ github.ref }}
-          release_name: ${{ github.ref }}
-          draft: true
-          prerelease: ${{ steps.changelog_reader.outputs.status == 'prereleased' }}
-          body: ${{ steps.changelog_reader.outputs.changes }}
-
-      - name: ZIP uncompiled universalJavaApplicationStub
-        run: |
-          echo "Zipping uncompiled script..."
-          zip --junk-paths universalJavaApplicationStub-uncompiled.zip src/universalJavaApplicationStub
-
-      - name: Upload release assets
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GH_API_PERSONAL_ACCESS_TOKEN }}
-        with:
-          upload_url: ${{ steps.create_draft_release.outputs.upload_url }}
-          asset_name: universalJavaApplicationStub-v${{ env.RELEASE_VERSION }}-source.zip
-          asset_path: ./universalJavaApplicationStub-uncompiled.zip
-          asset_content_type: application/zip
-
-
-  compile:
-    name: Compile the stub on ${{ matrix.os }}
-    needs: draft_release # we need to know the upload URL
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        os: [ macos-10.15 ] # macos-11.0
-
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Set env
-        run: echo "RELEASE_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV
-
-      - name: Install shc via HomeBrew
-        run: |
-          brew install shc
-          shc -h
-
-      - name: Compile universalJavaApplicationStub
-        run: |
-          echo "Running shc..."
-          shc -r -f src/universalJavaApplicationStub
-
-      - name: ZIP universalJavaApplicationStub binary
-        run: |
-          echo "Zipping binary..."
-          mv src/universalJavaApplicationStub.x universalJavaApplicationStub
-          rm src/universalJavaApplicationStub.x.c
-          zip --junk-paths universalJavaApplicationStub-${{ matrix.os }}.zip universalJavaApplicationStub
-
-      - name: Upload release assets
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GH_API_PERSONAL_ACCESS_TOKEN }}
-        with:
-          upload_url: ${{ needs.draft_release.outputs.upload_url }}
-          asset_name: universalJavaApplicationStub-${{ env.RELEASE_TAG }}-binary-${{ matrix.os }}.zip
-          asset_path: ./universalJavaApplicationStub-${{ matrix.os }}.zip
-          asset_content_type: application/zip
-
-
-  publish_release:
-    name: Publish drafted release
-    needs: [ draft_release, compile ]
-    runs-on: ubuntu-latest
-
-    steps:
-      - uses: eregon/publish-release@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GH_API_PERSONAL_ACCESS_TOKEN }}
-        with:
-          release_id: ${{ needs.draft_release.outputs.release_id }}
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 34e07d9..49cc67f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,59 +1,64 @@
-name: Tests and Shellcheck
-
-# Trigger the workflow on push or pull request
-on: [push, pull_request]
+name: Build on push
 
+on: push
 
 jobs:
+  compile:
+    name: Compile the stubs
+    runs-on: macos-latest
+    steps:
+      - uses: actions/checkout@v2
 
-  tests:
-    name: Run bash function tests on ${{ matrix.os }}
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        os: [ macos-10.15 ] # macos-11.0
+      - name: Install shc via HomeBrew
+        run: |
+          brew install shc
+          shc -h
 
-    steps:
-    - uses: actions/checkout@v2
-
-    # Run the 'java-version-tester' script and show&save (`tee`) the output to a
-    # logfile; then grep the logfile for 'FAILED' entries which would cause grep
-    # to exit with 0; then run 'test' command and swap the grep exit code
-    # to return with 0 if grep doesn't match and return with 1 if it matches.
-    - name: Run function tests in java-version-tester.sh
-      run: |
-        echo "Running tests..."
-        bash test/java-version-tester.sh | tee test/java-version-tester.log
-        echo "Tests done. Checking for failures..."
-        test/check-log.sh test/java-version-tester.log FAILED
-
-
-  shellcheck:
-    name: Check bash syntax with shellcheck on ${{ matrix.os }}
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        os: [ macos-10.15 ] # macos-11.0
+      - name: Compile universalJavaApplicationStub
+        run: |
+          echo "Running shc..."
+          shc -r -f src/universalJavaApplicationStub
 
-    steps:
-    - uses: actions/checkout@v2
-
-    - name: Install shellcheck via HomeBrew
-      run: |
-        brew install shellcheck
-        shellcheck -V
-
-    # Test with shellcheck for bash syntax errors which should fail the build.
-    # Until https://github.com/koalaman/shellcheck/issues/524 is implemented,
-    # grep for errors and fail manually.
-    #
-    # Show & save the output via `tee` to a logfile; then grep the logfile for
-    # 'error:' entries which would cause grep to exit with 0; then run 'test'
-    # command and swap the grep exit code to return with 0 if grep doesn't match
-    #  and return with 1 if it matches
-    - name: Run shellcheck on the launcher stub
-      run: |
-        echo "Running shellcheck..."
-        shellcheck -s bash -f gcc src/universalJavaApplicationStub | tee test/shellcheck.log
-        echo "Shellcheck done. Checking for errors..."
-        test/check-log.sh test/shellcheck.log "error:"
+      - name: Build universalJavaApplicationStub x86_64
+        run: |
+          echo "Running clang for universalJavaApplicationStub.x86_64"
+          clang -o universalJavaApplicationStub.x86_64 -target x86_64-apple-macos10.10 src/universalJavaApplicationStub.x.c 
+          strip universalJavaApplicationStub.x86_64
+
+      - name: Build universalJavaApplicationStub arm64
+        run: |
+          echo "Running clang for universalJavaApplicationStub.arm64"
+          clang -o universalJavaApplicationStub.arm64 -target arm64-apple-macos11 src/universalJavaApplicationStub.x.c
+          strip universalJavaApplicationStub.arm64
+
+      - name: Build universal universalJavaApplicationStub
+        run: |
+          echo "Runnning lipo"
+          lipo -create -output universalJavaApplicationStub universalJavaApplicationStub.x86_64 universalJavaApplicationStub.arm64
+          strip universalJavaApplicationStub
+          chmod ug=rwx,o=rx universalJavaApplicationStub
+
+      - name: Build nativeJavaApplicationStub
+        run: |
+          make universal
+
+      - name: Archive production artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: universalJavaApplicationStub
+          retention-days: 7
+          path: |
+            ./src/universalJavaApplicationStub
+            ./universalJavaApplicationStub.x86_64
+            ./universalJavaApplicationStub.arm64
+            ./universalJavaApplicationStub
+
+      - name: Archive production artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: nativeJavaApplicationStub
+          retention-days: 7
+          path: |
+            ./build/x86_64/nativeJavaApplicationStub
+            ./build/arm64/nativeJavaApplicationStub
+            ./build/universal/nativeJavaApplicationStub
diff --git a/.gitignore b/.gitignore
index 6053715..f575bcb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@
 
 # shc compiler
 *.x
-*.x.c
\ No newline at end of file
+*.x.c
+
+build
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3112f9f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,57 @@
+CC = clang
+
+CFLAGS = -Wall -Werror -g
+CFLAGS_ARM64 = -target arm64-apple-macos11
+CFLAGS_X86_64 = -target x86_64-apple-macos10.12
+LIBS = -lobjc -framework Foundation -framework AppKit
+INCLUDES =
+
+SRCS = src/nativeStub.m
+OBJS = $(SRCS:src/%.m=build/%.o)
+OBJS_ARM64 = $(SRCS:src/%.m=build/arm64/%.o)
+OBJS_X86_64 = $(SRCS:src/%.m=build/x86_64/%.o)
+
+# define the executable file
+APP_NAME = nativeJavaApplicationStub
+MAIN = build/$(APP_NAME)
+MAIN_ARM64 = build/arm64/$(APP_NAME)
+MAIN_X86_64 = build/x86_64/$(APP_NAME)
+MAIN_UNIVERSAL = build/universal/$(APP_NAME)
+
+.PHONY: depend clean
+
+default: $(MAIN)
+universal: $(MAIN_UNIVERSAL)
+
+$(MAIN): $(OBJS)
+	$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^ $(LFLAGS) $(LIBS)
+
+$(MAIN_ARM64): $(OBJS_ARM64)
+	$(CC) $(CFLAGS) $(CFLAGS_ARM64) $(INCLUDES) -o $@ $^ $(LFLAGS) $(LIBS)
+
+$(MAIN_X86_64): $(OBJS_X86_64)
+	$(CC) $(CFLAGS) $(CFLAGS_X86_64) $(INCLUDES) -o $@ $^ $(LFLAGS) $(LIBS)
+
+$(MAIN_UNIVERSAL): $(MAIN_ARM64) $(MAIN_X86_64)
+	@mkdir -p build/universal
+	lipo -create -output $@ $^
+
+build/%.o: src/%.m
+	@mkdir -p build
+	$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+build/arm64/%.o: src/%.m
+	@mkdir -p build/arm64
+	$(CC) $(CFLAGS) $(INCLUDES) $(CFLAGS_ARM64) -c $< -o $@
+
+build/x86_64/%.o: src/%.m
+	@mkdir -p build/x86_64
+	$(CC) $(CFLAGS) $(INCLUDES) $(CFLAGS_X86_64) -c $< -o $@
+
+clean:
+	rm -rf build/
+
+depend: $(SRCS)
+	makedepend $(INCLUDES) $^
+
+# DO NOT DELETE THIS LINE -- make depend needs it
\ No newline at end of file
diff --git a/src/nativeStub.h b/src/nativeStub.h
new file mode 100644
index 0000000..87fd855
--- /dev/null
+++ b/src/nativeStub.h
@@ -0,0 +1,15 @@
+#import <Foundation/Foundation.h>
+
+@interface JVMMetadata : NSObject
+@property(strong) NSString *path;
+@property(strong) NSString *version;
+@end
+
+NSString *resolvePlaceholders(NSString *src, NSString *javaFolder);
+NSString *execute(NSString *command, NSArray *args);
+NSString *fetchJavaVersion(NSString *path);
+NSString *normalizeJavaVersion(NSString *version);
+BOOL isValidRequirement(NSString *version);
+BOOL versionMeetsConstraint(NSString *version, NSString *constraint, BOOL hasMax);
+BOOL versionMeetsMaxConstraint(NSString *version, NSString *constraint);
+
diff --git a/src/nativeStub.m b/src/nativeStub.m
new file mode 100644
index 0000000..497f007
--- /dev/null
+++ b/src/nativeStub.m
@@ -0,0 +1,524 @@
+#import "nativeStub.h"
+#import <stdlib.h>
+#import <string.h>
+#import <AppKit/AppKit.h>
+
+int main(int argc, char** argv) {
+    // For future improvement we can make these localized strings actually have the translations
+    // like they do in the bash script
+    NSString *MSG_ERROR_LAUNCHING=NSLocalizedStringWithDefaultValue(@"MSG_ERROR_LAUNCHING", @"javaApplicationStub", NSBundle.mainBundle, @"ERROR launching '%s'.", nil);
+    NSString *MSG_MISSING_MAINCLASS=NSLocalizedStringWithDefaultValue(@"MSG_MISSING_MAINCLASS", @"javaApplicationStub", NSBundle.mainBundle, @"'MainClass' isn't specified!\nJava application cannot be started!", nil);
+    NSString *MSG_JVMVERSION_REQ_INVALID=NSLocalizedStringWithDefaultValue(@"MSG_JVMVERSION_REQ_INVALID", @"javaApplicationStub", NSBundle.mainBundle, @"The syntax of the required Java version is invalid: %@\nPlease contact the App developer.", nil);
+    NSString *MSG_NO_SUITABLE_JAVA=NSLocalizedStringWithDefaultValue(@"MSG_NO_SUITABLE_JAVA", @"javaApplicationStub", NSBundle.mainBundle, @"No suitable Java version found on your system!\nThis program requires Java %@", nil);
+    NSString *MSG_JAVA_VERSION_OR_LATER=NSLocalizedStringWithDefaultValue(@"MSG_JAVA_VERSION_OR_LATER", @"javaApplicationStub", NSBundle.mainBundle, @" or later", nil);
+    NSString *MSG_JAVA_VERSION_LATEST=NSLocalizedStringWithDefaultValue(@"MSG_JAVA_VERSION_LATEST", @"javaApplicationStub", NSBundle.mainBundle, @" (latest update)", nil);
+    NSString *MSG_JAVA_VERSION_MAX=NSLocalizedStringWithDefaultValue(@"MSG_JAVA_VERSION_MAX", @"javaApplicationStub", NSBundle.mainBundle, @"up to %@", nil);
+    NSString *MSG_NO_SUITABLE_JAVA_CHECK=NSLocalizedStringWithDefaultValue(@"MSG_NO_SUITABLE_JAVA_CHECK", @"javaApplicationStub", NSBundle.mainBundle, @"Make sure you install the required Java version.", nil);
+    NSString *MSG_INSTALL_JAVA=NSLocalizedStringWithDefaultValue(@"MSG_INSTALL_JAVA", @"javaApplicationStub", NSBundle.mainBundle, @"You need to have JAVA installed on your Mac!\nVisit java.com for installation instructions...", nil);
+    NSString *MSG_LATER=NSLocalizedStringWithDefaultValue(@"MSG_LATER", @"javaApplicationStub", NSBundle.mainBundle, @"Later", nil);
+    NSString *MSG_VISIT_JAVA_DOT_COM=NSLocalizedStringWithDefaultValue(@"MSG_VISIT_JAVA_DOT_COM", @"javaApplicationStub", NSBundle.mainBundle, @"Java by Oracle", nil);
+    NSString *MSG_VISIT_ADOPTIUM=NSLocalizedStringWithDefaultValue(@"MSG_VISIT_ADOPTIUM", @"javaApplicationStub", NSBundle.mainBundle, @"Java by Adoptium", nil);
+
+    NSBundle *main = [NSBundle mainBundle];
+    NSDictionary *info = [main infoDictionary];
+    const char *appName = [info[@"CFBundleName"] UTF8String];
+    NSLog(@"[%s] [StubPath] %@", appName, [main executablePath]);
+
+    NSString *iconFile = info[@"CFBundleIconFile"];
+    if(iconFile != nil && ![iconFile containsString:@".icns"]) {
+        iconFile = [iconFile stringByAppendingString:@".icns"];
+    }
+    NSDictionary *javaInfo = info[@"Java"];
+    if(javaInfo == nil) {
+        javaInfo = info[@"JavaX"];
+    }
+
+    NSString *javaFolder;
+    NSString *mainClass;
+    NSString *splashFile;
+    NSString *workingDirectory;
+    NSMutableArray *jvmOptions;
+    NSMutableArray *jvmDefaultOptions;
+    NSMutableArray *classPath;
+    NSMutableArray *mainArgs;
+    NSString *jvmVersion;
+    NSString *jvmMaxVersion = nil;
+    NSString *jvmOptionsFile;
+    NSString *bootstrapScript;
+    if(javaInfo != nil) {
+        NSLog(@"[%s] [PlistStyle] Apple", appName);
+        // Apple mode
+        javaFolder = [[main resourcePath] stringByAppendingPathComponent:@"Java"];
+        if(javaInfo[@"RelocateJar"]) {
+            javaFolder = [main resourcePath];
+        }
+
+        if(javaInfo[@"WorkingDirectory"] != nil) {
+            NSString *workDirWithPlaceholders = javaInfo[@"WorkingDirectory"];
+            workingDirectory = resolvePlaceholders(workDirWithPlaceholders, javaFolder);
+        } else {
+            workingDirectory = [[main bundlePath] stringByDeletingLastPathComponent];
+        }
+
+        mainClass = javaInfo[@"MainClass"];
+        splashFile = javaInfo[@"SplashFile"];
+        jvmOptions = [[NSMutableArray alloc] init];
+        NSDictionary *propertiesAttr = javaInfo[@"Properties"];
+        if(propertiesAttr != nil) {
+            for(NSString *key in propertiesAttr) {
+                [jvmOptions addObject:[NSString stringWithFormat:@"-D%@=%@", resolvePlaceholders(key, javaFolder), resolvePlaceholders(propertiesAttr[key], javaFolder)]];
+            }
+        }
+
+        classPath = [[NSMutableArray alloc] init];
+        id classPathAttr = javaInfo[@"ClassPath"];
+        if([classPathAttr isKindOfClass:[NSArray class]]) {
+            for(NSString *pathElement in classPathAttr) {
+                [classPath addObject:resolvePlaceholders(pathElement, javaFolder)];
+            }
+        } else if(classPathAttr != nil) {
+            [classPath addObject:resolvePlaceholders(classPathAttr, javaFolder)];
+        }
+
+        jvmDefaultOptions = [[NSMutableArray alloc] init];
+        id vmOptionsAttr = javaInfo[@"VMOptions"];
+        if([vmOptionsAttr isKindOfClass:[NSArray class]]) {
+            for(NSString *pathElement in vmOptionsAttr) {
+                [jvmDefaultOptions addObject:resolvePlaceholders(pathElement, javaFolder)];
+            }
+        } else if(vmOptionsAttr != nil) {
+            [jvmDefaultOptions addObject:resolvePlaceholders(vmOptionsAttr, javaFolder)];
+        }
+
+        if(javaInfo[@"StartOnMainThread"]) {
+            [jvmDefaultOptions addObject:@" -XstartOnFirstThread"];
+        }
+
+        mainArgs = [[NSMutableArray alloc] init];
+        id argumentsAttr = javaInfo[@"Arguments"];
+        if([argumentsAttr isKindOfClass:[NSArray class]]) {
+            for(NSString *pathElement in argumentsAttr) {
+                [mainArgs addObject:resolvePlaceholders(pathElement, javaFolder)];
+            }
+        } else if(argumentsAttr != nil) {
+            [mainArgs addObject:resolvePlaceholders(argumentsAttr, javaFolder)];
+        }
+
+        jvmVersion = javaInfo[@"JVMVersion"];
+        jvmOptionsFile = javaInfo[@"JVMOptionsFile"];
+        bootstrapScript = javaInfo[@"BootstrapScript"];
+    } else {
+        NSLog(@"[%s] [PlistStyle] Oracle", appName);
+        javaFolder = [[main bundlePath] stringByAppendingPathComponent:@"Contents/Java"];
+        workingDirectory = javaFolder;
+        mainClass = info[@"JVMMainClassName"];
+        mainClass = info[@"JVMSplashFile"];
+
+        jvmOptions = [[NSMutableArray alloc] init];
+        NSDictionary *propertiesAttr = info[@"JVMOptions"];
+        if(propertiesAttr != nil) {
+            for(NSString *key in propertiesAttr) {
+                [jvmOptions addObject:[NSString stringWithFormat:@"-D%@=%@", resolvePlaceholders(key, javaFolder), resolvePlaceholders(propertiesAttr[key], javaFolder)]];
+            }
+        }
+
+        classPath = [[NSMutableArray alloc] init];
+        id classPathAttr = info[@"JVMClassPath"];
+        if([classPathAttr isKindOfClass:[NSArray class]]) {
+            for(NSString *pathElement in classPathAttr) {
+                [classPath addObject:resolvePlaceholders(pathElement, javaFolder)];
+            }
+        } else if(classPathAttr != nil) {
+            [classPath addObject:resolvePlaceholders(classPathAttr, javaFolder)];
+        } else {
+            [classPath addObject:[javaFolder stringByAppendingString:@"/*"]];
+        }
+
+        jvmDefaultOptions = [[NSMutableArray alloc] init];
+        id vmOptionsAttr = info[@"JVMDefaultOptions"];
+        for(NSString *pathElement in vmOptionsAttr) {
+            [jvmDefaultOptions addObject:resolvePlaceholders(pathElement, javaFolder)];
+        }
+
+        mainArgs = [[NSMutableArray alloc] init];
+        id argumentsAttr = info[@"JVMArguments"];
+        if([argumentsAttr isKindOfClass:[NSArray class]]) {
+            for(NSString *pathElement in argumentsAttr) {
+                [mainArgs addObject:resolvePlaceholders(pathElement, javaFolder)];
+            }
+        } else if(argumentsAttr != nil) {
+            [mainArgs addObject:resolvePlaceholders(argumentsAttr, javaFolder)];
+        }
+
+        jvmVersion = info[@"JVMVersion"];
+        jvmOptionsFile = info[@"JVMOptionsFile"];
+        bootstrapScript = info[@"BootstrapScript"];
+    }
+    if([jvmVersion containsString:@";"]) {
+        NSArray *stringParts = [jvmVersion componentsSeparatedByString:@";"];
+        jvmVersion = stringParts[0];
+        jvmMaxVersion = stringParts[1];
+    }
+    NSLog(@"[%s] [JavaRequirement] JVM minimum version: %@", appName, jvmVersion);
+    NSLog(@"[%s] [JavaRequirement] JVM minimum version: %@", appName, jvmMaxVersion);
+
+    if(jvmVersion != nil && !isValidRequirement(jvmVersion)) {
+        NSString *errorMsg = [NSString stringWithFormat:MSG_JVMVERSION_REQ_INVALID, jvmVersion];
+        NSLog(@"[%s] [EXIT 4] %@", appName, errorMsg);
+        NSAlert *alert = [[NSAlert alloc] init];
+        [alert setMessageText:[NSString stringWithFormat:MSG_ERROR_LAUNCHING, appName]];
+        [alert setInformativeText:errorMsg];
+        [alert runModal];
+        exit(4);
+    }
+    if(jvmVersion != nil && !isValidRequirement(jvmMaxVersion)) {
+        NSString *errorMsg = [NSString stringWithFormat:MSG_JVMVERSION_REQ_INVALID, jvmMaxVersion];
+        NSLog(@"[%s] [EXIT 5] %@", appName, MSG_MISSING_MAINCLASS);
+        NSAlert *alert = [[NSAlert alloc] init];
+        [alert setMessageText:[NSString stringWithFormat:MSG_ERROR_LAUNCHING, appName]];
+        [alert setInformativeText:errorMsg];
+        [alert runModal];
+        exit(5);
+    }
+
+    NSLog(@"[%s] [JavaSearch] Checking for $JAVA_HOME ...", appName);
+    NSString *javaHome = [[[NSProcessInfo processInfo] environment] objectForKey:@"JAVA_HOME"];
+    NSString *javaCmd = nil;
+    if(javaHome != nil) {
+        NSLog(@"[%s] [JavaSearch] ... found JAVA_HOME with value %@", appName, javaHome);
+        if([javaHome characterAtIndex:0] == '/') {
+            javaCmd = [javaHome stringByAppendingString:@"/bin/java"];
+            NSLog(@"[%s] [JavaSearch] ... parsing JAVA_HOME as absolute path to the executable '%@'", appName, javaCmd);
+        } else {
+            javaCmd = [[main bundlePath] stringByAppendingString:[@"/" stringByAppendingString:[javaHome stringByAppendingString:@"/bin/java"]]];
+            NSLog(@"[%s] [JavaSearch] ... parsing JAVA_HOME as relative path inside the App bundle to the executable '%@'", appName, javaCmd);
+        }
+    } else {
+        NSLog(@"[%s] [JavaSearch] ... haven't found JAVA_HOME", appName);
+    }
+
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    if (javaCmd == nil || ![fileManager isExecutableFileAtPath:javaCmd]) {
+        if(javaCmd != nil) {
+            NSLog(@"[%s] [JavaSearch] ... but no 'java' executable was found at the JAVA_HOME location!", appName);
+            javaCmd = nil;
+        }
+
+        NSLog(@"[%s] [JavaSearch] Searching for JavaVirtualMachines on the system ...", appName);
+
+        NSMutableArray *allJvms = [[NSMutableArray alloc] init];
+
+        NSString *javaHomeRaw = execute(@"/usr/libexec/java_home", @[@"--xml"]);
+        NSData* plistData = [javaHomeRaw dataUsingEncoding:NSUTF8StringEncoding];
+        NSArray* plist = [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListImmutable format:nil error:nil];
+        if(plist != nil) {
+            for(NSDictionary *entry in plist) {
+                JVMMetadata *metadata = [[JVMMetadata alloc] init];
+                [metadata setPath:[entry[@"JVMHomePath"] stringByAppendingString:@"/bin/java"]];
+                [metadata setVersion:normalizeJavaVersion(entry[@"JVMVersion"])];
+                [allJvms addObject:metadata];
+            }
+        }
+
+        if([fileManager isExecutableFileAtPath:@"/Library/Java/Home/bin/java"]) {
+            JVMMetadata *metadata = [[JVMMetadata alloc] init];
+            [metadata setPath:@"/Library/Java/Home/bin/java"];
+            [metadata setVersion:normalizeJavaVersion(fetchJavaVersion([metadata path]))];
+            [allJvms addObject:metadata];
+        }
+
+        if([fileManager isExecutableFileAtPath:@"/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"]) {
+            JVMMetadata *metadata = [[JVMMetadata alloc] init];
+            [metadata setPath:@"/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"];
+            [metadata setVersion:normalizeJavaVersion(fetchJavaVersion([metadata path]))];
+            [allJvms addObject:metadata];
+        }
+
+        BOOL isDir = NO;
+        NSString *searchBase = [@"~/.sdkman/candidates/java" stringByExpandingTildeInPath];
+        if([fileManager fileExistsAtPath:searchBase isDirectory:&isDir] && isDir) {
+            for(NSString *path in [fileManager contentsOfDirectoryAtPath:searchBase error:nil]) {
+                if([path isEqualToString:@"current/"]) {
+                    continue;
+                }
+                JVMMetadata *metadata = [[JVMMetadata alloc] init];
+                [metadata setPath:[NSString stringWithFormat:@"%@/%@/bin/java", searchBase, path]];
+                if([fileManager isExecutableFileAtPath:[metadata path]]) {
+                    [metadata setVersion:normalizeJavaVersion(fetchJavaVersion([metadata path]))];
+                    [allJvms addObject:metadata];
+                }
+            }
+        }
+
+        searchBase = [@"~/.asdf/installs/java" stringByExpandingTildeInPath];
+        if([fileManager fileExistsAtPath:searchBase isDirectory:&isDir] && isDir) {
+            for(NSString *path in [fileManager contentsOfDirectoryAtPath:searchBase error:nil]) {
+                JVMMetadata *metadata = [[JVMMetadata alloc] init];
+                [metadata setPath:[NSString stringWithFormat:@"%@/%@/bin/java", searchBase, path]];
+                if([fileManager isExecutableFileAtPath:[metadata path]]) {
+                    [metadata setVersion:normalizeJavaVersion(fetchJavaVersion([metadata path]))];
+                    [allJvms addObject:metadata];
+                }
+            }
+        }
+
+        for(JVMMetadata *metadata in allJvms) {
+            NSLog(@"[%s] [JavaSearch] ... found JVM: %@", appName, metadata);
+        }
+
+        NSLog(@"[%s] [JavaSearch]  Filtering the result list for JVMs matching the min/max version requirement ...", appName);
+
+        NSMutableArray *matchingJvms = [[NSMutableArray alloc] init];
+        for(JVMMetadata *metadata in allJvms) {
+            if(jvmVersion != nil && !versionMeetsConstraint([metadata version], normalizeJavaVersion(jvmVersion), jvmMaxVersion != nil)) {
+                continue;
+            }
+            if(jvmMaxVersion != nil && !versionMeetsMaxConstraint([metadata version], normalizeJavaVersion(jvmMaxVersion))) {
+                continue;
+            }
+            [matchingJvms addObject:metadata];
+        }
+
+        for(JVMMetadata *metadata in matchingJvms) {
+            NSLog(@"[%s] [JavaSearch] ... matches all requirements: %@", appName, metadata);
+        }
+        NSArray *sortedMatchingJvms = [matchingJvms sortedArrayUsingComparator:^NSComparisonResult(JVMMetadata *obj1, JVMMetadata *obj2) {
+            return [[obj2 version] compare:[obj1 version] options:NSNumericSearch];
+        }];
+        for(JVMMetadata *metadata in sortedMatchingJvms) {
+            if([fileManager isExecutableFileAtPath: [metadata path]]) {
+                javaCmd = [metadata path];
+                break;
+            }
+        }
+    }
+
+    NSLog(@"[%s] [JavaCommand] '%@'", appName, javaCmd);
+    NSLog(@"[%s] [JavaVersion] %@", appName, javaCmd == nil ? nil : fetchJavaVersion(javaCmd));
+
+    if(javaCmd == nil || ![fileManager isExecutableFileAtPath:javaCmd]) {
+        if(jvmVersion != nil) {
+            NSString *expandedMessage;
+            if(jvmMaxVersion == nil) {
+                expandedMessage = [NSString stringWithFormat:MSG_NO_SUITABLE_JAVA, [
+                        [normalizeJavaVersion(jvmVersion) stringByReplacingOccurrencesOfString:@"*" withString: MSG_JAVA_VERSION_LATEST]
+                        stringByReplacingOccurrencesOfString:@"+" withString: MSG_JAVA_VERSION_OR_LATER]];
+            } else {
+                expandedMessage = [NSString stringWithFormat:@"%@ %@",
+                                   [NSString stringWithFormat:MSG_NO_SUITABLE_JAVA, normalizeJavaVersion(jvmVersion)],
+                                   [NSString stringWithFormat:MSG_JAVA_VERSION_MAX, [normalizeJavaVersion(jvmMaxVersion) stringByReplacingOccurrencesOfString:@"*" withString: MSG_JAVA_VERSION_LATEST]]];
+            }
+            NSLog(@"[%s] [EXIT 3] %@", appName, expandedMessage);
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:[NSString stringWithFormat:MSG_ERROR_LAUNCHING, appName]];
+            [alert setInformativeText:[NSString stringWithFormat:@"%@\n%@", expandedMessage, MSG_NO_SUITABLE_JAVA_CHECK]];
+            [alert addButtonWithTitle:MSG_LATER];
+            [alert addButtonWithTitle:MSG_VISIT_JAVA_DOT_COM];
+            [alert addButtonWithTitle:MSG_VISIT_ADOPTIUM];
+            NSModalResponse res = [alert runModal];
+            if(res == NSAlertSecondButtonReturn) {
+                [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.java.com/download/"]];
+            } else if(res == NSAlertThirdButtonReturn) {
+                [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://adoptium.net/temurin/releases/"]];
+            }
+            exit(3);
+        } else {
+            NSLog(@"[%s] [EXIT 1] %@", appName, MSG_INSTALL_JAVA);
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:[NSString stringWithFormat:MSG_ERROR_LAUNCHING, appName]];
+            [alert setInformativeText:MSG_INSTALL_JAVA];
+            [alert addButtonWithTitle:MSG_LATER];
+            [alert addButtonWithTitle:MSG_VISIT_JAVA_DOT_COM];
+            [alert addButtonWithTitle:MSG_VISIT_ADOPTIUM];
+            NSModalResponse res = [alert runModal];
+            if(res == NSAlertSecondButtonReturn) {
+                [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.java.com/download/"]];
+            } else if(res == NSAlertThirdButtonReturn) {
+                [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://adoptium.net/temurin/releases/"]];
+            }
+            exit(1);
+        }
+    }
+
+    if(mainClass == nil) {
+        NSLog(@"[%s] [EXIT 2] %@", appName, MSG_MISSING_MAINCLASS);
+        NSAlert *alert = [[NSAlert alloc] init];
+        [alert setMessageText:[NSString stringWithFormat:MSG_ERROR_LAUNCHING, appName]];
+        [alert setInformativeText:MSG_MISSING_MAINCLASS];
+        [alert runModal];
+        exit(2);
+    }
+
+    chdir([workingDirectory UTF8String]);
+    char cwd[PATH_MAX];
+    getcwd(cwd, PATH_MAX);
+    NSLog(@"[%s] [WorkingDirectory] %s", appName, cwd);
+    if(bootstrapScript != nil && [fileManager isExecutableFileAtPath:bootstrapScript]) {
+        execute(bootstrapScript, @[]);
+    }
+    if(jvmOptionsFile != nil && [fileManager fileExistsAtPath:jvmOptionsFile]) {
+        NSString *optionsFile = [[NSString alloc] initWithData:[fileManager contentsAtPath:jvmOptionsFile] encoding:NSUTF8StringEncoding];
+        NSArray *lines = [optionsFile componentsSeparatedByString:@"\n"];
+        for(NSString *line in lines) {
+            if([line hasPrefix:@"#"]) {
+                continue;
+            }
+            [jvmDefaultOptions addObject:line];
+        }
+    }
+    NSMutableArray *allArgs = [[NSMutableArray alloc] init];
+    [allArgs addObject:javaCmd];
+    [allArgs addObject:@"-cp"];
+    [allArgs addObject:[classPath componentsJoinedByString:@":"]];
+    if(splashFile != nil) {
+        [allArgs addObject:[@"-splash:" stringByAppendingFormat:@"%@/%@", [main resourcePath], splashFile]];
+    }
+    [allArgs addObject:[@"-Xdock:icon=" stringByAppendingFormat:@"%@/%@", [main resourcePath], iconFile]];
+    [allArgs addObject:[@"-Xdock:name=" stringByAppendingString:info[@"CFBundleName"]]];
+    [allArgs addObjectsFromArray:jvmOptions];
+    [allArgs addObjectsFromArray:jvmDefaultOptions];
+    [allArgs addObject:mainClass];
+    [allArgs addObjectsFromArray:mainArgs];
+    for(int i = 1; i < argc; i++) {
+        NSString *cliArg = [[NSString alloc] initWithCString:argv[i] encoding:NSUTF8StringEncoding];
+        if(i == 1 && [cliArg hasPrefix:@"-psn"]) {
+            break;
+        }
+        [allArgs addObject:cliArg];
+    }
+
+    int count = [allArgs count];
+    char** cargs = malloc(sizeof(char*) * (count + 1));
+
+    for (int i = 0; i < count; ++i) {
+        cargs[i] = (char *) [[allArgs objectAtIndex:i] UTF8String];
+    }
+
+    cargs[count] = nil;
+    NSLog(@"[%s] [Exec] %@", appName, [allArgs componentsJoinedByString:@" "]);
+
+    execv([javaCmd UTF8String], cargs);
+}
+
+@implementation JVMMetadata
+- (NSString *)description {
+    return [NSString stringWithFormat: @"JVMMetadata: path=%@, version=%@", _path, _version];
+}
+@end
+
+NSString *resolvePlaceholders(NSString *src, NSString *javaFolder) {
+    NSBundle *main = [NSBundle mainBundle];
+
+    NSString *ret = src;
+    ret = [ret
+            stringByReplacingOccurrencesOfString:@"$APP_PACKAGE"
+                                      withString:[main bundlePath]];
+    ret = [ret
+            stringByReplacingOccurrencesOfString:@"$APP_ROOT"
+                                      withString:[main bundlePath]];
+    ret = [ret
+            stringByReplacingOccurrencesOfString:@"$JAVAROOT"
+                                      withString:javaFolder];
+    ret = [ret
+            stringByReplacingOccurrencesOfString:@"$USER_HOME"
+                                      withString:NSHomeDirectory()];
+    return ret;
+}
+
+NSString *execute(NSString *command, NSArray *args) {
+    NSTask *task = [[NSTask alloc]init];
+    [task setLaunchPath:command];
+    [task setArguments:args];
+
+    NSPipe *pipe = [[NSPipe alloc]init];
+    [task setStandardOutput: pipe];
+    [task setStandardError: pipe];
+    [task launch];
+
+    NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
+    NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+
+    [task waitUntilExit];
+
+    return output;
+}
+
+NSString *fetchJavaVersion(NSString *path) {
+    NSString *result = execute(path, @[@"-version"]);
+    // The actual version will be between the first two quotes in the result
+    // We can reasonably ignore all the rest of the output
+    return [result componentsSeparatedByString:@"\""][1];
+}
+
+NSString *normalizeJavaVersion(NSString *version) {
+    if([version hasPrefix:@"1."]) {
+        version = [version substringFromIndex:2];
+    }
+    return [version stringByReplacingOccurrencesOfString:@"_" withString:@"."];
+}
+
+BOOL isValidRequirement(NSString *version) {
+    NSString *versionPatterns = @"^(1\\.[4-8](\\.[0-9]+)?(\\.0_[0-9]+)?[*+]?|[0-9]+(-ea|[*+]|(\\.[0-9]+){1,2}[*+]?)?)$";
+    NSPredicate *test = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", versionPatterns];
+
+    return [test evaluateWithObject: version];
+}
+
+BOOL versionMeetsConstraint(NSString *version, NSString *constraint, BOOL hasMax) {
+    NSArray *versionParts = [version componentsSeparatedByString:@"."];
+    NSArray *constraintParts = [constraint componentsSeparatedByString:@"."];
+    BOOL exceeds = NO;
+    for(int i = 0; i < constraintParts.count; i++) {
+        int v = [versionParts[i] intValue];
+        int c = [constraintParts[i] intValue];
+        if(v < c) {
+            return NO;
+        }
+        if(v > c) {
+            exceeds = YES;
+            break;
+        }
+    }
+    // At this point the numeric parts are all the same or greater, so compare the suffixes
+    // to see which rule to apply
+    char constraintModifier = [constraint characterAtIndex:([constraint length] - 1)];
+    // If there's a max, the bottom constraint is always effectively a min
+    if(constraintModifier == '+' || hasMax) {
+        return YES;
+    } else {
+        // no modifier is equivalent to an implicit *
+        return !exceeds;
+    }
+}
+
+BOOL versionMeetsMaxConstraint(NSString *version, NSString *constraint) {
+    NSArray *versionParts = [version componentsSeparatedByString:@"."];
+    NSArray *constraintParts = [constraint componentsSeparatedByString:@"."];
+    BOOL exceeds = NO;
+    for(int i = 0; i < [constraintParts count]; i++) {
+        if([constraintParts[i] length] == 0) {
+            break;
+        }
+        int v = [versionParts[i] intValue];
+        int c = [constraintParts[i] intValue];
+        if(v < c) {
+            return YES;
+        }
+        if(v > c) {
+            exceeds = YES;
+            break;
+        }
+    }
+
+    // At this point the numeric parts are all the same or greater, so compare the suffixes
+    // to see which rule to apply
+    char constraintModifier = [constraint characterAtIndex:([constraint length] - 1)];
+    if(constraintModifier == '+') {
+        return YES;
+    } else if(constraintModifier == '*') {
+        return !exceeds;
+    } else {
+        // no modifier means it must match exactly
+        return !exceeds && [constraintParts count] == [versionParts count];
+    }
+}
\ No newline at end of file
diff --git a/src/universalJavaApplicationStub b/src/universalJavaApplicationStub
index 43cb683..0dbbf8b 100755
--- a/src/universalJavaApplicationStub
+++ b/src/universalJavaApplicationStub
@@ -121,6 +121,7 @@ plist_get(){
 	/usr/libexec/PlistBuddy -c "print $1" "${InfoPlistFile}" 2> /dev/null
 }
 
+
 # function 'plist_get_java()'
 #
 # read a specific Plist key with 'PlistBuddy' utility
@@ -133,8 +134,6 @@ plist_get_java(){
 	plist_get ${JavaKey:-":Java"}$1
 }
 
-
-
 # read Info.plist and extract JVM options
 ############################################
 
@@ -166,6 +165,12 @@ if [ $exitcode -eq 0 ]; then
 	JavaFolder="${AppleJavaFolder}"
 	ResourcesFolder="${AppleResourcesFolder}"
 
+	# fix: relocate jar
+	RelocateJar="$(plist_get_java ':RelocateJar' | xargs)"
+	if [[ "$RelocateJar" == *true* ]]; then
+		JavaFolder=${ResourcesFolder}
+	fi
+
 	# set expandable variables
 	APP_ROOT="${AppPackageFolder}"
 	APP_PACKAGE="${AppPackageFolder}"
@@ -239,6 +244,12 @@ if [ $exitcode -eq 0 ]; then
 	JVMVersion=$(plist_get_java ':JVMVersion' | xargs)
 	# post processing of the version string follows below...
 
+	# read options file name
+	JVMOptionsFile=$(plist_get_java ':JVMOptionsFile')
+	JVMOptionsFile=$(eval echo "${JVMOptionsFile}")
+
+	# read bootstrap script file name
+	BootstrapScript=$(plist_get_java ':BootstrapScript')
 
 # read 'Info.plist' file in Oracle style
 else
@@ -300,8 +311,15 @@ else
 	# read the Java version we want to find
 	JVMVersion=$(plist_get ':JVMVersion' | xargs)
 	# post processing of the version string follows below...
-fi
 
+	# read options file name
+	JVMOptionsFile=$(plist_get ':JVMOptionsFile')
+	JVMOptionsFile=$(eval echo "${JVMOptionsFile}")
+
+	# read bootstrap script file name
+	BootstrapScript=$(plist_get ':BootstrapScript')
+
+fi
 
 # (#75) check for undefined icons or icon names without .icns extension and prepare
 # an osascript statement for those cases when the icon can be shown in the dialog
@@ -885,6 +903,12 @@ fi
 cd "${WorkingDirectory}" || exit 13
 stub_logger "[WorkingDirectory] ${WorkingDirectory}"
 
+# fix: invoke bootstrap script
+[ -x "$BootstrapScript" ] && "$BootstrapScript"
+
+# fix: read JVM options from file
+[ -f $JVMOptionsFile ] && IFS= && while read -r option; do JVMOptionsArr+=("$option"); done <<< $(sed "/^#.*$/d" $JVMOptionsFile) && unset IFS
+
 # execute Java and set
 # - classpath
 # - splash image
