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()