Skip to content

Commit dc11fd6

Browse files
author
Dzianis Lisiankou
committed
IJMP-2064: implement file cache clearing
1 parent de9c3a5 commit dc11fd6

File tree

12 files changed

+275
-25
lines changed

12 files changed

+275
-25
lines changed

src/main/kotlin/eu/ibagroup/formainframe/config/settings/ui/SettingsConfigurable.kt

+19
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@ package eu.ibagroup.formainframe.config.settings.ui
1717
import com.intellij.ide.BrowserUtil
1818
import com.intellij.openapi.observable.util.whenTextChanged
1919
import com.intellij.openapi.options.BoundSearchableConfigurable
20+
import com.intellij.openapi.progress.runModalTask
2021
import com.intellij.openapi.ui.DialogPanel
22+
import com.intellij.openapi.ui.Messages
2123
import com.intellij.ui.dsl.builder.bindIntText
2224
import com.intellij.ui.dsl.builder.bindSelected
2325
import com.intellij.ui.dsl.builder.panel
2426
import eu.ibagroup.formainframe.analytics.AnalyticsService
2527
import eu.ibagroup.formainframe.analytics.PolicyProvider
2628
import eu.ibagroup.formainframe.analytics.ui.AnalyticsPolicyDialog
2729
import eu.ibagroup.formainframe.config.ConfigService
30+
import eu.ibagroup.formainframe.dataops.DataOpsManager
2831
import eu.ibagroup.formainframe.rateus.RateUsNotification
2932
import eu.ibagroup.formainframe.utils.validateBatchSize
3033
import java.util.concurrent.atomic.AtomicBoolean
@@ -87,6 +90,22 @@ class SettingsConfigurable : BoundSearchableConfigurable("Settings", "mainframe"
8790
res.component.addItemListener { isAutoSyncEnabled.set(res.component.isSelected) }
8891
}
8992
}
93+
row {
94+
button("Clear File Cache") {
95+
var cacheCleared = false
96+
runModalTask("Cache Clearing", cancellable = false) {
97+
cacheCleared = DataOpsManager.getService().clearFileCache()
98+
}
99+
if (cacheCleared) {
100+
Messages.showInfoMessage(
101+
"The file cache has been successfully cleared.",
102+
"Cache Cleared",
103+
)
104+
}
105+
}.applyToComponent {
106+
toolTipText = "Clear the local contents of files downloaded from the remote system. All related files opened in the editor will be closed"
107+
}
108+
}
90109
}
91110
group("Rate Us") {
92111
row {

src/main/kotlin/eu/ibagroup/formainframe/dataops/DataOpsManager.kt

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ interface DataOpsManager : Disposable {
6767
*/
6868
fun getContentSynchronizer(file: VirtualFile): ContentSynchronizer?
6969

70+
fun clearFileCache(): Boolean
71+
7072
fun getMFContentAdapter(file: VirtualFile): MFContentAdapter
7173

7274
fun getNameResolver(source: VirtualFile, destination: VirtualFile): CopyPasteNameResolver

src/main/kotlin/eu/ibagroup/formainframe/dataops/DataOpsManagerImpl.kt

+31
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@ package eu.ibagroup.formainframe.dataops
1717
import com.intellij.execution.ui.ConsoleView
1818
import com.intellij.openapi.application.ApplicationManager
1919
import com.intellij.openapi.components.ComponentManager
20+
import com.intellij.openapi.fileEditor.FileEditorManager
2021
import com.intellij.openapi.progress.ProgressIndicator
22+
import com.intellij.openapi.project.ProjectManager
2123
import com.intellij.openapi.util.Disposer
2224
import com.intellij.openapi.vfs.VirtualFile
2325
import eu.ibagroup.formainframe.dataops.attributes.AttributesService
2426
import eu.ibagroup.formainframe.dataops.attributes.FileAttributes
2527
import eu.ibagroup.formainframe.dataops.content.adapters.DefaultContentAdapter
2628
import eu.ibagroup.formainframe.dataops.content.adapters.MFContentAdapter
2729
import eu.ibagroup.formainframe.dataops.content.synchronizer.ContentSynchronizer
30+
import eu.ibagroup.formainframe.dataops.content.synchronizer.checkForSync
2831
import eu.ibagroup.formainframe.dataops.fetch.FileFetchProvider
2932
import eu.ibagroup.formainframe.dataops.log.AbstractMFLoggerBase
3033
import eu.ibagroup.formainframe.dataops.log.LogFetcher
@@ -36,6 +39,8 @@ import eu.ibagroup.formainframe.dataops.operations.mover.names.DefaultNameResolv
3639
import eu.ibagroup.formainframe.utils.associateListedBy
3740
import eu.ibagroup.formainframe.utils.findAnyNullable
3841
import eu.ibagroup.formainframe.utils.log
42+
import eu.ibagroup.formainframe.utils.runInEdtAndWait
43+
import eu.ibagroup.formainframe.vfs.MFVirtualFile
3944

4045
/**
4146
* Data operation manager implementation class.
@@ -170,6 +175,32 @@ class DataOpsManagerImpl : DataOpsManager {
170175
return contentSynchronizers.firstOrNull { it.accepts(file) }
171176
}
172177

178+
/**
179+
* Closes all [MFVirtualFile] files opened in the editor and clears the cache of all registered content synchronizers.
180+
* @return true if the file cache is cleared and false otherwise.
181+
*/
182+
override fun clearFileCache(): Boolean {
183+
ProjectManager.getInstance().openProjects.forEach { project ->
184+
val fileEditorManager = FileEditorManager.getInstance(project)
185+
runInEdtAndWait {
186+
fileEditorManager.openFiles.forEach {
187+
if (it is MFVirtualFile) {
188+
fileEditorManager.closeFile(it)
189+
}
190+
}
191+
}
192+
}
193+
var syncInProgress = false
194+
runInEdtAndWait {
195+
syncInProgress = checkForSync()
196+
}
197+
if (!syncInProgress) {
198+
contentSynchronizers.forEach { it.clearFileCache() }
199+
return true
200+
}
201+
return false
202+
}
203+
173204
/**
174205
* Returns instance of content adapter to mainframe
175206
* @param file object that represents file/folder on mainframe

src/main/kotlin/eu/ibagroup/formainframe/dataops/content/service/SyncProcessService.kt

+6
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,10 @@ interface SyncProcessService {
5555
*/
5656
fun areDependentFilesSyncingNow(file: VirtualFile): Boolean
5757

58+
/**
59+
* Check if any file is currently synchronized.
60+
* @return true if any file is syncing now or false otherwise.
61+
*/
62+
fun isAnyFileSyncingNow(): Boolean
63+
5864
}

src/main/kotlin/eu/ibagroup/formainframe/dataops/content/service/SyncProcessServiceImpl.kt

+7
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,11 @@ class SyncProcessServiceImpl: SyncProcessService {
5656
}
5757
}
5858

59+
/**
60+
* Base implementation of [SyncProcessService.isAnyFileSyncingNow] method.
61+
*/
62+
override fun isAnyFileSyncingNow(): Boolean {
63+
return fileToProgressIndicatorMap.values.any { it.isRunning }
64+
}
65+
5966
}

src/main/kotlin/eu/ibagroup/formainframe/dataops/content/synchronizer/ContentSynchronizer.kt

+5
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,9 @@ interface ContentSynchronizer {
7979
*/
8080
fun markAsNotNeededForSync(syncProvider: SyncProvider)
8181

82+
/**
83+
* Clears all file records from the content storage and other related values.
84+
*/
85+
fun clearFileCache()
86+
8287
}

src/main/kotlin/eu/ibagroup/formainframe/dataops/content/synchronizer/RemoteAttributedContentSynchronizer.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ abstract class RemoteAttributedContentSynchronizer<FAttributes : FileAttributes>
9292
val attributesService by lazy { dataOpsManager.getAttributesService(attributesClass, vFileClass) }
9393
private val successfulStatesStorage by lazy { ContentStorage(SUCCESSFUL_CONTENT_STORAGE_NAME_PREFIX + entityName) }
9494
private val handlerToStorageIdMap = ConcurrentHashMap<SyncProvider, Int>()
95-
private val idToBinaryFileMap = ConcurrentHashMap<Int, VirtualFile>()
9695
private val fetchedAtLeastOnce = ConcurrentHashMap.newKeySet<SyncProvider>()
9796
private val needToUpload = ConcurrentHashMap.newKeySet<SyncProvider>()
9897

@@ -227,4 +226,16 @@ abstract class RemoteAttributedContentSynchronizer<FAttributes : FileAttributes>
227226
override fun markAsNotNeededForSync(syncProvider: SyncProvider) {
228227
needToUpload.remove(syncProvider)
229228
}
229+
230+
/**
231+
* Base implementation of [ContentSynchronizer.clearFileCache] method for each content synchronizer.
232+
*/
233+
override fun clearFileCache() {
234+
handlerToStorageIdMap.values.forEach {
235+
successfulStatesStorage.deleteRecord(it)
236+
}
237+
handlerToStorageIdMap.clear()
238+
fetchedAtLeastOnce.clear()
239+
needToUpload.clear()
240+
}
230241
}

src/main/kotlin/eu/ibagroup/formainframe/dataops/content/synchronizer/syncUtils.kt

+14
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,17 @@ fun checkFileForSync(
7979
true
8080
} else false
8181
}
82+
83+
/**
84+
* Check if any file is syncing now and show the warning dialog if so.
85+
* @param project project to show dialog.
86+
* @return true if any file is syncing now and false otherwise.
87+
*/
88+
fun checkForSync(project: Project? = null): Boolean {
89+
val message = "You can't perform this action because some file is currently being synchronized"
90+
val title = "Synchronization Is In Progress"
91+
return if (SyncProcessService.getService().isAnyFileSyncingNow()) {
92+
Messages.showWarningDialog(project, message, title)
93+
true
94+
} else false
95+
}

src/test/kotlin/eu/ibagroup/formainframe/dataops/ContentTestSpec.kt

+70-24
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import com.intellij.openapi.ui.Messages
2020
import com.intellij.openapi.vfs.VfsUtilCore
2121
import com.intellij.openapi.vfs.VirtualFile
2222
import eu.ibagroup.formainframe.dataops.content.service.SyncProcessService
23+
import eu.ibagroup.formainframe.dataops.content.service.SyncProcessServiceImpl
2324
import eu.ibagroup.formainframe.dataops.content.synchronizer.checkFileForSync
25+
import eu.ibagroup.formainframe.dataops.content.synchronizer.checkForSync
2426
import eu.ibagroup.formainframe.testutils.WithApplicationShouldSpec
2527
import eu.ibagroup.formainframe.testutils.testServiceImpl.TestSyncProcessServiceImpl
2628
import io.kotest.assertions.assertSoftly
@@ -115,6 +117,28 @@ class ContentTestSpec : WithApplicationShouldSpec({
115117
wasWarningShown shouldBe false
116118
}
117119
}
120+
// syncUtils.checkForSync
121+
should("check that any file is syncing") {
122+
syncProcessService.testInstance = object : TestSyncProcessServiceImpl() {
123+
override fun isAnyFileSyncingNow(): Boolean {
124+
return true
125+
}
126+
}
127+
128+
val result = checkForSync(mockk())
129+
assertSoftly {
130+
result shouldBe true
131+
wasWarningShown shouldBe true
132+
}
133+
}
134+
should("check that no file is syncing") {
135+
val result = checkForSync()
136+
137+
assertSoftly {
138+
result shouldBe false
139+
wasWarningShown shouldBe false
140+
}
141+
}
118142
// SyncAction.actionPerformed
119143
should("synchronize the file with the remote file") {}
120144
// MemberContentSynchronizer.fetchRemoteContentBytes
@@ -133,56 +157,59 @@ class ContentTestSpec : WithApplicationShouldSpec({
133157
should("adapt content for the dataset from mainframe with variable print length") {}
134158
}
135159
context("dataops module: content/service") {
160+
val syncProcessService = SyncProcessServiceImpl()
136161

137162
val virtualFileMock = mockk<VirtualFile>()
138163
val progressIndicatorMock = mockk<ProgressIndicator>()
139164

140165
beforeEach {
166+
syncProcessService.startFileSync(virtualFileMock, progressIndicatorMock)
167+
141168
val isAncestorRef: (VirtualFile, VirtualFile, Boolean) -> Boolean = VfsUtilCore::isAncestor
142169
mockkStatic(isAncestorRef as KFunction<*>)
143170
}
144171

145172
afterEach {
173+
syncProcessService.stopFileSync(virtualFileMock)
174+
146175
unmockkAll()
147176
}
148177

149178
// SyncProcessServiceImpl.isFileSyncingNow
150-
// TODO: rewrite to the service usage
151-
// should("file is syncing now") {
152-
// every { progressIndicatorMock.isRunning } returns true
153-
//
154-
// val result = SyncProcessService.getService().isFileSyncingNow(virtualFileMock)
155-
//
156-
// assertSoftly {
157-
// result shouldBe true
158-
// }
159-
// }
179+
should("file is syncing now") {
180+
every { progressIndicatorMock.isRunning } returns true
181+
182+
val result = syncProcessService.isFileSyncingNow(virtualFileMock)
183+
184+
assertSoftly {
185+
result shouldBe true
186+
}
187+
}
160188
should("file is not syncing now") {
161189
every { progressIndicatorMock.isRunning } returns false
162190

163-
val result = SyncProcessService.getService().isFileSyncingNow(virtualFileMock)
191+
val result = syncProcessService.isFileSyncingNow(virtualFileMock)
164192

165193
assertSoftly {
166194
result shouldBe false
167195
}
168196
}
169197
// SyncProcessServiceImpl.areDependentFilesSyncingNow
170-
// TODO: rewrite to the service usage
171-
// should("dependent files are syncing now") {
172-
// every { progressIndicatorMock.isRunning } returns true
173-
// every { VfsUtilCore.isAncestor(virtualFileMock, any(), true) } returns true
174-
//
175-
// val result = SyncProcessService.getService().areDependentFilesSyncingNow(virtualFileMock)
176-
//
177-
// assertSoftly {
178-
// result shouldBe true
179-
// }
180-
// }
198+
should("dependent files are syncing now") {
199+
every { progressIndicatorMock.isRunning } returns true
200+
every { VfsUtilCore.isAncestor(virtualFileMock, any(), true) } returns true
201+
202+
val result = syncProcessService.areDependentFilesSyncingNow(virtualFileMock)
203+
204+
assertSoftly {
205+
result shouldBe true
206+
}
207+
}
181208
should("dependent files are not syncing now") {
182209
every { progressIndicatorMock.isRunning } returns true
183210
every { VfsUtilCore.isAncestor(virtualFileMock, any(), true) } returns false
184211

185-
val result = SyncProcessService.getService().areDependentFilesSyncingNow(virtualFileMock)
212+
val result = syncProcessService.areDependentFilesSyncingNow(virtualFileMock)
186213

187214
assertSoftly {
188215
result shouldBe false
@@ -191,7 +218,26 @@ class ContentTestSpec : WithApplicationShouldSpec({
191218
should("dependent files are not syncing now because no sync is running") {
192219
every { progressIndicatorMock.isRunning } returns false
193220

194-
val result = SyncProcessService.getService().areDependentFilesSyncingNow(virtualFileMock)
221+
val result = syncProcessService.areDependentFilesSyncingNow(virtualFileMock)
222+
223+
assertSoftly {
224+
result shouldBe false
225+
}
226+
}
227+
// SyncProcessServiceImpl.isAnyFileSyncingNow
228+
should("any file is syncing now") {
229+
every { progressIndicatorMock.isRunning } returns true
230+
231+
val result = syncProcessService.isAnyFileSyncingNow()
232+
233+
assertSoftly {
234+
result shouldBe true
235+
}
236+
}
237+
should("no file is syncing now") {
238+
every { progressIndicatorMock.isRunning } returns false
239+
240+
val result = syncProcessService.isAnyFileSyncingNow()
195241

196242
assertSoftly {
197243
result shouldBe false

0 commit comments

Comments
 (0)