Files
narya/src/java/com/threerings/presents/peer/server/PeerNode.java
T
Michael Bayne 4a7cb2c82f Rethink! Instead of hacking up the peer ClientInfo system for the one thing
that actually needs to look clients up by visible name (chat forwarding), we'll
just leave things as is (as in mapped by auth username) and take care of our
own shit in ClientPeerManager.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5683 542714f4-19e9-0310-aa3c-eee0fc999fb1
2009-03-11 17:47:35 +00:00

333 lines
12 KiB
Java

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.presents.peer.server;
import java.net.ConnectException;
import java.util.Date;
import com.samskivert.util.ResultListenerList;
import com.threerings.presents.client.BlockingCommunicator;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.ClientObserver;
import com.threerings.presents.client.Communicator;
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;
import com.threerings.presents.server.PresentsDObjectMgr;
import com.threerings.presents.server.net.ConnectionManager;
import static com.threerings.presents.Log.log;
/**
* Contains all runtime information for one of our peer nodes.
*/
public class PeerNode
implements ClientObserver, Subscriber<NodeObject>
{
/** This peer's node object. */
public NodeObject nodeobj;
/**
* Initializes this peer node and creates its internal client.
*/
public void init (PeerManager peermgr, final PresentsDObjectMgr omgr,
final ConnectionManager conmgr, NodeRecord record)
{
_peermgr = peermgr;
_omgr = omgr;
_conmgr = conmgr;
_record = record;
_client = new Client(null, _omgr) {
@Override protected void convertFromRemote (DObject target, DEvent event) {
super.convertFromRemote(target, event);
// rewrite the event's target oid using the oid currently configured on the
// distributed object (we will have it mapped in our remote server's oid space,
// but it may have been remapped into the oid space of the local server)
event.setTargetOid(target.getOid());
// assign an eventId to this event so that our stale event detection code can
// properly deal with it
event.eventId = PeerNode.this._omgr.getNextEventId(true);
}
@Override protected Communicator createCommunicator () {
return PeerNode.this.createCommunicator(this);
}
};
_client.addClientObserver(this);
}
public Client getClient ()
{
return _client;
}
public String getNodeName ()
{
return _record.nodeName;
}
public String getPublicHostName ()
{
return _record.publicHostName;
}
public int getPort ()
{
return _record.port;
}
public void refresh (NodeRecord record)
{
// if the hostname of this node changed, kill our existing client and connect anew
if (!record.hostName.equals(_record.hostName) && _client.isActive()) {
_client.logoff(false);
}
// use our new record
_record = record;
// if our client is active, we're groovy
if (_client.isActive()) {
return;
}
// if our client hasn't updated its record since we last tried to logon, then just
// chill
if ((_lastConnectStamp - _record.lastUpdated.getTime()) > STALE_INTERVAL) {
log.debug("Not reconnecting to stale client", "record", _record,
"lastTry", new Date(_lastConnectStamp));
return;
}
// otherwise configure our client with the right bits and logon
_client.setCredentials(_peermgr.createCreds());
_client.setServer(_record.hostName, new int[] { _record.port });
_client.logon();
_lastConnectStamp = System.currentTimeMillis();
}
public void shutdown ()
{
if (_client.isActive()) {
log.info("Logging off of peer " + _record + ".");
_client.logoff(false);
}
}
// documentation inherited from interface ClientObserver
public void clientFailedToLogon (Client client, Exception cause)
{
if (cause instanceof ConnectException) {
// we'll reconnect at most one minute later in refreshPeers()
log.info("Peer not online " + _record + ": " + cause.getMessage());
} else {
log.warning("Peer logon attempt failed " + _record + ": " + cause);
}
}
// documentation inherited from interface ClientObserver
public void clientConnectionFailed (Client client, Exception cause)
{
// we'll reconnect at most one minute later in refreshPeers()
log.warning("Peer connection failed " + _record + ": " + cause);
}
// documentation inherited from interface ClientObserver
public void clientWillLogon (Client client)
{
// nothing doing
}
// documentation inherited from interface ClientObserver
public void clientDidLogon (Client client)
{
log.info("Connected to peer " + _record + ".");
// subscribe to this peer's node object
PeerBootstrapData pdata = (PeerBootstrapData)client.getBootstrapData();
client.getDObjectManager().subscribeToObject(pdata.nodeOid, this);
}
// documentation inherited from interface ClientObserver
public void clientObjectDidChange (Client client)
{
// nothing doing
}
// documentation inherited from interface ClientObserver
public boolean clientWillLogoff (Client client)
{
return true;
}
// documentation inherited from interface ClientObserver
public void clientDidLogoff (Client client)
{
_peermgr.disconnectedFromPeer(this);
nodeobj.removeListener(_listener);
_listener = null;
nodeobj = null;
}
// documentation inherited from interface ClientObserver
public void clientDidClear (Client client)
{
// nothing doing
}
// documentation inherited from interface Subscriber
public void objectAvailable (NodeObject object)
{
// listen for lock and cache updates
nodeobj = object;
nodeobj.addListener(_listener = createListener());
_peermgr.connectedToPeer(this);
}
// documentation inherited from interface Subscriber
public void requestFailed (int oid, ObjectAccessException cause)
{
log.warning("Failed to subscribe to peer's node object " +
"[peer=" + _record + ", cause=" + cause + "].");
}
/**
* Determines whether the first node named has priority over the second when resolving
* lock disputes.
*/
protected static boolean hasPriority (String nodeName1, String nodeName2)
{
return nodeName1.compareTo(nodeName2) < 0;
}
// TEMP: allow our communicator to be customized until we sort out our FreeBSD VM problems
protected Communicator createCommunicator (Client client)
{
// return new ServerCommunicator(client, _conmgr, _omgr);
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.clientLoggedOn(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) {
String name = event.getName();
if (NodeObject.CLIENTS.equals(name)) {
_peermgr.clientLoggedOff(getNodeName(), (ClientInfo)event.getOldEntry());
}
}
} // END: class NodeObjectListener
protected PeerManager _peermgr;
protected PresentsDObjectMgr _omgr;
protected ConnectionManager _conmgr;
protected NodeRecord _record;
protected NodeObjectListener _listener;
protected Client _client;
protected long _lastConnectStamp;
/** The amount of time after which a node record can be considered out of date and invalid. */
protected static final long STALE_INTERVAL = 5L * 60L * 1000L;
}