From a132138f18b064aa54a193a0da1d65ec8b35831d Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 4 Feb 2020 12:24:57 +0200 Subject: [PATCH 1/3] Handle uberjar scenario where the ES jdbc driver file is bundled inside another jar file. --- .../xpack/sql/client/Version.java | 10 ++-- .../xpack/sql/client/VersionTests.java | 50 ++++++++++++++++++- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/Version.java b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/Version.java index 3f7134272c9c5..dd217011081e0 100644 --- a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/Version.java +++ b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/Version.java @@ -85,14 +85,16 @@ static byte[] from(String ver) { // This is similar to how Elasticsearch's Build class digs up its build information. // Since version info is not critical, the parsing is lenient - URL url = Version.class.getProtectionDomain().getCodeSource().getLocation(); - String urlStr = url.toString(); + CURRENT = extractVersion(Version.class.getProtectionDomain().getCodeSource().getLocation()); + } + static Version extractVersion(URL url) { + String urlStr = url.toString(); byte maj = 0, min = 0, rev = 0; String ver = "Unknown"; String hash = ver; - if (urlStr.endsWith(".jar")) { + if (urlStr.endsWith(".jar") || urlStr.endsWith(".jar!/")) { try (JarInputStream jar = new JarInputStream(url.openStream())) { Manifest manifest = jar.getManifest(); hash = manifest.getMainAttributes().getValue("Change"); @@ -105,7 +107,7 @@ static byte[] from(String ver) { throw new IllegalArgumentException("Detected Elasticsearch JDBC jar but cannot retrieve its version", ex); } } - CURRENT = new Version(ver, hash, maj, min, rev); + return new Version(ver, hash, maj, min, rev); } @Override diff --git a/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java b/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java index 7ed772e352531..ce3b18e591bae 100644 --- a/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java +++ b/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java @@ -6,7 +6,17 @@ package org.elasticsearch.xpack.sql.client; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.sql.client.Version; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; public class VersionTests extends ESTestCase { public void test70Version() { @@ -43,4 +53,42 @@ public void testInvalidVersion() { IllegalArgumentException err = expectThrows(IllegalArgumentException.class, () -> Version.from("7.1")); assertEquals("Invalid version 7.1", err.getMessage()); } + + public void testVersionFromJarInJar() throws IOException { + Path dir = createTempDir(); + Path jarPath = dir.resolve("foo.jar"); + Path innerJarPath = dir.resolve("es-sql-jdbc.jar"); + + Manifest jdbcJarManifest = new Manifest(); + Attributes attributes = jdbcJarManifest.getMainAttributes(); + attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); + attributes.put(new Attributes.Name("Change"), "abc"); + attributes.put(new Attributes.Name("X-Compile-Elasticsearch-Version"), "1.2.3"); + + try (JarOutputStream jdbc = new JarOutputStream(Files.newOutputStream(innerJarPath, StandardOpenOption.CREATE), jdbcJarManifest)) {} + + try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(innerJarPath)); + JarOutputStream out = new JarOutputStream(Files.newOutputStream(jarPath, StandardOpenOption.CREATE), new Manifest())) { + JarEntry entry = new JarEntry("es-sql-jdbc.jar!/"); + out.putNextEntry(entry); + + byte[] buffer = new byte[1024]; + while (true) { + int count = in.read(buffer); + if (count == -1) { + break; + } + out.write(buffer, 0, count); + } + } + + URL jarInJar = new URL("jar:" + jarPath.toUri().toURL().toString() + "!/es-sql-jdbc.jar!/"); + + Version version = Version.extractVersion(jarInJar); + assertEquals(1, version.major); + assertEquals(2, version.minor); + assertEquals(3, version.revision); + assertEquals("abc", version.hash); + assertEquals("1.2.3", version.version); + } } From 0035ed2e1f52c17aadf4c6b9710b5897d2fd3e4d Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 4 Feb 2020 16:32:08 +0200 Subject: [PATCH 2/3] Disable caches for jar url connection creation, to prevent locks being kept alive in Windows --- .../xpack/sql/client/Version.java | 27 ++++++++++++------- .../xpack/sql/client/VersionTests.java | 12 +++++---- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/Version.java b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/Version.java index dd217011081e0..93db1b506369f 100644 --- a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/Version.java +++ b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/Version.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.net.URL; +import java.net.URLConnection; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashSet; @@ -85,7 +86,8 @@ static byte[] from(String ver) { // This is similar to how Elasticsearch's Build class digs up its build information. // Since version info is not critical, the parsing is lenient - CURRENT = extractVersion(Version.class.getProtectionDomain().getCodeSource().getLocation()); + URL url = Version.class.getProtectionDomain().getCodeSource().getLocation(); + CURRENT = extractVersion(url); } static Version extractVersion(URL url) { @@ -93,16 +95,21 @@ static Version extractVersion(URL url) { byte maj = 0, min = 0, rev = 0; String ver = "Unknown"; String hash = ver; - + if (urlStr.endsWith(".jar") || urlStr.endsWith(".jar!/")) { - try (JarInputStream jar = new JarInputStream(url.openStream())) { - Manifest manifest = jar.getManifest(); - hash = manifest.getMainAttributes().getValue("Change"); - ver = manifest.getMainAttributes().getValue("X-Compile-Elasticsearch-Version"); - byte[] vers = from(ver); - maj = vers[0]; - min = vers[1]; - rev = vers[2]; + try { + URLConnection conn = url.openConnection(); + conn.setUseCaches(false); + + try (JarInputStream jar = new JarInputStream(conn.getInputStream())) { + Manifest manifest = jar.getManifest(); + hash = manifest.getMainAttributes().getValue("Change"); + ver = manifest.getMainAttributes().getValue("X-Compile-Elasticsearch-Version"); + byte[] vers = from(ver); + maj = vers[0]; + min = vers[1]; + rev = vers[2]; + } } catch (Exception ex) { throw new IllegalArgumentException("Detected Elasticsearch JDBC jar but cannot retrieve its version", ex); } diff --git a/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java b/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java index ce3b18e591bae..5db6a69247684 100644 --- a/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java +++ b/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java @@ -56,17 +56,19 @@ public void testInvalidVersion() { public void testVersionFromJarInJar() throws IOException { Path dir = createTempDir(); - Path jarPath = dir.resolve("foo.jar"); - Path innerJarPath = dir.resolve("es-sql-jdbc.jar"); - + Path jarPath = dir.resolve("foo.jar"); // simulated uberjar containing the jdbc driver + Path innerJarPath = dir.resolve("es-sql-jdbc.jar"); // simulated ES JDBC driver file + Manifest jdbcJarManifest = new Manifest(); Attributes attributes = jdbcJarManifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); attributes.put(new Attributes.Name("Change"), "abc"); attributes.put(new Attributes.Name("X-Compile-Elasticsearch-Version"), "1.2.3"); - + + // create the jdbc driver file try (JarOutputStream jdbc = new JarOutputStream(Files.newOutputStream(innerJarPath, StandardOpenOption.CREATE), jdbcJarManifest)) {} - + + // create the uberjar and embed the jdbc driver one into it try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(innerJarPath)); JarOutputStream out = new JarOutputStream(Files.newOutputStream(jarPath, StandardOpenOption.CREATE), new Manifest())) { JarEntry entry = new JarEntry("es-sql-jdbc.jar!/"); From 265430471233fc829d129d711bec6a3cd6a05f79 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Thu, 6 Feb 2020 18:41:21 +0200 Subject: [PATCH 3/3] Address feedback --- .../elasticsearch/xpack/sql/client/VersionTests.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java b/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java index 5db6a69247684..03979a0a2b4e5 100644 --- a/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java +++ b/x-pack/plugin/sql/sql-client/src/test/java/org/elasticsearch/xpack/sql/client/VersionTests.java @@ -55,9 +55,12 @@ public void testInvalidVersion() { } public void testVersionFromJarInJar() throws IOException { + final String JDBC_JAR_NAME = "es-sql-jdbc.jar"; + final String JAR_PATH_SEPARATOR = "!/"; + Path dir = createTempDir(); - Path jarPath = dir.resolve("foo.jar"); // simulated uberjar containing the jdbc driver - Path innerJarPath = dir.resolve("es-sql-jdbc.jar"); // simulated ES JDBC driver file + Path jarPath = dir.resolve("uberjar.jar"); // simulated uberjar containing the jdbc driver + Path innerJarPath = dir.resolve(JDBC_JAR_NAME); // simulated ES JDBC driver file Manifest jdbcJarManifest = new Manifest(); Attributes attributes = jdbcJarManifest.getMainAttributes(); @@ -71,7 +74,7 @@ public void testVersionFromJarInJar() throws IOException { // create the uberjar and embed the jdbc driver one into it try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(innerJarPath)); JarOutputStream out = new JarOutputStream(Files.newOutputStream(jarPath, StandardOpenOption.CREATE), new Manifest())) { - JarEntry entry = new JarEntry("es-sql-jdbc.jar!/"); + JarEntry entry = new JarEntry(JDBC_JAR_NAME + JAR_PATH_SEPARATOR); out.putNextEntry(entry); byte[] buffer = new byte[1024]; @@ -84,7 +87,7 @@ public void testVersionFromJarInJar() throws IOException { } } - URL jarInJar = new URL("jar:" + jarPath.toUri().toURL().toString() + "!/es-sql-jdbc.jar!/"); + URL jarInJar = new URL("jar:" + jarPath.toUri().toURL().toString() + JAR_PATH_SEPARATOR + JDBC_JAR_NAME + JAR_PATH_SEPARATOR); Version version = Version.extractVersion(jarInJar); assertEquals(1, version.major);