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

com.mongodb.internal.connection.InternalStreamConnection Maven / Gradle / Ivy

Go to download

The Java operations layer for the MongoDB Java Driver. Third parties can wrap this layer to provide custom higher-level APIs

There is a newer version: 5.3.0-beta0
Show newest version
/*
 * Copyright 2008-present MongoDB, Inc.
 *
 * 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 com.mongodb.internal.connection;

import com.mongodb.LoggerSettings;
import com.mongodb.MongoClientException;
import com.mongodb.MongoCommandException;
import com.mongodb.MongoCompressor;
import com.mongodb.MongoException;
import com.mongodb.MongoInternalException;
import com.mongodb.MongoInterruptedException;
import com.mongodb.MongoSocketClosedException;
import com.mongodb.MongoSocketReadException;
import com.mongodb.MongoSocketReadTimeoutException;
import com.mongodb.MongoSocketWriteException;
import com.mongodb.RequestContext;
import com.mongodb.ServerAddress;
import com.mongodb.annotations.NotThreadSafe;
import com.mongodb.connection.AsyncCompletionHandler;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ClusterId;
import com.mongodb.connection.ConnectionDescription;
import com.mongodb.connection.ConnectionId;
import com.mongodb.connection.ServerConnectionState;
import com.mongodb.connection.ServerDescription;
import com.mongodb.connection.ServerId;
import com.mongodb.connection.ServerType;
import com.mongodb.event.CommandListener;
import com.mongodb.internal.ResourceUtil;
import com.mongodb.internal.VisibleForTesting;
import com.mongodb.internal.async.AsyncSupplier;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.diagnostics.logging.Logger;
import com.mongodb.internal.diagnostics.logging.Loggers;
import com.mongodb.internal.logging.StructuredLogger;
import com.mongodb.internal.session.SessionContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonBinaryReader;
import org.bson.BsonDocument;
import org.bson.ByteBuf;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.Decoder;
import org.bson.io.ByteBufferBsonInput;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

import static com.mongodb.assertions.Assertions.assertNotNull;
import static com.mongodb.assertions.Assertions.assertNull;
import static com.mongodb.assertions.Assertions.isTrue;
import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.internal.async.AsyncRunnable.beginAsync;
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
import static com.mongodb.internal.connection.Authenticator.shouldAuthenticate;
import static com.mongodb.internal.connection.CommandHelper.HELLO;
import static com.mongodb.internal.connection.CommandHelper.LEGACY_HELLO;
import static com.mongodb.internal.connection.CommandHelper.LEGACY_HELLO_LOWER;
import static com.mongodb.internal.connection.MessageHeader.MESSAGE_HEADER_LENGTH;
import static com.mongodb.internal.connection.OpCode.OP_COMPRESSED;
import static com.mongodb.internal.connection.ProtocolHelper.createSpecialWriteConcernException;
import static com.mongodb.internal.connection.ProtocolHelper.getClusterTime;
import static com.mongodb.internal.connection.ProtocolHelper.getCommandFailureException;
import static com.mongodb.internal.connection.ProtocolHelper.getMessageSettings;
import static com.mongodb.internal.connection.ProtocolHelper.getOperationTime;
import static com.mongodb.internal.connection.ProtocolHelper.getRecoveryToken;
import static com.mongodb.internal.connection.ProtocolHelper.getSnapshotTimestamp;
import static com.mongodb.internal.connection.ProtocolHelper.isCommandOk;
import static com.mongodb.internal.logging.LogMessage.Level.DEBUG;
import static com.mongodb.internal.thread.InterruptionUtil.translateInterruptedException;
import static java.util.Arrays.asList;

/**
 * 

This class is not part of the public API and may be removed or changed at any time

*/ @NotThreadSafe public class InternalStreamConnection implements InternalConnection { private static volatile boolean recordEverything = false; /** * Will attempt to record events to the command listener that are usually * suppressed. * * @param recordEverything whether to attempt to record everything */ @VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE) public static void setRecordEverything(final boolean recordEverything) { InternalStreamConnection.recordEverything = recordEverything; } private static final Set SECURITY_SENSITIVE_COMMANDS = new HashSet<>(asList( "authenticate", "saslStart", "saslContinue", "getnonce", "createUser", "updateUser", "copydbgetnonce", "copydbsaslstart", "copydb")); private static final Set SECURITY_SENSITIVE_HELLO_COMMANDS = new HashSet<>(asList( HELLO, LEGACY_HELLO, LEGACY_HELLO_LOWER)); private static final Logger LOGGER = Loggers.getLogger("connection"); private final ClusterConnectionMode clusterConnectionMode; @Nullable private final Authenticator authenticator; private final boolean isMonitoringConnection; private final ServerId serverId; private final ConnectionGenerationSupplier connectionGenerationSupplier; private final StreamFactory streamFactory; private final InternalConnectionInitializer connectionInitializer; private volatile ConnectionDescription description; private volatile ServerDescription initialServerDescription; private volatile Stream stream; private final AtomicBoolean isClosed = new AtomicBoolean(); private final AtomicBoolean opened = new AtomicBoolean(); private final AtomicBoolean authenticated = new AtomicBoolean(); private final List compressorList; private final LoggerSettings loggerSettings; private final CommandListener commandListener; @Nullable private volatile Compressor sendCompressor; private final Map compressorMap; private volatile boolean hasMoreToCome; private volatile int responseTo; private int generation = NOT_INITIALIZED_GENERATION; // Package-level access provided to avoid duplicating the list in test code static Set getSecuritySensitiveCommands() { return Collections.unmodifiableSet(SECURITY_SENSITIVE_COMMANDS); } // Package-level access provided to avoid duplicating the list in test code static Set getSecuritySensitiveHelloCommands() { return Collections.unmodifiableSet(SECURITY_SENSITIVE_HELLO_COMMANDS); } @VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE) public InternalStreamConnection(final ClusterConnectionMode clusterConnectionMode, final ServerId serverId, final ConnectionGenerationSupplier connectionGenerationSupplier, final StreamFactory streamFactory, final List compressorList, final CommandListener commandListener, final InternalConnectionInitializer connectionInitializer) { this(clusterConnectionMode, null, false, serverId, connectionGenerationSupplier, streamFactory, compressorList, LoggerSettings.builder().build(), commandListener, connectionInitializer); } public InternalStreamConnection(final ClusterConnectionMode clusterConnectionMode, @Nullable final Authenticator authenticator, final boolean isMonitoringConnection, final ServerId serverId, final ConnectionGenerationSupplier connectionGenerationSupplier, final StreamFactory streamFactory, final List compressorList, final LoggerSettings loggerSettings, final CommandListener commandListener, final InternalConnectionInitializer connectionInitializer) { this.clusterConnectionMode = clusterConnectionMode; this.authenticator = authenticator; this.isMonitoringConnection = isMonitoringConnection; this.serverId = notNull("serverId", serverId); this.connectionGenerationSupplier = notNull("connectionGeneration", connectionGenerationSupplier); this.streamFactory = notNull("streamFactory", streamFactory); this.compressorList = notNull("compressorList", compressorList); this.compressorMap = createCompressorMap(compressorList); this.loggerSettings = loggerSettings; this.commandListener = commandListener; this.connectionInitializer = notNull("connectionInitializer", connectionInitializer); description = new ConnectionDescription(serverId); initialServerDescription = ServerDescription.builder() .address(serverId.getAddress()) .type(ServerType.UNKNOWN) .state(ServerConnectionState.CONNECTING) .build(); if (clusterConnectionMode != ClusterConnectionMode.LOAD_BALANCED) { generation = connectionGenerationSupplier.getGeneration(); } } @Override public ConnectionDescription getDescription() { return description; } @Override public ServerDescription getInitialServerDescription() { return initialServerDescription; } @Override public int getGeneration() { return generation; } @Override public void open() { isTrue("Open already called", stream == null); stream = streamFactory.create(serverId.getAddress()); try { stream.open(); InternalConnectionInitializationDescription initializationDescription = connectionInitializer.startHandshake(this); initAfterHandshakeStart(initializationDescription); initializationDescription = connectionInitializer.finishHandshake(this, initializationDescription); initAfterHandshakeFinish(initializationDescription); } catch (Throwable t) { close(); if (t instanceof MongoException) { throw (MongoException) t; } else { throw new MongoException(t.toString(), t); } } } @Override public void openAsync(final SingleResultCallback callback) { assertNull(stream); try { stream = streamFactory.create(serverId.getAddress()); stream.openAsync(new AsyncCompletionHandler() { @Override public void completed(@Nullable final Void aVoid) { connectionInitializer.startHandshakeAsync(InternalStreamConnection.this, (initialResult, initialException) -> { if (initialException != null) { close(); callback.onResult(null, initialException); } else { assertNotNull(initialResult); initAfterHandshakeStart(initialResult); connectionInitializer.finishHandshakeAsync(InternalStreamConnection.this, initialResult, (completedResult, completedException) -> { if (completedException != null) { close(); callback.onResult(null, completedException); } else { assertNotNull(completedResult); initAfterHandshakeFinish(completedResult); callback.onResult(null, null); } }); } }); } @Override public void failed(final Throwable t) { close(); callback.onResult(null, t); } }); } catch (Throwable t) { close(); callback.onResult(null, t); } } private void initAfterHandshakeStart(final InternalConnectionInitializationDescription initializationDescription) { description = initializationDescription.getConnectionDescription(); initialServerDescription = initializationDescription.getServerDescription(); if (clusterConnectionMode == ClusterConnectionMode.LOAD_BALANCED) { generation = connectionGenerationSupplier.getGeneration(assertNotNull(description.getServiceId())); } } private void initAfterHandshakeFinish(final InternalConnectionInitializationDescription initializationDescription) { description = initializationDescription.getConnectionDescription(); initialServerDescription = initializationDescription.getServerDescription(); opened.set(true); authenticated.set(true); sendCompressor = findSendCompressor(description); } private Map createCompressorMap(final List compressorList) { Map compressorMap = new HashMap<>(this.compressorList.size()); for (MongoCompressor mongoCompressor : compressorList) { Compressor compressor = createCompressor(mongoCompressor); compressorMap.put(compressor.getId(), compressor); } return compressorMap; } @Nullable private Compressor findSendCompressor(final ConnectionDescription description) { if (description.getCompressors().isEmpty()) { return null; } String firstCompressorName = description.getCompressors().get(0); for (Compressor compressor : compressorMap.values()) { if (compressor.getName().equals(firstCompressorName)) { return compressor; } } throw new MongoInternalException("Unexpected compressor negotiated: " + firstCompressorName); } private Compressor createCompressor(final MongoCompressor mongoCompressor) { switch (mongoCompressor.getName()) { case "zlib": return new ZlibCompressor(mongoCompressor); case "snappy": return new SnappyCompressor(); case "zstd": return new ZstdCompressor(); default: throw new MongoClientException("Unsupported compressor " + mongoCompressor.getName()); } } @Override public void close() { // All but the first call is a no-op if (!isClosed.getAndSet(true) && (stream != null)) { stream.close(); } } @Override public boolean opened() { return opened.get(); } @Override public boolean isClosed() { return isClosed.get(); } @Nullable @Override public T sendAndReceive(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext, final RequestContext requestContext, final OperationContext operationContext) { Supplier sendAndReceiveInternal = () -> sendAndReceiveInternal( message, decoder, sessionContext, requestContext, operationContext); try { return sendAndReceiveInternal.get(); } catch (MongoCommandException e) { if (reauthenticationIsTriggered(e)) { return reauthenticateAndRetry(sendAndReceiveInternal); } throw e; } } @Override public void sendAndReceiveAsync(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext, final RequestContext requestContext, final OperationContext operationContext, final SingleResultCallback callback) { AsyncSupplier sendAndReceiveAsyncInternal = c -> sendAndReceiveAsyncInternal( message, decoder, sessionContext, requestContext, operationContext, c); beginAsync().thenSupply(c -> { sendAndReceiveAsyncInternal.getAsync(c); }).onErrorIf(e -> reauthenticationIsTriggered(e), (t, c) -> { reauthenticateAndRetryAsync(sendAndReceiveAsyncInternal, c); }).finish(callback); } private T reauthenticateAndRetry(final Supplier operation) { authenticated.set(false); assertNotNull(authenticator).reauthenticate(this); authenticated.set(true); return operation.get(); } private void reauthenticateAndRetryAsync(final AsyncSupplier operation, final SingleResultCallback callback) { beginAsync().thenRun(c -> { authenticated.set(false); assertNotNull(authenticator).reauthenticateAsync(this, c); }).thenSupply((c) -> { authenticated.set(true); operation.getAsync(c); }).finish(callback); } public boolean reauthenticationIsTriggered(@Nullable final Throwable t) { if (!shouldAuthenticate(authenticator, this.description)) { return false; } if (t instanceof MongoCommandException) { MongoCommandException e = (MongoCommandException) t; return e.getErrorCode() == 391; } return false; } @Nullable private T sendAndReceiveInternal(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext, final RequestContext requestContext, final OperationContext operationContext) { CommandEventSender commandEventSender; try (ByteBufferBsonOutput bsonOutput = new ByteBufferBsonOutput(this)) { message.encode(bsonOutput, sessionContext); commandEventSender = createCommandEventSender(message, bsonOutput, requestContext, operationContext); commandEventSender.sendStartedEvent(); try { sendCommandMessage(message, bsonOutput, sessionContext); } catch (Exception e) { commandEventSender.sendFailedEvent(e); throw e; } } if (message.isResponseExpected()) { return receiveCommandMessageResponse(decoder, commandEventSender, sessionContext, 0); } else { commandEventSender.sendSucceededEventForOneWayCommand(); return null; } } @Override public void send(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext) { try (ByteBufferBsonOutput bsonOutput = new ByteBufferBsonOutput(this)) { message.encode(bsonOutput, sessionContext); sendCommandMessage(message, bsonOutput, sessionContext); if (message.isResponseExpected()) { hasMoreToCome = true; } } } @Override public T receive(final Decoder decoder, final SessionContext sessionContext) { isTrue("Response is expected", hasMoreToCome); return receiveCommandMessageResponse(decoder, new NoOpCommandEventSender(), sessionContext, 0); } @Override public T receive(final Decoder decoder, final SessionContext sessionContext, final int additionalTimeout) { isTrue("Response is expected", hasMoreToCome); return receiveCommandMessageResponse(decoder, new NoOpCommandEventSender(), sessionContext, additionalTimeout); } @Override public boolean hasMoreToCome() { return hasMoreToCome; } private void sendCommandMessage(final CommandMessage message, final ByteBufferBsonOutput bsonOutput, final SessionContext sessionContext) { Compressor localSendCompressor = sendCompressor; if (localSendCompressor == null || SECURITY_SENSITIVE_COMMANDS.contains(message.getCommandDocument(bsonOutput).getFirstKey())) { List byteBuffers = bsonOutput.getByteBuffers(); try { sendMessage(byteBuffers, message.getId()); } finally { ResourceUtil.release(byteBuffers); bsonOutput.close(); } } else { ByteBufferBsonOutput compressedBsonOutput; List byteBuffers = bsonOutput.getByteBuffers(); try { CompressedMessage compressedMessage = new CompressedMessage(message.getOpCode(), byteBuffers, localSendCompressor, getMessageSettings(description)); compressedBsonOutput = new ByteBufferBsonOutput(this); compressedMessage.encode(compressedBsonOutput, sessionContext); } finally { ResourceUtil.release(byteBuffers); bsonOutput.close(); } List compressedByteBuffers = compressedBsonOutput.getByteBuffers(); try { sendMessage(compressedByteBuffers, message.getId()); } finally { ResourceUtil.release(compressedByteBuffers); compressedBsonOutput.close(); } } responseTo = message.getId(); } private T receiveCommandMessageResponse(final Decoder decoder, final CommandEventSender commandEventSender, final SessionContext sessionContext, final int additionalTimeout) { boolean commandSuccessful = false; try (ResponseBuffers responseBuffers = receiveMessageWithAdditionalTimeout(additionalTimeout)) { updateSessionContext(sessionContext, responseBuffers); if (!isCommandOk(responseBuffers)) { throw getCommandFailureException(responseBuffers.getResponseDocument(responseTo, new BsonDocumentCodec()), description.getServerAddress()); } commandSuccessful = true; commandEventSender.sendSucceededEvent(responseBuffers); T commandResult = getCommandResult(decoder, responseBuffers, responseTo); hasMoreToCome = responseBuffers.getReplyHeader().hasMoreToCome(); if (hasMoreToCome) { responseTo = responseBuffers.getReplyHeader().getRequestId(); } else { responseTo = 0; } return commandResult; } catch (Exception e) { if (!commandSuccessful) { commandEventSender.sendFailedEvent(e); } throw e; } } private void sendAndReceiveAsyncInternal(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext, final RequestContext requestContext, final OperationContext operationContext, final SingleResultCallback callback) { if (isClosed()) { callback.onResult(null, new MongoSocketClosedException("Can not read from a closed socket", getServerAddress())); return; } ByteBufferBsonOutput bsonOutput = new ByteBufferBsonOutput(this); ByteBufferBsonOutput compressedBsonOutput = new ByteBufferBsonOutput(this); try { message.encode(bsonOutput, sessionContext); CommandEventSender commandEventSender = createCommandEventSender(message, bsonOutput, requestContext, operationContext); commandEventSender.sendStartedEvent(); Compressor localSendCompressor = sendCompressor; if (localSendCompressor == null || SECURITY_SENSITIVE_COMMANDS.contains(message.getCommandDocument(bsonOutput).getFirstKey())) { sendCommandMessageAsync(message.getId(), decoder, sessionContext, callback, bsonOutput, commandEventSender, message.isResponseExpected()); } else { List byteBuffers = bsonOutput.getByteBuffers(); try { CompressedMessage compressedMessage = new CompressedMessage(message.getOpCode(), byteBuffers, localSendCompressor, getMessageSettings(description)); compressedMessage.encode(compressedBsonOutput, sessionContext); } finally { ResourceUtil.release(byteBuffers); bsonOutput.close(); } sendCommandMessageAsync(message.getId(), decoder, sessionContext, callback, compressedBsonOutput, commandEventSender, message.isResponseExpected()); } } catch (Throwable t) { bsonOutput.close(); compressedBsonOutput.close(); callback.onResult(null, t); } } private void sendCommandMessageAsync(final int messageId, final Decoder decoder, final SessionContext sessionContext, final SingleResultCallback callback, final ByteBufferBsonOutput bsonOutput, final CommandEventSender commandEventSender, final boolean responseExpected) { List byteBuffers = bsonOutput.getByteBuffers(); sendMessageAsync(byteBuffers, messageId, (result, t) -> { ResourceUtil.release(byteBuffers); bsonOutput.close(); if (t != null) { commandEventSender.sendFailedEvent(t); callback.onResult(null, t); } else if (!responseExpected) { commandEventSender.sendSucceededEventForOneWayCommand(); callback.onResult(null, null); } else { readAsync(MESSAGE_HEADER_LENGTH, new MessageHeaderCallback((responseBuffers, t1) -> { if (t1 != null) { commandEventSender.sendFailedEvent(t1); callback.onResult(null, t1); return; } assertNotNull(responseBuffers); try { updateSessionContext(sessionContext, responseBuffers); boolean commandOk = isCommandOk(new BsonBinaryReader(new ByteBufferBsonInput(responseBuffers.getBodyByteBuffer()))); responseBuffers.reset(); if (!commandOk) { MongoException commandFailureException = getCommandFailureException( responseBuffers.getResponseDocument(messageId, new BsonDocumentCodec()), description.getServerAddress()); commandEventSender.sendFailedEvent(commandFailureException); throw commandFailureException; } commandEventSender.sendSucceededEvent(responseBuffers); T result1 = getCommandResult(decoder, responseBuffers, messageId); callback.onResult(result1, null); } catch (Throwable localThrowable) { callback.onResult(null, localThrowable); } finally { responseBuffers.close(); } })); } }); } private T getCommandResult(final Decoder decoder, final ResponseBuffers responseBuffers, final int messageId) { T result = new ReplyMessage<>(responseBuffers, decoder, messageId).getDocument(); MongoException writeConcernBasedError = createSpecialWriteConcernException(responseBuffers, description.getServerAddress()); if (writeConcernBasedError != null) { throw new MongoWriteConcernWithResponseException(writeConcernBasedError, result); } return result; } @Override public void sendMessage(final List byteBuffers, final int lastRequestId) { notNull("stream is open", stream); if (isClosed()) { throw new MongoSocketClosedException("Cannot write to a closed stream", getServerAddress()); } try { stream.write(byteBuffers); } catch (Exception e) { close(); throw translateWriteException(e); } } @Override public ResponseBuffers receiveMessage(final int responseTo) { assertNotNull(stream); if (isClosed()) { throw new MongoSocketClosedException("Cannot read from a closed stream", getServerAddress()); } return receiveMessageWithAdditionalTimeout(0); } private ResponseBuffers receiveMessageWithAdditionalTimeout(final int additionalTimeout) { try { return receiveResponseBuffers(additionalTimeout); } catch (Throwable t) { close(); throw translateReadException(t); } } @Override public void sendMessageAsync(final List byteBuffers, final int lastRequestId, final SingleResultCallback callback) { assertNotNull(stream); if (isClosed()) { callback.onResult(null, new MongoSocketClosedException("Can not read from a closed socket", getServerAddress())); return; } writeAsync(byteBuffers, errorHandlingCallback(callback, LOGGER)); } private void writeAsync(final List byteBuffers, final SingleResultCallback callback) { try { stream.writeAsync(byteBuffers, new AsyncCompletionHandler() { @Override public void completed(@Nullable final Void v) { callback.onResult(null, null); } @Override public void failed(final Throwable t) { close(); callback.onResult(null, translateWriteException(t)); } }); } catch (Throwable t) { close(); callback.onResult(null, t); } } @Override public void receiveMessageAsync(final int responseTo, final SingleResultCallback callback) { assertNotNull(stream); if (isClosed()) { callback.onResult(null, new MongoSocketClosedException("Can not read from a closed socket", getServerAddress())); return; } readAsync(MESSAGE_HEADER_LENGTH, new MessageHeaderCallback((result, t) -> { if (t != null) { close(); callback.onResult(null, t); } else { callback.onResult(result, null); } })); } private void readAsync(final int numBytes, final SingleResultCallback callback) { if (isClosed()) { callback.onResult(null, new MongoSocketClosedException("Cannot read from a closed stream", getServerAddress())); return; } try { stream.readAsync(numBytes, new AsyncCompletionHandler() { @Override public void completed(@Nullable final ByteBuf buffer) { callback.onResult(buffer, null); } @Override public void failed(final Throwable t) { close(); callback.onResult(null, translateReadException(t)); } }); } catch (Exception e) { close(); callback.onResult(null, translateReadException(e)); } } private ConnectionId getId() { return description.getConnectionId(); } private ServerAddress getServerAddress() { return description.getServerAddress(); } private void updateSessionContext(final SessionContext sessionContext, final ResponseBuffers responseBuffers) { sessionContext.advanceOperationTime(getOperationTime(responseBuffers)); sessionContext.advanceClusterTime(getClusterTime(responseBuffers)); sessionContext.setSnapshotTimestamp(getSnapshotTimestamp(responseBuffers)); if (sessionContext.hasActiveTransaction()) { BsonDocument recoveryToken = getRecoveryToken(responseBuffers); if (recoveryToken != null) { sessionContext.setRecoveryToken(recoveryToken); } } } private MongoException translateWriteException(final Throwable e) { if (e instanceof MongoException) { return (MongoException) e; } Optional interruptedException = translateInterruptedException(e, "Interrupted while sending message"); if (interruptedException.isPresent()) { return interruptedException.get(); } else if (e instanceof IOException) { return new MongoSocketWriteException("Exception sending message", getServerAddress(), e); } else { return new MongoInternalException("Unexpected exception", e); } } private MongoException translateReadException(final Throwable e) { if (e instanceof MongoException) { return (MongoException) e; } Optional interruptedException = translateInterruptedException(e, "Interrupted while receiving message"); if (interruptedException.isPresent()) { return interruptedException.get(); } else if (e instanceof SocketTimeoutException) { return new MongoSocketReadTimeoutException("Timeout while receiving message", getServerAddress(), e); } else if (e instanceof IOException) { return new MongoSocketReadException("Exception receiving message", getServerAddress(), e); } else if (e instanceof RuntimeException) { return new MongoInternalException("Unexpected runtime exception", e); } else { return new MongoInternalException("Unexpected exception", e); } } private ResponseBuffers receiveResponseBuffers(final int additionalTimeout) throws IOException { ByteBuf messageHeaderBuffer = stream.read(MESSAGE_HEADER_LENGTH, additionalTimeout); MessageHeader messageHeader; try { messageHeader = new MessageHeader(messageHeaderBuffer, description.getMaxMessageSize()); } finally { messageHeaderBuffer.release(); } ByteBuf messageBuffer = stream.read(messageHeader.getMessageLength() - MESSAGE_HEADER_LENGTH, additionalTimeout); boolean releaseMessageBuffer = true; try { if (messageHeader.getOpCode() == OP_COMPRESSED.getValue()) { CompressedHeader compressedHeader = new CompressedHeader(messageBuffer, messageHeader); Compressor compressor = getCompressor(compressedHeader); ByteBuf buffer = getBuffer(compressedHeader.getUncompressedSize()); compressor.uncompress(messageBuffer, buffer); buffer.flip(); return new ResponseBuffers(new ReplyHeader(buffer, compressedHeader), buffer); } else { ResponseBuffers responseBuffers = new ResponseBuffers(new ReplyHeader(messageBuffer, messageHeader), messageBuffer); releaseMessageBuffer = false; return responseBuffers; } } finally { if (releaseMessageBuffer) { messageBuffer.release(); } } } private Compressor getCompressor(final CompressedHeader compressedHeader) { Compressor compressor = compressorMap.get(compressedHeader.getCompressorId()); if (compressor == null) { throw new MongoClientException("Unsupported compressor with identifier " + compressedHeader.getCompressorId()); } return compressor; } @Override public ByteBuf getBuffer(final int size) { notNull("open", stream); return stream.getBuffer(size); } private class MessageHeaderCallback implements SingleResultCallback { private final SingleResultCallback callback; MessageHeaderCallback(final SingleResultCallback callback) { this.callback = callback; } @Override public void onResult(@Nullable final ByteBuf result, @Nullable final Throwable t) { if (t != null) { callback.onResult(null, t); return; } try { assertNotNull(result); MessageHeader messageHeader = new MessageHeader(result, description.getMaxMessageSize()); readAsync(messageHeader.getMessageLength() - MESSAGE_HEADER_LENGTH, new MessageCallback(messageHeader)); } catch (Throwable localThrowable) { callback.onResult(null, localThrowable); } finally { if (result != null) { result.release(); } } } private class MessageCallback implements SingleResultCallback { private final MessageHeader messageHeader; MessageCallback(final MessageHeader messageHeader) { this.messageHeader = messageHeader; } @Override public void onResult(@Nullable final ByteBuf result, @Nullable final Throwable t) { if (t != null) { callback.onResult(null, t); return; } boolean releaseResult = true; assertNotNull(result); try { ReplyHeader replyHeader; ByteBuf responseBuffer; if (messageHeader.getOpCode() == OP_COMPRESSED.getValue()) { try { CompressedHeader compressedHeader = new CompressedHeader(result, messageHeader); Compressor compressor = getCompressor(compressedHeader); ByteBuf buffer = getBuffer(compressedHeader.getUncompressedSize()); compressor.uncompress(result, buffer); buffer.flip(); replyHeader = new ReplyHeader(buffer, compressedHeader); responseBuffer = buffer; } finally { releaseResult = false; result.release(); } } else { replyHeader = new ReplyHeader(result, messageHeader); responseBuffer = result; releaseResult = false; } callback.onResult(new ResponseBuffers(replyHeader, responseBuffer), null); } catch (Throwable localThrowable) { callback.onResult(null, localThrowable); } finally { if (releaseResult) { result.release(); } } } } } private static final StructuredLogger COMMAND_PROTOCOL_LOGGER = new StructuredLogger("protocol.command"); private CommandEventSender createCommandEventSender(final CommandMessage message, final ByteBufferBsonOutput bsonOutput, final RequestContext requestContext, final OperationContext operationContext) { boolean listensOrLogs = commandListener != null || COMMAND_PROTOCOL_LOGGER.isRequired(DEBUG, getClusterId()); if (!recordEverything && (isMonitoringConnection || !opened() || !authenticated.get() || !listensOrLogs)) { return new NoOpCommandEventSender(); } return new LoggingCommandEventSender( SECURITY_SENSITIVE_COMMANDS, SECURITY_SENSITIVE_HELLO_COMMANDS, description, commandListener, requestContext, operationContext, message, bsonOutput, COMMAND_PROTOCOL_LOGGER, loggerSettings); } private ClusterId getClusterId() { return description.getConnectionId().getServerId().getClusterId(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy