com.threerings.presents.dobj
Class DObject

java.lang.Object
  extended by com.threerings.presents.dobj.DObject
All Implemented Interfaces:
Streamable
Direct Known Subclasses:
AgentObject, AuthResponseData, ClientObject, ConfigObject, InvocationObject, NodeObject, PlaceObject, TimeBaseObject

public class DObject
extends Object
implements Streamable

The distributed object forms the foundation of the Presents system. All information shared among users of the system is done via distributed objects. A distributed object has a set of listeners. These listeners have access to the object or a proxy of the object and therefore have access to the data stored in the object's members at all times.

Additionally, an object has a set of subscribers. Subscribers manage the lifespan of the object; while a subscriber is subscribed, the listeners registered with an object will be notified of events. When the subscriber unsubscribes, the object becomes non-live and the listeners are no longer notified. Note: on the server, object subscription is merely a formality as all objects remain live all the time, so do not depend on event notifications ceasing when a subscriber has relinquished its subscription. Always unregister all listeners when they no longer need to hear from an object.

When there is any change to the the object's fields data, an event is generated which is dispatched to all listeners of the object, notifying them of that change and effecting that change to the copy of the object maintained at each client. In this way, both a repository of shared information and a mechanism for asynchronous notification are made available as a fundamental application building blocks.

To define what information is shared, an application creates a distributed object declaration which is much like a class declaration except that it is transformed into a proper derived class of DObject by a script. A declaration looks something like this:

 public dclass RoomObject
 {
     public String description;
     public int[] occupants;
 }
 
which is converted into an actual Java class that looks like this:
 public class RoomObject extends DObject
 {
     public String getDescription ()
     {
         // ...
     }

     public void setDescription (String description)
     {
         // ...
     }

     public int[] getOccupants ()
     {
         // ...
     }

     public void setOccupants (int[] occupants)
     {
         // ...
     }

     public void setOccupantsAt (int index, int value)
     {
         // ...
     }
 }
 
These method calls on the actual distributed object will result in the proper attribute change events being generated and dispatched.

Note that distributed object fields can be any of the following set of primitive types:

 boolean, byte, short, int, long, float, double
 Boolean, Byte, Short, Integer, Long, Float, Double, String
 boolean[], byte[], short[], int[], long[], float[], double[], String[]
 
Fields of type Streamable can also be used.


Field Summary
protected  AccessController _controller
          The entity that tells us if an event or subscription request should be allowed.
protected  boolean _deathWish
          Indicates whether we want to be destroyed when our last subscriber is removed.
protected  Field[] _fields
          An array of our fields, sorted for efficient lookup.
protected static Map<Class<?>,Field[]> _ftable
          Maintains a mapping of sorted field arrays for each distributed object class.
protected  Object[] _listeners
          Our event listeners list.
protected  Object[] _locattrs
          Any local attributes configured on this object or null.
protected  Object[] _locks
          A list of outstanding locks.
protected  int _oid
          Our object id.
protected  DObjectManager _omgr
          A reference to our object manager.
protected  int _scount
          Our subscriber count.
protected  Object[] _subs
          Our subscribers list.
protected  boolean _tcancelled
          Whether or not our nested transaction has been cancelled.
protected  int _tcount
          The nesting depth of our current transaction.
protected  CompoundEvent _tevent
          The compound event associated with our transaction, if we're currently in a transaction.
protected static Comparator<Field> FIELD_COMP
          Used to sort and search _fields.
protected static Object[] NO_ATTRS
          Simplifies code for objects that have no local attributes.
 
Constructor Summary
DObject()
           
 
Method Summary
 boolean acquireLock(String name)
          At times, an entity on the server may need to ensure that events it has queued up have made it through the event queue and are applied to their respective objects before a service may safely be undertaken again.
 void addListener(ChangeListener listener)
          Adds an event listener to this object.
 void addListener(ChangeListener listener, boolean weak)
          Adds an event listener to this object.
 void addSubscriber(Subscriber<?> sub)
          Don't call this function!
<T extends DSet.Entry>
void
addToSet(String setName, T entry)
          Request to have the specified item added to the specified DSet.
 void cancelTransaction()
          Cancels the transaction in which this distributed object is involved.
 void changeAttribute(String name, Object value)
          Requests that the specified attribute be changed to the specified value.
 boolean checkPermissions(DEvent event)
          Checks to ensure that this event which is about to be processed, has the appropriate permissions.
 boolean checkPermissions(Subscriber<?> sub)
          Checks to ensure that the specified subscriber has access to this object.
protected  void clearLock(String name)
          Don't call this function!
protected  void clearTransaction()
          Removes this object from participation in any transaction in which it might be taking part.
 void commitTransaction()
          Commits the transaction in which this distributed object is involved.
 void destroy()
          Requests that this distributed object be destroyed.
 AccessController getAccessController()
          Returns a reference to the access controller in use by this object or null if none has been configured.
 Object getAttribute(String name)
          Looks up the named attribute and returns a reference to it.
protected  Field getField(String name)
          Returns the Field with the specified name or null if there is none such.
protected  int getListenerIndex(ChangeListener listener)
          Returns the index of the identified listener, or -1 if not found.
<T> T
getLocal(Class<T> key)
          Retrieves a local attribute for the supplied key.
 List<Object> getLocals()
          Returns an array containing our local attributes.
 DObjectManager getManager()
          Returns the dobject manager under the auspices of which this object operates.
 int getOid()
          Returns the object id of this object.
<T extends DSet.Entry>
DSet<T>
getSet(String setName)
          Get the DSet with the specified name.
 boolean inTransaction()
          Returns true if this object is in the middle of a transaction or false if it is not.
 boolean isActive()
          Returns true if this object is active and registered with the distributed object system.
 void notifyListeners(DEvent event)
          Called by the distributed object manager after it has applied an event to this object.
 void notifyProxies(DEvent event)
          Called by the distributed object manager after it has applied an event to this object.
 void postEvent(DEvent event)
          Posts the specified event either to our dobject manager or to the compound event for which we are currently transacting.
 void postMessage(String name, Object... args)
          Posts a message event on this distributed object.
 void postMessage(Transport transport, String name, Object... args)
          Posts a message event on this distributed object.
 void releaseLock(String name)
          Queues up an event that when processed will release the lock of the specified name.
 void removeFromSet(String setName, Comparable<?> key)
          Request to have the specified key removed from the specified DSet.
 void removeListener(ChangeListener listener)
          Removes an event listener from this object.
 void removeSubscriber(Subscriber<?> sub)
          Don't call this function!
protected  void requestAttributeChange(String name, Object value, Object oldValue)
          Called by derived instances when an attribute setter method was called.
protected  void requestAttributeChange(String name, Object value, Object oldValue, Transport transport)
          Called by derived instances when an attribute setter method was called.
protected  void requestElementUpdate(String name, int index, Object value, Object oldValue)
          Called by derived instances when an element updater method was called.
protected  void requestElementUpdate(String name, int index, Object value, Object oldValue, Transport transport)
          Called by derived instances when an element updater method was called.
protected
<T extends DSet.Entry>
void
requestEntryAdd(String name, DSet<T> set, T entry)
          Calls by derived instances when a set adder method was called.
protected
<T extends DSet.Entry>
void
requestEntryRemove(String name, DSet<T> set, Comparable<?> key)
          Calls by derived instances when a set remover method was called.
protected
<T extends DSet.Entry>
void
requestEntryUpdate(String name, DSet<T> set, T entry)
          Calls by derived instances when a set updater method was called.
protected
<T extends DSet.Entry>
void
requestEntryUpdate(String name, DSet<T> set, T entry, Transport transport)
          Calls by derived instances when a set updater method was called.
protected  void requestOidAdd(String name, int oid)
          Calls by derived instances when an oid adder method was called.
protected  void requestOidRemove(String name, int oid)
          Calls by derived instances when an oid remover method was called.
 void setAccessController(AccessController controller)
          Provides this object with an entity that can be used to validate subscription requests and events before they are processed.
 void setAttribute(String name, Object value)
          Sets the named attribute to the specified value.
 void setDestroyOnLastSubscriberRemoved(boolean deathWish)
          Instructs this object to request to have a fork stuck in it when its last subscriber is removed.
<T> void
setLocal(Class<T> key, T attr)
          Configures a local attribute on this object.
 void setManager(DObjectManager omgr)
          Don't call this function!
 void setOid(int oid)
          Don't call this function.
 void startTransaction()
          Begins a transaction on this distributed object.
 String toString()
           
protected  void toString(StringBuilder buf)
          Generates a string representation of this object.
 void updateSet(String setName, DSet.Entry entry)
          Request to have the specified item updated in the specified DSet.
 String which()
          Generates a concise string representation of this object.
protected  void which(StringBuilder buf)
          Used to briefly describe this distributed object.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

_oid

protected int _oid
Our object id.


_fields

protected transient Field[] _fields
An array of our fields, sorted for efficient lookup.


_omgr

protected transient DObjectManager _omgr
A reference to our object manager.


_controller

protected transient AccessController _controller
The entity that tells us if an event or subscription request should be allowed.


_locks

protected transient Object[] _locks
A list of outstanding locks.


_subs

protected transient Object[] _subs
Our subscribers list.


_listeners

protected transient Object[] _listeners
Our event listeners list.


_scount

protected transient int _scount
Our subscriber count.


_tevent

protected transient CompoundEvent _tevent
The compound event associated with our transaction, if we're currently in a transaction.


_tcount

protected transient int _tcount
The nesting depth of our current transaction.


_tcancelled

protected transient boolean _tcancelled
Whether or not our nested transaction has been cancelled.


_deathWish

protected transient boolean _deathWish
Indicates whether we want to be destroyed when our last subscriber is removed.


_locattrs

protected transient Object[] _locattrs
Any local attributes configured on this object or null.


_ftable

protected static Map<Class<?>,Field[]> _ftable
Maintains a mapping of sorted field arrays for each distributed object class.


FIELD_COMP

protected static final Comparator<Field> FIELD_COMP
Used to sort and search _fields.


NO_ATTRS

protected static final Object[] NO_ATTRS
Simplifies code for objects that have no local attributes.

Constructor Detail

DObject

public DObject()
Method Detail

getOid

public int getOid()
Returns the object id of this object. All objects in the system have a unique object id.


getManager

public DObjectManager getManager()
Returns the dobject manager under the auspices of which this object operates. This could be null if the object is not active.


addSubscriber

public void addSubscriber(Subscriber<?> sub)
Don't call this function! Go through the distributed object manager instead to ensure that everything is done on the proper thread. This function can only safely be called directly when you know you are operating on the omgr thread (you are in the middle of a call to objectAvailable or to a listener callback).

See Also:
DObjectManager.subscribeToObject(int, com.threerings.presents.dobj.Subscriber)

removeSubscriber

public void removeSubscriber(Subscriber<?> sub)
Don't call this function! Go through the distributed object manager instead to ensure that everything is done on the proper thread. This function can only safely be called directly when you know you are operating on the omgr thread (you are in the middle of a call to objectAvailable or to a listener callback).

See Also:
DObjectManager.unsubscribeFromObject(int, com.threerings.presents.dobj.Subscriber)

setDestroyOnLastSubscriberRemoved

public void setDestroyOnLastSubscriberRemoved(boolean deathWish)
Instructs this object to request to have a fork stuck in it when its last subscriber is removed.


addListener

public void addListener(ChangeListener listener)
Adds an event listener to this object. The listener will be notified when any events are dispatched on this object that match their particular listener interface.

Note that the entity adding itself as a listener should have obtained the object reference by subscribing to it or should be acting on behalf of some other entity that subscribed to the object, and that it must be sure to remove itself from the listener list (via removeListener(com.threerings.presents.dobj.ChangeListener)) when it is done because unsubscribing from the object (done by whatever entity subscribed in the first place) is not guaranteed to result in the listeners added through that subscription being automatically removed (in most cases, they definitely will not be removed).

Parameters:
listener - the listener to be added.
See Also:
EventListener, AttributeChangeListener, SetListener, OidListListener

addListener

public void addListener(ChangeListener listener,
                        boolean weak)
Adds an event listener to this object. The listener will be notified when any events are dispatched on this object that match their particular listener interface.

Note that the entity adding itself as a listener should have obtained the object reference by subscribing to it or should be acting on behalf of some other entity that subscribed to the object, and that it must be sure to remove itself from the listener list (via removeListener(com.threerings.presents.dobj.ChangeListener)) when it is done because unsubscribing from the object (done by whatever entity subscribed in the first place) is not guaranteed to result in the listeners added through that subscription being automatically removed (in most cases, they definitely will not be removed).

Parameters:
listener - the listener to be added.
weak - if true, retain only a weak reference to the listener (do not prevent the listener from being garbage-collected).
See Also:
EventListener, AttributeChangeListener, SetListener, OidListListener

removeListener

public void removeListener(ChangeListener listener)
Removes an event listener from this object. The listener will no longer be notified when events are dispatched on this object.

Parameters:
listener - the listener to be removed.

setAccessController

public void setAccessController(AccessController controller)
Provides this object with an entity that can be used to validate subscription requests and events before they are processed. The access controller is handy for ensuring that clients are behaving as expected and for preventing impermissible modifications or event dispatches on a distributed object.


getAccessController

public AccessController getAccessController()
Returns a reference to the access controller in use by this object or null if none has been configured.


getSet

public final <T extends DSet.Entry> DSet<T> getSet(String setName)
Get the DSet with the specified name.


addToSet

public <T extends DSet.Entry> void addToSet(String setName,
                                            T entry)
Request to have the specified item added to the specified DSet.


updateSet

public void updateSet(String setName,
                      DSet.Entry entry)
Request to have the specified item updated in the specified DSet.


removeFromSet

public void removeFromSet(String setName,
                          Comparable<?> key)
Request to have the specified key removed from the specified DSet.


acquireLock

public boolean acquireLock(String name)
At times, an entity on the server may need to ensure that events it has queued up have made it through the event queue and are applied to their respective objects before a service may safely be undertaken again. To make this possible, it can acquire a lock on a distributed object, generate the events in question and then release the lock (via a call to releaseLock) which will queue up a final event, the processing of which will release the lock. Thus the lock will not be released until all of the previously generated events have been processed. If the service is invoked again before that lock is released, the associated call to acquireLock will fail and the code can respond accordingly. An object may have any number of outstanding locks as long as they each have a unique name.

Parameters:
name - the name of the lock to acquire.
Returns:
true if the lock was acquired, false if the lock was not acquired because it has not yet been released from a previous acquisition.
See Also:
releaseLock(java.lang.String)

releaseLock

public void releaseLock(String name)
Queues up an event that when processed will release the lock of the specified name.

See Also:
acquireLock(java.lang.String)

clearLock

protected void clearLock(String name)
Don't call this function! It is called by a remove lock event when that event is processed and shouldn't be called at any other time. If you mean to release a lock that was acquired with acquireLock you should be using releaseLock.

See Also:
acquireLock(java.lang.String), releaseLock(java.lang.String)

destroy

public void destroy()
Requests that this distributed object be destroyed. It does so by queueing up an object destroyed event which the server will validate and process.


checkPermissions

public boolean checkPermissions(Subscriber<?> sub)
Checks to ensure that the specified subscriber has access to this object. This will be called before satisfying a subscription request. If an AccessController has been specified for this object, it will be used to determine whether or not to allow the subscription request. If no controller is set, the subscription will be allowed.

Parameters:
sub - the subscriber that will subscribe to this object.
Returns:
true if the subscriber has access to the object, false if they do not.

checkPermissions

public boolean checkPermissions(DEvent event)
Checks to ensure that this event which is about to be processed, has the appropriate permissions. If an AccessController has been specified for this object, it will be used to determine whether or not to allow the even dispatch. If no controller is set, all events are allowed.

Parameters:
event - the event that will be dispatched, object permitting.
Returns:
true if the event is valid and should be dispatched, false if the event fails the permissions check and should be aborted.

notifyListeners

public void notifyListeners(DEvent event)
Called by the distributed object manager after it has applied an event to this object. This dispatches an event notification to all of the listeners registered with this object.

Parameters:
event - the event that was just applied.

notifyProxies

public void notifyProxies(DEvent event)
Called by the distributed object manager after it has applied an event to this object. This dispatches an event notification to all of the proxy listeners registered with this object.

Parameters:
event - the event that was just applied.

changeAttribute

public void changeAttribute(String name,
                            Object value)
                     throws ObjectAccessException
Requests that the specified attribute be changed to the specified value. Normally the generated setter methods should be used but in rare cases a caller may wish to update distributed fields in a generic manner.

Throws:
ObjectAccessException

setAttribute

public void setAttribute(String name,
                         Object value)
                  throws ObjectAccessException
Sets the named attribute to the specified value. This is only used by the internals of the event dispatch mechanism and should not be called directly by users. Use the generated attribute setter methods instead.

Throws:
ObjectAccessException

getAttribute

public Object getAttribute(String name)
                    throws ObjectAccessException
Looks up the named attribute and returns a reference to it. This should only be used by the internals of the event dispatch mechanism and should not be called directly by users. Use the generated attribute getter methods instead.

Throws:
ObjectAccessException

postMessage

public void postMessage(String name,
                        Object... args)
Posts a message event on this distributed object.


postMessage

public void postMessage(Transport transport,
                        String name,
                        Object... args)
Posts a message event on this distributed object.

Parameters:
transport - a hint as to the type of transport desired for the message.

postEvent

public void postEvent(DEvent event)
Posts the specified event either to our dobject manager or to the compound event for which we are currently transacting.


isActive

public final boolean isActive()
Returns true if this object is active and registered with the distributed object system. If an object is created via DObjectManager.createObject it will be active until such time as it is destroyed.


setManager

public void setManager(DObjectManager omgr)
Don't call this function! It initializes this distributed object with the supplied distributed object manager. This is called by the distributed object manager when an object is created and registered with the system.

See Also:
RootDObjectManager.registerObject(DObject)

setOid

public void setOid(int oid)
Don't call this function. It is called by the distributed object manager when an object is created and registered with the system.

See Also:
RootDObjectManager.registerObject(DObject)

setLocal

public <T> void setLocal(Class<T> key,
                         T attr)
Configures a local attribute on this object. Local attributes are not sent over the network and are thus only available on the server or client that set the attribute. Local attributes are keyed by the class of the value being set as an attribute (the expectation is that local attributes will be encapsulated into helper classes).

Also note that it is illegal to replace the value of a local attribute. Attempting to set a local attribute that already contains a value will fail. This is intended to catch programmer error as early as possible. You may clear a local attribute by setting it to null and then it can be set to a new value.

Lastly, note that key polymorphism is implemented to allow a lower level framework to define a local attribute and users of that framework to extend the attribute class and have it returned whether the derived or base class is used to look up the attribute. For example:

 class BaseLocalAttr {
     public int foo;
 }
 class DerivedLocalAttr extends BaseLocalAttr {
     public int bar;
 }

 // simple usage
 DObject o1 = new DObject();
 BaseLocalAttr base = new BaseLocalAttr();
 o1.setLocal(BaseLocalAttr.class, base);
 assertSame(o1.getLocal(BaseLocalAttr.class), base); // true

 // polymorphic usage
 DObject o2 = new DObject();
 DerivedLocalAttr derived = new DerivedLocalAttr();
 o2.setLocal(DerivedLocalAttr.class, derived);
 BaseLocalAttr upcasted = derived;
 assertSame(o2.getLocal(DerivedLocalAttr.class), derived); // true
 assertSame(o2.getLocal(BaseLocalAttr.class), upcasted); // true

 // cannot overwrite already set attribute
 DObject o3 = new DObject();
 o3.setLocal(DerivedLocalAttr.class, derived);
 o3.setLocal(DerivedLocalAttr.class, new DerivedLocalAttr()); // will fail
 o3.setLocal(BaseLocalAttr.class, new BaseLocalAttr()); // will fail
 

Throws:
IllegalStateException - thrown if an attempt is made to set a local attribute that already contains a non-null value with any non-null value.

getLocal

public <T> T getLocal(Class<T> key)
Retrieves a local attribute for the supplied key. See setLocal(java.lang.Class, T) for information on key polymorphism. Returns null if no attribute is found that matches the supplied key.


getLocals

public List<Object> getLocals()
Returns an array containing our local attributes.


which

public String which()
Generates a concise string representation of this object.


toString

public String toString()
Overrides:
toString in class Object

which

protected void which(StringBuilder buf)
Used to briefly describe this distributed object.


toString

protected void toString(StringBuilder buf)
Generates a string representation of this object.


startTransaction

public void startTransaction()
Begins a transaction on this distributed object. In some situations, it is desirable to cause multiple changes to distributed object fields in one unified operation. Starting a transaction causes all subsequent field modifications to be stored in a single compound event which can then be committed, dispatching and applying all included events in a single group. Additionally, the events are dispatched over the network in a single unit which can significantly enhance network efficiency.

When the transaction is complete, the caller must call commitTransaction() or CompoundEvent.commit() to commit the transaction and release the object back to its normal non-transacting state. If the caller decides not to commit their transaction, they must call cancelTransaction() or CompoundEvent.cancel() to cancel the transaction. Failure to do so will cause the pooch to be totally screwed.

Note: like all other distributed object operations, transactions are not thread safe. It is expected that a single thread will handle all distributed object operations and that thread will begin and complete a transaction before giving up control to unknown code which might try to operate on the transacting distributed object.

Note also: if the object is already engaged in a transaction, a transaction participant count will be incremented to note that an additional call to commitTransaction() is required before the transaction should actually be committed. Thus every call to startTransaction() must be accompanied by a call to either commitTransaction() or cancelTransaction(). Additionally, if any transaction participant cancels the transaction, the entire transaction is cancelled for all participants, regardless of whether the other participants attempted to commit the transaction.


commitTransaction

public void commitTransaction()
Commits the transaction in which this distributed object is involved.

See Also:
CompoundEvent.commit()

inTransaction

public boolean inTransaction()
Returns true if this object is in the middle of a transaction or false if it is not.


cancelTransaction

public void cancelTransaction()
Cancels the transaction in which this distributed object is involved.

See Also:
CompoundEvent.cancel()

clearTransaction

protected void clearTransaction()
Removes this object from participation in any transaction in which it might be taking part.


requestAttributeChange

protected void requestAttributeChange(String name,
                                      Object value,
                                      Object oldValue)
Called by derived instances when an attribute setter method was called.


requestAttributeChange

protected void requestAttributeChange(String name,
                                      Object value,
                                      Object oldValue,
                                      Transport transport)
Called by derived instances when an attribute setter method was called.


requestElementUpdate

protected void requestElementUpdate(String name,
                                    int index,
                                    Object value,
                                    Object oldValue)
Called by derived instances when an element updater method was called.


requestElementUpdate

protected void requestElementUpdate(String name,
                                    int index,
                                    Object value,
                                    Object oldValue,
                                    Transport transport)
Called by derived instances when an element updater method was called.


requestOidAdd

protected void requestOidAdd(String name,
                             int oid)
Calls by derived instances when an oid adder method was called.


requestOidRemove

protected void requestOidRemove(String name,
                                int oid)
Calls by derived instances when an oid remover method was called.


requestEntryAdd

protected <T extends DSet.Entry> void requestEntryAdd(String name,
                                                      DSet<T> set,
                                                      T entry)
Calls by derived instances when a set adder method was called.


requestEntryRemove

protected <T extends DSet.Entry> void requestEntryRemove(String name,
                                                         DSet<T> set,
                                                         Comparable<?> key)
Calls by derived instances when a set remover method was called.


requestEntryUpdate

protected <T extends DSet.Entry> void requestEntryUpdate(String name,
                                                         DSet<T> set,
                                                         T entry)
Calls by derived instances when a set updater method was called.


requestEntryUpdate

protected <T extends DSet.Entry> void requestEntryUpdate(String name,
                                                         DSet<T> set,
                                                         T entry,
                                                         Transport transport)
Calls by derived instances when a set updater method was called.


getField

protected final Field getField(String name)
Returns the Field with the specified name or null if there is none such.


getListenerIndex

protected int getListenerIndex(ChangeListener listener)
Returns the index of the identified listener, or -1 if not found.