Metadata for name, version, tags and type
[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-Version: <BLUEPRINT_VERSION>" +
236                     "\nTemplate-Type: <BLUEPRINT_TYPE>" +
237                     "\nTemplate-Tags: <TAGS>"
238         }
239
240         fun getBluePrintFile(fileName: String, targetPath: Path): File {
241             val filePath = targetPath.resolve(fileName).toString()
242             val file = File(filePath)
243             check(file.exists()) {
244                 throw BluePrintException(
245                     ErrorCode.BLUEPRINT_PATH_MISSING.value, "couldn't get definition file under " +
246                             "path(${file.absolutePath})"
247                 )
248             }
249             return file
250         }
251
252         fun getCbaStorageDirectory(path: String): Path {
253             check(StringUtils.isNotBlank(path)) {
254                 throw BluePrintException(
255                     ErrorCode.BLUEPRINT_PATH_MISSING.value, "couldn't get " +
256                             "Blueprint folder under path($path)"
257                 )
258             }
259
260             val fileStorageLocation = Paths.get(path).toAbsolutePath().normalize()
261
262             if (!Files.exists(fileStorageLocation))
263                 Files.createDirectories(fileStorageLocation)
264
265             return fileStorageLocation
266         }
267
268         fun compileCacheKey(basePath: String): String {
269             return normalizedPathName(basePath)
270         }
271
272         private fun compileJarFileName(artifactName: String, artifactVersion: String): String {
273             return "$artifactName-$artifactVersion-$COMPILED_JAR_SUFFIX"
274         }
275
276         fun compileJarFilePathName(basePath: String, artifactName: String, artifactVersion: String): String {
277             return normalizedPathName(
278                 basePath, BluePrintConstants.TOSCA_SCRIPTS_KOTLIN_DIR,
279                 compileJarFileName(artifactName, artifactVersion)
280             )
281         }
282
283         fun compileJarFile(basePath: String, artifactName: String, artifactVersion: String): File {
284             return normalizedFile(
285                 compileJarFilePathName(
286                     basePath,
287                     artifactName, artifactVersion
288                 )
289             )
290         }
291
292         fun stripFileExtension(fileName: String): String {
293             val dotIndexe = fileName.lastIndexOf('.')
294
295             // In case dot is in first position, we are dealing with a hidden file rather than an extension
296             return if (dotIndexe > 0) fileName.substring(0, dotIndexe) else fileName
297         }
298
299         fun getURLClassLoaderFromDirectory(directory: File): URLClassLoader {
300             if (!directory.exists()) {
301                 throw FileNotFoundException(directory.absolutePath)
302             } else if (!directory.isDirectory) {
303                 throw NotDirectoryException(directory.absolutePath)
304             }
305
306             val urls = arrayListOf<URL>()
307             directory.walkTopDown()
308                     .filter { it.name.endsWith(COMPILED_JAR_SUFFIX) }
309                     .forEach {
310                         log.debug("Adding (${it.absolutePath}) to classLoader (${directory.absolutePath})")
311
312                         urls.add(it.toURI().toURL())
313                     }
314             return URLClassLoader(urls.toTypedArray(), this.javaClass.classLoader)
315         }
316
317         fun getURLClassLoaderFromDirectory(path: String): URLClassLoader {
318             val directory = normalizedFile(path)
319             return getURLClassLoaderFromDirectory(directory)
320         }
321     }
322 }