com.torodb.mongowp.mongoserver.api.toro.ToroLastError.java Source code

Java tutorial

Introduction

Here is the source code for com.torodb.mongowp.mongoserver.api.toro.ToroLastError.java

Source

/*
 *     This file is part of ToroDB.
 *
 *     ToroDB is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     ToroDB is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with ToroDB. If not, see <http://www.gnu.org/licenses/>.
 *
 *     Copyright (c) 2014, 8Kdata Technology
 *     
 */

package com.torodb.mongowp.mongoserver.api.toro;

import com.eightkdata.mongowp.messages.request.RequestOpCode;
import com.eightkdata.mongowp.mongoserver.api.QueryCommandProcessor.QueryCommand;
import com.eightkdata.mongowp.mongoserver.api.callback.LastError;
import com.eightkdata.mongowp.mongoserver.api.callback.MessageReplier;
import com.eightkdata.mongowp.mongoserver.api.commands.QueryAndWriteOperationsQueryCommand;
import com.eightkdata.mongowp.mongoserver.protocol.MongoWP;
import com.eightkdata.nettybson.api.BSONDocument;
import com.eightkdata.nettybson.mongodriver.MongoBSONDocument;
import com.mongodb.WriteConcern;
import com.torodb.torod.core.connection.DeleteResponse;
import com.torodb.torod.core.connection.InsertResponse;
import com.torodb.torod.core.connection.UpdateResponse;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 */
public class ToroLastError implements LastError {

    private static final Logger LOGGER = LoggerFactory.getLogger(ToroLastError.class);
    @Nonnull
    private final RequestOpCode requestOpCode;
    private final QueryCommand queryCommand;
    private final Future<?> futureOperationResponse;
    private final Future<?> futureCommitResponse;
    private final boolean error;
    private final MongoWP.ErrorCode errorCode;

    public ToroLastError(@Nonnull RequestOpCode requestOpCode, QueryCommand queryCommand,
            Future<?> futureOperationResponse, Future<?> futureCommitResponse, boolean error,
            MongoWP.ErrorCode errorCode) {
        this.requestOpCode = requestOpCode;
        this.queryCommand = queryCommand;
        this.futureOperationResponse = futureOperationResponse;
        this.futureCommitResponse = futureCommitResponse;
        this.error = error;
        this.errorCode = errorCode;
    }

    @Override
    public void getLastError(@Nonnull Object w, boolean j, boolean fsync, int wtimeout,
            MessageReplier messageReplier) throws Exception {
        Map<String, Object> keyValues = new HashMap<String, Object>();

        WriteConcern writeConcern = getWriteConcern(w, j, fsync, wtimeout);
        LastErrorResult lastErrorResult = new LastErrorResult(this);

        if (!lastErrorResult.error) {
            switch (requestOpCode) {
            case OP_QUERY:
                if (queryCommand != null) {
                    if (queryCommand.equals(QueryAndWriteOperationsQueryCommand.insert)) {
                        getLastInsertError(writeConcern, lastErrorResult);
                    } else if (queryCommand.equals(QueryAndWriteOperationsQueryCommand.update)) {
                        getLastUpdateError(writeConcern, lastErrorResult);
                    } else if (queryCommand.equals(QueryAndWriteOperationsQueryCommand.delete)) {
                        getLastDeleteError(writeConcern, lastErrorResult);
                    }
                }
                break;
            case OP_INSERT:
                getLastInsertError(writeConcern, lastErrorResult);
                break;
            case OP_UPDATE:
                getLastUpdateError(writeConcern, lastErrorResult);
                break;
            case OP_DELETE:
                getLastDeleteError(writeConcern, lastErrorResult);
                break;
            case OP_GET_MORE:
            case OP_KILL_CURSORS:
            case OP_MSG:
            case RESERVED:
                break;
            }
        }
        lastErrorResult.wtime = System.currentTimeMillis() - lastErrorResult.wtime;

        keyValues.put("ok", lastErrorResult.error ? MongoWP.KO : MongoWP.OK);
        if (error) { //TODO: Check if we want to use this or lastErrorResult.error
            keyValues.put("err", lastErrorResult.errorCode.getErrorMessage());
            keyValues.put("code", lastErrorResult.errorCode.getErrorCode());
        }
        keyValues.put("connectionId", messageReplier.getConnectionId());
        //TODO: keyValues.put("lastOp", ???);
        //TODO: keyValues.put("shards", ???);
        //TODO: keyValues.put("singleShard", ???);

        keyValues.put("n", lastErrorResult.n);
        keyValues.put("updatedExisting", lastErrorResult.updatedExisting);
        keyValues.put("upserted", lastErrorResult.upserted);
        keyValues.put("wnote", lastErrorResult.writeConcernError ? MongoWP.OK : MongoWP.KO);
        //TODO: Implement timeout
        keyValues.put("wtimeout", false);
        //TODO: keyValues.put("waited", wtime);
        keyValues.put("wtime", (int) lastErrorResult.wtime);

        //TODO: Add undocumented "syncMillis" and "writtenTo"

        BSONDocument reply = new MongoBSONDocument(keyValues);
        messageReplier.replyMessageNoCursor(reply);
    }

    private void getLastInsertError(WriteConcern writeConcern, LastErrorResult lastErrorResult)
            throws InterruptedException, ExecutionException {
        if (futureOperationResponse == null && futureCommitResponse == null) {
            return;
        }
        if (writeConcern.getW() > 0) {
            try {
                if (futureOperationResponse != null) {
                    if (futureOperationResponse.isDone() || futureOperationResponse.isCancelled()) {
                        InsertResponse insertResponse = (InsertResponse) futureOperationResponse.get();
                        lastErrorResult.error = !insertResponse.isSuccess();
                        if (lastErrorResult.error) {
                            lastErrorResult.errorCode = MongoWP.ErrorCode.INTERNAL_ERROR;
                        } else {
                            lastErrorResult.n = insertResponse.getInsertedSize();
                        }
                    }
                    futureOperationResponse.get();
                }
            } catch (InterruptedException exception) {
                LOGGER.debug("Exception while last error was calculated", exception);
                lastErrorResult.error = true;
            } catch (ExecutionException exception) {
                LOGGER.debug("Exception while last error was calculated", exception);
                lastErrorResult.error = true;
            }
        }
    }

    private void getLastUpdateError(WriteConcern writeConcern, LastErrorResult lastErrorResult)
            throws InterruptedException, ExecutionException {
        if (futureOperationResponse == null || futureCommitResponse == null) {
            return;
        }
        if (writeConcern.getW() > 0) {
            try {
                futureOperationResponse.get();
                futureCommitResponse.get();
            } catch (Exception exception) {
                lastErrorResult.error = true;
            }
        }
        if (futureOperationResponse.isDone() || futureOperationResponse.isCancelled()) {
            UpdateResponse updateResponse = (UpdateResponse) futureOperationResponse.get();
            lastErrorResult.error = !updateResponse.getErrors().isEmpty();
            if (lastErrorResult.error) {
                lastErrorResult.errorCode = MongoWP.ErrorCode.INTERNAL_ERROR;
            } else {
                lastErrorResult.n = updateResponse.getModified();
                int modifiedCount = updateResponse.getModified() - updateResponse.getInsertedDocuments().size();
                lastErrorResult.updatedExisting = modifiedCount > 0;
                lastErrorResult.upserted = updateResponse.getInsertedDocuments().size();
            }
        }
    }

    private void getLastDeleteError(WriteConcern writeConcern, LastErrorResult lastErrorResult)
            throws InterruptedException, ExecutionException {
        if (futureOperationResponse == null || futureCommitResponse == null) {
            return;
        }
        if (writeConcern.getW() > 0) {
            try {
                futureOperationResponse.get();
                futureCommitResponse.get();
            } catch (Exception exception) {
                lastErrorResult.error = true;
            }
        }
        if (futureOperationResponse.isDone() || futureOperationResponse.isCancelled()) {
            DeleteResponse deleteResponse = (DeleteResponse) futureOperationResponse.get();
            lastErrorResult.error = !deleteResponse.isSuccess();
            if (lastErrorResult.error) {
                lastErrorResult.errorCode = MongoWP.ErrorCode.INTERNAL_ERROR;
            } else {
                lastErrorResult.n = deleteResponse.getDeleted();
            }
        }
    }

    @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
    private WriteConcern getWriteConcern(Object w, boolean j, boolean fsync, int wtimeout) {
        WriteConcern writeConcern = WriteConcern.ACKNOWLEDGED;

        if (w instanceof Number) {
            if (((Number) w).intValue() <= 1 && wtimeout > 0) {
                throw new IllegalArgumentException("wtimeout cannot be grater than 0 for w <= 1");
            }

            writeConcern = new WriteConcern(((Number) w).intValue(), wtimeout, fsync, j);
        } else if (w instanceof String && w.equals("majority")) {
            if (wtimeout > 0) {
                throw new IllegalArgumentException("wtimeout cannot be grater than 0 for w <= 1");
            }

            writeConcern = new WriteConcern.Majority(wtimeout, fsync, j);
        } else {
            throw new IllegalArgumentException("w:" + w + " is not supported");
        }

        return writeConcern;
    }

    private static class LastErrorResult {

        public long wtime = System.currentTimeMillis();
        public int n = 0;
        public boolean updatedExisting = false;
        public int upserted = 0;
        public boolean error;
        public MongoWP.ErrorCode errorCode;
        public boolean writeConcernError = false;

        LastErrorResult(ToroLastError toroLastError) {
            error = toroLastError.error;
            errorCode = toroLastError.errorCode;
        }
    }
}