io.helidon.dbclient.mongodb.MongoDbDMLExecutor Maven / Gradle / Ivy
/*
* Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
*
* 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 io.helidon.dbclient.mongodb;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.atomic.LongAdder;
import java.util.logging.Logger;
import io.helidon.dbclient.DbClientServiceContext;
import io.helidon.dbclient.DbStatementType;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.mongodb.reactivestreams.client.MongoCollection;
import com.mongodb.reactivestreams.client.Success;
import org.bson.Document;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
/**
* Executes Mongo specific DML statement and returns result.
* Utility class with static methods only.
*/
final class MongoDbDMLExecutor {
/** Local logger instance. */
private static final Logger LOGGER = Logger.getLogger(MongoDbDMLExecutor.class.getName());
private MongoDbDMLExecutor() {
throw new UnsupportedOperationException("Utility class MongoDbDMLExecutor instances are not allowed!");
}
static CompletionStage executeDml(
MongoDbStatement dbStatement,
DbStatementType dbStatementType,
MongoDbStatement.MongoStatement mongoStatement,
CompletionStage dbContextFuture,
CompletableFuture statementFuture,
CompletableFuture queryFuture
) {
// if the iterceptors fail with exception, we must fail as well
dbContextFuture.exceptionally(throwable -> {
statementFuture.completeExceptionally(throwable);
queryFuture.completeExceptionally(throwable);
return null;
});
return dbContextFuture.thenCompose(dbContext -> {
switch (dbStatementType) {
case INSERT:
return executeInsert(dbStatement, dbStatementType, mongoStatement, statementFuture, queryFuture);
case UPDATE:
return executeUpdate(dbStatement, dbStatementType, mongoStatement, statementFuture, queryFuture);
case DELETE:
return executeDelete(dbStatement, dbStatementType, mongoStatement, statementFuture, queryFuture);
default:
CompletableFuture result = new CompletableFuture<>();
Throwable failure = new UnsupportedOperationException(
String.format("Statement operation not yet supported: %s", dbStatementType.name()));
result.completeExceptionally(failure);
statementFuture.completeExceptionally(failure);
queryFuture.completeExceptionally(failure);
return result;
}
});
}
private abstract static class DmlResultSubscriber implements org.reactivestreams.Subscriber {
private final MongoDbStatement dbStatement;
private final DbStatementType dbStatementType;
private final CompletableFuture statementFuture;
private final CompletableFuture queryFuture;
private final LongAdder count;
private DmlResultSubscriber(
MongoDbStatement dbStatement,
DbStatementType dbStatementType,
CompletableFuture queryFuture,
CompletableFuture statementFuture
) {
this.dbStatement = dbStatement;
this.dbStatementType = dbStatementType;
this.statementFuture = statementFuture;
this.queryFuture = queryFuture;
this.count = new LongAdder();
}
@Override
public void onSubscribe(Subscription s) {
// no need for flow control, we only add the result
s.request(Long.MAX_VALUE);
}
@Override
public void onError(Throwable t) {
statementFuture.completeExceptionally(t);
queryFuture.completeExceptionally(t);
if (dbStatement.txManager() != null) {
dbStatement.txManager().stmtFailed(dbStatement);
}
LOGGER.fine(() -> String.format(
"%s DML %s execution failed", dbStatementType.name(), dbStatement.statementName()));
}
@Override
public void onComplete() {
statementFuture.complete(null);
queryFuture.complete(count.sum());
if (dbStatement.txManager() != null) {
dbStatement.txManager().stmtFinished(dbStatement);
}
LOGGER.fine(() -> String.format(
"%s DML %s execution succeeded", dbStatementType.name(), dbStatement.statementName()));
}
LongAdder count() {
return count;
}
}
private static final class InsertResultSubscriber extends DmlResultSubscriber {
private InsertResultSubscriber(
MongoDbStatement dbStatement,
DbStatementType dbStatementType,
CompletableFuture queryFuture,
CompletableFuture statementFuture
) {
super(dbStatement, dbStatementType, queryFuture, statementFuture);
}
@Override
public void onNext(Success r) {
count().increment();
}
}
private static final class UpdateResultSubscriber extends DmlResultSubscriber {
private UpdateResultSubscriber(
MongoDbStatement dbStatement,
DbStatementType dbStatementType,
CompletableFuture queryFuture,
CompletableFuture statementFuture
) {
super(dbStatement, dbStatementType, queryFuture, statementFuture);
}
@Override
public void onNext(UpdateResult r) {
count().add(r.getModifiedCount());
}
}
private static final class DeleteResultSubscriber extends DmlResultSubscriber {
private DeleteResultSubscriber(
MongoDbStatement dbStatement,
DbStatementType dbStatementType,
CompletableFuture queryFuture,
CompletableFuture statementFuture
) {
super(dbStatement, dbStatementType, queryFuture, statementFuture);
}
@Override
public void onNext(DeleteResult r) {
count().add(r.getDeletedCount());
}
}
private static CompletionStage executeInsert(
MongoDbStatement dbStatement,
DbStatementType dbStatementType,
MongoDbStatement.MongoStatement mongoStatement,
CompletableFuture statementFuture,
CompletableFuture queryFuture
) {
MongoCollection mc = dbStatement.db().getCollection(mongoStatement.getCollection());
Publisher insertPublisher = dbStatement.noTx()
? mc.insertOne(mongoStatement.getValue())
: mc.insertOne(dbStatement.txManager().tx(), mongoStatement.getValue());
insertPublisher.subscribe(new InsertResultSubscriber(dbStatement, dbStatementType, queryFuture, statementFuture));
return queryFuture;
}
@SuppressWarnings("SubscriberImplementation")
private static CompletionStage executeUpdate(
MongoDbStatement dbStatement,
DbStatementType dbStatementType,
MongoDbStatement.MongoStatement mongoStatement,
CompletableFuture statementFuture,
CompletableFuture queryFuture
) {
MongoCollection mc = dbStatement.db().getCollection(mongoStatement.getCollection());
Document query = mongoStatement.getQuery();
// TODO should the next line be used?
//Document value = mongoStatement.getValue();
Publisher updatePublisher = dbStatement.noTx()
? mc.updateMany(query, mongoStatement.getValue())
: mc.updateMany(dbStatement.txManager().tx(), query, mongoStatement.getValue());
updatePublisher.subscribe(new UpdateResultSubscriber(dbStatement, dbStatementType, queryFuture, statementFuture));
return queryFuture;
}
@SuppressWarnings("SubscriberImplementation")
private static CompletionStage executeDelete(
MongoDbStatement dbStatement,
DbStatementType dbStatementType,
MongoDbStatement.MongoStatement mongoStatement,
CompletableFuture statementFuture,
CompletableFuture queryFuture
) {
MongoCollection mc = dbStatement.db().getCollection(mongoStatement.getCollection());
Document query = mongoStatement.getQuery();
Publisher deletePublisher = dbStatement.noTx()
? mc.deleteMany(query)
: mc.deleteMany(dbStatement.txManager().tx(), query);
deletePublisher.subscribe(new DeleteResultSubscriber(dbStatement, dbStatementType, queryFuture, statementFuture));
return queryFuture;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy