Introduce NodeChangeListener interface that can be used to get notifications

about node changes (inserts, updates, deletes) from the NodeManager.
Change Transactor to implement NodeChangeListener.
This commit is contained in:
hns 2004-10-22 12:57:15 +00:00
parent a95dcd16a4
commit 9d53d33ef9
4 changed files with 152 additions and 100 deletions

View file

@ -0,0 +1,29 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.objectmodel.db;
import java.util.List;
public interface NodeChangeListener {
/**
* Called when a transaction is committed that has created, modified or
* deleted one or more nodes.
*/
public void nodesChanged(List inserted, List updated, List deleted);
}

View file

@ -36,12 +36,12 @@ public final class NodeManager {
protected Application app;
private ObjectCache cache;
private Replicator replicator;
protected IDatabase db;
protected IDGenerator idgen;
private long idBaseValue = 1L;
private boolean logSql;
protected boolean logReplication;
private ArrayList listeners = new ArrayList();
// a wrapper that catches some Exceptions while accessing this NM
public final WrappedNodeManager safe;
@ -72,10 +72,9 @@ public final class NodeManager {
app.logEvent("Setting up replication listener at " + replicationUrl);
}
replicator = new Replicator(this);
Replicator replicator = new Replicator(this);
replicator.addUrl(replicationUrl);
} else {
replicator = null;
addNodeChangeListener(replicator);
}
// get the initial id generator value
@ -1802,13 +1801,40 @@ public final class NodeManager {
}
/**
* Get a replicator for this node cache. A replicator is used to transfer updates
* in this node manager to other node managers in remote servers via RMI.
* Add a listener that is notified each time a transaction commits
* that adds, modifies or deletes any Nodes.
*/
protected Replicator getReplicator() {
return replicator;
public void addNodeChangeListener(NodeChangeListener listener) {
listeners.add(listener);
}
/**
* Remove a previously added NodeChangeListener.
*/
public void removeNodeChangeListener(NodeChangeListener listener) {
listeners.remove(listener);
}
/**
* Let transactors know if they should collect and fire NodeChangeListener
* events
*/
protected boolean hasNodeChangeListeners() {
return listeners.size() > 0;
}
/**
* Called by transactors after committing.
*/
protected void fireNodeChangeEvent(List inserted, List updated, List deleted) {
int l = listeners.size();
for (int i=0; i<l; i++) {
((NodeChangeListener) listeners.get(i)).nodesChanged(inserted, updated, deleted);
}
}
/**
* Receive notification from a remote app that objects in its cache have been
* modified.

View file

@ -18,11 +18,12 @@ package helma.objectmodel.db;
import java.rmi.Naming;
import java.util.Vector;
import java.util.List;
/**
* This class replicates the updates of transactions to other applications via RMI
*/
public class Replicator implements Runnable {
public class Replicator implements Runnable, NodeChangeListener {
Vector urls;
Vector add;
Vector delete;
@ -99,32 +100,17 @@ public class Replicator implements Runnable {
}
}
/**
*
*
* @param n ...
*/
public synchronized void addNewNode(Node n) {
add.addElement(n);
}
/**
*
*
* @param n ...
* Called when a transaction is committed that has created, modified or
* deleted one or more nodes.
*/
public synchronized void addModifiedNode(Node n) {
add.addElement(n);
public synchronized void nodesChanged(List inserted, List updated, List deleted) {
add.addAll(inserted);
add.addAll(updated);
delete.addAll(deleted);
}
/**
*
*
* @param n ...
*/
public synchronized void addDeletedNode(Node n) {
delete.addElement(n);
}
private synchronized boolean prepareReplication() {
if ((add.size() == 0) && (delete.size() == 0)) {

View file

@ -20,9 +20,7 @@ import helma.objectmodel.DatabaseException;
import helma.objectmodel.ITransaction;
import java.sql.Connection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.*;
/**
* A subclass of thread that keeps track of changed nodes and triggers
@ -227,10 +225,15 @@ public class Transactor extends Thread {
int updated = 0;
int deleted = 0;
if (!dirtyNodes.isEmpty()) {
Object[] dirty = dirtyNodes.values().toArray();
// the replicator to send update notifications to, if defined
Replicator replicator = nmgr.getReplicator();
ArrayList insertedNodes = new ArrayList();
ArrayList updatedNodes = new ArrayList();
ArrayList deletedNodes = new ArrayList();
// if nodemanager has listeners collect dirty nodes
boolean hasListeners = nmgr.hasNodeChangeListeners();
// the set to collect DbMappings to be marked as changed
HashSet dirtyDbMappings = new HashSet();
@ -248,8 +251,8 @@ public class Transactor extends Thread {
// register node with nodemanager cache
nmgr.registerNode(node);
if (replicator != null) {
replicator.addNewNode(node);
if (hasListeners) {
insertedNodes.add(node);
}
inserted++;
@ -265,8 +268,8 @@ public class Transactor extends Thread {
// update node with nodemanager cache
nmgr.registerNode(node);
if (replicator != null) {
replicator.addModifiedNode(node);
if (hasListeners) {
updatedNodes.add(node);
}
updated++;
@ -279,8 +282,8 @@ public class Transactor extends Thread {
// remove node from nodemanager cache
nmgr.evictNode(node);
if (replicator != null) {
replicator.addDeletedNode(node);
if (hasListeners) {
deletedNodes.add(node);
}
deleted++;
@ -289,9 +292,8 @@ public class Transactor extends Thread {
node.clearWriteLock();
}
long now = System.currentTimeMillis();
// set last data change times in db-mappings
long now = System.currentTimeMillis();
for (Iterator i = dirtyDbMappings.iterator(); i.hasNext(); ) {
DbMapping dbm = (DbMapping) i.next();
if (dbm != null) {
@ -299,30 +301,39 @@ public class Transactor extends Thread {
}
}
// save the id-generator for the embedded db, if necessary
if (nmgr.idgen.dirty) {
nmgr.db.saveIDGenerator(txn, nmgr.idgen);
nmgr.idgen.dirty = false;
}
if (hasListeners) {
nmgr.fireNodeChangeEvent(insertedNodes, updatedNodes, deletedNodes);
}
}
long now = System.currentTimeMillis();
if (!parentNodes.isEmpty()) {
// set last subnode change times in parent nodes
for (Iterator i = parentNodes.iterator(); i.hasNext(); ) {
Node node = (Node) i.next();
node.setLastSubnodeChange(now);
}
}
// clear the node collections
dirtyNodes.clear();
cleanNodes.clear();
parentNodes.clear();
// save the id-generator for the embedded db, if necessary
if (nmgr.idgen.dirty) {
nmgr.db.saveIDGenerator(txn, nmgr.idgen);
nmgr.idgen.dirty = false;
}
if (active) {
active = false;
nmgr.db.commitTransaction(txn);
txn = null;
}
nmgr.app.logAccess(tname + " " + dirty.length + " marked, " + inserted +
nmgr.app.logAccess(tname + " " + inserted +
" inserted, " + updated +
" updated, " + deleted + " deleted in " +
(now - tstart) + " millis");