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 + 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 + 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 + 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 + 1.8 + + + eu.wdaqua.qanary + qa.commons + + + true + true + + + + + compile + + + +