From c2316fe3146ef3b4eaf8d43d10f46f70c4df320a Mon Sep 17 00:00:00 2001 From: Tsimafei Raro Date: Mon, 22 May 2023 15:27:22 +0200 Subject: [PATCH] [Upd] Clean up in the RDF maven plugin --- .../main/java/RdfAnnotationProcessorMojo.java | 192 +++++++++++------- 1 file changed, 113 insertions(+), 79 deletions(-) diff --git a/s-pipes-modules-utils/s-pipes-module-creator-maven-plugin/src/main/java/RdfAnnotationProcessorMojo.java b/s-pipes-modules-utils/s-pipes-module-creator-maven-plugin/src/main/java/RdfAnnotationProcessorMojo.java index 8bc2589c..4de3cc2d 100644 --- a/s-pipes-modules-utils/s-pipes-module-creator-maven-plugin/src/main/java/RdfAnnotationProcessorMojo.java +++ b/s-pipes-modules-utils/s-pipes-module-creator-maven-plugin/src/main/java/RdfAnnotationProcessorMojo.java @@ -1,13 +1,15 @@ +import cz.cvut.spipes.constants.KBSS_MODULE; import cz.cvut.spipes.constants.SM; import cz.cvut.spipes.modules.annotations.SPipesModule; +import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.util.FileUtils; +import org.apache.jena.vocabulary.OWL; import org.apache.jena.vocabulary.RDF; import org.apache.jena.vocabulary.RDFS; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; @@ -29,15 +31,24 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; import java.util.stream.Collectors; @Mojo(name = "process-annotations", defaultPhase = LifecyclePhase.COMPILE) public class RdfAnnotationProcessorMojo extends AbstractMojo { + private static final Map DEFAULT_RDF_PREFIXES = Map.of( + "kbss-module", KBSS_MODULE.uri, + "owl", "http://www.w3.org/2002/07/owl#", + "rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs", "http://www.w3.org/2000/01/rdf-schema#", + "sm", "http://topbraid.org/sparqlmotion#", + "spin", "http://spinrdf.org/spin#", + "spl", "http://spinrdf.org/spl#", + "xsd", "http://www.w3.org/2001/XMLSchema#" + ); @Parameter(defaultValue = "${project}", readonly = true, required = true) MavenProject project; @@ -61,22 +72,21 @@ enum GenerationMode { RDF_FOR_ALL_CHILDREN } - private final Log log = getLog(); - private final Class PARAM_ANNOTATION = cz.cvut.spipes.modules.Parameter.class; private final Class MODULE_ANNOTATION = cz.cvut.spipes.modules.annotations.SPipesModule.class; + //region Entrypoint methods @Override public void execute() throws MojoExecutionException { if (mode == null) { final var defaultMode = GenerationMode.RDF_FOR_ALL_CHILDREN; - log.warn(String.format("No generation mode is specified, defaulting to %s. Available modes: %s", + getLog().warn(String.format("No generation mode is specified, defaulting to %s. Available modes: %s", defaultMode, Arrays.toString(GenerationMode.values()))); mode = defaultMode; } - log.info("Executing in the following mode: " + mode.name()); + getLog().info("Executing in the following mode: " + mode.name()); try { switch (mode) { @@ -97,69 +107,71 @@ public void execute() throws MojoExecutionException { private void generateRdfForAllModules() throws MalformedURLException, ClassNotFoundException, FileNotFoundException { //read all submodules - log.info("Generating an RDF for all sub-modules"); - List submodules = project.getCollectedProjects(); - - //todo create base RDF structure + getLog().info("Generating an RDF for all sub-modules"); + var model = initDefaultModel(); + for (MavenProject submodule : (List) project.getCollectedProjects()) { - //populate RDF with module info - for (MavenProject submodule : submodules) { //find module's main class var moduleClasses = readAllModuleClasses(submodule); - log.info("Module: " + submodule.getName() + " | Classes: [" + moduleClasses.stream() + getLog().info("Module: " + submodule.getName() + " | Classes: [" + moduleClasses.stream() .map(Class::getSimpleName) .collect(Collectors.joining(", ")) + "]"); //add module to the RDF structure for (Class moduleClass : moduleClasses) { - log.info("Creating RDF for module '" + moduleClass.getCanonicalName() + "'"); - final var moduleAnnotation = readModuleAnnotationFromClass(moduleClass); - final var constraints = readConstraintsFromClass(moduleClass); - writeConstraintsToOutputFile(constraints, moduleAnnotation); + getLog().info("Creating RDF for module '" + moduleClass.getCanonicalName() + "'"); + var moduleAnnotation = readModuleAnnotationFromClass(moduleClass); + var constraints = readConstraintsFromClass(moduleClass); + writeConstraintsToModel(model, constraints, moduleAnnotation); } - log.info("--------------------------------------"); + getLog().info("--------------------------------------"); + } + writeModelToStdout(model); + try { + writeToTargetFolderFile(model, "all-modules.ttl"); + } catch (IOException e) { + getLog().error("Failed to write model to the output file", e); } + getLog().info("======================================"); } private void generateRdfForModule() throws MojoExecutionException { try { - final Class classObject = readModuleClass(moduleClassName); - final var moduleAnnotation = readModuleAnnotationFromClass(classObject); - final var constraints = readConstraintsFromClass(classObject); - writeConstraintsToOutputFile(constraints, moduleAnnotation); + Set> moduleClasses = readAllModuleClasses(this.project); + var model = readModelFromDefaultFile(); + for (Class moduleClass : moduleClasses) { + var moduleAnnotation = readModuleAnnotationFromClass(moduleClass); + var constraints = readConstraintsFromClass(moduleClass); + writeConstraintsToModel(model, constraints, moduleAnnotation); + } + + var ontologyPath = modulePackageName.replaceAll("[.]", "/") + "/" + ontologyFilename; + writeToTargetFolderFile(model, ontologyPath); } catch (Exception e) { - log.error("Failed to execute s-pipes annotation processing: ", e); + getLog().error("Failed to execute s-pipes annotation processing: ", e); throw new MojoExecutionException("Exception during s-pipes execution", e); } } + //endregion - private Class readModuleClass(String className) throws MalformedURLException, ClassNotFoundException { - final File classesDirectory = new File(project.getBuild().getOutputDirectory()); - final URL classesUrl = classesDirectory.toURI().toURL(); - final URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classesUrl}, getClass().getClassLoader()); - final Class classObject = classLoader.loadClass(modulePackageName + "." + className); //todo fragile? - - log.info("Successfully loaded SPipes Module: " + classObject.toGenericString()); - return classObject; - } - + //region Parsing Java classes private Set> readAllModuleClasses(MavenProject project) throws MalformedURLException, ClassNotFoundException { - //Configure class searcher + //Configure the class searcher final File classesDirectory = new File(project.getBuild().getOutputDirectory()); final URL classesUrl = classesDirectory.toURI().toURL(); final URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classesUrl}, getClass().getClassLoader()); - ConfigurationBuilder reflectionConfig = new ConfigurationBuilder() + var reflectionConfig = new ConfigurationBuilder() .setUrls(ClasspathHelper.forClassLoader(classLoader)) .setScanners(new SubTypesScanner(false), new TypeAnnotationsScanner()) .filterInputsBy(new FilterBuilder().includePackage(modulePackageName)); - Reflections reflections = new Reflections(reflectionConfig); + var classSearcher = new Reflections(reflectionConfig); //Find classes with the module annotation Set> moduleClasses = new HashSet<>(); - for (String type : reflections.getAllTypes()) { + for (String type : classSearcher.getAllTypes()) { final Class classObject = classLoader.loadClass(type); - log.debug("Class: " + type + " | Annotations: [" + getLog().debug("Class: " + type + " | Annotations: [" + Arrays.stream(classObject.getAnnotations()) .map(Annotation::annotationType) .map(Class::getSimpleName) @@ -182,55 +194,77 @@ private List readConstraintsFromClass(Class .map(field -> field.getAnnotation(PARAM_ANNOTATION)) .collect(Collectors.toUnmodifiableList()); } + //endregion + + //region Working with RDF + + public Model initDefaultModel() { + var model = ModelFactory.createDefaultModel(); + + model.setNsPrefixes(DEFAULT_RDF_PREFIXES); + + var ontology = ResourceFactory.createResource(KBSS_MODULE.uri + "all-modules-ontology"); + model.add(ontology, RDF.type, OWL.Ontology); + return model; + } - private void writeConstraintsToOutputFile(List constraintAnnotations, - SPipesModule moduleAnnotation) throws FileNotFoundException { -// final var ontologyFolder = "/" + modulePackageName.replaceAll("[.]", "/") + "/"; -// final var ontologyFilepath = project.getBuild().getOutputDirectory() + ontologyFolder + ontologyFilename; - -// log.info("Reading ontology file: " + ontologyFilepath); - final var model = ModelFactory.createDefaultModel(); -// model.read(ontologyFilepath); - - //region todo replace with a proper initialization -// String prefixes = "@prefix kbss-module: .\n" + -// "@prefix owl: .\n" + -// "@prefix rdf: .\n" + -// "@prefix rdfs: .\n" + -// "@prefix sm: .\n" + -// "@prefix spin: .\n" + -// "@prefix spl: .\n" + -// "@prefix xsd: .\n" + -// "kbss-module:test-own-artifact-generated-ontology\n" + -// " rdf:type owl:Ontology ;\n" + -// "."; -// var prefixStream = new ByteArrayInputStream(prefixes.getBytes(StandardCharsets.UTF_8)); -// model.read(prefixStream, FileUtils.langTurtle); - //endregion - - final var root = ResourceFactory.createResource(); - model.add(root, RDF.type, SM.Module); - model.add(root, RDFS.comment, moduleAnnotation.comment()); - model.add(root, RDFS.label, moduleAnnotation.label()); - final var statements = model.listStatements(null, RDF.type, SM.Module); + private Model readModelFromDefaultFile() { + var ontologyFolder = "/" + modulePackageName.replaceAll("[.]", "/") + "/"; + var ontologyFilepath = project.getBuild().getOutputDirectory() + ontologyFolder + ontologyFilename; + var model = ModelFactory.createDefaultModel(); + model.read(ontologyFilepath); + getLog().info("Successfully read the existing ontology file: " + ontologyFilepath); + return model; + } + + private void writeConstraintsToModel(Model baseRdfModel, + List constraintAnnotations, + SPipesModule moduleAnnotation) { + final var root = ResourceFactory.createResource(KBSS_MODULE.uri + moduleAnnotation.label().replaceAll(" ", "-").toLowerCase()); //todo can be added to the annotation + baseRdfModel.add(root, RDF.type, SM.Module); + baseRdfModel.add(root, RDFS.comment, moduleAnnotation.comment()); + baseRdfModel.add(root, RDFS.label, moduleAnnotation.label()); + final var statements = baseRdfModel.listStatements(null, RDF.type, SM.Module); while (statements.hasNext()) { final var statement = statements.next(); final var subject = statement.getSubject(); for (var annotation : constraintAnnotations) { final var modelConstraint = ResourceFactory.createResource(); - model.add(modelConstraint, RDF.type, SPL.Argument); - model.add(modelConstraint, SPL.predicate, annotation.urlPrefix() + annotation.name()); - model.add(modelConstraint, RDFS.comment, "Automatically generated field: " + annotation.name()); - model.add(subject, SPIN.constraint, modelConstraint); + baseRdfModel.add(modelConstraint, RDF.type, SPL.Argument); + baseRdfModel.add(modelConstraint, SPL.predicate, annotation.urlPrefix() + annotation.name()); + baseRdfModel.add(modelConstraint, RDFS.comment, "Automatically generated field: " + annotation.name()); + baseRdfModel.add(subject, SPIN.constraint, modelConstraint); - log.info("Added model constraint based on annotation: " + + getLog().debug("Added model constraint based on annotation: " + "(name = " + annotation.name() + ", urlPrefix = " + annotation.urlPrefix() + ")"); } } -// model.write(new FileOutputStream(ontologyFilepath), FileUtils.langTurtle); -// log.info("Successfully written constraints to the ontology file: " + ontologyFilepath); + } + + private void writeToTargetFolderFile(Model model, String outputFilename) throws IOException { + var filepath = Path.of(project.getBuild().getOutputDirectory() + "/" + outputFilename); + if (Files.notExists(filepath)) { + getLog().debug("File does not exist, will create anew: " + filepath); + var dirPath = Path.of(project.getBuild().getOutputDirectory()); + if (Files.notExists(dirPath)) { + getLog().debug("Not all directories in the provided path exist, will create them as necessary: " + dirPath); + Files.createDirectories(dirPath); + } + Files.createFile(filepath); + } + + model.write(Files.newOutputStream(filepath), FileUtils.langTurtle); + getLog().info("Successfully written constraints to file: " + filepath); + } + + private void writeModelToStdout(Model model) { var out = new ByteArrayOutputStream(); model.write(out, FileUtils.langTurtle); - log.info("RDF for module: " + out); + + getLog().info("Generated RDF:\n" + + "-----------------------------------\n" + + out + + "-----------------------------------\n"); } + //endregion }