Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

Commit

Permalink
Adapt Neo4J backend to hash identifiers #19
Browse files Browse the repository at this point in the history
We do not need converters anymore. Store hash values only.
  • Loading branch information
tdauth committed Apr 10, 2019
1 parent d78e644 commit f66688e
Show file tree
Hide file tree
Showing 15 changed files with 74 additions and 385 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package de.retest.guistatemachine.api.neo4j
package de.retest.guistatemachine.api.example

import java.util.Arrays

import de.retest.guistatemachine.api.GuiStateMachineApi
Expand Down Expand Up @@ -48,7 +49,7 @@ object Example extends App {
"My Window"
)
if (numberOfContainedComponents > 0) {
r.addChildren(scala.collection.JavaConverters.seqAsJavaList[Element](0 to numberOfContainedComponents map { _ =>
r.addChildren(scala.collection.JavaConversions.seqAsJavaList[Element](0 to numberOfContainedComponents map { _ =>
getRootElement("x", 0)
}))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ class GuiStateMachinGMLSerializer(guiStateMachine: GuiStateMachine) extends GuiS

// get the gml writer
val writer =
new YedGmlWriter.Builder[SutStateIdentifier, GraphActionEdge, AnyRef](graphicsProvider, YedGmlWriter.PRINT_LABELS: _*)
.setEdgeLabelProvider(_.toString)
.setVertexLabelProvider(_.toString)
.build
new YedGmlWriter.Builder[SutStateIdentifier, GraphActionEdge, AnyRef](graphicsProvider, YedGmlWriter.PRINT_LABELS: _*).build

// write to file
val outputFile = new File(filePath)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
package de.retest.guistatemachine.api.neo4j
import de.retest.recheck.ui.descriptors.SutState
import de.retest.surili.commons.actions.Action
import org.neo4j.ogm.annotation.{EndNode, Index, RelationshipEntity, StartNode}

@RelationshipEntity(`type` = "ACTIONS")
class ActionTransitionEntity(s: SutState, e: SutState, a: Action) extends Entity {

def this() = this(null, null, null)
class ActionTransitionEntity(s: SutStateEntity, e: SutStateEntity, a: String) extends Entity {

@Index
@StartNode val start: SutState = s
@StartNode val start: SutStateEntity = s

@Index
@EndNode val end: SutState = e
@EndNode val end: SutStateEntity = e

@Index
// TODO #19 We need the previous SutState for the conversion back to the action since we rely on the retest ID only to keep the action small.
//@Convert(classOf[ActionConverter])
val actionXML: String = new ActionConverter().toGraphProperty(a)
val action: String = a

/// The number of times this action has been executed.
var counter: Int = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import org.neo4j.ogm.annotation.{GeneratedValue, Id}

abstract class Entity {
@Id @GeneratedValue private val id = 0L
def getId: Long = id
def getId: java.lang.Long = id
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package de.retest.guistatemachine.api.neo4j
import java.io.File

import com.typesafe.scalalogging.Logger
import de.retest.guistatemachine.api.impl.GuiStateMachineImpl
import de.retest.guistatemachine.api.{GuiStateMachine, GuiStateMachineApi}

import scala.collection.concurrent.TrieMap

class GuiStateMachineApiNeo4J extends GuiStateMachineApi {
private val logger = Logger[GuiStateMachineImpl]
private val logger = Logger[GuiStateMachineApiNeo4J]
private val stateMachines = TrieMap[String, GuiStateMachine]()
// TODO #19 Load existing state machines from the disk.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,43 @@
package de.retest.guistatemachine.api.neo4j

import de.retest.guistatemachine.api.{GuiStateMachine, State}
import de.retest.recheck.ui.descriptors.SutState
import de.retest.surili.commons.actions.Action
import de.retest.guistatemachine.api.{GuiStateMachine, State, SutStateIdentifier}
import org.neo4j.ogm.cypher.{ComparisonOperator, Filter}

import scala.collection.immutable.HashMap

class GuiStateMachineNeo4J(var uri: String) extends GuiStateMachine {
implicit val session = Neo4jSessionFactory.getSessionFactory(uri).openSession() // TODO #19 Close the session at some point?

override def getState(sutState: SutState): State = {
override def getState(sutStateIdentifier: SutStateIdentifier): State = {
Neo4jSessionFactory.transaction {
getNodeBySutState(sutState) match {
getNodeBySutStateIdentifier(sutStateIdentifier) match {
case None =>
// Create a new node for the SUT state in the graph database.
session.save(new SutStateEntity(sutState))
val sutStateEntity = new SutStateEntity(sutStateIdentifier)
session.save(sutStateEntity)

// Do nothing if the node for the SUT state does already exist.
case _ =>
}
}

StateNeo4J(sutState, this)
StateNeo4J(sutStateIdentifier, this)
}

override def executeAction(from: State, a: Action, to: State): State = {
from.addTransition(a, to)
to
}

override def getAllStates: Map[SutState, State] =
override def getAllStates: Map[SutStateIdentifier, State] =
Neo4jSessionFactory.transaction {
val allNodes = session.loadAll(classOf[SutStateEntity])
var result = HashMap[SutState, State]()
var result = HashMap[SutStateIdentifier, State]()
val iterator = allNodes.iterator()

while (iterator.hasNext) {
val node = iterator.next()
val sutState = node.sutState
val sutState = new SutStateIdentifier(node.id)
result = result + (sutState -> StateNeo4J(sutState, this))
}
result
}

override def getAllExploredActions: Set[Action] = Set() // TODO #19 get all relationships in a transaction

override def getActionExecutionTimes: Map[Action, Int] = Map() // TODO #19 get all execution time properties "counter" from all actions

override def clear(): Unit =
Neo4jSessionFactory.transaction {
// Deletes all nodes and relationships.
Expand All @@ -61,10 +51,8 @@ class GuiStateMachineNeo4J(var uri: String) extends GuiStateMachine {
uri = otherStateMachine.uri
}

// TODO #19 Create an index on the property "sutState": https://neo4j.com/docs/cypher-manual/current/schema/index/#schema-index-create-a-single-property-index
// TODO #19 Should always be used inside of a transaction.
private[neo4j] def getNodeBySutState(sutState: SutState): Option[SutStateEntity] = {
val filter = new Filter("sutState", ComparisonOperator.EQUALS, sutState) // TODO #19 Is this how we filter for an attribute?
private[neo4j] def getNodeBySutStateIdentifier(sutStateIdentifier: SutStateIdentifier): Option[SutStateEntity] = {
val filter = new Filter("id", ComparisonOperator.EQUALS, sutStateIdentifier.hash)
val first = session.loadAll(classOf[SutStateEntity], filter).stream().findFirst()
if (first.isPresent) {
Some(first.get())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ object Neo4jSessionFactory {
case Some(sessionFactory) => sessionFactory
case None =>
val conf = new Configuration.Builder().uri(uri).build
val sessionFactory = new SessionFactory(conf, "de.retest.guistatemachine.api.neo4j")
val sessionFactory = new SessionFactory(conf, this.getClass.getPackage.getName)
sessionFactories += (uri -> sessionFactory)
sessionFactory
}
Expand Down
68 changes: 39 additions & 29 deletions src/main/scala/de/retest/guistatemachine/api/neo4j/StateNeo4J.scala
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
package de.retest.guistatemachine.api.neo4j
import de.retest.guistatemachine.api.{ActionTransitions, State}
import de.retest.recheck.ui.descriptors.SutState
import de.retest.surili.commons.actions.Action
import de.retest.guistatemachine.api.{ActionIdentifier, ActionTransitions, State, SutStateIdentifier}
import org.neo4j.ogm.cypher.{ComparisonOperator, Filter}

import scala.collection.immutable.HashMap

case class StateNeo4J(sutState: SutState, guiStateMachine: GuiStateMachineNeo4J) extends State {
case class StateNeo4J(sutStateIdentifier: SutStateIdentifier, guiStateMachine: GuiStateMachineNeo4J) extends State {
implicit val session = guiStateMachine.session

override def getSutState: SutState = sutState
override def getOutgoingActionTransitions: Map[Action, ActionTransitions] =
override def getSutStateIdentifier: SutStateIdentifier = sutStateIdentifier
// TODO #19 Can we somehow convert the outgoing relations directly from the SutStateEntity?
override def getOutgoingActionTransitions: Map[ActionIdentifier, ActionTransitions] =
Neo4jSessionFactory.transaction {
val filter = new Filter("start", ComparisonOperator.EQUALS, sutState)
val filter = new Filter("start", ComparisonOperator.EQUALS, sutStateIdentifier.hash)
val transitions = session.loadAll(classOf[ActionTransitionEntity], filter)
var result = HashMap[Action, ActionTransitions]()
var result = HashMap[ActionIdentifier, ActionTransitions]()
val iterator = transitions.iterator()
while (iterator.hasNext) {
val relationship = iterator.next()
val action = new ActionConverter(Some(relationship.start)).toEntityAttribute(relationship.actionXML)
val targetSutState = relationship.end
val action = new ActionIdentifier(relationship.action)
val targetSutState = new SutStateIdentifier(relationship.end.id)
val counter = relationship.counter
val actionTransitions = if (result.contains(action)) {
val existing = result(action)
Expand All @@ -31,16 +30,16 @@ case class StateNeo4J(sutState: SutState, guiStateMachine: GuiStateMachineNeo4J)
}
result
}

def getIncomingActionTransitions: Map[Action, ActionTransitions] = Neo4jSessionFactory.transaction {
val filter = new Filter("end", ComparisonOperator.EQUALS, sutState)
// TODO #19 Can we somehow convert the incoming relations directly from the SutStateEntity?
def getIncomingActionTransitions: Map[ActionIdentifier, ActionTransitions] = Neo4jSessionFactory.transaction {
val filter = new Filter("end", ComparisonOperator.EQUALS, sutStateIdentifier.hash)
val transitions = session.loadAll(classOf[ActionTransitionEntity], filter)
var result = HashMap[Action, ActionTransitions]()
var result = HashMap[ActionIdentifier, ActionTransitions]()
val iterator = transitions.iterator()
while (iterator.hasNext) {
val relationship = iterator.next()
val action = new ActionConverter(Some(relationship.start)).toEntityAttribute(relationship.actionXML)
val sourceSutState = relationship.start
val action = new ActionIdentifier(relationship.action)
val sourceSutState = new SutStateIdentifier(relationship.start.id)
val counter = relationship.counter
val actionTransitions = if (result.contains(action)) {
val existing = result(action)
Expand All @@ -53,22 +52,33 @@ case class StateNeo4J(sutState: SutState, guiStateMachine: GuiStateMachineNeo4J)
result
}

private[api] override def addTransition(a: Action, to: State): Int = Neo4jSessionFactory.transaction {
val filterStart = new Filter("start", ComparisonOperator.EQUALS, sutState)
val filterAction = new Filter("action", ComparisonOperator.EQUALS, a)
val targetSutState = to.asInstanceOf[StateNeo4J].sutState
val filterEnd = new Filter("end", ComparisonOperator.EQUALS, targetSutState)
val transitions = session.loadAll(classOf[ActionTransitionEntity], filterStart.and(filterAction).and(filterEnd))
val first = transitions.stream().findFirst()
val counter = if (first.isPresent) {
first.get().counter = first.get().counter + 1
session.save(first.get())
first.get().counter
private[api] override def addTransition(a: ActionIdentifier, to: State): Int = Neo4jSessionFactory.transaction {
/*
TODO #19 Filter for start and end states.
val filterStart = new Filter("start", ComparisonOperator.EQUALS, sutStateIdentifier.hash)
val filterEnd = new Filter("end", ComparisonOperator.EQUALS, targetSutStateIdentifier.hash)
filterStart.and(filterAction).and(filterEnd)
*/

val filterAction = new Filter("action", ComparisonOperator.EQUALS, a.hash)
val targetSutStateIdentifier = to.asInstanceOf[StateNeo4J].sutStateIdentifier

import scala.collection.JavaConversions._
val transitions = session.loadAll(classOf[ActionTransitionEntity], filterAction).toSeq

val matchingTransitions = transitions.filter(actionTransitionEntity =>
actionTransitionEntity.start.id == sutStateIdentifier.hash && actionTransitionEntity.end.id == targetSutStateIdentifier.hash)
if (matchingTransitions.nonEmpty) {
val first: ActionTransitionEntity = matchingTransitions.head
first.counter = first.counter + 1
session.save(first)
first.counter
} else {
val transition = new ActionTransitionEntity(sutState, targetSutState, a)
val sourceState = guiStateMachine.getNodeBySutStateIdentifier(sutStateIdentifier).get
val targetState = guiStateMachine.getNodeBySutStateIdentifier(targetSutStateIdentifier).get
val transition = new ActionTransitionEntity(sourceState, targetState, a.hash)
session.save(transition)
1
}
counter
}
}

This file was deleted.

Loading

0 comments on commit f66688e

Please sign in to comment.