Kastor in 5 minutes

Version: Kastor RDF SDK 0.3.0-SNAPSHOT · Kotlin 2.x · Java 21+

This page is a single-sheet sampler: add one Gradle block, then skim the sections below—graph DSL, string & Kotlin SPARQL, providers, SHACL 1.2, Kastor Gen (plugin + KSP), ontology quality (Kotlin + CLI), and pointers to conformance. Each section has a short excerpt; Complete listing merges the runnable RDF API pieces into one main().

For a slower, step-by-step path, use Getting Started and Quick Start.

Not yet on Maven Central. Kastor artifacts are not published yet. Build from source and install locally with ./gradlew publishToMavenLocal (then add mavenLocal() to your repositories), or include the Kastor checkout as a Gradle composite build (includeBuild("../kastor")). The version shown (0.3.0-SNAPSHOT) is defined once in the root gradle.properties.

Prerequisites

  • JDK 21+
  • A Kotlin JVM Gradle module (kotlin("jvm"))

Dependencies

Minimal set for repository + DSL + SPARQL (pick one backend; both is fine):

dependencies {
    implementation("com.geoknoesis.kastor:rdf-core:0.3.0-SNAPSHOT")
    implementation("com.geoknoesis.kastor:rdf-jena:0.3.0-SNAPSHOT")   // or rdf-rdf4j:0.3.0-SNAPSHOT
}

Add these only when you use the matching section later on:

    // Kotlin SPARQL DSL — section 3 (select {})
    implementation("com.geoknoesis.kastor:rdf-sparql-lang:0.3.0-SNAPSHOT")

    // Ontology quality — section 7 (bundled SHACL catalogues; transitive SHACL + Jena stack)
    implementation("com.geoknoesis.kastor:onto-quality:0.3.0-SNAPSHOT")

Add these only if you follow the SHACL section:

    implementation("com.geoknoesis.kastor:rdf-shacl-validation:0.3.0-SNAPSHOT")
    implementation("com.geoknoesis.kastor:rdf-shacl-dsl:0.3.0-SNAPSHOT")

Kastor Gen uses the Gradle plugin (and usually kastor-gen-runtime in dependencies)—see §6.

Optional: rdf-sparql (SPARQL endpoint provider), reasoning modules—Installation.

repositories { mavenCentral() }

1. Repository and graph DSL

Rdf.memory() gives you an in-memory RdfRepository. Use repo.add { } with iri(...), FOAF-style has / with, and RDF.type:

import com.geoknoesis.kastor.rdf.*

val repo = Rdf.memory()
repo.add {
    val alice = iri("http://example.org/alice")
    alice has FOAF.name with "Alice Example"
    alice has RDF.type with FOAF.Person
}

The DSL is vocabulary-agnostic; FOAF and RDF constants are bundled for convenience. Your own predicates are just iri("…").


2. Swap the engine (Jena, RDF4J, SPARQL, …)

The same RdfRepository API works across providers. Examples:

// In-memory Jena (good parity check with your JVM stack)
val jenaMemory = Rdf.repository {
    providerId = "jena"
    variantId = "memory"
}

// In-memory RDF4J
val rdf4jMemory = Rdf.repository {
    providerId = "rdf4j"
    variantId = "memory"
}

Remote endpoints and TDB2 / NativeStore use RdfProviderRegistry.create(RdfConfig(...)) or the same Rdf.repository { } DSL with provider-specific options—see Configuration variants and Providers.


3. SPARQL queries

You need a SPARQL-capable repository on the classpath (the rdf-jena / rdf-rdf4j line above). Rdf.memory() already picks Jena or RDF4J memory when one of them is registered.

String SPARQL (SparqlSelectQuery)

Run a query against the repository’s default graph:

import com.geoknoesis.kastor.rdf.SparqlSelectQuery

val rows = repo.select(SparqlSelectQuery("""
    PREFIX foaf: <http://xmlns.com/foaf/0.1/>
    SELECT ?name WHERE {
      ?s foaf:name ?name .
    }
""".trimIndent()))

rows.forEach { println(it.getString("name")) }

Kotlin SPARQL DSL (select { })

Add rdf-sparql-lang. The DSL builds a SparqlSelect that repo.select accepts—same execution path as a string query.

import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.sparql.*
import com.geoknoesis.kastor.rdf.vocab.FOAF
import com.geoknoesis.kastor.rdf.vocab.RDF

val dslQuery = select("name") {
    version("1.2")
    prefix("foaf", FOAF.namespace)
    where {
        pattern(var_("person"), FOAF.name, var_("name"))
        pattern(var_("person"), RDF.type, FOAF.Person)
    }
}

repo.select(dslQuery).forEach { println(it.getString("name")) }

More patterns (filters, OPTIONAL, aggregates, RDF-star): SPARQL fundamentals, Kastor query DSL tutorial.


4. Datasets and RDF-star (where you need more than one graph)

Kastor models datasets (default graph + named graphs) and supports RDF-star constraints depending on provider variant—see How to use datasets and RDF-star.


5. SHACL 1.2 validation (native)

With rdf-shacl-validation + rdf-shacl-dsl, define shapes in Kotlin and validate the default graph against them:

import com.geoknoesis.kastor.rdf.dsl.*
import com.geoknoesis.kastor.rdf.shacl.ShaclValidation
import com.geoknoesis.kastor.rdf.shacl.ValidationProfile

val shapes = shacl {
    nodeShape("http://example.org/shapes/Person") {
        targetClass(FOAF.Person)
        property(FOAF.name) { minCount = 1 }
    }
}

val report = ShaclValidation.validator(ValidationProfile.SHACL_CORE)
    .validate(repo.defaultGraph, shapes)

println("Conforms: ${report.isValid}")  // sh:conforms: false for any result, including warnings

Details: SHACL validation, SHACL DSL guide.


6. Kastor Gen (ontology → Kotlin)

Kastor Gen generates interfaces, wrappers, vocabulary constants, and optional domain DSL from SHACL + JSON-LD context at build time—Kotlin stays domain-shaped; RDF stays explicit.

Gradle-only generation (no @Rdf in your sources)

Use the com.geoknoesis.kastor.gen plugin and declare ontologies under kastorGen (full reference):

// settings.gradle.kts — ensure Gradle can resolve the plugin
pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
}
// build.gradle.kts
plugins {
    id("org.jetbrains.kotlin.jvm") version "2.4.20"
    id("com.geoknoesis.kastor.gen") version "0.3.0-SNAPSHOT"
}

dependencies {
    implementation("com.geoknoesis.kastor:rdf-core:0.3.0-SNAPSHOT")
    implementation("com.geoknoesis.kastor:rdf-jena:0.3.0-SNAPSHOT")
    implementation("com.geoknoesis.kastor:kastor-gen-runtime:0.3.0-SNAPSHOT")
}

kastorGen {
    ontologies {
        create("demo") {
            shaclPath = "src/main/resources/shapes/demo.shacl.ttl"
            contextPath = "src/main/resources/context/demo.context.jsonld"
            interfacePackage = "com.example.generated"
            wrapperPackage = "com.example.generated"
        }
    }
}

Point shaclPath / contextPath at real files (see Ontology generation tutorial); run ./gradlew generateOntology (task name is documented in the plugin reference).

Annotation + KSP (interfaces in source)

plugins {
    kotlin("jvm") version "2.4.20"
    id("com.google.devtools.ksp") version "2.3.12"
}

dependencies {
    implementation("com.geoknoesis.kastor:rdf-core:0.3.0-SNAPSHOT")
    implementation("com.geoknoesis.kastor:rdf-jena:0.3.0-SNAPSHOT")
    implementation("com.geoknoesis.kastor:kastor-gen-runtime:0.3.0-SNAPSHOT")
    ksp("com.geoknoesis.kastor:kastor-gen-processor:0.3.0-SNAPSHOT")
}
import com.geoknoesis.kastor.gen.annotations.Rdf

@Rdf(iri = "http://xmlns.com/foaf/0.1/Person")
interface Person {
    @Rdf(iri = "http://xmlns.com/foaf/0.1/name")
    val name: List<String>
}

Tutorials: Kastor Gen README · Getting started.


7. Ontology quality (onto-quality)

Ontology quality runs bundled SHACL catalogues (OWL profile, SKOS, data quality, RDF 1.2-aware bundles, modern-engineering checks, …) and returns a structured QualityReport—broader than instance-only SHACL, aimed at vocabulary and catalogue hygiene.

Add com.geoknoesis.kastor:onto-quality:0.3.0-SNAPSHOT (see dependencies above). It brings a validator-facing stack transitively; keep rdf-jena or rdf-rdf4j aligned with the rest of your app when you mix APIs.

Kotlin: parse Turtle, run default checker

import com.geoknoesis.kastor.ontoquality.QualityChecker
import com.geoknoesis.kastor.rdf.Rdf
import com.geoknoesis.kastor.rdf.shacl.ShaclValidation

val ontology = Rdf.parse(
    """
    @prefix skos: <http://www.w3.org/2004/02/skos/core#> .

    <http://example.org/s> a skos:ConceptScheme ;
        skos:prefLabel "Demo scheme"@en .

    <http://example.org/c> a skos:Concept ;
        skos:inScheme <http://example.org/s> ;
        skos:prefLabel "Concept"@en .
    """.trimIndent(),
    format = "TURTLE",
)

val validator = ShaclValidation.validator()
val report = QualityChecker.default(validator).check(ontology)

println(report.describeText())
println("Conforms: ${report.conforms}")

Use QualityChecker.builder(validator).addCatalog(...) with BundledCatalogs entries when you want a subset of catalogues (for example SKOS-only stacks)—see How to check ontology quality.

CLI (onto-qa)

Same catalogues from the command line (requires the onto-qa distribution / classpath described in the how-to):

onto-qa check my-ontology.ttl --catalog all --reasoner none
onto-qa check my-ontology.ttl --catalog skos-vocabulary --reasoner none

Reasoning-aware runs (--reasoner rdfs, hermit, …) are covered in the guide.

Explanation / tiers: Ontology quality.


8. Standards and conformance

  • RDF 1.2 syntax: Jena and RDF4J providers are driven against W3C RDF 1.2 syntax manifests (Turtle, TriG, N-Triples, N-Quads). See RDF 1.2 conformance.
  • SHACL 1.2: CI runs the full W3C SHACL 1.2 test suite (pinned w3c/data-shapes commit) against the native validator with :rdf:shacl-validation:w3cConformanceTest. All node expression cases pass (W3cKnownDeviations holds 0 unsupported-feature deviations). One approved case that contradicts the SHACL 1.2 text (VALUES) is listed there as a spec override. SHACL-SPARQL needs rdf-jena or rdf-rdf4j at runtime. See the feature page linked above.

Complete listing (one main)

Use Gradle dependencies for rdf-core, rdf-jena (or RDF4J), rdf-sparql-lang, and rdf-shacl-validation + rdf-shacl-dsl when you want the SHACL block. onto-quality is separate—see §7.

import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.dsl.*
import com.geoknoesis.kastor.rdf.shacl.ShaclValidation
import com.geoknoesis.kastor.rdf.shacl.ValidationProfile
import com.geoknoesis.kastor.rdf.sparql.*
import com.geoknoesis.kastor.rdf.vocab.FOAF
import com.geoknoesis.kastor.rdf.vocab.RDF

fun main() {
    // --- Repository + DSL ---
    val repo = Rdf.memory()
    repo.add {
        val alice = iri("http://example.org/alice")
        alice has FOAF.name with "Alice Example"
        alice has RDF.type with FOAF.Person
    }

    // --- SPARQL (string) ---
    val names = repo.select(
        SparqlSelectQuery(
            """
            PREFIX foaf: <http://xmlns.com/foaf/0.1/>
            SELECT ?name WHERE { ?s foaf:name ?name . }
            """.trimIndent(),
        ),
    )
    names.forEach { println("String SPARQL: ${it.getString("name")}") }

    // --- SPARQL (Kotlin DSL — needs rdf-sparql-lang) ---
    val dslQuery = select("name") {
        version("1.2")
        prefix("foaf", FOAF.namespace)
        where {
            pattern(var_("person"), FOAF.name, var_("name"))
            pattern(var_("person"), RDF.type, FOAF.Person)
        }
    }
    repo.select(dslQuery).forEach { println("DSL SPARQL: ${it.getString("name")}") }

    // --- Same API, different backend (optional) ---
    val alsoJena = Rdf.repository {
        providerId = "jena"
        variantId = "memory"
    }
    alsoJena.add {
        val bob = iri("http://example.org/bob")
        bob has FOAF.name with "Bob"
    }

    // --- SHACL 1.2 (needs rdf-shacl-validation + rdf-shacl-dsl) ---
    val shapes = shacl {
        nodeShape("http://example.org/shapes/Person") {
            targetClass(FOAF.Person)
            property(FOAF.name) { minCount = 1 }
        }
    }
    val report =
        ShaclValidation.validator(ValidationProfile.SHACL_CORE)
            .validate(repo.defaultGraph, shapes)
    println("SHACL report valid: ${report.isValid}")
}

Where to go next

Goal Page
Install variants & optional modules Installation
Kotlin SPARQL DSL deep dive Kastor query DSL tutorial
Ontology quality (catalogues, CLI, reasoning) How to check ontology quality
Kastor Gen plugin & tasks Gradle plugin reference
Longer guided tour Quick Start
Architecture & modules Repository architecture
Reasoning providers Reasoning
Examples repo Examples