diff --git a/qanary_commons/pom.xml b/qanary_commons/pom.xml
index 4430acc0..d57dfdb9 100644
--- a/qanary_commons/pom.xml
+++ b/qanary_commons/pom.xml
@@ -3,7 +3,7 @@
4.0.0
eu.wdaqua.qanary
qa.commons
- 3.18.1
+ 3.19.0
org.springframework.boot
spring-boot-starter-parent
@@ -262,7 +262,16 @@
jaxb-api
2.3.1
-
+
+ org.aspectj
+ aspectjrt
+ 1.9.7
+
+
+ org.aspectj
+ aspectjweaver
+
+
Stardog
@@ -424,6 +433,40 @@
+
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+ 1.14.0
+
+ 1.8
+
+ 1.8
+ true
+ true
+ ignore
+ UTF-8
+
+
+
+
+
+ compile
+
+ test-compile
+
+
+
+
diff --git a/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/LoggingAspectComponent.java b/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/LoggingAspectComponent.java
new file mode 100644
index 00000000..58c6fa2d
--- /dev/null
+++ b/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/LoggingAspectComponent.java
@@ -0,0 +1,263 @@
+package eu.wdaqua.qanary.explainability.annotations;
+
+import eu.wdaqua.qanary.commons.QanaryMessage;
+import eu.wdaqua.qanary.commons.QanaryUtils;
+import eu.wdaqua.qanary.commons.triplestoreconnectors.QanaryTripleStoreConnector;
+import eu.wdaqua.qanary.commons.triplestoreconnectors.QanaryTripleStoreConnectorQanaryInternal;
+import eu.wdaqua.qanary.exceptions.SparqlQueryFailed;
+import org.apache.jena.rdf.model.ResourceFactory;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * This class' purpose is to log all method executions within the process()-method.
+ * It is done by starting the logging process before the process()-method is called and ending it when the method returns.
+ */
+
+@Aspect
+public class LoggingAspectComponent {
+
+ private URI processGraph;
+ private QanaryTripleStoreConnector qanaryTripleStoreConnector;
+
+ public QanaryTripleStoreConnector getQanaryTripleStoreConnector() {
+ return qanaryTripleStoreConnector;
+ }
+
+ public void setQanaryTripleStoreConnector(QanaryTripleStoreConnector qanaryTripleStoreConnector) {
+ this.qanaryTripleStoreConnector = qanaryTripleStoreConnector;
+ }
+
+ public URI getProcessGraph() {
+ return processGraph;
+ }
+
+ public void setProcessGraph(URI processGraph) {
+ this.processGraph = processGraph;
+ }
+
+ public QanaryTripleStoreConnectorQanaryInternal createTriplestoreConnectorFromJoinPoint(JoinPoint joinPoint,
+ String applicationName) throws URISyntaxException {
+ QanaryMessage qanaryMessage = (QanaryMessage) joinPoint.getArgs()[2];
+ return new QanaryTripleStoreConnectorQanaryInternal(qanaryMessage.getEndpoint(), applicationName);
+ }
+
+ public URI createGraphFromJoinPoint(JoinPoint joinPoint) {
+ QanaryMessage qanaryMessage = (QanaryMessage) joinPoint.getArgs()[2];
+ return qanaryMessage.getInGraph();
+ }
+
+ private Logger logger = LoggerFactory.getLogger(LoggingAspectComponent.class);
+ private Map methodList = new HashMap<>();
+ private final String LOGGING_QUERY = "/queries/logging/insert_method_data.rq";
+ private Stack callStack = new Stack();
+ private String applicationName;
+ public final String MAP_IS_NULL_ERROR = "Passed map is null; No method logged";
+ public final String EMPTY_STACK_ITEM = "init";
+
+ public Stack getCallStack() {
+ return callStack;
+ }
+
+ public void setCallStack(Stack callStack) {
+ this.callStack = callStack;
+ }
+
+ public Map getMethodList() {
+ return methodList;
+ }
+
+ public void setMethodList(Map methodList) {
+ this.methodList = methodList;
+ }
+
+ /**
+ * Logs method by using the defined logging query.
+ *
+ * @param methodUuid UUID of the method to be logged
+ * @param method Oject that contains all details needed to get logged
+ * @throws IOException During read-process of logging-query
+ * @throws SparqlQueryFailed During update-process with the final query
+ */
+ public void logMethodData(String methodUuid, MethodObject method)
+ throws IOException, SparqlQueryFailed, NullPointerException {
+ String query = QanaryTripleStoreConnector.readFileFromResources(LOGGING_QUERY);
+ query = query.replace("?graph", "<" + this.getProcessGraph().toASCIIString() + ">");
+ query = query.replace("?annotatedBy", "");
+ query = query.replace("?a", "<" + methodUuid + ">");
+ query = query.replace("?caller", "<" + method.getCaller() + ">");
+ query = query.replace("?method", "'" + method.getMethod() + "'");
+ query = query.replace("?output", generateOutputDataRepresentation(method.getOutput()));
+ query = query.replace("?input", generateInputDataRepresentation(method.getInput()));
+ logger.info("Method-log query: {}", query);
+ this.qanaryTripleStoreConnector.update(query);
+ }
+
+ /**
+ * Transforms object list to SPARQL-conform representation to store data
+ *
+ * @param inputData
+ * @return
+ */
+ public String generateInputDataRepresentation(Object[] inputData) {
+ String representation = "(";
+ if (inputData.length == 0 || inputData == null)
+ return "()";
+ for (int i = 0; i < inputData.length; i++) {
+ Object var = inputData[i];
+ String varRepresentation = "[ " + "rdf:type \"" + var.getClass() + "\" ;" + "rdf:value \""
+ + ResourceFactory.createPlainLiteral(var.toString().replace("\n", " ").replace("\\", "")) + "\" ]";
+ representation += varRepresentation;
+ }
+ ;
+ representation += ")";
+ return representation;
+ }
+
+ /**
+ * Creates SPARQL-conform representation for the output data
+ *
+ * @param outputData
+ * @return
+ */
+ public String generateOutputDataRepresentation(Object outputData) {
+ if (outputData.equals(null))
+ return "[]";
+ return "[ rdf:type \"" +
+ ResourceFactory.createPlainLiteral(outputData.getClass().toString()) +
+ "\" ; rdf:value \""
+ + ResourceFactory.createPlainLiteral(outputData.toString().replace("\n", " ").replace("\\", "'")) +
+ "\"]";
+ }
+
+ public void logMethods(Map methodMap) throws RuntimeException {
+ // k = UUID of the actual method; v = Method details
+ try {
+ methodMap.forEach((k, v) -> {
+ try {
+ this.logMethodData(k, v);
+ } catch (IOException | SparqlQueryFailed | NullPointerException e) {
+ logger.error("Method with uuid {} was not logged due to an exception with the error message: {}", k,
+ e.getMessage());
+ }
+ });
+ } catch (NullPointerException e) {
+ logger.error(MAP_IS_NULL_ERROR);
+ }
+ }
+
+ // Separated due to overlapping pointcuts and thus condition race
+ @Pointcut("!execution(* process(eu.wdaqua.qanary.commons.QanaryMessage)) && " +
+ "(execution(* eu.wdaqua.qanary.component..*(..)) || " +
+ "execution(* eu.wdaqua.qanary.QanaryPipelineComponent..*(..)))")
+ public void storeMethodExecutionInComponent() {
+ };
+
+ @Pointcut("execution(* process(eu.wdaqua.qanary.commons.QanaryMessage))")
+ public void processExecution() {
+ };
+
+ /**
+ * Sets the graph from the executed process context (i.e. the
+ * process(QanaryMessage message) method)
+ *
+ * @param joinPoint JoinPoint
+ * @throws URISyntaxException Get the endpoint from the QanaryMessage
+ */
+ @Before(value = "processExecution()")
+ public void setGraphFromProcessExecution(JoinPoint joinPoint) throws URISyntaxException {
+ resetConfiguration();
+ QanaryMessage qanaryMessage = (QanaryMessage) joinPoint.getArgs()[0];
+ this.setProcessGraph(qanaryMessage.getInGraph());
+ if (this.getQanaryTripleStoreConnector() == null) {
+ QanaryUtils qanaryUtils = new QanaryUtils(qanaryMessage,
+ new QanaryTripleStoreConnectorQanaryInternal(qanaryMessage.getEndpoint(), this.applicationName));
+ this.setQanaryTripleStoreConnector(qanaryUtils.getQanaryTripleStoreConnector());
+ }
+ implementationStoreMethodExecutionInComponentBefore(joinPoint);
+ }
+
+ /**
+ * Sets the returned object for each stored method
+ *
+ * @param joinPoint JoinPoint
+ * @param result Returned object
+ */
+ @AfterReturning(value = "storeMethodExecutionInComponent()", returning = "result")
+ public void implementationStoreMethodExecutionInComponentAfter(JoinPoint joinPoint, Object result) {
+ String currentMethodUuid = (String) this.callStack.peek();
+ MethodObject method = this.methodList.get(currentMethodUuid);
+ method.setOutput(result);
+ this.methodList.replace(currentMethodUuid, method);
+ this.callStack.pop();
+ if (this.callStack.isEmpty()) {
+ logMethods(this.methodList);
+ this.setProcessGraph(null); // Process ended
+ this.methodList.clear(); // Clear stored methods
+ }
+ }
+
+ /**
+ * Creates a UUID for the designated method and puts it on the execution-stack.
+ *
+ * @param joinPoint JoinPoint
+ */
+ @Before(value = "storeMethodExecutionInComponent()")
+ public void implementationStoreMethodExecutionInComponentBefore(JoinPoint joinPoint) {
+ // Get caller and self-push to stack
+ String caller = checkAndGetFromStack();
+ String uuid = UUID.randomUUID().toString();
+ this.callStack.push(uuid);
+
+ // Get required data
+ String method = joinPoint.getSignature().getName();
+ Object[] input = joinPoint.getArgs();
+
+ this.methodList.put(
+ uuid,
+ new MethodObject(caller, method, input, this.applicationName));
+ }
+
+ /**
+ * Helper function to get the caller method's uuid. If the stack is empty at the
+ * beginning, it returns "init"
+ *
+ * @return UUID for the latest stack item; "" if stack is empty (first method
+ * call)
+ */
+ public String checkAndGetFromStack() {
+ try {
+ return (String) this.callStack.peek();
+ } catch (EmptyStackException e) {
+ return EMPTY_STACK_ITEM;
+ }
+ }
+
+ public void resetConfiguration() {
+ this.callStack.clear();
+ this.methodList.clear();
+ }
+
+ // APPLICATION NAME RELATED
+
+ @Pointcut("execution(* getApplicationName())")
+ public void applicationNamePointcut() {
+ }
+
+ @AfterReturning(value = "applicationNamePointcut()", returning = "result")
+ public void getApplicationNameAfterReturning(JoinPoint joinPoint, Object result) {
+ this.applicationName = (String) result;
+ logger.info("Application name set to: {}", this.applicationName);
+ }
+
+}
diff --git a/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/LoggingAspectPipeline.java b/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/LoggingAspectPipeline.java
new file mode 100644
index 00000000..0a8dc429
--- /dev/null
+++ b/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/LoggingAspectPipeline.java
@@ -0,0 +1,253 @@
+package eu.wdaqua.qanary.explainability.annotations;
+
+import org.apache.jena.atlas.web.HttpException;
+import org.apache.jena.rdf.model.ResourceFactory;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import eu.wdaqua.qanary.commons.QanaryMessage;
+import eu.wdaqua.qanary.commons.QanaryUtils;
+import eu.wdaqua.qanary.commons.triplestoreconnectors.QanaryTripleStoreConnector;
+import eu.wdaqua.qanary.commons.triplestoreconnectors.QanaryTripleStoreConnectorQanaryInternal;
+import eu.wdaqua.qanary.exceptions.SparqlQueryFailed;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.EmptyStackException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+import java.util.UUID;
+
+// Potentially outsource duplicate code from LoggingAspectComponent, when known how abstract classes can be used in AspectJ.
+@Aspect
+public class LoggingAspectPipeline {
+
+ ////////////////// CONFIG FOR PIPELINE
+ /*
+ * START: /questionanswering from controller
+ * END: RETURN /questionanswering from controller
+ * GRAPH: Fetched from
+ * QanaryTripleStoreEndpoint: Fetched from
+ */
+
+ private URI processGraph;
+ private QanaryTripleStoreConnector qanaryTripleStoreConnector;
+ private final Logger logger = LoggerFactory.getLogger(LoggingAspectPipeline.class);
+ private Map methodObjectMap = new HashMap<>();
+ private Stack methodStack = new Stack<>();
+ private final String EMPTY_STACK_ITEM = "init";
+ private final String applicationName = "QanaryPipeline";
+ private final String MAP_IS_NULL_ERROR = "Method map is null";
+ private final String LOGGING_QUERY = "/queries/logging/insert_method_data.rq";
+
+ public URI getProcessGraph() {
+ return processGraph;
+ }
+
+ public QanaryTripleStoreConnector getQanaryTripleStoreConnector() {
+ return qanaryTripleStoreConnector;
+ }
+
+ public void setProcessGraph(URI processGraph) {
+ this.processGraph = processGraph;
+ }
+
+ public void setQanaryTripleStoreConnector(QanaryTripleStoreConnector qanaryTripleStoreConnector) {
+ this.qanaryTripleStoreConnector = qanaryTripleStoreConnector;
+ }
+
+ public Map getMethodObjectMap() {
+ return methodObjectMap;
+ }
+
+ public Stack getMethodStack() {
+ return methodStack;
+ }
+
+ public void setMethodObjectMap(Map methodObjectMap) {
+ this.methodObjectMap = methodObjectMap;
+ }
+
+ public void setMethodStack(Stack methodStack) {
+ this.methodStack = methodStack;
+ }
+
+ @Pointcut(
+ "execution(public org.springframework.http.ResponseEntity> eu.wdaqua.qanary.web.QanaryQuestionAnsweringController.startquestionansweringwithtextquestion(..)) || " +
+ "execution(public org.springframework.http.ResponseEntity eu.wdaqua.qanary.web.QanaryQuestionAnsweringController.startquestionansweringwithtextquestion(..)) || " +
+ "execution(public org.springframework.http.ResponseEntity eu.wdaqua.qanary.web.QanaryQuestionAnsweringController.startquestionansweringwithtextquestionThroughJson(..)) || " +
+ "execution(public org.springframework.http.ResponseEntity> eu.wdaqua.qanary.web.QanaryQuestionAnsweringController.createQuestionAnswering(..)) || " +
+ "execution(public org.springframework.http.ResponseEntity> eu.wdaqua.qanary.web.QanaryQuestionAnsweringController.createQuestionAnsweringFull(..))"
+ ) public void startProcessForPipeline() {};
+
+ @Pointcut("execution(* eu.wdaqua.qanary.web.QanaryQuestionAnsweringController.executeComponentList(..))")
+ public void setTriplestoreAndGraphForPipeline() {};
+
+ // Any class in the package eu.wdaqua.qanary.web or eu.wdaqua.qanary(Pipeline)
+ @Pointcut("execution(* eu.wdaqua.qanary.web..*(..)) || execution(* eu.wdaqua.qanary.QanaryPipeline..*(..)) || execution(* eu.wdaqua.qanary.QanaryComponentRegistrationChangeNotifier..*(..))")
+ public void logMethodCallPointcut() {};
+
+ @Before(value = "setTriplestoreAndGraphForPipeline()")
+ public void setTriplestoreAndGraphForPipeline(JoinPoint joinPoint) throws URISyntaxException {
+ QanaryMessage qanaryMessage = (QanaryMessage) joinPoint.getArgs()[2];
+ this.processGraph = qanaryMessage.getOutGraph();
+ if (this.getQanaryTripleStoreConnector() == null) {
+ QanaryUtils qanaryUtils = new QanaryUtils(qanaryMessage,
+ new QanaryTripleStoreConnectorQanaryInternal(qanaryMessage.getEndpoint(), this.applicationName));
+ this.setQanaryTripleStoreConnector(qanaryUtils.getQanaryTripleStoreConnector());
+ }
+ logger.info("Initialized triplestore and graph for pipeline: {}, {}", this.processGraph, this.qanaryTripleStoreConnector.getFullEndpointDescription());
+ }
+
+ @Before(value = "startProcessForPipeline()")
+ public void startProcessForPipelineBefore(JoinPoint joinPoint) {
+ this.methodStack.clear();
+ this.methodObjectMap.clear();
+
+ }
+
+ @Before(value = "logMethodCallPointcut()")
+ public void logMethodCallBefore(JoinPoint joinPoint) {
+ // Get caller and self-push to stack
+ String caller = checkAndGetFromStack();
+ UUID uuid = UUID.randomUUID();
+ this.methodStack.push(uuid);
+
+ // Get required data
+ String method = joinPoint.getSignature().getName();
+ Object[] input = joinPoint.getArgs();
+
+ this.methodObjectMap.put(
+ uuid,
+ new MethodObject(caller, method, input, this.applicationName));
+ }
+
+ @AfterReturning(value = "logMethodCallPointcut()", returning = "result")
+ public void logMethodCallAfter(JoinPoint joinPoint, Object result) {
+ UUID currentMethodUuid = this.methodStack.peek();
+ MethodObject method = null;
+ try {
+ method = this.methodObjectMap.get(currentMethodUuid);
+ } catch (NullPointerException e) {
+ logger.error("Method with uuid {} was not logged, check implementation.");
+ // Further handling
+ }
+ method.setOutput(result);
+ this.methodObjectMap.replace(currentMethodUuid, method);
+ this.methodStack.pop();
+ if (this.methodStack.isEmpty()) {
+ logMethods(this.methodObjectMap);
+ this.setProcessGraph(null); // Process ended
+ this.methodObjectMap.clear(); // Clear stored methods
+ }
+ }
+
+ public String checkAndGetFromStack() {
+ try {
+ return this.methodStack.peek().toString();
+ } catch (EmptyStackException e) {
+ return EMPTY_STACK_ITEM;
+ }
+ }
+
+ /**
+ * Logs method by using the defined logging query.
+ *
+ * @param methodUuid UUID of the method to be logged
+ * @param method Oject that contains all details needed to get logged
+ * @throws IOException During read-process of logging-query
+ * @throws SparqlQueryFailed During update-process with the final query
+ */
+ public void logMethodData(String methodUuid, MethodObject method)
+ throws IOException, SparqlQueryFailed, NullPointerException {
+ String query = QanaryTripleStoreConnector.readFileFromResources(LOGGING_QUERY);
+ query = query.replace("?graph", "<" + this.getProcessGraph().toASCIIString() + ">");
+ query = query.replace("?annotatedBy", "");
+ query = query.replace("?a", "<" + methodUuid + ">");
+ query = query.replace("?caller", "<" + method.getCaller() + ">");
+ query = query.replace("?method", "'" + method.getMethod() + "'");
+ query = query.replace("?output", generateOutputDataRepresentation(method.getOutput()));
+ query = query.replace("?input", generateInputDataRepresentation(method.getInput()));
+ logger.info("Method-log query: {}", query);
+ try {
+ this.qanaryTripleStoreConnector.update(query);
+ } catch(HttpException e) {
+ logger.error("Logging failed due to an HTTP exception with the error message: {}. If this isn't a test, check implementation", e.getMessage());
+ }
+
+ }
+
+ /**
+ * Transforms object list to SPARQL-conform representation to store data
+ *
+ * @param inputData
+ * @return
+ */
+ public String generateInputDataRepresentation(Object[] inputData) {
+ String representation = "(";
+ if (inputData.length == 0 || inputData == null)
+ return "()";
+ for (int i = 0; i < inputData.length; i++) {
+ Object var = inputData[i];
+ String varRepresentation = "[ " + "rdf:type \"" + var.getClass() + "\" ;" + "rdf:value \""
+ + ResourceFactory.createPlainLiteral(var.toString().replace("\n", " ").replace("\\", "")) + "\" ]";
+ representation += varRepresentation;
+ }
+ ;
+ representation += ")";
+ return representation;
+ }
+
+ /**
+ * Creates SPARQL-conform representation for the output data
+ *
+ * @param outputData
+ * @return
+ */
+ public String generateOutputDataRepresentation(Object outputData) {
+ if (outputData.equals(null))
+ return "[]";
+ return "[ rdf:type \"" +
+ ResourceFactory.createPlainLiteral(outputData.getClass().toString()) +
+ "\" ; rdf:value \""
+ + ResourceFactory.createPlainLiteral(outputData.toString().replace("\n", " ").replace("\\", "'")) +
+ "\"]";
+ }
+
+ public void logMethods(Map methodMap) throws RuntimeException {
+ // k = UUID of the actual method; v = Method details
+ try {
+ methodMap.forEach((k, v) -> {
+ try {
+ this.logMethodData(k.toString(), v);
+ } catch (IOException | SparqlQueryFailed | NullPointerException e) {
+ logger.error("Method with uuid {} was not logged due to an exception with the error message: {}", k,
+ e.getMessage());
+ }
+ });
+ } catch (NullPointerException e) {
+ logger.error(MAP_IS_NULL_ERROR);
+ }
+ }
+
+ /*
+ * Returns whether the method call is expected in terms of the call hierarchy, i.e. the call is within the expected flow
+ * Needs to know the caller for a JoinPoint
+ * Approaches to solve:
+ * * Exclude all external methods calls (what with methods that are called from those excluded methods?)
+ * * For research purposes: Use a stack to keep track of the method calls (best case w/o parallel calls)
+ */
+ public boolean isMethodCallExpected() {
+ return false;
+ }
+
+
+
+}
diff --git a/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/MethodObject.java b/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/MethodObject.java
new file mode 100644
index 00000000..14cfaafe
--- /dev/null
+++ b/qanary_commons/src/main/java/eu/wdaqua/qanary/explainability/annotations/MethodObject.java
@@ -0,0 +1,91 @@
+package eu.wdaqua.qanary.explainability.annotations;
+
+import java.util.Arrays;
+
+public class MethodObject {
+
+// private String uuid;
+ private String caller;
+ private String method;
+ private String explanationType; // enum?
+ private String explanationValue;
+ private Object[] input;
+ private Object output;
+ private String annotatedBy;
+
+ public MethodObject(String caller, String method, Object[] input, String annotatedBy) {
+ this.caller = caller;
+ this.method = method;
+ this.input = input;
+ this.annotatedBy = annotatedBy;
+ }
+
+ public Object[] getInput() {
+ return input;
+ }
+
+ public Object getOutput() {
+ return output;
+ }
+
+ public String getAnnotatedBy() {
+ return annotatedBy;
+ }
+
+ public String getCaller() {
+ return caller;
+ }
+
+ public String getExplanationType() {
+ return explanationType;
+ }
+
+ public String getExplanationValue() {
+ return explanationValue;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setAnnotatedBy(String annotatedBy) {
+ this.annotatedBy = annotatedBy;
+ }
+
+ public void setCaller(String caller) {
+ this.caller = caller;
+ }
+
+ public void setExplanationType(String explanationType) {
+ this.explanationType = explanationType;
+ }
+
+ public void setExplanationValue(String explanationValue) {
+ this.explanationValue = explanationValue;
+ }
+
+ public void setInput(Object[] input) {
+ this.input = input;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public void setOutput(Object output) {
+ this.output = output;
+ }
+
+ @Override
+ public String toString() {
+ return "MethodObject{" +
+ "caller=" + caller +
+ ", method='" + method + '\'' +
+ ", explanationType='" + explanationType + '\'' +
+ ", explanationValue='" + explanationValue + '\'' +
+ ", input=" + Arrays.toString(input) +
+ ", output=" + output +
+ ", annotatedBy='" + annotatedBy + '\'' +
+ '}';
+ }
+}
diff --git a/qanary_commons/src/main/resources/queries/logging/insert_method_data.rq b/qanary_commons/src/main/resources/queries/logging/insert_method_data.rq
new file mode 100644
index 00000000..cf1ef329
--- /dev/null
+++ b/qanary_commons/src/main/resources/queries/logging/insert_method_data.rq
@@ -0,0 +1,26 @@
+PREFIX qa:
+PREFIX oa:
+PREFIX xsd:
+PREFIX rdf:
+PREFIX prov:
+PREFIX x:
+
+INSERT {
+GRAPH ?graph {
+ ?a rdf:type qa:AnnotationOfLogMethod ;
+ prov:actedOnBehalfOf ?caller ;
+ qa:methodName ?method ;
+# x:explanation [
+# rdf:type ?explanationType ;
+# rdf:value ?explanationValue ;
+# prov:wasGeneratedBy ?explanation_generator ;
+# x:score ?explanationScore
+# ] ;
+ x:input ?input ;
+ x:output ?output ;
+ oa:annotatedAt ?time ;
+ oa:annotatedBy ?annotatedBy .
+}}
+WHERE {
+ BIND (now() as ?time)
+}
\ No newline at end of file
diff --git a/qanary_commons/src/test/java/qa/commons/LoggingAspectTest.java b/qanary_commons/src/test/java/qa/commons/LoggingAspectTest.java
new file mode 100644
index 00000000..cdbc171d
--- /dev/null
+++ b/qanary_commons/src/test/java/qa/commons/LoggingAspectTest.java
@@ -0,0 +1,135 @@
+package qa.commons;
+
+import com.complexible.stardog.plan.filter.functions.rdfterm.Object;
+import eu.wdaqua.qanary.explainability.annotations.LoggingAspectComponent;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Stack;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class LoggingAspectTest {
+
+ /*
+ * private URI TEST_GRAPH = new URI("test-graph");
+ * private URI TEST_ENDPOINT = new URI("test-endpoint");
+ */
+
+ private LoggingAspectComponent loggingAspectComponent;
+ private JoinPoint joinPoint;
+ private Signature signature;
+
+ @BeforeEach
+ public void setup() {
+ this.loggingAspectComponent = new LoggingAspectComponent();
+ // this.loggingAspectComponent.setQanaryTripleStoreConnector(mock(QanaryTripleStoreConnectorVirtuoso.class));
+ this.loggingAspectComponent.setCallStack(new Stack<>());
+ joinPoint = mock(JoinPoint.class);
+ signature = mock(Signature.class);
+ // Mock the Signature object to return sample values
+ when(signature.getName()).thenReturn("sampleMethodName");
+ when(signature.toShortString()).thenReturn("sampleMethodSignature");
+ // Set up the JoinPoint to return the mocked Signature and CodeSignature
+ when(joinPoint.getSignature()).thenReturn(signature);
+ }
+
+ /*
+ * @Test
+ * public void logMethodDataTest() {
+ *
+ * }
+ *
+ * @Test
+ * public void logMethodsWithEmptyMap() {
+ * // Setup
+ * Logger mockLogger = mock(Logger.class);
+ * LoggingAspect loggingAspect = new LoggingAspect();
+ * ReflectionTestUtils.setField(loggingAspect, "logger", mockLogger);
+ *
+ * // Act
+ * loggingAspect.logMethods(null);
+ *
+ * // Verify
+ * Mockito.verify(mockLogger).error(loggingAspect.MAP_IS_NULL_ERROR);
+ * }
+ *
+ * @Test
+ * public void logMethodsWithNonNullMap() throws IOException, SparqlQueryFailed
+ * {
+ * Mockito.doNothing().when(qanaryTripleStoreConnector).update(any());
+ * Map testMap = new HashMap<>() {
+ * {
+ * put("1", new MethodObject(null, null, null, null));
+ * put("2", new MethodObject(null, null, null, null));
+ * put("3", new MethodObject(null, null, null, null));
+ * }
+ * };
+ * loggingAspectWired.logMethods(testMap);
+ * Mockito.verify(loggingAspectWired, Mockito.times(1)).logMethodData(any(),
+ * any());
+ * Mockito.verify(loggingAspectWired, Mockito.times(3)).logMethods(any());
+ * }
+ *
+ * @Test
+ * public void setGraphFromProcessExecutionTest() throws URISyntaxException {
+ *
+ * // Setup
+ * LoggingAspect loggingAspect = new LoggingAspect();
+ * QanaryMessage qanaryMessage = new QanaryMessage();
+ * qanaryMessage.setValues(TEST_ENDPOINT, TEST_GRAPH, TEST_GRAPH);
+ * JoinPoint joinPoint = mock(JoinPoint.class);
+ * when(joinPoint.getArgs()).thenReturn(new Object[] { qanaryMessage });
+ *
+ * // Act
+ * loggingAspect.setGraphFromProcessExecution(joinPoint);
+ *
+ * // Verify
+ * assertNotNull(loggingAspect.getCurrentProcessGraph());
+ * assertNotNull(loggingAspect.getQanaryTripleStoreConnector());
+ * }
+ */
+
+ // STACK TESTS
+
+ @Test
+ public void emptyStackTest() {
+ Assertions.assertEquals(this.loggingAspectComponent.EMPTY_STACK_ITEM,
+ this.loggingAspectComponent.checkAndGetFromStack());
+ }
+
+ @Test
+ public void nonEmptyStackTest() {
+ this.loggingAspectComponent.getCallStack().push("test");
+ Assertions.assertEquals("test", this.loggingAspectComponent.checkAndGetFromStack());
+ }
+
+ // implementationStoreMethodExecutionInComponentBefore TESTS
+
+ @Test
+ public void implementationStoreMethodExecutionInComponentBeforeTest() {
+ this.loggingAspectComponent.implementationStoreMethodExecutionInComponentBefore(this.joinPoint);
+
+ assertEquals(1, this.loggingAspectComponent.getMethodList().size());
+ assertFalse(this.loggingAspectComponent.getCallStack().empty());
+ }
+
+ // implementationStoreMethodExecutionInComponentAfter TESTS
+
+ @Test
+ public void implementationStoreMethodExecutionInComponentAfterTest() {
+ this.loggingAspectComponent.implementationStoreMethodExecutionInComponentBefore(this.joinPoint);
+ assertFalse(this.loggingAspectComponent.getCallStack().empty());
+ assertEquals(1, this.loggingAspectComponent.getMethodList().size());
+ this.loggingAspectComponent.implementationStoreMethodExecutionInComponentAfter(this.joinPoint, mock(Object.class));
+
+ assertEquals(0, this.loggingAspectComponent.getMethodList().size());
+ }
+
+}
diff --git a/qanary_component-parent/pom.xml b/qanary_component-parent/pom.xml
index 3310ebc1..343f4791 100644
--- a/qanary_component-parent/pom.xml
+++ b/qanary_component-parent/pom.xml
@@ -279,6 +279,31 @@
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+ 1.14.0
+
+ 1.8
+
+ 1.8
+
+
+ eu.wdaqua.qanary
+ qa.commons
+
+
+ true
+ true
+
+
+
+
+ compile
+
+
+
+
diff --git a/qanary_component-template/pom.xml b/qanary_component-template/pom.xml
index 36193d58..0f714e33 100644
--- a/qanary_component-template/pom.xml
+++ b/qanary_component-template/pom.xml
@@ -184,12 +184,6 @@
-
org.apache.maven.plugins
maven-enforcer-plugin
@@ -306,6 +300,31 @@
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+ 1.14.0
+
+ 1.8
+
+ 1.8
+
+
+ eu.wdaqua.qanary
+ qa.commons
+
+
+ true
+ true
+
+
+
+
+ compile
+
+
+
+
diff --git a/qanary_pipeline-template/pom.xml b/qanary_pipeline-template/pom.xml
index 7ddcf4af..2c47b4a5 100644
--- a/qanary_pipeline-template/pom.xml
+++ b/qanary_pipeline-template/pom.xml
@@ -448,6 +448,31 @@
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+ 1.14.0
+
+ 1.8
+
+ 1.8
+
+
+ eu.wdaqua.qanary
+ qa.commons
+
+
+ true
+ true
+
+
+
+
+ compile
+
+
+
+