diff --git a/src/main/kotlin/Orchestrator.kt b/src/main/kotlin/Orchestrator.kt index 1a1669c..9629939 100644 --- a/src/main/kotlin/Orchestrator.kt +++ b/src/main/kotlin/Orchestrator.kt @@ -9,9 +9,9 @@ import kotlinx.coroutines.withTimeout import runner.Runner import runner.impl.NodeRunner import runner.jvm.JVMRunner -import technology.idlab.parser.intermediate.IRPipeline -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage +import technology.idlab.intermediate.IRPipeline +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage import technology.idlab.util.Log class Orchestrator(private val pipeline: IRPipeline, processors: List) { diff --git a/src/main/kotlin/extensions/File.kt b/src/main/kotlin/extensions/File.kt deleted file mode 100644 index 3206814..0000000 --- a/src/main/kotlin/extensions/File.kt +++ /dev/null @@ -1,18 +0,0 @@ -package technology.idlab.extensions - -import java.io.File -import org.apache.jena.ontology.OntModelSpec -import org.apache.jena.rdf.model.Model -import org.apache.jena.rdf.model.ModelFactory -import technology.idlab.util.Log - -/** - * Read a model from a file and recursively import all referenced ontologies based on - * statements. - */ -internal fun File.readModelRecursively(): Model { - val onthology = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM) - Log.shared.info("Importing file://${this.absolutePath}") - onthology.read(this.toURI().toString(), "TURTLE") - return onthology -} diff --git a/src/main/kotlin/extensions/Model.kt b/src/main/kotlin/extensions/Model.kt index 3154ee2..3388393 100644 --- a/src/main/kotlin/extensions/Model.kt +++ b/src/main/kotlin/extensions/Model.kt @@ -8,23 +8,31 @@ import org.apache.jena.rdf.model.Resource import org.apache.jena.shacl.ShaclValidator import technology.idlab.util.Log -/** Validates a model against the SHACL schema defined inside the model itself. */ -internal fun Model.validate(): Model { +/** + * Given an Apache Jena model, run the SHACL validation engine against itself. This means that all + * shapes embedded in the model will be used to validate the model itself. If the validation fails, + * the program will exit with a fatal error. + */ +internal fun Model.validate() { + // SHACL runs against the graph, so we need to convert first. Then, simply call a new validation + // instance and test the graph against itself. val graph = this.graph val report = ShaclValidator.get().validate(graph, graph) - // Exit if the validation failed. + // Exit if the validation failed by logging the report. if (!report.conforms()) { val out = ByteArrayOutputStream() report.model.write(out, "TURTLE") Log.shared.fatal("Validation failed\n$out") } - - return this } /** * Return the first object which corresponds to a subject and predicate. Returns null if not found. + * + * @param resource The subject of the query. + * @param property The predicate of the query. + * @return The first result of the query, or null if not found. */ internal fun Model.objectOfProperty(resource: Resource, property: Property): RDFNode? { return try { @@ -36,6 +44,10 @@ internal fun Model.objectOfProperty(resource: Resource, property: Property): RDF /** * Return the first subject which corresponds to a predicate and object. Returns null if not found. + * + * @param property The predicate of the query. + * @param obj The object of the query. + * @return The first result of the query, or null if not found. */ internal fun Model.subjectWithProperty(property: Property, obj: RDFNode): Resource? { return try { diff --git a/src/main/kotlin/parser/intermediate/IRArgument.kt b/src/main/kotlin/intermediate/IRArgument.kt similarity index 96% rename from src/main/kotlin/parser/intermediate/IRArgument.kt rename to src/main/kotlin/intermediate/IRArgument.kt index b224a0a..6dc8ccb 100644 --- a/src/main/kotlin/parser/intermediate/IRArgument.kt +++ b/src/main/kotlin/intermediate/IRArgument.kt @@ -1,4 +1,4 @@ -package technology.idlab.parser.intermediate +package technology.idlab.intermediate import technology.idlab.util.Log diff --git a/src/main/kotlin/parser/intermediate/IRDependency.kt b/src/main/kotlin/intermediate/IRDependency.kt similarity index 76% rename from src/main/kotlin/parser/intermediate/IRDependency.kt rename to src/main/kotlin/intermediate/IRDependency.kt index b91cea3..309e7b2 100644 --- a/src/main/kotlin/parser/intermediate/IRDependency.kt +++ b/src/main/kotlin/intermediate/IRDependency.kt @@ -1,4 +1,4 @@ -package technology.idlab.parser.intermediate +package technology.idlab.intermediate /** A dependency as listed in the configuration file. */ data class IRDependency( diff --git a/src/main/kotlin/parser/intermediate/IRPackage.kt b/src/main/kotlin/intermediate/IRPackage.kt similarity index 93% rename from src/main/kotlin/parser/intermediate/IRPackage.kt rename to src/main/kotlin/intermediate/IRPackage.kt index dbd79fc..9b06bcc 100644 --- a/src/main/kotlin/parser/intermediate/IRPackage.kt +++ b/src/main/kotlin/intermediate/IRPackage.kt @@ -1,4 +1,4 @@ -package technology.idlab.parser.intermediate +package technology.idlab.intermediate /** A resolved dependency, with all of its configuration parameters. */ data class IRPackage( diff --git a/src/main/kotlin/parser/intermediate/IRParameter.kt b/src/main/kotlin/intermediate/IRParameter.kt similarity index 97% rename from src/main/kotlin/parser/intermediate/IRParameter.kt rename to src/main/kotlin/intermediate/IRParameter.kt index a3e35d9..7951b52 100644 --- a/src/main/kotlin/parser/intermediate/IRParameter.kt +++ b/src/main/kotlin/intermediate/IRParameter.kt @@ -1,4 +1,4 @@ -package technology.idlab.parser.intermediate +package technology.idlab.intermediate import technology.idlab.util.Log diff --git a/src/main/kotlin/parser/intermediate/IRPipeline.kt b/src/main/kotlin/intermediate/IRPipeline.kt similarity index 72% rename from src/main/kotlin/parser/intermediate/IRPipeline.kt rename to src/main/kotlin/intermediate/IRPipeline.kt index f2bcddf..4ee9d34 100644 --- a/src/main/kotlin/parser/intermediate/IRPipeline.kt +++ b/src/main/kotlin/intermediate/IRPipeline.kt @@ -1,4 +1,4 @@ -package technology.idlab.parser.intermediate +package technology.idlab.intermediate data class IRPipeline( val uri: String, diff --git a/src/main/kotlin/parser/intermediate/IRProcessor.kt b/src/main/kotlin/intermediate/IRProcessor.kt similarity index 89% rename from src/main/kotlin/parser/intermediate/IRProcessor.kt rename to src/main/kotlin/intermediate/IRProcessor.kt index c082a99..c1f4150 100644 --- a/src/main/kotlin/parser/intermediate/IRProcessor.kt +++ b/src/main/kotlin/intermediate/IRProcessor.kt @@ -1,4 +1,4 @@ -package technology.idlab.parser.intermediate +package technology.idlab.intermediate import runner.Runner diff --git a/src/main/kotlin/parser/intermediate/IRStage.kt b/src/main/kotlin/intermediate/IRStage.kt similarity index 96% rename from src/main/kotlin/parser/intermediate/IRStage.kt rename to src/main/kotlin/intermediate/IRStage.kt index 11d5f47..54d0d13 100644 --- a/src/main/kotlin/parser/intermediate/IRStage.kt +++ b/src/main/kotlin/intermediate/IRStage.kt @@ -1,4 +1,4 @@ -package technology.idlab.parser.intermediate +package technology.idlab.intermediate import arrow.core.zip diff --git a/src/main/kotlin/parser/Ontology.kt b/src/main/kotlin/parser/Ontology.kt deleted file mode 100644 index 0c15f16..0000000 --- a/src/main/kotlin/parser/Ontology.kt +++ /dev/null @@ -1,29 +0,0 @@ -package technology.idlab.parser - -import org.apache.jena.rdf.model.ResourceFactory.createProperty -import org.apache.jena.rdf.model.ResourceFactory.createResource - -internal class RDFC { - companion object { - private const val NS = "https://www.rdf-connect.com/#" - val NAMESPACE = createResource(NS)!! - val processor = createProperty("${NS}Processor")!! - val `package` = createProperty("${NS}Package")!! - val stage = createProperty("${NS}stage")!! - val channel = createProperty("${NS}Channel")!! - val target = createProperty("${NS}target")!! - val metadata = createProperty("${NS}metadata")!! - val arguments = createProperty("${NS}arguments")!! - val kotlinRunner = createResource("${NS}Kotlin")!! - val dependency = createProperty("${NS}dependency")!! - val version = createProperty("${NS}version")!! - val author = createProperty("${NS}author")!! - val description = createProperty("${NS}description")!! - val repo = createProperty("${NS}repo")!! - val license = createProperty("${NS}license")!! - val prepare = createProperty("${NS}prepare")!! - val processors = createProperty("${NS}processors")!! - val pipeline = createProperty("${NS}Pipeline")!! - val stages = createProperty("${NS}stages")!! - } -} diff --git a/src/main/kotlin/parser/Parser.kt b/src/main/kotlin/parser/Parser.kt index 5da4358..520fb30 100644 --- a/src/main/kotlin/parser/Parser.kt +++ b/src/main/kotlin/parser/Parser.kt @@ -1,16 +1,16 @@ package technology.idlab.parser import java.io.File +import technology.idlab.intermediate.IRPackage +import technology.idlab.intermediate.IRPipeline +import technology.idlab.intermediate.IRProcessor import technology.idlab.parser.impl.JenaParser -import technology.idlab.parser.intermediate.IRPackage -import technology.idlab.parser.intermediate.IRPipeline -import technology.idlab.parser.intermediate.IRProcessor /** * Parse an RDF file into an intermediate representation, and validate it against the ontology and * SHACL shapes. */ -abstract class Parser(file: File) { +abstract class Parser { /** The pipelines in the current configuration. */ abstract val pipelines: List diff --git a/src/main/kotlin/parser/impl/JenaParser.kt b/src/main/kotlin/parser/impl/JenaParser.kt index 542f5b6..f7b2548 100644 --- a/src/main/kotlin/parser/impl/JenaParser.kt +++ b/src/main/kotlin/parser/impl/JenaParser.kt @@ -6,25 +6,51 @@ import org.apache.jena.rdf.model.Model import org.apache.jena.rdf.model.ModelFactory import org.apache.jena.rdf.model.RDFNode import org.apache.jena.rdf.model.Resource +import org.apache.jena.rdf.model.ResourceFactory.createProperty +import org.apache.jena.rdf.model.ResourceFactory.createResource import org.apache.jena.shacl.vocabulary.SHACLM import org.apache.jena.vocabulary.RDF import runner.Runner import technology.idlab.extensions.objectOfProperty import technology.idlab.extensions.subjectWithProperty import technology.idlab.extensions.validate +import technology.idlab.intermediate.IRArgument +import technology.idlab.intermediate.IRDependency +import technology.idlab.intermediate.IRPackage +import technology.idlab.intermediate.IRParameter +import technology.idlab.intermediate.IRPipeline +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage import technology.idlab.parser.Parser -import technology.idlab.parser.RDFC -import technology.idlab.parser.intermediate.IRArgument -import technology.idlab.parser.intermediate.IRDependency -import technology.idlab.parser.intermediate.IRPackage -import technology.idlab.parser.intermediate.IRParameter -import technology.idlab.parser.intermediate.IRPipeline -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage import technology.idlab.resolver.Resolver import technology.idlab.util.Log -internal fun Resource.toRunnerTarget(): Runner.Target { +private class RDFC { + companion object { + private const val NS = "https://www.rdf-connect.com/#" + val NAMESPACE = createResource(NS)!! + val processor = createProperty("${NS}Processor")!! + val `package` = createProperty("${NS}Package")!! + val stage = createProperty("${NS}stage")!! + val channel = createProperty("${NS}Channel")!! + val target = createProperty("${NS}target")!! + val metadata = createProperty("${NS}metadata")!! + val arguments = createProperty("${NS}arguments")!! + val kotlinRunner = createResource("${NS}Kotlin")!! + val dependency = createProperty("${NS}dependency")!! + val version = createProperty("${NS}version")!! + val author = createProperty("${NS}author")!! + val description = createProperty("${NS}description")!! + val repo = createProperty("${NS}repo")!! + val license = createProperty("${NS}license")!! + val prepare = createProperty("${NS}prepare")!! + val processors = createProperty("${NS}processors")!! + val pipeline = createProperty("${NS}Pipeline")!! + val stages = createProperty("${NS}stages")!! + } +} + +private fun Resource.toRunnerTarget(): Runner.Target { return when (this) { RDFC.kotlinRunner -> Runner.Target.JVM else -> Log.shared.fatal("Unknown runner type: $this") @@ -35,7 +61,7 @@ internal fun Resource.toRunnerTarget(): Runner.Target { * Maps a resource to an IRParameter.Type based on the URI. Note that this implementation is * actually quite slow, and we should probably use Apache Jena native APIs here. */ -internal fun Resource.toIRParameterType(): IRParameter.Type { +private fun Resource.toIRParameterType(): IRParameter.Type { return when (this.uri) { "http://www.w3.org/2001/XMLSchema#boolean" -> IRParameter.Type.BOOLEAN "http://www.w3.org/2001/XMLSchema#byte" -> IRParameter.Type.BYTE @@ -55,7 +81,7 @@ internal fun Resource.toIRParameterType(): IRParameter.Type { * Create a mapping of String to IRParameter from a SHACL property. This is a recursive * implementation that will automatically parse nested classes. */ -internal fun Model.parseSHACLProperty(property: Resource): Pair { +private fun Model.parseSHACLProperty(property: Resource): Pair { // Retrieve required fields. val minCount = objectOfProperty(property, SHACLM.minCount)?.asLiteral()?.int val maxCount = objectOfProperty(property, SHACLM.maxCount)?.asLiteral()?.int @@ -104,7 +130,7 @@ internal fun Model.parseSHACLProperty(property: Resource): Pair { +private fun Model.parseSHACLShape(shape: Resource): Map { val result = mutableMapOf() for (property in listObjectsOfProperty(shape, SHACLM.property)) { @@ -115,7 +141,7 @@ internal fun Model.parseSHACLShape(shape: Resource): Map { return result } -internal fun Model.nameOfSHACLPath(path: Resource): String { +private fun Model.nameOfSHACLPath(path: Resource): String { val property = subjectWithProperty(SHACLM.path, path) ?: Log.shared.fatal("No property found for path: $path") @@ -127,7 +153,7 @@ internal fun Model.nameOfSHACLPath(path: Resource): String { * Parse the arguments of a stage. This is a recursive implementation that will automatically parse * nested classes. Recursion will continue until all objects found are literals. */ -internal fun Model.parseArguments(node: Resource): Map { +private fun Model.parseArguments(node: Resource): Map { val simple = mutableMapOf>() val complex = mutableMapOf>>() @@ -154,7 +180,7 @@ internal fun Model.parseArguments(node: Resource): Map { complex.mapValues { (_, value) -> IRArgument(complex = value) } } -internal fun Model.parseProcessor(processor: Resource): IRProcessor { +private fun Model.parseProcessor(processor: Resource): IRProcessor { val uri = processor.toString() // Determine the target runner. @@ -191,7 +217,7 @@ internal fun Model.parseProcessor(processor: Resource): IRProcessor { return IRProcessor(uri, target, parameters, metadata) } -internal fun Model.parseStages(pipeline: Resource): List { +private fun Model.parseStages(pipeline: Resource): List { return listObjectsOfProperty(pipeline, RDFC.stages).toList().map { stage -> val processor = objectOfProperty(stage.asResource(), RDF.type)!!.asResource() val arguments = objectOfProperty(stage.asResource(), RDFC.arguments)!!.asResource() @@ -199,13 +225,13 @@ internal fun Model.parseStages(pipeline: Resource): List { } } -internal fun Model.parseDependencies(pipeline: Resource?): List { +private fun Model.parseDependencies(pipeline: Resource?): List { return listObjectsOfProperty(pipeline, RDFC.dependency).toList().map { dependency -> IRDependency(uri = dependency.toString()) } } -internal fun Model.parsePackage(pkg: Resource): IRPackage { +private fun Model.parsePackage(pkg: Resource): IRPackage { // Get all of its properties. val version = objectOfProperty(pkg, RDFC.version) val author = objectOfProperty(pkg, RDFC.author) @@ -228,11 +254,11 @@ internal fun Model.parsePackage(pkg: Resource): IRPackage { ) } -internal fun Model.parsePackages(): List { +private fun Model.parsePackages(): List { return listSubjectsWithProperty(RDF.type, RDFC.`package`).toList().map { parsePackage(it) } } -internal fun Model.parsePipeline(pipeline: Resource): IRPipeline { +private fun Model.parsePipeline(pipeline: Resource): IRPipeline { return IRPipeline( uri = pipeline.uri, stages = parseStages(pipeline), @@ -240,11 +266,11 @@ internal fun Model.parsePipeline(pipeline: Resource): IRPipeline { ) } -internal fun Model.parsePipelines(): List { +private fun Model.parsePipelines(): List { return listSubjectsWithProperty(RDF.type, RDFC.pipeline).toList().map { parsePipeline(it) } } -class JenaParser(file: File) : Parser(file) { +class JenaParser(file: File) : Parser() { /** The Apache Jena model. */ private val model: Model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM) diff --git a/src/main/kotlin/resolver/Resolver.kt b/src/main/kotlin/resolver/Resolver.kt index 8ddf3ee..c7dcbb7 100644 --- a/src/main/kotlin/resolver/Resolver.kt +++ b/src/main/kotlin/resolver/Resolver.kt @@ -2,7 +2,7 @@ package technology.idlab.resolver import java.io.File import org.jetbrains.kotlin.incremental.createDirectory -import technology.idlab.parser.intermediate.IRDependency +import technology.idlab.intermediate.IRDependency import technology.idlab.resolver.impl.GitResolver import technology.idlab.resolver.impl.LocalResolver import technology.idlab.util.Log diff --git a/src/main/kotlin/resolver/impl/GitResolver.kt b/src/main/kotlin/resolver/impl/GitResolver.kt index 4956c1f..08af825 100644 --- a/src/main/kotlin/resolver/impl/GitResolver.kt +++ b/src/main/kotlin/resolver/impl/GitResolver.kt @@ -1,7 +1,7 @@ package technology.idlab.resolver.impl import java.io.File -import technology.idlab.parser.intermediate.IRDependency +import technology.idlab.intermediate.IRDependency import technology.idlab.resolver.Resolver /** Resolve a Git repository by cloning it locally and reading its configuration file. */ diff --git a/src/main/kotlin/resolver/impl/LocalResolver.kt b/src/main/kotlin/resolver/impl/LocalResolver.kt index 543c82d..bf4cad0 100644 --- a/src/main/kotlin/resolver/impl/LocalResolver.kt +++ b/src/main/kotlin/resolver/impl/LocalResolver.kt @@ -1,7 +1,7 @@ package technology.idlab.resolver.impl import java.io.File -import technology.idlab.parser.intermediate.IRDependency +import technology.idlab.intermediate.IRDependency import technology.idlab.resolver.Resolver /** Resolve a file on the local file system. */ diff --git a/src/main/kotlin/runner/Runner.kt b/src/main/kotlin/runner/Runner.kt index 426dd45..e3bc709 100644 --- a/src/main/kotlin/runner/Runner.kt +++ b/src/main/kotlin/runner/Runner.kt @@ -1,8 +1,8 @@ package runner import kotlinx.coroutines.channels.Channel -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage import technology.idlab.util.Log abstract class Runner( diff --git a/src/main/kotlin/runner/impl/GRPCRunner.kt b/src/main/kotlin/runner/impl/GRPCRunner.kt index d85ff5e..e4befb5 100644 --- a/src/main/kotlin/runner/impl/GRPCRunner.kt +++ b/src/main/kotlin/runner/impl/GRPCRunner.kt @@ -12,10 +12,10 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow import runner.Runner -import technology.idlab.parser.intermediate.IRArgument -import technology.idlab.parser.intermediate.IRParameter -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage +import technology.idlab.intermediate.IRArgument +import technology.idlab.intermediate.IRParameter +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage import technology.idlab.util.Log import technology.idlab.util.retries diff --git a/src/main/kotlin/runner/impl/NodeRunner.kt b/src/main/kotlin/runner/impl/NodeRunner.kt index d4d10a8..36afe90 100644 --- a/src/main/kotlin/runner/impl/NodeRunner.kt +++ b/src/main/kotlin/runner/impl/NodeRunner.kt @@ -29,14 +29,14 @@ class NodeRunner(fromProcessors: Channel, port: Int) : thread { val stream = process.inputStream.bufferedReader() for (line in stream.lines()) { - Log.shared.runtime(command, line) + Log.shared.info(line, command) } } thread { val stream = process.errorStream.bufferedReader() for (line in stream.lines()) { - Log.shared.runtimeFatal(command, line) + Log.shared.fatal(line, command) } } } diff --git a/src/main/kotlin/runner/jvm/JVMRunner.kt b/src/main/kotlin/runner/jvm/JVMRunner.kt index aba4d39..440d86f 100644 --- a/src/main/kotlin/runner/jvm/JVMRunner.kt +++ b/src/main/kotlin/runner/jvm/JVMRunner.kt @@ -10,13 +10,15 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import org.jetbrains.kotlin.backend.common.push import runner.Runner -import technology.idlab.parser.intermediate.IRArgument -import technology.idlab.parser.intermediate.IRParameter -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage +import technology.idlab.intermediate.IRArgument +import technology.idlab.intermediate.IRParameter +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage import technology.idlab.runner.jvm.Arguments import technology.idlab.util.Log -import technology.idlab.util.Log.Cause.* + +private const val STAGE_NO_CLASS = "Processor has no class key set." +private const val REQUIRES_PROCESSOR_BASE_CLASS = "Class does not extend Processor." class JVMRunner( fromProcessors: Channel, @@ -31,13 +33,13 @@ class JVMRunner( private val readers = mutableMapOf>() override suspend fun load(processor: IRProcessor, stage: IRStage) { - /** Load the class into the JVM> */ - val className = processor.metadata["class"] ?: Log.shared.fatal(JVM_RUNNER_STAGE_NO_CLASS) + /** Load the class into the JVM. */ + val className = processor.metadata["class"] ?: Log.shared.fatal(STAGE_NO_CLASS) val clazz = Class.forName(className) as Class<*> /** Check if instantiatable. */ if (!Processor::class.java.isAssignableFrom(clazz)) { - Log.shared.fatal("Processor class does not extend Processor.") + Log.shared.fatal(REQUIRES_PROCESSOR_BASE_CLASS) } /** Build the argument map. */ diff --git a/src/main/kotlin/std/RDFValidator.kt b/src/main/kotlin/std/RDFValidator.kt index f4083e3..4e33875 100644 --- a/src/main/kotlin/std/RDFValidator.kt +++ b/src/main/kotlin/std/RDFValidator.kt @@ -3,13 +3,13 @@ package technology.idlab.std import java.io.ByteArrayOutputStream import java.io.File import org.apache.jena.graph.Graph +import org.apache.jena.ontology.OntModelSpec import org.apache.jena.rdf.model.ModelFactory import org.apache.jena.riot.RiotException import org.apache.jena.shacl.ShaclValidator import runner.jvm.Processor import runner.jvm.Reader import runner.jvm.Writer -import technology.idlab.extensions.readModelRecursively import technology.idlab.runner.jvm.Arguments import technology.idlab.util.Log @@ -26,7 +26,7 @@ class RDFValidator(args: Arguments) : Processor(args) { /** Runtime fields. */ private val shapes: Graph - private val model = ModelFactory.createDefaultModel() + private val model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM) private val validator = ShaclValidator.get() // Initialize the shape graph and validator. @@ -36,7 +36,7 @@ class RDFValidator(args: Arguments) : Processor(args) { val shapesModel = try { - file.readModelRecursively() + model.read(file.toURI().toString(), "TURTLE") } catch (e: RiotException) { Log.shared.fatal("Failed to read SHACL shapes from file://$path") } @@ -51,7 +51,6 @@ class RDFValidator(args: Arguments) : Processor(args) { val res = input.read() // Parse as a model. - Log.shared.assert(model.isEmpty, "Model should be empty.") try { model.read(res.inputStream(), null, "TURTLE") } catch (e: RiotException) { diff --git a/src/main/kotlin/util/Log.kt b/src/main/kotlin/util/Log.kt index d7318f5..a0d4fdb 100644 --- a/src/main/kotlin/util/Log.kt +++ b/src/main/kotlin/util/Log.kt @@ -1,165 +1,126 @@ package technology.idlab.util import java.time.format.DateTimeFormatter -import java.util.Date -import java.util.TimeZone -import kotlin.Exception -import technology.idlab.exception.RunnerException - -class Log private constructor() { - enum class Cause(val message: String) { - JVM_RUNNER_STAGE_NO_CLASS("The processor has no class key set."), - } - - enum class Level { +import java.util.* + +private const val TIME_PADDING = 15 +private const val THREAD_PADDING = 8 +private const val LEVEL_PADDING = 7 +private const val LOCATION_PADDING = 35 +private const val MESSAGE_PADDING = 50 + +/** + * A simple logging class that prints messages to the console. The class is a singleton, and all + * messages are outputted to the console. The class has three levels of logging: DEBUG, INFO, and + * FATAL. DEBUG is for debugging information, INFO is for general information, and FATAL is for + * errors that will cause the program to exit. At the moment of writing, all levels are outputted to + * the console regardless of the debug flag. + */ +class Log private constructor(val header: Boolean = true) { + private enum class Level { + DEBUG, INFO, - SEVERE, FATAL, - DEBUG, - ; - - fun style(string: String): String { - return when (this) { - INFO -> string - SEVERE -> string - FATAL -> string - DEBUG -> "\u001B[34m${string}\u001B[0m" - } - } - - fun code(): String { - return when (this) { - INFO -> "INFO" - SEVERE -> "SEVERE" - FATAL -> "FATAL" - DEBUG -> "DEBUG" - } - } } init { - val header = - listOf( - "TIME".padEnd(12, ' '), - "THREAD".padEnd(6, ' '), - "LEVEL".padEnd(7, ' '), - "LOCATION".padEnd(50, ' '), - "MESSAGE", - ) - .joinToString(" ") - println(header) + if (header) { + print("TIME".padEnd(TIME_PADDING, ' ')) + print("THREAD".padEnd(THREAD_PADDING, ' ')) + print("LEVEL".padEnd(LEVEL_PADDING, ' ')) + print("LOCATION".padEnd(LOCATION_PADDING, ' ')) + println("MESSAGE".padEnd(MESSAGE_PADDING, ' ')) + print("----".padEnd(TIME_PADDING, ' ')) + print("------".padEnd(THREAD_PADDING, ' ')) + print("-----".padEnd(LEVEL_PADDING, ' ')) + print("--------".padEnd(LOCATION_PADDING, ' ')) + println("-------") + } - val separator = - listOf( - "----".padEnd(12, ' '), - "------".padEnd(6, ' '), - "-----".padEnd(7, ' '), - "--------".padEnd(50, ' '), - "-------", - ) - .joinToString(" ") - println(separator) + Runtime.getRuntime().addShutdownHook(Thread { info("The JVM is shutting down.") }) } - private fun line(message: String, level: Level): String { + /** + * Print a message to the output using a given level. + * + * @param message The message to print. + * @param level The level of the message. + */ + private fun output(message: String, level: Level, location: String? = null) { + // Get the current time. val instant = Date().toInstant() val tz = instant.atZone(TimeZone.getDefault().toZoneId()) val iso = DateTimeFormatter.ISO_LOCAL_TIME val time = tz.format(iso) - val caller = Throwable().stackTrace[3] - val name = - "${caller.className.substringAfterLast(".")}::${caller.methodName}::${caller.lineNumber}" - - return listOf( - time.padEnd(12, '0'), - "[${Thread.currentThread().id}]".padEnd(6, ' '), - level.code().padEnd(7, ' '), - name.padEnd(50, ' '), - message, - ) - .joinToString(" ") - } - - private fun toConsole(message: String, level: Level) { - println(level.style(line(message, level))) - } - - fun info(message: String) { - toConsole(message, Level.INFO) - } - - fun severe(message: String) { - toConsole(message, Level.SEVERE) - } - - fun fatal(message: String): Nothing { - toConsole(message, Level.FATAL) - throw RunnerException() + // Get the thread ID. + val thread = Thread.currentThread().id.toString() + + // Get the level. + val levelCode = level.name + + // Get the location. + val usedLocation = + location + ?: run { + val call = Throwable().stackTrace[3] + val clazz = call.className.substringAfterLast(".").substringBefore("$") + "${clazz}::${call.methodName}::${call.lineNumber}" + } + + // Print to the console. + print(time.padEnd(TIME_PADDING, ' ')) + print(thread.padEnd(THREAD_PADDING, ' ')) + print(levelCode.padEnd(LEVEL_PADDING, ' ')) + print(usedLocation.padEnd(LOCATION_PADDING, ' ')) + println(message) } - fun fatal(exception: Exception): Nothing { - toConsole(exception.message.toString(), Level.FATAL) - throw RunnerException() + /** + * Print a general info message. Will get outputted in all modes. + * + * @param message The message to print. + */ + fun info(message: String, location: String? = null) { + output(message, Level.INFO, location = location) } - fun fatal(message: String, exception: Exception) { - toConsole("$message - ${exception.message}", Level.FATAL) - throw RunnerException() + /** + * Print a fatal message, after which the program will exit with an error. + * + * @param message The message to print. + */ + fun fatal(message: String, location: String? = null): Nothing { + output(message, Level.FATAL, location = location) + Runtime.getRuntime().exit(1) + throw Exception("This exception cannot be reached, but the Kotlin compiler doesn't know that.") } - fun fatal(cause: Cause): Nothing = fatal(cause.message) - + /** + * Print a message if and only if the debug flag is set. Note that the message will not be + * evaluated lazily. + * + * @param message The message to print. + */ fun debug(message: String) { - toConsole(message, Level.DEBUG) - } - - fun assert(condition: Boolean, message: String) { - if (!condition) { - fatal(message) - } - } - - fun runtime(runtime: String, message: String) { - val instant = Date().toInstant() - val tz = instant.atZone(TimeZone.getDefault().toZoneId()) - val iso = DateTimeFormatter.ISO_LOCAL_TIME - val time = tz.format(iso) - - val line = - listOf( - time.padEnd(12, '0'), - "[${Thread.currentThread().id}]".padEnd(6, ' '), - "INFO".padEnd(7, ' '), - runtime.padEnd(50, ' '), - message, - ) - .joinToString(" ") - - println(line) + output(message, Level.DEBUG) } - fun runtimeFatal(runtime: String, message: String): Nothing { - val instant = Date().toInstant() - val tz = instant.atZone(TimeZone.getDefault().toZoneId()) - val iso = DateTimeFormatter.ISO_LOCAL_TIME - val time = tz.format(iso) - - val line = - listOf( - time.padEnd(12, '0'), - "[${Thread.currentThread().id}]".padEnd(6, ' '), - "FATAL".padEnd(7, ' '), - runtime.padEnd(50, ' '), - message, - ) - .joinToString(" ") - - println(line) - throw RunnerException() + /** + * Print a message if and only if the debug flag is set. Note that the message will be evaluated + * lazily only if the debug flag is set. + * + * @param message A function determining the message to print. + */ + fun debug(message: () -> String) { + debug(message()) } companion object { + /** + * A globally available instance of the logger. Note that at its creation, the logger will + * output the header to the console, which is why we only allow a single instance. + */ val shared = Log() } } diff --git a/src/test/kotlin/OrchestratorTest.kt b/src/test/kotlin/OrchestratorTest.kt index 85fa7fe..e63c174 100644 --- a/src/test/kotlin/OrchestratorTest.kt +++ b/src/test/kotlin/OrchestratorTest.kt @@ -6,7 +6,7 @@ import processors.NodeTransparent import processors.TappedReader import processors.TappedWriter import technology.idlab.Orchestrator -import technology.idlab.parser.intermediate.IRPipeline +import technology.idlab.intermediate.IRPipeline val processors = listOf(TappedWriter.processor, NodeTransparent.processor, TappedReader.processor) diff --git a/src/test/kotlin/parser/ParserTest.kt b/src/test/kotlin/parser/ParserTest.kt index 737d6a8..5faf43e 100644 --- a/src/test/kotlin/parser/ParserTest.kt +++ b/src/test/kotlin/parser/ParserTest.kt @@ -7,8 +7,8 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue import runner.Runner +import technology.idlab.intermediate.IRParameter import technology.idlab.parser.Parser -import technology.idlab.parser.intermediate.IRParameter class ParserTest { private fun parse(resource: String): Parser { diff --git a/src/test/kotlin/processors/NodeTransparent.kt b/src/test/kotlin/processors/NodeTransparent.kt index 21f093d..60a569e 100644 --- a/src/test/kotlin/processors/NodeTransparent.kt +++ b/src/test/kotlin/processors/NodeTransparent.kt @@ -1,10 +1,10 @@ package processors import runner.Runner -import technology.idlab.parser.intermediate.IRArgument -import technology.idlab.parser.intermediate.IRParameter -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage +import technology.idlab.intermediate.IRArgument +import technology.idlab.intermediate.IRParameter +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage class NodeTransparent { companion object { diff --git a/src/test/kotlin/processors/TappedReader.kt b/src/test/kotlin/processors/TappedReader.kt index b7db2b0..6c721be 100644 --- a/src/test/kotlin/processors/TappedReader.kt +++ b/src/test/kotlin/processors/TappedReader.kt @@ -4,10 +4,10 @@ import kotlinx.coroutines.channels.Channel import runner.Runner import runner.jvm.Processor import runner.jvm.Reader -import technology.idlab.parser.intermediate.IRArgument -import technology.idlab.parser.intermediate.IRParameter -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage +import technology.idlab.intermediate.IRArgument +import technology.idlab.intermediate.IRParameter +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage import technology.idlab.runner.jvm.Arguments /** diff --git a/src/test/kotlin/processors/TappedWriter.kt b/src/test/kotlin/processors/TappedWriter.kt index 391f534..f0a9fc8 100644 --- a/src/test/kotlin/processors/TappedWriter.kt +++ b/src/test/kotlin/processors/TappedWriter.kt @@ -4,10 +4,10 @@ import kotlinx.coroutines.channels.Channel import runner.Runner import runner.jvm.Processor import runner.jvm.Writer -import technology.idlab.parser.intermediate.IRArgument -import technology.idlab.parser.intermediate.IRParameter -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage +import technology.idlab.intermediate.IRArgument +import technology.idlab.intermediate.IRParameter +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage import technology.idlab.runner.jvm.Arguments /** diff --git a/src/test/kotlin/runner/RunnerTest.kt b/src/test/kotlin/runner/RunnerTest.kt index b519b81..98c2ff0 100644 --- a/src/test/kotlin/runner/RunnerTest.kt +++ b/src/test/kotlin/runner/RunnerTest.kt @@ -7,10 +7,10 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals -import technology.idlab.parser.intermediate.IRArgument -import technology.idlab.parser.intermediate.IRParameter -import technology.idlab.parser.intermediate.IRProcessor -import technology.idlab.parser.intermediate.IRStage +import technology.idlab.intermediate.IRArgument +import technology.idlab.intermediate.IRParameter +import technology.idlab.intermediate.IRProcessor +import technology.idlab.intermediate.IRStage import technology.idlab.util.Log abstract class RunnerTest {