Files
narya/src/java/com/threerings/presents/util/SafeSubscriber.java
T
Michael Bayne 0a893e1de1 ZOMG, Narya now perfectly passes our coding standards as enforced by
CheckStyle. The checks are slightly looser than I'd like but way better than
nothing and we get a bunch of other useful warnings like shadowed names and
other handy bits.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5253 542714f4-19e9-0310-aa3c-eee0fc999fb1
2008-07-22 14:36:54 +00:00

233 lines
8.0 KiB
Java

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/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.util;
import com.samskivert.util.StringUtil;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.Subscriber;
import static com.threerings.presents.Log.log;
/**
* A class that safely handles the asynchronous subscription to a
* distributed object when it is not know if the subscription will
* complete before the subscriber decides they no longer wish to be
* subscribed.
*
* @param <T> the type of object to which we are subscribing.
*/
public class SafeSubscriber<T extends DObject>
implements Subscriber<T>
{
/**
* Creates a safe subscriber for the specified distributed object
* which will interact with the specified subscriber.
*/
public SafeSubscriber (int oid, Subscriber<T> subscriber)
{
// make sure they're not fucking us around
if (oid <= 0) {
throw new IllegalArgumentException(
"Invalid oid provided to safesub [oid=" + oid + "]");
}
if (subscriber == null) {
throw new IllegalArgumentException(
"Null subscriber provided to safesub [oid=" + oid + "]");
}
_oid = oid;
_subscriber = subscriber;
}
/**
* Returns true if we are currently subscribed to our object (or in
* the process of obtaining a subscription).
*/
public boolean isActive ()
{
return _active;
}
/**
* Initiates the subscription process.
*/
public void subscribe (DObjectManager omgr)
{
if (_active) {
log.warning("Active safesub asked to resubscribe " + this + ".");
Thread.dumpStack();
return;
}
// note that we are now again in the "wishing to be subscribed" state
_active = true;
// make sure we dont have an object reference (which should be
// logically impossible)
if (_object != null) {
log.warning("Incroyable! A safesub has an object and was " +
"non-active!? " + this + ".");
Thread.dumpStack();
// make do in the face of insanity
_subscriber.objectAvailable(_object);
return;
}
if (_pending) {
// we were previously asked to subscribe, then they asked to
// unsubscribe and now they've asked to subscribe again, all
// before the original subscription even completed; we need do
// nothing here except as the original subscription request
// will eventually come through and all will be well
return;
}
// we're not pending and we just became active, that means we need
// to request to subscribe to our object
_pending = true;
omgr.subscribeToObject(_oid, this);
}
/**
* Terminates the object subscription. If the initial subscription has
* not yet completed, the desire to terminate will be noted and the
* subscription will be terminated as soon as it completes.
*/
public void unsubscribe (DObjectManager omgr)
{
if (!_active) {
// we may be non-active and have no object which could mean
// that subscription failed; in which case we don't want to
// complain about anything, just quietly ignore the
// unsubscribe request
if (_object == null && !_pending) {
return;
}
log.warning("Inactive safesub asked to unsubscribe " + this + ".");
Thread.dumpStack();
}
// note that we no longer desire to be subscribed
_active = false;
if (_pending) {
// make sure we don't have an object reference
if (_object != null) {
log.warning("Incroyable! A safesub has an object and is " +
"pending!? " + this + ".");
Thread.dumpStack();
} else {
// nothing to do but wait for the subscription to complete
// at which point we'll pitch the object post-haste
return;
}
}
// make sure we have our object
if (_object == null) {
log.warning("Zut alors! A safesub _was_ active and not " +
"pending yet has no object!? " + this + ".");
Thread.dumpStack();
// nothing to do since we're apparently already unsubscribed
return;
}
// finally effect our unsubscription
_object = null;
omgr.unsubscribeFromObject(_oid, this);
}
// documentation inherited from interface
public void objectAvailable (T object)
{
// make sure life is not too cruel
if (_object != null) {
log.warning("Madre de dios! Our object came available but " +
"we've already got one!? " + this);
// go ahead and pitch the old one, God knows what's going on
_object = null;
}
if (!_pending) {
log.warning("J.C. on a pogo stick! Our object came available " +
"but we're not pending!? " + this);
// go with our badselves, it's the only way
}
// we're no longer pending
_pending = false;
// if we are no longer active, we don't want this damned thing
if (!_active) {
DObjectManager omgr = object.getManager();
// if the object's manager is null, that means the object is
// already destroyed and we need not trouble ourselves with
// unsubscription as it has already been pitched to the dogs
if (omgr != null) {
omgr.unsubscribeFromObject(_oid, this);
}
return;
}
// otherwise the air is fresh and clean and we can do our job
_object = object;
_subscriber.objectAvailable(object);
}
// documentation inherited from interface
public void requestFailed (int oid, ObjectAccessException cause)
{
// do the right thing with our pending state
if (!_pending) {
log.warning("Criminy creole! Our subscribe failed but we're " +
"not pending!? " + this);
// go with our badselves, it's the only way
}
_pending = false;
// if we're active, let our subscriber know that the shit hit the fan
if (_active) {
// deactivate ourselves as we never got our object (and thus
// the real subscriber need not call unsubscribe())
_active = false;
_subscriber.requestFailed(oid, cause);
}
}
@Override
public String toString ()
{
return "[oid=" + _oid +
", sub=" + StringUtil.shortClassName(_subscriber) +
", active=" + _active + ", pending=" + _pending +
", dobj=" + StringUtil.shortClassName(_object) + "]";
}
protected int _oid;
protected Subscriber<T> _subscriber;
protected T _object;
protected boolean _active;
protected boolean _pending;
}