CASSANDRASC-67 Fix relocation of native libraries for vertx-client-shaded

OpenSSL is currently unavailable in the vertx-client-shaded library. This is due to the
incorrect relocation of the netty native libraries under META-INF/native/libnetty.

In this commit we fix the relocation of the native libraries and we ensure that the
relocation is properly configured by adding tests that guarantee OpenSSL loads correctly
with the correct relocation.

patch by Francisco Guerrero; reviewed by Dinesh Joshi, Yifan Cai for CASSANDRASC-67
diff --git a/CHANGES.txt b/CHANGES.txt
index 7974993..3757c78 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 1.0.0
 -----
+ * Fix relocation of native libraries for vertx-client-shaded (CASSANDRASC-67)
  * Enrich RetriesExhaustedException to have more information for better visibility (CASSANDRASC-65)
  * Fix failing unit tests in Apache CI (CASSANDRASC-66)
  * Support credential rotation in JmxClient (CASSANDRASC-63)
diff --git a/client/src/main/java/org/apache/cassandra/sidecar/client/exception/RetriesExhaustedException.java b/client/src/main/java/org/apache/cassandra/sidecar/client/exception/RetriesExhaustedException.java
index 58ee398..9d765ce 100644
--- a/client/src/main/java/org/apache/cassandra/sidecar/client/exception/RetriesExhaustedException.java
+++ b/client/src/main/java/org/apache/cassandra/sidecar/client/exception/RetriesExhaustedException.java
@@ -32,6 +32,7 @@
      * @param attempts      the number of attempts performed for the request
      * @param request       the HTTP request
      * @param lastResponse  the last failed HTTP response
+     * @return the constructed {@link RetriesExhaustedException exception}
      */
     public static RetriesExhaustedException of(int attempts,
                                                Request request,
@@ -47,6 +48,7 @@
      * @param request       the HTTP request
      * @param lastResponse  the last failed HTTP response
      * @param throwable     the underlying exception
+     * @return the constructed {@link RetriesExhaustedException exception}
      */
     public static RetriesExhaustedException of(int attempts,
                                                Request request,
diff --git a/scripts/build-dtest-jars.sh b/scripts/build-dtest-jars.sh
index 80b40cc..9f4d79c 100755
--- a/scripts/build-dtest-jars.sh
+++ b/scripts/build-dtest-jars.sh
@@ -23,11 +23,13 @@
 SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; )
 DTEST_JAR_DIR="$(dirname "${SCRIPT_DIR}/")/dtest-jars"
 BUILD_DIR="${DTEST_JAR_DIR}/build"
+source "$SCRIPT_DIR/functions.sh"
 mkdir -p "${BUILD_DIR}"
 
 # host key verification
 mkdir -p ~/.ssh
-ssh-keyscan github.com >> ~/.ssh/known_hosts
+REPO_HOST=$(get_hostname "${REPO}")
+ssh-keyscan "${REPO_HOST}" >> ~/.ssh/known_hosts || true
 
 for branch in $BRANCHES; do
   cd "${BUILD_DIR}"
diff --git a/scripts/functions.sh b/scripts/functions.sh
new file mode 100755
index 0000000..149afce
--- /dev/null
+++ b/scripts/functions.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+get_hostname() {
+  if [[ $# -eq 0 ]] ; then
+      echo 'An URI must be provided'
+      exit 1
+  fi
+
+  # removes the scheme portion of the URI (i.e. https://, http://)
+  hostname=$(echo "${1}" | sed -E 's/^\s*.*:\/\///g')
+  # removes the first encounter of a forward slash / until the end of the string
+  hostname=${hostname%%/*}
+  # removes the first encounter of a colon : until the end of the string i.e git@github.com:apache -> git@github.com
+  hostname=${hostname%%:*}
+  # removes the username portion of an URI i.e. git@github.com -> github.com
+  hostname=$(echo "${hostname}" | cut -d@ -f2-)
+
+  echo "${hostname}"
+}
+
+test_get_hostname() {
+  get_hostname "https://github.com/apache/cassandra-sidecar" # returns github.com
+  get_hostname "git@github.com:apache/cassandra-sidecar.git" # returns github.com
+  get_hostname # returns An URI must be provided message with exit code 1
+}
diff --git a/vertx-client-shaded/build.gradle b/vertx-client-shaded/build.gradle
index 29aac42..7690614 100644
--- a/vertx-client-shaded/build.gradle
+++ b/vertx-client-shaded/build.gradle
@@ -27,6 +27,8 @@
 import shadow.org.apache.tools.zip.ZipEntry
 import shadow.org.apache.tools.zip.ZipOutputStream
 
+import java.nio.file.Paths
+
 plugins {
     id('java-library')
     id('maven-publish')
@@ -46,6 +48,21 @@
     shadow(group: 'org.slf4j', name: 'slf4j-api', version: "${project.slf4jVersion}")
     shadow(group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.14.2')
     api(project(':vertx-client'))
+
+    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
+    testImplementation(project(path: ':vertx-client-shaded', configuration: 'shadow'))
+}
+
+tasks.named('test') {
+    // Use JUnit Platform for unit tests.
+    useJUnitPlatform()
+    reports {
+        junitXml.enabled = true
+        def destDir = Paths.get(rootProject.rootDir.absolutePath, "build", "test-results", "vertx-client-shaded").toFile()
+        println("Destination directory for vertx-client-shaded tests: ${destDir}")
+        junitXml.destination = destDir
+        html.enabled = true
+    }
 }
 
 // Relocating a Package
@@ -55,8 +72,7 @@
     relocate 'com.fasterxml.jackson.databind', 'o.a.c.sidecar.client.shaded.com.fasterxml.jackson.databind'
     relocate 'io.netty', 'o.a.c.sidecar.client.shaded.io.netty'
     relocate 'io.vertx', 'o.a.c.sidecar.client.shaded.io.vertx'
-    relocate 'META-INF/native/libnetty', 'META-INF/native/sidecar_client_netty_shaded_netty'
-    relocate 'META-INF/native/netty', 'META-INF/native/io_sidecar_client_netty_shaded_netty'
+    relocate 'META-INF/native/libnetty', 'META-INF/native/libo_a_c_sidecar_client_shaded_netty'
     relocate 'META-INF/versions/11/io/vertx', 'META-INF/versions/11/o/a/c/sidecar/client/shaded/io/vertx'
     transform(NettyResourceTransformer.class)
     mergeServiceFiles()
diff --git a/vertx-client-shaded/src/test/java/org/apache/cassandra/sidecar/client/test/LibraryTest.java b/vertx-client-shaded/src/test/java/org/apache/cassandra/sidecar/client/test/LibraryTest.java
new file mode 100644
index 0000000..88f6a1a
--- /dev/null
+++ b/vertx-client-shaded/src/test/java/org/apache/cassandra/sidecar/client/test/LibraryTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.sidecar.client.test;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import o.a.c.sidecar.client.shaded.io.vertx.core.net.OpenSSLEngineOptions;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests for the vertx-client-shaded project
+ */
+class LibraryTest
+{
+    @DisplayName("Ensures that the shading is correct for the vertx-client-shaded project")
+    @Test
+    void openSslIsAvailable()
+    {
+        assertTrue(OpenSSLEngineOptions.isAvailable());
+    }
+}
diff --git a/vertx-client/build.gradle b/vertx-client/build.gradle
index 37933ed..ce02f60 100644
--- a/vertx-client/build.gradle
+++ b/vertx-client/build.gradle
@@ -56,7 +56,20 @@
     api(group: 'io.vertx', name: 'vertx-web-client', version: '4.4.1') {
         exclude group: 'com.fasterxml.jackson.core', module: 'jackson-core'
     }
-    implementation(group: 'io.netty', name: 'netty-tcnative-boringssl-static', version: '2.0.44.Final')  // for openSSL
+    implementation(group: 'io.netty', name: 'netty-tcnative-boringssl-static', version: '2.0.61.Final')  // for openSSL
+
+    // The newer versions (2.0.48.Final+) of tcnative require explicit dependency declarations,
+    // including the classifiers. See https://netty.io/wiki/forked-tomcat-native.html#gradle-and-bazel
+    // for details.
+
+    // openSSL native libraries for linux x86-64 architectures
+    implementation(group: 'io.netty', name: 'netty-tcnative-boringssl-static', version: '2.0.61.Final', classifier: 'linux-x86_64')
+    // openSSL native libraries for macOS aarch-64 architectures
+    implementation(group: 'io.netty', name: 'netty-tcnative-boringssl-static', version: '2.0.61.Final', classifier: 'osx-aarch_64')
+    // openSSL native libraries for linux aarch-64 architectures
+    implementation(group: 'io.netty', name: 'netty-tcnative-boringssl-static', version: '2.0.61.Final', classifier: 'linux-aarch_64')
+    // openSSL native libraries for macOS x86-64 architectures
+    implementation(group: 'io.netty', name: 'netty-tcnative-boringssl-static', version: '2.0.61.Final', classifier: 'osx-x86_64')
     implementation("org.slf4j:slf4j-api:${project.slf4jVersion}")
 
     compileOnly('org.jetbrains:annotations:23.0.0')
diff --git a/vertx-client/src/main/java/org/apache/cassandra/sidecar/client/VertxHttpClient.java b/vertx-client/src/main/java/org/apache/cassandra/sidecar/client/VertxHttpClient.java
index 3fa0f7d..02a07c3 100644
--- a/vertx-client/src/main/java/org/apache/cassandra/sidecar/client/VertxHttpClient.java
+++ b/vertx-client/src/main/java/org/apache/cassandra/sidecar/client/VertxHttpClient.java
@@ -304,8 +304,13 @@
 
         if (OpenSSLEngineOptions.isAvailable())
         {
+            LOGGER.info("Building Sidecar vertx client with OpenSSL");
             options = options.setOpenSslEngineOptions(new OpenSSLEngineOptions());
         }
+        else
+        {
+            LOGGER.warn("OpenSSL not available when building Sidecar vertx client");
+        }
 
         return options;
     }