package Ġnet . blue wizard hat . common . cache Ċ Ċ import Ġcom . fasterxml . jackson . core . type . Type Reference Ċ import Ġcom . fasterxml . jackson . databind . ObjectMapper Ċ import Ġmu . Kotlin Logging Ċ import Ġjava . time . Duration Ċ import Ġjava . time . Offset DateTime Ċ import Ġjava . time . temporal . Ch rono Unit Ċ import Ġjava . util . concurrent . Executor Ċ import Ġjava . util . function . Supplier Ċ import Ġjavax . servlet . http . HttpServletResponse Ċ Ċ /** Ċ Ġ* ĠA Ġsimple Ġredis Ġcache Ġimplementation . Ċ Ġ* Ċ Ġ* Ġ* ĠIf Ġa Ġvalue Ġis Ġnot Ġfound Ġin Ġthe Ġcache Ġthe Ġsupplier Ġis Ġcalled Ġto Ġfetch Ġthe Ġvalue , Ġthe Ġvalue Ġwill Ġbe Ġwritten Ġto Ġcache Ċ Ġ* Ġasynchronously Ġand Ġthe Ġvalue Ġis Ġreturned . Ċ Ġ* Ġ* ĠIf Ġa Ġvalue Ġis Ġfound Ġin Ġthe Ġcache , Ġand Ġit Ġis Ġnewer Ġthan Ġrefresh After , Ġthe Ġcached Ġvalue Ġwill Ġbe Ġreturned . Ċ Ġ* Ġ* ĠIf Ġa Ġvalue Ġis Ġfound Ġin Ġthe Ġcache , Ġbut Ġit Ġis Ġolder Ġthan Ġrefresh After , Ġthe Ġcached Ġvalue Ġwill Ġbe Ġreturned Ġand Ġthe Ċ Ġ* Ġsupplier Ġwill Ġbe Ġcalled Ġasynchronously Ġto Ġrefresh Ġthe Ġvalue Ġin Ġthe Ġcache . Ċ Ġ* Ċ Ġ* ĠIf Ġrefresh After Ġis Ġlonger Ġthan Ġexpire After Ġor Ġrefresh After Ġis Ġnull Ġthe Ġobject Ġwill Ġsimply Ġexpire Ġwithout Ġever Ġbeing Ċ Ġ* Ġref reshed . Ċ Ġ* Ċ Ġ* ĠNote Ġif Ġthe Ġvalue Ġis Ġnot Ġrequested Ġbefore Ġit Ġexpires Ġit Ġwill Ġalso Ġnot Ġbe Ġref reshed Ġasynchronously . ĠTo Ġkeep Ġa Ġvalue Ċ Ġ* Ġcached Ġit Ġwill Ġneed Ġto Ġbe Ġrequested Ġperiodically . Ċ Ġ*/ Ċ sealed Ġclass ĠSimple Redis Cache ( ĊĠĠĠ Ġprivate Ġval Ġredis Adapter : ĠRedis Adapter , ĊĠĠĠ Ġprivate Ġval ĠobjectMapper : ĠObjectMapper , ĊĠĠĠ Ġprivate Ġval Ġpool : ĠString , ĊĠĠĠ Ġprivate Ġval Ġlock Duration : ĠDuration , ĊĠĠĠ Ġprivate Ġval Ġexecutor : ĠExecutor Ċ ) Ġ{ ĊĠĠĠ Ġprivate Ġval Ġlog Ġ= ĠKotlin Logging . logger Ġ{} ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠMain Ġcaching Ġmechanism . ĊĠĠĠĠ Ġ* ĊĠĠĠĠ Ġ* Ġ@ param Ġkey Ġcache Ġkey . ĊĠĠĠĠ Ġ* Ġ@ param Ġexpire After Ġafter Ġhow Ġlong Ġthe Ġobject Ġshould Ġexpire Ġfrom Ġthe Ġcache . ĊĠĠĠĠ Ġ* Ġ@ param Ġrefresh After Ġafter Ġhow Ġlong Ġthe Ġobject Ġshould Ġbe Ġref reshed Ġin Ġthe Ġcache . ĊĠĠĠĠ Ġ* Ġ@ param Ġsupplier Ġis Ġused Ġto Ġsupply Ġthe Ġvalue Ġto Ġreturn Ġif Ġnot Ġfound Ġin Ġcache . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġinline Ġfun Ġ< re ified ĠT > Ġcache ( key : ĠString , Ġexpire After : ĠDuration , Ġrefresh After : ĠDuration ? Ġ= Ġnull , Ġsupplier : ĠSupplier < T >): ĠT Ġ= ĊĠĠĠĠĠĠĠ Ġcached Value ( key , Ġexpire After , Ġrefresh After , Ġobject Ġ: ĠType Reference < Cached Value < T >>() Ġ{}, Ġsupplier ). value ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠAltern ate Ġcaching Ġmechanism Ġwhere Ġyou Ġget Ġthe Ġvalue Ġalong Ġwith Ġinfo Ġon Ġwhen Ġthe Ġvalue Ġwas Ġcached Ġand Ġthe ĊĠĠĠĠ Ġ* Ġexpire After Ġand Ġrefresh After Ġvalues . ĊĠĠĠĠ Ġ* ĊĠĠĠĠ Ġ* Ġ@ param Ġkey Ġcache Ġkey . ĊĠĠĠĠ Ġ* Ġ@ param Ġexpire After Ġafter Ġhow Ġlong Ġthe Ġobject Ġshould Ġexpire Ġfrom Ġthe Ġcache . ĠIf Ġexpire After Ġis Ġless Ġthan Ġ 1 Ġsecond ĊĠĠĠĠ Ġ* Ġthe Ġvalue Ġwill Ġnot Ġbe Ġcached . ĊĠĠĠĠ Ġ* Ġ@ param Ġrefresh After Ġafter Ġhow Ġlong Ġthe Ġobject Ġshould Ġbe Ġref reshed Ġin Ġthe Ġcache . ĊĠĠĠĠ Ġ* Ġ@ param Ġsupplier Ġis Ġused Ġto Ġsupply Ġthe Ġvalue Ġto Ġreturn Ġif Ġnot Ġfound Ġin Ġcache . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġinline Ġfun Ġ< re ified ĠT > Ġcached Value ( key : ĠString , Ġexpire After : ĠDuration , Ġrefresh After : ĠDuration ? Ġ= Ġnull , Ġsupplier : ĠSupplier < T >): ĠCached Value < T > Ġ= ĊĠĠĠĠĠĠĠ Ġcached Value ( key , Ġexpire After , Ġrefresh After , Ġobject Ġ: ĠType Reference < Cached Value < T >>() Ġ{}, Ġsupplier ) ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠMain Ġcaching Ġmechanism Ġfor Ġspecifying Ġexpire After Ġand Ġrefresh After Ġin Ġthe Ġsupplier . ĊĠĠĠĠ Ġ* ĊĠĠĠĠ Ġ* Ġ@ param Ġkey Ġcache Ġkey . ĊĠĠĠĠ Ġ* Ġ@ param Ġsupplier Ġis Ġused Ġto Ġsupply Ġthe Ġvalue Ġto Ġreturn Ġif Ġnot Ġfound Ġin Ġcache . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġinline Ġfun Ġ< re ified ĠT > Ġcache ( key : ĠString , Ġsupplier : ĠSupplier < Value To Cache < T >> ): ĠT Ġ= ĊĠĠĠĠĠĠĠ Ġcached Value ( key , Ġobject Ġ: ĠType Reference < Cached Value < T >>() Ġ{}, Ġsupplier ). value ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠAltern ate Ġcaching Ġmechanism Ġwhere Ġyou Ġget Ġthe Ġvalue Ġalong Ġwith Ġinfo Ġon Ġwhen Ġthe Ġvalue Ġwas Ġcached Ġand Ġthe ĊĠĠĠĠ Ġ* Ġexpire After Ġand Ġrefresh After Ġvalues . ĊĠĠĠĠ Ġ* ĊĠĠĠĠ Ġ* Ġ@ param Ġkey Ġcache Ġkey . ĊĠĠĠĠ Ġ* Ġ@ param Ġsupplier Ġis Ġused Ġto Ġsupply Ġthe Ġvalue Ġto Ġreturn Ġif Ġnot Ġfound Ġin Ġcache . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġinline Ġfun Ġ< re ified ĠT > Ġcached Value ( key : ĠString , Ġsupplier : ĠSupplier < Value To Cache < T >> ): ĠCached Value < T > Ġ= ĊĠĠĠĠĠĠĠ Ġcached Value ( key , Ġobject Ġ: ĠType Reference < Cached Value < T >>() Ġ{}, Ġsupplier ) ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠInvalid ates Ġkeys Ġin Ġthe Ġcache . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġfun Ġinvalidate ( var arg Ġkeys : ĠString ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġactual Keys Ġ= Ġkeys . map Ġ{ Ġ"$ pool :$ it " Ġ} ĊĠĠĠĠĠĠĠ Ġlog . debug Ġ{ Ġ" Invalid ating Ġ[' ${ actual Keys . join ToString (" ', Ġ' ")} '] " Ġ} ĊĠĠĠĠĠĠĠ Ġredis Adapter . unlink ( actual Keys ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠInvalid ates Ġall Ġkeys Ġin Ġa Ġpool . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġfun Ġinvalidate All () Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġkeys Ġ= Ġredis Adapter . keys ("$ pool :* ") ĊĠĠĠĠĠĠĠ Ġif Ġ( keys . isNotEmpty ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlog . debug Ġ{ Ġ" Invalid ating Ġ[' ${ keys . join ToString (" ', Ġ' ")} '] " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġredis Adapter . unlink ( keys ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠVis ible Ġonly Ġso Ġit Ġcan Ġbe Ġcalled Ġfrom Ġinline Ġfunction . ĊĠĠĠĠ Ġ* Ġ@ see Ġ# cache ( String , ĠDuration , ĠDuration ?, ĠSupplier ) ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġabstract Ġfun Ġ< T > Ġcached Value ( key : ĠString , Ġexpire After : ĠDuration , Ġrefresh After : ĠDuration ? Ġ= Ġnull , Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < T >): ĠCached Value < T > ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠVis ible Ġonly Ġso Ġit Ġcan Ġbe Ġcalled Ġfrom Ġinline Ġfunction . ĊĠĠĠĠ Ġ* Ġ@ see Ġ# cached Value ( String , ĠSupplier ) ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġabstract Ġfun Ġ< T > Ġcached Value ( key : ĠString , Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < Value To Cache < T >> ): ĠCached Value < T > ĊĊĠĠĠ Ġprotected Ġfun Ġ< T > Ġcache Internal ( key : ĠString , Ġexpire After : ĠDuration , Ġrefresh After : ĠDuration ?, Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < T >): ĠCached Value < T > Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġactual Key Ġ= Ġ"$ pool :$ key " ĊĠĠĠĠĠĠĠ Ġreturn Ġread From Cache ( actual Key , Ġrefresh After , Ġ{ ĠValue To Cache ( supplier . get (), Ġexpire After , Ġrefresh After ) Ġ}, Ġtype Ref ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ?: ĠwriteTo Cache Bg ( actual Key , ĠCached Value ( expire After , Ġrefresh After , Ġsupplier . get ())) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprotected Ġfun Ġ< T > Ġcache Internal ( key : ĠString , Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < Value To Cache < T >> ): ĠCached Value < T > Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġactual Key Ġ= Ġ"$ pool :$ key " ĊĠĠĠĠĠĠĠ Ġreturn Ġread From Cache ( actual Key , Ġnull , Ġsupplier , Ġtype Ref ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ?: ĠwriteTo Cache Bg ( actual Key , ĠCached Value ( supplier . get ())) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġ< T > Ġread From Cache ( key : ĠString , Ġrefresh After : ĠDuration ?, Ġsupplier : ĠSupplier < Value To Cache < T >>, Ġtype Ref : ĠType Reference < Cached Value < T >> ): ĠCached Value < T >? Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġserialized : ĠString ? Ġ= Ġredis Adapter . get ( key ) ĊĠĠĠĠĠĠĠ Ġif Ġ( serialized Ġ!= Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlog . debug Ġ{ Ġ"( Hit ) ĠRead Ġ'$ key ' Ġfrom Ġcache " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġcached Value Ġ= ĠobjectMapper . readValue ( serialized , Ġtype Ref ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġrefresh After Actual Ġ= Ġrefresh After Ġ?: Ġcached Value . refresh After ĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( refresh After Actual Ġ!= Ġnull Ġ&& ĠOffset DateTime . now (). is After ( cached Value . cache Time . plus ( refresh After Actual ))) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġqueue Update ( key , Ġrefresh After Actual , Ġsupplier , Ġtype Ref ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġcached Value ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġlog . debug Ġ{ Ġ"( Miss ) ĠKey Ġ'$ key ' Ġnot Ġfound Ġin Ġcache " Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġnull ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġ< T > Ġqueue Update ( key : ĠString , Ġrefresh After : ĠDuration , Ġsupplier : ĠSupplier < Value To Cache < T >>, Ġtype Ref : ĠType Reference < Cached Value < T >>) Ġ{ ĊĠĠĠĠĠĠĠ Ġlog . debug Ġ{ Ġ" Queue ing Ġ'$ key ' Ġfor Ġrefresh " Ġ} ĊĠĠĠĠĠĠĠ Ġexecutor . execute Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdo With Lock ("$ key . refresh . lock ") Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġcache Time Ġ= Ġredis Adapter . get ( key )?. let Ġ{ ĠobjectMapper . readValue ( it , Ġtype Ref ). cache Time Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( cache Time Ġ== Ġnull Ġ|| ĠOffset DateTime . now (). is After ( cache Time . plus ( refresh After ))) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġvalue To Cache Ġ= Ġsupplier . get () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠwriteTo Cache ( key , Ġvalue To Cache . expire After , ĠCached Value ( value To Cache )) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlog . error ( e ) Ġ{ Ġ" Error Ġwhile Ġupdating Ġcache Ġfor Ġ'$ key ': Ġ${ e . message }" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġ< T > ĠwriteTo Cache Bg ( key : ĠString , Ġvalue : ĠCached Value < T >): ĠCached Value < T > Ġ{ ĊĠĠĠĠĠĠĠ Ġexecutor . execute Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠwriteTo Cache ( key , Ġvalue . expire After , Ġvalue ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlog . error ( e ) Ġ{ Ġ" Error Ġwhile Ġupdating Ġcache Ġfor Ġ'$ key ': Ġ${ e . message }" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġvalue ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġ< T > ĠwriteTo Cache ( key : ĠString , Ġexpire After : ĠDuration , Ġcached Value : ĠCached Value < T >) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( expire After . seconds Ġ> Ġ 0 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdo With Lock ("$ key . lock ") Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlog . debug Ġ{ Ġ" Writing Ġ'$ key ' Ġto Ġcache , Ġexpires Ġafter Ġ$ expire After " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġredis Adapter . set ( key , ĠobjectMapper . writeValue AsString ( cached Value ), Ġexpire After ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlog . info Ġ{ Ġ" Skip Ġwriting Ġ'$ key ' Ġto Ġcache Ġas Ġexpire After Ġis Ġless Ġthan Ġ 1 Ġsecond Ġ($ expire After )" Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġdo With Lock ( lock : ĠString , Ġtask : ĠRunnable ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġlock Acquire d Ġ= Ġredis Adapter . set If Absent ( lock , Ġ" true ", Ġlock Duration ) Ġ?: Ġfalse ĊĠĠĠĠĠĠĠ Ġif Ġ( lock Acquire d ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlog . debug Ġ{ Ġ" Acquire d Ġlock Ġ'$ lock '" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtask . run () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġfinally Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlog . debug Ġ{ Ġ" Re leasing Ġlock Ġ'$ lock '" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġredis Adapter . unlink ( lock ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlog . warn Ġ{ Ġ" Could Ġnot Ġacquire Ġlock Ġ'$ lock ', Ġunable Ġto Ġupdate Ġcache " Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} Ċ } Ċ Ċ /** Ċ Ġ* ĠConcrete Ġimplementation Ġof ĠSimple Redis Cache Ġbecause Ġsealed Ġclasses Ġare Ġabstract . Ċ Ġ*/ Ċ class ĠSimple Redis Cache Basic ( ĊĠĠĠ Ġredis Adapter : ĠRedis Adapter , ĊĠĠĠ ĠobjectMapper : ĠObjectMapper , ĊĠĠĠ Ġpool : ĠString , ĊĠĠĠ Ġlock Duration : ĠDuration , ĊĠĠĠ Ġexecutor : ĠExecutor Ċ ) Ġ: ĠSimple Redis Cache ( redis Adapter , ĠobjectMapper , Ġpool , Ġlock Duration , Ġexecutor ) Ġ{ ĊĠĠĠ Ġoverride Ġfun Ġ< T > Ġcached Value ( key : ĠString , Ġexpire After : ĠDuration , Ġrefresh After : ĠDuration ?, Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < T >): ĠCached Value < T > Ġ= ĊĠĠĠĠĠĠĠ Ġcache Internal ( key , Ġexpire After , Ġrefresh After , Ġtype Ref , Ġsupplier ) ĊĊĠĠĠ Ġoverride Ġfun Ġ< T > Ġcached Value ( key : ĠString , Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < Value To Cache < T >> ): ĠCached Value < T > Ġ= ĊĠĠĠĠĠĠĠ Ġcache Internal ( key , Ġtype Ref , Ġsupplier ) Ċ } Ċ Ċ /** Ċ Ġ* ĠExtension Ġof ĠSimple Redis Cache Ġthat Ġprovides Ġthe Ġability Ġto Ġset Ġhttp Ġcache Ġheaders . Ċ Ġ*/ Ċ class ĠSimple Redis Cache Web ( ĊĠĠĠ Ġprivate Ġval Ġredis Adapter : ĠRedis Adapter , ĊĠĠĠ Ġprivate Ġval ĠobjectMapper : ĠObjectMapper , ĊĠĠĠ Ġprivate Ġval Ġpool : ĠString , ĊĠĠĠ Ġprivate Ġval Ġlock Duration : ĠDuration , ĊĠĠĠ Ġprivate Ġval Ġexecutor : ĠExecutor Ċ ) Ġ: ĠSimple Redis Cache ( redis Adapter , ĠobjectMapper , Ġpool , Ġlock Duration , Ġexecutor ) Ġ{ ĊĠĠĠ Ġoverride Ġfun Ġ< T > Ġcached Value ( key : ĠString , Ġexpire After : ĠDuration , Ġrefresh After : ĠDuration ?, Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < T >): ĠCached Value < T > Ġ= ĊĠĠĠĠĠĠĠ Ġcache Internal ( key , Ġexpire After , Ġrefresh After , Ġtype Ref , Ġsupplier ) ĊĊĠĠĠ Ġoverride Ġfun Ġ< T > Ġcached Value ( key : ĠString , Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < Value To Cache < T >> ): ĠCached Value < T > Ġ= ĊĠĠĠĠĠĠĠ Ġcache Internal ( key , Ġtype Ref , Ġsupplier ) ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠSet Ġcache - control Ġheader Ġto Ġlimit Ġexternal Ġcaching . ĠWhen Ġusing ĠNo Cache Directives Ġthe Ġheader Ġis Ġset ĊĠĠĠĠ Ġ* Ġimmediately . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġfun Ġcache Control ( response : ĠHttpServletResponse , Ġvar arg Ġdirectives : ĠNo Cache Directives ): ĠSimple Redis Cache Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( directives . isEmpty ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠIllegalArgumentException (" directives Ġfor Ġcache - control Ġheader Ġshould Ġnot Ġbe Ġempty ") ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġresponse . add Header (" Cache - Control ", Ġdirectives . join ToString (", Ġ") Ġ{ Ġit . value Ġ}) ĊĠĠĠĠĠĠĠ Ġreturn Ġthis ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* ĠSet Ġcache - control Ġheaders Ġfor Ġexternal Ġcaches . ĊĠĠĠĠ Ġ* ĊĠĠĠĠ Ġ* ĠNote Ġthe Ġredis Ġcache Ġcannot Ġinvalidate Ġexternal Ġcaches , Ġso Ġfor Ġdata Ġyou Ġmay Ġneed Ġto Ġinvalidate ĊĠĠĠĠ Ġ* Ġit Ġmight Ġbe Ġgood Ġidea Ġto Ġadd Ġnon - caching Ġdirectives Ġinstead Ġof Ġcaching Ġones . ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġfun Ġcache Control ( response : ĠHttpServletResponse , Ġvar arg Ġdirectives : ĠCache Directives ): ĠSimple Redis Cache Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( directives . isEmpty ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠIllegalArgumentException (" directives Ġfor Ġcache - control Ġheader Ġshould Ġnot Ġbe Ġempty ") ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġreturn ĠSimple Redis Cache Headers ( redis Adapter , ĠobjectMapper , Ġpool , Ġlock Duration , Ġexecutor , Ġresponse , Ġdirectives ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġclass ĠSimple Redis Cache Headers ( ĊĠĠĠĠĠĠĠ Ġredis Adapter : ĠRedis Adapter , ĊĠĠĠĠĠĠĠ ĠobjectMapper : ĠObjectMapper , ĊĠĠĠĠĠĠĠ Ġpool : ĠString , ĊĠĠĠĠĠĠĠ Ġlock Duration : ĠDuration , ĊĠĠĠĠĠĠĠ Ġexecutor : ĠExecutor , ĊĠĠĠĠĠĠĠ Ġprivate Ġval Ġresponse : ĠHttpServletResponse , ĊĠĠĠĠĠĠĠ Ġprivate Ġval Ġdirectives : ĠArray < out ĠCache Directives > ĊĠĠĠ Ġ) Ġ: ĠSimple Redis Cache ( redis Adapter , ĠobjectMapper , Ġpool , Ġlock Duration , Ġexecutor ) Ġ{ ĊĠĠĠĠĠĠĠ Ġoverride Ġfun Ġ< T > Ġcached Value ( key : ĠString , Ġexpire After : ĠDuration , Ġrefresh After : ĠDuration ?, Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < T >): ĠCached Value < T > Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġcached Value Ġ= Ġcache Internal ( key , Ġexpire After , Ġrefresh After , Ġtype Ref , Ġsupplier ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġage Ġ= ĠCh rono Unit . SECONDS . between ( cached Value . cache Time , ĠOffset DateTime . now ()) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse . add Header (" Cache - Control ", Ġdirectives . join ToString (", Ġ") Ġ{ Ġit . value ( expire After . seconds , Ġage , Ġrefresh After ?. seconds ) Ġ}) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġcached Value ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġoverride Ġfun Ġ< T > Ġcached Value ( key : ĠString , Ġtype Ref : ĠType Reference < Cached Value < T >>, Ġsupplier : ĠSupplier < Value To Cache < T >> ): ĠCached Value < T > Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġcached Value Ġ= Ġcache Internal ( key , Ġtype Ref , Ġsupplier ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġage Ġ= ĠCh rono Unit . SECONDS . between ( cached Value . cache Time , ĠOffset DateTime . now ()) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse . add Header (" Cache - Control ", Ġdirectives . join ToString (", Ġ") Ġ{ Ġit . value ( cached Value . expire After . seconds , Ġage , Ġcached Value . refresh After ?. seconds ) Ġ}) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġcached Value ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* Ġhttps :// developer . mozilla . org / en - US / docs / Web / HTTP / Headers / Cache - Control ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġenum Ġclass ĠNo Cache Directives ( val Ġvalue : ĠString ) Ġ{ ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" no - cache " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠNo Cache (" no - cache "), ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" no - store " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠNo Store (" no - store "), ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" max - age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġ 0 Ġ*/ ĊĠĠĠĠĠĠĠ ĠMax Age 0 (" max - age = 0 "), ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" s - max age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġ 0 Ġ*/ ĊĠĠĠĠĠĠĠ ĠS Max Age 0 (" s - max age = 0 "), ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" must - re validate " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠMust Re validate (" must - re validate ") ĊĠĠĠ Ġ} ĊĠĠĠ Ġ/** ĊĠĠĠĠ Ġ* Ġhttps :// developer . mozilla . org / en - US / docs / Web / HTTP / Headers / Cache - Control ĊĠĠĠĠ Ġ*/ ĊĠĠĠ Ġenum Ġclass ĠCache Directives Ġ{ ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" max - age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġexpire After Ġ*/ ĊĠĠĠĠĠĠĠ ĠMax Age Expire After Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" max - age =$ expire After " ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" max - age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġexpire After Ġminus Ġage Ġof Ġthe Ġcached Ġobject Ġ*/ ĊĠĠĠĠĠĠĠ ĠMax Age Expire After A ged Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" max - age =${ expire After Ġ- Ġage }" ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" max - age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġrefresh After Ġ*/ ĊĠĠĠĠĠĠĠ ĠMax Age Refresh After Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrefresh After ?. let Ġ{ Ġ" max - age =$ refresh After " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ?: Ġthrow ĠIllegalArgumentException (" When Ġusing ĠMax Age Refresh After Ġrefresh After Ġcannot Ġbe Ġnull ") ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" max - age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġrefresh After Ġminus Ġage Ġof Ġthe Ġcached Ġobject Ġ*/ ĊĠĠĠĠĠĠĠ ĠMax Age Refresh After A ged Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrefresh After ?. let Ġ{ Ġ" max - age =${ it Ġ- Ġage }" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ?: Ġthrow ĠIllegalArgumentException (" When Ġusing ĠMax Age Refresh After A ged Ġrefresh After Ġcannot Ġbe Ġnull ") ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" s - max age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġexpire After Ġ*/ ĊĠĠĠĠĠĠĠ ĠS Max Age Expire After Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" s - max age =$ expire After " ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" s - max age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġexpire After Ġminus Ġage Ġof Ġthe Ġcached Ġobject Ġ*/ ĊĠĠĠĠĠĠĠ ĠS Max Age Expire After A ged Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" s - max age =${ expire After Ġ- Ġage }" ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" s - max age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġrefresh After Ġ*/ ĊĠĠĠĠĠĠĠ ĠS Max Age Refresh After Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrefresh After ?. let Ġ{ Ġ" s - max age =$ refresh After " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ?: Ġthrow ĠIllegalArgumentException (" When Ġusing ĠS Max Age Refresh After Ġrefresh After Ġcannot Ġbe Ġnull ") ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" s - max age " Ġdirective Ġwith Ġa Ġvalue Ġof Ġrefresh After Ġminus Ġage Ġof Ġthe Ġcached Ġobject Ġ*/ ĊĠĠĠĠĠĠĠ ĠS Max Age Refresh After A ged Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrefresh After ?. let Ġ{ Ġ" s - max age =${ it Ġ- Ġage }" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ?: Ġthrow ĠIllegalArgumentException (" When Ġusing ĠS Max Age Refresh After A ged Ġrefresh After Ġcannot Ġbe Ġnull ") ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" must - re validate " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠMust Re validate Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" must - re validate " ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" proxy - re validate " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠProxy Re validate Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" proxy - re validate " ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" private " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠPrivate Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" private " ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" public " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠPublic Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" public " ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" no - transform " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠNo Transform Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" no - transform " ĊĠĠĠĠĠĠĠ Ġ}, ĊĠĠĠĠĠĠĠ Ġ/** ĠAdds Ġthe Ġ" immutable " Ġdirective Ġ*/ ĊĠĠĠĠĠĠĠ ĠImmutable Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?) Ġ= Ġ" immutable " ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ; ĊĊĠĠĠĠĠĠĠ Ġabstract Ġfun Ġvalue ( expire After : ĠLong , Ġage : ĠLong , Ġrefresh After : ĠLong ?): ĠString ĊĠĠĠ Ġ} Ċ } Ċ