@@ -31,6 +31,32 @@ public EventEmitter(PNConfiguration configuration, List<SubscribeCallback> liste
3131 channelListenersMap = new Dictionary < string , List < SubscribeCallback > > ( ) ;
3232 }
3333
34+ /// <summary>
35+ /// Safely invokes a listener callback with exception handling to prevent listener exceptions from affecting SDK operations.
36+ /// </summary>
37+ /// <param name="listenerAction">The listener callback action to invoke.</param>
38+ /// <param name="eventType">The type of event being processed (for logging purposes).</param>
39+ /// <remarks>
40+ /// This method ensures that exceptions thrown by user-provided listener callbacks do not crash the SDK
41+ /// or prevent other listeners from receiving events. All exceptions are logged at Warning level.
42+ /// </remarks>
43+ private void SafeInvokeListener ( Action listenerAction , string eventType )
44+ {
45+ if ( listenerAction == null )
46+ {
47+ return ;
48+ }
49+
50+ try
51+ {
52+ listenerAction . Invoke ( ) ;
53+ }
54+ catch ( Exception ex )
55+ {
56+ configuration ? . Logger ? . Warn ( $ "Exception in listener callback execution for { eventType } : { ex . Message } ") ;
57+ }
58+ }
59+
3460 private TimetokenMetadata GetTimetokenMetadata ( object t )
3561 {
3662 Dictionary < string , object > ttOriginMetaData = jsonLibrary . ConvertToDictionaryObject ( t ) ;
@@ -214,22 +240,22 @@ public void EmitEvent<T>(object e)
214240 } ;
215241 foreach ( var listener in listeners )
216242 {
217- listener ? . Signal ( instance , signalMessage ) ;
243+ SafeInvokeListener ( ( ) => listener ? . Signal ( instance , signalMessage ) , "Signal" ) ;
218244 }
219245
220246 if ( ! string . IsNullOrEmpty ( signalMessage . Channel ) && channelListenersMap . ContainsKey ( signalMessage . Channel ) )
221247 {
222248 foreach ( var l in channelListenersMap [ signalMessage . Channel ] )
223249 {
224- l ? . Signal ( instance , signalMessage ) ;
250+ SafeInvokeListener ( ( ) => l ? . Signal ( instance , signalMessage ) , "Signal" ) ;
225251 }
226252 }
227253
228254 if ( ! string . IsNullOrEmpty ( signalMessage . Subscription ) && channelGroupListenersMap . ContainsKey ( signalMessage . Subscription ) )
229255 {
230256 foreach ( var l in channelGroupListenersMap [ signalMessage . Subscription ] )
231257 {
232- l ? . Signal ( instance , signalMessage ) ;
258+ SafeInvokeListener ( ( ) => l ? . Signal ( instance , signalMessage ) , "Signal" ) ;
233259 }
234260 }
235261 }
@@ -244,22 +270,22 @@ public void EmitEvent<T>(object e)
244270 {
245271 foreach ( var listener in listeners )
246272 {
247- listener ? . ObjectEvent ( instance , objectApiEvent ) ;
273+ SafeInvokeListener ( ( ) => listener ? . ObjectEvent ( instance , objectApiEvent ) , "ObjectEvent" ) ;
248274 }
249275
250276 if ( ! string . IsNullOrEmpty ( objectApiEvent . Channel ) && channelListenersMap . ContainsKey ( objectApiEvent . Channel ) )
251277 {
252278 foreach ( var l in channelListenersMap [ objectApiEvent . Channel ] )
253279 {
254- l ? . ObjectEvent ( instance , objectApiEvent ) ;
280+ SafeInvokeListener ( ( ) => l ? . ObjectEvent ( instance , objectApiEvent ) , "ObjectEvent" ) ;
255281 }
256282 }
257283
258284 if ( ! string . IsNullOrEmpty ( objectApiEvent . Subscription ) && channelGroupListenersMap . ContainsKey ( objectApiEvent . Subscription ) )
259285 {
260286 foreach ( var l in channelGroupListenersMap [ objectApiEvent . Subscription ] )
261287 {
262- l ? . ObjectEvent ( instance , objectApiEvent ) ;
288+ SafeInvokeListener ( ( ) => l ? . ObjectEvent ( instance , objectApiEvent ) , "ObjectEvent" ) ;
263289 }
264290 }
265291 }
@@ -274,22 +300,22 @@ public void EmitEvent<T>(object e)
274300 {
275301 foreach ( var listener in listeners )
276302 {
277- listener ? . MessageAction ( instance , messageActionEvent ) ;
303+ SafeInvokeListener ( ( ) => listener ? . MessageAction ( instance , messageActionEvent ) , "MessageAction" ) ;
278304 }
279305
280306 if ( ! string . IsNullOrEmpty ( messageActionEvent . Channel ) && channelListenersMap . ContainsKey ( messageActionEvent . Channel ) )
281307 {
282308 foreach ( var l in channelListenersMap [ messageActionEvent . Channel ] )
283309 {
284- l ? . MessageAction ( instance , messageActionEvent ) ;
310+ SafeInvokeListener ( ( ) => l ? . MessageAction ( instance , messageActionEvent ) , "MessageAction" ) ;
285311 }
286312 }
287313
288314 if ( ! string . IsNullOrEmpty ( messageActionEvent . Subscription ) && channelGroupListenersMap . ContainsKey ( messageActionEvent . Subscription ) )
289315 {
290316 foreach ( var l in channelGroupListenersMap [ messageActionEvent . Subscription ] )
291317 {
292- l ? . MessageAction ( instance , messageActionEvent ) ;
318+ SafeInvokeListener ( ( ) => l ? . MessageAction ( instance , messageActionEvent ) , "MessageAction" ) ;
293319 }
294320 }
295321 }
@@ -340,22 +366,22 @@ public void EmitEvent<T>(object e)
340366
341367 foreach ( var listener in listeners )
342368 {
343- listener ? . File ( instance , fileMessage ) ;
369+ SafeInvokeListener ( ( ) => listener ? . File ( instance , fileMessage ) , "File" ) ;
344370 }
345371
346372 if ( ! string . IsNullOrEmpty ( fileMessage . Channel ) && channelListenersMap . ContainsKey ( fileMessage . Channel ) )
347373 {
348374 foreach ( var l in channelListenersMap [ fileMessage . Channel ] )
349375 {
350- l ? . File ( instance , fileMessage ) ;
376+ SafeInvokeListener ( ( ) => l ? . File ( instance , fileMessage ) , "File" ) ;
351377 }
352378 }
353379
354380 if ( ! string . IsNullOrEmpty ( fileMessage . Subscription ) && channelGroupListenersMap . ContainsKey ( fileMessage . Subscription ) )
355381 {
356382 foreach ( var l in channelGroupListenersMap [ fileMessage . Subscription ] )
357383 {
358- l ? . File ( instance , fileMessage ) ;
384+ SafeInvokeListener ( ( ) => l ? . File ( instance , fileMessage ) , "File" ) ;
359385 }
360386 }
361387 }
@@ -372,22 +398,22 @@ public void EmitEvent<T>(object e)
372398 {
373399 foreach ( var listener in listeners )
374400 {
375- listener ? . Presence ( instance , presenceEvent ) ;
401+ SafeInvokeListener ( ( ) => listener ? . Presence ( instance , presenceEvent ) , "Presence" ) ;
376402 }
377403
378404 if ( ! string . IsNullOrEmpty ( presenceEvent . Channel ) && channelListenersMap . ContainsKey ( presenceEvent . Channel ) )
379405 {
380406 foreach ( var l in channelListenersMap [ presenceEvent . Channel ] )
381407 {
382- l ? . Presence ( instance , presenceEvent ) ;
408+ SafeInvokeListener ( ( ) => l ? . Presence ( instance , presenceEvent ) , "Presence" ) ;
383409 }
384410 }
385411
386412 if ( ! string . IsNullOrEmpty ( presenceEvent . Subscription ) && channelGroupListenersMap . ContainsKey ( presenceEvent . Subscription ) )
387413 {
388414 foreach ( var l in channelGroupListenersMap [ presenceEvent . Subscription ] )
389415 {
390- l ? . Presence ( instance , presenceEvent ) ;
416+ SafeInvokeListener ( ( ) => l ? . Presence ( instance , presenceEvent ) , "Presence" ) ;
391417 }
392418 }
393419 }
@@ -397,37 +423,29 @@ public void EmitEvent<T>(object e)
397423 jsonFields . Add ( "customMessageType" , eventData . CustomMessageType ) ;
398424 ResponseBuilder responseBuilder = new ResponseBuilder ( configuration , jsonLibrary ) ;
399425 PNMessageResult < T > userMessage = responseBuilder . GetEventResultObject < PNMessageResult < T > > ( jsonFields ) ;
400- try
426+ if ( userMessage != null )
401427 {
402- if ( userMessage != null )
428+ foreach ( var listener in listeners )
403429 {
404- foreach ( var listener in listeners )
405- {
406- listener ? . Message ( instance , userMessage ) ;
407- }
430+ SafeInvokeListener ( ( ) => listener ? . Message ( instance , userMessage ) , "Message" ) ;
431+ }
408432
409- if ( ! string . IsNullOrEmpty ( userMessage . Channel ) && channelListenersMap . ContainsKey ( userMessage . Channel ) )
433+ if ( ! string . IsNullOrEmpty ( userMessage . Channel ) && channelListenersMap . ContainsKey ( userMessage . Channel ) )
434+ {
435+ foreach ( var l in channelListenersMap [ userMessage . Channel ] )
410436 {
411- foreach ( var l in channelListenersMap [ userMessage . Channel ] )
412- {
413- l ? . Message ( instance , userMessage ) ;
414- }
437+ SafeInvokeListener ( ( ) => l ? . Message ( instance , userMessage ) , "Message" ) ;
415438 }
439+ }
416440
417- if ( ! string . IsNullOrEmpty ( userMessage . Subscription ) && channelGroupListenersMap . ContainsKey ( userMessage . Subscription ) )
441+ if ( ! string . IsNullOrEmpty ( userMessage . Subscription ) && channelGroupListenersMap . ContainsKey ( userMessage . Subscription ) )
442+ {
443+ foreach ( var l in channelGroupListenersMap [ userMessage . Subscription ] )
418444 {
419- foreach ( var l in channelGroupListenersMap [ userMessage . Subscription ] )
420- {
421- l ? . Message ( instance , userMessage ) ;
422- }
445+ SafeInvokeListener ( ( ) => l ? . Message ( instance , userMessage ) , "Message" ) ;
423446 }
424447 }
425448 }
426- catch ( Exception ex )
427- {
428- configuration . Logger ? . Error (
429- $ "Listener call back execution encounters error: { ex . Message } \n { ex ? . StackTrace } ") ;
430- }
431449 }
432450
433451 break ;
0 commit comments