EVENT BUS ARCHITECTURE

Abstract: A distributed computer system in which a plurality of applications are connected to an event bus through an adapter interface. Messages exchanged between applications are transmitted on the event bus and are processed according to rules stored in a rules repository.

Description: EVENT BUS ARCHITECTURE CROSS-REFERENCE TO RELATED APPLICATION(S) This application claims the benefit of U.S. Provisional Application No. 60/219,304 filed July 19, 2000, and is related to U.S. Provisional Application No. 60/249,412 filed November 16, 2000, U.S. Provisional Application No. 60/268,386 filed February 12, 2001 and U.S. Patent Application No. 09/747,307 filed December 22, 2000, which are hereby incorporated by reference. BACKGROUND A problem with existing application platforms is the difficulty of writing applications for the platforms. Take for example of the Windows platform. Prior to Windows, programmers worked under the constraints of DOS, hogging the full screen one application at a time, writing their own printer drivers, managing memory, dealing with TSRs - all a nasty set of problems that each vendor had to solve independently. Windows made many things easy for applications writers, including shared access to the screen through window management and memory segmentation for multiple processes, a shared printer driver model and abstracted access for applications to name a couple. The benefit to the consumer was a common user experience with an abundance of applications made available by the relative ease and low cost of development compared to the DOS world. A problem with platforms, like Windows, is the difficulty in porting the applications to other platforms. For example, Windows applications cannot run on a Unix platform without extensive rewriting. It would be desirable to have an environment in which applications can be developed and executed across multiple platforms without modification. It would also be desirable if these applications executing on different platforms could communicate in a reliable and robust manner. BRIEF DESCRIPTION OF THE DRAWINGS Figure 1 is a block diagram illustrating the functions of the Kernel. Figure 2 is a block diagram representing the five module types that together may be part of an application deployment. Figure 3 is a block diagram illustrating a small configuration with the Open Design platform installed on existing servers. Figure 4 is a block diagram representing a possible large deployment configuration. Figure 5 is a block diagram illustrating interactions with a deployment tool ("DT"). Figure 6 is a block diagram representing some of the deployment elements possible with Open Design application development. Figure 7 is a block diagram illustrating communications between adapters, applications, and the Event Bus. Figure 8 shows a high-level flow of control between the Network Layer, the Event Bus, the Rules Engine, and private calls between instances of the Network Layer across a network. Figure 9 is a block diagram illustrating the addressing layers of an Open Design Message. Figure 10 illustrates a more complete diagram that includes all of the architecture elements involved in node-to-node message exchanges between applications hosted on nodes A and B. DETAILED DESCRIPTION A programmable high-level network computer infrastructure on which distributed applications and services are hosted is provided. In one embodiment, the network computer infrastructure is built out of active machines connected together using existing networking technologies and protocols. These networked machines are called nodes. Applications and services run on one or more nodes, and communicate by exchanging messages. Applications and services are named and registered on the network. Nodes are also named and registered on the network. A single named application or service may span multiple nodes. The network uses: • Messages that contain addressing, logic and content that are issued and consumed by OS applications, or meta-applications (.R files). (OS applications are applications written to run on a specific OS and hardware platform. Examples of these applications are traditional desktop applications such as Word, or server applications such as an Apache Web server.) (Meta- applications are rules-based applications, also called modules.) • Applications of different types issue messages to accomplish tasks. • Messages may contain platform-supplied instructions, a payload, and destination addresses • Message instructions are a subset of the same instructions used to program the application network router (.R files) • Application communication supports several message patterns, which include request/response, one-way sends, or may be built up of combinations of requests and responses between multiple elements of the network or multiple exchanges with any one element of the network • Each node supports a routing processor infrastructure - router - which forms a platform for routing messages, evaluated according to meta-application, and routed to other nodes, or to a node local application processor • Processors which may execute code contained in messages or may deliver data in messages to applications or meta- applications • Applications and meta-applications may be composed of services exposed by other meta-applications. The Application Network Router, a specialized high scale message router, is an element of the computer infrastructure or platform. It may run on each node and may be capable of passing events to one or multiple end points, intra, or inter machines/nodes with control of quality of service delivery and content. The quality of service provides for controlling the duplex channel, authenticating communicating entity identities, distributing messages appropriately to distributed services based on system and application provided criteria, applying application and platform policy, and securing the content of messages. The message passing fabric is capable of emulating concepts of unicast (single destination) and multicast (multi- destination) messaging. Plug in logical modules are associated with inbound or outbound messages (This programmable routing may not be XML routing. XML routing and messaging middleware route on the basis of messages and subjects. The routing may be a fixed function and not programmable.) Messages are sent between connection points called ports. Ports can be unicast or multicast end points. The router controls inbound, outbound, and forwarded traffic by applying these modules in stages based on the direction of message flow, in-bound or out-bound. Modules types may be of many types. The common module types include, but are not limited to, routing, performance control based (performance metric based), configuration based, security policy, application policy, and proxy (specialized message forwarding). The network uses rules bundled together into 'modules' and an inference engine to program the router. These elements may be part of the Kernel. The network uses an XML-based rule language for application and system logic that controls (or programs the router for) message forwarding, message instruction parsing, and message delivery. Figure 1 is a block diagram illustrating the functions of the Kernel. Use of rules allows the providing of two core values based on rules based router programmability: • Extensible control of applications and networks to distribute services and connections in response to dynamic changes within the network. This control is extended to application providers, as well as system administrators and network providers. • Profiling: Monitoring of applications, users, message types, message sources, etc. become data stored per node, or collected centrally. This data can become events that are targeted by rules. These rules can provide both network and application reconfiguration logic, as well as allow the development of applications tailored to new business models. 1.0 Event Bus Approach Open Design Applications run (potentially) everywhere - under Windows, on Unix, under Macintosh, on Palm Pilots, on Internet-ready phone, and are distributed/distributable at runtime. 1.1 Distributed Platform The Open Design system can host and manage applications and services on a network. The system is built of a network of connected nodes that run a set of core infrastructure services. Services, under a single name may span multiple nodes. The services can expand and contract to make best use of network bandwidth, connection, and node resources under the policy control specified for the nodes and the application within an administrative domain. The applications, and the policy associated with the applications are distributed in concert, so policies established for the application may remain in effect regardless of the topologies or resources applied to hosting the service. Both the policies and application or service logic is expressed in an Open Design Rules language, and processed by the Open Design distributed platform. 1.1.1 Hosting and Management Approaches There are several ways of hosting and managing distributed applications that range on a spectrum from "highly centralized management" to "distributed management." 1 ) Single machine - "Big iron" in a data center, or low scale single PC applications. 2) Cluster - A group of tightly coupled machines run the application. 3) Data center - A group of centrally managed machines run the application. 4) Multiple data centers - A group of centrally managed data centers that run the application. 5) Distributed - Groups of independently managed machines run an application. For each of these, there is another spectrum from "tightly-coupled hardware-software" to "loosely coupled hardware-software"; the latter is often called a runtime, layered, or OS approach. Dedicated applications are the former - when underlying hardware configurations change, the software also changes. Operating systems are the latter - the software is insulated from underlying hardware changes, and vice versa. In general, the sophistication of runtimes decreases as you go from single machine to distributed: 1 ) Single machine - Windows or Linux. 2) Cluster - BEA application servers, high end Windows NT, Beowulf and Solaris 3) Data center - Loudcloud, J2EE and .NET 4) Multiple data centers - ASPs are evolving to this model. 5) Distributed -Sun's Jini, or Sony's HAVi The Open Design platform can be described as using the "distributed management" approach to control hardware resources and the "runtime or loosely-coupled approach" to hardware-software integration. The advantages of distributed management may include: 1) Organic expansion of the network. Central approval to extend the network or its resources is not needed. 2) Federation model. Organizations can enlist and un-enlist from applications without complex integration tasks. Systems are usually built "defensively," since they may need to operate in the presence of failures and security problems. 3) Decentralized customization. Organizations that participate in an application can customize or integrate their part of the application with their owned systems, for example integrating with their ERP system, without interfering with the overall application (or needing centralized approval). Currently, one integration task for one partner often affects the entire application or is bogged down in "lowest common denominator" approval processes. The advantages of a runtime approach may include: 1 ) Organic adoption of new technology. The application is written to an abstraction so the hardware underneath can change. Faster machines, more machines, different connection bandwidth, etc. are possible without forcing applications to be rewritten. 2) Simpler design model. A software programmer does not need to know details of the network architecture to write a program. A programmer can map their software concepts like user names to network concepts like filters, IP addresses, and port numbers. Likewise, a hardware designer does not have to worry about mapping their features to application objects. A designer can map MPLS tags to user priority levels. Currently, each mapping needs to be redone when changes occur in the software or hardware. 3) Easier management. An enterprise usually separates its application development and hardware management functions. Currently, networked applications such as B2B exchanges stress this interface and require on-going management and communications overhead. A system without these features is likely to be fragile because frequent centrally managed changes to the application are needed, which destabilizes the application. It is also likely to be unattractive to partners since the cost and risk of co-deployment and centrally managed applications is higher. The most successful example of this kind of system is the Internet for data networking. The disadvantages are that distributed systems are more complex, and runtimes are a distribution and reliability dependency (i.e., the runtime has to be everywhere your application is, and has to be at least as stable as your application). 1.1.2 Open Design Modules The points of control provided by the Open Design platform for control and management of distributed applications on the network are derived from the same as the set of module classes enumerated above - that is: routing, performance (performance metric based), configuration (node, or system policy based), security policy, applications, application policy, and proxy (specialized message forwarding.) The Open Design platform provides a consistent mechanism, and language, and multi-platform runtime with which to control, extract, route, and process data, as messages, on the network. Figure 2 is a block diagram representing the five module types that together may be part of an application deployment. Each module type, and its attendant benefits are described in the following sections. The application adapter, its features and interfaces are described in the Router Architecture section. Sample code for each of the rule types described below: routing, network configuration, security, application integration, and user profiling are provided in the Event Bus section of this document. 1.1.2.1 Distribution and Routing - application and system supplied routing rules The Open Design System provides application naming services and infrastructure to allow applications to specify the name of their service, the basis by which application request messages are routed throughout the network. Routing updates may be required when policy or network management rules cause service partitioning and/or replication. The Open Design infrastructure provides automatic routing updates, service names and associated routing data, to all nodes within the network that require the update. For example, a single program for a directory service namespace can: • Partition the directory namespace as it grows • Replicate the directory namespace • Query the directory namespace • Manage failures of nodes hosting the directory These mechanisms allow location and resource-abstracted access to network applications and services, and dynamic responsiveness of the network to load or failure, without service interruptions to service clients. This type of module is called a routelet. 1.1.2.2 Network and Use Policy - application and system supplied response modules Current, successful routing algorithms are "adaptive" or "dynamic." These algorithms change the route taken for messages depending upon failure modes and congestion in the network. These are currently the most effective routing algorithms because they scale very well, and also handle network faults transparently. Most sophisticated routing protocols focus on how to converge to new routes quickly and how to avoid live and deadlocks and routing loops. While this may be important, a greater benefit can come from making router algorithms adaptive to the application they route (e.g., understanding that the traffic is streaming multimedia, high priority, and so on). This benefit is theoretically driven from the desire of carriers to charge for differentiated services, i.e. more money for guaranteed bandwidth and lower latency. As a consequence, the highest growth market has been in network devices in the data center or near the edge of the network that interface with application services, such as web servers, and understand their traffic needs better. Over time, these routers and caches have become programmable, allowing a data center person to shape and prioritize data traffic based on a variety of criteria. A simple example of this is to prevent Napster traffic from swamping a campus network. Modern routers take into account more criteria than ever — jumping from just destination address and local connectivity, to several dozen parameters. As the criteria become more and more complex, they should be coupled to application development and application management environments. This is where programmable routing comes in. One can have tremendous efficiencies in a data network by knowing more about the application, and tailoring the router behavior based on this knowledge. The Open Design platform is built up of a set of connected nodes, each with the Open Design rules based programmable router. The nodes run routing algorithms that move programs, data, connections, and session state. These routing and distribution rules (described above) are supplied by administrators, application developers, and system management developers. The logic in these rules can effect not only application and message distribution and routing, but also to the selection of network peers, and responses to connection or request overload. These rules, such as response to load, connection requests, CPU or disk full responses, are called network management modules. The Open Design System provides OS and system event messages against which network management modules may be written. The Open Design System provides a platform by which application and system supplied routing criteria can be used to tailor routing and service distribution to make best use of hardware and communication resources. 1.1.2.3 Security - application and system supplied access control modules The Open Design System provides mechanisms by which policy for access to any part of the platform and service may be controlled. Rules to access services, OS, or node facilities may be written to include directives that disallow or restrict access to specified modules, users, domains, adapters, etc. These policy expressions evaluated at the destination, as one stage of rules processing, before activation occurs. The activation of the rule is not invoked if the policy expression fails. Policy rules are expressions that appear on the LHS ("left hand side") of any of the other rule types. 1.1.2.4 Application Integration - proxy and servicelet modules The Open Design applications are modules. Modules invoke the name registration mechanism, and provide the message matching for events on the event bus of Open Design services. This means that modules are effectively the actual implementation of any service hosted by the Open Design platform. The service logic to either directly handle a request message or forward it to an OS-application are expressed as the activation, or RHS of a rule. Servicelet is the name used to indicate that the logic of the service is entirely expressed as rules. A proxy module is one that forwards requests to adapters to invoke the appropriate logic to handle a request. 1.1.2.5 Profiling Applications that make use of profiling data available to the rules engine provide interesting and controversial business opportunity. Use of a rules engine as the mechanism for programming the router makes this opportunity possible. The kinds of profiling data available to a rules application may include user, application, node, message source, message destination, etc. Each of these data types, in combination with rules to respond to patterns that are developed by the data, represent interesting technical and business opportunities. A value of the Open Design Platform for user profiling is that it makes available to Open Design Applications the event history and user profile of the consumer. No new OS Applications need to be written to make this a compelling environment. The user's browser activity alone provides a richness of interactions. The user could benefit if their interaction with the browser resulted in an implicit user profile. Items the user buys, sites the user visits, the number of times the user causes an operation to occur - all of these actions stored and analyzed under the privacy of the user's machine can make for a much more customized, personalized experience when browsing the web. This combined with the contributions of user actions under other OS Applications, you get an even richer, more detailed version of who the user is. The Open Design Platform leverages these existing OS Applications rather than needing new OS Applications to be written. The Open Design platform provides is a mechanism by which OS Application-specific events and user actions are converted to a higher-level, abstracted Open Design event stream that can be analyzed and compiled into a rich user profile. As the profile grows in detail, rules can then be applied to cause new semantic actions to occur. 1.2 Network Topologies The Open Design platform may be installed in both small and large deployments, using existing hardware resources, or with the addition of specialized configurations of nodes. Node configurations may include hosting, proxy, router, storage, and management. Figure 3 is a block diagram illustrating a small configuration with the Open Design platform installed on existing servers. The Open Design platform installed in a large deployment may involve both software installed on existing servers, and specialized server configurations for proxy, management, and large scale routing. Figure 4 is a block diagram representing a possible large deployment configuration. Use of the Open Design Platform provided Distributed Management Framework (DMF) allows configuration, routing, policy, and application modules to be pushed to Open Design network elements. This provides fixed programmability and management within an administration domain. The policy modules pushed to network elements allows the dynamic routing, load balancing, application distribution, and network reconfiguration functions provided by the Open Design Platform to occur within the fixed framework specified by an administrator. 1.4 Module Language Open Design Applications are, for the most part, expressed in rules which may be chunks of XML in a specific format. The rules are managed by the Open Design Platform in a repository on the client machine, and are triggered in response to the abstracted events in the Open Design event stream. As rules fire, the Open Design Platform constructs a ticket that represents the transient state of the sets of rules it comprises. Rules are chunks of text. They are relatively easy to read, easy to edit, and easy to put together. Because they are XML, they are also very extensible along many axes. There is no compilation involved, no binaries to build. A set of like-minded rules, a module, forms a logical application under the Open Design Platform. There may be no compiled code to create or run an Open Design application or service. The modules that represent Open Design Applications contain declarative structures that define their UI, including DHTML for insertion in web pages, instructions for overriding the behavior of existing web pages, commands for controlling existing scriptable applications, policy, configuration, management and routing rules. In certain cases an application writer can provide customized UI, new application logic, or an adapter to an existing third-party legacy application. In these cases the code may be delivered in binary format for a particular OS platform to complement interactions with existing OS Applications. Figure 5 is a block diagram illustrating interactions with a deployment tool ("DT"). Figure 6 is a block diagram representing some of the deployment elements possible with Open Design application development. Not all applications require all elements, but the ability to not only provide application logic, but also add routing, policy, and fine grain distribution control to an application enable the deployment of rich, highly capable, available, and reliable applications and services in a distributed environment. 2.0 Router Architecture Elements of the Open Design architecture may include: • Adapters • Event Bus (aka Message Bus) • Rete-based Rules Engine • Rules Repository • Local Storage (aka Kernel Tree) • Modules (rule bundles) Adapters are the elements that translate raw application events to a standard form understood by the Open Design platform. They also translate activations into application specific commands or messages. The Event Bus captures Open Desigϊi-formatted messages as they are generated through application adapters. The Event Bus processes commands fired from rules and routes them to application adapters. The Rules Engine processes rules based on events and the data (or facts) in the Rete network and causes either new facts to be asserted into the Rete network, or actions, through adapter-specific activations, to occur based on those events. The Rules Repository contains the set of rule bundles or modules configured on a node. The module types are: default and site configuration modules, routing modules, and application modules. The Local Storage is an area of working memory accessible to modules to store session or persistent data. The Modules are the logic interpreted by the Rules Engine to control the router. Modules reside in the Rules Repository Figure 7 is a block diagram illustrating communications between adapters, applications, and the Event Bus. 2.1 Adapters OS applications spawn raw events unique and specific to that application, and each application may support a specific set of commands. The Open Design adapter object provides a means for abstracting those events and commands for general use. 2.2 Adapters and Messages An adapter is an object that enables procedural code to talk to the Open Design Event Bus (and indirectly to the Open Design Rules Engine). Through the adapter, an application can issues messages (events) to the Open Design platform for processing. The adapter has knowledge of its corresponding OS application, and also has understanding of Open Design platform structures and schemas. Therefore, it can bridge the gap between OS application events and Open Design abstracted events. Adapters can be written in a couple of different ways. First, they can be incorporated directly into an OS application, the adapter code written as part of the application mainline code. In this case, the OS application writer knows about the Open Design platform during the design and implementation of the application and builds this translation layer directly into the code. In the second case, adapters can be written for existing OS applications through whatever extensibility mechanisms the application provides. In certain cases, an application might provide an automation interface from which events can be detected. Or, in the case of certain Windows applications, the application's window can be subclassed or hooked to obtain raw events. To take Internet Explorer as an example, a programmer can write a Browser Helper Object in order to tap into the DHTML DOM to watch for raw events of interest. 2.3 Adapters and Activations Just as adapters convert raw events to an abstracted Open Design message, adapters also convert Open Design activations into application- specific commands or messages for processing. This allows the Open Design platform to make calls on applications through a generalized command interface. These activations are issued in response to rules being fired, with the activations written by rules authors who understand the capabilities and activation schema of the target OS application. 2.4 Adapters and Per-Application Schema Definitions The set of events and commands the Open Design platform can manage is infinite in size. The Open Design platform does not specifically understand the semantics behind a particular event or command. Rather, it provides the transport mechanism to move that event or activation between an adapter and the Open Design Rules Engine. The adapter writer defines the event schema and activation schema particular to an OS application, and the knowledge is shared with the rule author. The platform allows the transport of events and activations between the Rules Engine and adapters. Rule authors understand the capabilities of the particular adapters they are targeting. They understand what events the adapter can issue, and what activations it can accept. The rule author builds rules that act on these events and issue activations based on the command sets of adapters. The rule author typically knows about these schema through the documentation shipped with each adapter. The adapter defines that schema. As the schema is particular to the adapter, the overall schema of the platform is extensible. Only the endpoints (adapters and rules) need to understand the schema. The intermediary pieces, the platform pieces that transport the data around the system, serve as containing wrappers for the data. 3.0 The Open Design Adapter Interface In the Open Design platform, the term "adapter" denotes a piece of code connected to the message bus. An adapter may take many forms: web pages can include an adapter as part of their scripts, a Visual Basic application can implement adapter code directly, ASP pages can include adapters, and so on. The important point is that the adapter is the piece of code that allows messages to be issued to the message bus. Messages can also be routed back to the adapter by the kernel. 3.1 Adapter reguirements Adapters may only adhere to a small set of requirements. An adapter that connects to the message bus has an ID. This ID is represented as a GUID string. Each distinct class of adapter has a unique GUID. Internally, this GUID ('id') is used to route messages to adapters. The set of actions that an adapter can carry out in response to a message is specific to a particular adapter class. In order for the kernel to route messages to an adapter, the adapter provides an object model on which the kernel can dispatch messages. Messages processed by an adapter are method calls on the adapter's object model. This object model, however, need not be provided when the adapter only issues messages. In one embodiment, the programming model used to develop an adapter may be left to the choice of the adapter writer. Any programming language that supports the use of COM objects on Win32 can be used to write an adapter. This includes traditional programming languages like C/C++, Java, and Visual Basic, macro languages like Visual Basic for Applications, and script languages like PERL, JavaScript, and VBScript. 3.2 Basic adapter facilities This piece of code illustrates a very simple hypothetical adapter written in JavaScript. The script itself is hosted within an HTML file: <HTMLXHEADXSCRIPT> var adapter; function CreateAdapter ( ) { adapter = new ActiveXObj ect ("odi .AdptProx") ; adapter.Register("{CD24FC5C-5C36-4b9d-B098-F41F0227CCEE}", "JavaScript Adapter"); } function DestroyAdapte ( ) { adapter.Unregister () ; adapter = 0; } </SCRIPT> </HEAD> <B0DY onLoad="CreateAdapter () ;" onUnload="DestroyAdapter ( ) ; "></BODY> </HTML> This adapter creates itself whenever the HTML browser displays the page and registers with the message bus. Also, when the browser unloads the document, the adapter unregisters itself and goes away. 3.3 Creating the adapter The first step to create an adapter is to create an instance of the adapter proxy object. This is the object that 'sits between' the kernel and the adapter code itself. The following lines of code from the sample creates a new instance of the adapter proxy: var adapter; adapter = new ActiveXObj ect ("odi.AdptProx") ; In Visual Basic, the same code would look like this: Dim adapter as Odi.AdptProx Set adapter = new Odi.AdptProx A C++ developer would use this code: IAdapter * pAdapter = NULL; CoCreateInstance(CLSID_AdptProx, NULL, CLSCTX_INPROC_SERVER, IID_IAdapter, (void **) & pAdapter)); 3.4 Connecting to the Kernel In order for the kernel to recognize an adapter, the adapter registers itself with the kernel. This is accomplished through the Register() call on the adapter proxy object. There are four parameters to Register(); the last two parameters are optional. This code snippet chose to omit the last two parameters: adapter.Register("{CD24FC5C-SC36-4b9d-B098-F41F0227CCEE}", "JavaScript Adapter") ; This call attempts to connect the proxy to the kernel. The first parameter is a well-formed GUID that uniquely identifies this class of adapter. Developers should use the guidgen.exe utility to create unique adapter GUIDs. This parameter will be used in the kernel to identify messages that are generated by the adapter as well as to route messages back to the adapter. The second parameter to Register is a friendly human-readable name for the adapter. This parameter is used for convenience and can be null. If the call to Register succeeds, the adapter has connected to the kernel. At this point, the adapter is ready to issue messages and to receive incoming messages. Whenever an adapter wishes to disconnect from the kernel, it calls Unregister. There are no parameters to Unregister, but the adapter may call Unregister before being destroyed to avoid memory leaks that will not be cleaned up until the host processes exits. If an adapter is already connected to the kernel and attempts to call Register again, Register returns an error. It is invalid to change an adapter's ID, name, activation interface, or Running Object Table ("ROT") behavior while the adapter is registered. The adapter may Unregister itself before calling Register a second time. 3.5 Allowing deferred connections In one embodiment, the kernel has a built-in mechanism that allows adapters to be registered even when the kernel is not running. The fourth parameter to Register is a Boolean value that enables this feature when set to true. If this parameter is omitted, it defaults to true. In the Windows implementation of the Open Design platform, the Running Object Table is used to register adapters; this can be used by programs to "see" what objects are running. When the kernel is starting, it looks at this table for any adapters that might need to be registered. If it finds any, the kernel attempts to connect to the adapters so they can send and respond to messages. When the fourth parameter to Register is true, an entry is placed for the adapter on the Running Object Table. If the parameter is omitted or set to false, no entry is placed on the ROT for the adapter. Certain conditions cause the ROT to become corrupt— when this happens, the internal kernel code that enumerates through the running object table fails. This corruption results from how the ROT works. This behavior implies that when the ROT cannot be enumerated, the OpenDesign kernel will not find adapters that previously have registered on the ROT. It appears that the only condition that can cause ROT corruption is forcibly killing the odikrnl.exe process. If adapters specify that they wish to allow deferred connections by the kernel, it is possible for the adapter to register when the kernel is not running. Whenever the kernel starts, it looks for adapters that allow deferred connections and connect the adapters at that time. The adapter writer can sink the onConnect and onDisconnect events that the kernel generates. When the adapter calls Unregister, the kernel no longer attempts to connect to the adapter if its process is created. 3.6 Register/Unregister return values The following table shows the return values from the adapter proxy as well as the state of the adapter as it pertains to the kernel. 3.7 Issuing messages Adapters issue messages by invoking another method on the proxy object called SendMessage. The following JavaScript executed in the context of our hypothetical adapter HTML page issue a message of the type "hello world" with no parameters: adapter. SendMessage ("hello world') ; In Visual Basic: adapter. SendMessage "hello world' In C++: BSTR bstrType = SysAllocString ("hello world'); PAdapter->SendMessage (bstrType) ; SysFreeString (bstrType) ; If the adapter is not connected to the kernel at the SendMessage is invoked, the SendMessage returns an error. 3.8 Message parameters Many adapters will send a set of parameters with the messages they issue. In one embodiment, this is accomplished using the message parameter object (MsgParams) object. The MsgParams object is another kernel object that is used to pass parameters that are available as criteria in rules. MsgParams is an array object. This is a simple function that the HTML page adapter uses to issue parameterized events: function sendParameterizedMessage (paramO, paraml, param2) { // create a new MsgParams object var msgparams = new ActiveXObject ("Odi .MsgParams") ; // Set the values of the parameters msgparams (0) = paramO; msgparams (1) = paraml; msgparams (2) = param2; // Send the Message with the MsgParams object adapter. SendMessage ("MyMessage", msgparams) ; ) The kernel also supports structured parameters by allowing MsgParams objects to be nested. This means that the adapter writer can build arbitrary structures of parameters and map them in rules. For example: var a = new ActiveXObject ("odi .MsgParams") ; var b = new ActiveXObj ect ( "Odi . MsgParams" ) ; b (0 ) = "fork" ; b ( l ) = "spoon" ; a ( 0 ) = "shaving cream" ; a ( l ) = "straw" ; a (2 ) = b; a (3 ) = "bongo" ; adapter. SendMessage ("complexParams", msgparams) ; 3.9 Receiving messages Adapters in the platform are logical "end-points" for messages on the event (message) bus. Messages can be addressed to adapters if they register any message listeners when they connect. The following illustrates message listeners: function sayHello ( ) { alert ('hi') ; } function addSomeText (text) { document .writeln (text) ; } These are JavaScript function prototypes. In order for the kernel to dispatch activations to the adapter, the adapter registers an object model at the time Register() is called. The following is an example of how this can be done in the sample web page: <HTMLXHEADXSCRIPT> var adapter; var myListeners = new MessageListeners ( ) ; function CreateAdapte () { adapter = new ActiveXObject ("odi .AdptProx") ; adapter.Register("{CD24FC5C-SC36-4b9d-B098-F41F0227CCEE}", "JavaScript Adapter", myListener) ; } function DestroyAdapter ( ) { adapter.Unregister () ; adapter = 0; } function sayHello ( ) { alert ('hi' ) ; } function addSomeText (text) { document .writeln (text) ; } function MessageListeners () { this. sayHello = sayHello; this. addSomeText = addSomeText; } </SCRIPT> </HEAD> <BODY onLoad="CreateAdapter () ;" onUnload="DestroyAdapter ( ) ; "></BODY> </HTML> When this adapter registers, the kernel can append text to the page by issuing messages "addSomeText('hey there from the kernel');" or say hello by issuing "sayHelloQ". Because the object model provided by the adapter can be associated with existing objects in the adapter, HTML pages could register the document object model as the activation interface to couple full control of the DHTML page to the message bus. A Visual Basic application could expose a status bar or notification label in the form as the activation interface. Any object that is available for automation (IDispatch) in the programming environment of the developer's choice can be registered as the activation interface. In one embodiment, the kernel does not support property-get message listeners, but both property-put and methods are supported. 3.10 Determining if an adapter is connected In one embodiment, the kernel process, and therefore the event bus may be halted at any time; the adapter writer may not safely assume that the even (message) bus is available. The adapter proxy object supports a property called AdapterlsConected that returns true when connected to the message bus: if (! adapter .AdapterlsConnected) alert ("oops!') ; else adapter. SendMessage ("hi !' ) ; Another alternative is to use the language's error handling features to catch the error generated by SendMessage: try { adapter. SendMessage ("hi ! ' ) ; } catch (e) { if (! adapter.AdapterlsConnected) { // kernel process is not running } } The adapter writer can use whatever error handling constructs fit the requirements for a particular adapter. 3.11 Receiving adapter proxy events The adapter proxy supports two events that the adapter writer can trap to receive notifications when the message bus becomes available and when it becomes unavailable. These two events are called OnConnect and OnDisconnect. OnConnect is fired by the proxy whenever the message bus becomes available. Likewise, OnDisconnect is fired by the proxy whenever the message bus becomes unavailable. These events are implemented on Windows as COM connection points, so this is an example to "catch" these events in Visual Basic: Dim adapter As AdptProx Λ omitting adapter registration code... Private Sub adapter_θnConnect ( ) Set myStatus = 1 End Sub Private Sub adapter_θnDisconnect ( ) Set myStatus = 0 End Sub If the adapter sends many large messages or implements a heavyweight processing thread-pool, the adapter writer can use these events to manage allocation of resources. 3.12 Processing activation messages In the platform language, messages that are directed from the kernel to adapters are called activations. Because activations are dispatched from the kernel to adapters by way of rules firing in the rules engine, activation messages have additional information associated with them that help the adapter extend functionality in the event (message) bus. This extra information is called the activation context. . Adapters can use the activation context to access application data declared in module rules, data in the message that caused the activation, and information about the module that caused the activation. 3.12.1 The activation context object The adapter proxy object exposes the activation context to the adapter host through a property called ActivCtx. To illustrate the activation context object model and the data it contains, consider the following skeleton of an activation method: function myActivationf) { var yCtx = adapter.ActivCtx; } The activation context object is valid for the lifetime of the activation method call. In the example above, when the activation method exits, the context object is cleaned up by the JavaScript runtime. Also, if the adapter proxy is not currently processing an activation when the host calls ActivCtx, the call returns an error. The adapter writer can choose to keep a reference to the activation context in memory, if required. Each activation creates a new activation context object, so if an adapter keeps a reference to an activation context in memory, the data within the cached activation context will not change as new activations are dispatched from the kernel. With a valid context an adapter can display the ID of the module that caused the activation: alert (myCtx . mod. guid) ; Or, display the path of the module's resources: alert (myCtx . od. path) ; To use data values that were declared as part of the rule we use the Decls collection on the activation context object: var outstring = "'; for (var i = 0; i < myCtx. decls .Count; i++) { outstring += myCtx. decls. data (i) + " "; } // display data declared in rules document.writeln (outstring) ; The Decls object is a standard collection, so it supports enumeration in a for.. each loop, for example: For Each x In myCtx. Decls .Data outstring = outstring & x & "."; Next The activation context also exposes all the fields of the message that were matched by a rule in the kernel to cause the activation. This allows the adapter writer to "peer" in to the event data. 3.12.2 Determining if the kernel is processing an activation Some adapters may exist as code integrated directly with an application. In this situation, it may be useful to programmatically determine if the kernel is the invoking caller on adapter code. An adapter might be implemented as a window that does not allow the user to close it — only the kernel. If the adapter uses the same function to process this message as it does to handle the onClose event generated by user interaction, it needs to tell whether it is processing an activation message or not. To do this, the adapter code can query another property on the adapter proxy object called ProcessingActivation: function onClose ( ) { if (! adapter. ProcessingActivation) return; else this. destroy () ; } 3.12.3 Module messages Some adapters might allow messages to generate new messages. For example, a script host adapter may expose an activation method called SendMessage: function SendMessage (type) { adapter. SendMessage (type) ; } By allowing an activation to issue a message, there is a chance that some activation could use the same message type as the adapter uses for a message. This is a case where the second message is generated in the context of a kernel module. For cases like this, in which "derivative" messages are issued on behalf of the module invoking the adapter, instead of using SendMessage, the adapter may call another method on the proxy called SendModuleMessage. function SendMessage (type) { adapter. SendModuleMessage (type) ; } Module messages "look" like other messages. The only difference is that messages issued from SendModuleMessage have the activating module's ID as the message source instead of the adapter's ID. SendMessage and SendModuleMessage have the same signature, which means that the adapter could send a module message with parameters just as it would using SendMessage for a non-module message. SendModuleMessage has an optional third parameter that can be used to specify a module ID to use in the generated message. This can be useful for adapters that, say, queue activations to be processed asynchronously. When the activation is ready to be processed, the adapter can make its SendModuleMessage call with the third parameter specified: // cache the activation context for this // asynchronous call var ctx = adapter .ActivCtx, ■ // queue the activation // ... back at the ranch, we can // send a module message much // later by specifying the module // ID in SendModuleMessage var modulelD = ctx.mod. guid; adapter. SendModuleMessage (type, null, modulelD); If a call is made to SendModuleMessage outside the context of an activation and no module ID is specified, it returns an error. 3.13 Channel messages In one embodiment, channel messages allow constrained inter-module communication. The constraint is that only the modules that establish the channel will be allowed to 'see' channel messages on the event bus; the adapter proxy supports a SendChannelMessage method. This method can be used by adapters to issue messages over message channels. 3.14 Reguest-Response messages In one embodiment, the adapter proxy supports request-response messages. Request-response is a message exchange pattern in which a specific class of message is issued (MC_REQUEST) and a response is expected. The kernel instruments request-response rules such that only rules that match a given request will catch responses to those requests. Request messages can be issued using the SendRequestMessage adapter proxy method. Likewise, response messages can be issued by using the SendResponseMessage method. The way this works is by storing and matching against message correlation ids. Each message generated in the system has its own quasi- unique id. To match response messages to a given request, the response id field of the message is given the correlation id of the request message. The adapter proxy does this automatically whenever SendResponseMessage is called during the processing of an activation. If the adapter is not processing an activation, or if the adapter wishes to override the response id with the id of another request message, an optional parameter can be specified in SendResponseMessage to accomplish this. 3.15 Adapter drivers The adapter driver is a kernel module, a rules file that routes node messages to the adapter. In general, when application writers wish to communicate with adapters, they do not issue messages directly to the adapter. Instead, they issue messages according to a logical programming interface defined by the driver. A good illustration of this relationship is the internal kernel file system adapter and the file commands API. The file system adapter is a low-level piece of integrated adapter code in the kernel that carries out disk operations. It moves files and folders, copies them, searches for files, and so on. But applications generally do not communicate with the file adapter. They issue messages against a kernel service that implements the routing rules that translate an API request: var myParams = new ActiveXObject ("Odi .MsgParams' ) ; myParams. Initialize (2) ; myParams (0) = "source.txt'; myParams (1) = "destination.txt'; adapter. SendMessage ("CopyFile' , myParams) ; This call invokes the file adapter activations. Though the routing in the file system adapter example is simple, a driver may implement much more sophisticated routing rules. Such a driver might be required for a complex adapter, such as an email queue adapter or a payment transaction adapter. Drivers also allow adapters to "version" API's or even incrementally add features without changing the compiled adapter code. 3.16 Adapter instance handling Adapter classes are distinguished from each other by their IDs. Duplicate instances of the same adapter are distinguished from one another by a run-time registration cookie and an adapter instance ID when it connects to the kernel. The kernel issues a message upon registration called "AdapterRegistered" that module writers (and adapter driver writers) use to track a particular adapter instance. All adapter messages issued in the kernel are assigned a source and a source instance "cookie." The message source is either the adapter ID or the module ID (for module events), and the source-cookie is set to the registration instance value described above. The adapter proxy supports a read-only property "Srclnstance" that can be called to determine the adapter's source instance value when it is registered with the kernel. It may be used as follows: var mylnstance = adapter. Srclnstance; alert ("my source instance is " + mylnstance); In the case that the kernel is activating against an adapter that has the same ID as the event source, the kernel automatically routes the activation to the instance of the adapter that issued the original message. If the kernel is activating against an adapter that is not the adapter that cause the original message, the kernel directs the activation to the first adapter registered with that particular ID. The module writer can override this behavior by specifying the destination cookie as part of the activation in a rule. Because the instance cookie is not known until run-time, the module writer may determine which instance cookie to use. The module can identify adapter cookies for adapters currently participating in a message exchange by caching the source cookie in a tree-node. In general, it should not be necessary for module writers to implement custom instance handling, but the feature is available for special circumstances. 3.17 Kernel-managed adapters In one embodiment, some adapters can be loaded and unloaded from rules. This is supported through the kernel activations LoadAdapter and UnloadAdapter. In order for adapters to implement this functionality, they support the lAdapterControl interface. The adapter control interface has two member methods: Startup and Shutdown. Whenever the kernel loads an adapter through the LoadAdapter, it creates a new adapter control process, passing in the adapter's CLSID (guid) or ProglD as a parameter. The process then attempts, on Windows, to create the adapter using standard COM CoCreatelnstance calls. If this call succeeds, the process then calls lAdapterControl:: Startup on the newly created object. At this point, the adapter registers itself with the kernel. Likewise, whenever an UnloadAdapter activation is called, the kernel calls lAdapterControl:: Shutdown on the object. The process then exits. In general, it is not usually necessary to implement an adapter using lAdapterControl. 3.18 Adapter Proxy API This section describes each method and property exposed by the adapter proxy in one embodiment. This section uses a form of the methods and properties that is taken directly from an IDL file. For non-C++, the following explanation of IDL syntax will help with the interpretation the API described below. First, interface calls return an HRESULT. This is familiar for C++ Windows COM developers, but not for VB or script developers. With VB or script, a non-success HRESULT results in a run-time exception can be trapped with try/catch in JavaScript and On Error., in VB. Successful calls do not throw a run-time exception. Parameters that are listed here with [in] mean they are an input parameter. Parameters listed with [out] denote an output parameter. Some property-get methods have [out, retval] parameters. These parameters will be the return value of the call from VB or script. Some parameters are listed as [optional]. This means that the parameters may be omitted from the call. Other parameters list a [defaultvalue(... )]. If omitted, these parameters will have the default value listed within the parentheses in the call. Last, in script and VB, most of the time variables are a VARIANT type. This means that the variable may be a valid object pointer, integer value, string, and so on. Most of the time, COM converts the values if the variable is of the wrong type. Sometimes, the types cannot be converted, and this will throw a non-success HRESULT (resulting in a run-time error). 3.18.1 lAdapter lAdapter is an interface for the adapter proxy object. It supports sending message methods from an adapter. Methods HRESULT Register ( [in] BSTR bstrlD, [in] BSTR bstrName, [in, optional] VARIANT vtActivationlnterface, [in, optional] VARIANT vtAllowDeferredConnection) This method registers the adapter with the Kernel. The adapter's ID may be a GUID, formatted as a string. On Windows Guidgen.exe or uuidgen.exe may be used to create new GUIDs. The second parameter is a friendly name for the adapter. This parameter is not used for addressing — only for human-readable adapter names. It may be null. The third parameter is an optional activation interface for the adapter. It also may be null. The activation interface may support IDispatch — VB objects and JavaScript objects both support IDispatch. The last parameter is a true/false variant that describes whether or not the adapter wishes to allow deferred connections. HRESULT Unregister O This method disconnects an adapter from the kernel. HRESULT SendMessage ( [in] BSTR bstrType, [in, optional] VARIANT vtMsgParams ) This method sends a broadcast message. The type of the message is the first parameter (string), and a message parameter object (see IMsgParams) may be passed as the optional second object. HRESULT SendModuleMessage ( [in] BSTR bstrType, [in, optional] VARIANT vtMsgParams, [in, defaultvalue (-1) ] LONG llnstID) This method sends a module message. The type of the message is the first parameter (string), and a message parameter object may be passed as the optional second argument. The last argument is the instance id of the module to target for the module message. When calling SendModuleMessage while processing an activation, this parameter may be omitted. The adapter proxy automatically uses the module id from the activation context in this case. If called outside of processing an activation, the instance id parameter may be specified. HRESULT SendCorrelatedMessage ( [in] BSTR bstrType, [in, optional] VARIANT vtMsgParams, [out, retval] BSTR * pbstrCID) This method sends a broadcast message. This method is identical to SendMessage, only that the correlation ID of the message generated is passed back to the caller. HRESULT SendRequestMessage ( [in] BSTR bstrType, [in, optional] VARIANT vtMsgParams) This method sends a request message. The type of the message is the first parameter (string), and a message parameter object may be passed as the optional second argument. HRESULT SendResponseMessage ( [in] BSTR bstrType, [in, optional] VARIANT vtMsgParams, [in, defaultvalue (NULL) ] BSTR bstrResponselD) This method sends a response message. The type of the message is the first parameter (string), and a message parameter object may be passed as the optional second argument. The last argument is the response ID of the message that will be generated. When calling SendResponseMessage while processing an activation, this parameter may be omitted. The adapter proxy automatically uses the correlation ID from the message in the activation context in this case. If called outside of processing an activation, the response ID parameter may be specified. HRESULT SendChannelMessage ( [in] LONG llnstID, [in] BSTR bstrChannel, [in] BSTR bstrType, [in, optional] VARIANT vtMsgParams) This method sends a channel message. The instance ID of the targeted module is the first parameter. The second parameter (string) is the channel name over which the message will be issued. The third parameter is the message type to issue, and the last parameter is an optional message parameter object. Get Properties HRESULT AdapterlsConnectedf [out, retval] BOOL * pfConnected) This method returns true if the adapter is connected to the kernel and returns false if the adapter is not connected to the kernel. HRESULT ProcessingActivation ( [out, retval] BOOL * pfProcessingActiv) This method returns true if the adapter is processing an activation and returns false if the adapter is not processing an activation. HRESULT ActivCtx ( [out, retval] IDispatch ** ppDispActivCtx) This method returns the activation context for the current activation. If called while the adapter is not processing an activation, a run-time error is generated. HRESULT Srclnstance ( [out, retval] LONG * pllnstance) Return the source instance of the adapter (See Adapter instance handling). 3.18.2 lAdapterControl The adapter control interface allows the kernel to manage the lifetime of the adapter through the kernel LoadAdapter and UnloadAdapter activations. Methods HRESULT Startup () The kernel calls Startup whenever the adapter process is created. In the adapter's implementation of Startup, Register should be called to register the adapter with the kernel. HRESULT Shutdow () The kernel calls Shutdown whenever the adapter process is being terminated. In the adapter's implementation of Shutdown, Unregister should be called to unregister the adapter from the kernel. 3.18.3 IMsoParams IMsgParams is the interface exposed by the message parameter object. The message parameter object is a collection of parameters that are attached to a message. Adapters use the message parameter object to issue messages with parameters to the kernel through SendMessage and to access message parameters through the activation context when processing an activation. Methods HRESULT initialize ( [in, defaultvalue (0) ] long lParams) This method initializes the message parameter object. An optional size may be passed as the sole parameter. It is not required to call initialize in order to use the message parameter object. HRESULT clear () This method clears the message parameter object, releasing all parameters. Sets the parameter count to zero. This method is used when the same message parameter object is used in more than one SendMessage call. Get Properties HRESULT paramCount ( [out, retval] long * plParams ) This method returns the number of parameters in the message parameter object. HRESULT params ( [in] long llndex, [out, retval] VARIANT * pVarVal ) This method returns the parameter value at the given index. The first parameter is the zero-based index of the parameter to return. Put Properties HRESULT params ([in] long llndex, [in] VARIANT VarVal) This method assigns a parameter value to the parameter object. The first parameter is the zero-based index of the parameter to set. 3.18.4 lActivCtx The activation context is an object that provides meta-data associated with the current activation to the adapter. The activation context object model provides hooks to determine the module that caused the activation to occur, the message that caused the activation to occur, any session declarations in the rule that fired the activation, and any persisted declarations in the rule that fired the activation. Get Properties HRESULT mod ([out, retval] IModule ** ppModule) This method returns the module object for the activation. HRESULT msg([out, retval] IMsg ** ppMsg) This method returns the message object for the activation. HRESULT decls ([out, retval] IDecls ** ppDecls) This method returns the session declarations in the rule for the activation. HRESULT pdecls([out, retval] IDecls ** ppPDecls) This method returns the persisted declarations in the rule for the activation. 3.18.5 IModule The module interface is one child of the activation context. The module interface provides information about the module that caused an activation to occur. Get Properties HRESULT guid ([out, retval] BSTR * pbstrlD) This method returns the module's GUID (string). Not supported in activations. HRESULT name ([out, retval] BSTR * pbstrName) This method returns the module's name. Not supported in activations. HRESULT desc([out, retval] BSTR * pbstrDesc) This method returns the module's description. Not supported in activations. HRESULT fileName( [out, retval] BSTR * pbstrFileName) This method returns the module's filename. Not supported in activations. HRESULT path ( [out, retval] BSTR * pbstrPath) This method returns the module path for the module. HRESULT ring ( [out, retval] int * pnRing) This method returns the ring-level of the module. Not supported in activations. HRESULT instanceID( [out, retval] long * plInstID) This method returns the instance ID of the module that caused the activation. HRESULT isLibrary ( [out, retval] unsigned int * pnLib) This method returns true if the module is a library, false if not. Not supported in activations. 3.18.6 IMsg The message interface exposes the message that generated the activation through activation context. Get Properties HRESULT src ( [out, retval] BSTR * pbstrSrc) This method returns the source of the adapter that generated the message. HRESULT timestamp ( [out, retval] DATE * pdtTimestamp) This method returns the date and time the message was created (local timezone) HRESULT srclnstance ( [out, retval] LONG * plSrcInstance) This method returns the source instance of the adapter that generated the message. HRESULT type ( [out, retval] BSTR * pbstrType) This method returns the message type. HRESULT correlationID ( [out, retval] BSTR * pbstrCID) This method returns the correlation id for the message. HRESULT modulelD ( [out, retval] BSTR * pbstrMID) This method returns the module id for the message. This is only valid for messages having a class value of MC_MODULE or MC_CHANNEL. HRESULT responseID ( [out, retval] BSTR * pbstrRID) This method returns the response id of the message. This is only valid for messages having a class value of MC_RESPONSE. HRESULT channel ([out, retval] BSTR * pbstrChannel) This method returns the channel name for the message. This is only valid for messages having a class value of MC_CHANNEL. HRESULT els ([out, retval] MSG_CLASS * pmc) This method returns the class value of the message (see MSG_CLASS enumeration). HRESULT params ([in] long llndex, [out, retval] VARIANT * pVarVal) This method is used to access a parameter of the message. The first parameter to this call is the zero-based indexed of the message parameter to access. Identical to using params in the message params object. HRESULT paramCount ( [out, retval] long * plParams) This method returns the number of parameters in the message. 3.18.7 IDecls Declarations and Persisted Declarations interface. Declarations and persisted declarations are knowledge declared in a rule that is passed through the activation context object. It is used to allow adapters access to the declarations in rules. The Declarations interface is a standard Collection interface. Collection interfaces are those that allow you to use For each.. Next constructs. They also support indexed access. Methods HRESULT _NewEnu ( [out, retval] IUnknown ** ppunk) This method creates a new enumerator. It is usually not required to use this method. Get Properties HRESULT count ([out, retval] LONG * pDeclsCount) This method returns the number of declarations or persisted declarations. HRESULT data ([in] VARIANT vtDecls, [out, retval] VARIANT * pvtDeclsData) This method returns the value of a declaration or a persisted declaration. The first parameter can be a zero-based index. HRESULT item([in] VARIANT vtDecls, [out, retval] VARIANT * pvtDeclsData) This method returns the value of a declaration or a persisted declaration. The first parameter can be a zero-based index. Identical to data(). 3.18.8 MSG CLASS The message class enumeration relates numeric values to each class of message that may be generated. For example, SendModuleMessage results in a message that has a message class value of 32. MC UNDEFINED = 0 MC BROADCAST = 1 MC REQUEST = 2 MC RESPONSE = 4 MC PROXY = 8 MC CHANNEL = 16 MC_MODULE 32 = Network Layer The network layer is a component of the system, serving as the extension of the event (or communication) bus between devices on the network. In one embodiment, user-level modules cannot directly access the network layer - they can only talk to the network in terms of logical names. The Open Design system does not have an explicit network adapter, but rather exposes select network functionality through the kernel adapter. As with all adapters, the network adapter communicates with the system by sending messages to the Event Bus, and receiving messages from the Rules Engine via the Event Bus. The network adapter connects the router on each node to one or more peers nodes on the network. In one embodiment, adapters communicate with the Event Bus through the Open Design Message schema. Each adapter presents its own message schema that is understood by the rules written against it in the Rules Engine. The Network Layer is somewhat special in that it is a system-provided adapter that converses primarily with system-provided rules. First, the Network Layer is like any other adapter, sending events to the Event Bus containing Network Layer-specific schema. These events can be thought of as system events, generated directly by the Network Layer; system-supplied rules are compiled in the Rules Engine to deal with them. These sorts of events support the Open Design platform semantics such as "The network is on/offline," "The network bandwidth rate is XXX," an so on. These events are somewhat rare, but provide system-specific activities and UI presentation. Second, the Network Layer can receive a packet of data from across the network. This packet is wrapped in a Network Layer-specific schema and it is the job of the Network Layer to unwrap that packet into an Open Design Message structure. This Open Design Event is a wrapper for a remote message specified by a rule that had fired on the outbound machine. A system-provided rule on the receiving machine handles the event and uses the message header and payload to either forward the message on to another peer, or to deliver the message to the local event bus which in turn delivers the message to the appropriate adapter or destination application. This action taken (activation) is precisely what was specified on the originating machine, except that now it is instantiated on the receiving machine. Figure 8 shows a high-level flow of control between the Network Layer, the Event Bus, the Rules Engine, and private calls between instances of the Network Layer across a network. 4.1 Network Layer to Event Bus As with any other adapter, the Network Layer posts Open Design Events to the Event Bus as messages. 4.2 Event Bus to Rules Engine The Event Bus communicates with the Rules Engine, supplying input events as messages. 4.3 Rules Engine to Network Layer The Rules Engine fires activations embedded in messages to the Network Layer. 4.4 Network Layer to Network Layer The Network Layer communicates with a Network Layer across a network through the Open Design Network Services. These calls (which may pass through a proxy server layer) are represented by the dotted lines of Figure 8. Implied in this figure is that the data received by one Network Layer from another is convertible to an Open Design Message. Figure 9 is a block diagram illustrating the addressing layers of an Open Design Message. The event element below is the internal representation of the message as it appears on the local Event Bus. In one embodiment, the specific elements of any of the message headers, including tag names and values, may change. The transport, physical and logical headers that precede the event body provide inter node and hop-by-hop addressing information and instructions. These additional addressing elements are the wrappers for the event on the wire. Figure 10 illustrates a more complete diagram that includes all of the architecture elements involved in node-to-node message exchanges between applications hosted on nodes A and B. Open Design Messages are the internal message, unwrapped, from the on-the-wire format illustrated above. A schema for Event Bus messages follows. 4.5 Message Schema <?xml version="l.0"?> <event> <! — timestamp conforms to ISO-8601 —> <! YYYY-MM-DDTHH:MM:SS,mmm format —> <! — timestamps are generated automatically —> <! — when an event is created —> <timestamp>2000-02-09Tll:30:29,982</timestamp> <! — event-ctx denotes the event's source. —> <event-ctx> <! — this guid is generated by the adapter is instantiated. every event generated by this adapter will have the same guid, thus allowing us to follow the trail events generated by a given instance of a given adapter. —> <msg-src> FC684700-45A8-4692-AA94-0F47E06984DE </msg-src> <sreins ance>123</sreinstance> </event-ctx> <! — event type. —> <! — rules may be written against event types—> <! — by the same name with the rules engine. —> <event-type>visits</event-type> <! — optional parameter list —> <! — parameter lists may be nested —> <params> <param>"http: //www. amazon. com/"</param> <param>123</param> </params> </event> 5.0 Rules Engine Rules are a way of expressing logic. You write a condition, the left hand side (LHS), as a set of dependencies; and you also write actions, the right hand side (RHS), which may trigger conditions of other rules. For example: RULE 1 : if [person. age > 18] then [assert person. adult TRUE] RULE 42: if [person.adult = TRUE] AND [person. isAmember = TRUE] then [let-them-in] Rule 1 and 42 could be authored by separate developers or at separate times, and the system will "link" them together by their shared "person. adult" criterion. This ability to extend the rules of others is called composibility. A rule engine will link together rules in such a way as to determine what rules need to fire automatically very efficiently. In fact, a good rule engine scales better than linearly to the number of rules, so a system with 100,000 rules runs nearly as fast as a system with 2,000 rules. The Open Design event bus is an application message communication medium based on a rules engine for message dispatching, and application and system supplied modules that provide the rules by which message dispatching and forwarding within and between nodes is accomplished. 5.1 Rules advantages • Declarative instead of procedural • Allows (optional) optimization, as the rule specifies what to do, not how to do it. In one embodiment, a data-driven optimization called RETE is used • Allows analysis of rule sets (e.g., for safety or security), because rules explicitly state their dependencies and actions. In one embodiment, a router policy filter is based on rule conditions; • Enables general tools to build rules, e.g. Open Design an XML to rule translator, statistics, etc.; • Invertable, e.g. you can go from actions back to conditions as well as conditions to actions; • Allows rule systems to be inter-converted • Allows rules to be composable, that is, rules can be added and removed by different authors and the engine can maintain them. • Allows automatic detection of rule conflicts. • Allows different "authors" to create and see their own "views" of an underlying system without needing to look at all the events and conditions. • Reliable. Most declarative engines are far more reliable than procedural ones, since they have a simple mathematics underneath. • Powerful logic system • More powerful than relational calculus, in fact, it is a superset of SQL's expressive power • Puts the joins into an incrementally-updated data structure 5.2 Rules for routing Traditional routing algorithms are not programmable. Instead, they alter their behavior based on data shared "peer to peer." Put another way, a traditional routing algorithm is expressible in a fixed rule, with fixed LHS conditions. For example, the set of criteria inspected to make a routing decision will never change in BGP (Border Gateway Protocol). In one embodiment, rules can be used to add to criteria without changing the protocol globally. For example: OLD ROUTING RULE (pseudo-code) if [peer-router. status = overload] then [peer-router = find-least-loaded- router] // assumes the peer-router is not least-loaded NEW ROUTING RULE (pseudo-code) if [peer-router. status = overload] then if [peer-router. packet.f lag = high-priority] then [peer-router = least-loaded-router] else [peer-router = find-second-to-least-loaded-router] If several routers in the network do not update to the new routing rule, the global system continues to work. The routers that do update to the new routing rule can now differentiate between high priority and lower priority traffic. Traditional routers do not use rules; they use procedural code. Since the algorithm is infrequently updated, and new conditions and terms are not added on, and different people are not adding algorithms, procedural code works. In one embodiment, the Open Design system allows third parties to add new conditions to routing, such as application-specific criteria (customer number, time of day, order type). Traditional models would ask them to write procedural code, with the inherent loss of rules benefits of: safety, optimization, composibility, invertability, conflict checking, and so on. • Rules are used to add and modify criteria used for routing o As new schemas for information that should impact routing are added, that information is put into a "database" that the rules can access • Rules can be distributed o If rules are moved from one node to another, we can check them to see what data they access and what data they assert. This allows determination of some level of safety checking. • Rules can interact with programs o Unlike routing tables, rules can interact with programs by specifying program data or object attributes on their LHS or RHS. This allows a rule to "call" a program, or a program to cause a rule to fire. This integration with programs while keeping some level of safety and scalability is a feature beyond routing tables or traditional "layer 7" routing. o Interaction may occur at the protocol and/or XML level, i.e. programs emit or consume information using XML. • Rules are used to create distributed algorithms o When a directory of users is distributed, rules can be used to express when the directory should be moved around. 5.3 Rete Rete is an algorithm used by rule engines. There are two parts to the Rete algorithm: Expressions are not re-evaluated over and over since the value of a given expression is not likely to change from iteration to iteration. For example: if ((user.athlete is true) and (event. params. page is www.espn.com)) { // activations . . . } New facts are only compared against rules that are likely to be relevant. Rules are written against a "working memory" - the set of facts known about the world. In one embodiment, the working memory is made up of session and persisted data created by modules and the event-stream. A rule is the bottom-most node in an expression tree. As soon as there is more than one rule and common expressions among the rules, another optimization is to have a single expression reside in multiple expression trees. When an expression fires in the Rete, the firing percolates down until it reaches a node that cannot be satisfied. 5.4 Rules Language In one embodiment, platform language is an XML schema coupled with additional semantics imposed upon the contents of certain elements within that schema. (This means that <expr>msg.params(2)</expr> is interpreted differently than <expr>"Hello, world. "</expr>.) A program written in this language is called a "module." The module is the unit of programmability for the platform. This section is organized as follows. First, the Backus-Naur Form (BNF) of the grammar of the language is described. Next, exceptions to and clarifications of the BNF are described. Last, the use of expressions in the language is described - this will include the intrinsic symbols that are available, the types of literals that are permitted, and the set of functions that can be invoked at run-time. 5.4.1 The BNF The BNF is interpreted as follows: The start symbol of the grammar is denoted by "START". The productions for a given symbol are terminated by ";". Multiple productions for a given symbol are separated by "[". Symbols that are followed by "?" may appear zero or one times. Symbols that are followed by "*" may appear zero or more times. Symbols that are followed by "+" may appear one or more times. The symbols within a given production may appear in the order specified. START : := module module ::= app-properties app-events? ruleset? app-properties ::= app-na e app-uuid app-ver? app-desc? app-events ::= app-on-enable? app-on-disable? app-on-1aunch? ruleset : := rule* request-response* actions : := activ* tn-create* tn-create-with-set* tn-set* tn-destroy* ptn-create* ptn-create-with-set* ptn-set* ptn-destroy* ; activ : := activ-recpt-reqd? activ-dest activ-dest-instance? activ-cmd activ-params? activ-cmd : := expr? activ-dest : := ; activ-dest-instance : : = ; activ-param : : = activ-params | expr? activ-params : : = activ-param÷ ; activ-recpt-reqd : : = ; app-desc : : = ; app-na e : : = ; app-on-disable : := actions ; app-on-enable ::= actions ; app-on-launch ::= actions ; app-uuid : : = ; app-ver : : = ; decls ::= decls-data? ; decls-data : := decls-datum decls-datum : := decls-datum-name decls-datum-value? decls-datum-exist? ; decls-datum-exist ::= 'true' | 'false' ; decls-datum-name ::= expr? ; decls-datum-value ::= expr? expr : := func | 'msg. channel ' | 'msg. els' | 'msg. correlationID' | 'msg.params ( ' non-negative integer ')' | 'msg. responselD 1 | 'msg.src' | 'msg. srclnstance' | 'msg. type' | 'decls. data( ' non-negative integer ')' | 'pdecls.data ( ' non-negative integer ') ' | 'mod.desc' | 'mod. guid' | 'mod.instancelD' I ' od.name ' | 'mod.path' | 'mod.ver' | ' true ' I 'false' I 'null' I string \ integer | float func : := func-name expr* func-name : : = ' and' 'or' I 'not' I 'gf I 'It' I 'eq' I 'neq' | 'gte' I 'lte' I ' string-append' | ' string-compare ' | ' string-icompare ' | ' string-contains ' I ' string-icontains ' I 'escape-delimiter' I 'plus' I 'minus' | 'multiply' | ' divide ' msg-class : := 'broadcast' I 'request' | 'response' | 'proxy' I ' channel ' | 'module' msg-ctx ::= msg-class? msg-src? msg-type msg-src ::= expr? msg-type ::= expr? pdecls : := pdecls-data? ; pdecls-data : := pdecls-datuπri- pdecls-datum : := pdecls-datum-name pdecls-datum-value? pdecls-datum-exist? pdecls-datum-exist : := 'true' | 'false' pdecls-datum-name ::= expr? ; pdecls-datum-value : := expr? ptn-create : := ptn-na e ptn-value? ptn-create-with-set : := ptn-name ptn-value ptn-destroy ::= ptn-name ptn-name ::= expr? ptn-set ::= ptn-name ptn-value ptn-value : : = expr? request : : = msg-ctx decls? pdecls? expr? actions ; request-response : : = request response response : : = decls? pdecls? expr? actions rule : : = msg-ctx decls? pdecls? expr? actions tn-create : : = tn-name tn- value? tn-create-with-set : : = tn-name tn-value tn-destroy : : = tn-name tn-name ::= expr? tn-set : := tn-name tn-value tn-value : := expr? 5.4.2 Notes on the BNF In one embodiment, there are constructs that are permitted in the language that are either not described in the BNF or that may require further explanation than the production rule that is provided for the construct. 5.4.2.1 White Space Leading and trailing white space within XML elements may be trimmed. In other words, <activ-dest>foo</activ-dest> is equivalent to <activ- dest> foo </activ-dest>. 5.4.2.2 Expressions There are several elements in the language whose productions include optional <expr> elements; that is, the production is of a form similar to "::= expr? ;." Examples include activ-cmd, decls-datum-name, and tn-value. For these elements, in the absence of the child <expr> XML element, the contents of the element should be treated as if they were surrounded by "<expr>" and "</expr>." This is done for programmer convenience to permit code such as "<tn-value>7</tn-value>" to be written rather than the more verbose "<tn-value><expr>7</expr></tn-value>". The following elements are the exceptions to this rule: activ-param: may contain activ-params, or expr, or simple XML character data. rule, request, and response: omitting expr simply means that the rule does not include additional tests as part of its left-hand side (LHS). 5.4.2.3 Terminal Nodes There are elements in the language that are terminal nodes in the grammar; that is, the production is of the form "::= ;." This section explains how to interpret the contents of these elements. In one embodiment of the platform language and the translation engine, the existence of a symbol table in the translation engine will permit all terminal nodes to resolve to "expr?" activ-dest: No special processing or interpretation is done on the XML character data content of this element. activ-dest-instance: The XML character data of the element may conform to the rules for the content of <expr>; that is, the data may parse as a number, string, or symbol. activ-recpt-reqd: The content of the element is ignored - the presence or absence of the element is what is important. app-desc and app-name: The XML character data content of the element is escaped for use within the inference engine as a valid string. app-uuid: All lower-case letters are converted to their upper-case equivalent. app-ver: No special processing or interpretation is done on the XML character data content of this element. 5.4.2.4 Expressions Expressions form a dynamic part of the platform language, where the run-time substitution of symbols and the authoring-time use of literals combine to affect the behavior of the module. The module translation engine uses regular expressions to determine at translation time the type of each expression. 5.4.2.5 Literals The language supports string literals (e.g., "Hello, world."), decimal and hexadecimal integer literals (e.g., 7 and Oxa), and floating-point literals (e.g., 3.14). CDATA sections in expressions are interpreted as string literals. 5.4.2.6 Symbols The language also supports a number of specially interpreted symbols for accessing the values of the fields of messages and declarations at runtime, for accessing properties of the current module, and for notational convenience. In one embodiment of the platform language and the translation engine, user-defined symbols are supported. The supported symbols are: msg . params {non-negative integer) msg . s re msg. srclnstance msg. correlationID msg. type msg. responselD msg. channel msg. els decls .data (non-negative integer) pdecls .data (non-negative integer) mod. guid mod. name mod. ver mod. desc mod. path mod. instancelD true false null 5.4.2.7 Functions The language supports expressions that are function-valued. That is, the "func" production of "expr" is used rather than the literal or symbolic productions. Functions have a name and an argument list. The argument list is itself a set of expressions - it is in this way that the grammar permits arbitrarily complex expressions. The platform language supports the functions below. Each function is listed with the number of arguments that it expects and a short description. In one embodiment of the platform language and the translation engine, expressions are typed, functions are typed, and functions are typed return- values. and Arguments: at least one. Description: evaluates to true iff all arguments evaluate to true. or Arguments: at least one. Description: evaluates to true if any of the arguments evaluates to true. not Arguments: exactly one. Description: evaluates to true if the argument evaluates to false. gt Arguments: exactly two. Description : evaluates to true if the first argument is greater than the second. It Arguments: exactly two. Description: evaluates to true if the first argument is less than the second. eq Arguments: exactly two. Description: evaluates to true if the first argument is equal to the second. neq Arguments: exactly two. Description: evaluates to true if the first argument is not equal to the second. gte Arguments: exactly two. Description: evaluates to true if the first argument is greater than or equal to the second. lte Arguments: exactly two. Description: evaluates to true if the first argument is less than or equal to the second. string- append Arguments: at least two. Description: evaluates to the string that results from concatenating together all of the arguments. string- compare Arguments: exactly two. Description: evaluates to true if the first argument is lexically equivalent to the second. string-i compare Arguments: exactly two. Description: evaluates to true if the first argument is lexically equivalent to the second, ignoring case. string- contains Arguments: exactly two. Description: evaluates to true if the second argument appears somewhere within the first. string-icontains Arguments: exactly two. Description: evaluates to true if the second argument appears somewhere within the first, ignoring case. escape-delimiter Arguments: exactly one. Description: evaluates to the string that results from replacing all occurrences of "/" with "%2F." This is done to allow tree nodes that contain "/" to be created. plus Arguments: exactly two. Description: evaluates to the result of adding the first argument to the second. minus Arguments: exactly two. Description: evaluates to the result of subtracting the second argument from the first. multiply Arguments: exactly two. Description: evaluates to the result of multiplying the first argument with the second. divide Arguments: exactly two. Description: evaluates to the result of dividing the first argument by the second. 5.4.3 The Module Translation Engine The module translation engine is a sequence of transforms that each take the output of the previous transform as input, perform some type of processing, and hand the output to the next transform in the sequence. In the general case, this pipeline of transforms generates code that can be loaded and run by the platform. However, in some cases where modules only need to be validated, and thus, the code-generation transform is omitted, for example. This section is organized as follows. First, the transform pipeline framework is described. Next, each of the transforms is described. Last, the command-line driver is described as an example application of the translation engine. 5.4.3.1 The Transformation Pipeline Framework The module translation engine permits applications to be built on top of it that create sequences of transforms and invoke them one-by-one for a given input. To facilitate this, there is an abstract base transform class from which all other transforms will derive. The base will expose the following (virtual) methods: int Transform ([in] buffer, [out] buffer); int GetName([out] buffer); Given this, code of the following form can be written: #define NUMJTRANSFORMS 3 CTransformBase * apTransforms [NUMJTRANSFORMS] ; apTransforms [0] = new CReadTransform; apTransforms [1] = new CTranslateTransform; apTransforms [2] = new C riteTransform; out = in; for (int i=0; i < NUMJTRANS ORMS; i++) { in = out; apTransform[i] ->Transform(in, &out) ; } 5.4.3.2 Transforms This section covers each of the transforms in the module translation engine. ReadFile This transform takes a filename as input and returns the contents of the file as output. Preprocessor This transform takes a buffer of well-formed XML as input, executes pre-processing directives found in the buffer, and returns the result of having executed on these directives as the output. In the following a description of each of the pre-processing directives supported by the processor and the manner in which they are handled is provided. < preprocessors do nothing. <define>: this tag may include a "symbol" attribute. When it is encountered, add the symbol that is specified to the symbol table for the pre-processor for the current transformation invocation. <ifdef>: this tag may include a "symbol" attribute. If the specified symbol exists in the symbol table, the contents of the opening and closing <ifdef> tags are included in the output buffer. If not, the contents are omitted. If the <ifdef> tag is within an enclosing <ifdef> or <ifndef> directive that has switched off output, then output remains off. <ifndef>: this tag may include a "symbol" attribute. If the specified symbol exists in the symbol table, the contents of the open and closing <ifndef> tags are omitted from the output buffer. If not, the contents are included. If the <ifndef> tag is within an enclosing <ifdef> or <ifndef> directive that has switched off output, then output remains off. <include>: this tag may include a "file" attribute. The contents of the file (".ri") are placed in-line in place of the include directive. The maximum inclusion depth that will be supported is fifty. The pre-processor directives are omitted from the output buffer that is returned. As a general rule, these directives are semantically identical to those found in the C pre-processor. Validate This transform takes a buffer of well-formed XML as input, validates it according to the platform language grammar, and returns the buffer unchanged as its output if all is well. An error is returned if the buffer is not well-formed XML or if it invalid with respect to the platform language grammar. Translate This transform takes a buffer of well-formed XML as input, translates it into Eclipse code, and returns the buffer of Eclipse code as output. WriteFile This transform takes an arbitrary buffer as input, writes the file to disk, and returns the buffer unchanged as output. The filename that is used is based on the name of the file currently being processed. All path-delimiters are converted to underscores; a leading "." is removed; and ".rea" is appended. 5.4.3.3 Example Application: appgen The module translation engine is linked into the platform binary at compile for use in loading modules at run-time. This tool is called "appgen" (or, "appgen.exe" on Win32). It instantiates five transforms listed above and connect them in the following order: Read -> Pre-process -> Validate -> Translate -> Write The supported command-line options are: -sourcefile: Expects a single filename argument. If this option is specified then the -sourcedir and -targetdir options are ignored. The filename that is specified is used as the input to the ReadFile transform. -sourcedir: Expects a single directory name argument and defaults to "." if left unspecified. If the -sourcefile option is not specified, then appgen traverses the file-system below the specified directory and processes every .r file that is encountered. -targetdir: Expects a single directory name argument and defaults to "." if left unspecified. Translated files are deposited in this directory. -ring: Expects a single argument between 0 and 10, inclusive. Defaults to 10. This value is passed to the Translate transform. -D: Expects a single string argument. This defines a symbol for use by the Preprocessor transform. The following are some possible invocations of the driver: appgen -sourcefile foo.r appgen -D debug -sourcefile foo.r appgen -sourcedir /tmp/r -targetdir /tmp/rea appgen -sourcefile foo.r -targetdir /tmp appgen The last invocation above will process the directory tree below the current directory and drop all translated files into the current directory. 5.4.4 Code Generation The purpose of this section is to describe how code is generated from the constructs of the platform language. It is organized into sections according to each major unit of code-generation. 5.4.4.1 Expressions This section covers how code is generated from the contents of the <expr> language construct. Literals are covered first, then symbols, and then functions. 5.4.4.2 Literals Integers Decimal and hexadecimal are supported formats for integer expressions. Regardless of which format is used, the representation may be converted to decimal before being output. That is, both the following code snippets: <expr>10</expr> and <expr>0xa</expr> would result in the following generated code: 10 Float-point Values Like decimal-formatted integers, floating-point values are output directly - no special transformations are required to do code-generation. That is, the following code snippet: <expr>l .0</expr> would result in the following generated code: 1. 0 Strings Strings may come in two forms: simple, double-quoted strings formatted according to the regular-expression listed in section 2.1 , and CDATA sections. The first case might appear like so: <expr>"Hello. "</expr> No special transformations are performed on this value before the following code is generated: "Hello . " The second case is the following: <expr>< ! [CDATA[ "Hello, world. \n" ] ] ></expr> In this case, the CDATA section delimiters are stripped, all double quotes and backslashes are escaped with a backslash, and the resulting string is wrapped in double quotes to yield the following code: "V'Hello, world. \\n\"" 5.4.4.3 Symbols msg.channel: Resolves to "?channel". msg.cls: Resolves to one of the possible message class strings. msg.correlationID: Resolves to "?cid". msg.params(n): Resolves to "?pn". msg.responselD: Resolves to "?rid". msg.src: If the rule specified msg-src, this symbol resolves to what was specified. If not, this symbol resolves to "?source". msg .srclnstance: Resolves to "?source-cookie". msg.type: If the rule specified msg-type, this symbol resolves to what was specified. If not, this symbol resolves to "?type". decls.data(n): Resolves to "?decln". pdecls. data(π) : Resolves to "?pdecln". mod.desc mod.guid mod.name mod.ver: Resolves to what was specified for app-desc, except that the data is escaped exactly as CDATA-delimited string-valued expressions are. mod.instancelD: Resolves to two zero bytes. mod. path: Resolves to "?path". true: Resolves to "(= 1 1)". false: Resolves to "(= 0 1)". null: Resolves to "0". 5.4.4.4 Functions Code is generated for functions by recursively descending through the expression trees that make up the arguments to the function. Each function follows the following template: (function-name expression-argl expression-arg2 ... ) To generate code for a given expression-argument, the rules for expressions (which include functions) are simply applied again. For the purposes of this documentation, the code that is generated for the arguments of a given function will be denoted as "args." and: "(and " args ") or: "(or " args ") not: "(not " args ")" gt: "(> " args " II) Λ"H It: "(< " args " II) -v'If eq: "(= " args ")" neq: "(!= " args ")" gte: "(>= " args ")" Ite: "(<= " args ")" string-append: "(string-append " args ")" string-compare: (string-compare " args ")" string-icompare: (string-icompare " args ")" string-contains: (string-contains " args ")" string-icontains: (string-icontains " args ")" escape-delimiter: (escape-treenode-delimiter " args plus: (+ " args ")" minus: (- " args ")" multiply: (* " args ")" divide: (/ " args ")" 6.0 Open Design Application Model An XML-based schema called an Open Design module defines Open Design Applications. <module> is the entry tag into this schema, and it contains tags for identifying the name of the application, the author, the version, the sets of rules defining the application, and other schema that allows the Open Design kernel to provide entry points into the system user interface. Rules can be externally referenced or may be referenced inline within the Open Design app definition. The following sample application or "servicelet" where the application logic is entirely written as a module in the .r file rules language of Open Design. This application shows the "about window" for the Open Design platform. <?xml version="1.0"?> <module> <app-properties> <app-name>About OpenDesign</app-name> <app-uuid>{F40D8FA0-B3CA-4bca-95EF-452CA4EC265E}</app-uuid> </app-properties> <app-events> <ap -on-1 unch> <actions> <activ> <activ-dest>odi . syssvc</activ-dest> <activ-cmd>"SendMessage"</activ-cmd> <activ-params> <activ-param>"θnAbout"</activ-param> </activ-params> </activ> </actions> </app-on-launch> <app-on-disable> <actions> <activ> <activ-dest>{622091AE-5088-41FB-8920- FC50E6B2AB90}</activ-dest> <activ-cmd>"Destroy in"</activ-cmd> <activ-params> <activ-param>"about"</activ-param> </activ-params> </activ> <activ> <activ-dest>{622091AE-5088-41FB-8920- FC50E6B2AB90 }</activ-dest> <activ-cmd>"Tray.ClearMenu terns"</activ-cmd> </activ> </actions> </app-on-disable> </app-events> <ruleset> <! — add an item to the tray menu —> <rule> <msg-ctx> <msg-src>"{95BD8F75-696B-49A9-AlC9-F415614FE96A}"</msg- src> <msg-type>"AdapterRegistered"</msg-type> </msg-ctx> <expr> <func> <func-name>string-compare</func-name> <expr>"{622091AE-5088-41FB-8920-FC50E6B2AB90}"</expr> <expr>msg.params (0) </expr> </func> </expr> <actions> <activ> <activ-dest>{622091AE-5088-41FB-8920- FC50E6B2AB90 } </activ-dest> <activ-cmd>"Tray.AddMenuItem"</activ-cmd> <activ-params> <activ-param>l</activ-param> <activ-param>"θnAbout"</activ-param> <activ-param>"About OpenDesign"</activ-param> <activ-param>9999</activ-param> </activ-params> </activ> </actions> </rule> <! — open the about window —> <rule> <msg-ctx> <msg-src>mod. instanceID</msg-src> <msg-type>"θnAbout"</msg-type> </msg-ctx> <actions> <activ> <activ-dest>{622091AE-5088-41FB-8920- FC50E6B2AB90 } </activ-dest> <activ-cmd>"CreateSkinWin"</activ-cmd> <activ-params> <activ-param>"about"</activ-param> <activ-param>"About OpenDesign"</activ-param> <activ-param>"resources\\aboutmask.bmp"</activ-param> <activ-param>0</activ-param> <activ-param>50</activ-param> <activ-param>50</activ-param> <activ-param>"resources\\about.html"</activ-param> </activ-params> </activ> </actions> </rule> <! — close the about window —> <rule> <msg-ctx> <msg-src>mod.instanceID</msg-src> <msg-type>"About_CloseMe"</msg-type> </msg-ctx> <actions> <activ> <activ-dest>{ 622091AE-5088-41FB-8920- FC5OE6B2AB90 }</activ-dest> <activ-cmd>"DestroyWin"</activ-cmd> <activ-params> <activ-param>"about"</activ-param> </activ-params> </activ> </actions> </rule> <! — get the version information of the current build —> <request-response> <request> <msg-ctx> <msg-class>request</msg-class> <msg-type>"about_getversion"</msg-type> </msg-ctx> <actions> <activ> <activ-dest>{95BD8F75-696B-49A9-AlC9- F415614FE96A}</activ-dest> <activ-cmd>"GetVersionInfo"</activ-cmd> </activ> </actions> </request> <response> <actions> <activ> <activ-dest>{C8CB5C55-139A-4bc7-B288- E74379A7Al7B}</activ-dest> <activ-cmd>"ReceiveVersionInfo"</activ-cmd> <activ-params> <activ-param>msg.params (0) </activ-param> </activ-params> </activ> </actions> </response> </request-response> </ruleset> </module> Additional rule elements that could be added to this application include routing rules, policy rules, etc. Examples of these additional rule elements to build up a more realistic application appear within the Event Bus Section. 7.0 Event Bus The Event Bus is the messaging interface exposed by the Open Design Application Router. The interfaces allow each module type described earlier to be written and installed within the Rules Repository on any Open Design node. These interfaces are invoked by rules installed within the system. The basic capabilities of the event bus are to: • Load and Unload modules • Send Messages • Register Names • Expose platform, network, and OS policy triggers: message queue length, CPU usage, etc. • Establish channels • Manage the Event Bus • Configure the network 7.1 Event Bus Interfaces Some of the rules described samples invoke adapter interfaces. These samples are written in the Rules Language. This is not the complete set of Event Bus interfaces, the samples provided here are to illustrate the interface types of the Event Bus, and how rules are written to invoke them. 7.1.1 Load Module Rule This method loads a module from a file. The module file can be a translated file which may have a ".rea" extension or a raw ODI XML file that has a ".r" extension. If the module has a ".r" extension it may be automatically be run through the translator. LoadModuleFromFile ( strModulePath ) The interface Load Module is one mechanism by which new rules are added to the Repository. The expression part of this rule expresses the conditions under which the actions below, invoking LoadModuleFromFile, occur. <actions> <activ> <activ-dest>{95BD8F75-696B-49A9-AlC9- F415614FE96A}</activ-dest> <activ- cmd>"ModMngr.LoadModuleFromFile"</activ-cmd> <activ-params> <activ-param>"c: \myModule\modl . r"</activ- param> </activ-params> </activ> </actions> 7.1.2 Send Message Rule The SendMessage interface is used to send messages to a destination. SendMessage ( bstrType, msgPara s, ) The rule to invoke this interface: <activ> <activ-dest>{95BD8F75-696B-49A9-AlC9- F415614FE96A}</activ-dest> <activ-cmd>SendMessage</activ-cmd> <activ-params> <activ-param>"Hello"</activ-param> </activ-params> </activation> The destination is expected to understand the parameters of the call. In this example the parameter is a simple string that could be displayed by the destination application for this message. 7.1.3 Register Name Rule Publishing a service or application on the network is accomplished by invoking RegisterLogicalName. The interface: RegisterLogicalResource ( "myLogicalResource" , szFunc, szCrit, szlnstructions , szDescription) ; This interface is invoked form application supplied name registration rules. This is an example call to register an application name: <rule> <msg-ctx> <msg-type>"RegisterLogicalName"</msg-type> </msg-ctx> <actions> <activ> <activ-dest>"odi . syssvc"</activ-dest> <activ-cmd>"_RegisterLogicalName"</activ-cmd> <activ-params> <activ-param>msg.params (0) </activ-param> <activ-param>msg.params (1) </activ-param> <activ-param>msg.params (2) </activ-param> <activ-param>msg.params (3) </activ-param> <activ-param>msg.params (4) </activ-param> <activ-param>msg.params (5) </activ-param> <activ-param>msg.params (6) </activ-param> </activ-params> </activ> </actions> </rule> Some of the parameters to the call are rule snippets themselves. The C logic used to build up the parameter list, and then fire the rule, via an adapter to the event bus, to match the register name rule above is: var msgparams; var initialNodeData; // logical name to register msgparams (0) = "AddressBook"; // the build-tree logical routing function msgparams (1) = "<exprXfuncxfunc-name>atoi</func- name><expr>substr (msg.params (0) ,1) </exprX/funcx/expr>"; // the lookup-tree logical routing function — the two functions // are the same in this case, but the source of input data differs . msgparams (2) = "<exprXfunc><func-name>atoi</func- namexexpr>substr (msg. crit ("lastname" ) , 1) </exprx/funcx/expr> // no criteria decls in this case msgparams (3) = null; // no published criteria decls in this case msgparams (4) = null; // use the "deliver" message instructions - special processing msgparams (5) = "deliver"; // submit the initial key to use— let's choose "Z" // as the upper bound because all the address records // are hosted on the current node initialNodeData(O) = "2"; // submit additional data to be stored // in the tree bucket (none in this case) initialNodeData(l) = GetCurrentNodelD ( ) ; // nest the initial tree entry information // in the message parameters msgparams (6) = initialNodeData; SendMessage ("RegisterLogicalName", params) ; The final complete routing rule installed for the service name 'AddressBook' registered in this example then looks like this: <module> <app-properties> <app-id>AddressBook</app-id> </app-properties> <ruleset> <rule> <! — build table rule —> <msg-ctx> <msg-src>mod.id</msg-src> <msg-type>LogicalRoutingTableBuildRequest</msg-type> </msg-ctx> <pdecls /> <! — if any crit or published crit were specified, they would appear here —> <actions> <tn-create-with-set> <tn-name>"buildExpressionValue"</tn-name> <tn-value> <! — the value of this treenode is the build-table expression —> <expr> <func> <func-name>atoi</func-name> <expr>msg.params (0)</expr> </func> </expr> </tn-value> </tn-create-with-set> </actions> </rule> <rule> <! — lookup rule —> <msg-ctx> <msg-src>mod.id</msg-src> <msg-type>LogicalRoutingTableLookupRequest</msg-type> </msg-ctx> <pdecls /> <! — if any crit or published crit were specified, they would appear here —> <actions> <tn-create-with-set> <tn-name>"lookupExpressionValue"</tn-name> <tn-value> <! — the value of this treenode is the lookup-table expression —> <expr> <func> <func-name>atoi</func-name> <expr>msg. params (0)</expr> </func> </expr> </tn-value> </tn-create-with-set> </actions> </rule> </ruleset> </module> This name registration combed the parameters in the name registration call with a routing rule template to form a complete routing rule. This routing rule is propagated by the infrastructure to any clients invoking the 'AddressBook' service. Calls to the service are via the SendMessage activations described above. The code to generate the SendMessage event, and place it on the event bus: Logical name: "AddressBook" Service: "Add" Arguments: "Murtz", "Fred", "555-1212" // code var mparams, mpLog; mparams(O) = "Murtz"; mparams (1) = "Fred"; mparams (2) = "555-1212"; mpLog(O) = "AddressBook"; mpLog(l) = "Add"; mpLog(2) = mparams; SendMessage ("LogicalRoutingService", mpLog) ; The message above is caught by the client runtime routing rule as an message on the event bus: // routing. r <rule> <msg-ctx> <msg-type>"LogicalRoutingService"</msg-type> </msg-ctx> <actions> <activ> <activ-dest>"odi . syssvc"</activ-dest> <activ-cmd>"_SendLogicalMessage"</activ-cmd> <activ-params> <activ-param>msg.params (0) </activ-param> <activ-param>msg.params (1) </activ-param> <activ-param>msg.params (2) </activ-param> </activ-params> </activ> </actions> </rule> Client routing rule catches the invoke message and activates the syssvc adapter _SendLogicalMessage activation: CSyssvc: :_SendLogicalMessage (szLogicalName, szService, pParams) { // pseudocode CMsg msg; var val, entryData, objLogRes; // create the message that will cause the // lookup rule to fire msg. class = "module"; sg.src = szLogicalName; msg. type = szService; msg.params = pParams; // assert the event to the local message bus RulesEngine->AssertEvent (msg) ; // at this point, the lookup rule has fired and stored // the evaluation result of the lookup function in the // treenode "AddressBook/lookupExpressionValue" . Let's grab // this value val = RulesEngine- >GetTnValue ( "AddressBook/lookupExpressionValue" ) ; // ... and lookup the destination in the in-memory route table entryData = DoLookupInLogicalTable (val) ; objLogRes = LookupLogicalResource (szLogicalName) ; // finally, send the network message NetSend(entryData.nodeld, objLogRes, szService, pParams); } The final call to NetSend invokes the network 'adapter' to wrap, and encode the message for delivery to a peer connection (if appropriate.) 7.1.4 Response Rule - Threshold Load Increased The eb_Loadlncreased event is fired when the number of pending events on the event bus is greater than the threshold value. First time it fires when the threshold is first hit, and then each time the load increases by the interval value. After the number of pending events on the event bus grows greater than threshold value, system services start sending EBLoadlncreased/Decreased events. The events are issued as the load changes by the "interval" number of events. Default value for threshold is 1000 events. Default value for interval is 200 events. The EBLoadlncreased/Decreased events are not added to event bus but go directly to the rules engine for immediate processing. <msg-ctx> <msg-src>"{95BD8F75-696B-49A9-AlC9- F415614FE96A} "</msg-src> <msg-type>"EB_LoadIncreased"</msg-type> </msg-ctx> <expr> <func> <func-name>gt</func-name> <expr>msg.params (0) </expr> <expr>400</expr> </func> </expr> 7.1.5 Establish Channels - create a channel The CreateChannel interface creates a channel for messages between one module and another. Channels are used to ensure that messages sent to the event bus will only be received by the intended recipient of the message. CreateChannel ( targetModule, channelName, ) The activation rule snippet to invoke this event bus interface is: <activ> <activ-dest>{95BD8F75-696B-49A9-AlC9- F415614FE96A}</activ-dest> <activ-cmd>"CreateChannel"</activ-cmd> <activ-params> <activ-param>10</activ-param> <activ-param>"channel_one"</activ-param> </activ-params> </activ> The event bus is a 'broadcast' bus, where any rule supplied by any application may fire against messages on the bus. Channels provide a mechanism by which communication may be targeted. This prevents snooping or inappropriate rule firings for private messages. 7.1.6 Manage Event Bus Rule - set a threshold The EBLoadThreshold Activation sets the event bus load threshold value. After the number of pending events on the event bus grows greater than threshold value, system services start sending EBLoadlncreased/Decreased events. The events are issued as the load changes on the "interval" number of events. Default value for threshold is 1000 events. The EBLoadlncreased/Decreased events are not added to event bus but go directly to the rules engine for processing. EBLoadThreshold f numE vents, ) There is no minimal value for the threshold. If an attempt is made to set threshold to a negative value, the activation fails with ODIKRNL_E_ILLIGALTHRESHOLDVALUE result value. This activation resets value of the threshold to 100 events. <activ> <activ-dest>"{95BD8F75-696B-49A9-AlC9- F415614FE96A} "</activ-dest> <activ-cmd>"EBLoadThreshold"</activ-cmd> <activ-params> <activ-param>100</activ-param> </activ-params> </activ> The left hand side of this rule should provide the appropriate expression of policy for access to this activation. 7.1.7 Configuration Rule - make a connection This rule invokes the interface MakeConnection exposed by the Open Design router kernel. MakeConnection ( BSTR bstrNodeName, BSTR bstrlpaddr, LONG lPort, ) The rule snippet, with the activation only is: <rule> <expires/> <actions> <activ> <! — adapter info signifying which adapter this activation is for —> <activ-dest>θdi . Syssvc</activ-dest> <activ-cmd>"MakeConnection"</activ-cmd> <activ-params> <activ- param>"10.opendesign.com"</activ-param> <activ-param>"10.0.0.20"</activ-param> <activ-param>1960</activ-param> </activ-params> </activ> </actions> </rule> A rule of this type could be supplied by an administrator to ensure that a highly connected central node is one of the configured peers of all Open Design nodes within an administrative domain. The left-hand-side of this rule would be written to ensure that only entities with the right access would cause this rule to fire. 8.0 Open Design Policy Model The purpose of this section is to describe the mechanism by which the platform supports the declaration and execution of rules-based policy. "Policy" is used here as a general term referring to the processing of messages that pass through the system that is conditionally applied based on the contents of those messages. This conditional application of policy is accomplished through rules defined both by the system and by the user. This section covers: • The concept of message-states is described • The scheme by which messages are advanced through the various states • How policy rules are accommodated by this mechanism. • The issue of conflicting policy and detail the design by which the system will detect and handle conflicts. • A brief description of message processing scenarios 8.1 Message States The lifetime of a message is modeled as a set of state-transitions in order to partition the types of processing that may occur on the message as it moves through the system. The set of states that have been defined to model this lifetime are referred to collectively as the message states. The message states have been divided into two complementary classifications based on their direction: the message has either been issued by an application and is headed outbound towards the routing infrastructure, or the message has been lifted off the wire and is headed inbound towards an application. Subsets of these paths are possible. For example, intra-node messages issued between processors and modules transition through no other states than in|app and out|app. The states and their definitions are listed below. They are listed by directionality. Within each direction, they are listed in the order through which messages will transition. Inbound: in I wire: The message has just been lifted off the wire. The physical routing infrastructure has determined that it is destined for the node and has marshaled the message into the rule engine. in I instructions: The instructions that the message carries are to be processed. in I logical: The logical routing header of the message is to be processed. in I app: The message is to be dispatched to an application for processing. Outbound: out| app: The message has been issued by an application. out| logical: The message may be processed in order to generate its logical routing header. out I instructions: The message may be processed such that it includes the instructions that cause it to be handled properly at its destination. out I wire: The message is ready to be serialized onto the wire. 8.2 The State-Transition Mechanism To implement the state-transition mechanism, a field is added to the data-model used to represent messages in the rule engine. The purpose of this field is to mark the current state of the message. Messages in the system are actually represented using an aggregate data-model to permit arbitrarily long parameter lists, so only the header component of the model may carry this field: (deftemplate message-header (field state (type string) ) (field id (type integer) ) (field source (type string) ) ..-) For each pair of adjacent states, in|instructions to in|logical, for example, is a state-transition rule that matches against messages in the old state and advances them to the new state: (defrule in-instructions-to-in-logical ?message-header <- (message-header (state in-instructions)) => (modify ?message-header (state in-logical) ) ) This rule does properly affect the state-transition on the message, but it is incomplete for two reasons: it may fire before the policy rules that were intended for the in|instructions state, and it does not allow us to detect potential conflicts in those policy rules. 8.3 Policy Rules Policy rules are defined by the system and by the user. A given policy rule is written against a particular message state. The actions that can be taken given a matching message are limited by the supported verbs at a given state. To declare policy rules, the following may be provided: - The state at which to apply the policy. - Optionally, further criteria to which the message may conform before applying the policy. - The actions to take given a message that has matched the criteria. - The actions to take given a detected conflict. The language specification will specify the mechanics of actually authoring and installing policy rules. 8.4 Policy Conflict Detection and Handling Policy rules are defined both by the system and by the end-user. Further, there may be multiple, independent end-user policy authors. Policy rules that conflict with each other (say, one policy that asserts that a message should be consumed and another that asserts that it should be discarded) are likely. In the general case, conflicts among policy rules for a given message state cannot be discerned. Rather, conflicts may be detected at a subset of the supported message states and, in the case that a conflict is detected, a message is issued to that effect such that it can be further handled by application logic. When a conflict is detected at a given state, all further application of policy is halted. Instead, a conflict-detected message is issued whose payload is the offending message. The following scheme is used to detect conflicts: - State-transition rules are tagged with a low salience value to ensure that the policy rules for the old state fire before the state- transition rule is activated. - The rule that moves the message from the state at which we would like to detect conflicts to the next state contains a call-out in its actions that scans the action-dispatch queue maintained by the rule engine wrapper object. Since the policy rules are more salient than the state-transition rule, we know that by the time the call-out is invoked, the action-dispatch queue contains all the actions that will be taken for that stage. - The implementation of the call-out is to scan the action-dispatch queue for conflicting verbs. A verb is an abstract way to refer to an operation to perform on a message. Conflict detection may be constrained to a sub-set of the message- states, and in those states, only certain high-level actions are permitted to be taken (forward, discard, consume, etc). - If a conflict is detected (i.e., actions have been specified that instruct us to both consume and discard a message), the call-out removes all actions from the action-dispatch queue that were placed there by rules from the current state and issues a new conflict-detected message with the offending message as the payload. - The call-out then returns false back into the rule, preventing the rule from advancing the message to the next state. Further processing and application of policy for this message are halted. Given the above, an example state-transition rule might look like so: (defrule in-instructions-to-in-logical (declare (salience 0) ) ?message-header <- (message-header (state in-instructions) ) (test (detect-conflicts "in-instructions" "deliver" "forward" ) ) => (modify ?message-header (state in-logical))) The detect-conflicts function takes as its first argument the message state for which it is to search in the rule engine's outbound queue of actions. The rest of the arguments to the detect-conflicts call are those verbs that are to be considered mutually exclusive - so if two or more of those verbs appear on the outbound rule engine queue as actions to take on the message, there is a conflict. The detect-conflicts function removes all the actions for the specified state in this case, issues a "conflict-detected" message, and returns false to prevent the firing of the state-transition rule. The detect-conflicts arguments are system-defined but well-known. That is, policy authors have enough knowledge to write non-conflicting policy, but the system decides what verbs are mutually exclusive. The implication of the above is that, for each state, there is a well- defined set of "verbs" that can be performed on the message against which the policy rule was written. The message states and their associated verbs are listed below: in-wire: discard in-instructions: deliver, request, response, forwarded, execute in-logical: discard, dispatch in-app: discard, dispatch out-app: discard out-logical: discard, route out-instructions: out-wire: discard, issue 8.5 Scenarios The possible message processing paths through the system are: • From the network layer up to a Module • From a Module to the network layer • From a service to a Module • From a Module to a Service • From a service to another service A module is a set of rules, as described earlier. A service here is an external process that hosts service logic. This may be in addition to the logic of a service expressed in the Open Design .r language. In processing paths, the application service router provides the connection between the elements listed above, i.e. there is preferrably no mechanism by which Open Design messages 'short circuit' the router core rules engine to communicate outside the policy infrastructure imposed by the router. From the foregoing, it will be appreciated that although specific embodiments of the invention have been described herein for purposes of illustration, various modifications may be made without deviating from the scope of the invention. Accordingly, the invention is not limited except as by the appended claims.

Claims: CLAIMS l/we claim: [d] 1. A computer system comprising: a plurality of applications; an adapter for each application, each adapter providing an interface between the application and an event bus interface; an event bus providing an event bus interface for receiving messages and distributing messages to adapters and a rules engine; a rules repository having rules for processing messages; and a rules engine for receiving messages, generating messages in accordance with the rules repository, and sending generated messages via the event bus.

Inventors: BEARMAN ROB, BUSH STEVE, BUTCHER TOM, JUNG EDWARD, KATARIYA SANJEEV, KHOURY SAMI, KYNE FAJEN, BEARMAN, ROB, BUSH, STEVE, BUTCHER, TOM, JUNG, EDWARD, KATARIYA, SANJEEV, KHOURY, SAMI, KYNE, FAJEN
Applicants: BEARMAN ROB, BUSH STEVE, BUTCHER TOM, JUNG EDWARD, KATARIYA SANJEEV, KHOURY SAMI, KYNE FAJEN, OPENDESIGN INC, BEARMAN, ROB, BUSH, STEVE, BUTCHER, TOM, JUNG, EDWARD, KATARIYA, SANJEEV, KHOURY, SAMI, KYNE, FAJEN, OPENDESIGN, INC.
Application year: 2001
Main classification: G06F9
IPC codes: G06F9, G06F13, G06F15, H04L12