Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove loadConfiguration where it is possible #276

Merged
merged 8 commits into from
Sep 30, 2024
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cz.cvut.spipes.modules;

import cz.cvut.spipes.exception.ModuleConfigurationInconsistentException;
import cz.cvut.spipes.modules.handlers.*;
import org.apache.jena.rdf.model.ResourceFactory;
import org.slf4j.Logger;
Expand All @@ -9,6 +10,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

/**
* The `AnnotatedAbstractModule` class extends the `AbstractModule` class and provides
Expand All @@ -18,40 +20,94 @@
* {@link Parameter} annotation to indicate that they are configurable parameters.
* The class will automatically detect these parameters, validate them, and populate
* them with the appropriate values during the module's initialization.
*
* <p>The {@link #loadConfiguration()} method is overridden to handle this process.
* It identifies all fields annotated with {@link Parameter}, checks for duplicate
* parameter names, and uses the appropriate `Setter` and `Handler` implementations
* to assign values to the fields.
*/
public abstract class AnnotatedAbstractModule extends AbstractModule {

private static final Logger log = LoggerFactory.getLogger(AnnotatedAbstractModule.class);

/**
* Loads the configuration for the module by scanning fields annotated with {@link Parameter}
* and setting their values using the appropriate {@link Setter} and {@link Handler} implementations.
*
* <p>This method is responsible for processing the fields declared in the class and its superclasses,
* that are annotated with {@link Parameter}. It ensures that each field is processed only once and
* verifies that no two fields share the same {@link Parameter#iri()}. If a duplicate is found,
* it throws a {@link ModuleConfigurationInconsistentException}.
*
* <p>For each annotated field:
* <ul>
* <li>A {@link Setter} is selected based on the field's type (e.g., {@link FieldSetter} for regular fields, {@link ListSetter} for lists).</li>
* <li>The {@link HandlerRegistry} retrieves a matching {@link Handler} for the field's type,
* which is responsible for setting the value of the field based on its {@link Parameter#iri()}.</li>
* </ul>
*
* <p>This method processes all declared fields in the class and its subclasses,
* moving down through the class hierarchy until it reaches the most derived class.
* This ensures that all relevant fields are considered for configuration.</p>
*
* <p>After all automatically configurable fields have been processed, it invokes the
* {@link #loadManualConfiguration()} method to allow subclasses to handle any custom configuration
* that requires manual intervention.
*
* @throws ModuleConfigurationInconsistentException if two or more fields in the class hierarchy share the same
* {@link Parameter#iri()}.
*/
@Override
public void loadConfiguration() {
final Map<String,Field> vars = new HashMap<>();
for(final Field f: this.getClass().getDeclaredFields()) {
final Parameter p = f.getAnnotation(Parameter.class);
if ( p == null ) {
continue;
} else if (vars.containsKey(p.iri())) {
throw new RuntimeException(String.format("Two parameters have same iri %s", p.iri()));
} else {
vars.put(p.iri(), f);
}

log.trace("Processing parameter {} ", f.getName());
Class<? extends AnnotatedAbstractModule> clazz = this.getClass();

Stack<Class<? extends AnnotatedAbstractModule>> classStack = new Stack<>();

while (clazz != AnnotatedAbstractModule.class) {
classStack.push(clazz);
clazz = (Class<? extends AnnotatedAbstractModule>) clazz.getSuperclass();
}

while(!classStack.isEmpty()){
clazz = classStack.pop();
final Map<String, Field> vars = new HashMap<>();
for (final Field f : clazz.getDeclaredFields()) {
final Parameter p = f.getAnnotation(Parameter.class);
if (p == null) {
continue;
} else if (vars.containsKey(p.iri())) {
throw new ModuleConfigurationInconsistentException(String.format("Two parameters have same iri %s", p.iri()));
} else {
vars.put(p.iri(), f);
}

Setter setter;
if(f.getType() == List.class){
setter = new ListSetter(f, this);
}else{
setter = new FieldSetter(f, this);
log.trace("Processing parameter {} ", f.getName());

Setter setter;
if (f.getType() == List.class) {
setter = new ListSetter(f, this);
} else {
setter = new FieldSetter(f, this);
}
HandlerRegistry handlerRegistry = HandlerRegistry.getInstance();
Handler<?> handler = handlerRegistry.getHandler(f.getType(), resource, executionContext, setter);
handler.setValueByProperty(ResourceFactory.createProperty(p.iri()));
}
HandlerRegistry handlerRegistry = HandlerRegistry.getInstance();
Handler<?> handler = handlerRegistry.getHandler(f.getType(), resource, executionContext, setter);
handler.setValueByProperty(ResourceFactory.createProperty(p.iri()));
}
loadManualConfiguration();
}

/**
* This abstract method is intended to be overridden by subclasses to manually load
* specific configurations that are not automatically processed by the
* {@link #loadConfiguration()} method.
* <p>
* The {@link #loadConfiguration()} method first handles automated configuration by
* scanning annotated fields and invoking handlers to set their values. Once that
* process is complete, {@code loadManualConfiguration} is called to allow subclasses
* to define any additional custom configuration logic that is required.
* <p>
* Subclasses can choose to override this method to provide custom configurations
* that cannot be handled automatically. If no manual configuration is necessary,
* the default implementation can be used without changes.
*/
public void loadManualConfiguration(){
// Default implementation: no manual configuration
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cz.cvut.spipes.modules.handlers;

import cz.cvut.spipes.engine.ExecutionContext;
import cz.cvut.spipes.registry.StreamResource;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.topbraid.spin.model.Select;
Expand Down Expand Up @@ -59,6 +60,7 @@ private void initHandlers() {
registerHandler(Path.class, PathHandler.class);
registerHandler(Resource.class, ResourceHandler.class);
registerHandler(List.class, ListHandler.class);
registerHandler(StreamResource.class, StreamResourceHandler.class);
}

public synchronized Handler getHandler(Class clazz, Resource resource, ExecutionContext context, Setter setter) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cz.cvut.spipes.modules.handlers;

import cz.cvut.spipes.engine.ExecutionContext;
import cz.cvut.spipes.exception.ResourceNotFoundException;
import cz.cvut.spipes.registry.StreamResource;
import cz.cvut.spipes.registry.StreamResourceRegistry;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;

public class StreamResourceHandler extends BaseRDFNodeHandler<StreamResource> {

public StreamResourceHandler(Resource resource, ExecutionContext executionContext, Setter<? super StreamResource> setter) {
super(resource, executionContext, setter);
}

@Override
StreamResource getRDFNodeValue(RDFNode node) throws Exception {
StreamResource res = StreamResourceRegistry.getInstance().getResourceByUrl(node.asLiteral().toString());

if (res == null) {
throw new ResourceNotFoundException("Stream resource " + node.asLiteral().toString() + " not found. ");
}

return res;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,13 @@
@SPipesModule(label = "dataset discovery v1", comment =
"Discovers dataset based on keyword userInput in repository linked.opendata.cz-federated-descriptor-faceted-search " +
"hosted at http://onto.fel.cvut.cz/rdf4j-server.")
public class DatasetDiscoveryModule extends AbstractModule {
public class DatasetDiscoveryModule extends AnnotatedAbstractModule {

private static final String TYPE_URI = KBSS_MODULE.uri + "dataset-discovery-v1";

private static final Property P_USER_INPUT = getParameter("prp-user-input");
@Parameter(iri = TYPE_URI + "/" + "prp-user-input", comment = "Keywords query. Keywords are separated by space.")
private String userInput;

private static Property getParameter(final String name) {
return ResourceFactory.createProperty(TYPE_URI + "/" + name);
}

private List<String> getDatasetsForQuery(final String s, final String endpoint) {
final List<String> datasets = new ArrayList<>();

Expand Down Expand Up @@ -138,8 +133,4 @@ public String getTypeURI() {
return TYPE_URI;
}

@Override
public void loadConfiguration() {
userInput = this.getStringPropertyValue(P_USER_INPUT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import cz.cvut.spipes.constants.KBSS_MODULE;
import cz.cvut.spipes.engine.ExecutionContext;
import cz.cvut.spipes.modules.annotations.SPipesModule;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.Model;
Expand All @@ -20,18 +22,13 @@
import java.util.Optional;

@Slf4j
@Getter
@Setter
@SPipesModule(label = "get dataset descriptors v1", comment = "Retrieve dataset descriptor for dataset" +
" with dataset-iri in endpoint-url.")
public class GetDatasetDescriptorsModule extends AbstractModule {
public class GetDatasetDescriptorsModule extends AnnotatedAbstractModule {

private static final String TYPE_URI = KBSS_MODULE.uri + "get-dataset-descriptors-v1";
private static final String PARAM_URI = TYPE_URI + "/";

/**
* URL of the Sesame server.
*/
private static final Property P_DATASET_IRI = getParameter("p-dataset-iri");
private static final Property P_ENDPOINT_URL = getParameter("endpoint-url");

@Parameter(iri = TYPE_URI + "/" + "p-dataset-iri", comment = "IRI of the dataset.")// TODO - revise comment
private String prpDatasetIri;
Expand All @@ -40,18 +37,6 @@ public class GetDatasetDescriptorsModule extends AbstractModule {
" is 'http://onto.fel.cvut.cz/rdf4j-server/repositories/descriptors-metadata'")
private String endpointUrl = "http://onto.fel.cvut.cz/rdf4j-server/repositories/descriptors-metadata";

private static Property getParameter(final String name) {
return ResourceFactory.createProperty(TYPE_URI + "/" + name);
}

public String getPrpDatasetIri() {
return prpDatasetIri;
}

public void setPrpDatasetIri(String prpDatasetIri) {
this.prpDatasetIri = prpDatasetIri;
}

@Override
ExecutionContext executeSelf() {
prpDatasetIri = executionContext.getVariablesBinding().getNode("p-dataset-iri").toString();
Expand Down Expand Up @@ -96,9 +81,4 @@ public String getTypeURI() {
return TYPE_URI;
}

@Override
public void loadConfiguration() {
prpDatasetIri = this.getStringPropertyValue(P_DATASET_IRI);
endpointUrl = Optional.ofNullable(this.getStringPropertyValue(P_ENDPOINT_URL)).orElse(endpointUrl);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import cz.cvut.spipes.registry.StreamResource;
import cz.cvut.spipes.registry.StreamResourceRegistry;
import cz.cvut.spipes.util.JenaUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.util.FileUtils;
Expand All @@ -34,14 +36,15 @@
import java.util.Arrays;

@Slf4j
@Getter
@Setter
@SPipesModule(label = "import e5x", comment = "Convert e5x xml files to rdf.")
public class ImportE5XModule extends AbstractModule {
public class ImportE5XModule extends AnnotatedAbstractModule {

// TODO - this parameter id defined with IRI <http://onto.fel.cvut.cz/ontologies/lib/module-param/has-resource-uri> in s-pipes-modules\module.sms.ttl
// TODO - we should be able to annotate directly "StreamResource e5xResource" instead
@Parameter(iri = KBSS_MODULE.has_resource_uri, comment = "Uri of a resource referencing content of an e5x file.")
private String e5xResourceUriStr;

@Parameter(iri = KBSS_MODULE.has_resource_uri, comment = "Uri of a resource referencing content of an e5x file.")
StreamResource e5xResource;

private boolean computeEccairsToAviationSafetyOntologyMapping = true;
Expand Down Expand Up @@ -147,44 +150,4 @@ public String getTypeURI() {
return KBSS_MODULE.uri + "import-e5x";
}

@Override
public void loadConfiguration() {
e5xResourceUriStr = getEffectiveValue(KBSS_MODULE.JENA.has_resource_uri).asLiteral().toString();
e5xResource = getResourceByUri(e5xResourceUriStr);
}

public String getE5xResourceUri() {
return e5xResource.getUri();
}

public StreamResource getE5xResource() {
return e5xResource;
}

public void setE5xResourceUri(String e5xResourceUri) {
e5xResource = getResourceByUri(e5xResourceUri);
}

public void setE5xResource(@NotNull StreamResource e5xResource) {
this.e5xResource = e5xResource;
}

@NotNull
private StreamResource getResourceByUri(@NotNull String e5xResourceUriStr) {

StreamResource res = StreamResourceRegistry.getInstance().getResourceByUrl(e5xResourceUriStr);

if (res == null) {
throw new ResourceNotFoundException("Stream resource " + e5xResourceUriStr + " not found. ");
}
return res;
}

public boolean isComputeEccairsToAviationSafetyOntologyMapping() {
return computeEccairsToAviationSafetyOntologyMapping;
}

public void setComputeEccairsToAviationSafetyOntologyMapping(boolean computeEccairsToAviationSafetyOntologyMapping) {
this.computeEccairsToAviationSafetyOntologyMapping = computeEccairsToAviationSafetyOntologyMapping;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public class IdentityModule extends AnnotatedAbstractModule {

private static final Logger LOG = LoggerFactory.getLogger(IdentityModule.class);


@Override
ExecutionContext executeSelf() {
return executionContext;
Expand All @@ -22,7 +21,4 @@ public String getTypeURI() {
return KBSS_MODULE.uri + "identity";
}

@Override
public void loadConfiguration() {
}
}
Loading
Loading