The SHSorter Inheritance API (derives from SHComponent -> SHDistributableObject -> BArchivable)

The SHSorter class defines an interface that tells a node on SockHop's virtual tree where any given BMessage should be forwarded to.  While most SockHop programs won't need to implement subclasses of SHSorter (because every node comes with a handy default SHSorter object already installed), more adventurous types who wish to write their own BMessage routing algorithms can subclass SHSorter to do so.

Here are the methods that your SHSorter subclass is required to implement:



SHSorter(BMessage * archive)

You must create a constructor for your class that can create an object from the data contained in the passed-in BMessage.  This constructor essentially does the opposite of your Archive() method;  instead of converting an object to a BMessage, it converts a BMessage back to an object.  Make sure your constructor calls SHSorter(archive) in its initialization list!
 



static BArchivable * Instantiate(BMessage * archive)

This static method is also used to turn BMessages back into real objects, and should be implemented like this:

BArchivable *
MySHSorterSubclass::
Instantiate(BMessage * archive)
{
   // replace the second argument to validate_instantiation with your class's name!
   if (!validate_instantiation(archive, "MySHSorterSubclass")) return NULL;
   return new MySHSorterSubclass(archive);
}
 



virtual status_t GetAddOnSpec(SHFileSpec & spec) const

This method (from the SHDistributableObject API) should be overridden to return an SHFileSpec that indicates where the add-on executable file for this class can be found:

status_t
MySHSorterSubclass::GetAddOnSpec(SHFileSpec & spec) const
{
   // Allow my parent class to say what files he needs
   status_t ret = SHSorter::GetAddOnSpec(spec);
   if (ret != B_NO_ERROR) return ret;

   // Specify where the add-on file for Intel architecture is
   SHFlavor x86("add-ons/x86/SHMySorterSubclassAddOn", SH_ARCH_BEOS_X86);
   ret = spec.AddFlavor(x86);
   if (ret != B_NO_ERROR) return ret;

   // Specify where the add-on file for PowerPC computers is
   SHFlavor ppc("add-ons/ppc/SHMySorterSubclassAddOn", SH_ARCH_BEOS_PPC);
   ret = spec.AddFlavor(ppc);
   if (ret != B_NO_ERROR) return ret;
 
   return B_NO_ERROR;
}
 



virtual const char * GetName()

This method (from the SHDistributableObject API) should be overridden to supply an identifying text string for your SHSorter object.  There are no conditions imposed on what this name is, in particular it is not required (by SockHop) to be unique in any way.  However, when SockHop gets a message with the SH_NAME_SORTER field present, it will iterate through its list of loaded SHSorters and use the first one it finds with the name specified.  This means that if there are two SHSorters loaded on the same node with the same name, one of them will never be used!
 



virtual status_t Archive(BMessage * archive, bool deep=true) const

This method must be overridden to store all relevant state for your SHSorter into (archive).  This method helps fulfill the SHSorter's BArchivable interface, and is how your SHSorter object gets transported over the TCP streams.  At a bare minimum, your Archive() method should look like this:

status_t
SHMySorterSubclass::
Archive(BMessage * archive, bool deep) const
{
   // Let our base class do its archiving first
   status_t ret = SHSorter::Archive(archive, deep);
   if (ret != B_NO_ERROR) return ret;

   // store any additional data describing the current state
   // of this SHMySorterSubclass object into the BMessage here
   // ...

   return B_NO_ERROR;
}
 



virtual bool DoesMessageGoTo(BMessage & msg, const SHNodeSpec & child, uint32 flags)

Whenever a node wants to know where a BMessage should be forwarded to, it asks the appropriate SHSorter object by calling this method.  In fact, for each received BMessage, the node calls this method once for every child node it has, plus once for its parent node.  Your SHSorters' pattern of true/false replies to this method will determine how BMessages propagate throughout the tree.

You must override this method to decide:  Should the BMessage (msg) be sent to the node represented by (child)?  If so, you should return true; otherwise, return false.

The (flags) argument is a bit-chord that is composed of some combination of the following constants:
 
 
Constants that may be in the (flags) parameter and what they mean
SH_FLAG_IS_PARENT This target is the parent of the current node.
SH_FLAG_IS_LOCAL This target is running in the same address space as the current node.
SH_FLAG_IS_SYMLINK This target is not a real child, but just a symbolic link to some other node.
 



virtual bool DoesMessageDistributeLocally(BMessage & msg)

Every time a BMessage is received by a node, this method is called once.  Its job is to decide if the BMessage should be given to the SHWorkers who live on this node.  If so, this method should return true; if not, it should return false.
 


And there are two methods which you may override if you wish, but don't have to:



virtual void BeforeMessageRelay(BMessage & msg)

This method is called once for every BMessage that is received.  It is called after all the calls to DoesMessageGoTo() and DoesMessageDistributeLocally() have been made, but before the BMessage has actually been forwarded to any other nodes or been given to any SHWorkers.  By default, this method does nothing, but you can override it to do various things, such as modify the BMessage before it gets forwarded out.
 



virtual bool DoesMessageGoTo(BMessage & msg, const char * workerName)

Once the sorter has determined that a BMessage should be given to the local node (i.e., once DoesMessageDistributeLocally() has returned true), this method will be called to determine which SHWorkers the BMessage should be given to.  This method is implemented by default to always return true--so, unless you override this method, any user BMessage given to this node will go to all SHWorkers living on this node.  If you do override this method, have it return (true) if the worker with name (workerName) is to be given a copy of (msg), false if not.  Note that the SHWorker must be interested in (msg), or it will not be sent a copy even this method returns true!