diff --git a/etc/checkstyle/config.xml b/etc/checkstyle/config.xml index 4a010e5e5..8d5869597 100644 --- a/etc/checkstyle/config.xml +++ b/etc/checkstyle/config.xml @@ -42,6 +42,7 @@ { + + System.setProperty("java.version", "1.4"); + System.setProperty("java.vm.vendor", "ms"); + System.setProperty("java.vm.name", "fake"); + System.clearProperty("java.vm.version"); + System.setProperty("neo4j.jdbc.version", "xxx"); + + try (var connection = getConnection(); var statement = connection.createStatement()) { + connection.setClientInfo("ApplicationName", "StubServerTest"); + connection.setAutoCommit(autoCommit); + + Neo4jMetadataWriter metadataWriter = onConnection ? connection.unwrap(Neo4jMetadataWriter.class) + : statement.unwrap(Neo4jMetadataWriter.class); + metadataWriter.withMetadata(Map.of("akey", "aval")); + + statement.unwrap(Neo4jMetadataWriter.class).withMetadata(Map.of("akey2", "aval2")); + + var result = statement.executeQuery("RETURN 1 as n"); + while (result.next()) { + assertThat(result.getInt(1)).isEqualTo(1); + } + if (!autoCommit) { + connection.commit(); + } } - if (!autoCommit) { - connection.commit(); - } - } + }); verifyStubServer(); } diff --git a/neo4j-jdbc-it/neo4j-jdbc-it-stub/src/test/resources/docker/scripts/tx_meta.script b/neo4j-jdbc-it/neo4j-jdbc-it-stub/src/test/resources/docker/scripts/tx_meta.script index 7ef782617..17d75ca25 100644 --- a/neo4j-jdbc-it/neo4j-jdbc-it-stub/src/test/resources/docker/scripts/tx_meta.script +++ b/neo4j-jdbc-it/neo4j-jdbc-it-stub/src/test/resources/docker/scripts/tx_meta.script @@ -3,7 +3,7 @@ A: HELLO {"{}": "*"} A: LOGON {"scheme": "basic", "principal": "neo4j", "credentials": "password"} *: RESET -C: BEGIN {"db": "neo4j", "[tx_type]": "*", "tx_metadata": {"akey": "aval", "akey2": "aval2"}} +C: BEGIN {"db": "neo4j", "[tx_type]": "*", "tx_metadata": {"app": "StubServerTest Java/1.4 (ms fake -) neo4j-jdbc/xxx", "akey": "aval", "akey2": "aval2"}} S: SUCCESS {} C: RUN "RETURN 1 as n" {} {} S: SUCCESS {"fields": ["n"]} diff --git a/neo4j-jdbc/pom.xml b/neo4j-jdbc/pom.xml index 521fda14a..ecc767a26 100644 --- a/neo4j-jdbc/pom.xml +++ b/neo4j-jdbc/pom.xml @@ -67,6 +67,11 @@ org.neo4j neo4j-jdbc-translator-spi + + com.github.stefanbirkner + system-lambda + test + com.tngtech.archunit archunit diff --git a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/ConnectionImpl.java b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/ConnectionImpl.java index bfbce4db3..3a1cc45bf 100644 --- a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/ConnectionImpl.java +++ b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/ConnectionImpl.java @@ -44,6 +44,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletionStage; @@ -53,6 +54,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.logging.Level; @@ -175,17 +177,14 @@ UnaryOperator getTranslator(boolean force, Consumer warningC return sqlTranslator; } - @SuppressWarnings("MagicConstant") // This is the whole purpose here to encapsulate - // those + @SuppressWarnings("MagicConstant") // On purpose @Override public Statement createStatement() throws SQLException { return this.createStatement(ResultSetImpl.SUPPORTED_TYPE, ResultSetImpl.SUPPORTED_CONCURRENCY, ResultSetImpl.SUPPORTED_HOLDABILITY); } - @SuppressWarnings({ "MagicConstant", "SqlSourceToSinkFlow" }) // This is the whole - // purpose here to - // encapsulate those + @SuppressWarnings({ "MagicConstant", "SqlSourceToSinkFlow" }) // On purpose @Override public PreparedStatement prepareStatement(String sql) throws SQLException { return prepareStatement(sql, ResultSetImpl.SUPPORTED_TYPE, ResultSetImpl.SUPPORTED_CONCURRENCY, @@ -738,9 +737,12 @@ Neo4jTransaction getTransaction(Map additionalTransactionMetadat && Neo4jTransaction.State.FAILED.equals(this.transaction.getState())) ? this.boltConnection.reset(false) : null; Map combinedTransactionMetadata = new HashMap<>( - this.transactionMetadata.size() + additionalTransactionMetadata.size()); + this.transactionMetadata.size() + additionalTransactionMetadata.size() + 1); combinedTransactionMetadata.putAll(this.transactionMetadata); combinedTransactionMetadata.putAll(additionalTransactionMetadata); + if (!combinedTransactionMetadata.containsKey("app")) { + combinedTransactionMetadata.put("app", this.getApp()); + } this.transaction = new DefaultTransactionImpl(this.boltConnection, this.bookmarkManager, combinedTransactionMetadata, this::handleFatalException, resetStage, this.autoCommit, getAccessMode(), null); @@ -813,6 +815,33 @@ public Neo4jConnection withMetadata(Map metadata) { return this; } + /** + * A string suitable to be used as {@code app} value inside Neo4j transactional + * metadata. + * @return a string suitable to be used as {@code app} value inside Neo4j + * transactional metadata + */ + String getApp() throws SQLException { + var applicationName = getClientInfo("ApplicationName"); + return String.format("%sJava/%s (%s %s %s) neo4j-jdbc/%s", + (applicationName == null || applicationName.isBlank()) ? "" : applicationName.trim() + " ", + Optional.ofNullable(System.getProperty("java.version")) + .filter(Predicate.not(String::isBlank)) + .orElse("-"), + Optional.ofNullable(System.getProperty("java.vm.vendor")) + .filter(Predicate.not(String::isBlank)) + .orElse("-"), + Optional.ofNullable(System.getProperty("java.vm.name")) + .filter(Predicate.not(String::isBlank)) + .orElse("-"), + Optional.ofNullable(System.getProperty("java.vm.version")) + .filter(Predicate.not(String::isBlank)) + .orElse("-"), + Optional.ofNullable(System.getProperty("neo4j.jdbc.version")) + .filter(Predicate.not(String::isBlank)) + .orElseGet(ProductVersion::getValue)); + } + static class TranslatorChain implements UnaryOperator { private final List translators; diff --git a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/Neo4jDriver.java b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/Neo4jDriver.java index 29bcedc52..1f4b7750a 100644 --- a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/Neo4jDriver.java +++ b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/Neo4jDriver.java @@ -51,7 +51,7 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import org.neo4j.jdbc.internal.bolt.AuthTokens; -import org.neo4j.jdbc.internal.bolt.BoltAgentUtil; +import org.neo4j.jdbc.internal.bolt.BoltAgent; import org.neo4j.jdbc.internal.bolt.BoltConnectionProvider; import org.neo4j.jdbc.internal.bolt.BoltConnectionProviders; import org.neo4j.jdbc.internal.bolt.BoltServerAddress; @@ -333,7 +333,7 @@ public Connection connect(String url, Properties info) throws SQLException { case KERBEROS -> AuthTokens.kerberos(password); }; - var boltAgent = BoltAgentUtil.boltAgent(); + var boltAgent = BoltAgent.of(ProductVersion.getValue()); var userAgent = driverConfig.agent; var connectTimeoutMillis = driverConfig.timeout; diff --git a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/ProductVersion.java b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/ProductVersion.java index 33e7ed363..54eb767fd 100644 --- a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/ProductVersion.java +++ b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/ProductVersion.java @@ -63,7 +63,7 @@ static int getMinorVersion() { private static int getVersion(int idx) { var value = getValue(); - if ("unknown".equalsIgnoreCase(value)) { + if ("dev".equalsIgnoreCase(value)) { throw new IllegalArgumentException("Unsupported or unknown version '%s'".formatted(value)); } var part = value.split("\\.")[idx]; @@ -73,6 +73,8 @@ private static int getVersion(int idx) { private static String getVersionImpl() { try { + // Using Neo4jDriver.class.getPackage().getImplementationVersion() + // doesn't work on the module path Enumeration resources = Neo4jDriver.class.getClassLoader().getResources("META-INF/MANIFEST.MF"); while (resources.hasMoreElements()) { URL url = resources.nextElement(); @@ -87,7 +89,7 @@ private static String getVersionImpl() { throw new UncheckedIOException("Unable to read from neo4j-jdbc manifest.", ex); } - return "unknown"; + return "dev"; } private static boolean isApplicableManifest(Manifest manifest) { diff --git a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/internal/bolt/BoltAgent.java b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/internal/bolt/BoltAgent.java index e6291e163..f7b5e43e8 100644 --- a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/internal/bolt/BoltAgent.java +++ b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/internal/bolt/BoltAgent.java @@ -18,5 +18,46 @@ */ package org.neo4j.jdbc.internal.bolt; +import java.util.Optional; + public record BoltAgent(String product, String platform, String language, String languageDetails) { + + public static BoltAgent of(String driverVersion) { + var platformBuilder = new StringBuilder(); + getProperty("os.name").ifPresent(value -> append(value, platformBuilder)); + getProperty("os.version").ifPresent(value -> append(value, platformBuilder)); + getProperty("os.arch").ifPresent(value -> append(value, platformBuilder)); + + var language = getProperty("java.version").map(version -> "Java/" + version); + + var languageDetailsBuilder = new StringBuilder(); + getProperty("java.vm.vendor").ifPresent(value -> append(value, languageDetailsBuilder)); + getProperty("java.vm.name").ifPresent(value -> append(value, languageDetailsBuilder)); + getProperty("java.vm.version").ifPresent(value -> append(value, languageDetailsBuilder)); + + return new BoltAgent(String.format("neo4j-jdbc/%s", driverVersion), + platformBuilder.isEmpty() ? null : platformBuilder.toString(), language.orElse(null), + languageDetailsBuilder.isEmpty() ? null : languageDetailsBuilder.toString()); + } + + private static Optional getProperty(String key) { + try { + var value = System.getProperty(key); + if (value != null) { + value = value.trim(); + } + return (value != null && !value.isEmpty()) ? Optional.of(value) : Optional.empty(); + } + catch (SecurityException exception) { + return Optional.empty(); + } + } + + private static void append(String value, StringBuilder builder) { + if (value != null && !value.isEmpty()) { + var separator = builder.isEmpty() ? "" : "; "; + builder.append(separator).append(value); + } + } + } diff --git a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/internal/bolt/BoltAgentUtil.java b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/internal/bolt/BoltAgentUtil.java deleted file mode 100644 index f9d856eee..000000000 --- a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/internal/bolt/BoltAgentUtil.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2023-2025 "Neo4j," - * Neo4j Sweden AB [https://neo4j.com] - * - * This file is part of Neo4j. - * - * 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 - * - * https://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.neo4j.jdbc.internal.bolt; - -import java.util.Optional; - -import org.neo4j.jdbc.internal.bolt.internal.BoltProtocol; - -public final class BoltAgentUtil { - - private BoltAgentUtil() { - } - - public static BoltAgent boltAgent() { - var platformBuilder = new StringBuilder(); - getProperty("os.name").ifPresent(value -> append(value, platformBuilder)); - getProperty("os.version").ifPresent(value -> append(value, platformBuilder)); - getProperty("os.arch").ifPresent(value -> append(value, platformBuilder)); - - var language = getProperty("java.version").map(version -> "Java/" + version); - - var languageDetails = language.map(ignored -> { - var languageDetailsBuilder = new StringBuilder(); - getProperty("java.vm.vendor").ifPresent(value -> append(value, languageDetailsBuilder)); - getProperty("java.vm.name").ifPresent(value -> append(value, languageDetailsBuilder)); - getProperty("java.vm.version").ifPresent(value -> append(value, languageDetailsBuilder)); - return languageDetailsBuilder.isEmpty() ? null : languageDetailsBuilder; - }); - - return new BoltAgent(String.format("neo4j-jdbc/%s", driverVersion()), - platformBuilder.isEmpty() ? null : platformBuilder.toString(), language.orElse(null), - languageDetails.isEmpty() ? null : languageDetails.toString()); - } - - /** - * Extracts the driver version from the driver jar MANIFEST.MF file. - * @return the driver version - */ - public static String driverVersion() { - // "Session" is arbitrary - the only thing that matters is that the class we use - // here is in the - // 'org.neo4j.jdbc' package, because that is where the jar manifest specifies - // the version. - // This is done as part of the build, adding a MANIFEST.MF file to the generated - // jarfile. - var pkg = BoltProtocol.class.getPackage(); - if (pkg != null && pkg.getImplementationVersion() != null) { - return pkg.getImplementationVersion(); - } - - // If there is no version, we're not running from a jar file, but from raw - // compiled class files. - // This should only happen during development, so call the version 'dev'. - return "dev"; - } - - private static Optional getProperty(String key) { - try { - var value = System.getProperty(key); - if (value != null) { - value = value.trim(); - } - return (value != null && !value.isEmpty()) ? Optional.of(value) : Optional.empty(); - } - catch (SecurityException exception) { - return Optional.empty(); - } - } - - private static void append(String value, StringBuilder builder) { - if (value != null && !value.isEmpty()) { - var separator = builder.isEmpty() ? "" : "; "; - builder.append(separator).append(value); - } - } - -} diff --git a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/BoltAgentTests.java b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/BoltAgentTests.java new file mode 100644 index 000000000..0125d849b --- /dev/null +++ b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/BoltAgentTests.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023-2025 "Neo4j," + * Neo4j Sweden AB [https://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 + * + * https://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.neo4j.jdbc; + +import org.junit.jupiter.api.Test; +import org.neo4j.jdbc.internal.bolt.BoltAgent; + +import static com.github.stefanbirkner.systemlambda.SystemLambda.restoreSystemProperties; +import static org.assertj.core.api.Assertions.assertThat; + +class BoltAgentTests { + + @Test + void factoryMethodShouldWork() throws Exception { + + restoreSystemProperties(() -> { + System.setProperty("os.name", "foo"); + System.setProperty("os.version", "bar"); + System.setProperty("os.arch", "bazbar"); + + System.setProperty("java.version", "1.4"); + System.setProperty("java.vm.vendor", "ms"); + System.setProperty("java.vm.name", "fake"); + System.clearProperty("java.vm.version"); + + var whatever = BoltAgent.of("whatever"); + assertThat(whatever.product()).isEqualTo("neo4j-jdbc/whatever"); + assertThat(whatever.platform()).isEqualTo("foo; bar; bazbar"); + assertThat(whatever.language()).isEqualTo("Java/1.4"); + assertThat(whatever.languageDetails()).isEqualTo("ms; fake"); + }); + } + +} diff --git a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/ConnectionImplTests.java b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/ConnectionImplTests.java index 0e76f301e..f0d0862de 100644 --- a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/ConnectionImplTests.java +++ b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/ConnectionImplTests.java @@ -55,6 +55,7 @@ import org.neo4j.jdbc.internal.bolt.exception.BoltException; import org.neo4j.jdbc.translator.spi.Translator; +import static com.github.stefanbirkner.systemlambda.SystemLambda.restoreSystemProperties; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -158,8 +159,8 @@ void shouldCommitTransactionOnAutoCommitUpdate(boolean autoCommit) throws SQLExc var connection = makeConnection(boltConnection); connection.setAutoCommit(!autoCommit); var transactionType = connection.getAutoCommit() ? TransactionType.UNCONSTRAINED : TransactionType.DEFAULT; - given(boltConnection.beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, transactionType, - false)) + given(boltConnection.beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), + eq(transactionType), eq(false))) .willReturn(CompletableFuture.completedStage(null)); given(boltConnection.commit()).willReturn(CompletableFuture.completedStage(null)); var transaction = connection.getTransaction(Map.of()); @@ -168,7 +169,7 @@ void shouldCommitTransactionOnAutoCommitUpdate(boolean autoCommit) throws SQLExc assertThat(Neo4jTransaction.State.COMMITTED.equals(transaction.getState())).isTrue(); then(boltConnection).should() - .beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, transactionType, false); + .beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), eq(transactionType), eq(false)); then(boltConnection).should().commit(); then(boltConnection).shouldHaveNoMoreInteractions(); } @@ -189,7 +190,7 @@ void shouldNotCommitTransactionOnSameAutoCommit(boolean autoCommit) throws SQLEx assertThat(Neo4jTransaction.State.NEW.equals(transaction.getState())).isTrue(); then(boltConnection).should() - .beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, transactionType, false); + .beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), eq(transactionType), eq(false)); then(boltConnection).shouldHaveNoMoreInteractions(); } @@ -209,7 +210,8 @@ void shouldThrowOnManagingAutoCommitTransaction(boolean rollback) throws SQLExce assertThat(Neo4jTransaction.State.NEW.equals(transaction.getState())).isTrue(); then(boltConnection).should() - .beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, TransactionType.UNCONSTRAINED, false); + .beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), + eq(TransactionType.UNCONSTRAINED), eq(false)); then(boltConnection).shouldHaveNoMoreInteractions(); } @@ -219,8 +221,8 @@ void shouldManageTransaction(boolean rollback) throws SQLException { var boltConnection = mock(BoltConnection.class); var connection = makeConnection(boltConnection); connection.setAutoCommit(false); - given(boltConnection.beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, - TransactionType.DEFAULT, false)) + given(boltConnection.beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), + eq(TransactionType.DEFAULT), eq(false))) .willReturn(CompletableFuture.completedStage(null)); if (rollback) { given(boltConnection.rollback()).willReturn(CompletableFuture.completedStage(null)); @@ -240,7 +242,8 @@ void shouldManageTransaction(boolean rollback) throws SQLException { assertThat(transaction.getState() .equals(rollback ? Neo4jTransaction.State.ROLLEDBACK : Neo4jTransaction.State.COMMITTED)).isTrue(); then(boltConnection).should() - .beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, TransactionType.DEFAULT, false); + .beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), eq(TransactionType.DEFAULT), + eq(false)); then(boltConnection).should(times(rollback ? 1 : 0)).rollback(); then(boltConnection).should(times(rollback ? 0 : 1)).commit(); then(boltConnection).shouldHaveNoMoreInteractions(); @@ -270,8 +273,8 @@ void shouldClose() throws SQLException { @Test void shouldRollbackOnClose() throws SQLException { var boltConnection = mock(BoltConnection.class); - given(boltConnection.beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, - TransactionType.UNCONSTRAINED, false)) + given(boltConnection.beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), + eq(TransactionType.UNCONSTRAINED), eq(false))) .willReturn(CompletableFuture.completedStage(null)); given(boltConnection.rollback()).willReturn(CompletableFuture.completedStage(null)); given(boltConnection.close()).willReturn(CompletableFuture.completedStage(null)); @@ -283,7 +286,8 @@ void shouldRollbackOnClose() throws SQLException { assertThat(connection.isClosed()).isTrue(); assertThat(Neo4jTransaction.State.ROLLEDBACK.equals(transaction.getState())).isTrue(); then(boltConnection).should() - .beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, TransactionType.UNCONSTRAINED, false); + .beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), + eq(TransactionType.UNCONSTRAINED), eq(false)); then(boltConnection).should().rollback(); then(boltConnection).should().close(); then(boltConnection).shouldHaveNoMoreInteractions(); @@ -323,7 +327,8 @@ void shouldPassAccessModeToBoltConnection(boolean readOnly) throws SQLException // then assertThat(transaction).isNotNull(); then(boltConnection).should() - .beginTransaction(Collections.emptySet(), Map.of(), accessMode, TransactionType.UNCONSTRAINED, false); + .beginTransaction(eq(Collections.emptySet()), any(), eq(accessMode), eq(TransactionType.UNCONSTRAINED), + eq(false)); } @Test @@ -426,8 +431,8 @@ void shouldSendResetOnValidatingWithoutActiveTransaction(boolean setupClosedTran var boltConnection = mock(BoltConnection.class); var connection = makeConnection(boltConnection); if (setupClosedTransaction) { - given(boltConnection.beginTransaction(Collections.emptySet(), Map.of(), AccessMode.WRITE, - TransactionType.UNCONSTRAINED, false)) + given(boltConnection.beginTransaction(eq(Collections.emptySet()), any(), eq(AccessMode.WRITE), + eq(TransactionType.UNCONSTRAINED), eq(false))) .willReturn(CompletableFuture.completedStage(null)); given(boltConnection.commit()).willReturn(CompletableFuture.completedStage(null)); var transaction = connection.getTransaction(Map.of()); @@ -770,6 +775,29 @@ void shouldNotAcceptNegativeNetworkTimeout() { assertThat(connection.getNetworkTimeout()).isEqualTo(0); } + @ParameterizedTest + @ValueSource(strings = { "foo", "n/a" }) + void getAppShouldWork(String applicationName) throws Exception { + + restoreSystemProperties(() -> { + var connection = makeConnection(mock(BoltConnection.class)); + + var expected = ""; + if (!"n/a".equals(applicationName)) { + connection.setClientInfo("ApplicationName", applicationName); + expected = applicationName + " "; + } + expected += "Java/1.4 (ms fake -) neo4j-jdbc/" + ProductVersion.getValue(); + + System.setProperty("java.version", "1.4"); + System.setProperty("java.vm.vendor", "ms"); + System.setProperty("java.vm.name", "fake"); + System.clearProperty("java.vm.version"); + + assertThat(connection.getApp()).isEqualTo(expected); + }); + } + @FunctionalInterface private interface ConnectionMethodRunner { diff --git a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/DatabaseMetadataImplTests.java b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/DatabaseMetadataImplTests.java index 6eafde1a6..051137905 100644 --- a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/DatabaseMetadataImplTests.java +++ b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/DatabaseMetadataImplTests.java @@ -80,7 +80,7 @@ void getDriverMajorVersion() { var databaseMetadata = newDatabaseMetadata(); Assertions.assertThatIllegalArgumentException() .isThrownBy(databaseMetadata::getDriverMajorVersion) - .withMessage("Unsupported or unknown version 'unknown'"); + .withMessage("Unsupported or unknown version 'dev'"); } @Test @@ -88,7 +88,7 @@ void getDriverMinorVersion() { var databaseMetadata = newDatabaseMetadata(); Assertions.assertThatIllegalArgumentException() .isThrownBy(databaseMetadata::getDriverMinorVersion) - .withMessage("Unsupported or unknown version 'unknown'"); + .withMessage("Unsupported or unknown version 'dev'"); } @Test diff --git a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/Neo4jDriverTests.java b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/Neo4jDriverTests.java index e4a91acb5..e42efcf6e 100644 --- a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/Neo4jDriverTests.java +++ b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/Neo4jDriverTests.java @@ -146,7 +146,7 @@ void sqlTranslatorShouldBeLoadedImmediateWithAutomaticTranslation() { @Test void defaultUAShouldWork() { - assertThat(Neo4jDriver.getDefaultUserAgent()).matches("neo4j-jdbc/unknown"); + assertThat(Neo4jDriver.getDefaultUserAgent()).matches("neo4j-jdbc/dev"); } } diff --git a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/Neo4jDriverUrlParsingTests.java b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/Neo4jDriverUrlParsingTests.java index 43d234cb9..8b959f7a0 100644 --- a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/Neo4jDriverUrlParsingTests.java +++ b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/Neo4jDriverUrlParsingTests.java @@ -301,7 +301,7 @@ void testMinimalGetPropertyInfo() throws SQLException { case "user" -> assertThat(info.value).isEqualTo("neo4j"); case "password" -> assertThat(info.value).isEqualTo("password"); case "authRealm" -> assertThat(info.value).isEqualTo(""); - case "agent" -> assertThat(info.value).isEqualTo("neo4j-jdbc/unknown"); + case "agent" -> assertThat(info.value).isEqualTo("neo4j-jdbc/dev"); case "timeout" -> assertThat(info.value).isEqualTo("1000"); case "enableSQLTranslation", "ssl", "s2c.alwaysEscapeNames", "s2c.prettyPrint" -> assertThat(info.value).isEqualTo("false"); @@ -328,7 +328,7 @@ void testMinimalParseConfig() throws SQLException { assertThat(config.user()).isEqualTo("neo4j"); assertThat(config.password()).isEqualTo("password"); assertThat(config.authRealm()).isEqualTo(""); - assertThat(config.agent()).isEqualTo("neo4j-jdbc/unknown"); + assertThat(config.agent()).isEqualTo("neo4j-jdbc/dev"); assertThat(config.timeout()).isEqualTo(1000); assertThat(config.enableSQLTranslation()).isFalse(); assertThat(config.enableTranslationCaching()).isFalse(); @@ -390,7 +390,7 @@ void testGetPropertyInfoPropertyOverrides() throws SQLException { case "user" -> assertThat(info.value).isEqualTo("user1"); case "password" -> assertThat(info.value).isEqualTo("user1Password"); case "authRealm" -> assertThat(info.value).isEqualTo("myRealm"); - case "agent" -> assertThat(info.value).isEqualTo("neo4j-jdbc/unknown"); + case "agent" -> assertThat(info.value).isEqualTo("neo4j-jdbc/dev"); case "timeout" -> assertThat(info.value).isEqualTo("2000"); case "cacheSQLTranslations" -> assertThat(info.value).isEqualTo("true"); case "ssl", "rewriteBatchedStatements", "s2c.alwaysEscapeNames", "s2c.prettyPrint", diff --git a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/ProductVersionTests.java b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/ProductVersionTests.java index dab34aa96..c7c9c8d03 100644 --- a/neo4j-jdbc/src/test/java/org/neo4j/jdbc/ProductVersionTests.java +++ b/neo4j-jdbc/src/test/java/org/neo4j/jdbc/ProductVersionTests.java @@ -25,7 +25,7 @@ class ProductVersionTests { @Test void getValueShouldWork() { - Assertions.assertThat(ProductVersion.getValue()).isEqualTo("unknown"); + Assertions.assertThat(ProductVersion.getValue()).isEqualTo("dev"); } @Test @@ -33,7 +33,7 @@ void getMajorVersion() { Assertions.assertThatIllegalArgumentException() .isThrownBy(ProductVersion::getMajorVersion) - .withMessage("Unsupported or unknown version 'unknown'"); + .withMessage("Unsupported or unknown version 'dev'"); } @Test @@ -41,7 +41,7 @@ void getMinorVersion() { Assertions.assertThatIllegalArgumentException() .isThrownBy(ProductVersion::getMinorVersion) - .withMessage("Unsupported or unknown version 'unknown'"); + .withMessage("Unsupported or unknown version 'dev'"); } } diff --git a/pom.xml b/pom.xml index 98565e97e..1a0157c88 100644 --- a/pom.xml +++ b/pom.xml @@ -196,6 +196,7 @@ 4.0.0 3.4.1 0.0.43 + 1.2.1 1.20.4 @@ -243,6 +244,11 @@ pom import + + com.github.stefanbirkner + system-lambda + ${system-lambda.version} + com.opencsv opencsv @@ -749,6 +755,10 @@ MINOR + + + org.neo4j.jdbc.internal.bolt +