- Monitor logins to other nodes, boot any local sessions with the same

authentication name.
- Use new (ca. 2002) pattern of creating an overridable listener,
rather than making the whole class a listener.

Mdb told me to add this at the msoy level, but I think it belongs here.
Presents simply expects a user to be in one place.

It also occurs to me that we probably want something more sophisticated:
there may be unpersisted data connected to the session that should
be flushed prior to the session starting on another node. We may
want something like a lock when users log-in, so that other nodes
can terminate the session and flush any persistent data FIRST.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5635 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2009-01-15 23:19:02 +00:00
parent 9d28bdbeef
commit b2b8f57454
2 changed files with 104 additions and 45 deletions
@@ -984,6 +984,19 @@ public abstract class PeerManager
return new PeerCreds(_nodeName, _sharedSecret);
}
/**
* Called when we hear about a client logging on to another node.
*/
protected void clientLoggedOnOtherNode (String nodeName, ClientInfo clinfo)
{
PresentsSession session = _clmgr.getClient(clinfo.username);
if (session != null) {
log.info("Booting user that has connected on another node",
"username", clinfo.username, "otherNode", nodeName);
session.endSession();
}
}
/**
* Called when a peer connects to this server.
*/
@@ -35,8 +35,14 @@ import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.presents.dobj.DEvent;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.EntryAddedEvent;
import com.threerings.presents.dobj.EntryRemovedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.SetListener;
import com.threerings.presents.dobj.Subscriber;
import com.threerings.presents.peer.data.ClientInfo;
import com.threerings.presents.peer.data.NodeObject;
import com.threerings.presents.peer.net.PeerBootstrapData;
import com.threerings.presents.peer.server.persist.NodeRecord;
@@ -49,7 +55,7 @@ import static com.threerings.presents.Log.log;
* Contains all runtime information for one of our peer nodes.
*/
public class PeerNode
implements ClientObserver, Subscriber<NodeObject>, AttributeChangeListener
implements ClientObserver, Subscriber<NodeObject>
{
/** This peer's node object. */
public NodeObject nodeobj;
@@ -190,6 +196,8 @@ public class PeerNode
public void clientDidLogoff (Client client)
{
_peermgr.disconnectedFromPeer(this);
nodeobj.removeListener(_listener);
_listener = null;
nodeobj = null;
}
@@ -204,7 +212,7 @@ public class PeerNode
{
// listen for lock and cache updates
nodeobj = object;
nodeobj.addListener(this);
nodeobj.addListener(_listener = createListener());
_peermgr.connectedToPeer(this);
}
@@ -216,49 +224,6 @@ public class PeerNode
"[peer=" + _record + ", cause=" + cause + "].");
}
// documentation inherited from interface AttributeChangeListener
public void attributeChanged (AttributeChangedEvent event)
{
String name = event.getName();
if (name.equals(NodeObject.ACQUIRING_LOCK)) {
NodeObject.Lock lock = nodeobj.acquiringLock;
PeerManager.LockHandler handler = _peermgr.getLockHandler(lock);
if (handler == null) {
if (_peermgr.getNodeObject().locks.contains(lock)) {
log.warning("Peer trying to acquire lock owned by this node " +
"[lock=" + lock + ", node=" + _record.nodeName + "].");
return;
}
_peermgr.createLockHandler(this, lock, true);
return;
}
// if the other node has priority, we're done
if (hasPriority(handler.getNodeName(), _record.nodeName)) {
return;
}
// this node has priority, so cancel the existing handler and take over its listeners
ResultListenerList<String> olisteners = handler.listeners;
handler.cancel();
handler = _peermgr.createLockHandler(this, lock, true);
handler.listeners = olisteners;
} else if (name.equals(NodeObject.RELEASING_LOCK)) {
NodeObject.Lock lock = nodeobj.releasingLock;
PeerManager.LockHandler handler = _peermgr.getLockHandler(lock);
if (handler == null) {
_peermgr.createLockHandler(this, lock, false);
} else {
log.warning("Received request to release resolving lock [node=" +
_record.nodeName + ", handler=" + handler + "].");
}
} else if (name.equals(NodeObject.CACHE_DATA)) {
_peermgr.changedCacheData(nodeobj.cacheData.cache, nodeobj.cacheData.data);
}
}
/**
* Determines whether the first node named has priority over the second when resolving
* lock disputes.
@@ -275,10 +240,91 @@ public class PeerNode
return new BlockingCommunicator(client);
}
/**
* Create the NodeObjectListener to use. Overrideable.
*/
protected NodeObjectListener createListener ()
{
return new NodeObjectListener();
}
/**
* Listens to node object changes.
*/
protected class NodeObjectListener
implements AttributeChangeListener, SetListener<DSet.Entry>
{
// documentation inherited from interface AttributeChangeListener
public void attributeChanged (AttributeChangedEvent event)
{
String name = event.getName();
if (name.equals(NodeObject.ACQUIRING_LOCK)) {
NodeObject.Lock lock = nodeobj.acquiringLock;
PeerManager.LockHandler handler = _peermgr.getLockHandler(lock);
if (handler == null) {
if (_peermgr.getNodeObject().locks.contains(lock)) {
log.warning("Peer trying to acquire lock owned by this node " +
"[lock=" + lock + ", node=" + _record.nodeName + "].");
return;
}
_peermgr.createLockHandler(PeerNode.this, lock, true);
return;
}
// if the other node has priority, we're done
if (hasPriority(handler.getNodeName(), _record.nodeName)) {
return;
}
// this node has priority, so cancel the existing handler and take over
// its listeners
ResultListenerList<String> olisteners = handler.listeners;
handler.cancel();
handler = _peermgr.createLockHandler(PeerNode.this, lock, true);
handler.listeners = olisteners;
} else if (name.equals(NodeObject.RELEASING_LOCK)) {
NodeObject.Lock lock = nodeobj.releasingLock;
PeerManager.LockHandler handler = _peermgr.getLockHandler(lock);
if (handler == null) {
_peermgr.createLockHandler(PeerNode.this, lock, false);
} else {
log.warning("Received request to release resolving lock",
"node", _record.nodeName, "handler", handler);
}
} else if (name.equals(NodeObject.CACHE_DATA)) {
_peermgr.changedCacheData(nodeobj.cacheData.cache, nodeobj.cacheData.data);
}
}
// documentation inherited from interface SetListener
public void entryAdded (EntryAddedEvent<DSet.Entry> event)
{
String name = event.getName();
if (NodeObject.CLIENTS.equals(name)) {
_peermgr.clientLoggedOnOtherNode(getNodeName(), (ClientInfo)event.getEntry());
}
}
// documentation inherited from interface SetListener
public void entryUpdated (EntryUpdatedEvent<DSet.Entry> event)
{
// nada
}
// documentation inherited from interface SetListener
public void entryRemoved (EntryRemovedEvent<DSet.Entry> event)
{
// nada
}
} // END: class NodeObjectListener
protected PeerManager _peermgr;
protected PresentsDObjectMgr _omgr;
protected ConnectionManager _conmgr;
protected NodeRecord _record;
protected NodeObjectListener _listener;
protected Client _client;
protected long _lastConnectStamp;