@@ -36,11 +36,26 @@ final private[openfeature] class FeatureFlagsLive(
3636 // Read provider name dynamically so events after a provider swap use the new name
3737 def currentMetadata (runtime : Runtime [Any ]): ProviderMetadata = {
3838 val name = Unsafe .unsafe { implicit u =>
39- runtime.unsafe.run(providerNameRef.get).getOrThrowFiberFailure()
39+ runtime.unsafe
40+ .run(
41+ providerNameRef.get.catchAllCause(c =>
42+ ZIO .logErrorCause(" event bridge: providerNameRef.get" , c).as(" unknown" )
43+ )
44+ )
45+ .getOrElse(_ => " unknown" )
4046 }
4147 ProviderMetadata (name)
4248 }
4349
50+ // Run an event-publish effect from a Java SDK thread. Failures are logged via the ZIO logger
51+ // rather than thrown, so the Java SDK's event dispatch thread is never killed by a defect.
52+ def runHandler (runtime : Runtime [Any ], label : String )(effect : UIO [Unit ]): Unit =
53+ Unsafe .unsafe { implicit u =>
54+ runtime.unsafe
55+ .run(effect.catchAllCause(c => ZIO .logErrorCause(s " event bridge: $label" , c)))
56+ .getOrElse(_ => ())
57+ }
58+
4459 ZIO .runtime[Any ].flatMap { runtime =>
4560 def extractEventMetadata (details : EventDetails ): FlagMetadata =
4661 try {
@@ -49,57 +64,47 @@ final private[openfeature] class FeatureFlagsLive(
4964 else convertImmutableMetadata(javaMeta)
5065 } catch { case _ : Exception => FlagMetadata .empty }
5166
52- val readyHandler : java.util.function.Consumer [EventDetails ] = details =>
53- Unsafe .unsafe { implicit u =>
54- val em = extractEventMetadata(details)
55- runtime.unsafe
56- .run(
57- state.statusRef.set(ProviderStatus .Ready ) *>
58- state.eventHub.publish(ProviderEvent .Ready (currentMetadata(runtime), em))
59- )
60- .getOrThrowFiberFailure()
61- onReady.foreach(_.countDown())
62- }
67+ val readyHandler : java.util.function.Consumer [EventDetails ] = details => {
68+ val em = extractEventMetadata(details)
69+ runHandler(runtime, " PROVIDER_READY" )(
70+ state.statusRef.set(ProviderStatus .Ready ) *>
71+ state.eventHub.publish(ProviderEvent .Ready (currentMetadata(runtime), em)).unit
72+ )
73+ onReady.foreach(_.countDown())
74+ }
6375
64- val errorHandler : java.util.function.Consumer [EventDetails ] = details =>
65- Unsafe .unsafe { implicit u =>
66- val error = new RuntimeException (Option (details.getMessage).getOrElse(" Provider error" ))
67- val errorCode = Option (details.getErrorCode).map(ErrorCodeConverter .fromJava)
68- val em = extractEventMetadata(details)
69- runtime.unsafe
70- .run(
71- state.statusRef.set(ProviderStatus .Error ) *>
72- state.eventHub.publish(
73- ProviderEvent .Error (error, currentMetadata(runtime), errorCode, Option (details.getMessage), em)
74- )
75- )
76- .getOrThrowFiberFailure()
77- }
76+ val errorHandler : java.util.function.Consumer [EventDetails ] = details => {
77+ val error = new RuntimeException (Option (details.getMessage).getOrElse(" Provider error" ))
78+ val errorCode = Option (details.getErrorCode).map(ErrorCodeConverter .fromJava)
79+ val em = extractEventMetadata(details)
80+ runHandler(runtime, " PROVIDER_ERROR" )(
81+ state.statusRef.set(ProviderStatus .Error ) *>
82+ state.eventHub
83+ .publish(ProviderEvent .Error (error, currentMetadata(runtime), errorCode, Option (details.getMessage), em))
84+ .unit
85+ )
86+ }
7887
79- val staleHandler : java.util.function.Consumer [EventDetails ] = details =>
80- Unsafe .unsafe { implicit u =>
81- val reason = Option (details.getMessage).getOrElse(" Provider stale" )
82- val em = extractEventMetadata(details)
83- runtime.unsafe
84- .run(
85- state.statusRef.set(ProviderStatus .Stale ) *>
86- state.eventHub.publish(ProviderEvent .Stale (reason, currentMetadata(runtime), em))
87- )
88- .getOrThrowFiberFailure()
89- }
88+ val staleHandler : java.util.function.Consumer [EventDetails ] = details => {
89+ val reason = Option (details.getMessage).getOrElse(" Provider stale" )
90+ val em = extractEventMetadata(details)
91+ runHandler(runtime, " PROVIDER_STALE" )(
92+ state.statusRef.set(ProviderStatus .Stale ) *>
93+ state.eventHub.publish(ProviderEvent .Stale (reason, currentMetadata(runtime), em)).unit
94+ )
95+ }
9096
91- val configHandler : java.util.function.Consumer [EventDetails ] = details =>
92- Unsafe .unsafe { implicit u =>
93- val flags = Option (details.getFlagsChanged)
94- .map(_.asScala.toSet)
95- .getOrElse(Set .empty[String ])
96- val em = extractEventMetadata(details)
97- runtime.unsafe
98- .run(
99- state.eventHub.publish(ProviderEvent .ConfigurationChanged (flags, currentMetadata(runtime), em))
100- )
101- .getOrThrowFiberFailure()
102- }
97+ val configHandler : java.util.function.Consumer [EventDetails ] = details => {
98+ val flags = Option (details.getFlagsChanged)
99+ .map(_.asScala.toSet)
100+ .getOrElse(Set .empty[String ])
101+ val em = extractEventMetadata(details)
102+ runHandler(runtime, " PROVIDER_CONFIGURATION_CHANGED" )(
103+ state.eventHub
104+ .publish(ProviderEvent .ConfigurationChanged (flags, currentMetadata(runtime), em))
105+ .unit
106+ )
107+ }
103108
104109 for {
105110 _ <- ZIO .succeed {
@@ -790,8 +795,12 @@ final private[openfeature] class FeatureFlagsLive(
790795 ZIO .attempt {
791796 val jCtx = toJavaHookContext(ctx)
792797 val jHints = hints.values.map { case (k, v) => k -> v.asInstanceOf [Object ] }.asJava
793- val ex = err.cause.getOrElse(new RuntimeException (err.message))
794- hook.error(jCtx, ex.asInstanceOf [Exception ], jHints)
798+ val ex : Exception = err.cause match {
799+ case Some (e : Exception ) => e
800+ case Some (t) => new RuntimeException (t.getMessage, t)
801+ case None => new RuntimeException (err.message)
802+ }
803+ hook.error(jCtx, ex, jHints)
795804 }.ignore
796805
797806 override def finallyAfter (
0 commit comments