com.mongodb.internal.connection.DefaultServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mongodb-driver-core Show documentation
Show all versions of mongodb-driver-core Show documentation
The Java operations layer for the MongoDB Java Driver. Third parties can ' +
'wrap this layer to provide custom higher-level APIs
/*
* 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.MongoException;
import com.mongodb.MongoServerUnavailableException;
import com.mongodb.MongoSocketException;
import com.mongodb.ReadPreference;
import com.mongodb.RequestContext;
import com.mongodb.ServerApi;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ConnectionDescription;
import com.mongodb.connection.ServerId;
import com.mongodb.event.CommandListener;
import com.mongodb.event.ServerClosedEvent;
import com.mongodb.event.ServerListener;
import com.mongodb.event.ServerOpeningEvent;
import com.mongodb.internal.VisibleForTesting;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.connection.SdamServerDescriptionManager.SdamIssue;
import com.mongodb.internal.diagnostics.logging.Logger;
import com.mongodb.internal.diagnostics.logging.Loggers;
import com.mongodb.internal.session.SessionContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
import org.bson.FieldNameValidator;
import org.bson.codecs.Decoder;
import java.util.concurrent.atomic.AtomicInteger;
import static com.mongodb.assertions.Assertions.assertNotNull;
import static com.mongodb.assertions.Assertions.assertTrue;
import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.internal.VisibleForTesting.AccessModifier.PRIVATE;
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
import static com.mongodb.internal.connection.ServerDescriptionHelper.unknownConnectingServerDescription;
class DefaultServer implements ClusterableServer {
private static final Logger LOGGER = Loggers.getLogger("connection");
private final ServerId serverId;
private final ConnectionPool connectionPool;
private final ClusterConnectionMode clusterConnectionMode;
private final ConnectionFactory connectionFactory;
private final ServerMonitor serverMonitor;
private final SdamServerDescriptionManager sdam;
private final ServerListener serverListener;
private final CommandListener commandListener;
private final ClusterClock clusterClock;
@Nullable
private final AtomicInteger operationCount;
private volatile boolean isClosed;
DefaultServer(final ServerId serverId, final ClusterConnectionMode clusterConnectionMode, final ConnectionPool connectionPool,
final ConnectionFactory connectionFactory, final ServerMonitor serverMonitor,
final SdamServerDescriptionManager sdam, final ServerListener serverListener,
final CommandListener commandListener, final ClusterClock clusterClock, final boolean trackOperationCount) {
this.sdam = assertNotNull(sdam);
this.serverListener = notNull("serverListener", serverListener);
this.commandListener = commandListener;
this.clusterClock = notNull("clusterClock", clusterClock);
notNull("serverAddress", serverId);
this.clusterConnectionMode = notNull("clusterConnectionMode", clusterConnectionMode);
this.connectionFactory = notNull("connectionFactory", connectionFactory);
this.connectionPool = notNull("connectionPool", connectionPool);
this.serverId = serverId;
serverListener.serverOpening(new ServerOpeningEvent(this.serverId));
this.serverMonitor = serverMonitor;
operationCount = trackOperationCount ? new AtomicInteger() : null;
}
@Override
public Connection getConnection() {
if (isClosed) {
throw new MongoServerUnavailableException(String.format("The server at %s is no longer available", serverId.getAddress()));
}
SdamIssue.Context exceptionContext = sdam.context();
operationBegin();
try {
return OperationCountTrackingConnection.decorate(this,
connectionFactory.create(connectionPool.get(), new DefaultServerProtocolExecutor(), clusterConnectionMode));
} catch (Throwable e) {
operationEnd();
if (e instanceof MongoException) {
sdam.handleExceptionBeforeHandshake(SdamIssue.specific(e, exceptionContext));
}
throw e;
}
}
@Override
public void getConnectionAsync(final SingleResultCallback callback) {
if (isClosed) {
callback.onResult(null, new MongoServerUnavailableException(
String.format("The server at %s is no longer available", serverId.getAddress())));
return;
}
SdamIssue.Context exceptionContext = sdam.context();
operationBegin();
connectionPool.getAsync((result, t) -> {
if (t != null) {
try {
operationEnd();
sdam.handleExceptionBeforeHandshake(SdamIssue.specific(t, exceptionContext));
} finally {
callback.onResult(null, t);
}
} else {
callback.onResult(AsyncOperationCountTrackingConnection.decorate(DefaultServer.this,
connectionFactory.createAsync(assertNotNull(result), new DefaultServerProtocolExecutor(), clusterConnectionMode)),
null);
}
});
}
@Override
public int operationCount() {
return operationCount == null ? -1 : operationCount.get();
}
private void operationBegin() {
if (operationCount != null) {
operationCount.incrementAndGet();
}
}
private void operationEnd() {
if (operationCount != null) {
assertTrue(operationCount.decrementAndGet() >= 0);
}
}
@Override
public void resetToConnecting() {
sdam.update(unknownConnectingServerDescription(serverId, null));
}
@Override
public void invalidate() {
if (!isClosed()) {
sdam.handleExceptionAfterHandshake(SdamIssue.unspecified(sdam.context()));
}
}
@Override
public void close() {
if (!isClosed()) {
connectionPool.close();
serverMonitor.close();
isClosed = true;
serverListener.serverClosed(new ServerClosedEvent(serverId));
}
}
@Override
public boolean isClosed() {
return isClosed;
}
@Override
public void connect() {
serverMonitor.connect();
}
@VisibleForTesting(otherwise = PRIVATE)
ConnectionPool getConnectionPool() {
return connectionPool;
}
@VisibleForTesting(otherwise = PRIVATE)
SdamServerDescriptionManager sdamServerDescriptionManager() {
return sdam;
}
@VisibleForTesting(otherwise = PRIVATE)
ServerId serverId() {
return serverId;
}
private class DefaultServerProtocolExecutor implements ProtocolExecutor {
@SuppressWarnings("unchecked")
@Override
public T execute(final CommandProtocol protocol, final InternalConnection connection,
final SessionContext sessionContext) {
try {
protocol.sessionContext(new ClusterClockAdvancingSessionContext(sessionContext, clusterClock));
return protocol.execute(connection);
} catch (MongoException e) {
sdam.handleExceptionAfterHandshake(SdamIssue.specific(e, sdam.context(connection)));
if (e instanceof MongoWriteConcernWithResponseException) {
return (T) ((MongoWriteConcernWithResponseException) e).getResponse();
} else {
if (e instanceof MongoSocketException && sessionContext.hasSession()) {
sessionContext.markSessionDirty();
}
throw e;
}
}
}
@SuppressWarnings("unchecked")
@Override
public void executeAsync(final CommandProtocol protocol, final InternalConnection connection,
final SessionContext sessionContext, final SingleResultCallback callback) {
protocol.sessionContext(new ClusterClockAdvancingSessionContext(sessionContext, clusterClock));
protocol.executeAsync(connection, errorHandlingCallback((result, t) -> {
if (t != null) {
try {
sdam.handleExceptionAfterHandshake(SdamIssue.specific(t, sdam.context(connection)));
} finally {
if (t instanceof MongoWriteConcernWithResponseException) {
callback.onResult((T) ((MongoWriteConcernWithResponseException) t).getResponse(), null);
} else {
if (t instanceof MongoSocketException && sessionContext.hasSession()) {
sessionContext.markSessionDirty();
}
callback.onResult(null, t);
}
}
} else {
callback.onResult(result, null);
}
}, LOGGER));
}
}
private static final class OperationCountTrackingConnection implements Connection {
private final DefaultServer server;
private final Connection wrapped;
static Connection decorate(final DefaultServer server, final Connection connection) {
return server.operationCount() < 0
? connection
: new OperationCountTrackingConnection(server, connection);
}
private OperationCountTrackingConnection(final DefaultServer server, final Connection connection) {
this.server = server;
wrapped = connection;
}
@Override
public int getCount() {
return wrapped.getCount();
}
@Override
public int release() {
int count = wrapped.release();
if (count == 0) {
server.operationEnd();
}
return count;
}
@Override
public Connection retain() {
wrapped.retain();
return this;
}
@Override
public ConnectionDescription getDescription() {
return wrapped.getDescription();
}
@Override
public T command(final String database, final BsonDocument command, final FieldNameValidator fieldNameValidator,
@Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final SessionContext sessionContext,
@Nullable final ServerApi serverApi, final RequestContext requestContext) {
return wrapped.command(database, command, fieldNameValidator, readPreference, commandResultDecoder, sessionContext, serverApi,
requestContext);
}
@Override
public T command(final String database, final BsonDocument command, final FieldNameValidator commandFieldNameValidator,
@Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final SessionContext sessionContext,
@Nullable final ServerApi serverApi, final RequestContext requestContext, final boolean responseExpected,
@Nullable final SplittablePayload payload, @Nullable final FieldNameValidator payloadFieldNameValidator) {
return wrapped.command(database, command, commandFieldNameValidator, readPreference, commandResultDecoder, sessionContext,
serverApi, requestContext, responseExpected, payload, payloadFieldNameValidator);
}
@Override
public void markAsPinned(final PinningMode pinningMode) {
wrapped.markAsPinned(pinningMode);
}
}
private static final class AsyncOperationCountTrackingConnection implements AsyncConnection {
private final DefaultServer server;
private final AsyncConnection wrapped;
static AsyncConnection decorate(final DefaultServer server, final AsyncConnection connection) {
return server.operationCount() < 0
? connection
: new AsyncOperationCountTrackingConnection(server, connection);
}
AsyncOperationCountTrackingConnection(final DefaultServer server, final AsyncConnection connection) {
this.server = server;
wrapped = connection;
}
@Override
public int getCount() {
return wrapped.getCount();
}
@Override
public int release() {
int count = wrapped.release();
if (count == 0) {
server.operationEnd();
}
return count;
}
@Override
public AsyncConnection retain() {
wrapped.retain();
return this;
}
@Override
public ConnectionDescription getDescription() {
return wrapped.getDescription();
}
@Override
public void commandAsync(final String database, final BsonDocument command, final FieldNameValidator fieldNameValidator,
@Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final SessionContext sessionContext,
@Nullable final ServerApi serverApi, final RequestContext requestContext, final SingleResultCallback callback) {
wrapped.commandAsync(database, command, fieldNameValidator, readPreference, commandResultDecoder, sessionContext, serverApi,
requestContext, callback);
}
@Override
public void commandAsync(final String database, final BsonDocument command, final FieldNameValidator commandFieldNameValidator,
@Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final SessionContext sessionContext,
@Nullable final ServerApi serverApi, final RequestContext requestContext, final boolean responseExpected,
@Nullable final SplittablePayload payload, @Nullable final FieldNameValidator payloadFieldNameValidator,
final SingleResultCallback callback) {
wrapped.commandAsync(database, command, commandFieldNameValidator, readPreference, commandResultDecoder, sessionContext,
serverApi, requestContext, responseExpected, payload, payloadFieldNameValidator, callback);
}
@Override
public void markAsPinned(final Connection.PinningMode pinningMode) {
wrapped.markAsPinned(pinningMode);
}
}
}