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

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