org.neo4j.driver.internal.async.InternalAsyncSession Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-java-driver Show documentation
Show all versions of neo4j-java-driver Show documentation
Access to the Neo4j graph database through Java
The newest version!
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [https://neo4j.com]
*
* 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 org.neo4j.driver.internal.async;
import static java.util.Collections.emptyMap;
import static org.neo4j.driver.internal.util.Futures.completedWithNull;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.neo4j.driver.AccessMode;
import org.neo4j.driver.Bookmark;
import org.neo4j.driver.Query;
import org.neo4j.driver.TransactionConfig;
import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.async.AsyncTransaction;
import org.neo4j.driver.async.AsyncTransactionCallback;
import org.neo4j.driver.async.AsyncTransactionWork;
import org.neo4j.driver.async.ResultCursor;
import org.neo4j.driver.exceptions.ClientException;
import org.neo4j.driver.internal.InternalBookmark;
import org.neo4j.driver.internal.bolt.api.GqlStatusError;
import org.neo4j.driver.internal.bolt.api.TelemetryApi;
import org.neo4j.driver.internal.telemetry.ApiTelemetryWork;
import org.neo4j.driver.internal.util.Futures;
public class InternalAsyncSession extends AsyncAbstractQueryRunner implements AsyncSession {
private final NetworkSession session;
public InternalAsyncSession(NetworkSession session) {
this.session = session;
}
@Override
public CompletionStage runAsync(Query query) {
return runAsync(query, TransactionConfig.empty());
}
@Override
public CompletionStage runAsync(String query, TransactionConfig config) {
return runAsync(query, emptyMap(), config);
}
@Override
public CompletionStage runAsync(
String query, Map parameters, TransactionConfig config) {
return runAsync(new Query(query, parameters), config);
}
@Override
public CompletionStage runAsync(Query query, TransactionConfig config) {
return session.runAsync(query, config);
}
@Override
public CompletionStage closeAsync() {
return session.closeAsync();
}
@Override
public CompletionStage beginTransactionAsync() {
return beginTransactionAsync(TransactionConfig.empty());
}
@Override
public CompletionStage beginTransactionAsync(TransactionConfig config) {
return session.beginTransactionAsync(config, new ApiTelemetryWork(TelemetryApi.UNMANAGED_TRANSACTION))
.thenApply(InternalAsyncTransaction::new);
}
@Override
@Deprecated
public CompletionStage readTransactionAsync(AsyncTransactionWork> work) {
return readTransactionAsync(work, TransactionConfig.empty());
}
@Override
@Deprecated
public CompletionStage readTransactionAsync(
AsyncTransactionWork> work, TransactionConfig config) {
return transactionAsync(AccessMode.READ, work, config);
}
@Override
public CompletionStage executeReadAsync(
AsyncTransactionCallback> callback, TransactionConfig config) {
return readTransactionAsync(tx -> callback.execute(new DelegatingAsyncTransactionContext(tx)), config);
}
@Override
@Deprecated
public CompletionStage writeTransactionAsync(AsyncTransactionWork> work) {
return writeTransactionAsync(work, TransactionConfig.empty());
}
@Override
@Deprecated
public CompletionStage writeTransactionAsync(
AsyncTransactionWork> work, TransactionConfig config) {
return transactionAsync(AccessMode.WRITE, work, config);
}
@Override
public CompletionStage executeWriteAsync(
AsyncTransactionCallback> callback, TransactionConfig config) {
return writeTransactionAsync(tx -> callback.execute(new DelegatingAsyncTransactionContext(tx)), config);
}
@Override
@Deprecated
public Bookmark lastBookmark() {
return InternalBookmark.from(session.lastBookmarks());
}
@Override
public Set lastBookmarks() {
return new HashSet<>(session.lastBookmarks());
}
private CompletionStage transactionAsync(
AccessMode mode,
@SuppressWarnings("deprecation") AsyncTransactionWork> work,
TransactionConfig config) {
var apiTelemetryWork = new ApiTelemetryWork(TelemetryApi.MANAGED_TRANSACTION);
return session.retryLogic().retryAsync(() -> {
var resultFuture = new CompletableFuture();
var txFuture = session.beginTransactionAsync(mode, config, apiTelemetryWork);
txFuture.whenComplete((tx, completionError) -> {
var error = Futures.completionExceptionCause(completionError);
if (error != null) {
resultFuture.completeExceptionally(error);
} else {
executeWork(resultFuture, tx, work);
}
});
return resultFuture;
});
}
private void executeWork(
CompletableFuture resultFuture,
UnmanagedTransaction tx,
@SuppressWarnings("deprecation") AsyncTransactionWork> work) {
var workFuture = safeExecuteWork(tx, work);
workFuture.whenComplete((result, completionError) -> {
var error = Futures.completionExceptionCause(completionError);
if (error != null) {
closeTxAfterFailedTransactionWork(tx, resultFuture, error);
} else if (result instanceof ResultCursor) {
var message = String.format(
"%s is not a valid return value, it should be consumed before producing a return value",
ResultCursor.class.getName());
error = new ClientException(
GqlStatusError.UNKNOWN.getStatus(),
GqlStatusError.UNKNOWN.getStatusDescription(message),
"N/A",
message,
GqlStatusError.DIAGNOSTIC_RECORD,
null);
closeTxAfterFailedTransactionWork(tx, resultFuture, error);
} else {
closeTxAfterSucceededTransactionWork(tx, resultFuture, result);
}
});
}
private CompletionStage safeExecuteWork(
UnmanagedTransaction tx, @SuppressWarnings("deprecation") AsyncTransactionWork> work) {
// given work might fail in both async and sync way
// async failure will result in a failed future being returned
// sync failure will result in an exception being thrown
try {
var result = work.execute(new InternalAsyncTransaction(tx));
// protect from given transaction function returning null
return result == null ? completedWithNull() : result;
} catch (Throwable workError) {
// work threw an exception, wrap it in a future and proceed
return CompletableFuture.failedFuture(workError);
}
}
private void closeTxAfterFailedTransactionWork(
UnmanagedTransaction tx, CompletableFuture resultFuture, Throwable error) {
tx.closeAsync().whenComplete((ignored, rollbackError) -> {
if (rollbackError != null) {
error.addSuppressed(rollbackError);
}
resultFuture.completeExceptionally(error);
});
}
private void closeTxAfterSucceededTransactionWork(
UnmanagedTransaction tx, CompletableFuture resultFuture, T result) {
tx.closeAsync(true).whenComplete((ignored, completionError) -> {
var commitError = Futures.completionExceptionCause(completionError);
if (commitError != null) {
resultFuture.completeExceptionally(commitError);
} else {
resultFuture.complete(result);
}
});
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy