From e97d4fc5fe4e03f52bc4ebc6e2ba302ec148f8b7 Mon Sep 17 00:00:00 2001 From: Barry Nouwt Date: Fri, 30 Aug 2024 17:02:38 +0200 Subject: [PATCH 1/6] Added a more useful ReasonerPlan toString. Using the graphviz visualization. --- .../engine/reasoner/ReasonerPlan.java | 5 +++++ .../engine/reasoner/rulestore/RuleStore.java | 18 +++++++++++++----- .../smartconnector/impl/ReasonerProcessor.java | 18 ++++++++++++------ 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java index 537234c17..a5421b3bc 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java @@ -356,4 +356,9 @@ public RuleStore getStore() { return this.store; } + @Override + public String toString() { + return "ReasonerPlan [link=" + this.store.getGraphVizCode(this, true) + "]"; + } + } diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java index f0fc54426..d0df6b67b 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java @@ -154,11 +154,19 @@ public void reset() { } } + public void printGraphVizCode(ReasonerPlan aPlan) { + LOG.info(getGraphVizCode(aPlan, false)); + } + /** * Prints all the rules and the connections between them in GraphViz encoding. - * Use code in: {@link http://viz-js.com/} + * Use code in: {@link https://dreampuf.github.io/GraphvizOnline/} */ - public void printGraphVizCode(ReasonerPlan aPlan) { + public void printGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) { + LOG.info(getGraphVizCode(aPlan, urlOnly)); + } + + public String getGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) { String color = "red"; String width = "2"; @@ -251,9 +259,9 @@ public void printGraphVizCode(ReasonerPlan aPlan) { sb.append("}"); - LOG.info("Visualize on website: https://dreampuf.github.io/GraphvizOnline/#" - + URLEncoder.encode(sb.toString(), StandardCharsets.UTF_8).replaceAll("\\+", "%20") + "\n" - + sb.toString()); + return "Visualize on website: https://dreampuf.github.io/GraphvizOnline/#" + + URLEncoder.encode(sb.toString(), StandardCharsets.UTF_8).replaceAll("\\+", "%20") + + (urlOnly ? "" : "\n" + sb.toString()); } private String toStringRule(BaseRule neighR) { diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java index 529286dd0..4a803b1da 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java @@ -106,8 +106,10 @@ public ReasonerProcessor(Set knowledgeInteractions, Me if (kii.getType().equals(Type.ANSWER)) { AnswerKnowledgeInteraction aki = (AnswerKnowledgeInteraction) ki; GraphPattern gp = aki.getPattern(); - store.addRule(new Rule(ruleName, new HashSet<>(translateGraphPatternTo(gp)), - new AnswerBindingSetHandler(kii))); + Rule aRule = new Rule(ruleName, new HashSet<>(translateGraphPatternTo(gp)), + new AnswerBindingSetHandler(kii)); + store.addRule(aRule); + LOG.debug("Adding ANSWER to store: {}", aRule); } else if (kii.getType().equals(Type.REACT)) { ReactKnowledgeInteraction rki = (ReactKnowledgeInteraction) ki; GraphPattern argGp = rki.getArgument(); @@ -124,6 +126,7 @@ public ReasonerProcessor(Set knowledgeInteractions, Me } store.addRule(aRule); + LOG.debug("Adding REACT to store: {}", aRule); } } @@ -144,8 +147,10 @@ public void planAskInteraction(MyKnowledgeInteractionInfo aAKI) { ProactiveRule aRule = new ProactiveRule(ruleName, translateGraphPatternTo(aki.getPattern()), new HashSet<>()); this.store.addRule(aRule); - this.reasonerPlan = new ReasonerPlan(this.store, aRule, - ki.includeMetaKIs() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy); + MatchStrategy aStrategy = ki.isMeta() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy; + LOG.debug("Creating reasoner plan with strategy: {}", aStrategy); + this.reasonerPlan = new ReasonerPlan(this.store, aRule, aStrategy); + } else { LOG.warn("Type should be Ask, not {}", this.myKnowledgeInteraction.getType()); this.finalBindingSetFuture.complete(new eu.knowledge.engine.reasoner.api.BindingSet()); @@ -210,8 +215,9 @@ public void planPostInteraction(MyKnowledgeInteractionInfo aPKI) { ProactiveRule aRule = new ProactiveRule(ruleName, new HashSet<>(), new HashSet<>(translatedGraphPattern)); store.addRule(aRule); - this.reasonerPlan = new ReasonerPlan(this.store, aRule, - pki.includeMetaKIs() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy); + MatchStrategy aStrategy = pki.isMeta() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy; + LOG.debug("Creating reasoner plan with strategy: {}", aStrategy); + this.reasonerPlan = new ReasonerPlan(this.store, aRule, aStrategy); } else { LOG.warn("Type should be Post, not {}", this.myKnowledgeInteraction.getType()); From 2cafb7ead7680b6d2805068d392ab8e104547b7c Mon Sep 17 00:00:00 2001 From: Barry Nouwt Date: Fri, 6 Sep 2024 16:54:55 +0200 Subject: [PATCH 2/6] Change back to includeMetaKIs(). We need to think about how to solve this, because if we change it to isMeta() (which it should be), it runs out of memory because the MockedKnowledgeBase (and KnowledgeNetwork) are not meta, but we do not want to run those in normal mode (apparently). --- .../engine/smartconnector/impl/ReasonerProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java index 4a803b1da..4a196d622 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/ReasonerProcessor.java @@ -147,7 +147,7 @@ public void planAskInteraction(MyKnowledgeInteractionInfo aAKI) { ProactiveRule aRule = new ProactiveRule(ruleName, translateGraphPatternTo(aki.getPattern()), new HashSet<>()); this.store.addRule(aRule); - MatchStrategy aStrategy = ki.isMeta() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy; + MatchStrategy aStrategy = ki.includeMetaKIs() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy; LOG.debug("Creating reasoner plan with strategy: {}", aStrategy); this.reasonerPlan = new ReasonerPlan(this.store, aRule, aStrategy); @@ -215,7 +215,7 @@ public void planPostInteraction(MyKnowledgeInteractionInfo aPKI) { ProactiveRule aRule = new ProactiveRule(ruleName, new HashSet<>(), new HashSet<>(translatedGraphPattern)); store.addRule(aRule); - MatchStrategy aStrategy = pki.isMeta() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy; + MatchStrategy aStrategy = pki.includeMetaKIs() ? MatchStrategy.ENTRY_LEVEL : this.defaultStrategy; LOG.debug("Creating reasoner plan with strategy: {}", aStrategy); this.reasonerPlan = new ReasonerPlan(this.store, aRule, aStrategy); From 836779b7c79f7be03b8fb7179d153cef1adb90f1 Mon Sep 17 00:00:00 2001 From: Barry Nouwt Date: Mon, 23 Sep 2024 10:09:04 +0200 Subject: [PATCH 3/6] Improve performance by caching hashcode. --- .../knowledge/engine/reasoner/BaseRule.java | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java index d3108c421..906e28d52 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java @@ -34,6 +34,11 @@ public class BaseRule { public static final String ARROW = "->"; + /** + * Precalculated hashcode to improve performance of the matching algorithm. + */ + private int hashCodeValue; + /** * A comparator to make sure the smaller matches collection is ordered from big * to small. @@ -147,6 +152,7 @@ protected BaseRule(Set anAntecedent, Set aConseque this.antecedent = anAntecedent; this.consequent = aConsequent; + this.hashCodeValue = this.calcHashCode(); } public static Set getVars(Set aPattern) { @@ -287,8 +293,7 @@ public String getName() { return name; } - @Override - public int hashCode() { + private int calcHashCode() { final int prime = 31; int result = 1; result = prime * result + ((antecedent == null) ? 0 : antecedent.hashCode()); @@ -297,6 +302,11 @@ public int hashCode() { return result; } + @Override + public int hashCode() { + return this.hashCodeValue; + } + @Override public boolean equals(Object obj) { if (this == obj) @@ -393,13 +403,14 @@ public static Map> getMatches(BaseRule aTargetRule, Set> combiMatchesPerTriple = getMatchesPerTriplePerRule(targetGP, new ArrayList<>(someCandidateRules), antecedentOfTarget); - printCombiMatchesPerTriple(combiMatchesPerTriple); - // if not every triple pattern can be matched, we stop the process if we require // a full match. - if (config.contains(MatchFlag.FULLY_COVERED) && combiMatchesPerTriple.keySet().size() < targetGP.size()) + if (targetGP.isEmpty() || (config.contains(MatchFlag.FULLY_COVERED) + && combiMatchesPerTriple.keySet().size() < targetGP.size())) return new HashMap<>(); + printCombiMatchesPerTriple(aTargetRule, combiMatchesPerTriple); + List biggestMatches = new ArrayList<>(); List smallerMatches = new ArrayList<>(); List toBeAddedToBiggestMatches = null, toBeAddedToSmallerMatches = null; @@ -474,10 +485,12 @@ public static Map> getMatches(BaseRule aTargetRule, Set> combiMatchesPerTriple) { + private static void printCombiMatchesPerTriple(BaseRule aTargetRule, + Map> combiMatchesPerTriple) { StringBuilder sb = new StringBuilder(); int total = 1; @@ -547,7 +561,7 @@ private static void printCombiMatchesPerTriple(Map Date: Tue, 24 Sep 2024 17:59:49 +0200 Subject: [PATCH 4/6] Make part of the matching algorithm parallel. Not sure yet if it fixes the long announce times that break the build on github. --- .../knowledge/engine/reasoner/BaseRule.java | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java index 906e28d52..07bec683d 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/BaseRule.java @@ -13,8 +13,10 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import org.apache.jena.sparql.core.Var; import org.slf4j.Logger; @@ -464,23 +466,26 @@ public static Map> getMatches(BaseRule aTargetRule, Set { + return mergeCombiMatches(candidateCombiMatch, aSmallerMatch, config); + }).filter(Objects::nonNull).sorted(new CombiMatchSizeComparator()).collect(Collectors.toList()); + + // determine where to add new combi matches + for (CombiMatch newCombiMatch : newCombiMatches) { + + // merge successful, add to smaller matches + if (candidateWasMerged) { + if (isSubCombiMatch(newCombiMatch, toBeAddedToBiggestMatches)) { + toBeAddedToSmallerMatches.add(newCombiMatch); } else { - // add to biggest matches - candidateWasMerged = true; toBeAddedToBiggestMatches.add(newCombiMatch); + candidateWasMerged = true; } - + } else { + // add to biggest matches + candidateWasMerged = true; + toBeAddedToBiggestMatches.add(newCombiMatch); } } } @@ -507,8 +512,6 @@ public static Map> getMatches(BaseRule aTargetRule, Set Date: Fri, 27 Sep 2024 11:46:51 +0200 Subject: [PATCH 5/6] Improved the performance of the announcing of SCs. With many SCs they started to take a long time and started to exceed some thresholds. --- .../impl/InteractionProcessorImpl.java | 94 +++++++++++-------- .../TestRegisterSmartConnectorWithSameId.java | 4 +- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java index 3a9e7d044..f72fb426c 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java @@ -119,14 +119,15 @@ public AskPlan planAskFromKnowledgeBase(MyKnowledgeInteractionInfo anAKI, Recipi for (OtherKnowledgeBase otherKB : filteredOtherKnowledgeBases) { // Use the knowledge interactions from the other KB var knowledgeInteractions = otherKB.getKnowledgeInteractions().stream().filter((r) -> { - // But filter on the communicative act. These have to match! - return communicativeActMatcher(anAKI, r); - }).filter((r) -> { return anAKI.getKnowledgeInteraction().includeMetaKIs() ? true : !r.isMeta(); }); + otherKnowledgeInteractions.addAll(knowledgeInteractions.collect(Collectors.toList())); } + // But filter on the communicative act. These have to match! + filterWithCommunicativeActMatcher(anAKI, otherKnowledgeInteractions); + // create a new SingleInteractionProcessor to handle this ask. SingleInteractionProcessor processor; if (this.reasonerEnabled) { @@ -289,14 +290,14 @@ public PostPlan planPostFromKnowledgeBase(MyKnowledgeInteractionInfo aPKI, Recip for (OtherKnowledgeBase otherKB : filteredOtherKnowledgeBases) { // Use the knowledge interactions from the other KB var knowledgeInteractions = otherKB.getKnowledgeInteractions().stream().filter((r) -> { - // But filter on the communicative act. These have to match! - return communicativeActMatcher(aPKI, r); - }).filter((r) -> { return aPKI.getKnowledgeInteraction().includeMetaKIs() ? true : !r.isMeta(); }); otherKnowledgeInteractions.addAll(knowledgeInteractions.collect(Collectors.toList())); } + // But filter on the communicative act. These have to match! + filterWithCommunicativeActMatcher(aPKI, otherKnowledgeInteractions); + // create a new SingleInteractionProcessor to handle this ask. SingleInteractionProcessor processor; if (this.reasonerEnabled) { @@ -412,7 +413,8 @@ public void unsetMessageRouter() { * @return {@code true} if the communicative acts of the given * KnowledgeInteractions match, {@code false} otherwise. */ - private boolean communicativeActMatcher(MyKnowledgeInteractionInfo myKI, KnowledgeInteractionInfo otherKI) { + private void filterWithCommunicativeActMatcher(MyKnowledgeInteractionInfo myKI, + Set otherKIs) { Instant start = Instant.now(); @@ -441,23 +443,25 @@ private boolean communicativeActMatcher(MyKnowledgeInteractionInfo myKI, Knowled } // then add the other knowledge interaction communicative act - CommunicativeAct otherAct = otherKI.getKnowledgeInteraction().getAct(); - Resource otherActResource = ResourceFactory.createResource(otherKI.id + "/act"); + for (KnowledgeInteractionInfo otherKI : otherKIs) { + CommunicativeAct otherAct = otherKI.getKnowledgeInteraction().getAct(); + Resource otherActResource = ResourceFactory.createResource(otherKI.id + "/act"); - m.add(otherActResource, RDF.type, Vocab.COMMUNICATIVE_ACT); + m.add(otherActResource, RDF.type, Vocab.COMMUNICATIVE_ACT); - Resource otherRequirementPurpose = ResourceFactory.createResource(otherActResource + "/requirement"); - Resource otherSatisfactionPurpose = ResourceFactory.createResource(otherActResource + "/satisfaction"); + Resource otherRequirementPurpose = ResourceFactory.createResource(otherActResource + "/requirement"); + Resource otherSatisfactionPurpose = ResourceFactory.createResource(otherActResource + "/satisfaction"); - m.add(otherActResource, Vocab.HAS_REQ, otherRequirementPurpose); - m.add(otherSatisfactionPurpose, Vocab.HAS_SAT, otherSatisfactionPurpose); + m.add(otherActResource, Vocab.HAS_REQ, otherRequirementPurpose); + m.add(otherSatisfactionPurpose, Vocab.HAS_SAT, otherSatisfactionPurpose); - // give the purposes the correct types - for (Resource r : otherAct.getRequirementPurposes()) { - m.add(otherRequirementPurpose, RDF.type, r); - } - for (Resource r : otherAct.getSatisfactionPurposes()) { - m.add(otherSatisfactionPurpose, RDF.type, r); + // give the purposes the correct types + for (Resource r : otherAct.getRequirementPurposes()) { + m.add(otherRequirementPurpose, RDF.type, r); + } + for (Resource r : otherAct.getSatisfactionPurposes()) { + m.add(otherSatisfactionPurpose, RDF.type, r); + } } // then apply the reasoner @@ -468,32 +472,42 @@ private boolean communicativeActMatcher(MyKnowledgeInteractionInfo myKI, Knowled // faster. either we set multiple iris for the same params. Or we change the ASK // to include myReq/otherReq and mySat/otherSat vars. - // my perspective - Var reqVar = Var.alloc("req"); - Var satVar = Var.alloc("sat"); - org.apache.jena.sparql.engine.binding.Binding theFirstBinding = BindingFactory.binding(reqVar, - NodeFactory.createURI(myRequirementPurpose.toString()), satVar, - NodeFactory.createURI(otherSatisfactionPurpose.toString())); + // my and other perspective + var iter = otherKIs.iterator(); + while (iter.hasNext()) { + KnowledgeInteractionInfo otherKI = iter.next(); + Resource otherActResource = ResourceFactory.createResource(otherKI.id + "/act"); + Resource otherRequirementPurpose = ResourceFactory.createResource(otherActResource + "/requirement"); + Resource otherSatisfactionPurpose = ResourceFactory.createResource(otherActResource + "/satisfaction"); - org.apache.jena.sparql.engine.binding.Binding theSecondBinding = BindingFactory.binding(reqVar, - NodeFactory.createURI(otherRequirementPurpose.toString()), satVar, - NodeFactory.createURI(mySatisfactionPurpose.toString())); + Var reqVar = Var.alloc("req"); + Var satVar = Var.alloc("sat"); + org.apache.jena.sparql.engine.binding.Binding theFirstBinding = BindingFactory.binding(reqVar, + NodeFactory.createURI(myRequirementPurpose.toString()), satVar, + NodeFactory.createURI(otherSatisfactionPurpose.toString())); - Query q = (Query) query.clone(); - ElementData de = ((ElementData) ((ElementGroup) q.getQueryPattern()).getLast()); + org.apache.jena.sparql.engine.binding.Binding theSecondBinding = BindingFactory.binding(reqVar, + NodeFactory.createURI(otherRequirementPurpose.toString()), satVar, + NodeFactory.createURI(mySatisfactionPurpose.toString())); - List data = de.getRows(); - data.add(theFirstBinding); - data.add(theSecondBinding); + Query q = (Query) query.clone(); + ElementData de = ((ElementData) ((ElementGroup) q.getQueryPattern()).getLast()); - QueryExecution myQe = QueryExecutionFactory.create(q, infModel); - boolean execAskMy = myQe.execAsk(); - myQe.close(); + List data = de.getRows(); + data.add(theFirstBinding); + data.add(theSecondBinding); - doTheyMatch = !execAskMy; - LOG.trace("Communicative Act time ({}): {}ms", doTheyMatch, Duration.between(start, Instant.now()).toMillis()); + QueryExecution myQe = QueryExecutionFactory.create(q, infModel); + boolean execAskMy = myQe.execAsk(); + myQe.close(); + + doTheyMatch = !execAskMy; - return doTheyMatch; + if (!doTheyMatch) { + iter.remove(); + } + } + LOG.trace("Communicative Act time ({}): {}ms", doTheyMatch, Duration.between(start, Instant.now()).toMillis()); } @Override diff --git a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/runtime/messaging/TestRegisterSmartConnectorWithSameId.java b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/runtime/messaging/TestRegisterSmartConnectorWithSameId.java index 5bac8cbbe..3d5f336a7 100644 --- a/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/runtime/messaging/TestRegisterSmartConnectorWithSameId.java +++ b/smart-connector/src/test/java/eu/knowledge/engine/smartconnector/runtime/messaging/TestRegisterSmartConnectorWithSameId.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URI; +import java.util.concurrent.Phaser; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -14,10 +15,11 @@ public class TestRegisterSmartConnectorWithSameId { private static final Logger LOG = LoggerFactory.getLogger(TestRegisterSmartConnectorWithSameId.class); - + private Phaser readyPhaser = new Phaser(1); @Test public void testRegisterSmartConnectorWithSameIdInSameRuntimeThrows() { var kb1 = new MockedKnowledgeBase("http://example.org/kb1"); + kb1.setPhaser(this.readyPhaser); kb1.start(); var kb1AsWell = new MockedKnowledgeBase("http://example.org/kb1"); From 2e7882e34e566fa9a2cfecfea08f00999c4a79c9 Mon Sep 17 00:00:00 2001 From: Barry Nouwt Date: Mon, 30 Sep 2024 10:15:14 +0200 Subject: [PATCH 6/6] Several improvements found while debugging. - MessageRouterImpl now logs TimeoutExceptions explicitly. - RuleStore printGraphViz(...) now does not print duplicate edges. - performance improvement reasoner plan creation. --- .../engine/reasoner/ReasonerPlan.java | 7 +-- .../engine/reasoner/rulestore/RuleStore.java | 44 +++++++++++----- .../impl/InteractionProcessorImpl.java | 5 +- .../impl/MessageRouterImpl.java | 50 +++++++++++-------- 4 files changed, 66 insertions(+), 40 deletions(-) diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java index a03f67206..d4fcb64fe 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/ReasonerPlan.java @@ -267,13 +267,14 @@ private RuleNode createOrGetReasonerNode(BaseRule aRule, BaseRule aParent) { // determine whether our parent matches us partially boolean ourAntecedentFullyMatchesParentConsequent = false; - if (aParent != null && this.store.getAntecedentNeighbors(aRule, this.matchConfig).containsKey(aParent)) { + Map> antecedentNeighbors = this.store.getAntecedentNeighbors(aRule, this.matchConfig); + if (aParent != null && antecedentNeighbors.containsKey(aParent)) { ourAntecedentFullyMatchesParentConsequent = antecedentFullyMatchesConsequent(aRule, aParent, - this.store.getAntecedentNeighbors(aRule, this.matchConfig).get(aParent)); + antecedentNeighbors.get(aParent)); } if (!ourAntecedentFullyMatchesParentConsequent) { - this.store.getAntecedentNeighbors(aRule, this.matchConfig).forEach((rule, matches) -> { + antecedentNeighbors.forEach((rule, matches) -> { if (!(rule instanceof ProactiveRule)) { assert reasonerNode instanceof AntSide; var newNode = createOrGetReasonerNode(rule, aRule); diff --git a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java index fbbba5748..123116de0 100644 --- a/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java +++ b/reasoner/src/main/java/eu/knowledge/engine/reasoner/rulestore/RuleStore.java @@ -6,12 +6,13 @@ import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,7 +70,7 @@ public void addRules(Set someRules) { */ public Set getRules() { - return this.ruleToRuleNode.values().stream().map(rn -> rn.getRule()).collect(Collectors.toSet()); + return this.ruleToRuleNode.keySet(); } /** @@ -174,19 +175,15 @@ public String getGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) { StringBuilder sb = new StringBuilder(); - sb.append("digraph {\n"); + sb.append("strict digraph {\n"); Map ruleToName = new HashMap<>(); - int ruleNumber = 1; - for (MatchNode r : ruleToRuleNode.values()) { String currentName = ruleToName.get(r.getRule()); - boolean sourceInPlan = false, destInPlan = false; if (currentName == null) { currentName = /* "rule" + ruleNumber; */ generateName(r.getRule()); assert !currentName.isEmpty(); - ruleNumber++; String replaceAll = toStringRule(r.getRule()).replaceAll("\\\"", "\\\\\""); // check the colouring @@ -195,7 +192,6 @@ public String getGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) { RuleNode rn = aPlan.getRuleNodeForRule(r.getRule()); if (rn != null) { pen = "color=\"" + color + "\", penwidth=\"" + width + "\","; - sourceInPlan = true; } } @@ -209,16 +205,17 @@ public String getGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) { ruleToName.put(r.getRule(), currentName); } - + Map> antecedentNeighbors = r.getAntecedentNeighbors(); Set anteNeigh = antecedentNeighbors.keySet(); String neighName; + for (BaseRule neighR : anteNeigh) { neighName = ruleToName.get(neighR); + if (neighName == null) { neighName = /* "rule" + ruleNumber; */ generateName(neighR); assert !neighName.isEmpty(); - ruleNumber++; String replaceAll = toStringRule(neighR).replaceAll("\\\"", "\\\\\""); // check the colouring @@ -227,7 +224,6 @@ public String getGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) { RuleNode rn = aPlan.getRuleNodeForRule(neighR); if (rn != null) { pen = "color=\"" + color + "\", penwidth=\"" + width + "\","; - destInPlan = true; } } @@ -238,6 +234,7 @@ public String getGraphVizCode(ReasonerPlan aPlan, boolean urlOnly) { sb.append(neighName).append("[").append(shape).append(pen).append("tooltip=").append("\"") .append(replaceAll).append("\"").append("]").append("\n"); + ruleToName.put(neighR, neighName); } @@ -285,6 +282,7 @@ private String trimAtLength(String aString, int aLength) { * * @param r * @return + * @throws NoSuchAlgorithmException */ private String generateName(BaseRule r) { @@ -310,7 +308,29 @@ private String generateName(BaseRule r) { String consequent = trimAtLength(sb.toString(), MAX_STR_LENGTH); - return "\"" + Integer.toHexString(r.hashCode()) + "\\n" + antecedent + "->\\n" + consequent + "\""; + String name = r.getName(); + MessageDigest digest; + byte[] encodedhash = new byte[0]; + try { + digest = MessageDigest.getInstance("SHA-256"); + encodedhash = digest.digest(name.getBytes(StandardCharsets.UTF_8)); + } catch (NoSuchAlgorithmException e) { + LOG.error("{}", e); + } + + return "\"" + bytesToHex(encodedhash) + "\\n" + antecedent + "->\\n" + consequent + "\""; + } + + private static String bytesToHex(byte[] hash) { + StringBuilder hexString = new StringBuilder(2 * hash.length); + for (int i = 0; i < hash.length; i++) { + String hex = Integer.toHexString(0xff & hash[i]); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); } private String generateName(TriplePattern tp) { diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java index f72fb426c..d6ca242e8 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/InteractionProcessorImpl.java @@ -409,9 +409,8 @@ public void unsetMessageRouter() { * model. * * @param myKI - * @param otherKI - * @return {@code true} if the communicative acts of the given - * KnowledgeInteractions match, {@code false} otherwise. + * @param otherKIs The collection that will be modified by removing all + * non-matching KIs. */ private void filterWithCommunicativeActMatcher(MyKnowledgeInteractionInfo myKI, Set otherKIs) { diff --git a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/MessageRouterImpl.java b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/MessageRouterImpl.java index 62d7152ab..e4114a92d 100644 --- a/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/MessageRouterImpl.java +++ b/smart-connector/src/main/java/eu/knowledge/engine/smartconnector/impl/MessageRouterImpl.java @@ -8,6 +8,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.slf4j.Logger; @@ -104,6 +105,17 @@ public CompletableFuture sendAskMessage(AskMessage askMessage) th } future.whenComplete((m, e) -> { + if (m == null) + if (e != null) + if (e instanceof TimeoutException) + LOG.error("KB '{}' did not respond within {}s to AskMessage '{}'.", + askMessage.getToKnowledgeBase(), this.getWaitTimeout(), askMessage.getMessageId(), e); + else + LOG.error("A {} occurred while sending an AskMessage.", e.getClass().getSimpleName(), e); + else + LOG.error( + "The AnswerMessage future should complete either exceptionally or normally. Not with both AnswerMessage and Exception null."); + this.openAskMessages.remove(askMessage.getMessageId()); }); @@ -130,6 +142,16 @@ public CompletableFuture sendPostMessage(PostMessage postMessage) } future.whenComplete((m, e) -> { + if (m == null) + if (e != null) + if (e instanceof TimeoutException) + LOG.error("KB '{}' did not respond within {}s to PostMessage '{}'.", + postMessage.getToKnowledgeBase(), this.getWaitTimeout(), postMessage.getMessageId(), e); + else + LOG.error("A {} occurred while sending an PostMessage.", e.getClass().getSimpleName(), e); + else + LOG.error( + "The ReactMessage future should complete either exceptionally or normally. Not with both ReactMessage and Exception null."); this.openAskMessages.remove(postMessage.getMessageId()); }); @@ -210,20 +232,12 @@ public void handlePostMessage(PostMessage message) { */ @Override public void handleAnswerMessage(AnswerMessage answerMessage) { - CompletableFuture future = this.openAskMessages.remove(answerMessage.getReplyToAskMessage()); + CompletableFuture future = this.openAskMessages.get(answerMessage.getReplyToAskMessage()); if (future == null) { this.LOG.warn("I received a reply for an AskMessage with ID " + answerMessage.getReplyToAskMessage() - + ", but I don't remember sending a message with that ID"); + + ", but I don't remember sending a message with that ID. It might have taken more than {}s to respond.", + this.getWaitTimeout()); } else { - future.handle((r, e) -> { - - if (r == null) { - LOG.error("An exception has occured while handling Answer Message ", e); - return null; - } else { - return r; - } - }); LOG.debug("Received AnswerMessage: {}", answerMessage); future.complete(answerMessage); } @@ -235,22 +249,14 @@ public void handleAnswerMessage(AnswerMessage answerMessage) { */ @Override public void handleReactMessage(ReactMessage reactMessage) { - CompletableFuture future = this.openPostMessages.remove(reactMessage.getReplyToPostMessage()); + CompletableFuture future = this.openPostMessages.get(reactMessage.getReplyToPostMessage()); if (future == null) { this.LOG.warn("I received a reply for a PostMessage with ID " + reactMessage.getReplyToPostMessage() - + ", but I don't remember sending a message with that ID"); + + ", but I don't remember sending a message with that ID. It might have take more than {}s to respond.", + this.getWaitTimeout()); } else { assert reactMessage != null; assert future != null; - future.handle((r, e) -> { - - if (r == null) { - LOG.error("An exception has occured while handling React Message ", e); - return null; - } else { - return r; - } - }); LOG.debug("Received ReactMessage: {}", reactMessage); future.complete(reactMessage); }