From a0d14d4d49e1267367ee0179bc40700a5b0de7d7 Mon Sep 17 00:00:00 2001 From: Tamino Dauth Date: Wed, 10 Apr 2019 20:06:41 +0200 Subject: [PATCH] Bolt support and more documentation #28 --- README.md | 23 +++++++++++-- build.sbt | 1 + .../guistatemachine/api/example/Example.scala | 32 ++++++++++++++----- .../api/neo4j/GuiStateMachineApiNeo4J.scala | 6 ++-- .../api/neo4j/Neo4jSessionFactory.scala | 17 ++++++++-- 5 files changed, 63 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a1d66eb..463b671 100644 --- a/README.md +++ b/README.md @@ -68,12 +68,29 @@ There can be different backends which manage the state machine. ### Neo4J -This backend uses the GraphDB [Neo4J](https://neo4j.com/) (community edition) with an embedded database. +This backend uses the GraphDB [Neo4J](https://neo4j.com/) (community edition). It uses [Neo4J-OGM](https://neo4j.com/docs/ogm-manual/current/) to map our types to the graph database. Each state machine is represented by a separate graph database stored in a separate directory. The relationship types correspond to actions. Each relation has the property "counter" which contains the execution counter of the action. -Visualization: -Desktop application: +We have to use a Bolt driver since the corresponding software does not support to visualize embedded databases etc. + +Useful software: + +* Visualization: +* Gephi: +* Desktop application: +* IntelliJ IDEA plugin: + +Run neo4j with Docker: +```bash +docker run \ + --publish=7474:7474 --publish=7687:7687 \ + --volume=$HOME/neo4j/data:/data \ + --volume=$HOME/neo4j/logs:/logs \ + neo4j:3.5 +``` +The user has to be part of the group `docker`. +See . diff --git a/build.sbt b/build.sbt index 5bef904..55d7f49 100644 --- a/build.sbt +++ b/build.sbt @@ -24,6 +24,7 @@ resolvers += "sonatype-snapshots" at "https://oss.sonatype.org/content/repositor libraryDependencies += "org.neo4j" % "neo4j" % "3.5.4" libraryDependencies += "org.neo4j" % "neo4j-ogm-core" % "3.1.8" libraryDependencies += "org.neo4j" % "neo4j-ogm-embedded-driver" % "3.1.8" +libraryDependencies += "org.neo4j" % "neo4j-ogm-bolt-driver" % "3.1.8" libraryDependencies += "org.neo4j" % "neo4j-bolt" % "3.5.4" // Dependencies to represent states and actions: diff --git a/src/main/scala/de/retest/guistatemachine/api/example/Example.scala b/src/main/scala/de/retest/guistatemachine/api/example/Example.scala index 578413f..96615d7 100644 --- a/src/main/scala/de/retest/guistatemachine/api/example/Example.scala +++ b/src/main/scala/de/retest/guistatemachine/api/example/Example.scala @@ -13,22 +13,38 @@ object Example extends App { private val rootElementB = getRootElement("b", 0) private val rootElementC = getRootElement("c", 0) private val action0 = new NavigateToAction("http://google.com") + private val action1 = new NavigateToAction("http://wikipedia.org") val stateMachine = GuiStateMachineApi.neo4j.createStateMachine("tmp") stateMachine.clear() - val startState = new SutState(Arrays.asList(rootElementA, rootElementB, rootElementC)) - val endState = new SutState(Arrays.asList(rootElementA)) - stateMachine.getState(startState) - stateMachine.getState(endState) + println(s"All states after clearing: ${stateMachine.getAllStates.size}") - // TODO #19 The states do not exist after this although saved. Concurrent transactions? + val startSutState = new SutState(Arrays.asList(rootElementA, rootElementB, rootElementC)) + val endSutState = new SutState(Arrays.asList(rootElementA)) - println(s"All states ${stateMachine.getAllStates.size}") + stateMachine.getState(startSutState) - stateMachine.executeAction(startState, action0, endState) + println(s"All states after adding start state: ${stateMachine.getAllStates.size}") - println(s"All states ${stateMachine.getAllStates.size}") + stateMachine.getState(endSutState) + + println(s"All states after adding end state: ${stateMachine.getAllStates.size}") + + val startStateTmp = stateMachine.getState(startSutState) + + stateMachine.executeAction(startSutState, action0, endSutState) + stateMachine.executeAction(startSutState, action1, endSutState) + + val startState = stateMachine.getState(startSutState) + val numberOfOutgoingActionTransitions = startState.getOutgoingActionTransitions.size + println(s"Number of outgoing action transitions: $numberOfOutgoingActionTransitions") // TODO #19 No outgoing actions. + + val endState = stateMachine.getState(endSutState) + val numberOfIncomingActionTransitions = endState.getIncomingActionTransitions.size + println(s"Number of incoming action transitions: $numberOfOutgoingActionTransitions") // TODO #19 No incoming actions. + + println(s"All states after executing action0: ${stateMachine.getAllStates.size}") /** * Creates a new identifying attributes collection which should only match other identifying attributes with the same ID. diff --git a/src/main/scala/de/retest/guistatemachine/api/neo4j/GuiStateMachineApiNeo4J.scala b/src/main/scala/de/retest/guistatemachine/api/neo4j/GuiStateMachineApiNeo4J.scala index 610aba7..9bc64e6 100644 --- a/src/main/scala/de/retest/guistatemachine/api/neo4j/GuiStateMachineApiNeo4J.scala +++ b/src/main/scala/de/retest/guistatemachine/api/neo4j/GuiStateMachineApiNeo4J.scala @@ -10,11 +10,11 @@ import scala.collection.concurrent.TrieMap class GuiStateMachineApiNeo4J extends GuiStateMachineApi { private val logger = Logger[GuiStateMachineApiNeo4J] private val stateMachines = TrieMap[String, GuiStateMachineNeo4J]() - // TODO #19 Load existing state machines from the disk. + // TODO #19 Load existing state machines based on Neo4J graph databases. override def createStateMachine(name: String): GuiStateMachine = { val uri = getUri(name) - Neo4jSessionFactory.getSessionFactory(uri) + Neo4jSessionFactory.getSessionFactoryEmbedded(uri) logger.info("Created new graph DB in {}.", uri) val guiStateMachine = new GuiStateMachineNeo4J(uri) @@ -26,7 +26,7 @@ class GuiStateMachineApiNeo4J extends GuiStateMachineApi { case Some(stateMachine) => stateMachine.clear() val uri = getUri(name) - Neo4jSessionFactory.getSessionFactory(uri).close() + Neo4jSessionFactory.getSessionFactoryEmbedded(uri).close() true case None => false } diff --git a/src/main/scala/de/retest/guistatemachine/api/neo4j/Neo4jSessionFactory.scala b/src/main/scala/de/retest/guistatemachine/api/neo4j/Neo4jSessionFactory.scala index 20945b2..d77a305 100644 --- a/src/main/scala/de/retest/guistatemachine/api/neo4j/Neo4jSessionFactory.scala +++ b/src/main/scala/de/retest/guistatemachine/api/neo4j/Neo4jSessionFactory.scala @@ -9,7 +9,7 @@ import scala.collection.concurrent.TrieMap object Neo4jSessionFactory { private val sessionFactories = TrieMap[String, SessionFactory]() - def getSessionFactory(uri: String): SessionFactory = sessionFactories.get(uri) match { + def getSessionFactoryEmbedded(uri: String): SessionFactory = sessionFactories.get(uri) match { case Some(sessionFactory) => sessionFactory case None => val conf = new Configuration.Builder().uri(uri).build @@ -18,9 +18,22 @@ object Neo4jSessionFactory { sessionFactory } + def getSessionFactoryBolt(uri: String): SessionFactory = sessionFactories.get(uri) match { + case Some(sessionFactory) => sessionFactory + case None => + // TODO #19 Retrieve server and login information from some user-defined config. + val conf = new Configuration.Builder() + .uri("bolt://localhost:7687") + .credentials("neo4j", "bla") + .build() + val sessionFactory = new SessionFactory(conf, this.getClass.getPackage.getName) + sessionFactories += (uri -> sessionFactory) + sessionFactory + } + def transaction[A](f: Session => A)(implicit uri: String): A = { // We have to create a session for every transaction since sessions are not thread-safe. - val session = Neo4jSessionFactory.getSessionFactory(uri).openSession() // TODO #19 Close the session at some point? + val session = sessionFactories(uri).openSession() var txn: Option[Transaction] = None try { val transaction = session.beginTransaction()