org.sandev.tools.util
Class DirectCallMessager

java.lang.Object
  extended by org.sandev.tools.util.DirectCallMessager
All Implemented Interfaces:
org.sandev.basics.nodecommon.Messager
Direct Known Subclasses:
AuthorizedJMSMessager, JMSMessager

public class DirectCallMessager
extends java.lang.Object
implements org.sandev.basics.nodecommon.Messager

A Messager implementation that translates all communications into direct method calls. This class is only appropriate for for use on single server deployments, or as a base class for other Messager implementations. While the DirectCallMessager provides a reasonable facsimile of the maximum possible messaging speed in Java, it has some noteworthy aspects:

  1. Because all calls are direct, and we don't waste time spawning threads for each call, a node which decides to go into an infinite loop while processing a message will hold up the entire system. It is generally good practice for any node that does significant work on receiving an asynchronous message to spawn a work thread.
  2. On a single CPU system, direct messaging can work like a single stream of procedure calls. Real messaging doesn't behave like that. Move your asynchronous message sends around in your tests to verify you aren't making timing assumptions that won't hold up under load.

Direct messaging doesn't cause problems, but it can definitely bring some hidden ugly code to the foreground, while hiding other issues. Be aware of your resource locks and message paths.

How it works:

Each DirectCallMessager instance keeps a dictionary of Messageable instances indexed by instance name. We look up the instance name to get the object and then make the call.

If the instance is not found in the dictionary, then we convert our Messageable into a Controllable (we are assuming here that our Messageable is a node, and all nodes are Controllable so this works). From the root, we traverse the tree looking for the node with the specified instance name. If found, we add it to the dictionary and make the call. If not, then we fail.

On failure we throw an exception. Subclasses can override this behavior and take the direct call failure as an indication that the target node is not local. At that point they would try to get the message across via the supporting communications technology.


Field Summary
protected  org.sandev.basics.structs.NodeInstance ourInstance
          The instance of the node we are serving.
protected  org.sandev.basics.nodecommon.Messageable ourNode
          The node we are serving.
protected  java.util.HashMap subTable
          All known direct subscribers
 
Fields inherited from interface org.sandev.basics.nodecommon.Messager
MODE_DIRECT, MODE_INVALID, MODE_SECURE, OPTIMIZE_IF_POSSIBLE, OPTIMIZE_NEVER, WIRE_BASICSECURITY, WIRE_SECURECOMMS, WIRE_TRANSPORTONLY
 
Constructor Summary
DirectCallMessager()
          Default ctor does nothing.
 
Method Summary
protected  void addSubscriber(org.sandev.basics.nodecommon.Messageable node, java.lang.String msgClass)
          Add the specified node to our list of subscribers
protected  org.sandev.basics.structs.SandMessage doQuery(org.sandev.basics.structs.SandMessage msg, java.lang.String recipient, int optimize)
          Query the specified node.
protected  void doSend(org.sandev.basics.structs.SandMessage msg, java.util.ArrayList al)
          Workhorse for interface doSend method.
protected  void doSend(org.sandev.basics.structs.SandMessage msg, int optimize)
          Send the message.
protected  void doSubscribe(org.sandev.basics.nodecommon.Messageable node, java.lang.String msgClass, java.lang.String sender, int optimize)
          Make the subscription happen.
protected  void doUnsubscribe(org.sandev.basics.nodecommon.Messageable node, java.lang.String msgClass, java.lang.String sender, int optimize)
          Make the subscription go away.
protected  void error(java.lang.String text)
          General error reporting method.
protected  int getImplementationWireSecurity()
          Override this method to return the level of security provided by this messager implementation.
protected  org.sandev.basics.nodecommon.Messageable getMessageable(java.lang.String instanceName)
          Return the Messageable corresponding to the instance name specified.
 org.sandev.basics.structs.NodeInstance getOurInstance()
          accessor for ourInstance
 org.sandev.basics.nodecommon.Messageable getOurNode()
          accessor for ourNode
protected  java.util.Map getSubTable()
           
 int getWireSecurity()
          Return the wire security level this messager implementation provides.
protected  void info(java.lang.String text)
          General info logging method.
 void init(org.sandev.basics.nodecommon.Messageable node, org.sandev.basics.structs.NodeInstance instance)
           
 org.sandev.basics.structs.SandMessage query(org.sandev.basics.structs.SandMessage msg, java.lang.String recipient, int optimize, int mode, java.lang.String localAuth)
          Query the specified recipient and return the result.
 void queryReceiver(org.sandev.basics.nodecommon.Messageable node, java.lang.String messageClass, java.lang.String localAuth)
          Get the local authorizer and register this node as the recipient for these kinds of queries.
protected  void removeSubscriber(org.sandev.basics.nodecommon.Messageable node, java.lang.String msgClass)
          Remove the specified node from our list of subscribers
 void send(org.sandev.basics.structs.SandMessage msg, int optimize, int mode, java.lang.String auth)
          If this is a secure send, call our local authorizer directly and have them wrap and send.
 void setOurInstance(org.sandev.basics.structs.NodeInstance instance)
          mutator for ourInstance
 void setOurNode(org.sandev.basics.nodecommon.Messageable node)
          mutator for ourNode
 void shutdown()
           
 void subscribe(org.sandev.basics.nodecommon.Messageable node, java.lang.String msgClass, java.lang.String sender, int optimize, int mode, java.lang.String localAuth, org.sandev.basics.structs.SandAttrVal[] matchDescription)
          Hook us up for broadcast messages of the specified type from the specified source.
 void unsubscribe(org.sandev.basics.nodecommon.Messageable node, java.lang.String msgClass, java.lang.String sender, int optimize, int mode, java.lang.String localAuth)
          Undo the subscription.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

subTable

protected java.util.HashMap subTable
All known direct subscribers


ourNode

protected org.sandev.basics.nodecommon.Messageable ourNode
The node we are serving. This is set at initialization time.


ourInstance

protected org.sandev.basics.structs.NodeInstance ourInstance
The instance of the node we are serving. This is set at initialization time.

Constructor Detail

DirectCallMessager

public DirectCallMessager()
Default ctor does nothing.

Method Detail

getSubTable

protected java.util.Map getSubTable()

getOurNode

public org.sandev.basics.nodecommon.Messageable getOurNode()
accessor for ourNode


setOurNode

public void setOurNode(org.sandev.basics.nodecommon.Messageable node)
mutator for ourNode


getOurInstance

public org.sandev.basics.structs.NodeInstance getOurInstance()
accessor for ourInstance


setOurInstance

public void setOurInstance(org.sandev.basics.structs.NodeInstance instance)
mutator for ourInstance


init

public void init(org.sandev.basics.nodecommon.Messageable node,
                 org.sandev.basics.structs.NodeInstance instance)
          throws org.sandev.basics.nodecommon.MessagerException
Specified by:
init in interface org.sandev.basics.nodecommon.Messager
Throws:
org.sandev.basics.nodecommon.MessagerException

shutdown

public void shutdown()
Specified by:
shutdown in interface org.sandev.basics.nodecommon.Messager

subscribe

public void subscribe(org.sandev.basics.nodecommon.Messageable node,
                      java.lang.String msgClass,
                      java.lang.String sender,
                      int optimize,
                      int mode,
                      java.lang.String localAuth,
                      org.sandev.basics.structs.SandAttrVal[] matchDescription)
               throws org.sandev.basics.nodecommon.MessagerException
Hook us up for broadcast messages of the specified type from the specified source. For direct subscriptions, the sender is a node. For secure subscriptions, the sender is the remote authorizer. When our local authorizer gets an incoming AuthWrapper and unwraps it, it delivers it directly to all Messageable instances that have registered to receive it.

The matchDescription is ignored. The node already has logic to check this, and it doesn't matter if this checking occurs up front or not since it's all direct calls.

Specified by:
subscribe in interface org.sandev.basics.nodecommon.Messager
Throws:
org.sandev.basics.nodecommon.MessagerException

unsubscribe

public void unsubscribe(org.sandev.basics.nodecommon.Messageable node,
                        java.lang.String msgClass,
                        java.lang.String sender,
                        int optimize,
                        int mode,
                        java.lang.String localAuth)
                 throws org.sandev.basics.nodecommon.MessagerException
Undo the subscription. Our node is no longer running.

Specified by:
unsubscribe in interface org.sandev.basics.nodecommon.Messager
Throws:
org.sandev.basics.nodecommon.MessagerException

send

public void send(org.sandev.basics.structs.SandMessage msg,
                 int optimize,
                 int mode,
                 java.lang.String auth)
          throws org.sandev.basics.nodecommon.MessagerException
If this is a secure send, call our local authorizer directly and have them wrap and send. Otherwise call doSend

Specified by:
send in interface org.sandev.basics.nodecommon.Messager
Throws:
org.sandev.basics.nodecommon.MessagerException

query

public org.sandev.basics.structs.SandMessage query(org.sandev.basics.structs.SandMessage msg,
                                                   java.lang.String recipient,
                                                   int optimize,
                                                   int mode,
                                                   java.lang.String localAuth)
                                            throws org.sandev.basics.nodecommon.MessagerException
Query the specified recipient and return the result. While most messaging implementations will wrap the exception into a message and return it across the network, this messager actually throws the exception directly since it is part of the same process call. The result is a stack trace across the messaging path without having to go through the messages to figure out what happened.

For secure queries, the recipient is the remote authorizer instance name. That authorizer finds the appropriate recipient through the receive registrations established on startup.

Specified by:
query in interface org.sandev.basics.nodecommon.Messager
Throws:
org.sandev.basics.nodecommon.MessagerException

queryReceiver

public void queryReceiver(org.sandev.basics.nodecommon.Messageable node,
                          java.lang.String messageClass,
                          java.lang.String localAuth)
                   throws org.sandev.basics.nodecommon.MessagerException
Get the local authorizer and register this node as the recipient for these kinds of queries.

Specified by:
queryReceiver in interface org.sandev.basics.nodecommon.Messager
Throws:
org.sandev.basics.nodecommon.MessagerException

getWireSecurity

public int getWireSecurity()
Return the wire security level this messager implementation provides.

Specified by:
getWireSecurity in interface org.sandev.basics.nodecommon.Messager

getImplementationWireSecurity

protected int getImplementationWireSecurity()
Override this method to return the level of security provided by this messager implementation. Since direct in-memory messaging is as secure as a method call, additional encryption would be a waste of processing and makes debugging a pain.


doSubscribe

protected void doSubscribe(org.sandev.basics.nodecommon.Messageable node,
                           java.lang.String msgClass,
                           java.lang.String sender,
                           int optimize)
                    throws org.sandev.basics.nodecommon.MessagerException
Make the subscription happen. We completely ignore the optimize flag, since we are only capable of optimized delivery. We actually add ourselves to the source node's subscribers directly.

When overriding this method in a subclass, you can first call here to try optimized delivery. If that succeeds, you're done. If optimized delivery doesn't succeed it will throw.

Throws:
org.sandev.basics.nodecommon.MessagerException

doUnsubscribe

protected void doUnsubscribe(org.sandev.basics.nodecommon.Messageable node,
                             java.lang.String msgClass,
                             java.lang.String sender,
                             int optimize)
                      throws org.sandev.basics.nodecommon.MessagerException
Make the subscription go away.

Throws:
org.sandev.basics.nodecommon.MessagerException

doSend

protected void doSend(org.sandev.basics.structs.SandMessage msg,
                      int optimize)
               throws org.sandev.basics.nodecommon.MessagerException
Send the message. We completely ignore the optimize flag, since we are only capable of optimized delivery. We actually call the deliver method of the subscriber node directly.

If for any reason our send fails, we just log it and continue since the failure is in the subscriber node and not our stuff.

When overriding this method in a subclass, you can first call here to try optimized delivery. If that succeeds, you're done. If optimized delivery doesn't succeed then this method will throw, and non-optimized delivery would then be attempted.

It is possible to subscribe to an interface, rather than a specific message. For example the CacheManager needs to subscribe to any kind of update from the DataManager, and it can't know all the specific kinds of updates at declaration time. Another case is the MessageDriver, which needs to listen for all broadcast messages. We can add cases for other general messaging interfaces as needed, for now it's just those two.

Throws:
org.sandev.basics.nodecommon.MessagerException

doSend

protected void doSend(org.sandev.basics.structs.SandMessage msg,
                      java.util.ArrayList al)
               throws org.sandev.basics.nodecommon.MessagerException
Workhorse for interface doSend method.

Throws:
org.sandev.basics.nodecommon.MessagerException

doQuery

protected org.sandev.basics.structs.SandMessage doQuery(org.sandev.basics.structs.SandMessage msg,
                                                        java.lang.String recipient,
                                                        int optimize)
                                                 throws org.sandev.basics.nodecommon.MessagerException
Query the specified node. We ignore the optimize flag since this messager only does optimized delivery.

When overriding this method in a subclass, you can first call here to try optimized delivery. If that succeeds, you're done. If optimized delivery doesn't succeed it will throw.

Throws:
org.sandev.basics.nodecommon.MessagerException

error

protected void error(java.lang.String text)
General error reporting method. Basically just logs it safely.


info

protected void info(java.lang.String text)
General info logging method. Basically just logs it safely.


addSubscriber

protected void addSubscriber(org.sandev.basics.nodecommon.Messageable node,
                             java.lang.String msgClass)
Add the specified node to our list of subscribers


removeSubscriber

protected void removeSubscriber(org.sandev.basics.nodecommon.Messageable node,
                                java.lang.String msgClass)
Remove the specified node from our list of subscribers


getMessageable

protected org.sandev.basics.nodecommon.Messageable getMessageable(java.lang.String instanceName)
Return the Messageable corresponding to the instance name specified.