vertx-lang-kotlin icon indicating copy to clipboard operation
vertx-lang-kotlin copied to clipboard

Kotlin Launcher sub-class does not function as expected

Open rgmz opened this issue 5 years ago • 5 comments

Sub-classing the Launcher in Kotlin produces a fat jar that does not run with any commands:

Usage: java -jar build/libs/kotlin-launcher-bug-1.0-SNAPSHOT-fat.jar [COMMAND] [OPTIONS] [arg...]

Commands:
    bare      Creates a bare instance of vert.x.
    list      List vert.x applications
    run       Runs a verticle called <main-verticle> in its own instance of
              vert.x.
    start     Start a vert.x application in background
    stop      Stop a vert.x application
    version   Displays the version.

Run 'java -jar build/libs/kotlin-launcher-bug-1.0-SNAPSHOT-fat.jar COMMAND
--help' for more information on a command.

To reproduce this, compare the master branch of this project (an alternative Launcher written in Java):

> ./gradlew shadowJar

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.
See https://docs.gradle.org/4.5.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

> java -jar build/libs/kotlin-launcher-bug-1.0-SNAPSHOT-fat.jar
Sep 30, 2018 12:26:41 AM io.vertx.starter.Launcher
INFO: Hello, Vert.x Launcher!
Sep 30, 2018 12:26:41 AM io.vertx.core.impl.launcher.commands.VertxIsolatedDeployer
INFO: Succeeded in deploying verticle

With the kotlin-launcher-bug branch:

> ./gradlew shadowJar
w: /home/user/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jre7/1.2.20/.../kotlin-stdlib-jre7-1.2.20.jar: kotlin-stdlib-jre7 is deprecated. Please use kotlin-stdlib-jdk7 instead
w: /home/user/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jre8/1.2.20/.../kotlin-stdlib-jre8-1.2.20.jar: kotlin-stdlib-jre8 is deprecated. Please use kotlin-stdlib-jdk8 instead

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.
See https://docs.gradle.org/4.5.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 executed

> java -jar build/libs/kotlin-launcher-bug-1.0-SNAPSHOT-fat.jar
Usage: java -jar build/libs/kotlin-launcher-bug-1.0-SNAPSHOT-fat.jar [COMMAND]
            [OPTIONS] [arg...]
...etc

(I did some debugging but was not able to determine the issue, however, it is entirely possible that I am just dense.)

rgmz avatar Sep 30 '18 04:09 rgmz

This works for me. I think your problem is this line in your gradle file: def mainVerticleName = 'io.vertx.starter.MainVerticle'

Because Kotlin it should be def mainVerticleName = 'io.vertx.starter.MainVerticleKt'

See my project as an example if you still have problems

https://github.com/lfmunoz/vertx-kt-rocket/blob/master/src/main/kotlin/com/lfmunoz/App.kt

lfmunoz avatar Oct 17 '18 01:10 lfmunoz

I ran into the same issue and finally got it working:

This is my starter:

class Starter : VertxCommandLauncher(), VertxLifecycleHooks {

    companion object {
        @JvmStatic
        fun main(vararg args: String) {
            Starter().dispatch(args)
        }
    }
//implement empty interface methods here

Note that it must not extend from io.vertx.core.Launcher due to signature clashes with fun main.

Also, I used the "Main-Class" manifest entry (as opposed to "Launcher-Class"):

shadowJar {
    archiveClassifier = 'fat'

    manifest {
        attributes('Main-Verticle': mainVerticle, 'Main-Class': mainClass, 'Commit': grgit.head().id)
    }
}

devluencer-089 avatar Apr 05 '19 21:04 devluencer-089

(I did some debugging but was not able to determine the issue, however, it is entirely possible that I am just dense.)

I ran into the same issue and found the cause.

Take the following custom launcher, CustomLauncher.kt:

import io.vertx.core.Launcher

fun main(vararg args: String) {
    CustomLauncher().dispatch(args)
}

class CustomLauncher : Launcher() {

    init {
        println("Custom launcher")
    }
}

This results in the following Java equivalent code:

public final class CustomLauncherKt {
   public static final void main(String... args) {
      (new CustomLauncher()).dispatch(args);
   }
}

import io.vertx.core.Launcher;

public final class CustomLauncher extends Launcher {
   public CustomLauncher() {
      System.out.println("Custom launcher");
   }
}

You can see this generates 2 classes, CustomLauncherKt containing the main method and the Launcher subclass called CustomLauncher.

However, the VertxCommandLauncher.getCommandFromManifest() method does the following (simplified for brevity):

String mainClass = attributes.getValue("Main-Class");
if (main != null && main.getClass().getName().equals(mainClass)) {
    return attributes.getValue("Main-Verticle");
}
return null;

In this snippet, main is the instance of your Launcher subclass (so CustomLauncher, in this case). But the Main-Class entry in the manifest contains the class with the main method, so that would be CustomLauncherKt. And CustomLauncher != CustomLauncherKt, so instead of returning your main verticle it just returns null.

The getCommandFromManifest() method assumes the main method is in your Launcher subclass, which isn't Kotlin friendly.

dvdmunckhof avatar Apr 29 '19 08:04 dvdmunckhof

Is there any update on this? I'm currently doing what @michaelom does and just creating my own launcher class...that seems to be the best solution at the moment

nicksc423 avatar Jan 03 '20 17:01 nicksc423

@nicksc423 doing the same and don't know about any other solution so far.

u6f6o avatar Jul 22 '20 07:07 u6f6o

Updating's @dvdmunckhof solution:

import io.vertx.core.Launcher

fun main(vararg args: String) {
    val launcher = CustomLauncher()
    launcher.dispatch(launcher, args) // call the dispatch(main, args) method
}

class CustomLauncher : Launcher() {

    init {
        println("Custom launcher")
    }
}

tsegismont avatar Oct 06 '23 15:10 tsegismont