Class OortObject<T>

java.lang.Object
org.eclipse.jetty.util.component.AbstractLifeCycle
org.cometd.oort.OortObject<T>
Type Parameters:
T - the type of value object stored in this oort object
All Implemented Interfaces:
java.lang.Iterable<OortObject.Info<T>>, java.util.EventListener, ConfigurableServerChannel.Initializer, Oort.CometListener, org.eclipse.jetty.util.component.Dumpable, org.eclipse.jetty.util.component.LifeCycle
Direct Known Subclasses:
OortContainer

public class OortObject<T>
extends org.eclipse.jetty.util.component.AbstractLifeCycle
implements ConfigurableServerChannel.Initializer, Oort.CometListener, java.lang.Iterable<OortObject.Info<T>>, org.eclipse.jetty.util.component.Dumpable

An OortObject represents a named composite entity that is distributed in an Oort cluster.

A typical example is an oort object that stores the number of users connected to an Oort node. The entity in this case is a long value representing the number of connected users. Such oort object may be named 'user_count', and there will be an oort object instance with this name in each node. Each oort object instance will have a different local value, along with all the values from the other nodes.

A particular OortObject has a unique name across the node and is made of N parts, where N is the number of active nodes. A part is represented by OortObject.Info instances. Each OortObject.Info instance represents the contribution of that node to the whole OortObject and stores the Oort URL of the node it represents along with the entity in that node.

     +------------+
     | user_count |
 +---+---+----+---+--------------------+
 | part1 | 13 | local - http://oort1/  |
 +-------+----+------------------------+
 | part2 | 19 | remote - http://oort2/ |
 +-------+----+------------------------+
 | part3 | 29 | remote - http://oort3/ |
 +-------+----+------------------------+
 

An OortObject must be created and then started:

 Oort oort1 = ...;
 OortObject userCount1 = new OortObject(oort1, "user_count", OortObjectFactories.forLong());
 userCount1.start();
 

Once started, it connects via Oort facilities to the other nodes and communicates with the oort object instances that have the same name that live in the other nodes. The communication is performed on a channel constructed from the oort object's name and returned by getChannelName().

Oort objects work best when the entity they hold is an immutable, value-type object: OortObject<Long> works better than OortObject<AtomicLong> because AtomicLong is mutable and its APIs (for example, AtomicLong.compareAndSet(long, long)) are not exposed by OortObject.

Objects stored by an oort object are created using a OortObject.Factory. This is necessary to recreate objects that may be serialized differently when transmitted via JSON, without forcing a global JSON serializer. A number of factories are available in OortObjectFactories, and applications can write their own.

Applications can change the entity value of the oort object and broadcast the change to other nodes via setAndShare(Object, Result). The other nodes will receive a message on the oort object's channel and set the new entity value in the part that corresponds to the node that changed the entity. The diagram below shows one oort object with name "user_count" in two nodes. On the left of the arrow (A), the situation before calling:

 userCount1.setAndShare(17, result -> { ... });
 

and on the right of the arrow (A) the situation afterwards, that shows how the value is first changed (1) locally on node_1, then a message (2) is broadcast on the cluster, reaches node_2, where it updates (3) the part corresponding to node_1 to the new value.

 +-------------+  +-------------+         +-----------------+       +-----------------+
 |   node_1    |  |   node_2    |         |     node_1      |       |     node_2      |
 +-------------+  +-------------+         +-----------------+  (2)  +-----------------+
 | user_count  |  | user_count  |   (A)   |   user_count    | ----> |   user_count    |
 +--------+----+  +--------+----+  ---->  +--------+--------+       +--------+--------+
 | local  | 13 |  | local  | 19 |         | local  | 17 (1) |       | local  | 19     |
 +--------+----+  +--------+----+         +--------+--------+       +--------+--------+
 | remote | 19 |  | remote | 13 |         | remote | 19     |       | remote | 17 (3) |
 +--------+----+  +-------+----+          +--------+--------+       +-------+---------+
 

When an entity is updated, either locally or remotely, an event is fired to registered OortObject.Listeners.

Oort objects can only update the entity they own; in the example above, node_1 can only update the "local" value 13 to 17, but cannot modify the "remote" value 19, which is owned by node_2. Only update messages from node_1 can update the "remote" value on node_2. Every node has a part that belongs to a particular node, and only that particular node can update it.

Values of oort objects may be merged using a OortObject.Merger via merge(Merger). A number of mergers are available in OortObjectMergers, and applications can write their own. For example:

 long totalUsersOnAllNodes = userCount1.merge(OortObjectMergers.longSum()); // yields 17+19=36
 

Oort objects implement a strategy where value objects are replicated in each node, trading increased memory usage for reduced latency accessing the data. An alternative strategy that trades reduced memory usage for increased latency is implemented by OortService.

  • Field Details

    • OORT_OBJECTS_CHANNEL

      public static final java.lang.String OORT_OBJECTS_CHANNEL
      See Also:
      Constant Field Values
  • Constructor Details

  • Method Details

    • doStart

      protected void doStart()
      Overrides:
      doStart in class org.eclipse.jetty.util.component.AbstractLifeCycle
    • doStop

      protected void doStop()
      Overrides:
      doStop in class org.eclipse.jetty.util.component.AbstractLifeCycle
    • configureChannel

      public void configureChannel​(ConfigurableServerChannel channel)
      Configures the channel used by this oort object. By default does nothing, but subclasses may override this method to make the channel lazy, for example.
      Specified by:
      configureChannel in interface ConfigurableServerChannel.Initializer
      Parameters:
      channel - the channel to configure
      See Also:
      getChannelName()
    • getOort

      public Oort getOort()
      Returns:
      the Oort instance associated with this oort object
    • getName

      public java.lang.String getName()
      Returns:
      the name of this oort object, that must be unique across the node.
    • getFactory

      public OortObject.Factory<T> getFactory()
      Returns:
      the factory to create objects contained in this oort object
    • getLocalSession

      public LocalSession getLocalSession()
      Returns:
      the local session that sends messages to other nodes
    • getChannelName

      public java.lang.String getChannelName()
      Returns the channel name used by this oort object for communication with other oort objects in other nodes. The channel is of the form "/oort/objects/<name>" where <name> is this oort object's name.
      Returns:
      the channel name used by this oort object
      See Also:
      configureChannel(ConfigurableServerChannel)
    • setAndShare

      public void setAndShare​(T newObject, OortObject.Result<T> callback)

      Sets the given new object on this oort object, and then broadcast the new object to all nodes in the cluster.

      Setting an object triggers notification of OortObject.Listeners, both on this node and on remote nodes.

      The object is guaranteed to be set not when this method returns, but when the OortObject.Result parameter is notified.

      Parameters:
      newObject - the new object to set
      callback - the callback invoked with the old object, or null if there is no interest in the old object
    • serialize

      protected java.lang.Object serialize​(T object)
    • deserialize

      protected java.lang.Object deserialize​(java.lang.Object object)
    • newInfo

      protected OortObject.Info<T> newInfo​(T local)
    • cometJoined

      public void cometJoined​(Oort.CometListener.Event event)
      Description copied from interface: Oort.CometListener
      Callback method invoked when a new comet joins the cloud
      Specified by:
      cometJoined in interface Oort.CometListener
      Parameters:
      event - the comet event
    • cometLeft

      public void cometLeft​(Oort.CometListener.Event event)
      Description copied from interface: Oort.CometListener
      Callback method invoked when a comet leaves the cloud
      Specified by:
      cometLeft in interface Oort.CometListener
      Parameters:
      event - the comet event
    • iterator

      public java.util.Iterator<OortObject.Info<T>> iterator()
      Specified by:
      iterator in interface java.lang.Iterable<T>
      Returns:
      an iterator over the OortObject.Info known to this oort object
    • getInfo

      public OortObject.Info<T> getInfo​(java.lang.String oortURL)
      Parameters:
      oortURL - the oort URL used to search the corresponding OortObject.Info
      Returns:
      the OortObject.Info with the given oort URL, or null if no such OortObject.Info exists
    • getInfoByObject

      public OortObject.Info<T> getInfoByObject​(T object)
      Parameters:
      object - the object used to search the corresponding OortObject.Info
      Returns:
      the first OortObject.Info whose object equals (via a possibly overridden Object.equals(Object)) the given object
    • merge

      public <R> R merge​(OortObject.Merger<T,​R> strategy)
      Merges the objects of all the OortObject.Infos known to this oort object using the given strategy.
      Type Parameters:
      R - the merge result type
      Parameters:
      strategy - the strategy to merge the objects
      Returns:
      the merged result
    • addListener

      public void addListener​(OortObject.Listener<T> listener)
      Parameters:
      listener - the listener to add
      See Also:
      removeListener(Listener)
    • removeListener

      public void removeListener​(OortObject.Listener<T> listener)
      Parameters:
      listener - the listener to remove
      See Also:
      addListener(Listener)
    • removeListeners

      public void removeListeners()
      Removes all listeners.
      See Also:
      addListener(Listener), removeListener(Listener)
    • notifyUpdated

      protected void notifyUpdated​(OortObject.Info<T> oldInfo, OortObject.Info<T> newInfo)
    • notifyRemoved

      protected void notifyRemoved​(OortObject.Info<T> info)
    • onObject

      protected void onObject​(java.util.Map<java.lang.String,​java.lang.Object> data)
    • pushInfo

      protected void pushInfo​(java.lang.String oortURL, java.util.Map<java.lang.String,​java.lang.Object> fields)
    • pullInfo

      protected void pullInfo​(java.lang.String oortURL)
    • getInfos

      protected java.util.Collection<OortObject.Info<T>> getInfos()
    • dump

      public void dump​(java.lang.Appendable out, java.lang.String indent) throws java.io.IOException
      Specified by:
      dump in interface org.eclipse.jetty.util.component.Dumpable
      Throws:
      java.io.IOException
    • toString

      public java.lang.String toString()
      Overrides:
      toString in class org.eclipse.jetty.util.component.AbstractLifeCycle