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;
import java.util.logging.Level;
import com.samskivert.util.ObserverList;
import com.samskivert.util.ObserverList.ObserverOp;
import com.samskivert.util.ResultListener;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.util.SafeSubscriber;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.Subscriber;
import com.threerings.crowd.Log;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.CrowdCodes;
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.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
* 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.
*/
public class LocationDirector extends BasicDirector
implements LocationCodes, Subscriber<PlaceObject>, LocationReceiver,
LocationService.MoveListener
implements LocationCodes, LocationReceiver, LocationService.MoveListener
{
/**
* 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
if (placeId < 0) {
Log.warning("Refusing moveTo(): invalid placeId " + placeId + ".");
log.warning("Refusing moveTo(): invalid placeId " + placeId + ".");
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
// this new one through in an attempt to recover from dropped moveTo requests
if (refuse) {
Log.warning("Refusing moveTo; We have a request outstanding " +
log.warning("Refusing moveTo; We have a request outstanding " +
"[ppid=" + _pendingPlaceId + ", npid=" + placeId + "].");
return false;
} else {
Log.warning("Overriding stale moveTo request [ppid=" + _pendingPlaceId +
log.warning("Overriding stale moveTo request [ppid=" + _pendingPlaceId +
", npid=" + placeId + "].");
}
}
@@ -155,7 +158,7 @@ public class LocationDirector extends BasicDirector
_pendingPlaceId = placeId;
// issue a moveTo request
Log.info("Issuing moveTo(" + placeId + ").");
log.info("Issuing moveTo(" + placeId + ").");
_lservice.moveTo(_ctx.getClient(), placeId, this);
return true;
}
@@ -244,8 +247,8 @@ public class LocationDirector extends BasicDirector
try {
_controller.mayLeavePlace(_plobj);
} catch (Exception e) {
Log.warning("Place controller choked in mayLeavePlace [plobj=" + _plobj + "].");
Log.logStackTrace(e);
log.log(Level.WARNING, "Place controller choked in mayLeavePlace " +
"[plobj=" + _plobj + "].", e);
}
}
}
@@ -279,15 +282,36 @@ public class LocationDirector extends BasicDirector
_placeId = placeId;
// start up a new place controller to manage the new place
_controller = createController(config);
if (_controller == null) {
Log.warning("Place config returned null controller [config=" + config + "].");
return;
}
_controller.init(_ctx, config);
try {
_controller = createController(config);
if (_controller == null) {
log.warning("Place config returned null controller [config=" + config + "].");
return;
}
_controller.init(_ctx, config);
// subscribe to our new place object to complete the move
_ctx.getDObjectManager().subscribeToObject(_placeId, this);
// subscribe to our new place object to complete the move
_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 ()
{
if (_plobj != null) {
// let the old controller know that things are going away
if (_controller != null) {
try {
_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;
// unsubscribe from our old place object
if (_subber != null) {
_subber.unsubscribe(_ctx.getDObjectManager());
_subber = null;
}
// 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;
// let our observers know what's up
notifyFailure(placeId, reason);
handleFailure(placeId, reason);
}
/**
@@ -367,7 +392,7 @@ public class LocationDirector extends BasicDirector
gotBodyObject(object);
}
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 + "].");
}
};
@@ -408,6 +433,28 @@ public class LocationDirector extends BasicDirector
_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)
{
// 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;
_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
notifyFailure(placeId, reason);
handleFailure(placeId, reason);
}
// 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
// spurious one as it would be in the case of lag causing rapid-fire repeat requests
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 + "].");
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
mayLeavePlace();
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
* request.
* 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 objectAvailable (PlaceObject object)
public void setFailureHandler (FailureHandler handler)
{
// yay, we have our new place object
_plobj = object;
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 + "].");
// 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.warning("Controller choked in willEnterPlace [place=" + _plobj + "].");
Log.logStackTrace(e);
}
} else {
_failureHandler = handler;
}
// let our observers know that all is well on the western front
_observers.apply(_didChangeOp);
}
/**
* 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)
protected void handleFailure (final int placeId, final String reason)
{
// 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
notifyFailure(placeId, "m.unable_to_fetch_place_object");
_observers.apply(new ObserverOp<LocationObserver>() {
public boolean apply (LocationObserver obs) {
obs.locationChangeFailed(placeId, reason);
return true;
}
});
// 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()
@@ -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
* 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>(
ObserverList.SAFE_IN_ORDER_NOTIFY);
/** Used to subscribe to our place object. */
protected SafeSubscriber<PlaceObject> _subber;
/** The oid of the place we currently occupy. */
protected int _placeId = -1;