@@ -6,6 +6,7 @@ package software.aws.toolkits.jetbrains.core.notifications
66import com.fasterxml.jackson.databind.DeserializationFeature
77import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
88import com.fasterxml.jackson.module.kotlin.readValue
9+ import com.intellij.ide.util.RunOnceUtil
910import com.intellij.openapi.Disposable
1011import com.intellij.openapi.application.ApplicationManager
1112import com.intellij.openapi.components.PersistentStateComponent
@@ -15,7 +16,6 @@ import com.intellij.util.AlarmFactory
1516import com.intellij.util.io.HttpRequests
1617import software.aws.toolkits.core.utils.RemoteResolveParser
1718import software.aws.toolkits.core.utils.RemoteResource
18- import software.aws.toolkits.core.utils.debug
1919import software.aws.toolkits.core.utils.error
2020import software.aws.toolkits.core.utils.getLogger
2121import software.aws.toolkits.jetbrains.core.DefaultRemoteResourceResolverProvider
@@ -31,6 +31,7 @@ private const val RETRY_DELAY_MS = 1000L
3131
3232interface NotificationPollingService {
3333 fun startPolling ()
34+ fun addObserver (observer : (Path ) -> Unit )
3435 fun dispose ()
3536}
3637
@@ -52,6 +53,7 @@ class NotificationPollingServiceImpl :
5253 PersistentStateComponent <NotificationPollingServiceImpl .State >,
5354 Disposable {
5455
56+ private val observers = mutableListOf< (Path ) -> Unit > ()
5557 private var state = State ()
5658 private val alarm = AlarmFactory .getInstance().create(Alarm .ThreadToUse .POOLED_THREAD , this )
5759 private val pollingIntervalMs = Duration .ofMinutes(10 ).toMillis()
@@ -62,24 +64,13 @@ class NotificationPollingServiceImpl :
6264 override val remoteResolveParser: RemoteResolveParser = NotificationFileValidator
6365 }
6466
65- data class State (
66- var currentETag : String? = null ,
67- var cachedFilePath : String? = null
68- )
69-
70- override fun getState (): State = state
71-
72- override fun loadState (state : State ) {
73- this .state = state
74- }
75-
76- override fun dispose () {
77- alarm.dispose()
78- }
79-
8067 override fun startPolling () {
81- pollForNotifications()
82- // todo notify observers
68+ val newNotifications = pollForNotifications()
69+ if (newNotifications) {
70+ getCachedPath()?.let { path ->
71+ notifyObservers(path)
72+ }
73+ }
8374 alarm.addRequest(
8475 { startPolling() },
8576 pollingIntervalMs
@@ -98,7 +89,12 @@ class NotificationPollingServiceImpl :
9889 try {
9990 val newETag = getNotificationETag()
10091 if (newETag == state.currentETag) {
101- LOG .debug { " No updates available for notifications" }
92+ RunOnceUtil .runOnceForApp(this ::class .qualifiedName.toString()) {
93+ // try startup notifications regardless of file change
94+ getCachedPath()?.let { path ->
95+ notifyObservers(path)
96+ }
97+ }
10298 return false
10399 }
104100 val resolvedPath = resourceResolver.get()
@@ -133,9 +129,14 @@ class NotificationPollingServiceImpl :
133129 fun getCachedPath (): Path ? =
134130 state.cachedFilePath?.let { Paths .get(it) }
135131
136- /* *
137- * Emits telemetry metric for polling failures
138- */
132+ override fun addObserver (observer : (Path ) -> Unit ) {
133+ observers.add(observer)
134+ }
135+
136+ private fun notifyObservers (path : Path ) {
137+ observers.forEach { it(path) }
138+ }
139+
139140 private fun emitFailureMetric (exception : Exception ? ) {
140141 // todo: add metric
141142 }
@@ -145,4 +146,19 @@ class NotificationPollingServiceImpl :
145146 fun getInstance (): NotificationPollingService =
146147 ApplicationManager .getApplication().getService(NotificationPollingService ::class .java)
147148 }
149+
150+ data class State (
151+ var currentETag : String? = null ,
152+ var cachedFilePath : String? = null ,
153+ )
154+
155+ override fun getState (): State = state
156+
157+ override fun loadState (state : State ) {
158+ this .state = state
159+ }
160+
161+ override fun dispose () {
162+ alarm.dispose()
163+ }
148164}
0 commit comments