Report a more useful error if we have a bogus controller. Tidied some other

bits up.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4861 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2007-11-02 23:02:36 +00:00
parent 87a1e8968e
commit d84b2ac8a0
@@ -21,18 +21,20 @@
package com.threerings.crowd.client; package com.threerings.crowd.client;
import java.util.logging.Level;
import com.samskivert.util.ObserverList; import com.samskivert.util.ObserverList;
import com.samskivert.util.ObserverList.ObserverOp; import com.samskivert.util.ObserverList.ObserverOp;
import com.samskivert.util.ResultListener; import com.samskivert.util.ResultListener;
import com.threerings.presents.client.BasicDirector; import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client; import com.threerings.presents.client.Client;
import com.threerings.presents.util.SafeSubscriber;
import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.ObjectAccessException; import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.Subscriber; import com.threerings.presents.dobj.Subscriber;
import com.threerings.crowd.Log;
import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.CrowdCodes; import com.threerings.crowd.data.CrowdCodes;
import com.threerings.crowd.data.LocationCodes; import com.threerings.crowd.data.LocationCodes;
@@ -40,6 +42,8 @@ import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext; import com.threerings.crowd.util.CrowdContext;
import static com.threerings.crowd.Log.log;
/** /**
* The location director provides a means by which entities on the client can request to move from * The location director provides a means by which entities on the client can request to move from
* place to place and can be notified if other entities have caused the client to move to a new * place to place and can be notified if other entities have caused the client to move to a new
@@ -47,8 +51,7 @@ import com.threerings.crowd.util.CrowdContext;
* actually issuing the request. * actually issuing the request.
*/ */
public class LocationDirector extends BasicDirector public class LocationDirector extends BasicDirector
implements LocationCodes, Subscriber<PlaceObject>, LocationReceiver, implements LocationCodes, LocationReceiver, LocationService.MoveListener
LocationService.MoveListener
{ {
/** /**
* Used to recover from a moveTo request that was accepted but resulted in a failed attempt to * Used to recover from a moveTo request that was accepted but resulted in a failed attempt to
@@ -123,7 +126,7 @@ public class LocationDirector extends BasicDirector
{ {
// make sure the placeId is valid // make sure the placeId is valid
if (placeId < 0) { if (placeId < 0) {
Log.warning("Refusing moveTo(): invalid placeId " + placeId + "."); log.warning("Refusing moveTo(): invalid placeId " + placeId + ".");
return false; return false;
} }
@@ -141,12 +144,12 @@ public class LocationDirector extends BasicDirector
// if the pending request has been outstanding more than a minute, go ahead and let // if the pending request has been outstanding more than a minute, go ahead and let
// this new one through in an attempt to recover from dropped moveTo requests // this new one through in an attempt to recover from dropped moveTo requests
if (refuse) { if (refuse) {
Log.warning("Refusing moveTo; We have a request outstanding " + log.warning("Refusing moveTo; We have a request outstanding " +
"[ppid=" + _pendingPlaceId + ", npid=" + placeId + "]."); "[ppid=" + _pendingPlaceId + ", npid=" + placeId + "].");
return false; return false;
} else { } else {
Log.warning("Overriding stale moveTo request [ppid=" + _pendingPlaceId + log.warning("Overriding stale moveTo request [ppid=" + _pendingPlaceId +
", npid=" + placeId + "]."); ", npid=" + placeId + "].");
} }
} }
@@ -155,7 +158,7 @@ public class LocationDirector extends BasicDirector
_pendingPlaceId = placeId; _pendingPlaceId = placeId;
// issue a moveTo request // issue a moveTo request
Log.info("Issuing moveTo(" + placeId + ")."); log.info("Issuing moveTo(" + placeId + ").");
_lservice.moveTo(_ctx.getClient(), placeId, this); _lservice.moveTo(_ctx.getClient(), placeId, this);
return true; return true;
} }
@@ -244,8 +247,8 @@ public class LocationDirector extends BasicDirector
try { try {
_controller.mayLeavePlace(_plobj); _controller.mayLeavePlace(_plobj);
} catch (Exception e) { } catch (Exception e) {
Log.warning("Place controller choked in mayLeavePlace [plobj=" + _plobj + "]."); log.log(Level.WARNING, "Place controller choked in mayLeavePlace " +
Log.logStackTrace(e); "[plobj=" + _plobj + "].", e);
} }
} }
} }
@@ -279,15 +282,36 @@ public class LocationDirector extends BasicDirector
_placeId = placeId; _placeId = placeId;
// start up a new place controller to manage the new place // start up a new place controller to manage the new place
_controller = createController(config); try {
if (_controller == null) { _controller = createController(config);
Log.warning("Place config returned null controller [config=" + config + "]."); if (_controller == null) {
return; log.warning("Place config returned null controller [config=" + config + "].");
} return;
_controller.init(_ctx, config); }
_controller.init(_ctx, config);
// subscribe to our new place object to complete the move // subscribe to our new place object to complete the move
_ctx.getDObjectManager().subscribeToObject(_placeId, this); _subber = new SafeSubscriber<PlaceObject>(_placeId, new Subscriber<PlaceObject>() {
public void objectAvailable (PlaceObject object) {
gotPlaceObject(object);
}
public void requestFailed (int oid, ObjectAccessException cause) {
// aiya! we were unable to fetch our new place object; something is badly wrong
log.warning("Aiya! Unable to fetch place object for new location [plid=" + oid +
", reason=" + cause + "].");
// clear out our half initialized place info
int placeId = _placeId;
_placeId = -1;
// let the kids know shit be fucked
handleFailure(placeId, "m.unable_to_fetch_place_object");
}
});
_subber.subscribe(_ctx.getDObjectManager());
} catch (Exception e) {
log.log(Level.WARNING, "Failed to create place controller [config=" + config + "].", e);
handleFailure(_placeId, LocationCodes.E_INTERNAL_ERROR);
}
} }
/** /**
@@ -297,25 +321,26 @@ public class LocationDirector extends BasicDirector
*/ */
public void didLeavePlace () public void didLeavePlace ()
{ {
if (_plobj != null) { // unsubscribe from our old place object
// let the old controller know that things are going away if (_subber != null) {
if (_controller != null) { _subber.unsubscribe(_ctx.getDObjectManager());
try { _subber = null;
_controller.didLeavePlace(_plobj);
} catch (Exception e) {
Log.warning("Place controller choked in didLeavePlace [plobj=" + _plobj + "].");
Log.logStackTrace(e);
}
_controller = null;
}
// unsubscribe from our old place object
_ctx.getDObjectManager().unsubscribeFromObject(_plobj.getOid(), this);
_plobj = null;
// and clear out the associated place id
_placeId = -1;
} }
// let the old controller know that things are going away
if (_plobj != null && _controller != null) {
try {
_controller.didLeavePlace(_plobj);
} catch (Exception e) {
log.log(Level.WARNING, "Place controller choked in didLeavePlace " +
"[plobj=" + _plobj + "].", e);
}
}
// and clear out other bits
_plobj = null;
_controller = null;
_placeId = -1;
} }
/** /**
@@ -337,7 +362,7 @@ public class LocationDirector extends BasicDirector
_lastRequestTime = 0; _lastRequestTime = 0;
// let our observers know what's up // let our observers know what's up
notifyFailure(placeId, reason); handleFailure(placeId, reason);
} }
/** /**
@@ -367,7 +392,7 @@ public class LocationDirector extends BasicDirector
gotBodyObject(object); gotBodyObject(object);
} }
public void requestFailed (int oid, ObjectAccessException cause) { public void requestFailed (int oid, ObjectAccessException cause) {
Log.warning("Location director unable to fetch body object; all has gone " + log.warning("Location director unable to fetch body object; all has gone " +
"horribly wrong [cause=" + cause + "]."); "horribly wrong [cause=" + cause + "].");
} }
}; };
@@ -408,6 +433,28 @@ public class LocationDirector extends BasicDirector
_lservice = (LocationService)client.requireService(LocationService.class); _lservice = (LocationService)client.requireService(LocationService.class);
} }
protected void gotPlaceObject (PlaceObject object)
{
// yay, we have our new place object
_plobj = object;
// fill in our manager caller
_plobj.initManagerCaller(_ctx.getClient().getDObjectManager());
// let the place controller know that we're ready to roll
if (_controller != null) {
try {
_controller.willEnterPlace(_plobj);
} catch (Exception e) {
log.log(Level.WARNING, "Controller choked in willEnterPlace " +
"[place=" + _plobj + "].", e);
}
}
// let our observers know that all is well on the western front
_observers.apply(_didChangeOp);
}
protected void gotBodyObject (BodyObject clobj) protected void gotBodyObject (BodyObject clobj)
{ {
// TODO? check to see if we are already in a location, in which case we'll want to be going // TODO? check to see if we are already in a location, in which case we'll want to be going
@@ -431,10 +478,10 @@ public class LocationDirector extends BasicDirector
int placeId = _pendingPlaceId; int placeId = _pendingPlaceId;
_pendingPlaceId = -1; _pendingPlaceId = -1;
Log.info("moveTo failed [pid=" + placeId + ", reason=" + reason + "]."); log.info("moveTo failed [pid=" + placeId + ", reason=" + reason + "].");
// let our observers know that something has gone horribly awry // let our observers know that something has gone horribly awry
notifyFailure(placeId, reason); handleFailure(placeId, reason);
} }
// documentation inherited from interface // documentation inherited from interface
@@ -444,12 +491,12 @@ public class LocationDirector extends BasicDirector
// just finish up what we're doing and assume that the repeated move request was the // just finish up what we're doing and assume that the repeated move request was the
// spurious one as it would be in the case of lag causing rapid-fire repeat requests // spurious one as it would be in the case of lag causing rapid-fire repeat requests
if (movePending()) { if (movePending()) {
Log.info("Dropping forced move because we have a move pending " + log.info("Dropping forced move because we have a move pending " +
"[pendId=" + _pendingPlaceId + ", reqId=" + placeId + "]."); "[pendId=" + _pendingPlaceId + ", reqId=" + placeId + "].");
return; return;
} }
Log.info("Moving at request of server [placeId=" + placeId + "]."); log.info("Moving at request of server [placeId=" + placeId + "].");
// clear out our old place information // clear out our old place information
mayLeavePlace(); mayLeavePlace();
didLeavePlace(); didLeavePlace();
@@ -458,48 +505,34 @@ public class LocationDirector extends BasicDirector
} }
/** /**
* Called when we receive the place object to which we subscribed after a successful moveTo * Sets the failure handler which will recover from place object fetching failures. In the
* request. * event that we are unable to fetch our place object after making a successful moveTo request,
* we attempt to rectify the failure by moving back to the last known working location. Because
* entites that cooperate with the location director may need to become involved in this
* failure recovery, we provide this interface whereby they can interject themseves into the
* failure recovery process and do their own failure recovery.
*/ */
public void objectAvailable (PlaceObject object) public void setFailureHandler (FailureHandler handler)
{ {
// yay, we have our new place object if (_failureHandler != null) {
_plobj = object; log.warning("Requested to set failure handler, but we've already got one. The " +
"conflicting entities will likely need to perform more sophisticated " +
"coordination to deal with failures. [old=" + _failureHandler +
", new=" + handler + "].");
// fill in our manager caller } else {
_plobj.initManagerCaller(_ctx.getClient().getDObjectManager()); _failureHandler = handler;
// let the place controller know that we're ready to roll
if (_controller != null) {
try {
_controller.willEnterPlace(_plobj);
} catch (Exception e) {
Log.warning("Controller choked in willEnterPlace [place=" + _plobj + "].");
Log.logStackTrace(e);
}
} }
// let our observers know that all is well on the western front
_observers.apply(_didChangeOp);
} }
/** protected void handleFailure (final int placeId, final String reason)
* Called if we are unable to subscribe to the place object that was provided to us with our
* successful moveTo request. This is generally a bad scene and we do our best to recover by
* going back to the previously known location.
*/
public void requestFailed (int oid, ObjectAccessException cause)
{ {
// aiya! we were unable to fetch our new place object; something is badly wrong _observers.apply(new ObserverOp<LocationObserver>() {
Log.warning("Aiya! Unable to fetch place object for new location [plid=" + oid + public boolean apply (LocationObserver obs) {
", reason=" + cause + "]."); obs.locationChangeFailed(placeId, reason);
return true;
// clear out our half initialized place info }
int placeId = _placeId; });
_placeId = -1;
// let the kids know shit be fucked
notifyFailure(placeId, "m.unable_to_fetch_place_object");
// we need to sort out what to do about the half-initialized place controller. presently we // we need to sort out what to do about the half-initialized place controller. presently we
// punt and hope that calling didLeavePlace() without ever having called willEnterPlace() // punt and hope that calling didLeavePlace() without ever having called willEnterPlace()
@@ -518,37 +551,6 @@ public class LocationDirector extends BasicDirector
} }
} }
/**
* Sets the failure handler which will recover from place object fetching failures. In the
* event that we are unable to fetch our place object after making a successful moveTo request,
* we attempt to rectify the failure by moving back to the last known working location. Because
* entites that cooperate with the location director may need to become involved in this
* failure recovery, we provide this interface whereby they can interject themseves into the
* failure recovery process and do their own failure recovery.
*/
public void setFailureHandler (FailureHandler handler)
{
if (_failureHandler != null) {
Log.warning("Requested to set failure handler, but we've already got one. The " +
"conflicting entities will likely need to perform more sophisticated " +
"coordination to deal with failures. [old=" + _failureHandler +
", new=" + handler + "].");
} else {
_failureHandler = handler;
}
}
protected void notifyFailure (final int placeId, final String reason)
{
_observers.apply(new ObserverOp<LocationObserver>() {
public boolean apply (LocationObserver obs) {
obs.locationChangeFailed(placeId, reason);
return true;
}
});
}
/** /**
* Called to create our place controller using the supplied place configuration. This lives in * Called to create our place controller using the supplied place configuration. This lives in
* a separate method so that derived instances can do funny class loader business if necessary * a separate method so that derived instances can do funny class loader business if necessary
@@ -569,6 +571,9 @@ public class LocationDirector extends BasicDirector
protected ObserverList<LocationObserver> _observers = new ObserverList<LocationObserver>( protected ObserverList<LocationObserver> _observers = new ObserverList<LocationObserver>(
ObserverList.SAFE_IN_ORDER_NOTIFY); ObserverList.SAFE_IN_ORDER_NOTIFY);
/** Used to subscribe to our place object. */
protected SafeSubscriber<PlaceObject> _subber;
/** The oid of the place we currently occupy. */ /** The oid of the place we currently occupy. */
protected int _placeId = -1; protected int _placeId = -1;