From f6a37b462408fa147f78516734e173a8485bec79 Mon Sep 17 00:00:00 2001 From: Julien Viet Date: Fri, 21 Apr 2023 17:18:01 +0200 Subject: [PATCH] When a transport is loaded by the service loader mechanism, Vert.x should check the transport is available before using it. --- .../java/io/vertx/core/impl/VertxBuilder.java | 7 ++- .../core/impl/ServiceLoaderTransportTest.java | 19 +++++++ .../core/spi/metrics/MetricsOptionsTest.java | 15 ++---- .../java/io/vertx/test/core/TestUtils.java | 32 ++++++++++-- .../test/faketransport/FakeTransport.java | 51 +++++++++++++++++++ 5 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 src/test/java/io/vertx/core/impl/ServiceLoaderTransportTest.java create mode 100644 src/test/java/io/vertx/test/faketransport/FakeTransport.java diff --git a/src/main/java/io/vertx/core/impl/VertxBuilder.java b/src/main/java/io/vertx/core/impl/VertxBuilder.java index f428568dfe6..48d96355264 100644 --- a/src/main/java/io/vertx/core/impl/VertxBuilder.java +++ b/src/main/java/io/vertx/core/impl/VertxBuilder.java @@ -405,8 +405,11 @@ static Transport findTransport(boolean preferNative) { if (preferNative) { Collection transports = ServiceHelper.loadFactories(Transport.class); Iterator it = transports.iterator(); - if (it.hasNext()) { - return it.next(); + while (it.hasNext()) { + Transport transport = it.next(); + if (transport.isAvailable()) { + return transport; + } } Transport nativeTransport = nativeTransport(); if (nativeTransport != null && nativeTransport.isAvailable()) { diff --git a/src/test/java/io/vertx/core/impl/ServiceLoaderTransportTest.java b/src/test/java/io/vertx/core/impl/ServiceLoaderTransportTest.java new file mode 100644 index 00000000000..284971e026c --- /dev/null +++ b/src/test/java/io/vertx/core/impl/ServiceLoaderTransportTest.java @@ -0,0 +1,19 @@ +package io.vertx.core.impl; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.spi.transport.Transport; +import io.vertx.test.core.AsyncTestBase; +import io.vertx.test.core.TestUtils; +import io.vertx.test.faketransport.FakeTransport; +import org.junit.Test; + +public class ServiceLoaderTransportTest extends AsyncTestBase { + + @Test + public void testServiceLoaderTransportNotAvailable() { + Vertx vertx = TestUtils.runWithServiceLoader(Transport.class, FakeTransport.class, () -> + Vertx.vertx(new VertxOptions().setPreferNativeTransport(true))); + assertNotSame(FakeTransport.class, ((VertxInternal)vertx).transport()); + } +} diff --git a/src/test/java/io/vertx/core/spi/metrics/MetricsOptionsTest.java b/src/test/java/io/vertx/core/spi/metrics/MetricsOptionsTest.java index 4468fe27ebf..e28f0bdbabd 100644 --- a/src/test/java/io/vertx/core/spi/metrics/MetricsOptionsTest.java +++ b/src/test/java/io/vertx/core/spi/metrics/MetricsOptionsTest.java @@ -94,7 +94,7 @@ public void testMetricsFromServiceLoader() { vertx.close(); MetricsOptions metricsOptions = new MetricsOptions().setEnabled(true); VertxOptions options = new VertxOptions().setMetricsOptions(metricsOptions); - vertx = createVertxLoadingMetricsFromMetaInf(options, "io.vertx.test.fakemetrics.FakeMetricsFactory"); + vertx = createVertxLoadingMetricsFromMetaInf(options, io.vertx.test.fakemetrics.FakeMetricsFactory.class); VertxMetrics metrics = ((VertxInternal) vertx).metricsSPI(); assertNotNull(metrics); assertTrue(metrics instanceof FakeVertxMetrics); @@ -106,19 +106,14 @@ public void testSetMetricsInstanceTakesPrecedenceOverServiceLoader() { DummyVertxMetrics metrics = DummyVertxMetrics.INSTANCE; vertx.close(); VertxOptions options = new VertxOptions().setMetricsOptions(new MetricsOptions().setEnabled(true).setFactory(new SimpleVertxMetricsFactory<>(metrics))); - vertx = createVertxLoadingMetricsFromMetaInf(options, "io.vertx.test.fakemetrics.FakeMetricsFactory"); + vertx = createVertxLoadingMetricsFromMetaInf(options, io.vertx.test.fakemetrics.FakeMetricsFactory.class); assertSame(metrics, ((VertxInternal) vertx).metricsSPI()); } - static Vertx createVertxLoadingMetricsFromMetaInf(VertxOptions options, String factoryFqn) { - ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); - ClassLoader cl = createMetricsFromMetaInfLoader(factoryFqn); - Thread.currentThread().setContextClassLoader(cl); - try { + static Vertx createVertxLoadingMetricsFromMetaInf(VertxOptions options, Class factory) { + return TestUtils.runWithServiceLoader(VertxServiceProvider.class, factory, () -> { return Vertx.vertx(options); - } finally { - Thread.currentThread().setContextClassLoader(oldCL); - } + }); } public static ClassLoader createMetricsFromMetaInfLoader(String factoryFqn) { diff --git a/src/test/java/io/vertx/test/core/TestUtils.java b/src/test/java/io/vertx/test/core/TestUtils.java index 27a2129413b..b489865a2b4 100644 --- a/src/test/java/io/vertx/test/core/TestUtils.java +++ b/src/test/java/io/vertx/test/core/TestUtils.java @@ -16,18 +16,17 @@ import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.cert.Certificate; -import java.util.EnumSet; -import java.util.List; -import java.util.Random; -import java.util.Set; +import java.util.*; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -542,4 +541,29 @@ public static Throwable rootCause(Throwable throwable) { } return root; } + + + public static T runWithServiceLoader(Class service, Class impl, Supplier supplier) { + URLClassLoader cl = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()) { + @Override + public Enumeration findResources(String name) throws IOException { + if (name.equals("META-INF/services/" + service.getName())) { + File f = File.createTempFile("vertx", ".txt"); + f.deleteOnExit(); + Files.write(f.toPath(), impl.getName().getBytes()); + return Collections.enumeration(Collections.singleton(f.toURI().toURL())); + } + return super.findResources(name); + } + }; + Thread th = Thread.currentThread(); + ClassLoader previousCL = th.getContextClassLoader(); + th.setContextClassLoader(cl); + try { + return supplier.get(); + } finally { + th.setContextClassLoader(previousCL); + } + } + } diff --git a/src/test/java/io/vertx/test/faketransport/FakeTransport.java b/src/test/java/io/vertx/test/faketransport/FakeTransport.java new file mode 100644 index 00000000000..2d9c72f8d8b --- /dev/null +++ b/src/test/java/io/vertx/test/faketransport/FakeTransport.java @@ -0,0 +1,51 @@ +package io.vertx.test.faketransport; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFactory; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.ServerChannel; +import io.netty.channel.socket.DatagramChannel; +import io.netty.channel.socket.InternetProtocolFamily; +import io.vertx.core.spi.transport.Transport; + +import java.util.concurrent.ThreadFactory; + +public class FakeTransport implements Transport { + + private static final Throwable CAUSE = new UnsupportedOperationException("Unavailable"); + + @Override + public boolean isAvailable() { + return false; + } + + @Override + public Throwable unavailabilityCause() { + return CAUSE; + } + + @Override + public EventLoopGroup eventLoopGroup(int type, int nThreads, ThreadFactory threadFactory, int ioRatio) { + throw new UnsupportedOperationException(); + } + + @Override + public DatagramChannel datagramChannel() { + throw new UnsupportedOperationException(); + } + + @Override + public DatagramChannel datagramChannel(InternetProtocolFamily family) { + throw new UnsupportedOperationException(); + } + + @Override + public ChannelFactory channelFactory(boolean domainSocket) { + throw new UnsupportedOperationException(); + } + + @Override + public ChannelFactory serverChannelFactory(boolean domainSocket) { + throw new UnsupportedOperationException(); + } +}