All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.javacrumbs.shedlock.provider.neo4j.Neo4jStorageAccessor Maven / Gradle / Ivy

There is a newer version: 5.16.0
Show newest version
/**
 * Copyright 2009 the original author or authors.
 *
 * 

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 net.javacrumbs.shedlock.provider.neo4j; import static java.util.Objects.requireNonNull; import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; import net.javacrumbs.shedlock.core.ClockProvider; import net.javacrumbs.shedlock.core.LockConfiguration; import net.javacrumbs.shedlock.support.AbstractStorageAccessor; import net.javacrumbs.shedlock.support.LockException; import net.javacrumbs.shedlock.support.annotation.NonNull; import net.javacrumbs.shedlock.support.annotation.Nullable; import org.neo4j.driver.Driver; import org.neo4j.driver.Result; import org.neo4j.driver.Session; import org.neo4j.driver.SessionConfig; import org.neo4j.driver.Transaction; class Neo4jStorageAccessor extends AbstractStorageAccessor { private final String collectionName; private final Driver driver; private final String databaseName; public Neo4jStorageAccessor(@NonNull Driver driver, @NonNull String collectionName, @Nullable String databaseName) { this.collectionName = requireNonNull(collectionName, "collectionName can not be null"); this.driver = requireNonNull(driver, "driver can not be null"); this.databaseName = databaseName; createLockNameUniqueConstraint(); } private void createLockNameUniqueConstraint() { try (Session session = getSession(); Transaction transaction = session.beginTransaction()) { transaction.run(String.format( "CREATE CONSTRAINT UNIQUE_%s_name IF NOT EXISTS FOR (lock:%s) REQUIRE lock.name IS UNIQUE", collectionName, collectionName)); transaction.commit(); } } @Override public boolean insertRecord(@NonNull LockConfiguration lockConfiguration) { // Try to insert if the record does not exists String cypher = String.format( "CREATE (lock:%s {name: $lockName, lock_until: $lockUntil, locked_at: $now, locked_by: $lockedBy })", collectionName); Map parameters = createParameterMap(lockConfiguration); return executeCommand( cypher, result -> { int insertedNodes = result.consume().counters().nodesCreated(); return insertedNodes > 0; }, parameters, this::handleInsertionException); } private Map createParameterMap(@NonNull LockConfiguration lockConfiguration) { return Map.of( "lockName", lockConfiguration.getName(), "lockedBy", getHostname(), "now", ClockProvider.now().toString(), "lockUntil", lockConfiguration.getLockAtMostUntil().toString()); } @Override public boolean updateRecord(@NonNull LockConfiguration lockConfiguration) { String cypher = String.format( "MATCH (lock:%s) " + "WHERE lock.name = $lockName AND lock.lock_until <= $now " + "SET lock._LOCK_ = true " + "WITH lock as l " + "WHERE l.lock_until <= $now " + "SET l.lock_until = $lockUntil, l.locked_at = $now, l.locked_by = $lockedBy " + "REMOVE l._LOCK_ ", collectionName); Map parameters = createParameterMap(lockConfiguration); return executeCommand( cypher, statement -> { int updatedProperties = statement.consume().counters().propertiesSet(); return updatedProperties > 1; // ignore explicit lock when counting the updated properties }, parameters, this::handleUpdateException); } @Override public boolean extend(@NonNull LockConfiguration lockConfiguration) { String cypher = String.format( "MATCH (lock:%s) " + "WHERE lock.name = $lockName AND lock.locked_by = $lockedBy AND lock.lock_until > $now " + "SET lock._LOCK_ = true " + "WITH lock as l " + "WHERE l.name = $lockName AND l.locked_by = $lockedBy AND l.lock_until > $now " + "SET l.lock_until = $lockUntil " + "REMOVE l._LOCK_ ", collectionName); Map parameters = createParameterMap(lockConfiguration); logger.debug("Extending lock={} until={}", lockConfiguration.getName(), lockConfiguration.getLockAtMostUntil()); return executeCommand( cypher, statement -> { int updatedProperties = statement.consume().counters().propertiesSet(); return updatedProperties > 1; // ignore the explicit lock when counting the updated properties }, parameters, this::handleUnlockException); } @Override public void unlock(@NonNull LockConfiguration lockConfiguration) { String cypher = String.format( "MATCH (lock:%s) WHERE lock.name = $lockName " + "SET lock.lock_until = $lockUntil ", collectionName); Map parameters = Map.of( "lockName", lockConfiguration.getName(), "lockUntil", lockConfiguration.getUnlockTime().toString()); executeCommand(cypher, statement -> null, parameters, this::handleUnlockException); } private T executeCommand( String cypher, Function body, Map parameters, BiFunction exceptionHandler) { try (Session session = getSession(); Transaction transaction = session.beginTransaction()) { Result result = transaction.run(cypher, parameters); T apply = body.apply(result); transaction.commit(); return apply; } catch (Exception e) { return exceptionHandler.apply(cypher, e); } } private Session getSession() { return databaseName == null ? driver.session() : driver.session(SessionConfig.forDatabase(databaseName)); } boolean handleInsertionException(String cypher, Exception e) { // lock record already exists return false; } boolean handleUpdateException(String cypher, Exception e) { throw new LockException("Unexpected exception when locking", e); } boolean handleUnlockException(String cypher, Exception e) { throw new LockException("Unexpected exception when unlocking", e); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy