module Neo4j
# == Handles Transactional Events
#
# You can use this to receive event before the transaction commits.
# The following events are supported:
# * on_neo4j_started
# * on_neo4j_shutdown
# * on_node_created
# * on_node_deleted
# * on_relationship_created
# * on_relationship_deleted
# * on_property_changed
# * on_rel_property_changed
# * on_after_commit
#
# ==== on_neo4j_started(db)
#
# Called when the neo4j engine starts.
# Notice that the neo4j will be started automatically when the first neo4j operation is performed.
# You can also start Neo4j: Neo4j.start
#
# * db :: the Neo4j::Database instance
#
# ==== on_neo4j_shutdown(db)
#
# Called when the neo4j engine shutdown. You don't need to call Neo4j.shutdown since
# the it will automatically be shutdown when the application exits (using the at_exit ruby hook).
#
# * db :: the Neo4j::Database instance
#
# ==== on_after_commit(data, state)
#
# Called after the transaction has successfully committed.
# See http://api.neo4j.org/1.4/org/neo4j/graphdb/event/TransactionEventHandler.html for the data and state parameter.
#
# ==== on_node_created(node)
#
# * node :: the node that was created
#
# ==== on_node_deleted(node, old_props, deleted_relationship_set, deleted_node_identity_map)
#
# * node :: the node that was deleted
# * old_props :: a hash of the old properties this node had
# * deleted_relationship_set :: the set of deleted relationships. See Neo4j::RelationshipSet
# * deleted_node_identity_map :: the identity map of deleted nodes. The key is the node id, and the value is the node
#
# ==== on_relationship_created(rel, created_node_identity_map)
#
# * rel :: the relationship that was created
# * created_node_identity_map :: the identity map of created nodes. The key is the node id, and the value is the node
#
# ==== on_relationship_deleted(rel, old_props, deleted_relationship_set, deleted_node_identity_map)
#
# * rel :: the relationship that was created
# * old_props :: a hash of the old properties this relationship had
# * deleted_relationship_set :: the set of deleted relationships. See Neo4j::RelationshipSet
# * deleted_node_identity_map :: the identity map of deleted nodes. The key is the node id, and the value is the node
#
# ==== on_property_changed(node, key, old_value, new_value)
#
# * node :: the node
# * key :: the name of the property that was changed (String)
# * old_value :: old value of the property
# * new_value :: new value of the property
#
# ==== on_rel_property_changed(rel, key, old_value, new_value)
#
# * rel :: the node that was created
# * key :: the name of the property that was changed (String)
# * old_value :: old value of the property
# * new_value :: new value of the property
#
# == Usage
#
# class MyListener
# def on_node_deleted(node, old_props, deleted_relationship_set, deleted_node_identity_map)
# end
# end
#
# # to add an listener without starting neo4j:
# Neo4j.unstarted_db.event_handler.add(MyListener.new)
#
# You only need to implement the methods that you need.
#
class EventHandler
include org.neo4j.graphdb.event.TransactionEventHandler
def initialize
@listeners = []
end
def after_commit(data, state)
@listeners.each {|li| li.on_after_commit(data, state) if li.respond_to?(:on_after_commit)}
end
def after_rollback(data, state)
end
def before_commit(data)
created_node_identity_map = node_identity_map(data.created_nodes)
deleted_node_identity_map = node_identity_map(data.deleted_nodes)
deleted_relationship_set = relationship_set(data.deleted_relationships)
removed_node_properties_map = property_map(data.removed_node_properties)
removed_relationship_properties_map = property_map(data.removed_relationship_properties)
empty_map = java.util.HashMap.new
data.created_nodes.each{|node| node_created(node)}
data.assigned_node_properties.each { |tx_data| property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, tx_data.value) }
data.removed_node_properties.each { |tx_data| property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, nil) unless deleted_node_identity_map.containsKey(tx_data.entity.getId) }
data.deleted_nodes.each { |node| node_deleted(node, removed_node_properties_map.get(node.getId)||empty_map, deleted_relationship_set, deleted_node_identity_map)}
data.created_relationships.each {|rel| relationship_created(rel, created_node_identity_map)}
data.deleted_relationships.each {|rel| relationship_deleted(rel, removed_relationship_properties_map.get(rel.getId)||empty_map, deleted_relationship_set, deleted_node_identity_map)}
data.assigned_relationship_properties.each { |tx_data| rel_property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, tx_data.value) }
data.removed_relationship_properties.each {|tx_data| rel_property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, nil) unless deleted_relationship_set.contains_rel?(tx_data.entity) }
end
def node_identity_map(nodes)
identity_map = java.util.HashMap.new(nodes.size)
nodes.each{|node| identity_map.put(node.neo_id,node)}#using put due to a performance regression in JRuby 1.6.4
identity_map
end
def relationship_set(relationships)
relationship_set = Neo4j::RelationshipSet.new(relationships.size)
relationships.each{|rel| relationship_set.add(rel)}
relationship_set
end
def property_map(properties)
map = java.util.HashMap.new
properties.each do |property|
map(property.entity.getId, map).put(property.key, property.previously_commited_value)
end
map
end
def map(key,map)
map.get(key) || add_map(key,map)
end
def add_map(key,map)
map.put(key, java.util.HashMap.new)
map.get(key)
end
def add(listener)
@listeners << listener unless @listeners.include?(listener)
end
def remove(listener)
@listeners.delete(listener)
end
def remove_all
@listeners = []
end
def print
puts "Listeners #{@listeners.size}"
@listeners.each {|li| puts " Listener '#{li}'"}
end
def neo4j_started(db)
@listeners.each { |li| li.on_neo4j_started(db) if li.respond_to?(:on_neo4j_started) }
end
def neo4j_shutdown(db)
@listeners.each { |li| li.on_neo4j_shutdown(db) if li.respond_to?(:on_neo4j_shutdown) }
end
def node_created(node)
@listeners.each {|li| li.on_node_created(node) if li.respond_to?(:on_node_created)}
end
def node_deleted(node,old_properties, deleted_relationship_set, deleted_node_identity_map)
@listeners.each {|li| li.on_node_deleted(node,old_properties, deleted_relationship_set, deleted_node_identity_map) if li.respond_to?(:on_node_deleted)}
end
def relationship_created(relationship, created_node_identity_map)
@listeners.each {|li| li.on_relationship_created(relationship, created_node_identity_map) if li.respond_to?(:on_relationship_created)}
end
def relationship_deleted(relationship, old_props, deleted_relationship_set, deleted_node_identity_map)
@listeners.each {|li| li.on_relationship_deleted(relationship, old_props, deleted_relationship_set, deleted_node_identity_map) if li.respond_to?(:on_relationship_deleted)}
end
def property_changed(node, key, old_value, new_value)
@listeners.each {|li| li.on_property_changed(node, key, old_value, new_value) if li.respond_to?(:on_property_changed)}
end
def rel_property_changed(rel, key, old_value, new_value)
@listeners.each {|li| li.on_rel_property_changed(rel, key, old_value, new_value) if li.respond_to?(:on_rel_property_changed)}
end
end
end