Convert Narya (most of the way) over to a Maven Ant task based build. The

ActionScript bits remain belligerent, but the Java stuff is mostly shipshape.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6222 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2010-10-22 21:12:29 +00:00
parent 555b865bbf
commit 9d2ca42eac
434 changed files with 163 additions and 208 deletions
@@ -0,0 +1,57 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.peer.data.NodeObject.Lock;
/**
* Defines requests made from one peer to another.
*/
public interface PeerService extends InvocationService
{
/**
* Informs the node that the sending peer ratifies its acquisition or release of the specified
* lock.
*/
void ratifyLockAction (Client client, Lock lock, boolean acquire);
/**
* Requests that the specified action be invoked on this server.
*/
void invokeAction (Client client, byte[] serializedAction);
/**
* Requests that the specified request be invoked on this server and wants a confirmation
* when it's complete.
*/
void invokeRequest (Client client, byte[] serializedAction, ResultListener listener);
/**
* Generates a server status report for this peer and returns it to the supplied listener. The
* result must be a string.
*
* @param type the type of report to generate. See ReportManager for more information.
*/
void generateReport (Client client, String type, ResultListener listener);
}
@@ -0,0 +1,44 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.util.Name;
import com.threerings.presents.dobj.DSet;
/**
* Contains information on a particular client.
*/
public class ClientInfo extends SimpleStreamableObject
implements DSet.Entry
{
/** The username used by this client to authenticate. */
public Name username;
// documentation inherited from interface DSet.Entry
public Comparable<?> getKey ()
{
return username;
}
}
@@ -0,0 +1,372 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import javax.annotation.Generated;
import com.google.common.base.Objects;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.io.Streamable;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
/**
* Contains information that one node published for all of its peers.
*/
public class NodeObject extends DObject
{
// AUTO-GENERATED: FIELDS START
/** The field name of the <code>nodeName</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String NODE_NAME = "nodeName";
/** The field name of the <code>bootStamp</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String BOOT_STAMP = "bootStamp";
/** The field name of the <code>peerService</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String PEER_SERVICE = "peerService";
/** The field name of the <code>clients</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String CLIENTS = "clients";
/** The field name of the <code>locks</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String LOCKS = "locks";
/** The field name of the <code>acquiringLock</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String ACQUIRING_LOCK = "acquiringLock";
/** The field name of the <code>releasingLock</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String RELEASING_LOCK = "releasingLock";
/** The field name of the <code>cacheData</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String CACHE_DATA = "cacheData";
// AUTO-GENERATED: FIELDS END
/** Identifies a locked resource. */
public static class Lock extends SimpleStreamableObject
implements Comparable<Lock>, DSet.Entry
{
/** The resource type. Only resources of the same type will have their ids compared. */
public String type;
/** The resource identifier, which can be <code>null</code> for singleton resources. */
public Comparable<?> id;
public Lock ()
{
}
public Lock (String type, Comparable<?> id)
{
this.type = type;
this.id = id;
}
// documentation inherited from interface Comparable
public int compareTo (Lock olock)
{
int v1 = type.compareTo(olock.type);
if (v1 != 0 || id == null) {
return v1;
}
return DSet.compare(id, olock.id);
}
// documentation inherited from interface DSet.Entry
public Comparable<?> getKey ()
{
return this;
}
@Override
public int hashCode ()
{
return type.hashCode() + (id == null ? 0 : id.hashCode());
}
@Override
public boolean equals (Object other)
{
Lock olock = (Lock)other;
return type.equals(olock.type) && Objects.equal(id, olock.id);
}
}
/** Used for informing peers of changes to persistent data. */
public static class CacheData extends SimpleStreamableObject
{
/** The cache that should be purged. */
public String cache;
/** The stale data in the cache. */
public Streamable data;
public CacheData ()
{
}
public CacheData (String cache, Streamable data)
{
this.cache = cache;
this.data = data;
}
}
/** The node name of this peer. */
public String nodeName;
/** The time that this node's JVM started up. */
public long bootStamp;
/** The service used to make requests of the node. */
public PeerMarshaller peerService;
/** Contains information on all clients connected to this node. */
public DSet<ClientInfo> clients = new DSet<ClientInfo>();
/** The set of locks held by this node. */
public DSet<Lock> locks = new DSet<Lock>();
/** Used to broadcast a node's desire to acquire a lock. */
public Lock acquiringLock;
/** Used to broadcast a node's desire to release a lock. */
public Lock releasingLock;
/** A field we use to broadcast changes to possible cached data. */
public CacheData cacheData;
// AUTO-GENERATED: METHODS START
/**
* Requests that the <code>nodeName</code> field be set to the
* specified value. The local value will be updated immediately and an
* event will be propagated through the system to notify all listeners
* that the attribute did change. Proxied copies of this object (on
* clients) will apply the value change when they received the
* attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setNodeName (String value)
{
String ovalue = this.nodeName;
requestAttributeChange(
NODE_NAME, value, ovalue);
this.nodeName = value;
}
/**
* Requests that the <code>bootStamp</code> field be set to the
* specified value. The local value will be updated immediately and an
* event will be propagated through the system to notify all listeners
* that the attribute did change. Proxied copies of this object (on
* clients) will apply the value change when they received the
* attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setBootStamp (long value)
{
long ovalue = this.bootStamp;
requestAttributeChange(
BOOT_STAMP, Long.valueOf(value), Long.valueOf(ovalue));
this.bootStamp = value;
}
/**
* Requests that the <code>peerService</code> field be set to the
* specified value. The local value will be updated immediately and an
* event will be propagated through the system to notify all listeners
* that the attribute did change. Proxied copies of this object (on
* clients) will apply the value change when they received the
* attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setPeerService (PeerMarshaller value)
{
PeerMarshaller ovalue = this.peerService;
requestAttributeChange(
PEER_SERVICE, value, ovalue);
this.peerService = value;
}
/**
* Requests that the specified entry be added to the
* <code>clients</code> set. The set will not change until the event is
* actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void addToClients (ClientInfo elem)
{
requestEntryAdd(CLIENTS, clients, elem);
}
/**
* Requests that the entry matching the supplied key be removed from
* the <code>clients</code> set. The set will not change until the
* event is actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void removeFromClients (Comparable<?> key)
{
requestEntryRemove(CLIENTS, clients, key);
}
/**
* Requests that the specified entry be updated in the
* <code>clients</code> set. The set will not change until the event is
* actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void updateClients (ClientInfo elem)
{
requestEntryUpdate(CLIENTS, clients, elem);
}
/**
* Requests that the <code>clients</code> field be set to the
* specified value. Generally one only adds, updates and removes
* entries of a distributed set, but certain situations call for a
* complete replacement of the set value. The local value will be
* updated immediately and an event will be propagated through the
* system to notify all listeners that the attribute did
* change. Proxied copies of this object (on clients) will apply the
* value change when they received the attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setClients (DSet<ClientInfo> value)
{
requestAttributeChange(CLIENTS, value, this.clients);
DSet<ClientInfo> clone = (value == null) ? null : value.clone();
this.clients = clone;
}
/**
* Requests that the specified entry be added to the
* <code>locks</code> set. The set will not change until the event is
* actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void addToLocks (NodeObject.Lock elem)
{
requestEntryAdd(LOCKS, locks, elem);
}
/**
* Requests that the entry matching the supplied key be removed from
* the <code>locks</code> set. The set will not change until the
* event is actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void removeFromLocks (Comparable<?> key)
{
requestEntryRemove(LOCKS, locks, key);
}
/**
* Requests that the specified entry be updated in the
* <code>locks</code> set. The set will not change until the event is
* actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void updateLocks (NodeObject.Lock elem)
{
requestEntryUpdate(LOCKS, locks, elem);
}
/**
* Requests that the <code>locks</code> field be set to the
* specified value. Generally one only adds, updates and removes
* entries of a distributed set, but certain situations call for a
* complete replacement of the set value. The local value will be
* updated immediately and an event will be propagated through the
* system to notify all listeners that the attribute did
* change. Proxied copies of this object (on clients) will apply the
* value change when they received the attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setLocks (DSet<NodeObject.Lock> value)
{
requestAttributeChange(LOCKS, value, this.locks);
DSet<NodeObject.Lock> clone = (value == null) ? null : value.clone();
this.locks = clone;
}
/**
* Requests that the <code>acquiringLock</code> field be set to the
* specified value. The local value will be updated immediately and an
* event will be propagated through the system to notify all listeners
* that the attribute did change. Proxied copies of this object (on
* clients) will apply the value change when they received the
* attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setAcquiringLock (NodeObject.Lock value)
{
NodeObject.Lock ovalue = this.acquiringLock;
requestAttributeChange(
ACQUIRING_LOCK, value, ovalue);
this.acquiringLock = value;
}
/**
* Requests that the <code>releasingLock</code> field be set to the
* specified value. The local value will be updated immediately and an
* event will be propagated through the system to notify all listeners
* that the attribute did change. Proxied copies of this object (on
* clients) will apply the value change when they received the
* attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setReleasingLock (NodeObject.Lock value)
{
NodeObject.Lock ovalue = this.releasingLock;
requestAttributeChange(
RELEASING_LOCK, value, ovalue);
this.releasingLock = value;
}
/**
* Requests that the <code>cacheData</code> field be set to the
* specified value. The local value will be updated immediately and an
* event will be propagated through the system to notify all listeners
* that the attribute did change. Proxied copies of this object (on
* clients) will apply the value change when they received the
* attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setCacheData (NodeObject.CacheData value)
{
NodeObject.CacheData ovalue = this.cacheData;
requestAttributeChange(
CACHE_DATA, value, ovalue);
this.cacheData = value;
}
// AUTO-GENERATED: METHODS END
}
@@ -0,0 +1,40 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.threerings.util.Name;
/**
* Represents an authenticated peer client.
*/
public class PeerAuthName extends Name
{
public PeerAuthName (String nodeName)
{
super(nodeName);
}
// used when unserializing
public PeerAuthName ()
{
}
}
@@ -0,0 +1,90 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import javax.annotation.Generated;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.peer.client.PeerService;
/**
* Provides the implementation of the {@link PeerService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from PeerService.java.")
public class PeerMarshaller extends InvocationMarshaller
implements PeerService
{
/** The method id used to dispatch {@link #generateReport} requests. */
public static final int GENERATE_REPORT = 1;
// from interface PeerService
public void generateReport (Client arg1, String arg2, InvocationService.ResultListener arg3)
{
InvocationMarshaller.ResultMarshaller listener3 = new InvocationMarshaller.ResultMarshaller();
listener3.listener = arg3;
sendRequest(arg1, GENERATE_REPORT, new Object[] {
arg2, listener3
});
}
/** The method id used to dispatch {@link #invokeAction} requests. */
public static final int INVOKE_ACTION = 2;
// from interface PeerService
public void invokeAction (Client arg1, byte[] arg2)
{
sendRequest(arg1, INVOKE_ACTION, new Object[] {
arg2
});
}
/** The method id used to dispatch {@link #invokeRequest} requests. */
public static final int INVOKE_REQUEST = 3;
// from interface PeerService
public void invokeRequest (Client arg1, byte[] arg2, InvocationService.ResultListener arg3)
{
InvocationMarshaller.ResultMarshaller listener3 = new InvocationMarshaller.ResultMarshaller();
listener3.listener = arg3;
sendRequest(arg1, INVOKE_REQUEST, new Object[] {
arg2, listener3
});
}
/** The method id used to dispatch {@link #ratifyLockAction} requests. */
public static final int RATIFY_LOCK_ACTION = 4;
// from interface PeerService
public void ratifyLockAction (Client arg1, NodeObject.Lock arg2, boolean arg3)
{
sendRequest(arg1, RATIFY_LOCK_ACTION, new Object[] {
arg2, Boolean.valueOf(arg3)
});
}
}
@@ -0,0 +1,33 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.net;
import com.threerings.presents.net.BootstrapData;
/**
* Extensd the standard bootstrap with some information needed by our peers.
*/
public class PeerBootstrapData extends BootstrapData
{
/** The id of this peer's node object. */
public int nodeOid;
}
@@ -0,0 +1,42 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.net;
import com.threerings.presents.net.ServiceCreds;
/**
* Used by peer servers in a cluster installation to authenticate with one another.
*/
public class PeerCreds extends ServiceCreds
{
public PeerCreds (String nodeName, String sharedSecret)
{
super(nodeName, sharedSecret);
}
/**
* Used when unserializing an instance from the network.
*/
public PeerCreds ()
{
}
}
@@ -0,0 +1,190 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.samskivert.util.Tuple;
import com.threerings.presents.peer.data.NodeObject;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.distribution.CacheManagerPeerProvider;
import net.sf.ehcache.distribution.CacheManagerPeerProviderFactory;
import net.sf.ehcache.distribution.CachePeer;
import static com.threerings.presents.Log.log;
/**
* An EHCache peer coordinator that obtains its information from the Narya peer system.
*/
public class EHCachePeerCoordinator extends CacheManagerPeerProviderFactory
{
/** Must correspond to what's provided to the PeerManagerCacheListener in ehcache.xml. */
public static final int RMI_PORT = 40001;
public static void initWithPeers (PeerManager peerMan)
{
if (_instance == null) {
log.warning("No provider initialized -- not coordinating Presents and EHCache peers.");
return;
}
_instance.initWithPeers(peerMan);
}
/** Return our provider, creating it if needed. */
@Override
public CacheManagerPeerProvider createCachePeerProvider (
CacheManager cacheManager, Properties properties)
{
if (_instance == null) {
_instance = new Provider(cacheManager);
}
return _instance;
}
protected static class Provider implements CacheManagerPeerProvider
{
public Provider (CacheManager cacheManager)
{
_cacheMan = cacheManager;
}
public void initWithPeers (PeerManager peerMan)
{
_peerMan = peerMan;
}
// these are internal to EHCache and I have NO clue why they're in the API
public void registerPeer (String rmiUrl) { }
public void unregisterPeer (String rmiUrl) { }
public List<?> listRemoteCachePeers (Ehcache cache)
throws CacheException
{
if (_peerMan == null) {
// the ehcache subsystem has fired up but the server is still booting; we return
// empty here and ehcache will try again
return Collections.emptyList();
}
// list the current peers
final List<CachePeer> result = Lists.newArrayList();
final Set<String> nodes = Sets.newHashSet();
for (NodeObject node : _peerMan.getNodeObjects()) {
if (node != _peerMan.getNodeObject()) {
addCacheForNode(result, node.nodeName, cache.getName());
nodes.add(node.nodeName);
}
}
// if any previously known peer is no longer with us, clear out the cache
Set<Tuple<String, String>> toRemove = Sets.newHashSet();
for (Tuple<String, String> key : _peerCache.keySet()) {
if (!nodes.contains(key.left)) {
toRemove.add(key);
}
}
for (Tuple<String, String> key : toRemove) {
log.info("Removing EHCache peer: " + key);
_peerCache.remove(key);
}
return result;
}
public void init ()
{
// do nothing
}
public void dispose ()
throws CacheException
{
// do nothing
}
public long getTimeForClusterToForm ()
{
// this is only used when bootstrapping, which we don't do, but whatever
return 10000;
}
public String getScheme ()
{
return "RMI";
}
protected void addCacheForNode (List<CachePeer> result, String nodeName, String cacheName)
{
String nodeHost = _peerMan.getPeerInternalHostName(nodeName);
if (nodeHost == null) {
log.warning("Eek, couldn't find the public host name of peer", "node", nodeName);
return;
}
try {
String rmiBase = "//" + nodeHost + ":" + RMI_PORT + "/";
result.add(getCache(nodeName, rmiBase + cacheName));
} catch (Exception e) {
log.warning("Could not resolve remote peer", "host", nodeHost,
"cache", cacheName, e);
}
}
protected CachePeer getCache (String nodeName, String url)
throws MalformedURLException, RemoteException, NotBoundException
{
Tuple<String, String> key = Tuple.newTuple(nodeName, url);
// retrieve the RMI handle for the given peer
synchronized(_peerCache) {
CachePeer peer = _peerCache.get(key);
if (peer == null) {
// do the (blocking) lookup and stow away the result
log.info("RMI lookup of remote cache", "url", url);
peer = (CachePeer) Naming.lookup(url);
_peerCache.put(key, peer);
}
return peer;
}
}
protected Map<Tuple<String, String>, CachePeer> _peerCache = Maps.newHashMap();
protected PeerManager _peerMan;
protected CacheManager _cacheMan;
}
protected static Provider _instance;
}
@@ -0,0 +1,53 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.util.Map;
import com.threerings.presents.client.InvocationService;
/**
* Communicates the {@link NodeRequestsResult} of a {@link PeerManager.NodeRequest} sent to one
* or more peer nodes.
*/
public interface
NodeRequestsListener<T> extends InvocationService.InvocationListener
{
/**
* Called upon the successful completion of {@link PeerManager#invokeNodeRequest}, regardless
* of how many nodes were contacted or applicable.
*/
public void requestsProcessed (NodeRequestsResult<T> result);
/**
* Contains the result of a {@link PeerManager.NodeRequest} sent to one or more peer nodes.
* Any node that returned true for {@link PeerManager.NodeRequest#isApplicable} will appear
* in either {@link #getNodeResults()} or {@link #getNodeErrors()}. The wasDropped() method
* will return true iff both mappings are empty.
*/
public interface NodeRequestsResult<T>
{
public Map<String, T> getNodeResults ();
public Map<String, String> getNodeErrors ();
boolean wasDropped ();
}
}
@@ -0,0 +1,31 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 com.threerings.presents.server.ClientResolver;
/**
* Handles the resolution of peer server client data.
*/
public class PeerClientResolver extends ClientResolver
{
}
@@ -0,0 +1,90 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 javax.annotation.Generated;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.peer.data.NodeObject;
import com.threerings.presents.peer.data.PeerMarshaller;
import com.threerings.presents.server.InvocationDispatcher;
import com.threerings.presents.server.InvocationException;
/**
* Dispatches requests to the {@link PeerProvider}.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from PeerService.java.")
public class PeerDispatcher extends InvocationDispatcher<PeerMarshaller>
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public PeerDispatcher (PeerProvider provider)
{
this.provider = provider;
}
@Override
public PeerMarshaller createMarshaller ()
{
return new PeerMarshaller();
}
@Override
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case PeerMarshaller.GENERATE_REPORT:
((PeerProvider)provider).generateReport(
source, (String)args[0], (InvocationService.ResultListener)args[1]
);
return;
case PeerMarshaller.INVOKE_ACTION:
((PeerProvider)provider).invokeAction(
source, (byte[])args[0]
);
return;
case PeerMarshaller.INVOKE_REQUEST:
((PeerProvider)provider).invokeRequest(
source, (byte[])args[0], (InvocationService.ResultListener)args[1]
);
return;
case PeerMarshaller.RATIFY_LOCK_ACTION:
((PeerProvider)provider).ratifyLockAction(
source, (NodeObject.Lock)args[0], ((Boolean)args[1]).booleanValue()
);
return;
default:
super.dispatchRequest(source, methodId, args);
return;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,361 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.google.inject.Inject;
import com.samskivert.util.ResultListenerList;
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 com.threerings.presents.server.net.ServerCommunicator;
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 (NodeRecord record)
{
_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);
}
/**
* Returns the {@link Client} instance that manages our connection to this peer.
*/
public Client getClient ()
{
return _client;
}
/**
* Returns this peer's unique string identifier.
*/
public String getNodeName ()
{
return _record.nodeName;
}
/**
* Returns the hostname for external clients to use when connecting to this peer.
*/
public String getPublicHostName ()
{
return _record.publicHostName;
}
/**
* Returns the hostname for internal clients to use when connecting to this peer.
*/
public String getInternalHostName ()
{
return _record.hostName;
}
/**
* Returns the port on which to connect to this peer.
*/
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)
{
if (nodeobj != null) {
String nodeName = getNodeName();
for (ClientInfo clinfo : nodeobj.clients) {
_peermgr.clientLoggedOff(nodeName, clinfo);
}
nodeobj.removeListener(_listener);
}
_peermgr.disconnectedFromPeer(this);
_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);
String nodeName = getNodeName();
for (ClientInfo clinfo : nodeobj.clients) {
_peermgr.clientLoggedOn(nodeName, clinfo);
}
}
// 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;
}
protected Communicator createCommunicator (Client client)
{
return new ServerCommunicator(client, _conmgr, _omgr);
}
/**
* 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 NodeRecord _record;
protected NodeObjectListener _listener;
protected Client _client;
protected long _lastConnectStamp;
@Inject protected PeerManager _peermgr;
@Inject protected PresentsDObjectMgr _omgr;
@Inject protected ConnectionManager _conmgr;
/** 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;
}
@@ -0,0 +1,61 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 javax.annotation.Generated;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.peer.client.PeerService;
import com.threerings.presents.peer.data.NodeObject;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationProvider;
/**
* Defines the server-side of the {@link PeerService}.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from PeerService.java.")
public interface PeerProvider extends InvocationProvider
{
/**
* Handles a {@link PeerService#generateReport} request.
*/
void generateReport (ClientObject caller, String arg1, InvocationService.ResultListener arg2)
throws InvocationException;
/**
* Handles a {@link PeerService#invokeAction} request.
*/
void invokeAction (ClientObject caller, byte[] arg1);
/**
* Handles a {@link PeerService#invokeRequest} request.
*/
void invokeRequest (ClientObject caller, byte[] arg1, InvocationService.ResultListener arg2)
throws InvocationException;
/**
* Handles a {@link PeerService#ratifyLockAction} request.
*/
void ratifyLockAction (ClientObject caller, NodeObject.Lock arg1, boolean arg2);
}
@@ -0,0 +1,166 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 com.google.inject.Inject;
import com.samskivert.util.Throttle;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.net.BootstrapData;
import com.threerings.presents.net.DownstreamMessage;
import com.threerings.presents.net.Message;
import com.threerings.presents.peer.data.NodeObject;
import com.threerings.presents.peer.net.PeerBootstrapData;
import com.threerings.presents.server.PresentsSession;
import static com.threerings.presents.Log.log;
/**
* Manages a peer session.
*/
public class PeerSession extends PresentsSession
{
/**
* Creates a peer session and provides it with a reference to the peer manager.
*/
@Inject public PeerSession (PeerManager peermgr)
{
_peermgr = peermgr;
}
/**
* Passes in a stats instance that this peer can use to note when it trafficks.
*/
public void setStats (PeerManager.Stats stats)
{
_stats = stats;
}
@Override // from PresentsSession
public void handleMessage (Message message)
{
super.handleMessage(message);
_stats.peerMessagesIn.incrementAndGet();
}
@Override // from PresentsSession
protected BootstrapData createBootstrapData ()
{
return new PeerBootstrapData();
}
@Override // from PresentsSession
protected void populateBootstrapData (BootstrapData data)
{
super.populateBootstrapData(data);
// tell our peer about our node object so they can wire up
PeerBootstrapData pdata = (PeerBootstrapData)data;
pdata.nodeOid = _peermgr.getNodeObject().getOid();
}
@Override // from PresentsSession
protected void sessionWillStart ()
{
super.sessionWillStart();
// save the client oid so we know it even after the object itself is cleared out
_cloid = _clobj.getOid();
// let the peer manager know that we're here
_peermgr.peerStartedSession(this);
}
@Override // from PresentsSession
protected void sessionConnectionClosed ()
{
super.sessionConnectionClosed();
// if we lose contact with our peer and we have not already ended our session, end it now;
// we don't want to wait 8 minutes for it to reconnect, there's no need to preserve its
// session state and there is need to let the peer manager know it's gone ASAP
if (_clobj != null) {
log.info("Lost connection to peer, ending session " + this + ".");
endSession();
}
}
@Override // from PresentsSession
protected void sessionDidEnd ()
{
super.sessionDidEnd();
// let the peer manager know that we're audi
_peermgr.peerEndedSession(this);
}
@Override // from PresentsSession
protected final boolean postMessage (DownstreamMessage msg)
{
if (!super.postMessage(msg)) {
return false;
}
_stats.peerMessagesOut++;
return true;
}
@Override // from PresentsSession
protected void subscribedToObject (DObject object)
{
super.subscribedToObject(object);
if (object instanceof NodeObject) {
_peermgr.clientSubscribedToNode(_cloid);
}
}
@Override // from PresentsSession
protected void unsubscribedFromObject (DObject object)
{
super.unsubscribedFromObject(object);
if (object instanceof NodeObject) {
_peermgr.clientUnsubscribedFromNode(_cloid);
}
}
@Override // from PresentsSession
protected Throttle createIncomingMessageThrottle ()
{
// more than 100 messages per second and we complain about it
return new Throttle(100, 1000L);
}
@Override // from PresentsSession
protected void handleThrottleExceeded ()
{
long now = System.currentTimeMillis();
if (now >= _nextThrottleWarning) {
log.warning("Peer sent more than 100 messages in one second " + this + ".");
_nextThrottleWarning = now + 5000L; // don't warn more than once every 5 seconds
}
}
protected PeerManager _peermgr;
protected PeerManager.Stats _stats;
protected int _cloid;
protected long _nextThrottleWarning;
}
@@ -0,0 +1,114 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.persist;
import java.sql.Timestamp;
import com.samskivert.util.StringUtil;
import com.samskivert.depot.Key;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.annotation.Column;
import com.samskivert.depot.annotation.Entity;
import com.samskivert.depot.annotation.Id;
import com.samskivert.depot.expression.ColumnExp;
/**
* Contains information on an active node in a Presents server cluster.
*/
@Entity(name="NODES")
public class NodeRecord extends PersistentRecord
{
// AUTO-GENERATED: FIELDS START
public static final Class<NodeRecord> _R = NodeRecord.class;
public static final ColumnExp NODE_NAME = colexp(_R, "nodeName");
public static final ColumnExp HOST_NAME = colexp(_R, "hostName");
public static final ColumnExp PUBLIC_HOST_NAME = colexp(_R, "publicHostName");
public static final ColumnExp PORT = colexp(_R, "port");
public static final ColumnExp LAST_UPDATED = colexp(_R, "lastUpdated");
// AUTO-GENERATED: FIELDS END
/** Increment this value if you modify the definition of this persistent
* object in a way that will result in a change to its SQL counterpart. */
public static final int SCHEMA_VERSION = 1;
/** The unique name assigned to this node. */
@Id
@Column(name="NODE_NAME", length=64)
public String nodeName;
/** The DNS name used to connect to this node by other peers. */
@Column(name="HOST_NAME", length=64)
public String hostName;
/** The DNS name used to connect to this node by normal clients. */
@Column(name="PUBLIC_HOST_NAME", length=64)
public String publicHostName;
/** The port on which to connect to this node. */
@Column(name="PORT")
public int port;
/** The last time this node has reported in. */
@Column(name="LAST_UPDATED")
public Timestamp lastUpdated;
/** Used to create a blank instance when loading from the database. */
public NodeRecord ()
{
}
/** Creates a record for the specified node. */
public NodeRecord (String nodeName, String hostName, String publicHostName, int port)
{
this.nodeName = nodeName;
this.hostName = hostName;
this.publicHostName = publicHostName;
this.port = port;
}
/** Used for queries. */
public NodeRecord (String nodeName)
{
this.nodeName = nodeName;
}
@Override
public String toString ()
{
return StringUtil.fieldsToString(this);
}
// AUTO-GENERATED: METHODS START
/**
* Create and return a primary {@link Key} to identify a {@link NodeRecord}
* with the supplied key values.
*/
public static Key<NodeRecord> getKey (String nodeName)
{
return new Key<NodeRecord>(
NodeRecord.class,
new ColumnExp[] { NODE_NAME },
new Comparable[] { nodeName });
}
// AUTO-GENERATED: METHODS END
}
@@ -0,0 +1,114 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.persist;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.sql.Timestamp;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.samskivert.util.StringUtil;
import com.samskivert.depot.DepotRepository;
import com.samskivert.depot.PersistenceContext;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.clause.QueryClause;
import com.samskivert.depot.clause.Where;
/**
* Used to share information on active nodes in a Presents server cluster.
*/
@Singleton
public class NodeRepository extends DepotRepository
{
/**
* Constructs a new repository with the specified persistence context.
*/
@Inject public NodeRepository (PersistenceContext ctx)
{
super(ctx);
}
/**
* Returns a list of all nodes registered in the repository.
*/
public List<NodeRecord> loadNodes ()
{
return loadNodes("");
}
/**
* Returns a list of all nodes registered in the repository with names starting with the given
* string.
*/
public List<NodeRecord> loadNodes (String namespace)
{
Iterable<QueryClause> clauses;
if (StringUtil.isBlank(namespace)) {
clauses = Collections.emptyList();
} else {
QueryClause clause = new Where(NodeRecord.NODE_NAME.like(namespace + "%"));
clauses = Lists.newArrayList(clause);
}
// we specifically avoid caching this query because we want the servers to always see the
// most up to date set of nodes
return findAll(NodeRecord.class, CacheStrategy.NONE, clauses);
}
/**
* Updates the supplied node record, inserting it into the database if necessary.
*/
public void updateNode (NodeRecord record)
{
record.lastUpdated = new Timestamp(System.currentTimeMillis());
store(record);
}
/**
* Updates {@link NodeRecord#lastUpdated} for the specified node, indicating that the node is
* alive and well.
*/
public void heartbeatNode (String nodeName)
{
updatePartial(NodeRecord.getKey(nodeName),
NodeRecord.LAST_UPDATED, new Timestamp(System.currentTimeMillis()));
}
/**
* Deletes the identified node record.
*/
public void deleteNode (String nodeName)
{
delete(NodeRecord.getKey(nodeName));
}
@Override // from DepotRepository
protected void getManagedRecords (Set<Class<? extends PersistentRecord>> classes)
{
classes.add(NodeRecord.class);
}
}
@@ -0,0 +1,76 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import com.google.common.collect.Maps;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.server.InvocationProvider;
/**
* Static methods of general utility for peer nodes.
*/
public class PeerUtil
{
/**
* Creates a proxy object implementing the specified provider interface (a subinterface of
* {@link InvocationProvider} that forwards requests to the given service implementation
* (a subinterface of {@link InvocationService} corresponding to the provider interface)
* on the specified client. This is useful for server entities that need to call a method
* either on the current server (with <code>null</code> as the caller parameter) or on a
* peer server.
*
* @param clazz the subclass of {@link InvocationProvider} desired to be implemented
* @param svc the implementation of the corresponding subclass of {@link InvocationService}
* @param client the client to pass to the service methods
*/
public static <S extends InvocationProvider, T extends InvocationService>
S createProviderProxy (Class<S> clazz, final T svc, final Client client)
{
return clazz.cast(Proxy.newProxyInstance(
clazz.getClassLoader(), new Class<?>[] { clazz },
new InvocationHandler() {
public Object invoke (Object proxy, Method method, Object[] args)
throws Throwable {
Method smethod = _pmethods.get(method);
if (smethod == null) {
Class<?>[] ptypes = method.getParameterTypes();
ptypes[0] = Client.class;
_pmethods.put(method, smethod = svc.getClass().getMethod(
method.getName(), ptypes));
}
args[0] = client;
return smethod.invoke(svc, args);
}
}));
}
/** Maps provider interface methods to service interface methods. */
protected static HashMap<Method, Method> _pmethods = Maps.newHashMap();
}