[CALCITE-5218] Verify HTTP client class before instantiating it
diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
index 420da0e..e0f3446 100644
--- a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
@@ -138,14 +138,23 @@
}
private AvaticaHttpClient instantiateClient(String className, URL url) {
+ AvaticaHttpClient client = null;
+ Exception clientCreationException = null;
try {
- Class<?> clz = Class.forName(className);
- Constructor<?> constructor = clz.getConstructor(URL.class);
- Object instance = constructor.newInstance(Objects.requireNonNull(url));
- return AvaticaHttpClient.class.cast(instance);
+ // Ensure that the given class is actually a subclass of AvaticaHttpClient
+ Class<? extends AvaticaHttpClient> clz =
+ Class.forName(className).asSubclass(AvaticaHttpClient.class);
+ Constructor<? extends AvaticaHttpClient> constructor = clz.getConstructor(URL.class);
+ client = constructor.newInstance(Objects.requireNonNull(url));
} catch (Exception e) {
+ clientCreationException = e;
+ }
+
+ if (client == null) {
throw new RuntimeException("Failed to construct AvaticaHttpClient implementation "
- + className, e);
+ + className, clientCreationException);
+ } else {
+ return client;
}
}
diff --git a/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java b/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java
index 8e1397c..27c9bcb 100644
--- a/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java
+++ b/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java
@@ -43,7 +43,7 @@
client instanceof AvaticaCommonsHttpClientImpl);
}
- @Test public void testOverridenHttpClient() throws Exception {
+ @Test public void testOverriddenHttpClient() throws Exception {
Properties props = new Properties();
props.setProperty(BuiltInConnectionProperty.HTTP_CLIENT_IMPL.name(),
AvaticaHttpClientImpl.class.getName());
@@ -55,6 +55,18 @@
assertTrue("Client was an instance of " + client.getClass(),
client instanceof AvaticaHttpClientImpl);
}
+
+ @Test(expected = RuntimeException.class) public void testInvalidHttpClient() throws Exception {
+ Properties props = new Properties();
+ props.setProperty(BuiltInConnectionProperty.HTTP_CLIENT_IMPL.name(),
+ Properties.class.getName()); // Properties is intentionally *not* a valid class
+ URL url = new URL("http://localhost:8765");
+ ConnectionConfig config = new ConnectionConfigImpl(props);
+ AvaticaHttpClientFactory httpClientFactory = new AvaticaHttpClientFactoryImpl();
+
+ // This should throw since the Properties class is invalid
+ httpClientFactory.getClient(url, config, null);
+ }
}
// End AvaticaHttpClientFactoryTest.java