Finished wiring up oid list tracking; made shutdown() work on the cher

server; added an easy mechanism to write test modules and invoke them on
the server.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@192 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2001-08-08 00:28:49 +00:00
parent 91d5813b37
commit 6fe9ad8a6e
7 changed files with 410 additions and 24 deletions
@@ -1,5 +1,5 @@
//
// $Id: DObject.java,v 1.20 2001/08/07 20:38:58 mdb Exp $
// $Id: DObject.java,v 1.21 2001/08/08 00:28:49 mdb Exp $
package com.threerings.cocktail.cher.dobj;
@@ -165,7 +165,7 @@ public class DObject
// scan the lock array to see if this lock is already acquired
int slot = -1;
for (int i = 0; i < _locks.length; i++) {
if (_locks[i] == null) {
if (_locks[i] == null && slot == -1) {
// keep track of this for later
slot = i;
} else if (name.equals(_locks[i])) {
@@ -343,6 +343,17 @@ public class DObject
}
}
/**
* Returns true if this object is active and registered with the
* distributed object system. If an object is created via
* <code>DObjectManager.createObject</code> it will be active until
* such time as it is destroyed.
*/
public boolean isActive ()
{
return _mgr != null;
}
/**
* Don't call this function! It initializes this distributed object
* with the supplied distributed object manager. This is called by the
@@ -1,9 +1,9 @@
//
// $Id: PresentsDObjectMgr.java,v 1.11 2001/08/07 21:25:13 mdb Exp $
// $Id: PresentsDObjectMgr.java,v 1.12 2001/08/08 00:28:49 mdb Exp $
package com.threerings.cocktail.cher.server;
import java.lang.reflect.Method;
import java.lang.reflect.*;
import java.util.HashMap;
import com.samskivert.util.Queue;
@@ -172,9 +172,191 @@ public class CherDObjectMgr implements DObjectManager
*/
public void objectDestroyed (DEvent event, DObject target)
{
// Log.info("Removing destroyed object from table " +
// "[oid=" + target.getOid() + "].");
_objects.remove(target.getOid());
int oid = target.getOid();
// Log.info("Removing destroyed object from table " +
// "[oid=" + oid + "].");
// remove the object from the table
_objects.remove(oid);
// inactivate the object
target.setManager(null);
// deal with any remaining oid lists that reference this object
Reference[] refs = (Reference[])_refs.remove(oid);
if (refs != null) {
for (int i = 0; i < refs.length; i++) {
// skip empty spots
if (refs[i] == null) {
continue;
}
Reference ref = refs[i];
DObject reffer = (DObject)_objects.get(ref.reffingOid);
// ensure that the referencing object is still around
if (reffer != null) {
// post an object removed event to clear the reference
postEvent(new ObjectRemovedEvent(
ref.reffingOid, ref.field, oid));
// Log.info("Forcing removal " + ref + ".");
} else {
Log.info("Dangling reference from inactive object " +
ref + ".");
}
}
}
// if this object has any oid list fields that are still
// referencing other objects, we need to clear out those
// references
Class oclass = target.getClass();
Field[] fields = oclass.getFields();
for (int f = 0; f < fields.length; f++) {
Field field = fields[f];
// ignore static and non-public fields
int mods = field.getModifiers();
if ((mods & Modifier.STATIC) != 0 ||
(mods & Modifier.PUBLIC) == 0) {
continue;
}
// ignore non-oidlist fields
if (!OidList.class.isAssignableFrom(field.getType())) {
continue;
}
try {
OidList list = (OidList)field.get(target);
for (int i = 0; i < list.size(); i++) {
clearReference(target, field.getName(), list.get(i));
}
} catch (Exception e) {
Log.warning("Unable to clean up after oid list field " +
"[target=" + target + ", field=" + field + "].");
}
}
}
/**
* Called by <code>objectDestroyed</code>; clears out the tracking
* info for a reference by the supplied object to the specified oid
* via the specified field.
*/
protected void clearReference (
DObject reffer, String field, int reffedOid)
{
// look up the reference vector for the referenced object
Reference[] refs = (Reference[])_refs.get(reffedOid);
Reference ref = null;
if (refs != null) {
for (int i = 0; i < refs.length; i++) {
if (refs[i].equals(reffer.getOid(), field)) {
ref = refs[i];
refs[i] = null;
break;
}
}
}
if (ref == null) {
Log.warning("Requested to clear out non-existent reference " +
"[refferOid=" + reffer.getOid() +
", field=" + field +
", reffedOid=" + reffedOid + "].");
// } else {
// Log.info("Cleared out reference " + ref + ".");
}
}
/**
* Called as a helper for <code>ObjectAddedEvent</code> events. It
* updates the object/oid list tracking structures.
*/
public void objectAdded (DEvent event, DObject target)
{
ObjectAddedEvent oae = (ObjectAddedEvent)event;
int oid = oae.getOid();
Reference ref = new Reference(target.getOid(), oae.getName(), oid);
// get the reference vector for the referenced object. we use bare
// arrays rather than something like an array list to conserve
// memory. there will be many objects and references
Reference[] refs = (Reference[])_refs.get(oid);
if (refs == null) {
refs = new Reference[DEFREFVEC_SIZE];
_refs.put(oid, refs);
}
// determine where to add the reference
int rpos = -1;
for (int i = 0; i < refs.length; i++) {
if (ref.equals(refs[i])) {
Log.warning("Ignoring request to track existing " +
"reference " + ref + ".");
return;
} else if (refs[i] == null && rpos == -1) {
rpos = i;
}
}
// expand the refvec if necessary
if (rpos == -1) {
Reference[] nrefs = new Reference[refs.length*2];
System.arraycopy(refs, 0, nrefs, 0, refs.length);
rpos = refs.length;
_refs.put(oid, refs = nrefs);
}
// finally add the reference
refs[rpos] = ref;
// Log.info("Tracked reference " + ref + ".");
}
/**
* Called as a helper for <code>ObjectRemovedEvent</code> events. It
* updates the object/oid list tracking structures.
*/
public void objectRemoved (DEvent event, DObject target)
{
ObjectRemovedEvent ore = (ObjectRemovedEvent)event;
String field = ore.getName();
int toid = target.getOid();
int oid = ore.getOid();
// get the reference vector for the referenced object
Reference[] refs = (Reference[])_refs.get(oid);
if (refs == null) {
// this can happen normally when an object is destroyed. it
// will remove itself from the reference system and then
// generate object removed events for all of its referencees.
// so we opt not to log anything in this case
// Log.warning("Object removed without reference to track it " +
// "[toid=" + toid + ", field=" + field +
// ", oid=" + oid + "].");
return;
}
// look for the matching reference
for (int i = 0; i < refs.length; i++) {
if (refs[i].equals(toid, field)) {
// Log.info("Removed reference " + refs[i] + ".");
refs[i] = null;
return;
}
}
Log.warning("Unable to locate reference for removal " +
"[reffingOid=" + toid + ", field=" + field +
", reffedOid=" + oid + "].");
}
protected synchronized boolean isRunning ()
@@ -224,7 +406,7 @@ public class CherDObjectMgr implements DObjectManager
// insert it into the table
_objects.put(oid, obj);
// Log.info("Created object [obj=" + obj + "].");
// Log.info("Created object [obj=" + obj + "].");
if (_target != null) {
// add the subscriber to this object's subscriber list
@@ -351,17 +533,74 @@ public class CherDObjectMgr implements DObjectManager
method = omgrcl.getMethod("objectDestroyed", ptypes);
_helpers.put(ObjectDestroyedEvent.class, method);
method = omgrcl.getMethod("objectAdded", ptypes);
_helpers.put(ObjectAddedEvent.class, method);
method = omgrcl.getMethod("objectRemoved", ptypes);
_helpers.put(ObjectRemovedEvent.class, method);
} catch (Exception e) {
Log.warning("Unable to register event helpers " +
"[error=" + e + "].");
}
}
/**
* Used to track references of objects in oid lists.
*/
protected static class Reference
{
public int reffingOid;
public String field;
public int reffedOid;
public Reference (int reffingOid, String field, int reffedOid)
{
this.reffingOid = reffingOid;
this.field = field;
this.reffedOid = reffedOid;
}
public boolean equals (Reference other)
{
if (other == null) {
return false;
} else {
return (reffingOid == other.reffingOid &&
field.equals(other.field));
}
}
public boolean equals (int reffingOid, String field)
{
return (this.reffingOid == reffingOid && this.field.equals(field));
}
public String toString ()
{
return "[reffingOid=" + reffingOid + ", field=" + field +
", reffedOid=" + reffedOid + "]";
}
}
/** A flag indicating that the event dispatcher is still running. */
protected boolean _running = true;
/** The event queue via which all events are processed. */
protected Queue _evqueue = new Queue();
/** The managed distributed objects table. */
protected IntMap _objects = new IntMap();
/** Used to assign a unique oid to each distributed object. */
protected int _nextOid = 0;
/** Used to track oid list references of distributed objects. */
protected IntMap _refs = new IntMap();
/** The default size of an oid list refs vector. */
protected static final int DEFREFVEC_SIZE = 4;
/**
* This table maps event classes to helper methods that perform some
* additional processing for particular events.
@@ -1,5 +1,5 @@
//
// $Id: PresentsServer.java,v 1.9 2001/07/19 21:29:01 mdb Exp $
// $Id: PresentsServer.java,v 1.10 2001/08/08 00:28:49 mdb Exp $
package com.threerings.cocktail.cher.server;
@@ -132,15 +132,49 @@ public class CherServer
// start up the connection manager
conmgr.start();
// invoke the dobjmgr event loop
((CherDObjectMgr)omgr).run();
omgr.run();
}
/**
* Requests that the server shut down.
*/
public static void shutdown ()
{
// shut down our managers
authmgr.shutdown();
conmgr.shutdown();
omgr.shutdown();
}
public static void main (String[] args)
{
Log.info("Cher server starting...");
CherServer server = new CherServer();
try {
// initialize the server
server.init();
// check to see if we should load and invoke a test module
// before running the server
String testmod = System.getProperty("test_module");
if (testmod != null) {
try {
Log.info("Invoking test module [mod=" + testmod + "].");
Class tmclass = Class.forName(testmod);
Runnable trun = (Runnable)tmclass.newInstance();
trun.run();
} catch (Exception e) {
Log.warning("Unable to invoke test module " +
"[mod=" + testmod + "].");
Log.logStackTrace(e);
}
}
// start the server to running (this method call won't return
// until the server is shut down)
server.run();
} catch (Exception e) {
Log.warning("Unable to initialize server.");
Log.logStackTrace(e);
@@ -1,5 +1,5 @@
//
// $Id: AuthManager.java,v 1.3 2001/06/01 22:12:03 mdb Exp $
// $Id: AuthManager.java,v 1.4 2001/08/08 00:28:49 mdb Exp $
package com.threerings.cocktail.cher.server.net;
@@ -55,8 +55,16 @@ public class AuthManager extends LoopingThread
protected void iterate ()
{
// grab the next authing connection from the queue
AuthingConnection aconn = (AuthingConnection)_authq.get();
Object item = _authq.get();
// if we're being kicked and requested to exit, we'll just post
// some bogus item on the queue to wake up the auth manager and
// get him the hell out of dodge
if (!(item instanceof AuthingConnection)) {
return;
}
AuthingConnection aconn = (AuthingConnection)item;
try {
// instruct the authenticator to process the auth request
AuthResponse rsp = _author.process(aconn.getAuthRequest());
@@ -74,6 +82,12 @@ public class AuthManager extends LoopingThread
}
}
protected void kick ()
{
// we post something bogus to the queue to wake up the authmgr
_authq.append(new Integer(0));
}
protected Authenticator _author;
protected ConnectionManager _conmgr;
protected Queue _authq = new Queue();