summaryrefslogtreecommitdiffstats
path: root/ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/scripts/BluePrintCompileService.kt
diff options
context:
space:
mode:
Diffstat (limited to 'ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/scripts/BluePrintCompileService.kt')
-rw-r--r--ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/scripts/BluePrintCompileService.kt187
1 files changed, 187 insertions, 0 deletions
diff --git a/ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/scripts/BluePrintCompileService.kt b/ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/scripts/BluePrintCompileService.kt
new file mode 100644
index 000000000..230097f3c
--- /dev/null
+++ b/ms/blueprintsprocessor/modules/blueprints/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/scripts/BluePrintCompileService.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.cds.controllerblueprints.core.scripts
+
+import com.google.common.cache.CacheBuilder
+import com.google.common.cache.CacheLoader
+import com.google.common.cache.LoadingCache
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import org.jetbrains.kotlin.cli.common.ExitCode
+import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
+import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
+import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
+import org.jetbrains.kotlin.cli.common.messages.MessageCollector
+import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
+import org.jetbrains.kotlin.config.Services
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
+import org.onap.ccsdk.cds.controllerblueprints.core.checkFileExists
+import org.onap.ccsdk.cds.controllerblueprints.core.logger
+import java.io.File
+import java.net.URLClassLoader
+import java.util.ArrayList
+import kotlin.script.experimental.api.SourceCode
+import kotlin.script.experimental.jvm.util.classpathFromClasspathProperty
+import kotlin.system.measureTimeMillis
+
+open class BluePrintCompileService {
+
+ val log = logger(BluePrintCompileService::class)
+
+ companion object {
+
+ val classPaths = classpathFromClasspathProperty()?.joinToString(File.pathSeparator) {
+ it.absolutePath
+ }
+ val mutexCache: LoadingCache<String, Mutex> = CacheBuilder.newBuilder()
+ .build(CacheLoader.from { s -> Mutex() })
+ }
+
+ /** Compile the [bluePrintSourceCode] and get the [kClassName] instance for the constructor [args] */
+ suspend fun <T> eval(
+ bluePrintSourceCode: BluePrintSourceCode,
+ kClassName: String,
+ args: ArrayList<Any?>?
+ ): T {
+ /** Compile the source code if needed */
+ log.debug("Jar Exists : ${bluePrintSourceCode.targetJarFile.exists()}, Regenerate : ${bluePrintSourceCode.regenerate}")
+
+ mutexCache.get(bluePrintSourceCode.targetJarFile.absolutePath).withLock {
+ if (!bluePrintSourceCode.targetJarFile.exists() || bluePrintSourceCode.regenerate) {
+ compile(bluePrintSourceCode)
+ }
+ }
+
+ val classLoaderWithDependencies = BluePrintCompileCache.classLoader(bluePrintSourceCode.cacheKey)
+
+ /** Create the instance from the class loader */
+ return instance(classLoaderWithDependencies, kClassName, args)
+ }
+
+ /** Compile [bluePrintSourceCode] and put into cache */
+ suspend fun compile(bluePrintSourceCode: BluePrintSourceCode) {
+ // TODO("Include Multiple folders")
+ val sourcePath = bluePrintSourceCode.blueprintKotlinSources.first()
+ val compiledJarFile = bluePrintSourceCode.targetJarFile
+
+ log.info("compiling for cache key(${bluePrintSourceCode.cacheKey})")
+ coroutineScope {
+ val timeTaken = measureTimeMillis {
+ /** Create compile arguments */
+ val args = mutableListOf<String>().apply {
+ add("-no-stdlib")
+ add("-no-reflect")
+ add("-module-name")
+ add(bluePrintSourceCode.moduleName)
+ add("-cp")
+ add(classPaths!!)
+ add(sourcePath)
+ add("-d")
+ add(compiledJarFile.absolutePath)
+ }
+ val deferredCompile = async {
+ val k2jvmCompiler = K2JVMCompiler()
+
+ /** Construct Arguments */
+ val arguments = k2jvmCompiler.createArguments()
+ parseCommandLineArguments(args, arguments)
+ val messageCollector = CompilationMessageCollector()
+
+ /** Compile with arguments */
+ val exitCode: ExitCode = k2jvmCompiler.exec(messageCollector, Services.EMPTY, arguments)
+ when (exitCode) {
+ ExitCode.OK -> {
+ checkFileExists(compiledJarFile) { "couldn't generate compiled jar(${compiledJarFile.absolutePath})" }
+ }
+ else -> {
+ throw BluePrintException("$exitCode :${messageCollector.errors().joinToString("\n")}")
+ }
+ }
+ }
+ deferredCompile.await()
+ }
+ log.info("compiled in ($timeTaken)mSec for cache key(${bluePrintSourceCode.cacheKey})")
+ }
+ }
+
+ /** create class [kClassName] instance from [classLoader] */
+ fun <T> instance(classLoader: URLClassLoader, kClassName: String, args: ArrayList<Any?>? = arrayListOf()): T {
+ val kClazz = classLoader.loadClass(kClassName)
+ ?: throw BluePrintException("failed to load class($kClassName) from current class loader.")
+
+ val instance = if (args.isNullOrEmpty()) {
+ kClazz.newInstance()
+ } else {
+ kClazz.constructors
+ .single().newInstance(*args.toArray())
+ } ?: throw BluePrintException("failed to create class($kClassName) instance for constructor argument($args).")
+
+ return instance as T
+ }
+}
+
+/** Compile source code information */
+open class BluePrintSourceCode : SourceCode {
+
+ lateinit var blueprintKotlinSources: MutableList<String>
+ lateinit var moduleName: String
+ lateinit var targetJarFile: File
+ lateinit var cacheKey: String
+ var regenerate: Boolean = false
+
+ override val text: String
+ get() = ""
+
+ override val locationId: String? = null
+
+ override val name: String?
+ get() = moduleName
+}
+
+/** Class to collect compilation Data */
+data class CompiledMessageData(
+ val severity: CompilerMessageSeverity,
+ val message: String,
+ val location: CompilerMessageLocation?
+)
+
+/** Class to collect compilation results */
+class CompilationMessageCollector : MessageCollector {
+
+ private val compiledMessages: MutableList<CompiledMessageData> = arrayListOf()
+
+ override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
+ synchronized(compiledMessages) {
+ compiledMessages.add(CompiledMessageData(severity, message, location))
+ }
+ }
+
+ override fun hasErrors() =
+ synchronized(compiledMessages) {
+ compiledMessages.any { it.severity.isError }
+ }
+
+ override fun clear() {
+ synchronized(compiledMessages) {
+ compiledMessages.clear()
+ }
+ }
+
+ fun errors(): List<CompiledMessageData> = compiledMessages.filter { it.severity == CompilerMessageSeverity.ERROR }
+}