We have a simple project with 3 verticles, it would be interesting to use the vert.x launcher to allow using the 3 from the same image as we do from a single jar.

Sadly vert.x lancher relies on reflection which is requires configuration to get working on native.

To configure reflection a json file is required and it must be configured on the native-image.properties :

Args = --initialize-at-run-time=io.netty.channel.DefaultChannelId, \ io.netty.buffer.PooledByteBufAllocator, \ io.netty.util.NetUtil, \ io.netty.channel.socket.InternetProtocolFamily, \ io.netty.resolver.HostsFileEntriesResolver, \ io.netty.resolver.dns.DnsNameResolver, \ io.netty.resolver.dns.DnsServerAddressStreamProviders, \ io.netty.resolver.dns.PreferredAddressTypeComparator \$ 1, \ io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider, \ io.vertx.core.impl.AddressResolver, \ io.netty.handler.codec.http.websocketx.extensions.compression.DeflateEncoder, \ io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder, \ io.netty.handler.codec.http.HttpObjectEncoder, \ io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder, \ io.netty.handler.codec.http2.Http2CodecUtil, \ io.netty.handler.codec.http2.Http2ConnectionHandler, \ io.netty.handler.codec.http2.DefaultHttp2FrameWriter, \ io.netty.util.internal.logging.Log4JLogger, \ io.netty.handler.ssl.ReferenceCountedOpenSslServerContext, \ io.netty.handler.ssl.JdkNpnApplicationProtocolNegotiator, \ io.netty.handler.ssl.ReferenceCountedOpenSslEngine, \ io.netty.handler.ssl.ConscryptAlpnSslEngine, \ io.netty.handler.ssl.JettyNpnSslEngine, \ io.netty.handler.ssl.JettyAlpnSslEngine$ClientEngine, \ io.netty.handler.ssl.JettyAlpnSslEngine$ServerEngine, \ io.netty.handler.ssl.ReferenceCountedOpenSslContext, \ io.netty.handler.ssl.ReferenceCountedOpenSslClientContext, \ io.vertx.core.net.impl.transport.EpollTransport, \ io.vertx.core.net.impl.transport.KQueueTransport, \ io.vertx.core.http.impl.VertxHttp2ClientUpgradeCodec, \ io.vertx.core.eventbus.impl.clustered.ClusteredEventBus \ -H:ReflectionConfigurationResources=${.}/reflection.json

This is telling to the compiler to load a file called reflection.json which is relative to the current configuration. This file can be generated manually or with the help of an agent. The agent is currently unstable so this is the manually generated one:

[ { "name" : "io.vertx.core.impl.launcher.commands.RunCommand" , "allDeclaredConstructors" : true , "allDeclaredMethods" : true }, { "name" : "io.vertx.core.impl.launcher.commands.VertxIsolatedDeployer" , "allDeclaredConstructors" : true , "allDeclaredMethods" : true }, { "name" : "java.lang.Long" , "allDeclaredConstructors" : true }, { "name" : "java.lang.Integer" , "allDeclaredConstructors" : true }, { "name" : "vertx.HTTPVerticle" , "allDeclaredConstructors" : true , "allDeclaredMethods" : true }, { "name" : "vertx.HTTPSVerticle" , "allDeclaredConstructors" : true , "allDeclaredMethods" : true }, { "name" : "vertx.APIClientVerticle" , "allDeclaredConstructors" : true , "allDeclaredMethods" : true } ]

Since the commands are loaded by reflection you need to declare the RunCommand , VertxIsolatedDeployer , Long and Integer classes to be accessed by reflection and their default constructors. Long and integer are used by the launcher to parse arguments if you’re wondering why.

Then we can remove all public static void main methods from our verticles and list the verticles in the reflection file too.

If you build the image, everything will work except the default main verticle will not start automatically. The reason is that the main verticle is defined in the MANIFEST.MF file. So this means we need to add resources to the image:

Args = --initialize-at-build-time=io.netty,io.vertx,com.fasterxml.jackson,javax \ --initialize-at-run-time=io.vertx.core.net.impl.PartialPooledByteBufAllocator,io.netty.handler.codec.http.websocketx.extensions.compression.DeflateEncoder,io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder,io.vertx.core.eventbus.impl.clustered.ClusteredEventBus,io.netty.util.internal.logging.Log4JLogger,io.netty.handler.codec.http.HttpObjectEncoder,io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,io.netty.handler.codec.http2.Http2CodecUtil,io.netty.handler.codec.http2.DefaultHttp2FrameWriter,io.netty.handler.ssl.ReferenceCountedOpenSslServerContext,io.netty.handler.ssl.JdkNpnApplicationProtocolNegotiator,io.netty.handler.ssl.ReferenceCountedOpenSslEngine,io.netty.handler.ssl.ConscryptAlpnSslEngine,io.netty.handler.ssl.JettyNpnSslEngine,io.netty.handler.ssl.ReferenceCountedOpenSslClientContext,io.vertx.core.net.impl.transport.EpollTransport,io.vertx.core.net.impl.transport.KQueueTransport,io.vertx.core.http.impl.VertxHttp2ClientUpgradeCodec,io.netty.handler.codec.http2.Http2ConnectionHandler \ -H:+UseServiceLoaderFeature \ -H:IncludeResources=(META-INF|static|webroot|template)/.* \ -H:ReflectionConfigurationResources=${.}/reflection.json

So the include resources configuration now tells the compiler to include all the META-INF files in the image.

$ mvn package [INFO] Scanning for projects... ... [hello_native:12244] compile: 15,823.76 ms [hello_native:12244] image: 2,144.95 ms [hello_native:12244] write: 306.25 ms [hello_native:12244] [total]: 48,724.03 ms [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ $ ./target/hello_native Got HTTP response with status 200 with data Sometimes I tuck my knees into my chest and lean forward. Thatâs just how I roll. ^C $ ./target/hello_native run vertx.HTTPVerticle Server listening on http://localhost:8080/ ^C $ ./target/hello_native run vertx.HTTPSVerticle Server listening on https://localhost:8443/ ^C

You have now all the verticles in the same image. Remember that you can do now all the things you would with the launcher, for example scale the number of verticles for the HTTP server:

$ ./target/hello_native run vertx.HTTPVerticle -instances 4 Server listening on http://localhost:8080/ Server listening on http://localhost:8080/ Server listening on http://localhost:8080/ Server listening on http://localhost:8080/ ^C