@@ -4,15 +4,6 @@ import NIOConcurrencyHelpers
44
55/// Options to use when creating a `MongoClient`.
66public struct ClientOptions : CodingStrategyProvider , Decodable {
7- /**
8- * Indicates whether this client should publish command monitoring events. If true, the following event types will
9- * be published, under the listed names (which are defined as static properties of `Notification.Name`):
10- * - `CommandStartedEvent`: `.commandStarted`
11- * - `CommandSucceededEvent`: `.commandSucceeded`
12- * - `CommandFailedEvent`: `.commandFailed`
13- */
14- public var commandMonitoring : Bool = false
15-
167 // swiftlint:disable redundant_optional_initialization
178
189 /// Specifies the `DataCodingStrategy` to use for BSON encoding/decoding operations performed by this client and any
@@ -23,10 +14,6 @@ public struct ClientOptions: CodingStrategyProvider, Decodable {
2314 /// databases or collections that derive from it.
2415 public var dateCodingStrategy : DateCodingStrategy ? = nil
2516
26- /// If command and/or server monitoring is enabled, indicates the `NotificationCenter` events are posted to. If one
27- /// is not specified, the application's default `NotificationCenter` will be used.
28- public var notificationCenter : NotificationCenter ?
29-
3017 /// Specifies a ReadConcern to use for the client.
3118 public var readConcern : ReadConcern ?
3219
@@ -39,21 +26,6 @@ public struct ClientOptions: CodingStrategyProvider, Decodable {
3926 /// Determines whether the client should retry supported write operations (on by default).
4027 public var retryWrites : Bool ?
4128
42- /**
43- * Indicates whether this client should publish command monitoring events. If true, the following event types will
44- * be published, under the listed names (which are defined as static properties of `Notification.Name`):
45- * - `ServerOpeningEvent`: `.serverOpening`
46- * - `ServerClosedEvent`: `.serverClosed`
47- * - `ServerDescriptionChangedEvent`: `.serverDescriptionChanged`
48- * - `TopologyOpeningEvent`: `.topologyOpening`
49- * - `TopologyClosedEvent`: `.topologyClosed`
50- * - `TopologyDescriptionChangedEvent`: `.topologyDescriptionChanged`
51- * - `ServerHeartbeatStartedEvent`: `serverHeartbeatStarted`
52- * - `ServerHeartbeatSucceededEvent`: `serverHeartbeatSucceeded`
53- * - `ServerHeartbeatFailedEvent`: `serverHeartbeatFailed`
54- */
55- public var serverMonitoring : Bool = false
56-
5729 /**
5830 * `MongoSwift.MongoClient` provides an asynchronous API by running all blocking operations off of their
5931 * originating threads in a thread pool. `MongoSwiftSync.MongoClient` is implemented as a wrapper of the async
@@ -81,29 +53,23 @@ public struct ClientOptions: CodingStrategyProvider, Decodable {
8153
8254 /// Convenience initializer allowing any/all to be omitted or optional.
8355 public init (
84- commandMonitoring: Bool = false ,
8556 dataCodingStrategy: DataCodingStrategy ? = nil ,
8657 dateCodingStrategy: DateCodingStrategy ? = nil ,
87- notificationCenter: NotificationCenter ? = nil ,
8858 readConcern: ReadConcern ? = nil ,
8959 readPreference: ReadPreference ? = nil ,
9060 retryReads: Bool ? = nil ,
9161 retryWrites: Bool ? = nil ,
92- serverMonitoring: Bool = false ,
9362 threadPoolSize: Int = MongoClient . defaultThreadPoolSize,
9463 tlsOptions: TLSOptions ? = nil ,
9564 uuidCodingStrategy: UUIDCodingStrategy ? = nil ,
9665 writeConcern: WriteConcern ? = nil
9766 ) {
98- self . commandMonitoring = commandMonitoring
9967 self . dataCodingStrategy = dataCodingStrategy
10068 self . dateCodingStrategy = dateCodingStrategy
101- self . notificationCenter = notificationCenter
10269 self . readConcern = readConcern
10370 self . readPreference = readPreference
10471 self . retryWrites = retryWrites
10572 self . retryReads = retryReads
106- self . serverMonitoring = serverMonitoring
10773 self . threadPoolSize = threadPoolSize
10874 self . tlsOptions = tlsOptions
10975 self . uuidCodingStrategy = uuidCodingStrategy
@@ -199,8 +165,11 @@ public class MongoClient {
199165 /// Indicates whether this client has been closed.
200166 internal private( set) var isClosed = false
201167
202- /// If command and/or server monitoring is enabled, stores the NotificationCenter events are posted to.
203- internal let notificationCenter : NotificationCenter
168+ /// Handlers for command monitoring events.
169+ internal var commandEventHandlers : [ CommandEventHandler ]
170+
171+ /// Handlers for SDAM monitoring events.
172+ internal var sdamEventHandlers : [ SDAMEventHandler ]
204173
205174 /// Counter for generating client _ids.
206175 internal static var clientIdGenerator = NIOAtomic< Int> . makeAtomic( value: 0 )
@@ -275,13 +244,9 @@ public class MongoClient {
275244 self . readPreference = connString. readPreference
276245 self . encoder = BSONEncoder ( options: options)
277246 self . decoder = BSONDecoder ( options: options)
278- self . notificationCenter = options? . notificationCenter ?? NotificationCenter . default
279-
280- self . connectionPool. initializeMonitoring (
281- commandMonitoring: options? . commandMonitoring ?? false ,
282- serverMonitoring: options? . serverMonitoring ?? false ,
283- client: self
284- )
247+ self . sdamEventHandlers = [ ]
248+ self . commandEventHandlers = [ ]
249+ self . connectionPool. initializeMonitoring ( client: self )
285250 }
286251
287252 deinit {
@@ -583,6 +548,46 @@ public class MongoClient {
583548 return self . operationExecutor. execute ( operation, client: self , session: session)
584549 }
585550
551+ /**
552+ * Attach a `CommandEventHandler` that will receive `CommandEvent`s emitted by this client.
553+ *
554+ * Note: the client stores a weak reference to this handler, so it must be kept alive separately in order for it
555+ * to continue to receive events.
556+ */
557+ public func addCommandEventHandler< T: CommandEventHandler > ( _ handler: T ) {
558+ self . commandEventHandlers. append ( WeakEventHandler < T > ( referencing: handler) )
559+ }
560+
561+ /**
562+ * Attach a callback that will receive `CommandEvent`s emitted by this client.
563+ *
564+ * Note: if the provided callback captures this client, it must do so weakly. Otherwise, it will constitute a
565+ * strong reference cycle and potentially result in memory leaks.
566+ */
567+ public func addCommandEventHandler( _ handlerFunc: @escaping ( CommandEvent ) -> Void ) {
568+ self . commandEventHandlers. append ( CallbackEventHandler ( handlerFunc) )
569+ }
570+
571+ /**
572+ * Attach an `SDAMEventHandler` that will receive `CommandEvent`s emitted by this client.
573+ *
574+ * Note: the client stores a weak reference to this handler, so it must be kept alive separately in order for it
575+ * to continue to receive events.
576+ */
577+ public func addSDAMEventHandler< T: SDAMEventHandler > ( _ handler: T ) {
578+ self . sdamEventHandlers. append ( WeakEventHandler ( referencing: handler) )
579+ }
580+
581+ /**
582+ * Attach a callback that will receive `SDAMEvent`s emitted by this client.
583+ *
584+ * Note: if the provided callback captures this client, it must do so weakly. Otherwise, it will constitute a
585+ * strong reference cycle and potentially result in memory leaks.
586+ */
587+ public func addSDAMEventHandler( _ handlerFunc: @escaping ( SDAMEvent ) -> Void ) {
588+ self . sdamEventHandlers. append ( CallbackEventHandler ( handlerFunc) )
589+ }
590+
586591 /// Executes an `Operation` using this `MongoClient` and an optionally provided session.
587592 internal func executeOperation< T: Operation > (
588593 _ operation: T ,
@@ -598,3 +603,50 @@ extension MongoClient: Equatable {
598603 return lhs. _id == rhs. _id
599604 }
600605}
606+
607+ /// Event handler constructed from a callback.
608+ /// Stores a strong reference to the provided callback.
609+ private class CallbackEventHandler < EventType> {
610+ private let handlerFunc : ( EventType ) -> Void
611+
612+ fileprivate init ( _ handlerFunc: @escaping ( EventType ) -> Void ) {
613+ self . handlerFunc = handlerFunc
614+ }
615+ }
616+
617+ /// Extension to make `CallbackEventHandler` an `SDAMEventHandler` when the event type is an `SDAMEvent`.
618+ extension CallbackEventHandler : SDAMEventHandler where EventType == SDAMEvent {
619+ fileprivate func handleSDAMEvent( _ event: SDAMEvent ) {
620+ self . handlerFunc ( event)
621+ }
622+ }
623+
624+ /// Extension to make `CallbackEventHandler` a `CommandEventHandler` when the event type is a `CommandEvent`.
625+ extension CallbackEventHandler : CommandEventHandler where EventType == CommandEvent {
626+ fileprivate func handleCommandEvent( _ event: CommandEvent ) {
627+ self . handlerFunc ( event)
628+ }
629+ }
630+
631+ /// Event handler that stores a weak reference to the underlying handler.
632+ private class WeakEventHandler < T: AnyObject > {
633+ private weak var handler : T ?
634+
635+ fileprivate init ( referencing handler: T ) {
636+ self . handler = handler
637+ }
638+ }
639+
640+ /// Extension to make `WeakEventHandler` a `CommandEventHandler` when the referenced handler is a `CommandEventHandler`.
641+ extension WeakEventHandler : CommandEventHandler where T: CommandEventHandler {
642+ fileprivate func handleCommandEvent( _ event: CommandEvent ) {
643+ self . handler? . handleCommandEvent ( event)
644+ }
645+ }
646+
647+ /// Extension to make `WeakEventHandler` an `SDAMEventHandler` when the referenced handler is an `SDAMEventHandler`.
648+ extension WeakEventHandler : SDAMEventHandler where T: SDAMEventHandler {
649+ fileprivate func handleSDAMEvent( _ event: SDAMEvent ) {
650+ self . handler? . handleSDAMEvent ( event)
651+ }
652+ }
0 commit comments