diff --git a/pom.xml b/pom.xml
index 97c020916..fc46b2952 100644
--- a/pom.xml
+++ b/pom.xml
@@ -426,6 +426,32 @@
+
+
+ alt_build_dir
+
+
+ alt.build.dir
+
+
+
+ ${alt.build.dir}
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.0
+
+
+
+ ${alt.build.dir}/single.jar
+
+
+
+
+
+
+
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/SingleJarTestApplication.java b/src/test/java/com/google/cloud/spanner/jdbc/SingleJarTestApplication.java
new file mode 100644
index 000000000..e9cfba1b2
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/SingleJarTestApplication.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed 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 com.google.cloud.spanner.jdbc;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+
+/**
+ * Simple Java application that is used to verify the working of the single-jar-with-dependencies.
+ */
+public class SingleJarTestApplication {
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 3) {
+ throw new IllegalArgumentException("expected 3 arguments");
+ }
+ String project = args[0];
+ String instance = args[1];
+ String database = args[2];
+ String extraOptions = "";
+ String host = "";
+ if (System.getenv("SPANNER_EMULATOR_HOST") != null) {
+ extraOptions = "?autoConfigEmulator=true";
+ host = "//" + System.getenv("SPANNER_EMULATOR_HOST");
+ }
+
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:%s/projects/%s/instances/%s/databases/%s%s",
+ host, project, instance, database, extraOptions))) {
+ try (ResultSet resultSet =
+ connection.createStatement().executeQuery("select 'Hello World from Real Spanner!'")) {
+ while (resultSet.next()) {
+ System.out.println(resultSet.getString(1));
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITSingleJarTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITSingleJarTest.java
new file mode 100644
index 000000000..9ae8f6003
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITSingleJarTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed 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 com.google.cloud.spanner.jdbc.it;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.cloud.spanner.Database;
+import com.google.cloud.spanner.DatabaseId;
+import com.google.cloud.spanner.ParallelIntegrationTest;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.CharStreams;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests that the following works:
+ *
+ *
+ * - Build a single-jar-with-dependencies
+ *
- Compile a simple Java application consisting of a single file and no dependencies to a
+ * class file
+ *
- Run the simple Java application with only itself + the single-jar-with-dependencies on the
+ * class path
+ *
+ */
+@Category(ParallelIntegrationTest.class)
+@RunWith(JUnit4.class)
+public class ITSingleJarTest extends ITAbstractJdbcTest {
+ @ClassRule public static JdbcIntegrationTestEnv env = new JdbcIntegrationTestEnv();
+
+ private Database database;
+
+ @Before
+ public void setup() {
+ database =
+ env.getOrCreateDatabase(
+ getDialect(),
+ ImmutableList.of("create table test (id int64, value string(max)) primary key (id)"));
+ }
+
+ @Test
+ public void testUseSingleJar() throws Exception {
+ buildSingleJar();
+ buildMainClass();
+ runTestApplication();
+ }
+
+ @Test
+ public void testUseShadedJar() throws Exception {
+ buildShadedJar();
+ buildMainClass();
+ runTestApplication();
+ }
+
+ private void runTestApplication() throws Exception {
+ DatabaseId id = database.getId();
+ ProcessBuilder builder = new ProcessBuilder();
+ if (System.getenv("SPANNER_EMULATOR_HOST") != null) {
+ builder.environment().put("SPANNER_EMULATOR_HOST", System.getenv("SPANNER_EMULATOR_HOST"));
+ }
+ // This runs the simple test application with only the shaded jar on the classpath.
+ builder.command(
+ "java",
+ "-cp",
+ "./target/single/test/:target/single/single.jar",
+ "com/google/cloud/spanner/jdbc/SingleJarTestApplication",
+ id.getInstanceId().getProject(),
+ id.getInstanceId().getInstance(),
+ id.getDatabase());
+ execute(builder);
+ }
+
+ private void buildSingleJar() throws Exception {
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command("mvn", "clean", "package", "-DskipTests", "-Dalt.build.dir=./target/single");
+ execute(builder);
+ }
+
+ private void buildShadedJar() throws Exception {
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command(
+ "mvn", "clean", "-Pshade", "package", "-DskipTests", "-Dalt.build.dir=./target/single");
+ execute(builder);
+ }
+
+ private void buildMainClass() throws Exception {
+ Files.createDirectories(Paths.get("target", "single", "test"));
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command(
+ "javac",
+ "src/test/java/com/google/cloud/spanner/jdbc/SingleJarTestApplication.java",
+ "-d",
+ "./target/single/test");
+ execute(builder);
+ }
+
+ private void execute(ProcessBuilder builder) throws Exception {
+ Process process = builder.start();
+ String errors;
+ try (InputStreamReader reader = new InputStreamReader(process.getErrorStream())) {
+ errors = CharStreams.toString(reader);
+ }
+ assertEquals(errors, 0, process.waitFor());
+ }
+}