|
Introduction
In the first three articles of this series [1] [2] [3], we described an approach to application software architecture that is based on a core set of "RESTful" principles. This Resource Oriented Computing (ROC) approach is motivated by the desire to see if the flexibility found in the World Wide Web can be incorporated into application software.
Previous articles examined the physical level of such an architecture and the boundary between the logical and physical levels. This article describes programming at the logical level and discusses some logical level design patterns available for ROC application design.
Programming at the Logical level
Hello World
The classic "Hello World" program is useful to show the minimal structures required to create an application which responds to requests from a client. For the ROC implementation we describe, the following must be in place:
A transport to detect the external event that is the request for the "Hello World" resource,
The "Hello World" resource,
A module containing the "Hello World" resource,
Mapping definitions to route the transport's internal request to an endpoint that will return the resource representation.
These basic components have been introduced in our previous articles, so here we concentrate on explaining how the components work together to create the "Hello World" application.
Remember that a transport is an event detector that resides at the edge of the system. It is responsible for detecting and handling external events while hiding the details; including the transport protocol and all protocol-specific issues. We intentionally leave the transport unspecified for this example and presume that the chosen transport detects the external request event and issues an internal root request for the resource located at the logical address "resource:/helloworld".
A transport is typically implemented in its own module and imports one or more modules from applications which wish to receive requests via the transport. Each module within the system has a unique URI identifier [4] and this is used in the import statement. Module version numbers (min, max) can be specified in the import statement or, if unspecified, the latest available module version is imported.
<import>
<uri>urn:com:mycomp:demo:helloworld</uri>
<version-min>2.0</version-min>
</import>
The implications of using logical identifiers and module version numbers are significant. Because module relationships are defined with logical addresses, the same resolution process that locates a physical endpoint for a logical URI address is also used to resolve the relationships between modules. This means that modules can be added, updated, and rolled back while an application is running. Different versions of a module can also operate simultaneously within a single system without conflict.
For example, imagine that an existing module at version 1.0.1 exhibits a defect and an updated module (version 1.0.2) needs to be deployed. A module deployment manager could upload the module's updated JAR file and perform a "Hot Restart". This would allow the microkernel to dynamically load the new module into memory and then resolve all subsequent requests to it. If desired, the original version of the module could even continue to operate in parallel with the updated version. This would, however, require that requests to the original version specifically target the original module by including the desired version number (1.0.1).
In order to expose the "Hello World" resource, our "Hello World" module must export the address of the resource:
<export>
<uri>resource:/helloworld</uri>
</export>
It must also map the logical resource address to a physical endpoint that returns the resource representation. It does this within its internal private address space as follows:
<map>
<match>resource:/helloworld</match>
<class>com.mycomp.HelloWorldEndpoint</class>
</map>
With this setup for the "Hello World" example, we are ready to follow the flow of a request/response cycle. When the external client request arrives at a transport, the transport detects the event and issues a root request for the resource with the address "resource:/helloworld". The microkernel attempts to resolve this request within the address space of the transport's module and finds the import statements identifying our "Hello World" module. Since the "Hello World" application module has been imported by the transport, the resolution process will check our module's export statements and, seeing a match, it enters the module to continue the resolution search. Inside our module, the microkernel finds a mapping to a physical endpoint. The microkernel will then schedule the endpoint processing on a worker thread. When the endpoint returns a representation, the microkernel will pass that representation back to the transport which, in turn, delivers it back to the external requesting client. |
|