diff --git a/LICENSES.txt b/LICENSES.txt index be97825be..4599bb031 100644 --- a/LICENSES.txt +++ b/LICENSES.txt @@ -30,12 +30,15 @@ Apache-2.0 cassandra-driver-core-3.10.0.jar commons-cli-1.9.0.jar commons-codec-1.17.1.jar + commons-codec-1.19.0.jar commons-collections4-4.4.jar commons-compress-1.27.1.jar + commons-compress-1.28.0.jar commons-configuration2-2.11.0.jar commons-csv-1.9.0.jar commons-daemon-1.0.13.jar commons-io-2.17.0.jar + commons-io-2.20.0.jar commons-lang-2.6.jar commons-lang3-3.18.0.jar commons-logging-1.3.2.jar @@ -46,9 +49,9 @@ Apache-2.0 curator-client-5.2.0.jar curator-framework-5.2.0.jar curator-recipes-5.2.0.jar - docker-java-api-3.4.0.jar - docker-java-transport-3.4.0.jar - docker-java-transport-zerodep-3.4.0.jar + docker-java-api-3.7.0.jar + docker-java-transport-3.7.0.jar + docker-java-transport-zerodep-3.7.0.jar ehcache-3.8.2.jar error_prone_annotations-2.2.0.jar failureaccess-1.0.1.jar @@ -94,6 +97,7 @@ Apache-2.0 jPowerShell-3.0.jar jProcesses-1.6.5.jar jackson-annotations-2.18.0.jar + jackson-annotations-2.20.jar jackson-core-2.18.0.jar jackson-databind-2.18.0.jar jackson-dataformat-cbor-2.18.0.jar @@ -133,6 +137,7 @@ Apache-2.0 jffi-1.2.16.jar jmespath-java-1.12.643.jar jna-5.15.0.jar + jna-5.18.1.jar jnr-constants-0.9.9.jar jnr-ffi-2.1.7.jar joda-time-2.8.1.jar @@ -1685,10 +1690,10 @@ Eclipse Distribution License - v 1.0 jakarta.activation-api-1.2.2.jar jakarta.xml.bind-api-2.3.2.jar jaxb-runtime-2.3.2.jar - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar stax-ex-1.8.1.jar txw2-2.3.2.jar ------------------------------------------------------------------------------ @@ -1942,12 +1947,12 @@ Eclipse Public License - v 2.0 jakarta.annotation-api-1.3.5.jar jakarta.inject-2.6.1.jar jakarta.ws.rs-api-2.1.6.jar - jersey-client-2.43.jar - jersey-common-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar - jersey-server-2.43.jar + jersey-client-2.46.jar + jersey-common-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar + jersey-server-2.46.jar junit-jupiter-5.11.2.jar junit-jupiter-api-5.11.2.jar junit-jupiter-engine-5.11.2.jar @@ -2639,28 +2644,28 @@ MIT bcpkix-jdk18on-1.79.jar bcprov-jdk18on-1.79.jar bcutil-jdk18on-1.79.jar - cassandra-1.20.2.jar checker-qual-2.5.2.jar - couchbase-1.20.2.jar - database-commons-1.20.2.jar duct-tape-1.0.8.jar - elasticsearch-1.20.2.jar - jdbc-1.20.2.jar - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar jnr-x86asm-1.0.2.jar - localstack-1.20.2.jar mockito-core-5.14.1.jar mssql-jdbc-6.2.1.jre7.jar - mysql-1.20.2.jar - neo4j-1.20.2.jar - postgresql-1.20.2.jar reactive-streams-1.0.4.jar slf4j-api-2.0.11.jar slf4j-api-2.0.17.jar - testcontainers-1.20.2.jar + testcontainers-2.0.2.jar + testcontainers-cassandra-2.0.2.jar + testcontainers-couchbase-2.0.2.jar + testcontainers-database-commons-2.0.2.jar + testcontainers-elasticsearch-2.0.2.jar + testcontainers-jdbc-2.0.2.jar + testcontainers-localstack-2.0.2.jar + testcontainers-mysql-2.0.2.jar + testcontainers-neo4j-2.0.2.jar + testcontainers-postgresql-2.0.2.jar ------------------------------------------------------------------------------ The MIT License @@ -3068,11 +3073,11 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice Public Domain, per Creative Commons CC0 HdrHistogram-2.1.9.jar aopalliance-1.0.jar - jersey-client-2.43.jar - jersey-common-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-common-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar ------------------------------------------------------------------------------ Creative Commons Legal Code diff --git a/NOTICE.txt b/NOTICE.txt index 1623bc99d..21ae6b32a 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -26,12 +26,12 @@ Third-party licenses -------------------- Apache License, 2.0 - jersey-client-2.43.jar - jersey-common-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar - jersey-server-2.43.jar + jersey-client-2.46.jar + jersey-common-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar + jersey-server-2.46.jar Apache-2.0 FastInfoset-1.2.16.jar @@ -60,12 +60,15 @@ Apache-2.0 cassandra-driver-core-3.10.0.jar commons-cli-1.9.0.jar commons-codec-1.17.1.jar + commons-codec-1.19.0.jar commons-collections4-4.4.jar commons-compress-1.27.1.jar + commons-compress-1.28.0.jar commons-configuration2-2.11.0.jar commons-csv-1.9.0.jar commons-daemon-1.0.13.jar commons-io-2.17.0.jar + commons-io-2.20.0.jar commons-lang-2.6.jar commons-lang3-3.18.0.jar commons-logging-1.3.2.jar @@ -76,9 +79,9 @@ Apache-2.0 curator-client-5.2.0.jar curator-framework-5.2.0.jar curator-recipes-5.2.0.jar - docker-java-api-3.4.0.jar - docker-java-transport-3.4.0.jar - docker-java-transport-zerodep-3.4.0.jar + docker-java-api-3.7.0.jar + docker-java-transport-3.7.0.jar + docker-java-transport-zerodep-3.7.0.jar ehcache-3.8.2.jar error_prone_annotations-2.2.0.jar failureaccess-1.0.1.jar @@ -124,6 +127,7 @@ Apache-2.0 jPowerShell-3.0.jar jProcesses-1.6.5.jar jackson-annotations-2.18.0.jar + jackson-annotations-2.20.jar jackson-core-2.18.0.jar jackson-databind-2.18.0.jar jackson-dataformat-cbor-2.18.0.jar @@ -163,6 +167,7 @@ Apache-2.0 jffi-1.2.16.jar jmespath-java-1.12.643.jar jna-5.15.0.jar + jna-5.18.1.jar jnr-constants-0.9.9.jar jnr-ffi-2.1.7.jar joda-time-2.8.1.jar @@ -262,10 +267,10 @@ Apache-2.0 zookeeper-jute-3.8.4.jar BSD 2-Clause - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar BSD-2-Clause jline-3.9.0.jar @@ -322,10 +327,10 @@ Eclipse Distribution License - v 1.0 jakarta.activation-api-1.2.2.jar jakarta.xml.bind-api-2.3.2.jar jaxb-runtime-2.3.2.jar - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar stax-ex-1.8.1.jar txw2-2.3.2.jar @@ -372,12 +377,12 @@ Eclipse Public License - v 2.0 jakarta.annotation-api-1.3.5.jar jakarta.inject-2.6.1.jar jakarta.ws.rs-api-2.1.6.jar - jersey-client-2.43.jar - jersey-common-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar - jersey-server-2.43.jar + jersey-client-2.46.jar + jersey-common-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar + jersey-server-2.46.jar junit-jupiter-5.11.2.jar junit-jupiter-api-5.11.2.jar junit-jupiter-engine-5.11.2.jar @@ -409,10 +414,10 @@ GPL2 w/ CPE jakarta.annotation-api-1.3.5.jar jakarta.inject-2.6.1.jar jakarta.ws.rs-api-2.1.6.jar - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar osgi-resource-locator-1.0.3.jar GPL2 w/ CPE @@ -424,34 +429,35 @@ LGPL 2.1 LGPL-2.1-or-later jna-5.15.0.jar + jna-5.18.1.jar MIT animal-sniffer-annotations-1.17.jar bcpkix-jdk18on-1.79.jar bcprov-jdk18on-1.79.jar bcutil-jdk18on-1.79.jar - cassandra-1.20.2.jar checker-qual-2.5.2.jar - couchbase-1.20.2.jar - database-commons-1.20.2.jar duct-tape-1.0.8.jar - elasticsearch-1.20.2.jar - jdbc-1.20.2.jar - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar jnr-x86asm-1.0.2.jar - localstack-1.20.2.jar mockito-core-5.14.1.jar mssql-jdbc-6.2.1.jre7.jar - mysql-1.20.2.jar - neo4j-1.20.2.jar - postgresql-1.20.2.jar reactive-streams-1.0.4.jar slf4j-api-2.0.11.jar slf4j-api-2.0.17.jar - testcontainers-1.20.2.jar + testcontainers-2.0.2.jar + testcontainers-cassandra-2.0.2.jar + testcontainers-couchbase-2.0.2.jar + testcontainers-database-commons-2.0.2.jar + testcontainers-elasticsearch-2.0.2.jar + testcontainers-jdbc-2.0.2.jar + testcontainers-localstack-2.0.2.jar + testcontainers-mysql-2.0.2.jar + testcontainers-neo4j-2.0.2.jar + testcontainers-postgresql-2.0.2.jar MPL 1.1 javassist-3.30.2-GA.jar @@ -460,36 +466,36 @@ MPL-2.0 kiama_2.13-2.5.1.jar Modified BSD - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar - jersey-server-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar + jersey-server-2.46.jar Public Domain, per Creative Commons CC0 HdrHistogram-2.1.9.jar aopalliance-1.0.jar - jersey-client-2.43.jar - jersey-common-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-common-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar The GNU General Public License (GPL), Version 2, With Classpath Exception - jersey-common-2.43.jar - jersey-server-2.43.jar + jersey-common-2.46.jar + jersey-server-2.46.jar The Go license re2j-1.1.jar W3C license - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar jQuery license - jersey-client-2.43.jar - jersey-container-servlet-2.43.jar - jersey-container-servlet-core-2.43.jar - jersey-hk2-2.43.jar + jersey-client-2.46.jar + jersey-container-servlet-2.46.jar + jersey-container-servlet-core-2.46.jar + jersey-hk2-2.46.jar diff --git a/core/src/main/java/apoc/cypher/CypherInitializer.java b/core/src/main/java/apoc/cypher/CypherInitializer.java index 04099f07a..ef1d67fb5 100644 --- a/core/src/main/java/apoc/cypher/CypherInitializer.java +++ b/core/src/main/java/apoc/cypher/CypherInitializer.java @@ -22,6 +22,7 @@ import apoc.ApocConfig; import apoc.SystemLabels; +import apoc.SystemPropertyKeys; import apoc.util.LogsUtil; import apoc.util.Util; import apoc.util.collection.Iterators; @@ -30,6 +31,7 @@ import java.util.Map; import java.util.Objects; import java.util.TreeMap; +import java.util.stream.StreamSupport; import org.apache.commons.configuration2.Configuration; import org.apache.commons.lang3.StringUtils; import org.neo4j.common.DependencyResolver; @@ -37,6 +39,7 @@ import org.neo4j.dbms.api.DatabaseManagementService; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.event.DatabaseEventContext; import org.neo4j.graphdb.event.DatabaseEventListener; import org.neo4j.kernel.availability.AvailabilityListener; @@ -99,6 +102,28 @@ public void available() { + "The two first numbers of both versions needs to be the same.", apocVersion, neo4jVersion); } + + // Create a uniqueness constraint on system db to avoid race conditions when installing + // triggers + try (Transaction tx = db.beginTx()) { + var constraintName = "triggerConstraint"; + var maybeConstraint = StreamSupport.stream( + tx.schema() + .getConstraints(SystemLabels.ApocTriggerMeta) + .spliterator(), + false) + .filter(x -> x.getName().equals(constraintName)) + .toList(); + if (maybeConstraint.isEmpty()) { + tx.schema() + .constraintFor(SystemLabels.ApocTriggerMeta) + .withName(constraintName) + .assertPropertyIsUnique(SystemPropertyKeys.database.name()) + .create(); + tx.commit(); + } + } + databaseEventListeners.registerDatabaseEventListener(new SystemFunctionalityListener()); } diff --git a/core/src/main/java/apoc/trigger/TriggerHandlerNewProcedures.java b/core/src/main/java/apoc/trigger/TriggerHandlerNewProcedures.java index ae3142709..feccccaa5 100644 --- a/core/src/main/java/apoc/trigger/TriggerHandlerNewProcedures.java +++ b/core/src/main/java/apoc/trigger/TriggerHandlerNewProcedures.java @@ -30,9 +30,11 @@ import java.util.Map; import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; +import org.neo4j.graphdb.ConstraintViolationException; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Transaction; +import org.neo4j.kernel.internal.GraphDatabaseAPI; public class TriggerHandlerNewProcedures { public static final String NOT_ENABLED_ERROR = "Triggers have not been enabled." @@ -49,6 +51,7 @@ public static void checkEnabled() { } public static TriggerInfo install( + GraphDatabaseAPI db, String databaseName, String triggerName, String statement, @@ -72,12 +75,12 @@ public static TriggerInfo install( // we'll return current trigger info result = fromNode(node, true); - setLastUpdate(databaseName, tx); + setLastUpdate(db, databaseName, tx); return result; } - public static TriggerInfo drop(String databaseName, String triggerName, Transaction tx) { + public static TriggerInfo drop(GraphDatabaseAPI db, String databaseName, String triggerName, Transaction tx) { final TriggerInfo[] previous = new TriggerInfo[1]; getTriggerNodes(databaseName, tx, triggerName).forEachRemaining(node -> { @@ -85,12 +88,13 @@ public static TriggerInfo drop(String databaseName, String triggerName, Transact node.delete(); }); - setLastUpdate(databaseName, tx); + setLastUpdate(db, databaseName, tx); return previous[0]; } - public static TriggerInfo updatePaused(String databaseName, String name, boolean paused, Transaction tx) { + public static TriggerInfo updatePaused( + GraphDatabaseAPI db, String databaseName, String name, boolean paused, Transaction tx) { final TriggerInfo[] result = new TriggerInfo[1]; getTriggerNodes(databaseName, tx, name).forEachRemaining(node -> { @@ -100,12 +104,12 @@ public static TriggerInfo updatePaused(String databaseName, String name, boolean result[0] = fromNode(node, true); }); - setLastUpdate(databaseName, tx); + setLastUpdate(db, databaseName, tx); return result[0]; } - public static List dropAll(String databaseName, Transaction tx) { + public static List dropAll(GraphDatabaseAPI db, String databaseName, Transaction tx) { final List previous = new ArrayList<>(); getTriggerNodes(databaseName, tx).forEachRemaining(node -> { @@ -113,7 +117,7 @@ public static List dropAll(String databaseName, Transaction tx) { previous.add(fromNode(node, false)); node.delete(); }); - setLastUpdate(databaseName, tx); + setLastUpdate(db, databaseName, tx); return previous; } @@ -135,11 +139,28 @@ public static ResourceIterator getTriggerNodes(String databaseName, Transa return tx.findNodes(label, dbNameKey, databaseName, SystemPropertyKeys.name.name(), name); } - public static void setLastUpdate(String databaseName, Transaction tx) { + public static void setLastUpdate(GraphDatabaseAPI db, String databaseName, Transaction tx) { + setLastUpdate(db, databaseName, tx, 0); + } + + public static void setLastUpdate(GraphDatabaseAPI db, String databaseName, Transaction tx, int retryNumber) { Node node = tx.findNode(SystemLabels.ApocTriggerMeta, SystemPropertyKeys.database.name(), databaseName); if (node == null) { - node = tx.createNode(SystemLabels.ApocTriggerMeta); - node.setProperty(SystemPropertyKeys.database.name(), databaseName); + try { + node = tx.createNode(SystemLabels.ApocTriggerMeta); + node.setProperty(SystemPropertyKeys.database.name(), databaseName); + } catch (ConstraintViolationException e) { + // This can happen if two threads try to create the same node concurrently, + // after both having passed the null check. + // In this case we retry once or otherwise ignore the failing tx. + if (retryNumber < 1) { + try (final var newTx = db.beginTx()) { + TriggerHandlerNewProcedures.setLastUpdate(db, databaseName, newTx, retryNumber + 1); + newTx.commit(); + } + } + return; + } } final long value = System.currentTimeMillis(); node.setProperty(SystemPropertyKeys.lastUpdated.name(), value); diff --git a/core/src/main/java/apoc/trigger/TriggerNewProcedures.java b/core/src/main/java/apoc/trigger/TriggerNewProcedures.java index 2eea687b1..424cec6c7 100644 --- a/core/src/main/java/apoc/trigger/TriggerNewProcedures.java +++ b/core/src/main/java/apoc/trigger/TriggerNewProcedures.java @@ -105,7 +105,7 @@ public Stream install( return withUpdatingTransaction( databaseName, tx -> Stream.of( - TriggerHandlerNewProcedures.install(databaseName, name, statement, selector, params, tx))); + TriggerHandlerNewProcedures.install(db, databaseName, name, statement, selector, params, tx))); } // TODO - change with @SystemOnlyProcedure @@ -120,7 +120,7 @@ public Stream drop( checkInSystemWriter(); return withUpdatingTransaction( - databaseName, tx -> Stream.ofNullable(TriggerHandlerNewProcedures.drop(databaseName, name, tx))); + databaseName, tx -> Stream.ofNullable(TriggerHandlerNewProcedures.drop(db, databaseName, name, tx))); } // TODO - change with @SystemOnlyProcedure @@ -134,7 +134,7 @@ public Stream dropAll( checkInSystemWriter(); return withUpdatingTransaction( - databaseName, tx -> TriggerHandlerNewProcedures.dropAll(databaseName, tx).stream() + databaseName, tx -> TriggerHandlerNewProcedures.dropAll(db, databaseName, tx).stream() .sorted(Comparator.comparing(i -> i.name))); } @@ -150,7 +150,7 @@ public Stream stop( checkInSystemWriter(); return withUpdatingTransaction(databaseName, tx -> { - final TriggerInfo triggerInfo = TriggerHandlerNewProcedures.updatePaused(databaseName, name, true, tx); + final TriggerInfo triggerInfo = TriggerHandlerNewProcedures.updatePaused(db, databaseName, name, true, tx); return Stream.ofNullable(triggerInfo); }); } @@ -167,7 +167,7 @@ public Stream start( checkInSystemWriter(); return withUpdatingTransaction(databaseName, tx -> { - final TriggerInfo triggerInfo = TriggerHandlerNewProcedures.updatePaused(databaseName, name, false, tx); + final TriggerInfo triggerInfo = TriggerHandlerNewProcedures.updatePaused(db, databaseName, name, false, tx); return Stream.ofNullable(triggerInfo); }); } @@ -194,7 +194,7 @@ public T withUpdatingTransaction(String databaseName, Function task = () -> { + latch.await(); // wait until both threads are ready + testCall(sysDb, query, map(), r -> {}); + return null; + }; + + Future f1 = executor.submit(task); + Future f2 = executor.submit(task); + + // release both threads + latch.countDown(); + + // Will throw if anything inside the tasks failed + f1.get(); + f2.get(); + } finally { + executor.shutdown(); + } + } + @Test public void testIssue2247() { db.executeTransactionally("CREATE (n:ToBeDeleted)");