Skip to content

Commit

Permalink
GH-226 Enhancement: Check connection for config file in parallel
Browse files Browse the repository at this point in the history
Signed-off-by: Katsiaryna Tsytsenia <[email protected]>
  • Loading branch information
Katsiaryna Tsytsenia authored and ktsytsen committed Feb 17, 2025
1 parent 4646b27 commit 523b3ad
Show file tree
Hide file tree
Showing 2 changed files with 397 additions and 88 deletions.
229 changes: 164 additions & 65 deletions src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ import com.intellij.notification.NotificationType
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.components.service
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.vfs.VirtualFileManager
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.zowe.explorer.config.ConfigService
import org.zowe.explorer.config.connect.ConnectionConfig
import org.zowe.explorer.config.connect.CredentialService
Expand Down Expand Up @@ -180,7 +183,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
}
}
} catch (e: Exception) {
NotificationsService.errorNotification(e, project = myProject, custTitle="Error with Zowe config file")
NotificationsService.errorNotification(e, project = myProject, custTitle = "Error with Zowe config file")
return null
}
}
Expand Down Expand Up @@ -239,42 +242,99 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
}

/**
* Checks the zowe connection using InfoOperation. It is also needed to load zos version and
* Checks all passed zowe connections using InfoOperation in parallel. It is also needed to load zos version and
* real owner of the connection (acceptable for alias users).
* IMPORTANT!!! It modifies passed connection object by setting zVersion and owner.
* @param zoweConnection connection to check and prepare.
* @throws Throwable if something went wrong (connection was not established or one of requests was failed ...)
* IMPORTANT!!! It modifies passed connection objects by setting zVersion and owner.
* @param zoweConnections list of connection to check and prepare.
* @param type of zowe config
* @throws ProcessCanceledException if connection has been canceled by user
* @return a pair of lists with successfully tested connections and invalid connections
*/
private fun testAndPrepareConnection(zoweConnection: ConnectionConfig) {
val throwable = runTask("Testing Connection to ${zoweConnection.url}", myProject) { indicator ->
return@runTask try {
runCatching {
DataOpsManager.getService().performOperation(InfoOperation(zoweConnection), indicator)
}.onSuccess {
indicator.text = "Retrieving z/OS information"
val systemInfo = DataOpsManager.getService().performOperation(ZOSInfoOperation(zoweConnection), indicator)
zoweConnection.zVersion = when (systemInfo.zosVersion) {
"04.25.00" -> ZVersion.ZOS_2_2
"04.26.00" -> ZVersion.ZOS_2_3
"04.27.00" -> ZVersion.ZOS_2_4
"04.28.00" -> ZVersion.ZOS_2_5
"04.29.00" -> ZVersion.ZOS_3_1
else -> ZVersion.ZOS_2_1
@Throws(ProcessCanceledException::class)
private fun testAndPrepareConnections(
zoweConnections: List<ZOSConnection>,
type: ZoweConfigType
): Pair<MutableList<ConnectionConfig>, MutableList<ConnectionConfig>> {
val succeededConnections = mutableListOf<ConnectionConfig>()
val failedConnections = mutableListOf<ConnectionConfig>()
val results = mutableListOf<Deferred<ConnectionConfig>>()
var throwable: ProcessCanceledException? = null
runTask("Testing Connections...", myProject, cancellable = true) { indicator ->
runBlocking {
for (zosmfConnection in zoweConnections) {
results.add(async {
val zoweConnection: ConnectionConfig = prepareConnection(zosmfConnection, type)
try {
runCatching {
if (!indicator.isCanceled) {
indicator.text = if (zoweConnections.size == 1)
"Testing Connection to ${zoweConnection.url}"
else
"Testing Connection to ${zoweConnections.size} connections"
DataOpsManager.getService().performOperation(InfoOperation(zoweConnection), indicator)
} else {
throw ProcessCanceledException()
}
}.onSuccess {
if (!indicator.isCanceled) {

indicator.text = if (zoweConnections.size == 1)
"Retrieving z/OS information for ${zoweConnection.url}"
else
"Retrieving z/OS information for ${zoweConnections.size} connections"
val systemInfo =
DataOpsManager.getService().performOperation(
ZOSInfoOperation(zoweConnection),
indicator
)
zoweConnection.zVersion = when (systemInfo.zosVersion) {
"04.25.00" -> ZVersion.ZOS_2_2
"04.26.00" -> ZVersion.ZOS_2_3
"04.27.00" -> ZVersion.ZOS_2_4
"04.28.00" -> ZVersion.ZOS_2_5
"04.29.00" -> ZVersion.ZOS_3_1
else -> ZVersion.ZOS_2_1
}
} else {
throw ProcessCanceledException()
}
}.onSuccess {
if (!indicator.isCanceled) {
indicator.text = if (zoweConnections.size == 1)
"Retrieving user information for ${zoweConnection.url}"
else
"Retrieving user information for ${zoweConnections.size} connections"
zoweConnection.owner = whoAmI(zoweConnection) ?: ""
} else {
throw ProcessCanceledException()
}
}.onFailure {
if (indicator.isCanceled)
throw ProcessCanceledException()
else {
throw it
}
}
} catch (t: Throwable) {
if (t is ProcessCanceledException)
throwable = t
failedConnections.add(zoweConnection)
return@async zoweConnection
}
succeededConnections.add(zoweConnection)
return@async zoweConnection
}
}.onSuccess {
indicator.text = "Retrieving user information"
zoweConnection.owner = whoAmI(zoweConnection) ?: ""
}.onFailure {
throw it
)
}
null
} catch (t: Throwable) {
t
}
}
if (throwable != null) {
throw throwable
throw ProcessCanceledException()
}
runBlocking {
results.map { it.await() }
}
return succeededConnections to failedConnections
}

/**
Expand All @@ -290,19 +350,46 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
this.globalZoweConfig
zoweConfig ?: throw Exception("Cannot get $type Zowe config")

val (allPreparedConn, failedConnections) = testAndPrepareAllZosmfConnections(zoweConfig, type)
val allConnectionsToTest = zoweConfig.getListOfZosmfConections()
val uniqueConnectionsToTest = allConnectionsToTest.filterIndexed { _, element ->
allConnectionsToTest.filter {
it.host == element.host &&
it.zosmfPort == element.zosmfPort &&
it.user == element.user &&
it.password == element.password &&
it.protocol == element.protocol &&
it.rejectUnauthorized == element.rejectUnauthorized &&
it.basePath == element.basePath &&
it.encoding == element.encoding &&
it.responseTimeout == element.responseTimeout
}.sortedWith(compareBy { it.profileName.length })[0] == element
}
val duplicatedConnections = (allConnectionsToTest + uniqueConnectionsToTest).groupBy { it.profileName }
.filter { it.value.size == 1 }
.flatMap { it.value }

val (succeededConnections, failedConnections) =
testAndPrepareConnections(uniqueConnectionsToTest, type)

for (tmpZOSMFConn in duplicatedConnections) {
println(succeededConnections)
println(failedConnections)
restoreFullConnectionList(tmpZOSMFConn, succeededConnections, type)
restoreFullConnectionList(tmpZOSMFConn, failedConnections, type)
}

if (checkConnection and failedConnections.isNotEmpty()) {
val andMore = if (failedConnections.size > 3) "..." else ""
notifyUiOnConnectionFailure(
"Connection failed to:",
"${failedConnections.joinToString(separator = ", <p>") { it.url }} $andMore",
"Unsuccessfully tested profiles:",
"${failedConnections.joinToString(separator = ", <p>") { getProfileNameFromConnName(it.name) }} $andMore",
type
)
}
val conToAdd = if (checkConnection)
allPreparedConn.subtract(failedConnections.toSet())
succeededConnections
else
allPreparedConn
succeededConnections + failedConnections
conToAdd.forEach { zosmfConnection ->
val connectionOpt = configCrudable.addOrUpdate(zosmfConnection)
if (!connectionOpt.isEmpty) {
Expand All @@ -315,7 +402,43 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
}

} catch (e: Exception) {
NotificationsService.errorNotification(e, project = myProject, custTitle="Error with Zowe config file")
if (e !is ProcessCanceledException)
NotificationsService.errorNotification(
e,
project = myProject,
custTitle = "Error with Zowe config file"
)
else { /* Nothing to do */
}
}
}

/**
* Restore connection lists after testing
* @param tmpZOSMFConn duplicated connection to restore.
* @param connListToRestore list with tested connections (succeeded or failed)
* @param type of zowe config
*/
private fun restoreFullConnectionList(
tmpZOSMFConn: ZOSConnection,
connListToRestore: MutableList<ConnectionConfig>,
type: ZoweConfigType
) {
val tmpConn = tmpZOSMFConn.toConnectionConfig(uuid = UUID.randomUUID().toString(), type = type)
val tmpDuplicatedConnList = connListToRestore.filter {
it.isAllowSelfSigned == tmpConn.isAllowSelfSigned &&
it.url == tmpConn.url &&
it.name != tmpConn.name &&
CredentialService.getUsername(it) == tmpZOSMFConn.user &&
CredentialService.getPassword(it).contentEquals(tmpZOSMFConn.password.toCharArray())
}
if (tmpDuplicatedConnList.isNotEmpty()) {
val tmpDuplConn = tmpDuplicatedConnList[0].clone()
tmpDuplConn.name = tmpConn.name
tmpDuplConn.uuid = tmpConn.uuid
CredentialService.getService()
.setCredentials(tmpConn.uuid, tmpZOSMFConn.user, tmpZOSMFConn.password.toCharArray())
connListToRestore.add(tmpDuplConn)
}
}

Expand All @@ -338,31 +461,6 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
return zoweConnection
}

/**
* Convert all zosmf connections from zowe config file to ConnectionConfig and tests them
* @param zoweConfig
* @param type of zowe config
* @return pair of lists, one is the connections list, the second is the list of URLs which did not pass the test
*/
private fun testAndPrepareAllZosmfConnections(
zoweConfig: ZoweConfig,
type: ZoweConfigType
): Pair<List<ConnectionConfig>, List<ConnectionConfig>> {
return zoweConfig.getListOfZosmfConections()
.fold(
mutableListOf<ConnectionConfig>() to mutableListOf<ConnectionConfig>()
) { (allConnectionConfigs, failedURLs), zosmfConnection ->
val zoweConnection = prepareConnection(zosmfConnection, type)
try {
testAndPrepareConnection(zoweConnection)
} catch (t: Throwable) {
failedURLs.add(zoweConnection)
}
allConnectionConfigs.add(zoweConnection)
allConnectionConfigs to failedURLs
}
}

/**
* @see ZoweConfigService.deleteZoweConfig
*/
Expand Down Expand Up @@ -398,7 +496,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
}

} catch (e: Exception) {
NotificationsService.errorNotification(e, project = myProject, custTitle="Error with Zowe config file")
NotificationsService.errorNotification(e, project = myProject, custTitle = "Error with Zowe config file")
}
}

Expand All @@ -421,13 +519,13 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
var host = "localhost"
var port = "10443"
if (matcher.matches()) {
if (matcher.group(2) != null)
if (!matcher.group(2).isNullOrBlank())
host = matcher.group(2)
if (matcher.group(3) != null)
if (!matcher.group(3).isNullOrBlank())
port = matcher.group(3).substring(1)
}

val content = getResourceAsStreamWrappable(ZoweConfigServiceImpl::class.java.classLoader ,"files/${ZOWE_CONFIG_NAME}")
val content = getResourceAsStreamWrappable(ZoweConfigServiceImpl::class.java.classLoader, "files/${ZOWE_CONFIG_NAME}")
.use { iS -> iS?.readAllBytes()?.let { String(it, charset) } }
?.replace("<PORT>".toRegex(), port)
?.replace("<HOST>".toRegex(), "\"$host\"")
Expand Down Expand Up @@ -521,6 +619,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
if (findAllZosmfExistingConnection(type).isEmpty()) return ZoweConfigState.NEED_TO_ADD

val zoweConfigZosmfConnections = zoweConfig.getListOfZosmfConections()

return zoweConfigZosmfConnections
.fold(ZoweConfigState.SYNCHRONIZED) { prevZoweConfigState, zosConnection ->
val existingConnection = findExistingConnection(type, zosConnection.profileName)
Expand Down
Loading

0 comments on commit 523b3ad

Please sign in to comment.