9e1047389e7e058a3ec7b991cfef6e08d9fc15db
[ccsdk/cds.git] / ms / blueprintsprocessor / modules / blueprints / blueprint-core / src / main / kotlin / org / onap / ccsdk / cds / controllerblueprints / core / utils / BluePrintFileUtils.kt
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Modifications Copyright © 2019 Bell Canada.
4  * Modifications Copyright © 2019 IBM.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 package org.onap.ccsdk.cds.controllerblueprints.core.utils
20
21 import kotlinx.coroutines.runBlocking
22 import org.apache.commons.io.FileUtils
23 import org.apache.commons.lang3.StringUtils
24 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
25 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
26 import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode
27 import org.onap.ccsdk.cds.controllerblueprints.core.data.ImportDefinition
28 import org.onap.ccsdk.cds.controllerblueprints.core.data.ServiceTemplate
29 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedFile
30 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedPathName
31 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext
32 import org.slf4j.LoggerFactory
33 import java.io.File
34 import java.io.FileFilter
35 import java.io.FileNotFoundException
36 import java.net.URL
37 import java.net.URLClassLoader
38 import java.nio.file.Files
39 import java.nio.file.NotDirectoryException
40 import java.nio.file.Path
41 import java.nio.file.Paths
42 import java.nio.file.StandardOpenOption
43
44 class BluePrintFileUtils {
45     companion object {
46
47         const val COMPILED_JAR_SUFFIX = "cba-kts.jar"
48
49         private val log = LoggerFactory.getLogger(this::class.toString())
50
51         fun createEmptyBluePrint(basePath: String) {
52
53             val blueprintDir = File(basePath)
54             FileUtils.deleteDirectory(blueprintDir)
55
56             Files.createDirectories(blueprintDir.toPath())
57
58             val metaDataDir = File(blueprintDir.absolutePath.plus(File.separator).plus(BluePrintConstants.TOSCA_METADATA_DIR))
59             Files.createDirectories(metaDataDir.toPath())
60
61             val metaFile = File(
62                 blueprintDir.absolutePath.plus(File.separator).plus(
63                     BluePrintConstants
64                         .TOSCA_METADATA_ENTRY_DEFINITION_FILE
65                 )
66             )
67             Files.write(metaFile.toPath(), getMetaDataContent().toByteArray(), StandardOpenOption.CREATE_NEW)
68
69             val definitionsDir = File(blueprintDir.absolutePath.plus(File.separator).plus(BluePrintConstants.TOSCA_DEFINITIONS_DIR))
70             Files.createDirectories(definitionsDir.toPath())
71
72             val scriptsDir = File(blueprintDir.absolutePath.plus(File.separator).plus(BluePrintConstants.TOSCA_SCRIPTS_DIR))
73             Files.createDirectories(scriptsDir.toPath())
74
75             val plansDir = File(blueprintDir.absolutePath.plus(File.separator).plus(BluePrintConstants.TOSCA_PLANS_DIR))
76             Files.createDirectories(plansDir.toPath())
77
78             val templatesDir = File(blueprintDir.absolutePath.plus(File.separator).plus(BluePrintConstants.TOSCA_TEMPLATES_DIR))
79             Files.createDirectories(templatesDir.toPath())
80         }
81
82         fun copyBluePrint(sourcePath: String, targetPath: String) {
83             val sourceFile = File(sourcePath)
84             val targetFile = File(targetPath)
85             sourceFile.copyRecursively(targetFile, true)
86         }
87
88         fun deleteBluePrintTypes(basePath: String) {
89             val definitionPath = basePath.plus(File.separator).plus(BluePrintConstants.TOSCA_DEFINITIONS_DIR)
90             log.info("deleting definition types under : $definitionPath")
91
92             val definitionDir = File(definitionPath)
93             // Find the Type Definitions
94             val fileFilter = FileFilter { pathname -> pathname.absolutePath.endsWith("_types.json") }
95             // Delete the Type Files
96             definitionDir.listFiles(fileFilter).forEach {
97                 Files.deleteIfExists(it.toPath())
98             }
99         }
100
101         fun writeEnhancedBluePrint(blueprintContext: BluePrintContext) {
102
103             // Write Blueprint Types
104             writeBluePrintTypes(blueprintContext)
105             // Re Populate the Imports
106             populateDefaultImports(blueprintContext)
107             // Rewrite the Entry Definition Files
108             writeEntryDefinitionFile(blueprintContext)
109         }
110
111         fun writeBluePrintTypes(blueprintContext: BluePrintContext) {
112
113             val basePath = blueprintContext.rootPath
114             val definitionPath = basePath.plus(File.separator).plus(BluePrintConstants.TOSCA_DEFINITIONS_DIR)
115             val definitionDir = File(definitionPath)
116
117             check(definitionDir.exists()) {
118                 throw BluePrintException(
119                     ErrorCode.BLUEPRINT_PATH_MISSING.value, "couldn't get definition file under " +
120                             "path(${definitionDir.absolutePath})"
121                 )
122             }
123
124             blueprintContext.serviceTemplate.dataTypes?.let {
125                 val dataTypesContent = JacksonUtils.getWrappedJson(BluePrintConstants.PATH_DATA_TYPES, it.toSortedMap(), true)
126                 writeTypeFile(definitionDir.absolutePath, BluePrintConstants.PATH_DATA_TYPES, dataTypesContent)
127             }
128
129             blueprintContext.serviceTemplate.relationshipTypes?.let {
130                 val nodeTypesContent = JacksonUtils.getWrappedJson(BluePrintConstants.PATH_RELATIONSHIP_TYPES, it.toSortedMap(), true)
131                 writeTypeFile(definitionDir.absolutePath, BluePrintConstants.PATH_RELATIONSHIP_TYPES, nodeTypesContent)
132             }
133
134             blueprintContext.serviceTemplate.artifactTypes?.let {
135                 val artifactTypesContent = JacksonUtils.getWrappedJson(BluePrintConstants.PATH_ARTIFACT_TYPES, it.toSortedMap(), true)
136                 writeTypeFile(definitionDir.absolutePath, BluePrintConstants.PATH_ARTIFACT_TYPES, artifactTypesContent)
137             }
138
139             blueprintContext.serviceTemplate.nodeTypes?.let {
140                 val nodeTypesContent = JacksonUtils.getWrappedJson(BluePrintConstants.PATH_NODE_TYPES, it.toSortedMap(), true)
141                 writeTypeFile(definitionDir.absolutePath, BluePrintConstants.PATH_NODE_TYPES, nodeTypesContent)
142             }
143
144             blueprintContext.serviceTemplate.policyTypes?.let {
145                 val nodeTypesContent = JacksonUtils.getWrappedJson(BluePrintConstants.PATH_POLICY_TYPES, it.toSortedMap(), true)
146                 writeTypeFile(definitionDir.absolutePath, BluePrintConstants.PATH_POLICY_TYPES, nodeTypesContent)
147             }
148         }
149
150         private fun populateDefaultImports(blueprintContext: BluePrintContext) {
151             // Get the Default Types
152             val types = arrayListOf(
153                 BluePrintConstants.PATH_DATA_TYPES, BluePrintConstants.PATH_RELATIONSHIP_TYPES,
154                 BluePrintConstants.PATH_ARTIFACT_TYPES, BluePrintConstants.PATH_NODE_TYPES,
155                 BluePrintConstants.PATH_POLICY_TYPES
156             )
157
158             // Clean Type Imports
159             cleanImportTypes(blueprintContext.serviceTemplate)
160
161             val imports = mutableListOf<ImportDefinition>()
162             types.forEach { typeName ->
163                 val import = ImportDefinition()
164                 import.file = BluePrintConstants.TOSCA_DEFINITIONS_DIR.plus("/$typeName.json")
165                 imports.add(import)
166             }
167
168             blueprintContext.serviceTemplate.imports = imports
169         }
170
171         fun cleanImportTypes(serviceTemplate: ServiceTemplate) {
172             // Clean the Type imports
173             val toDeleteTypes = serviceTemplate.imports?.filter {
174                 it.file.endsWith("_types.json")
175             }
176
177             if (toDeleteTypes != null && toDeleteTypes.isNotEmpty()) {
178                 serviceTemplate.imports?.removeAll(toDeleteTypes)
179             }
180         }
181
182         /**
183          * Re Generate the Blueprint Service Template Definition file based on BluePrint Context.
184          */
185         private fun writeEntryDefinitionFile(blueprintContext: BluePrintContext) {
186
187             val absoluteEntryDefinitionFile = blueprintContext.rootPath.plus(File.separator).plus(blueprintContext.entryDefinition)
188
189             val serviceTemplate = blueprintContext.serviceTemplate
190
191             // Clone the Service Template
192             val writeServiceTemplate = serviceTemplate.clone()
193             writeServiceTemplate.dataTypes = null
194             writeServiceTemplate.artifactTypes = null
195             writeServiceTemplate.policyTypes = null
196             writeServiceTemplate.relationshipTypes = null
197             writeServiceTemplate.nodeTypes = null
198
199             // Write the Service Template
200             writeDefinitionFile(absoluteEntryDefinitionFile, JacksonUtils.getJson(writeServiceTemplate, true))
201         }
202
203         fun writeDefinitionFile(definitionFileName: String, content: String) = runBlocking {
204             val definitionFile = File(definitionFileName)
205             // Delete the File If exists
206             Files.deleteIfExists(definitionFile.toPath())
207
208             Files.write(definitionFile.toPath(), content.toByteArray(), StandardOpenOption.CREATE_NEW)
209             check(definitionFile.exists()) {
210                 throw BluePrintException(
211                     ErrorCode.BLUEPRINT_WRITING_FAIL.value, "couldn't write definition file under " +
212                             "path(${definitionFile.absolutePath})"
213                 )
214             }
215         }
216
217         private fun writeTypeFile(definitionPath: String, type: String, content: String) = runBlocking {
218             val typeFile = File(definitionPath.plus(File.separator).plus("$type.json"))
219
220             Files.write(typeFile.toPath(), content.toByteArray(), StandardOpenOption.CREATE_NEW)
221             check(typeFile.exists()) {
222                 throw BluePrintException(
223                     ErrorCode.BLUEPRINT_WRITING_FAIL.value, "couldn't write $type.json file under " +
224                             "path(${typeFile.absolutePath})"
225                 )
226             }
227         }
228
229         private fun getMetaDataContent(): String {
230             return "TOSCA-Meta-File-Version: 1.0.0" +
231                     "\nCSAR-Version: <VERSION>" +
232                     "\nCreated-By: <AUTHOR NAME>" +
233                     "\nEntry-Definitions: Definitions/<BLUEPRINT_NAME>.json" +
234                     "\nTemplate-Name: <BLUEPRINT_NAME>" +
235                     "\nTemplate-Tags: <BLUEPRINT_VERSION>" +
236                     "\nTemplate-Tags: <TAGS>"
237         }
238
239         fun getBluePrintFile(fileName: String, targetPath: Path): File {
240             val filePath = targetPath.resolve(fileName).toString()
241             val file = File(filePath)
242             check(file.exists()) {
243                 throw BluePrintException(
244                     ErrorCode.BLUEPRINT_PATH_MISSING.value, "couldn't get definition file under " +
245                             "path(${file.absolutePath})"
246                 )
247             }
248             return file
249         }
250
251         fun getCbaStorageDirectory(path: String): Path {
252             check(StringUtils.isNotBlank(path)) {
253                 throw BluePrintException(
254                     ErrorCode.BLUEPRINT_PATH_MISSING.value, "couldn't get " +
255                             "Blueprint folder under path($path)"
256                 )
257             }
258
259             val fileStorageLocation = Paths.get(path).toAbsolutePath().normalize()
260
261             if (!Files.exists(fileStorageLocation))
262                 Files.createDirectories(fileStorageLocation)
263
264             return fileStorageLocation
265         }
266
267         fun compileCacheKey(basePath: String): String {
268             return normalizedPathName(basePath)
269         }
270
271         private fun compileJarFileName(artifactName: String, artifactVersion: String): String {
272             return "$artifactName-$artifactVersion-$COMPILED_JAR_SUFFIX"
273         }
274
275         fun compileJarFilePathName(basePath: String, artifactName: String, artifactVersion: String): String {
276             return normalizedPathName(
277                 basePath, BluePrintConstants.TOSCA_SCRIPTS_KOTLIN_DIR,
278                 compileJarFileName(artifactName, artifactVersion)
279             )
280         }
281
282         fun compileJarFile(basePath: String, artifactName: String, artifactVersion: String): File {
283             return normalizedFile(
284                 compileJarFilePathName(
285                     basePath,
286                     artifactName, artifactVersion
287                 )
288             )
289         }
290
291         fun stripFileExtension(fileName: String): String {
292             val dotIndexe = fileName.lastIndexOf('.')
293
294             // In case dot is in first position, we are dealing with a hidden file rather than an extension
295             return if (dotIndexe > 0) fileName.substring(0, dotIndexe) else fileName
296         }
297
298         fun getURLClassLoaderFromDirectory(directory: File): URLClassLoader {
299             if (!directory.exists()) {
300                 throw FileNotFoundException(directory.absolutePath)
301             } else if (!directory.isDirectory) {
302                 throw NotDirectoryException(directory.absolutePath)
303             }
304
305             val urls = arrayListOf<URL>()
306             directory.walkTopDown()
307                     .filter { it.name.endsWith(COMPILED_JAR_SUFFIX) }
308                     .forEach {
309                         log.debug("Adding (${it.absolutePath}) to classLoader (${directory.absolutePath})")
310
311                         urls.add(it.toURI().toURL())
312                     }
313             return URLClassLoader(urls.toTypedArray(), this.javaClass.classLoader)
314         }
315
316         fun getURLClassLoaderFromDirectory(path: String): URLClassLoader {
317             val directory = normalizedFile(path)
318             return getURLClassLoaderFromDirectory(directory)
319         }
320     }
321 }