Skip to content

Commit

Permalink
[Upd] Clean up in the RDF maven plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Tsimafei Raro committed May 23, 2023
1 parent 77f212d commit c2316fe
Showing 1 changed file with 113 additions and 79 deletions.
@@ -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;
Expand All @@ -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<String, String> 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;

Expand All @@ -61,22 +72,21 @@ enum GenerationMode {
RDF_FOR_ALL_CHILDREN
}

private final Log log = getLog();

private final Class<cz.cvut.spipes.modules.Parameter> PARAM_ANNOTATION = cz.cvut.spipes.modules.Parameter.class;

private final Class<SPipesModule> 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) {
Expand All @@ -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<MavenProject> submodules = project.getCollectedProjects();

//todo create base RDF structure
getLog().info("Generating an RDF for all sub-modules");
var model = initDefaultModel();
for (MavenProject submodule : (List<MavenProject>) 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<Class<?>> 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<Class<?>> 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<Class<?>> 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)
Expand All @@ -182,55 +194,77 @@ private List<cz.cvut.spipes.modules.Parameter> 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<cz.cvut.spipes.modules.Parameter> 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: <http://onto.fel.cvut.cz/ontologies/lib/module/> .\n" +
// "@prefix owl: <http://www.w3.org/2002/07/owl#> .\n" +
// "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n" +
// "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n" +
// "@prefix sm: <http://topbraid.org/sparqlmotion#> .\n" +
// "@prefix spin: <http://spinrdf.org/spin#> .\n" +
// "@prefix spl: <http://spinrdf.org/spl#> .\n" +
// "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\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<cz.cvut.spipes.modules.Parameter> 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
}

0 comments on commit c2316fe

Please sign in to comment.