aboutsummaryrefslogtreecommitdiffstats
path: root/ms/controllerblueprints/modules/service/src/main/kotlin
diff options
context:
space:
mode:
authorMuthuramalingam, Brinda Santh(bs2796) <bs2796@att.com>2018-12-14 20:38:44 -0500
committerMuthuramalingam, Brinda Santh(bs2796) <bs2796@att.com>2018-12-14 20:38:44 -0500
commit6de2171cd73873d5d2f5252e7238cb8d31aca806 (patch)
tree82cc59eee7fad581a41dea4396f70ab3b0dbc05a /ms/controllerblueprints/modules/service/src/main/kotlin
parent5045877d046ebcddc80a8b83c14351a0d9fe6a49 (diff)
Add multiple path load service.
Change-Id: Ib2e5f60663991d097b7446106bb883a45db1bdb8 Issue-ID: CCSDK-746 Signed-off-by: Muthuramalingam, Brinda Santh(bs2796) <bs2796@att.com>
Diffstat (limited to 'ms/controllerblueprints/modules/service/src/main/kotlin')
-rw-r--r--ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/BluePrintCoreConfiguration.kt60
-rw-r--r--ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogLoadService.kt68
-rw-r--r--ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogServiceImpl.kt66
-rw-r--r--ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintDatabaseLoadService.kt79
-rw-r--r--ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintLoadConfiguration.kt (renamed from ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogLoad.kt)18
-rw-r--r--ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ModelTypeLoadService.kt6
-rw-r--r--ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ResourceDictionaryLoadService.kt104
7 files changed, 349 insertions, 52 deletions
diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/BluePrintCoreConfiguration.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/BluePrintCoreConfiguration.kt
new file mode 100644
index 00000000..2e5fc5ba
--- /dev/null
+++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/BluePrintCoreConfiguration.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.ccsdk.apps.controllerblueprints.service
+
+import org.onap.ccsdk.apps.controllerblueprints.service.load.BluePrintLoadConfiguration
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.context.properties.bind.Bindable
+import org.springframework.boot.context.properties.bind.Binder
+import org.springframework.boot.context.properties.source.ConfigurationPropertySources
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.core.env.Environment
+import org.springframework.stereotype.Service
+
+@Configuration
+open class BluePrintCoreConfiguration(private val bluePrintProperties: BluePrintProperties) {
+
+ companion object {
+ const val PREFIX_BLUEPRINT_LOAD_CONFIGURATION = "controllerblueprints"
+ }
+
+ @Bean
+ open fun bluePrintLoadConfiguration(): BluePrintLoadConfiguration {
+ return bluePrintProperties
+ .propertyBeanType(PREFIX_BLUEPRINT_LOAD_CONFIGURATION, BluePrintLoadConfiguration::class.java)
+ }
+}
+
+@Configuration
+open class BlueprintPropertyConfiguration {
+ @Autowired
+ lateinit var environment: Environment
+
+ @Bean
+ open fun bluePrintPropertyBinder(): Binder {
+ val configurationPropertySource = ConfigurationPropertySources.get(environment)
+ return Binder(configurationPropertySource)
+ }
+}
+
+@Service
+open class BluePrintProperties(var bluePrintPropertyBinder: Binder) {
+ fun <T> propertyBeanType(prefix: String, type: Class<T>): T {
+ return bluePrintPropertyBinder.bind(prefix, Bindable.of(type)).get()
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogLoadService.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogLoadService.kt
new file mode 100644
index 00000000..07ee0a9b
--- /dev/null
+++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogLoadService.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.ccsdk.apps.controllerblueprints.service.load
+
+import com.att.eelf.configuration.EELFManager
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.runBlocking
+import org.apache.commons.lang3.text.StrBuilder
+import org.onap.ccsdk.apps.controllerblueprints.core.interfaces.BluePrintCatalogService
+import org.springframework.stereotype.Service
+import java.io.File
+
+@Service
+open class BluePrintCatalogLoadService(private val bluePrintCatalogService: BluePrintCatalogService) {
+
+ private val log = EELFManager.getInstance().getLogger(BluePrintCatalogLoadService::class.java)
+
+ open fun loadPathsBluePrintModelCatalog(paths: List<String>) {
+ paths.forEach { loadPathBluePrintModelCatalog(it) }
+ }
+
+ open fun loadPathBluePrintModelCatalog(path: String) {
+
+ val files = File(path).listFiles()
+ runBlocking {
+ val errorBuilder = StrBuilder()
+ val deferredResults = mutableListOf<Deferred<Unit>>()
+
+ for (file in files) {
+ deferredResults += async {
+ loadBluePrintModelCatalog(errorBuilder, file)
+ }
+ }
+
+ for (deferredResult in deferredResults) {
+ deferredResult.await()
+ }
+
+ if (!errorBuilder.isEmpty) {
+ log.error(errorBuilder.toString())
+ }
+ }
+ }
+
+ open fun loadBluePrintModelCatalog(errorBuilder: StrBuilder, file: File) {
+ try {
+ bluePrintCatalogService.uploadToDataBase(file.absolutePath)
+ } catch (e: Exception) {
+ errorBuilder.appendln("Couldn't load DataType(${file.name}: ${e.message}")
+ }
+ }
+
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogServiceImpl.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogServiceImpl.kt
new file mode 100644
index 00000000..8cddbb4c
--- /dev/null
+++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogServiceImpl.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.ccsdk.apps.controllerblueprints.service.load
+
+import org.onap.ccsdk.apps.controllerblueprints.core.interfaces.BluePrintCatalogService
+import org.onap.ccsdk.apps.controllerblueprints.core.interfaces.BluePrintValidatorService
+import org.onap.ccsdk.apps.controllerblueprints.core.utils.BluePrintArchiveUtils
+import org.onap.ccsdk.apps.controllerblueprints.core.utils.BluePrintMetadataUtils
+import org.springframework.stereotype.Service
+import java.io.File
+import java.util.*
+
+@Service
+class BluePrintCatalogServiceImpl(private val bluePrintLoadConfiguration: BluePrintLoadConfiguration,
+ private val bluePrintValidatorService: BluePrintValidatorService) : BluePrintCatalogService {
+
+ override fun uploadToDataBase(file: String): String {
+ val id = UUID.randomUUID().toString()
+ val blueprintFile = File(file)
+ // If the file is directory
+ if (blueprintFile.isDirectory) {
+ val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(id, blueprintFile.absolutePath)
+ val valid = bluePrintValidatorService.validateBluePrints(bluePrintRuntimeService)
+ if (valid) {
+ val zipFile = File("${bluePrintLoadConfiguration.blueprintArchivePath}/${id}.zip")
+ // zip the directory
+ BluePrintArchiveUtils.compress(blueprintFile, zipFile, true)
+
+ // TODO(Upload to the Data Base)
+
+ // After Upload to Database delete the zip file
+ zipFile.deleteOnExit()
+ }
+ } else {
+ // If the file is ZIP
+ // TODO(Upload to the Data Base)
+ }
+ return id
+ }
+
+ override fun downloadFromDataBase(name: String, version: String, path: String): String {
+ // If path ends with zip, then compress otherwise download as extracted folder
+
+ TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+ }
+
+ override fun prepareBluePrint(name: String, version: String): String {
+ val preparedPath = "${bluePrintLoadConfiguration.blueprintDeployPath}/$name/$version"
+ downloadFromDataBase(name, version, preparedPath)
+ return preparedPath
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintDatabaseLoadService.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintDatabaseLoadService.kt
new file mode 100644
index 00000000..eeea97c9
--- /dev/null
+++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintDatabaseLoadService.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.ccsdk.apps.controllerblueprints.service.load
+
+import com.att.eelf.configuration.EELFManager
+import org.springframework.boot.context.event.ApplicationReadyEvent
+import org.springframework.context.event.EventListener
+import org.springframework.stereotype.Service
+
+@Service
+open class BluePrintDatabaseLoadService(private val bluePrintLoadConfiguration: BluePrintLoadConfiguration,
+ private val modelTypeLoadService: ModelTypeLoadService,
+ private val resourceDictionaryLoadService: ResourceDictionaryLoadService,
+ private val bluePrintCatalogLoadService: BluePrintCatalogLoadService) {
+
+ private val log = EELFManager.getInstance().getLogger(BluePrintDatabaseLoadService::class.java)
+
+
+ @EventListener(ApplicationReadyEvent::class)
+ open fun init() {
+ if (bluePrintLoadConfiguration.loadInitialData) {
+ initModelTypes()
+ initResourceDictionary()
+ initBluePrintCatalog()
+ } else {
+ log.info("Initial data load is disabled")
+ }
+ }
+
+ open fun initModelTypes() {
+ log.info("model types load configuration(${bluePrintLoadConfiguration.loadModelType}) " +
+ "under paths(${bluePrintLoadConfiguration.loadModeTypePaths})")
+
+ if (bluePrintLoadConfiguration.loadModelType) {
+ val paths = bluePrintLoadConfiguration.loadModeTypePaths?.split(",")
+ paths?.let {
+ modelTypeLoadService.loadPathsModelType(paths)
+ }
+ }
+ }
+
+ open fun initResourceDictionary() {
+ log.info("resource dictionary load configuration(${bluePrintLoadConfiguration.loadResourceDictionary}) " +
+ "under paths(${bluePrintLoadConfiguration.loadResourceDictionaryPaths})")
+
+ if (bluePrintLoadConfiguration.loadResourceDictionary) {
+ val paths = bluePrintLoadConfiguration.loadResourceDictionaryPaths?.split(",")
+ paths?.let {
+ resourceDictionaryLoadService.loadPathsResourceDictionary(paths)
+ }
+ }
+ }
+
+ open fun initBluePrintCatalog() {
+ log.info("blueprint load configuration(${bluePrintLoadConfiguration.loadBluePrint}) " +
+ "under paths(${bluePrintLoadConfiguration.loadBluePrintPaths})")
+
+ if (bluePrintLoadConfiguration.loadBluePrint) {
+ val paths = bluePrintLoadConfiguration.loadBluePrintPaths?.split(",")
+ paths?.let {
+ bluePrintCatalogLoadService.loadPathsBluePrintModelCatalog(paths)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogLoad.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintLoadConfiguration.kt
index 8a5cc4c6..cf36a3e5 100644
--- a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintCatalogLoad.kt
+++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/BluePrintLoadConfiguration.kt
@@ -16,13 +16,19 @@
package org.onap.ccsdk.apps.controllerblueprints.service.load
-import org.springframework.stereotype.Service
+open class BluePrintLoadConfiguration {
-@Service
-open class BluePrintCatalogLoad {
+ lateinit var blueprintDeployPath: String
+ lateinit var blueprintArchivePath: String
+ lateinit var blueprintEnrichmentPath: String
- open fun loadBluePrintModelCatalog() {
- // TODO
- }
+ var loadInitialData: Boolean = false
+ var loadBluePrint: Boolean = false
+ var loadBluePrintPaths: String? = null
+ var loadModelType: Boolean = false
+ var loadModeTypePaths: String? = null
+
+ var loadResourceDictionary: Boolean = false
+ var loadResourceDictionaryPaths: String? = null
} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ModelTypeLoadService.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ModelTypeLoadService.kt
index ac9834b9..1c5276c8 100644
--- a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ModelTypeLoadService.kt
+++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ModelTypeLoadService.kt
@@ -36,11 +36,11 @@ open class ModelTypeLoadService(private val modelTypeService: ModelTypeService)
private val log = EELFManager.getInstance().getLogger(ModelTypeLoadService::class.java)
private val updateBySystem = "System"
- open fun loadModelType(modelTypePaths: List<String>) {
- modelTypePaths.forEach { loadModelType(it) }
+ open fun loadPathsModelType(modelTypePaths: List<String>) {
+ modelTypePaths.forEach { loadPathModelType(it) }
}
- open fun loadModelType(modelTypePath: String) {
+ open fun loadPathModelType(modelTypePath: String) {
log.info(" *************************** loadModelType **********************")
try {
val errorBuilder = StrBuilder()
diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ResourceDictionaryLoadService.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ResourceDictionaryLoadService.kt
index eddaa1cc..4bb8a2f3 100644
--- a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ResourceDictionaryLoadService.kt
+++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/service/load/ResourceDictionaryLoadService.kt
@@ -17,6 +17,9 @@
package org.onap.ccsdk.apps.controllerblueprints.service.load
import com.att.eelf.configuration.EELFManager
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.runBlocking
import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.text.StrBuilder
import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintException
@@ -33,55 +36,70 @@ open class ResourceDictionaryLoadService(private val resourceDictionaryService:
private val log = EELFManager.getInstance().getLogger(ResourceDictionaryLoadService::class.java)
- open fun loadResourceDictionary(resourceDictionaryPaths: List<String>) {
- resourceDictionaryPaths.forEach { loadResourceDictionary(it) }
+ open fun loadPathsResourceDictionary(paths: List<String>) {
+ paths.forEach { loadPathResourceDictionary(it) }
}
- open fun loadResourceDictionary(resourceDictionaryPath: String) {
+ open fun loadPathResourceDictionary(path: String) {
log.info(" *************************** loadResourceDictionary **********************")
- val resourceDictionaries = File(resourceDictionaryPath).listFiles()
- val errorBuilder = StrBuilder()
-
- resourceDictionaries.forEach { file ->
- try {
- log.trace("Loading NodeType(${file.name}")
- val definitionContent = file.readText(Charset.defaultCharset())
- val resourceDefinition = JacksonUtils.readValue(definitionContent, ResourceDefinition::class.java)
- if (resourceDefinition != null) {
-
- checkNotNull(resourceDefinition.property) { "Failed to get Property Definition" }
- val resourceDictionary = ResourceDictionary()
- resourceDictionary.name = resourceDefinition.name
- resourceDictionary.definition = resourceDefinition
-
- checkNotNull(resourceDefinition.property) { "Property field is missing" }
- resourceDictionary.description = resourceDefinition.property.description
- resourceDictionary.dataType = resourceDefinition.property.type
-
- if (resourceDefinition.property.entrySchema != null) {
- resourceDictionary.entrySchema = resourceDefinition.property.entrySchema!!.type
- }
- resourceDictionary.updatedBy = resourceDefinition.updatedBy
-
- if (StringUtils.isBlank(resourceDefinition.tags)) {
- resourceDictionary.tags = (resourceDefinition.name + ", " + resourceDefinition.updatedBy
- + ", " + resourceDefinition.updatedBy)
-
- } else {
- resourceDictionary.tags = resourceDefinition.tags
- }
- resourceDictionaryService.saveResourceDictionary(resourceDictionary)
-
- log.trace("Resource dictionary(${file.name}) loaded successfully ")
- } else {
- throw BluePrintException("couldn't get dictionary from content information")
+ val files = File(path).listFiles()
+
+ runBlocking {
+ val errorBuilder = StrBuilder()
+ val deferredResults = mutableListOf<Deferred<Unit>>()
+
+ for (file in files) {
+ deferredResults += async {
+ loadResourceDictionary(errorBuilder, file)
}
- } catch (e: Exception) {
- errorBuilder.appendln("Couldn't load Resource dictionary (${file.name}: ${e.message}")
+ }
+
+ for (deferredResult in deferredResults) {
+ deferredResult.await()
+ }
+
+ if (!errorBuilder.isEmpty) {
+ log.error(errorBuilder.toString())
}
}
- if (!errorBuilder.isEmpty) {
- log.error(errorBuilder.toString())
+ }
+
+ private fun loadResourceDictionary(errorBuilder: StrBuilder, file: File) {
+ try {
+ log.trace("Loading NodeType(${file.name}")
+ val definitionContent = file.readText(Charset.defaultCharset())
+ val resourceDefinition = JacksonUtils.readValue(definitionContent, ResourceDefinition::class.java)
+ if (resourceDefinition != null) {
+
+ checkNotNull(resourceDefinition.property) { "Failed to get Property Definition" }
+ val resourceDictionary = ResourceDictionary()
+ resourceDictionary.name = resourceDefinition.name
+ resourceDictionary.definition = resourceDefinition
+
+ checkNotNull(resourceDefinition.property) { "Property field is missing" }
+ resourceDictionary.description = resourceDefinition.property.description
+ resourceDictionary.dataType = resourceDefinition.property.type
+
+ if (resourceDefinition.property.entrySchema != null) {
+ resourceDictionary.entrySchema = resourceDefinition.property.entrySchema!!.type
+ }
+ resourceDictionary.updatedBy = resourceDefinition.updatedBy
+
+ if (StringUtils.isBlank(resourceDefinition.tags)) {
+ resourceDictionary.tags = (resourceDefinition.name + ", " + resourceDefinition.updatedBy
+ + ", " + resourceDefinition.updatedBy)
+
+ } else {
+ resourceDictionary.tags = resourceDefinition.tags
+ }
+ resourceDictionaryService.saveResourceDictionary(resourceDictionary)
+
+ log.trace("Resource dictionary(${file.name}) loaded successfully ")
+ } else {
+ throw BluePrintException("couldn't get dictionary from content information")
+ }
+ } catch (e: Exception) {
+ errorBuilder.appendln("Couldn't load Resource dictionary (${file.name}: ${e.message}")
}
}