Widening in preparation for modifications.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4523 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2007-02-02 22:38:19 +00:00
parent 8925db6450
commit cadde41ccb
@@ -45,29 +45,24 @@ import com.threerings.presents.dobj.*;
import static com.threerings.presents.Log.log; import static com.threerings.presents.Log.log;
/** /**
* The presents distributed object manager implements the {@link * The presents distributed object manager implements the {@link DObjectManager} interface,
* DObjectManager} interface, providing an object manager that runs on the * providing an object manager that runs on the server. By virtue of running on the server, it
* server. By virtue of running on the server, it manages its objects * manages its objects directly rather than managing proxies of objects which is what is done on
* directly rather than managing proxies of objects which is what is done * the client. Thus it simply queues up events and dispatches them to listeners.
* on the client. Thus it simply queues up events and dispatches them to
* listeners.
* *
* <p> The server object manager is meant to run on the main thread of the * <p> The server object manager is meant to run on the main thread of the server application and
* server application and thus provides a method to be invoked by the * thus provides a method to be invoked by the application main thread which won't return until the
* application main thread which won't return until the manager has been * manager has been requested to shut down.
* requested to shut down.
*/ */
public class PresentsDObjectMgr public class PresentsDObjectMgr
implements RootDObjectManager, RunQueue, PresentsServer.Reporter implements RootDObjectManager, RunQueue, PresentsServer.Reporter
{ {
/** Contains operational statistics that are tracked by the distributed /** Contains operational statistics that are tracked by the distributed object manager between
* object manager between {@link PresentsServer#Reporter} intervals. The * {@link PresentsServer#Reporter} intervals. The snapshot for the most recently completed
* snapshot for the most recently completed period can be requested via * period can be requested via {@link #getStats()}. . */
* {@link #getStats()}. . */
public static class Stats public static class Stats
{ {
/** The largest size of the distributed object queue during the /** The largest size of the distributed object queue during the period. */
* period. */
public int maxQueueSize; public int maxQueueSize;
/** The number of events dispatched during the period. */ /** The number of events dispatched during the period. */
@@ -79,8 +74,8 @@ public class PresentsDObjectMgr
*/ */
public PresentsDObjectMgr () public PresentsDObjectMgr ()
{ {
// we create a dummy object to live as oid zero and we'll use that // we create a dummy object to live as oid zero and we'll use that for some internal event
// for some internal event trickery // trickery
DObject dummy = new DObject(); DObject dummy = new DObject();
dummy.setOid(0); dummy.setOid(0);
dummy.setManager(this); dummy.setManager(this);
@@ -91,18 +86,16 @@ public class PresentsDObjectMgr
} }
/** /**
* Sets up an access controller that will be provided to any * Sets up an access controller that will be provided to any distributed objects created on the
* distributed objects created on the server. The controllers can * server. The controllers can subsequently be overridden if desired, but a default controller
* subsequently be overridden if desired, but a default controller is * is useful for implementing basic access control policies.
* useful for implementing basic access control policies.
*/ */
public void setDefaultAccessController (AccessController controller) public void setDefaultAccessController (AccessController controller)
{ {
AccessController oldDefault = _defaultController; AccessController oldDefault = _defaultController;
_defaultController = controller; _defaultController = controller;
// switch all objects from the old default (null, usually) // switch all objects from the old default (null, usually) to the new default.
// to the new default.
for (Iterator itr = _objects.elements(); itr.hasNext(); ) { for (Iterator itr = _objects.elements(); itr.hasNext(); ) {
DObject obj = (DObject) itr.next(); DObject obj = (DObject) itr.next();
if (oldDefault == obj.getAccessController()) { if (oldDefault == obj.getAccessController()) {
@@ -123,12 +116,10 @@ public class PresentsDObjectMgr
int oid, Subscriber<T> target) int oid, Subscriber<T> target)
{ {
if (oid <= 0) { if (oid <= 0) {
target.requestFailed( target.requestFailed(oid, new ObjectAccessException("Invalid oid " + oid + "."));
oid, new ObjectAccessException("Invalid oid " + oid + "."));
} else { } else {
// queue up an access object event // queue up an access object event
postEvent(new AccessObjectEvent<T>( postEvent(new AccessObjectEvent<T>(oid, target, AccessObjectEvent.SUBSCRIBE));
oid, target, AccessObjectEvent.SUBSCRIBE));
} }
} }
@@ -137,8 +128,7 @@ public class PresentsDObjectMgr
int oid, Subscriber<T> target) int oid, Subscriber<T> target)
{ {
// queue up an access object event // queue up an access object event
postEvent(new AccessObjectEvent<T>( postEvent(new AccessObjectEvent<T>(oid, target, AccessObjectEvent.UNSUBSCRIBE));
oid, target, AccessObjectEvent.UNSUBSCRIBE));
} }
// from interface DObjectManager // from interface DObjectManager
@@ -183,11 +173,28 @@ public class PresentsDObjectMgr
} }
/** /**
* Posts a self-contained unit of code that should be run on the * Returns the object in the object table with the specified oid or null if no object has that
* distributed object manager thread at the next available * oid. Be sure only to call this function from the dobjmgr thread and not to do anything funny
* opportunity. The code will be queued up with the rest of the events * with the object. If subscription is desired, use {@link #subscribeToObject}.
* and invoked in turn. Like event processing code, the code should */
* not take long to complete and should <em>definitely</em> not block. public DObject getObject (int oid)
{
return _objects.get(oid);
}
/**
* Returns the runtime statistics for the most recently completed reporting period.
*/
public Stats getStats ()
{
return _recent;
}
/**
* Posts a self-contained unit of code that should be run on the distributed object manager
* thread at the next available opportunity. The code will be queued up with the rest of the
* events and invoked in turn. Like event processing code, the code should not take long to
* complete and should <em>definitely</em> not block.
* *
* From interface RunQueue * From interface RunQueue
*/ */
@@ -198,30 +205,9 @@ public class PresentsDObjectMgr
} }
/** /**
* Returns the object in the object table with the specified oid or null if * Returns true if the thread invoking this method is the same thread that is doing distributed
* no object has that oid. Be sure only to call this function from the * object event dispatch. Code that wishes to enforce that it is either always or never called
* dobjmgr thread and not to do anything funny with the object. If * on the event dispatch thread will want to make use of this method.
* subscription is desired, use {@link #subscribeToObject}.
*/
public DObject getObject (int oid)
{
return _objects.get(oid);
}
/**
* Returns the runtime statistics for the most recently completed
* reporting period.
*/
public Stats getStats ()
{
return _recent;
}
/**
* Returns true if the thread invoking this method is the same thread
* that is doing distributed object event dispatch. Code that wishes
* to enforce that it is either always or never called on the event
* dispatch thread will want to make use of this method.
* *
* From interface RunQueue * From interface RunQueue
*/ */
@@ -231,8 +217,8 @@ public class PresentsDObjectMgr
} }
/** /**
* Runs the dobjmgr event loop until it is requested to exit. This * Runs the dobjmgr event loop until it is requested to exit. This should be called from the
* should be called from the main application thread. * main application thread.
*/ */
public void run () public void run ()
{ {
@@ -267,8 +253,7 @@ public class PresentsDObjectMgr
try { try {
if (unit instanceof Runnable) { if (unit instanceof Runnable) {
// if this is a runnable, it's just an executable unit // if this is a runnable, it's just an executable unit that should be invoked
// that should be invoked
((Runnable)unit).run(); ((Runnable)unit).run();
} else if (unit instanceof CompoundEvent) { } else if (unit instanceof CompoundEvent) {
@@ -293,25 +278,23 @@ public class PresentsDObjectMgr
// report excessively long units // report excessively long units
if (elapsed > 500000) { if (elapsed > 500000) {
log.warning("Long dobj unit [u=" + StringUtil.safeToString(unit) + log.warning("Long dobj unit [u=" + StringUtil.safeToString(unit) +
" (" + StringUtil.shortClassName(unit) + ")" + " (" + StringUtil.shortClassName(unit) + ")" +
", time=" + (elapsed/1000) + "ms]."); ", time=" + (elapsed/1000) + "ms].");
} }
// periodically sample and record the time spent processing a unit // periodically sample and record the time spent processing a unit
if (UNIT_PROF_ENABLED && _eventCount % UNIT_PROF_INTERVAL == 0) { if (UNIT_PROF_ENABLED && _eventCount % UNIT_PROF_INTERVAL == 0) {
String cname; String cname;
// do some jiggery pokery to get more fine grained profiling // do some jiggery pokery to get more fine grained profiling details on certain
// details on certain "popular" unit types // "popular" unit types
if (unit instanceof Interval.RunBuddy) { if (unit instanceof Interval.RunBuddy) {
Interval ival = ((Interval.RunBuddy)unit).getInterval(); Interval ival = ((Interval.RunBuddy)unit).getInterval();
cname = StringUtil.shortClassName(ival); cname = StringUtil.shortClassName(ival);
} else if (unit instanceof InvocationRequestEvent) { } else if (unit instanceof InvocationRequestEvent) {
InvocationRequestEvent ire = (InvocationRequestEvent)unit; InvocationRequestEvent ire = (InvocationRequestEvent)unit;
Class c = PresentsServer.invmgr.getDispatcherClass( Class c = PresentsServer.invmgr.getDispatcherClass(ire.getInvCode());
ire.getInvCode()); cname = (c == null) ? "dobj.InvocationRequestEvent:(no longer registered)" :
cname = (c == null) StringUtil.shortClassName(c) + ":" + ire.getMethodId();
? "dobj.InvocationRequestEvent:(no longer registered)"
: StringUtil.shortClassName(c) + ":" + ire.getMethodId();
} else { } else {
cname = StringUtil.shortClassName(unit); cname = StringUtil.shortClassName(unit);
} }
@@ -324,8 +307,7 @@ public class PresentsDObjectMgr
} }
/** /**
* Performs the processing associated with a compound event, notifying * Performs the processing associated with a compound event, notifying listeners and the like.
* listeners and the like.
*/ */
protected void processCompoundEvent (CompoundEvent event) protected void processCompoundEvent (CompoundEvent event)
{ {
@@ -335,8 +317,7 @@ public class PresentsDObjectMgr
// look up the target object // look up the target object
DObject target = _objects.get(event.getTargetOid()); DObject target = _objects.get(event.getTargetOid());
if (target == null) { if (target == null) {
log.fine("Compound event target no longer exists " + log.fine("Compound event target no longer exists [event=" + event + "].");
"[event=" + event + "].");
return; return;
} }
@@ -344,8 +325,8 @@ public class PresentsDObjectMgr
for (int ii = 0; ii < ecount; ii++) { for (int ii = 0; ii < ecount; ii++) {
DEvent sevent = (DEvent)events.get(ii); DEvent sevent = (DEvent)events.get(ii);
if (!target.checkPermissions(sevent)) { if (!target.checkPermissions(sevent)) {
log.warning("Event failed permissions check " + log.warning("Event failed permissions check [event=" + sevent +
"[event=" + sevent + ", target=" + target + "]."); ", target=" + target + "].");
return; return;
} }
} }
@@ -360,8 +341,7 @@ public class PresentsDObjectMgr
} }
/** /**
* Performs the processing associated with an event, notifying * Performs the processing associated with an event, notifying listeners and the like.
* listeners and the like.
*/ */
protected void processEvent (DEvent event) protected void processEvent (DEvent event)
{ {
@@ -374,8 +354,8 @@ public class PresentsDObjectMgr
// check the event's permissions // check the event's permissions
if (!target.checkPermissions(event)) { if (!target.checkPermissions(event)) {
log.warning("Event failed permissions check " + log.warning("Event failed permissions check [event=" + event +
"[event=" + event + ", target=" + target + "]."); ", target=" + target + "].");
return; return;
} }
@@ -386,9 +366,8 @@ public class PresentsDObjectMgr
} }
/** /**
* Dispatches an event after the target object has been resolved and * Dispatches an event after the target object has been resolved and the permissions have been
* the permissions have been checked. This is used by {@link * checked. This is used by {@link #processEvent} and {@link #processCompoundEvent}.
* #processEvent} and {@link #processCompoundEvent}.
* *
* @return the value returned by {@link DEvent#applyToObject}. * @return the value returned by {@link DEvent#applyToObject}.
*/ */
@@ -410,9 +389,8 @@ public class PresentsDObjectMgr
// everything's good so far, apply the event to the object // everything's good so far, apply the event to the object
notify = event.applyToObject(target); notify = event.applyToObject(target);
// if the event returns false from applyToObject, this // if the event returns false from applyToObject, this means it's a silent event and we
// means it's a silent event and we shouldn't notify the // shouldn't notify the listeners
// listeners
if (notify) { if (notify) {
target.notifyListeners(event); target.notifyListeners(event);
} }
@@ -422,7 +400,7 @@ public class PresentsDObjectMgr
} catch (Throwable t) { } catch (Throwable t) {
log.log(Level.WARNING, "Failure processing event [event=" + event + log.log(Level.WARNING, "Failure processing event [event=" + event +
", target=" + target + "].", t); ", target=" + target + "].", t);
} }
// track the number of events dispatched // track the number of events dispatched
@@ -432,22 +410,21 @@ public class PresentsDObjectMgr
} }
/** /**
* Attempts to recover from fatal errors but rethrows if things are * Attempts to recover from fatal errors but rethrows if things are freaking out too
* freaking out too frequently. * frequently.
*/ */
protected void handleFatalError (Object causer, Error error) protected void handleFatalError (Object causer, Error error)
{ {
if (_fatalThrottle.throttleOp()) { if (_fatalThrottle.throttleOp()) {
throw error; throw error;
} }
log.log(Level.WARNING, log.log(Level.WARNING, "Fatal error caused by '" + causer + "': " + error, error);
"Fatal error caused by '" + causer + "': " + error, error);
} }
/** /**
* Requests that the dobjmgr shut itself down soon- you may * Requests that the dobjmgr shut itself down soon- you may want to try using {@link
* want to try using {@link Invoker#shutdown} which will make sure that * Invoker#shutdown} which will make sure that both the Invoker and DObjectMgr are empty and
* both the Invoker and DObjectMgr are empty and then shut them both down. * then shut them both down.
*/ */
public void harshShutdown () public void harshShutdown ()
{ {
@@ -469,18 +446,16 @@ public class PresentsDObjectMgr
} }
/** /**
* Called as a helper for <code>ObjectDestroyedEvent</code> events. It * Called as a helper for <code>ObjectDestroyedEvent</code> events. It removes the object from
* removes the object from the object table. * the object table.
* *
* @return true if the event should be dispatched, false if it should * @return true if the event should be dispatched, false if it should be aborted.
* be aborted.
*/ */
public boolean objectDestroyed (DEvent event, DObject target) public boolean objectDestroyed (DEvent event, DObject target)
{ {
int oid = target.getOid(); int oid = target.getOid();
// log.info("Removing destroyed object from table " + // log.info("Removing destroyed object from table [oid=" + oid + "].");
// "[oid=" + oid + "].");
// remove the object from the table // remove the object from the table
_objects.remove(oid); _objects.remove(oid);
@@ -503,20 +478,17 @@ public class PresentsDObjectMgr
// ensure that the referencing object is still around // ensure that the referencing object is still around
if (reffer != null) { if (reffer != null) {
// post an object removed event to clear the reference // post an object removed event to clear the reference
postEvent(new ObjectRemovedEvent( postEvent(new ObjectRemovedEvent(ref.reffingOid, ref.field, oid));
ref.reffingOid, ref.field, oid)); // log.info("Forcing removal " + ref + ".");
// log.info("Forcing removal " + ref + ".");
} else { } else {
log.info("Dangling reference from inactive object " + log.info("Dangling reference from inactive object " + ref + ".");
ref + ".");
} }
} }
} }
// if this object has any oid list fields that are still // if this object has any oid list fields that are still referencing other objects, we need
// referencing other objects, we need to clear out those // to clear out those references
// references
Class oclass = target.getClass(); Class oclass = target.getClass();
Field[] fields = oclass.getFields(); Field[] fields = oclass.getFields();
for (int f = 0; f < fields.length; f++) { for (int f = 0; f < fields.length; f++) {
@@ -524,8 +496,7 @@ public class PresentsDObjectMgr
// ignore static and non-public fields // ignore static and non-public fields
int mods = field.getModifiers(); int mods = field.getModifiers();
if ((mods & Modifier.STATIC) != 0 || if ((mods & Modifier.STATIC) != 0 || (mods & Modifier.PUBLIC) == 0) {
(mods & Modifier.PUBLIC) == 0) {
continue; continue;
} }
@@ -541,8 +512,8 @@ public class PresentsDObjectMgr
} }
} catch (Exception e) { } catch (Exception e) {
log.warning("Unable to clean up after oid list field " + log.warning("Unable to clean up after oid list field [target=" + target +
"[target=" + target + ", field=" + field + "]."); ", field=" + field + "].");
} }
} }
@@ -550,12 +521,10 @@ public class PresentsDObjectMgr
} }
/** /**
* Called by <code>objectDestroyed</code>; clears out the tracking * Called by <code>objectDestroyed</code>; clears out the tracking info for a reference by the
* info for a reference by the supplied object to the specified oid * supplied object to the specified oid via the specified field.
* via the specified field.
*/ */
protected void clearReference ( protected void clearReference (DObject reffer, String field, int reffedOid)
DObject reffer, String field, int reffedOid)
{ {
// look up the reference vector for the referenced object // look up the reference vector for the referenced object
Reference[] refs = _refs.get(reffedOid); Reference[] refs = _refs.get(reffedOid);
@@ -571,16 +540,14 @@ public class PresentsDObjectMgr
} }
} }
// if a referred object and referring object are both destroyed without // if a referred object and referring object are both destroyed without allowing the
// allowing the referred object destruction to process the ObjectRemoved // referred object destruction to process the ObjectRemoved event which is auto-generated,
// event which is auto-generated, the subsequent destruction of the // the subsequent destruction of the referring object will attempt to clear the reference
// referring object will attempt to clear the reference to the referred // to the referred object which no longer exists; so we don't complain about non- existent
// object which no longer exists; so we don't complain about non- // references if the referree is already destroyed
// existent references if the referree is already destroyed
if (ref == null && _objects.containsKey(reffedOid)) { if (ref == null && _objects.containsKey(reffedOid)) {
log.warning("Requested to clear out non-existent reference " + log.warning("Requested to clear out non-existent reference " +
"[refferOid=" + reffer.getOid() + "[refferOid=" + reffer.getOid() + ", field=" + field +
", field=" + field +
", reffedOid=" + reffedOid + "]."); ", reffedOid=" + reffedOid + "].");
// } else { // } else {
@@ -589,11 +556,10 @@ public class PresentsDObjectMgr
} }
/** /**
* Called as a helper for <code>ObjectAddedEvent</code> events. It * Called as a helper for <code>ObjectAddedEvent</code> events. It updates the object/oid list
* updates the object/oid list tracking structures. * tracking structures.
* *
* @return true if the event should be dispatched, false if it should * @return true if the event should be dispatched, false if it should be aborted.
* be aborted.
*/ */
public boolean objectAdded (DEvent event, DObject target) public boolean objectAdded (DEvent event, DObject target)
{ {
@@ -603,14 +569,13 @@ public class PresentsDObjectMgr
// ensure that the target object exists // ensure that the target object exists
if (!_objects.containsKey(oid)) { if (!_objects.containsKey(oid)) {
log.info("Rejecting object added event of non-existent object " + log.info("Rejecting object added event of non-existent object " +
"[refferOid=" + target.getOid() + "[refferOid=" + target.getOid() + ", reffedOid=" + oid + "].");
", reffedOid=" + oid + "].");
return false; return false;
} }
// get the reference vector for the referenced object. we use bare // get the reference vector for the referenced object. we use bare arrays rather than
// arrays rather than something like an array list to conserve // something like an array list to conserve memory. there will be many objects and
// memory. there will be many objects and references // references
Reference[] refs = _refs.get(oid); Reference[] refs = _refs.get(oid);
if (refs == null) { if (refs == null) {
refs = new Reference[DEFREFVEC_SIZE]; refs = new Reference[DEFREFVEC_SIZE];
@@ -622,8 +587,7 @@ public class PresentsDObjectMgr
int rpos = -1; int rpos = -1;
for (int i = 0; i < refs.length; i++) { for (int i = 0; i < refs.length; i++) {
if (ref.equals(refs[i])) { if (ref.equals(refs[i])) {
log.warning("Ignoring request to track existing " + log.warning("Ignoring request to track existing reference " + ref + ".");
"reference " + ref + ".");
return true; return true;
} else if (refs[i] == null && rpos == -1) { } else if (refs[i] == null && rpos == -1) {
rpos = i; rpos = i;
@@ -646,11 +610,10 @@ public class PresentsDObjectMgr
} }
/** /**
* Called as a helper for <code>ObjectRemovedEvent</code> events. It * Called as a helper for <code>ObjectRemovedEvent</code> events. It updates the object/oid
* updates the object/oid list tracking structures. * list tracking structures.
* *
* @return true if the event should be dispatched, false if it should * @return true if the event should be dispatched, false if it should be aborted.
* be aborted.
*/ */
public boolean objectRemoved (DEvent event, DObject target) public boolean objectRemoved (DEvent event, DObject target)
{ {
@@ -659,20 +622,17 @@ public class PresentsDObjectMgr
int toid = target.getOid(); int toid = target.getOid();
int oid = ore.getOid(); int oid = ore.getOid();
// log.info("Processing object removed [from=" + toid + // log.info("Processing object removed [from=" + toid + ", roid=" + toid + "].");
// ", roid=" + toid + "].");
// get the reference vector for the referenced object // get the reference vector for the referenced object
Reference[] refs = _refs.get(oid); Reference[] refs = _refs.get(oid);
if (refs == null) { if (refs == null) {
// this can happen normally when an object is destroyed. it // this can happen normally when an object is destroyed. it will remove itself from the
// will remove itself from the reference system and then // reference system and then generate object removed events for all of its referencees.
// generate object removed events for all of its referencees.
// so we opt not to log anything in this case // so we opt not to log anything in this case
// log.info("Object removed without reference to track it " + // log.info("Object removed without reference to track it [toid=" + toid +
// "[toid=" + toid + ", field=" + field + // ", field=" + field + ", oid=" + oid + "].");
// ", oid=" + oid + "].");
return true; return true;
} }
@@ -686,15 +646,14 @@ public class PresentsDObjectMgr
} }
} }
log.warning("Unable to locate reference for removal " + log.warning("Unable to locate reference for removal [reffingOid=" + toid +
"[reffingOid=" + toid + ", field=" + field + ", field=" + field + ", reffedOid=" + oid + "].");
", reffedOid=" + oid + "].");
return true; return true;
} }
/** /**
* Should not need to be called except by the invoker during shutdown * Should not need to be called except by the invoker during shutdown to ensure that things are
* to ensure that things are proceeding smoothly. * proceeding smoothly.
*/ */
public boolean queueIsEmpty () public boolean queueIsEmpty ()
{ {
@@ -708,35 +667,28 @@ public class PresentsDObjectMgr
protected int getNextOid () protected int getNextOid ()
{ {
// look for the next unused oid. in theory if we had two billion // look for the next unused oid. in theory if we had two billion objects, this would loop
// objects, this would loop infinitely, but the world would have // infinitely, but the world will come to an end long before we have two billion objects
// come to an end long before we had two billion objects
do { do {
_nextOid = (_nextOid + 1) % Integer.MAX_VALUE; _nextOid = (_nextOid + 1) % Integer.MAX_VALUE;
} while (_objects.containsKey(_nextOid)); } while (_objects.containsKey(_nextOid));
return _nextOid; return _nextOid;
} }
// from interface PresentsServer.Reporter // from interface PresentsServer.Reporter
public void appendReport ( public void appendReport (StringBuilder report, long now, long sinceLast, boolean reset)
StringBuilder report, long now, long sinceLast, boolean reset)
{ {
report.append("* presents.PresentsDObjectMgr:\n"); report.append("* presents.PresentsDObjectMgr:\n");
int queueSize = _evqueue.size(); int queueSize = _evqueue.size();
report.append("- Queue size: ").append(queueSize).append("\n"); report.append("- Queue size: ").append(queueSize).append("\n");
report.append("- Max queue size: ").append(_current.maxQueueSize); report.append("- Max queue size: ").append(_current.maxQueueSize).append("\n");
report.append("\n"); report.append("- Units executed: ").append(_current.eventCount).append("\n");
report.append("- Units executed: ").append(_current.eventCount);
report.append("\n");
if (UNIT_PROF_ENABLED) { if (UNIT_PROF_ENABLED) {
report.append("- Unit profiles: ").append(_profiles.size()); report.append("- Unit profiles: ").append(_profiles.size()).append("\n");
report.append("\n");
for (Map.Entry<String,UnitProfile> entry : _profiles.entrySet()) { for (Map.Entry<String,UnitProfile> entry : _profiles.entrySet()) {
report.append(" ").append(entry.getKey()); report.append(" ").append(entry.getKey());
report.append(" ").append(entry.getValue()); report.append(" ").append(entry.getValue()).append("\n");
report.append("\n");
} }
} }
@@ -749,32 +701,29 @@ public class PresentsDObjectMgr
} }
/** /**
* Calls {@link Subscriber#objectAvailable} and catches and logs any * Calls {@link Subscriber#objectAvailable} and catches and logs any exception thrown by the
* exception thrown by the subscriber during the call. * subscriber during the call.
*/ */
protected static <T extends DObject> void informObjectAvailable ( protected static <T extends DObject> void informObjectAvailable (Subscriber<T> sub, T obj)
Subscriber<T> sub, T obj)
{ {
try { try {
sub.objectAvailable(obj); sub.objectAvailable(obj);
} catch (Exception e) { } catch (Exception e) {
log.log(Level.WARNING, "Subscriber choked during object " + log.log(Level.WARNING, "Subscriber choked during object available " +
"available [obj=" + StringUtil.safeToString(obj) + "[obj=" + StringUtil.safeToString(obj) + ", sub=" + sub + "].", e);
", sub=" + sub + "].", e);
} }
} }
/** /**
* Used to make an object available to a subscriber (with or without * Used to make an object available to a subscriber (with or without the associated
* the associated subscription). * subscription).
*/ */
protected class AccessObjectEvent<T extends DObject> extends DEvent protected class AccessObjectEvent<T extends DObject> extends DEvent
{ {
public static final int SUBSCRIBE = 0; public static final int SUBSCRIBE = 0;
public static final int UNSUBSCRIBE = 1; public static final int UNSUBSCRIBE = 1;
public AccessObjectEvent (int oid, Subscriber<T> target, public AccessObjectEvent (int oid, Subscriber<T> target, int action)
int action)
{ {
super(0); // target the bogus object super(0); // target the bogus object
_oid = oid; _oid = oid;
@@ -820,8 +769,8 @@ public class PresentsDObjectMgr
// let them know that things are groovy // let them know that things are groovy
informObjectAvailable(_target, obj); informObjectAvailable(_target, obj);
// return false to ensure that this event is not dispatched to // return false to ensure that this event is not dispatched to the fake object's
// the fake object's subscriber list (even though it's empty) // subscriber list (even though it's empty)
return false; return false;
} }
@@ -850,8 +799,7 @@ public class PresentsDObjectMgr
_helpers.put(ObjectRemovedEvent.class, method); _helpers.put(ObjectRemovedEvent.class, method);
} catch (Exception e) { } catch (Exception e) {
log.warning("Unable to register event helpers " + log.warning("Unable to register event helpers [error=" + e + "].");
"[error=" + e + "].");
} }
} }
@@ -876,8 +824,7 @@ public class PresentsDObjectMgr
if (other == null) { if (other == null) {
return false; return false;
} else { } else {
return (reffingOid == other.reffingOid && return (reffingOid == other.reffingOid && field.equals(other.field));
field.equals(other.field));
} }
} }
@@ -893,8 +840,8 @@ public class PresentsDObjectMgr
} }
} }
/** Used to profile time spent invoking units and processing events if /** Used to profile time spent invoking units and processing events if such profiling is
* such profiling is enabled. */ * enabled. */
protected static class UnitProfile protected static class UnitProfile
{ {
public void record (long start, long elapsed) public void record (long start, long elapsed)
@@ -906,8 +853,7 @@ public class PresentsDObjectMgr
public String toString () public String toString ()
{ {
int count = _histo.size(); int count = _histo.size();
return _totalElapsed + "us/" + count + " = " + return _totalElapsed + "us/" + count + " = " + (_totalElapsed/count) + "us avg " +
(_totalElapsed/count) + "us avg " +
StringUtil.toString(_histo.getBuckets()); StringUtil.toString(_histo.getBuckets());
} }
@@ -930,29 +876,26 @@ public class PresentsDObjectMgr
/** Used to track the number of events dispatched over time. */ /** Used to track the number of events dispatched over time. */
protected long _eventCount = 0; protected long _eventCount = 0;
/** Track fatal errors so that we can stick a fork in ourselves if /** Track fatal errors so that we can stick a fork in ourselves if things get too far out of
* things get too far out of hand. More than 30 fatal errors in the * hand. More than 30 fatal errors in the span of a minute and we throw in the towel. */
* span of a minute and we throw in the towel. */
protected Throttle _fatalThrottle = new Throttle(30, 60*1000L); protected Throttle _fatalThrottle = new Throttle(30, 60*1000L);
/** Used to track oid list references of distributed objects. */ /** Used to track oid list references of distributed objects. */
protected HashIntMap<Reference[]> _refs = new HashIntMap<Reference[]>(); protected HashIntMap<Reference[]> _refs = new HashIntMap<Reference[]>();
/** The default access controller to use when creating distributed /** The default access controller to use when creating distributed objects. */
* objects. */
protected AccessController _defaultController; protected AccessController _defaultController;
/** We keep track of which thread is executing the event loop so that /** We keep track of which thread is executing the event loop so that other services can
* other services can enforce restrictions on code that should or * enforce restrictions on code that should or should not be called from the event dispatch
* should not be called from the event dispatch thread. */ * thread. */
protected Thread _dobjThread; protected Thread _dobjThread;
/** Used during unit profiling for timing values. */ /** Used during unit profiling for timing values. */
protected Perf _timer = Perf.getPerf(); protected Perf _timer = Perf.getPerf();
/** Used to profile our events and runnable units. */ /** Used to profile our events and runnable units. */
protected HashMap<String,UnitProfile> _profiles = protected HashMap<String,UnitProfile> _profiles = new HashMap<String,UnitProfile>();
new HashMap<String,UnitProfile>();
/** Used to track runtime statistics. */ /** Used to track runtime statistics. */
protected Stats _recent = new Stats(), _current = _recent; protected Stats _recent = new Stats(), _current = _recent;
@@ -966,11 +909,8 @@ public class PresentsDObjectMgr
/** The default size of an oid list refs vector. */ /** The default size of an oid list refs vector. */
protected static final int DEFREFVEC_SIZE = 4; protected static final int DEFREFVEC_SIZE = 4;
/** /** This table maps event classes to helper methods that perform some additional processing for
* This table maps event classes to helper methods that perform some * particular events. */
* additional processing for particular events. protected static HashMap<Class,Method> _helpers = new HashMap<Class,Method>();
*/
protected static HashMap<Class,Method> _helpers =
new HashMap<Class,Method>();
static { registerEventHelpers(); } static { registerEventHelpers(); }
} }