/** * An implementation of the insert wire protocol. This class also takes care of applying the write * concern. * * @mongodb.driver.manual ../meta-driver/latest/legacy/mongodb-wire-protocol/#op-insert OP_INSERT */ class InsertProtocol extends WriteProtocol { private static final Logger LOGGER = Loggers.getLogger("protocol.insert"); private final List<InsertRequest> insertRequestList; /** * Construct a new instance. * * @param namespace the namespace * @param ordered whether the inserts are ordered * @param writeConcern the write concern * @param insertRequestList the list of documents to insert */ public InsertProtocol( final MongoNamespace namespace, final boolean ordered, final WriteConcern writeConcern, final List<InsertRequest> insertRequestList) { super(namespace, ordered, writeConcern); this.insertRequestList = insertRequestList; } @Override public WriteConcernResult execute(final InternalConnection connection) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( format( "Inserting %d documents into namespace %s on connection [%s] to server %s", insertRequestList.size(), getNamespace(), connection.getDescription().getConnectionId(), connection.getDescription().getServerAddress())); } WriteConcernResult writeConcernResult = super.execute(connection); LOGGER.debug("Insert completed"); return writeConcernResult; } @Override public void executeAsync( final InternalConnection connection, final SingleResultCallback<WriteConcernResult> callback) { try { if (LOGGER.isDebugEnabled()) { LOGGER.debug( format( "Asynchronously inserting %d documents into namespace %s on connection [%s] to server %s", insertRequestList.size(), getNamespace(), connection.getDescription().getConnectionId(), connection.getDescription().getServerAddress())); } super.executeAsync( connection, new SingleResultCallback<WriteConcernResult>() { @Override public void onResult(final WriteConcernResult result, final Throwable t) { if (t != null) { callback.onResult(null, t); } else { LOGGER.debug("Asynchronous insert completed"); callback.onResult(result, null); } } }); } catch (Throwable t) { callback.onResult(null, t); } } @Override protected BsonDocument getAsWriteCommand( final ByteBufferBsonOutput bsonOutput, final int firstDocumentPosition) { return getBaseCommandDocument("insert") .append( "documents", new BsonArray(ByteBufBsonDocument.create(bsonOutput, firstDocumentPosition))); } protected RequestMessage createRequestMessage(final MessageSettings settings) { return new InsertMessage( getNamespace().getFullName(), isOrdered(), getWriteConcern(), insertRequestList, settings); } @Override protected void appendToWriteCommandResponseDocument( final RequestMessage curMessage, final RequestMessage nextMessage, final WriteConcernResult writeConcernResult, final BsonDocument response) { response.append( "n", new BsonInt32( nextMessage == null ? ((InsertMessage) curMessage).getInsertRequestList().size() : ((InsertMessage) curMessage).getInsertRequestList().size() - ((InsertMessage) nextMessage).getInsertRequestList().size())); } @Override protected com.mongodb.diagnostics.logging.Logger getLogger() { return LOGGER; } }
final class OperationHelper { public static final Logger LOGGER = Loggers.getLogger("operation"); interface CallableWithConnection<T> { T call(Connection connection); } interface CallableWithConnectionAndSource<T> { T call(ConnectionSource source, Connection connection); } interface AsyncCallableWithConnection { void call(AsyncConnection connection, Throwable t); } interface AsyncCallableWithConnectionAndSource { void call(AsyncConnectionSource source, AsyncConnection connection, Throwable t); } static void checkValidReadConcern(final Connection connection, final ReadConcern readConcern) { if (!serverIsAtLeastVersionThreeDotTwo(connection.getDescription()) && !readConcern.isServerDefault()) { throw new IllegalArgumentException( format( "ReadConcern not supported by server version: %s", connection.getDescription().getServerVersion())); } } static void checkValidReadConcern( final AsyncConnection connection, final ReadConcern readConcern, final AsyncCallableWithConnection callable) { Throwable throwable = null; if (!serverIsAtLeastVersionThreeDotTwo(connection.getDescription()) && !readConcern.isServerDefault()) { throwable = new IllegalArgumentException( format( "ReadConcern not supported by server version: %s", connection.getDescription().getServerVersion())); } callable.call(connection, throwable); } static void checkValidReadConcern( final AsyncConnectionSource source, final AsyncConnection connection, final ReadConcern readConcern, final AsyncCallableWithConnectionAndSource callable) { checkValidReadConcern( connection, readConcern, new AsyncCallableWithConnection() { @Override public void call(final AsyncConnection connection, final Throwable t) { callable.call(source, connection, t); } }); } static void checkValidCollation(final Connection connection, final Collation collation) { if (!serverIsAtLeastVersionThreeDotFour(connection.getDescription()) && collation != null) { throw new IllegalArgumentException( format( "Collation not supported by server version: %s", connection.getDescription().getServerVersion())); } } static void checkValidCollation( final AsyncConnection connection, final Collation collation, final AsyncCallableWithConnection callable) { Throwable throwable = null; if (!serverIsAtLeastVersionThreeDotFour(connection.getDescription()) && collation != null) { throwable = new IllegalArgumentException( format( "Collation not supported by server version: %s", connection.getDescription().getServerVersion())); } callable.call(connection, throwable); } static void checkValidCollation( final AsyncConnectionSource source, final AsyncConnection connection, final Collation collation, final AsyncCallableWithConnectionAndSource callable) { checkValidCollation( connection, collation, new AsyncCallableWithConnection() { @Override public void call(final AsyncConnection connection, final Throwable t) { callable.call(source, connection, t); } }); } static void checkValidWriteRequestCollations( final Connection connection, final List<? extends WriteRequest> requests) { Collation collation = null; for (WriteRequest request : requests) { if (request instanceof UpdateRequest) { collation = ((UpdateRequest) request).getCollation(); } else if (request instanceof DeleteRequest) { collation = ((DeleteRequest) request).getCollation(); } if (collation != null) { break; } } checkValidCollation(connection, collation); } static void checkValidWriteRequestCollations( final AsyncConnection connection, final List<? extends WriteRequest> requests, final AsyncCallableWithConnection callable) { Collation collation = null; for (WriteRequest request : requests) { if (request instanceof UpdateRequest) { collation = ((UpdateRequest) request).getCollation(); } else if (request instanceof DeleteRequest) { collation = ((DeleteRequest) request).getCollation(); } if (collation != null) { break; } } checkValidCollation( connection, collation, new AsyncCallableWithConnection() { @Override public void call(final AsyncConnection connection, final Throwable t) { callable.call(connection, t); } }); } static void checkValidIndexRequestCollations( final Connection connection, final List<IndexRequest> requests) { for (IndexRequest request : requests) { if (request.getCollation() != null) { checkValidCollation(connection, request.getCollation()); break; } } } static void checkValidIndexRequestCollations( final AsyncConnection connection, final List<IndexRequest> requests, final AsyncCallableWithConnection callable) { boolean calledTheCallable = false; for (IndexRequest request : requests) { if (request.getCollation() != null) { calledTheCallable = true; checkValidCollation( connection, request.getCollation(), new AsyncCallableWithConnection() { @Override public void call(final AsyncConnection connection, final Throwable t) { callable.call(connection, t); } }); break; } } if (!calledTheCallable) { callable.call(connection, null); } } static void checkValidReadConcernAndCollation( final Connection connection, final ReadConcern readConcern, final Collation collation) { checkValidReadConcern(connection, readConcern); checkValidCollation(connection, collation); } static void checkValidReadConcernAndCollation( final AsyncConnection connection, final ReadConcern readConcern, final Collation collation, final AsyncCallableWithConnection callable) { checkValidReadConcern( connection, readConcern, new AsyncCallableWithConnection() { @Override public void call(final AsyncConnection connection, final Throwable t) { if (t != null) { callable.call(connection, t); } else { checkValidCollation(connection, collation, callable); } } }); } static void checkValidReadConcernAndCollation( final AsyncConnectionSource source, final AsyncConnection connection, final ReadConcern readConcern, final Collation collation, final AsyncCallableWithConnectionAndSource callable) { checkValidReadConcernAndCollation( connection, readConcern, collation, new AsyncCallableWithConnection() { @Override public void call(final AsyncConnection connection, final Throwable t) { callable.call(source, connection, t); } }); } static boolean bypassDocumentValidationNotSupported( final Boolean bypassDocumentValidation, final WriteConcern writeConcern, final ConnectionDescription description) { return bypassDocumentValidation != null && serverIsAtLeastVersionThreeDotTwo(description) && !writeConcern.isAcknowledged(); } static MongoClientException getBypassDocumentValidationException() { return new MongoClientException( "Specifying bypassDocumentValidation with an unacknowledged WriteConcern " + "is not supported"); } static <T> QueryBatchCursor<T> createEmptyBatchCursor( final MongoNamespace namespace, final Decoder<T> decoder, final ServerAddress serverAddress, final int batchSize) { return new QueryBatchCursor<T>( new QueryResult<T>(namespace, Collections.<T>emptyList(), 0L, serverAddress), 0, batchSize, decoder); } static <T> AsyncBatchCursor<T> createEmptyAsyncBatchCursor( final MongoNamespace namespace, final Decoder<T> decoder, final ServerAddress serverAddress, final int batchSize) { return new AsyncQueryBatchCursor<T>( new QueryResult<T>(namespace, Collections.<T>emptyList(), 0L, serverAddress), 0, batchSize, decoder); } static <T> BatchCursor<T> cursorDocumentToBatchCursor( final BsonDocument cursorDocument, final Decoder<T> decoder, final ConnectionSource source, final int batchSize) { return new QueryBatchCursor<T>( OperationHelper.<T>cursorDocumentToQueryResult( cursorDocument, source.getServerDescription().getAddress()), 0, batchSize, decoder, source); } static <T> AsyncBatchCursor<T> cursorDocumentToAsyncBatchCursor( final BsonDocument cursorDocument, final Decoder<T> decoder, final AsyncConnectionSource source, final AsyncConnection connection, final int batchSize) { return new AsyncQueryBatchCursor<T>( OperationHelper.<T>cursorDocumentToQueryResult( cursorDocument, source.getServerDescription().getAddress()), 0, batchSize, 0, decoder, source, connection); } static <T> QueryResult<T> cursorDocumentToQueryResult( final BsonDocument cursorDocument, final ServerAddress serverAddress) { return cursorDocumentToQueryResult(cursorDocument, serverAddress, "firstBatch"); } static <T> QueryResult<T> getMoreCursorDocumentToQueryResult( final BsonDocument cursorDocument, final ServerAddress serverAddress) { return cursorDocumentToQueryResult(cursorDocument, serverAddress, "nextBatch"); } private static <T> QueryResult<T> cursorDocumentToQueryResult( final BsonDocument cursorDocument, final ServerAddress serverAddress, final String fieldNameContainingBatch) { long cursorId = ((BsonInt64) cursorDocument.get("id")).getValue(); MongoNamespace queryResultNamespace = new MongoNamespace(cursorDocument.getString("ns").getValue()); return new QueryResult<T>( queryResultNamespace, BsonDocumentWrapperHelper.<T>toList(cursorDocument, fieldNameContainingBatch), cursorId, serverAddress); } static <T> SingleResultCallback<T> releasingCallback( final SingleResultCallback<T> wrapped, final AsyncConnection connection) { return new ReferenceCountedReleasingWrappedCallback<T>(wrapped, singletonList(connection)); } static <T> SingleResultCallback<T> releasingCallback( final SingleResultCallback<T> wrapped, final AsyncConnectionSource source, final AsyncConnection connection) { return new ReferenceCountedReleasingWrappedCallback<T>(wrapped, asList(connection, source)); } static <T> SingleResultCallback<T> releasingCallback( final SingleResultCallback<T> wrapped, final AsyncReadBinding readBinding, final AsyncConnectionSource source, final AsyncConnection connection) { return new ReferenceCountedReleasingWrappedCallback<T>( wrapped, asList(readBinding, connection, source)); } private static class ReferenceCountedReleasingWrappedCallback<T> implements SingleResultCallback<T> { private final SingleResultCallback<T> wrapped; private final List<? extends ReferenceCounted> referenceCounted; ReferenceCountedReleasingWrappedCallback( final SingleResultCallback<T> wrapped, final List<? extends ReferenceCounted> referenceCounted) { this.wrapped = wrapped; this.referenceCounted = notNull("referenceCounted", referenceCounted); } @Override public void onResult(final T result, final Throwable t) { for (ReferenceCounted cur : referenceCounted) { cur.release(); } wrapped.onResult(result, t); } } static boolean serverIsAtLeastVersionTwoDotSix(final ConnectionDescription description) { return serverIsAtLeastVersion(description, new ServerVersion(2, 6)); } static boolean serverIsAtLeastVersionThreeDotZero(final ConnectionDescription description) { return serverIsAtLeastVersion(description, new ServerVersion(asList(3, 0, 0))); } static boolean serverIsAtLeastVersionThreeDotTwo(final ConnectionDescription description) { return serverIsAtLeastVersion(description, new ServerVersion(asList(3, 1, 9))); } static boolean serverIsAtLeastVersionThreeDotFour(final ConnectionDescription description) { return serverIsAtLeastVersion(description, new ServerVersion(asList(3, 3, 10))); } static boolean serverIsAtLeastVersion( final ConnectionDescription description, final ServerVersion serverVersion) { return description.getServerVersion().compareTo(serverVersion) >= 0; } static <T> T withConnection(final ReadBinding binding, final CallableWithConnection<T> callable) { ConnectionSource source = binding.getReadConnectionSource(); try { return withConnectionSource(source, callable); } finally { source.release(); } } static <T> T withConnection( final ReadBinding binding, final CallableWithConnectionAndSource<T> callable) { ConnectionSource source = binding.getReadConnectionSource(); try { return withConnectionSource(source, callable); } finally { source.release(); } } static <T> T withConnection( final WriteBinding binding, final CallableWithConnection<T> callable) { ConnectionSource source = binding.getWriteConnectionSource(); try { return withConnectionSource(source, callable); } finally { source.release(); } } static <T> T withConnectionSource( final ConnectionSource source, final CallableWithConnection<T> callable) { Connection connection = source.getConnection(); try { return callable.call(connection); } finally { connection.release(); } } static <T> T withConnectionSource( final ConnectionSource source, final CallableWithConnectionAndSource<T> callable) { Connection connection = source.getConnection(); try { return callable.call(source, connection); } finally { connection.release(); } } static void withConnection( final AsyncWriteBinding binding, final AsyncCallableWithConnection callable) { binding.getWriteConnectionSource( errorHandlingCallback(new AsyncCallableWithConnectionCallback(callable), LOGGER)); } static void withConnection( final AsyncReadBinding binding, final AsyncCallableWithConnection callable) { binding.getReadConnectionSource( errorHandlingCallback(new AsyncCallableWithConnectionCallback(callable), LOGGER)); } static void withConnection( final AsyncReadBinding binding, final AsyncCallableWithConnectionAndSource callable) { binding.getReadConnectionSource( errorHandlingCallback(new AsyncCallableWithConnectionAndSourceCallback(callable), LOGGER)); } private static class AsyncCallableWithConnectionCallback implements SingleResultCallback<AsyncConnectionSource> { private final AsyncCallableWithConnection callable; public AsyncCallableWithConnectionCallback(final AsyncCallableWithConnection callable) { this.callable = callable; } @Override public void onResult(final AsyncConnectionSource source, final Throwable t) { if (t != null) { callable.call(null, t); } else { withConnectionSource(source, callable); } } } private static void withConnectionSource( final AsyncConnectionSource source, final AsyncCallableWithConnection callable) { source.getConnection( new SingleResultCallback<AsyncConnection>() { @Override public void onResult(final AsyncConnection connection, final Throwable t) { source.release(); if (t != null) { callable.call(null, t); } else { callable.call(connection, null); } } }); } private static void withConnectionSource( final AsyncConnectionSource source, final AsyncCallableWithConnectionAndSource callable) { source.getConnection( new SingleResultCallback<AsyncConnection>() { @Override public void onResult(final AsyncConnection result, final Throwable t) { callable.call(source, result, t); } }); } private static class AsyncCallableWithConnectionAndSourceCallback implements SingleResultCallback<AsyncConnectionSource> { private final AsyncCallableWithConnectionAndSource callable; public AsyncCallableWithConnectionAndSourceCallback( final AsyncCallableWithConnectionAndSource callable) { this.callable = callable; } @Override public void onResult(final AsyncConnectionSource source, final Throwable t) { if (t != null) { callable.call(null, null, t); } else { withConnectionSource(source, callable); } } } private OperationHelper() {} }
/** * A protocol for executing a command against a MongoDB server using the OP_QUERY wire protocol * message. * * @param <T> the type returned from execution * @mongodb.driver.manual ../meta-driver/latest/legacy/mongodb-wire-protocol/#op-query OP_QUERY */ class CommandProtocol<T> implements Protocol<T> { public static final Logger LOGGER = Loggers.getLogger("protocol.command"); private static final Set<String> SECURITY_SENSITIVE_COMMANDS = new HashSet<String>( asList( "authenticate", "saslStart", "saslContinue", "getnonce", "createUser", "updateUser", "copydbgetnonce", "copydbsaslstart", "copydb")); private final MongoNamespace namespace; private final BsonDocument command; private final Decoder<T> commandResultDecoder; private final FieldNameValidator fieldNameValidator; private boolean slaveOk; private CommandListener commandListener; private volatile String commandName; /** * Construct an instance. * * @param database the database * @param command the command * @param fieldNameValidator the field name validator to apply tot the command * @param commandResultDecoder the decoder to use to decode the command result */ public CommandProtocol( final String database, final BsonDocument command, final FieldNameValidator fieldNameValidator, final Decoder<T> commandResultDecoder) { notNull("database", database); this.namespace = new MongoNamespace(database, MongoNamespace.COMMAND_COLLECTION_NAME); this.command = notNull("command", command); this.commandResultDecoder = notNull("commandResultDecoder", commandResultDecoder); this.fieldNameValidator = notNull("fieldNameValidator", fieldNameValidator); } public boolean isSlaveOk() { return slaveOk; } public CommandProtocol<T> slaveOk(final boolean slaveOk) { this.slaveOk = slaveOk; return this; } @Override public T execute(final InternalConnection connection) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( format( "Sending command {%s : %s} to database %s on connection [%s] to server %s", getCommandName(), command.values().iterator().next(), namespace.getDatabaseName(), connection.getDescription().getConnectionId(), connection.getDescription().getServerAddress())); } long startTimeNanos = System.nanoTime(); CommandMessage commandMessage = new CommandMessage( namespace.getFullName(), command, slaveOk, fieldNameValidator, ProtocolHelper.getMessageSettings(connection.getDescription())); ResponseBuffers responseBuffers = null; try { sendMessage(commandMessage, connection); responseBuffers = connection.receiveMessage(commandMessage.getId()); if (!ProtocolHelper.isCommandOk( new BsonBinaryReader(new ByteBufferBsonInput(responseBuffers.getBodyByteBuffer())))) { throw getCommandFailureException( getResponseDocument(responseBuffers, commandMessage, new BsonDocumentCodec()), connection.getDescription().getServerAddress()); } T retval = getResponseDocument(responseBuffers, commandMessage, commandResultDecoder); if (commandListener != null) { sendSucceededEvent( connection.getDescription(), startTimeNanos, commandMessage, getResponseDocument(responseBuffers, commandMessage, new RawBsonDocumentCodec())); } LOGGER.debug("Command execution completed"); return retval; } catch (RuntimeException e) { sendFailedEvent(connection.getDescription(), startTimeNanos, commandMessage, e); throw e; } finally { if (responseBuffers != null) { responseBuffers.close(); } } } private static <D> D getResponseDocument( final ResponseBuffers responseBuffers, final CommandMessage commandMessage, final Decoder<D> decoder) { responseBuffers.reset(); ReplyMessage<D> replyMessage = new ReplyMessage<D>(responseBuffers, decoder, commandMessage.getId()); return replyMessage.getDocuments().get(0); } @Override public void executeAsync( final InternalConnection connection, final SingleResultCallback<T> callback) { long startTimeNanos = System.nanoTime(); CommandMessage message = new CommandMessage( namespace.getFullName(), command, slaveOk, fieldNameValidator, ProtocolHelper.getMessageSettings(connection.getDescription())); boolean sentStartedEvent = false; try { if (LOGGER.isDebugEnabled()) { LOGGER.debug( format( "Asynchronously sending command {%s : %s} to database %s on connection [%s] to server %s", getCommandName(), command.values().iterator().next(), namespace.getDatabaseName(), connection.getDescription().getConnectionId(), connection.getDescription().getServerAddress())); } ByteBufferBsonOutput bsonOutput = new ByteBufferBsonOutput(connection); int documentPosition = ProtocolHelper.encodeMessageWithMetadata(message, bsonOutput).getFirstDocumentPosition(); sendStartedEvent(connection, bsonOutput, message, documentPosition); sentStartedEvent = true; SingleResultCallback<ResponseBuffers> receiveCallback = new CommandResultCallback(callback, message, connection.getDescription(), startTimeNanos); connection.sendMessageAsync( bsonOutput.getByteBuffers(), message.getId(), new SendMessageCallback<T>( connection, bsonOutput, message, getCommandName(), startTimeNanos, commandListener, callback, receiveCallback)); } catch (Throwable t) { if (sentStartedEvent) { sendFailedEvent(connection.getDescription(), startTimeNanos, message, t); } callback.onResult(null, t); } } @Override public void setCommandListener(final CommandListener commandListener) { this.commandListener = commandListener; } private String getCommandName() { return commandName != null ? commandName : command.keySet().iterator().next(); } private void sendMessage(final CommandMessage message, final InternalConnection connection) { ByteBufferBsonOutput bsonOutput = new ByteBufferBsonOutput(connection); try { int documentPosition = message.encodeWithMetadata(bsonOutput).getFirstDocumentPosition(); sendStartedEvent(connection, bsonOutput, message, documentPosition); connection.sendMessage(bsonOutput.getByteBuffers(), message.getId()); } finally { bsonOutput.close(); } } private void sendStartedEvent( final InternalConnection connection, final ByteBufferBsonOutput bsonOutput, final CommandMessage message, final int documentPosition) { if (commandListener != null) { ByteBufBsonDocument byteBufBsonDocument = createOne(bsonOutput, documentPosition); BsonDocument commandDocument; if (byteBufBsonDocument.containsKey("$query")) { commandDocument = byteBufBsonDocument.getDocument("$query"); commandName = commandDocument.keySet().iterator().next(); } else { commandDocument = byteBufBsonDocument; commandName = byteBufBsonDocument.getFirstKey(); } BsonDocument commandDocumentForEvent = (SECURITY_SENSITIVE_COMMANDS.contains(commandName)) ? new BsonDocument() : commandDocument; sendCommandStartedEvent( message, namespace.getDatabaseName(), commandName, commandDocumentForEvent, connection.getDescription(), commandListener); } } private void sendSucceededEvent( final ConnectionDescription connectionDescription, final long startTimeNanos, final CommandMessage commandMessage, final BsonDocument response) { if (commandListener != null) { BsonDocument responseDocumentForEvent = (SECURITY_SENSITIVE_COMMANDS.contains(getCommandName())) ? new BsonDocument() : response; sendCommandSucceededEvent( commandMessage, getCommandName(), responseDocumentForEvent, connectionDescription, startTimeNanos, commandListener); } } private void sendFailedEvent( final ConnectionDescription connectionDescription, final long startTimeNanos, final CommandMessage commandMessage, final Throwable t) { if (commandListener != null) { Throwable commandEventException = t; if (t instanceof MongoCommandException && (SECURITY_SENSITIVE_COMMANDS.contains(getCommandName()))) { commandEventException = new MongoCommandException(new BsonDocument(), connectionDescription.getServerAddress()); } sendCommandFailedEvent( commandMessage, getCommandName(), connectionDescription, startTimeNanos, commandEventException, commandListener); } } class CommandResultCallback extends ResponseCallback { private final SingleResultCallback<T> callback; private final CommandMessage message; private final ConnectionDescription connectionDescription; private final long startTimeNanos; CommandResultCallback( final SingleResultCallback<T> callback, final CommandMessage message, final ConnectionDescription connectionDescription, final long startTimeNanos) { super(message.getId(), connectionDescription.getServerAddress()); this.callback = callback; this.message = message; this.connectionDescription = connectionDescription; this.startTimeNanos = startTimeNanos; } @Override protected void callCallback( final ResponseBuffers responseBuffers, final Throwable throwableFromCallback) { try { if (throwableFromCallback != null) { throw throwableFromCallback; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Command execution completed"); } if (!ProtocolHelper.isCommandOk( new BsonBinaryReader(new ByteBufferBsonInput(responseBuffers.getBodyByteBuffer())))) { throw getCommandFailureException( getResponseDocument(responseBuffers, message, new BsonDocumentCodec()), connectionDescription.getServerAddress()); } if (commandListener != null) { sendSucceededEvent( connectionDescription, startTimeNanos, message, getResponseDocument(responseBuffers, message, new RawBsonDocumentCodec())); } callback.onResult( getResponseDocument(responseBuffers, message, commandResultDecoder), null); } catch (Throwable t) { sendFailedEvent(connectionDescription, startTimeNanos, message, t); callback.onResult(null, t); } finally { if (responseBuffers != null) { responseBuffers.close(); } } } } }