Home‎ > ‎Portfolio‎ > ‎

Autogeny Message Server Unit Test Output

Summary

This page contains the rather terse, raw output of an early unit test harness used to exercise the Autogeny messaging layer.  Feel free to skip directly to the output about halfway down the page.  However, if the reader is interested, some of the messaging architecture details are described in the next section.

Here is a diagram depicting the test case processes, their connectivity, and the services/methods invoked via RPC messages in this test.




Messaging Architecture Details

Note that all first-class objects, including MessageServer process instances, were accessible via the messaging layer.  This messaging system transparently supported one-to-one and one-to-many message-based communication.

All Autogeny messages were RPC requests and were routed to their target object(s) and methods by the following four message header fields, effectively forming a multi-part RPC target address:
  • Namespace Name (ns)
    • Autogeny namespace names were arbitrary, hierarchical strings designed to distribute/partition data and computing.
    • Each first-class object instance could be associated with one or more namespaces.
    • Example namespace names:
      • /Conus/California/SacramentoCounty/Alerts/WaterSensorAlerts
      • /MyCompany/Employees/HourlyEmployees
    • Planned, but not implemented, was the ability to specify association with namespaces via rules/decision trees.
  • Class Name (cn)
    • Perhaps this parameter should have been named Type Name.
    • Each first-class object was inherently associated with each of its Java type names (where the is-a relationship is true).
    • These include:
      • An object's fully-qualified class name
      • All class names up the inheritance hierarchy
      • Any interfaces an object class or its ancestor classes implement
    • Thus, "polymorphic" RPC requests were supported.  For example, an RPC request could specify the type:
      • Aircraft (an abstract base class) in which case all first-class objects having Aircraft as an ancestor class would be addressed
      • Boeing737 (a leaf class) in which case all first-class instances of Boeing737 would be addressed
  • UUID (uuid)
    • A unique string assigned to all first-class Autogeny objects
    • This includes running instances of server processes which were modeled as first-class objects and had corresponding instances with fields/methods inside object containers.  (Application management was built right into the Autogeny platform.)
  • Method Name (mn)
    • This is the target RPC method addressed in the message's RPC request.
    • The method invocation arguments in the message payload enabled overloaded methods to be invoked.
The message routing algorithm used was such that the target RPC method mn was invoked on each object o for which the following expression was true:

( o isAssocWith ns || ns == * ) && ( o instanceOf cn || cn == * ) && ( o.uuid == uuid || uuid == * )

Note that this behavior was "pluggable" in that the MessageServers routed messages based on DecisionTree (DT) objects, and the DT used by the system at this time was the above expression.  However, arbitrary message routing DTs could have been dynamically adopted/inserted at runtime.

Also, note that a message addressed to:

( ns = *, cn = *, uuid = 70af208c-6b46-44e5-b5c9-112fc1b2499e )

would be applied to every object with the specified UUID.  (There could be more than one object with this UUID if fault-tolerance redundancy was activated.)  Unless used very sparingly, addressing a message via only the UUID to access a target object would be very inefficient.  However, it did work.

Note that both polymorphic class name parameters plus the support for wildcards in the first three fields listed above made Autogeny's RPC approach what could be termed as "multicast RPC" (both one-to-one and one-to-many).  This offered significant advantages for certain classes of applications.

MessageServers routed messages based on DecisionTree (DT) objects:
  • Each client application's MessageAdapter registered its DT with its associated MessageServer.
  • Each MessageServer registered its aggregate DT with its adjacent MessageServers.
  • DTs could contain simple or complex expressions.
  • DT expressions could be evaluated against any object implementing the net.autogeny.traits.Propertied interface (which included a getProperty() method). 
  • It turns out the the primary message base interface, PrioritizedHttpMessage, extended this interface.
In practice, MessageServers had a message processing thread which pulled messages out of a thread-safe prioritized queue and:
  • Used the MessageServer instance's DT to determine if the message RPC request pertained to the MessageServer instance itself, in which case the local process method was invoked.
  • For each registered MessageDestination object (adjacent MessageServers and connected clients):
    • That MessageDestination's DT was passed in the current message and asked if the message should be routed to its corresonding MessageDestination.
It is worth noting that tangible benefits were derived (by design) because MessageServer message routing DTs could be a function of the RPC target method name parameter, including:
  • Any object could subscribe to the method invocations of any other object or object subset.  This was useful for logging, fault-tolerance/redundancy, and security concerns.  More complex method invocation subscriptions could be attained via the use of wildcards.
  • When desirable, it was possible to split individual objects' data and methods' processing across multiple object containers.  Uninteresting (infrequently-accessed) fields could be stored in one group of high-latency object containers and subnets, whereas, interesting (frequently-accessed) fields could be stored in a different group of low-latency object containers and subnets.
  • This, in turn, made it possible to:
    • Optimize system scalability by reducing the amount of data per object that a particular object container needed to persist locally or cache in memory, thereby increasing the number of objects of a particular class that it could host.
    • Optimize and engineer subnet message flow and RPC request processing loads.
Finally, it would have been very easy to implement traditional message topic/queue functionality with this architecture.



About the Output

The invoked unit test harness interface commands are listed in the first line of each test in the output below.

Next, for each message sent, the four RPC "address" parameters (ns, cn, uuid, and mn) are printed, followed by the invoked RPC method responses.  (The method invocation arguments are not printed such as in the sumIntArray() test.)  It should be noted that the sumIntArray() test argument was a serialized array of 100,000 integers (0 - 99,999).  Note that the returned result correctly matches the integer series summation formula of N*(N+1)/2 for N = 99,999.

Also, please note that:
  • Messages could contain more than one RPC request.
  • Wildcards are used in several unit tests.
In one test case below, all MessageServerImpl objects are asked for their DT objects (by RPC-invoking its implementation of MessageServer.getDtNode()) and the returned objects are then dumped.  Unfortunately, the string representation of the DT objects is not very intuitive.

In this particular test setup:
  • A local MessageAdapter was instantiated in the test harness application
  • Two MessageServers processes were then spawned
  • The test harness then opened a connection to its configured MessageServer via its MessageAdapter
  • Two TestServer processes were spawned
  • As per the connectivity diagram above, RPC requests from the test harness addressed to the TestServer_2 process methods were first sent to MsgServer1, which then relayed the messages to MsgServer2 which, in turn, delivered them to TestServer_2.  Thus, the test harness tested DT-based message relaying between MessageServers.
Finally, note that the last test case prints out the asynchronously received responses and the results are a bit confusing as a result.



Unit Test Output

Here is the output from a messaging layer unit test harness session:

-----------------------------------------------------------------------
cmd selected: Create Local Msg Adapter

-----------------------------------------------------------------------
cmd selected: Open Connection

-----------------------------------------------------------------------
cmd selected: Start Test Servers

-----------------------------------------------------------------------
cmd selected: MsgServer1,2.getUuid()
        ns = /autogeny/sys    cn = net.autogeny.sockproto.MsgServerImpl    uuid = *    mn = getUuid

Responses:

MsgServer1
MsgServer2

-----------------------------------------------------------------------
cmd selected: TestServer.reverseCase()
        ns = /autogeny/servers    cn = net.autogeny.sockproto.TestServer    uuid = TestServer_1    mn = reverseCase

Responses:

<abcdefg> reverse case is: <ABCDEFG>

-----------------------------------------------------------------------
cmd selected: TestServer2.addTwoInts()
        ns = /autogeny/servers    cn = net.autogeny.sockproto.TestServer    uuid = TestServer_2    mn = addTwoInts
        ns = /autogeny/servers    cn = net.autogeny.sockproto.TestServer    uuid = TestServer_2    mn = addTwoInts
        ns = /autogeny/servers    cn = net.autogeny.sockproto.TestServer    uuid = TestServer_2    mn = addTwoInts

Responses:

10
10
10

-----------------------------------------------------------------------
cmd selected: TestServer.sumIntArray
        ns = /autogeny/servers    cn = net.autogeny.sockproto.TestServer    uuid = TestServer_1    mn = sumIntArray

Responses:

4999950000

-----------------------------------------------------------------------
cmd selected: Send 2 RPC Requests
        ns = /autogeny/servers    cn = net.autogeny.sockproto.TestServer    uuid = TestServer_1    mn = sumIntAray, reverseCase

Responses:

4999950000
<abcdefg> reverse case is: <ABCDEFG>

-----------------------------------------------------------------------
cmd selected: Send getDtNode() to all MsgServers
        ns = /autogeny/sys    cn = net.autogeny.sockproto.MsgServerImpl    uuid = *    mn = getDtNode

Responses:

+ null AND
    + null OR
        + getProperty( Dest-Namespace-Name ) Operand1StringStartsWithOperand2String /autogeny/sys
        - getProperty( Dest-Namespace-Name ) StringNullZeroLengthOrAsterisk
    + null OR
        + getProperty( Dest-Class-Name ) StringEquality net.autogeny.sockproto.MsgServerImpl
        - getProperty( Dest-Class-Name ) StringEquality net.autogeny.sockproto.MsgServer
        - getProperty( Dest-Class-Name ) StringEquality java.lang.Runnable
        - getProperty( Dest-Class-Name ) StringEquality net.autogeny.traits.Discoverable
        - getProperty( Dest-Class-Name ) StringNullZeroLengthOrAsterisk
    + null OR
        - getProperty( Dest-UUID ) StringEquality MsgServer1
        + getProperty( Dest-UUID ) StringNullZeroLengthOrAsterisk
+ null AND
    + null OR
        + getProperty( Dest-Namespace-Name ) Operand1StringStartsWithOperand2String /autogeny/sys
        - getProperty( Dest-Namespace-Name ) StringNullZeroLengthOrAsterisk
    + null OR
        + getProperty( Dest-Class-Name ) StringEquality net.autogeny.sockproto.MsgServerImpl
        - getProperty( Dest-Class-Name ) StringEquality net.autogeny.sockproto.MsgServer
        - getProperty( Dest-Class-Name ) StringEquality java.lang.Runnable
        - getProperty( Dest-Class-Name ) StringEquality net.autogeny.traits.Discoverable
        - getProperty( Dest-Class-Name ) StringNullZeroLengthOrAsterisk
    + null OR
        - getProperty( Dest-UUID ) StringEquality MsgServer2
        + getProperty( Dest-UUID ) StringNullZeroLengthOrAsterisk

-----------------------------------------------------------------------
cmd selected: New Tests
        ns = *    cn = *    uuid = *    mn = getUuid
        ns = *    cn = *    uuid = *    mn = getNamespaceName
        ns = *    cn = net.autogeny.traits.Discoverable    uuid = *    mn = getDiscoveryData

Responses:

MsgServer1
/autogeny/sys
ns: </autogeny/sys> cn: <net.autogeny.sockproto.MsgServerImpl> uuid: <MsgServer1>
TestServer_1
MsgServer2
GuiTestHarness_3
/autogeny/servers
/autogeny/clients
/autogeny/sys
ns: </autogeny/sys> cn: <net.autogeny.sockproto.MsgServerImpl> uuid: <MsgServer2>
TestServer_2
ns: </autogeny/servers> cn: <net.autogeny.sockproto.TestServer> uuid: <TestServer_1>
ns: </autogeny/clients> cn: <net.autogeny.sockproto.GuiTestHarness3B> uuid: <GuiTestHarness_3>
/autogeny/servers
ns: </autogeny/servers> cn: <net.autogeny.sockproto.TestServer> uuid: <TestServer_2>

-----------------------------------------------------------------------
cmd selected: Close Connection



Copyright (c) Richard Creamer 2002 - 2010 All Rights Reserved