/** Ċ Ġ* ĠCopyright Ġ( c ) Ġ 2 0 2 1 ĠQ I WI Ċ Ġ* Ċ Ġ* ĠPermission Ġis Ġhereby Ġgranted , Ġfree Ġof Ġcharge , Ġto Ġany Ġperson Ġobtaining Ġa Ġcopy Ċ Ġ* Ġof Ġthis Ġsoftware Ġand Ġassociated Ġdocumentation Ġfiles Ġ( the Ġ" Software "), Ġto Ġdeal Ċ Ġ* Ġin Ġthe ĠSoftware Ġwithout Ġrestriction , Ġincluding Ġwithout Ġlimitation Ġthe Ġrights Ċ Ġ* Ġto Ġuse , Ġcopy , Ġmodify , Ġmerge , Ġpublish , Ġdistribute , Ġsublicense , Ġand / or Ġsell Ċ Ġ* Ġcopies Ġof Ġthe ĠSoftware , Ġand Ġto Ġpermit Ġpersons Ġto Ġwhom Ġthe ĠSoftware Ġis Ċ Ġ* Ġfurnished Ġto Ġdo Ġso , Ġsubject Ġto Ġthe Ġfollowing Ġconditions : Ċ Ġ* Ċ Ġ* ĠThe Ġabove Ġcopyright Ġnotice Ġand Ġthis Ġpermission Ġnotice Ġshall Ġbe Ġincluded Ġin Ġall Ċ Ġ* Ġcopies Ġor Ġsubstantial Ġportions Ġof Ġthe ĠSoftware . Ċ Ġ* Ċ Ġ* ĠTHE ĠSOFTWARE ĠIS ĠPROVIDED Ġ" AS ĠIS ", ĠWITHOUT ĠWARRANTY ĠOF ĠANY ĠKIND , ĠEXPRESS ĠOR Ċ Ġ* ĠIMPLIED , ĠINCLUDING ĠBUT ĠNOT ĠLIMITED ĠTO ĠTHE ĠWARRANTIES ĠOF ĠMERCHANTABILITY , Ċ Ġ* ĠFITNESS ĠFOR ĠA ĠPARTICULAR ĠPURPOSE ĠAND ĠNONINFRINGEMENT . ĠIN ĠNO ĠEVENT ĠSHALL ĠTHE Ċ Ġ* ĠAUTHORS ĠOR ĠCOPYRIGHT ĠHOLDERS ĠBE ĠLIABLE ĠFOR ĠANY ĠCLAIM , ĠDAMAGES ĠOR ĠOTHER Ċ Ġ* ĠLIABILITY , ĠWHETHER ĠIN ĠAN ĠACTION ĠOF ĠCONTRACT , ĠTORT ĠOR ĠOTHERWISE , ĠARISING ĠFROM , Ċ Ġ* ĠOUT ĠOF ĠOR ĠIN ĠCONNECTION ĠWITH ĠTHE ĠSOFTWARE ĠOR ĠTHE ĠUSE ĠOR ĠOTHER ĠDEALINGS ĠIN ĠTHE Ċ Ġ* ĠSOFTWARE . Ċ Ġ*/ Ċ package Ġcom . qi wi . feature toggle Ċ Ċ import Ġcom . qi wi . feature toggle . converter . Feature Flag Converter Ċ import Ġcom . qi wi . feature toggle . creator . Feature Creator Ċ import Ġcom . qi wi . feature toggle . factory . Feature Factory Ċ import Ġcom . qi wi . feature toggle . flag . Simple Feature Flag Ċ import Ġcom . qi wi . feature toggle . model . Feature Flags Container Ċ import Ġcom . qi wi . feature toggle . model . Feature Flags Source Type Ċ import Ġcom . qi wi . feature toggle . registry . Feature Flag Registry Ċ import Ġcom . qi wi . feature toggle . repository . Feature Repository Ċ import Ġcom . qi wi . feature toggle . storage . Actual Flags Storage Ċ import Ġcom . qi wi . feature toggle . util . Logger Ċ import Ġkotlinx . coroutines .* Ċ import Ġkotlinx . coroutines . channels . Channel Ċ import Ġkotlinx . coroutines . channels . Channel . Factory . BUFFER ED Ċ import Ġkotlinx . coroutines . channels . Channel . Factory . CON FL ATED Ċ import Ġkotlinx . coroutines . flow .* Ċ Ċ /** Ċ Ġ* ĠDefault Ġimplementation Ġof Ġ[ Feature Manager ]. Ċ Ġ*/ Ċ internal Ġclass ĠReal Feature Manager ( ĊĠĠĠ Ġprivate Ġval Ġflag Registry : ĠFeature Flag Registry , ĊĠĠĠ Ġprivate Ġval Ġrepository : ĠFeature Repository , ĊĠĠĠ Ġprivate Ġval Ġcreator : ĠFeature Creator , ĊĠĠĠ Ġprivate Ġval Ġstorage : ĠActual Flags Storage , ĊĠĠĠ Ġprivate Ġval Ġconverter : ĠFeature Flag Converter , ĊĠĠĠ Ġprivate Ġval Ġlogger : ĠLogger , ĊĠĠĠ Ġprivate Ġval Ġdispatcher : ĠCor outine Dispatcher Ġ= ĠDispatch ers . IO Ċ ) Ġ: ĠFeature Manager Ġ{ ĊĊĠĠĠ Ġprivate Ġval Ġcoroutine Scope : ĠCor outine Scope Ġ= ĠCor outine Scope ( Supervisor Job () Ġ+ Ġdispatcher ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ+ ĠCor outine ExceptionHandler Ġ{ Ġ_, Ġthrowable Ġ-> Ġlog Exception Inside Coroutine Scope ( throwable ) Ġ}) ĊĠĠĠ Ġprivate Ġval Ġflags Sources State Ġ= ĠMutable State Flow ( Flags Sources Container ()) ĊĠĠĠ Ġprivate Ġvar Ġfetch Flags Job : ĠJob ? Ġ= Ġnull ĊĠĠĠ Ġprivate Ġval Ġsave Events Ġ= ĠChannel < Map < String , ĠAny >>( capacity Ġ= ĠCON FL ATED ) ĊĠĠĠ Ġprivate Ġval Ġlog Events Ġ= ĠChannel < Feature Flags Container >( capacity Ġ= ĠBUFFER ED ) ĊĊĠĠĠ Ġinit Ġ{ ĊĠĠĠĠĠĠĠ Ġstart Save Events Processing () ĊĠĠĠĠĠĠĠ Ġstart Log Events Processing () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Suppress (" UN CHECK ED _ CAST ") ĊĠĠĠ Ġoverride Ġfun Ġ< Flag > Ġget Feature Flag ( flag Class : ĠClass < Flag >): ĠFlag ? Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġflag Key Ġ= Ġflag Registry . get Feature Keys Map ()[ flag Class ] ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ?: Ġthrow ĠIllegalStateException (" Feature Ġflag Ġ$ flag Class Ġnot Ġfound Ġin Ġregistry ") ĊĠĠĠĠĠĠĠ Ġreturn Ġstorage . get Flag ( flag Key ) Ġas ĠFlag ? ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġ< Feature > Ġget Feature ( feature Class : ĠClass < Feature >): ĠFeature Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġcreator . create Feature ( feature Class ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġ< Feature , ĠFlag > Ġget Feature ( feature Class : ĠClass < Feature >, Ġfactory : ĠFeature Factory < Feature , ĠFlag >): ĠFeature Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġcreator . create Feature ( feature Class , Ġfactory ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġsuspend Ġfun Ġ< Feature > Ġawait Feature ( feature Class : ĠClass < Feature >, ĠdataSource Key : ĠString ): ĠFeature Ġ{ ĊĠĠĠĠĠĠĠ Ġflags Sources State . first Ġ{ Ġcontainer Ġ-> Ġcontainer . dataSource Keys . contains ( dataSource Key ) Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġcreator . create Feature ( feature Class ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġsuspend Ġfun Ġ< Feature > Ġawait Feature ( feature Class : ĠClass < Feature >, Ġsource Type : ĠFeature Flags Source Type ): ĠFeature Ġ{ ĊĠĠĠĠĠĠĠ Ġflags Sources State . first Ġ{ Ġcontainer Ġ-> Ġcontainer . source Types . contains ( source Type ) Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġcreator . create Feature ( feature Class ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġsuspend Ġfun Ġawait Full Loading () Ġ{ ĊĠĠĠĠĠĠĠ Ġflags Sources State . first Ġ{ Ġcontainer Ġ-> Ġcontainer . dataSource Keys Ġ== Ġrepository . getDataSource Keys () Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġfetch Flags () Ġ= Ġsynchronized ( this ) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( fetch Flags Job Ġ== Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġfetch Flags Job Ġ= Ġrepository ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. get Flags () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. flow On ( dispatcher ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. on Each Ġ{ Ġcontainer Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġprocess Flags ( container ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. launch In ( coroutine Scope ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġreset All Flags () Ġ= Ġsynchronized ( this ) Ġ{ ĊĠĠĠĠĠĠĠ Ġfetch Flags Job ?. cancel () ĊĠĠĠĠĠĠĠ Ġfetch Flags Job Ġ= Ġnull ĊĠĠĠĠĠĠĠ Ġflags Sources State . value Ġ= ĠFlags Sources Container () ĊĠĠĠĠĠĠĠ Ġstorage . reset All Flags () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġshutdown () Ġ{ ĊĠĠĠĠĠĠĠ Ġreset All Flags () ĊĠĠĠĠĠĠĠ Ġcoroutine Scope . cancel () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġreset Used Flags () Ġ{ ĊĠĠĠĠĠĠĠ Ġstorage . reset Used Flags () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġavailable Data Sources (): ĠSet < String > Ġ= Ġflags Sources State . value . dataSource Keys . to Sorted Set () ĊĊĠĠĠ Ġoverride Ġfun ĠgetAll Known Flags Keys (): ĠSet < String > Ġ= Ġflag Registry . get Feature Flags Map (). keys . to Sorted Set () ĊĊĠĠĠ Ġoverride Ġfun ĠgetAll Known Simple Flags Keys (): ĠSet < String > Ġ= Ġflag Registry . get Feature Flags Map () ĊĠĠĠĠĠĠĠ Ġ. filter Ġ{ Ġit . value . super class Ġ== ĠSimple Feature Flag :: class . java Ġ}. map Ġ{ Ġit . key Ġ} ĊĠĠĠĠĠĠĠ Ġ. to Sorted Set () ĊĊĠĠĠ Ġprivate Ġsuspend Ġfun Ġprocess Flags ( container : ĠFeature Flags Container ) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( container . flags . isNotEmpty ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstorage . update Flags ( container . flags , Ġfalse ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġflags Sources State . value Ġ= ĠFlags Sources Container ( container . source Types , Ġcontainer . dataSource Keys ) ĊĠĠĠĠĠĠĠ Ġlog Events . send ( container ) ĊĠĠĠĠĠĠĠ Ġif Ġ( container . source Types . contains ( Feature Flags Source Type . REMOTE ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ&& Ġcontainer . source Types . contains ( Feature Flags Source Type . CACHE ) ĊĠĠĠĠĠĠĠ Ġ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsave Events . send ( container . flags ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġstart Save Events Processing () Ġ{ ĊĠĠĠĠĠĠĠ Ġsave Events ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. consume As Flow () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. on Each Ġ{ Ġflags Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrun Catch ing Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrepository . save Flags ( flags ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ}. on Success Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . log ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlevel Ġ= ĠLogger . Level . INFO , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtag Ġ= ĠTAG , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmessage Ġ= Ġ" flags Ġsaved Ġto Ġcache " ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ}. on Failure Ġ{ Ġthrowable Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . log ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlevel Ġ= ĠLogger . Level . ERROR , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtag Ġ= ĠTAG , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmessage Ġ= Ġ" exception Ġwhile Ġsaving Ġflags Ġto Ġcache ", ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġthrowable Ġ= Ġthrowable ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. flow On ( dispatcher ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. launch In ( coroutine Scope ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġstart Log Events Processing () Ġ{ ĊĠĠĠĠĠĠĠ Ġlog Events ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. consume As Flow () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. on Each Ġ{ Ġcontainer Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrun Catch ing Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . log ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlevel Ġ= ĠLogger . Level . INFO , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtag Ġ= ĠTAG , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmessage Ġ= Ġ" flags Ġloaded Ġfrom Ġdata Ġsources : Ġ[ ${ container . dataSource Keys . join ToString ()} ] Ġ; Ġ" Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ" actual Ġflags : Ġ${ converter . write ( container . flags )}" ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. flow On ( dispatcher ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. launch In ( coroutine Scope ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġlog Exception Inside Coroutine Scope ( throwable : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠ Ġlogger . log ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlevel Ġ= ĠLogger . Level . ERROR , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtag Ġ= ĠTAG , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmessage Ġ= Ġ" Exception Ġinside ĠCor outine Scope ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrowable Ġ= Ġthrowable ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġdata Ġclass ĠFlags Sources Container ( ĊĠĠĠĠĠĠĠ Ġval Ġsource Types : ĠSet < Feature Flags Source Type > Ġ= Ġempty Set (), ĊĠĠĠĠĠĠĠ Ġval ĠdataSource Keys : ĠSet < String > Ġ= Ġempty Set () ĊĠĠĠ Ġ) ĊĊĠĠĠ Ġcompanion Ġobject Ġ{ ĊĠĠĠĠĠĠĠ Ġinternal Ġconst Ġval ĠTAG Ġ= Ġ" Feature Manager " ĊĠĠĠ Ġ} Ċ Ċ }