iCommerce.com Corporation
eCommerce


Search our
entire site

Enter your search
terms below, or visit
our
search page



Search case
studies only

Enter your search
terms below:




For the table
of contents and
hyperlinks to
general topics
proceed to
toc



























Object Service Architecture

Intermediary Architecture Infrastructure

Project Summary

  September 15, 1998  

Executive Summary

The Intermediary Architecture Infrastructure is an enabling technology which facilitates the dynamic augmentation of applications with added functionality. Specifically, it enables the run-time insertion of object services to extend the behavior of applications on the fly. The implementation of the infrastructure is application-independent and minimally requires the addition of only three classes to be integrated with any application. Further, the infrastructure is itself component-based (and very extensible), and can self-assemble on-the-fly as required. We have used this infrastructure to extend the functionality of a web server , a proxy server, and Netscape's Mozilla Web Browser. In these implementations we've added services to dynamically insert some of the following functionality: authorization, change notification, web page content modification, network weather service, URL request modification, and assisting to provide Mozilla with a dynamic user interface.

Introduction

The world-wide web (WWW) is composed of web clients and web servers and an underlying communications infrastructure capable of supporting any number of high level protocols such as HTTP, IIOP, and RMI. Given the variety of uses (and anticipated uses) of the WWW, clients and servers need to be flexible enough to satisfy the wide range of applications. As it is unlikely that they cannot do this out of the box, web clients and servers need to be able to solve this by being extensible. In fact, most web clients and servers do offer some degree of extensibility through the use of browser plug ins, server-side includes, DLLs, Java applets, Javascript, etc. to provide added functionality.

As an alternative we have explored the use of distributed object component architectures (e.g. CORBA, DCOM, etc.) as an avenue to provide extensibility to the WWW so we could better understand how such distributed architectures scale to the internet. This document presents an overview of the implementation of an infrastructure which facilitates this goal.
 

Objectives

Addressing the goal above, the Intermediary Architecture project focused on designing an infrastructure to integrate web architectures and distributed object component/service architectures.

The principal objectives of this project included:

  • Given the lack of extensibility of client and server applications, investigate the design of an architecture that uses object services to extend the functionality of the WWW
  • While not only is the ability to extend web applications limited, but in addition, the way in which servers and clients are extended is completely different -- there is no common / shared extensibility infrastructure. Therefore, explore the design of a single scalable, reusable architecture that can be applied to a wide range of web applications.

Approach

Given that our focus was to extend, and not to rebuild current web architectures, we needed to develop an architecture that would allow us to augment the current functionality of web applications. However, the closed nature of these systems was a limiting factor. Given this, conceptually what we wanted to do was to insert a layer between clients and servers (see Figure 1 below). This would provide an access point from which we could monitor web events, and conditionally augment the apparent behavior of the web client and web server by invoking object services.     Figure 1. Conceptual Intermediary Architecture

From an implementation standpoint this could be accomplished in one of two ways. First, we could implement the layer external to the application(s) of interest, and effectively filter the incoming and outgoing communications. This, for example, could be accomplished by using a web proxy through which a client and server would communicate. Alternatively, a second implementation approach would be to tie directly into an application, monitor internal communications, and selectively augment the behavior of that application.

The first approach is necessary when the applications are closed, since only the inter-communications can be intercepted and possibly modified. However, the approach is limited because it can only effect change through modification of inter-application communications. On the other hand, the second approach can be used when the application's design exposes suitable hooks from which this external architecture can augment the application's behavior by accessing and modifying intra-application communications (and data structures). If the appropriate hooks, or insertion points, needed to effect the desired changes are not available, it might be necessary (if at all possible) to modify the application's source code to expose such points.

We can effectively implement both approaches using the second architecture described above and illustrated in Figure 2 below. Using binding code, which accesses (some portion of) the application's internals via available hooks or source modification, interesting events are filtered and passed on to the Intermediary Architecture for processing and possible behavior augmentation via the execution of local and remote object services.
    Figure 2. Implementation Architecture

Granularity of Augmentation
Once the insertion point has been identified (some applications could conceivably have a number of insertion points) and binding code implemented, the next issue to address is when to augment the behavior of the application. Say, for example, that the application is a web server and that we would like to insert an HTML-based menu on each page returned. Then, we would insert this service-provided functionality (which modifies a page before returning it to the client) every time an HTTP GET occurs. The granularity of augmentation is then set coarsely at the level of a GET event.

Alternatively, we may decide that we want to retrieve and insert annotations applicable to some subset of pages (internal scientific reports). While this behavior augmentation is still applicable to HTTP GET events, the granularity has become finer -- to that of a single page.

In fact, any level of granularity can be supported within the IA. What is required is that a service specification  exists which describes what services should be invoked for a given event (and target). In the first example, since all pages were to be modified when a GET event occurred, a specification need only identify the services and associate them with the event GET. In the second example, a specification would not only have to list the services, but also the targets (or in this example, specific web resources) to which they should be applied. Therefore, in general, a specification must include three things: an event, a (possibly null) target, and a set of services to execute.

Note that a target object can be anything: a single web page or file, a client's IP address or name, the time of day, etc. The target can be simple, or it can be complex (e.g. time and a file). The target could also be derived from a conditional statement. Given this event-target information pair, the IA will retrieve the appropriate specification if it exists, and execute the defined services. Since service execution is tied to the occurrence of events, we call this event-based behavior augmentation. The use of targets then enables us to control the granularity at which the augmentation occurs.

Service Specifications
We have mentioned the need for service specifications which define what services to execute. Further, we tie these services to an event-target pair. The format of a specification is not constrained as components of the IA can be specialized to handle any format. As such, there must be some means to determine the format of the specification so that the proper component specialization (called a Specification Manager) can be loaded to access it. For example, the format may be stored in a database, or embedded within the specification itself; it may be written in XML or IDL, etc.

Service Execution
Given that the specification format is unrestricted, it is also necessary to identify which specialization of IA component will direct the execution of the services defined within the specification (the class of component in charge of service execution is called the Dispatch Director) . Before addressing this in greater detail we need to discuss the kinds of behavior augmentation that might be supported.

First, what constitutes an event can be defined by the application or the binding code, there is no rigid definition. We could decide that whenever x=6 we will define that as an event. Or when any HTTP method invocation (GET, PUT, POST, etc.)  is received from a client could be defined as an event. Next, we need to identify when we can execute services to augment the behavior of the application in response to this event. It may be possible to capture the event and execute services before the application performs its normal action in response to the event; or, after the normal action; or, perhaps it may be possible to deactivate the application's normal action, and perform a replacement action. The LISP language has a concept similar to this called around methods. We call the temporal points (in relationship to the occurrence of an event) at which services can be inserted the phases of augmentation.

Again referring to a web server example involving a client-submitted GET event where the target is a file resource, a before action phase might invoke a service which alters the URL to redirect the request for server load-leveling; an after action phase service might insert an HTML-based menu or pertinent annotations into the file to be returned to the client; and, a replacement action phase service might dynamically generate the content of the page from a database.

So, depending upon the architecture of the application, its openness, the programming language used, etc., some or all of these phases may be supportable. Therefore, in addition to specifying the services for an event-target pair, we need to identify in which phase the services should be executed. So, the set of services to be executed is then associated with an event-target-phase triplet. A sample service specification in XML is here.

Services

The interface of a service will depend upon which phases it supports -- there is a specific API for each invocation phase. In fact, generically, a service that implements the generic high-level API could conceivably be used in any implementation of the IA architecture. A simple example is a service that just counts the number of events of each type which occur.

When a service is invoked, at a high-level the application state is passed into the services via request and reply objects. For before-phase services, a request object is passed in, and returned (services can only modify the request); for replacement-phase services, a request object is passed in, and a reply object is returned (services must return a non-null reply object); and, for after-phase services, a reply is passed in, and a reply object is returned (services can only modify the reply). This high-level di-parametric approach frees the IA core architecture from implementation details and is one reason that the implementation is reusable.

However, to write a generally useful service, access to application state will be required. Therefore, once a service is passed in the request and/or reply objects, they are cast to context-specific object types so that more of their content is exposed. This context/API can be generic enough so that different applications of a given kind (e.g. all web servers) can share services (see interoperability issues for a more detailed discussion). However, a service could as well cast the objects to application-specific types and expose application-dependent structures. Yet it is important to understand that the context-specific types provide a means to hide or wrap implementation details so that services can interoperate between IA implementations.

Approach Summary

The purpose of the IA architecture is to enable application behavior to be augmented with local and remote object services. This is done by identifying application access points, and writing binding code that extracts application state (events and target information) and passes it on to the IA. The IA in turn loads in the appropriate service specifications (if they exist), executes those services, and passes the results back to the binding code. If necessary the binding code may merge those results back into the state of the application.

While the IA has a demonstrated ability to insert object service technology into WWW applications, it may actually be used to augment the behavior of many kinds of applications.

Implementation

The implementation of the IA included in this distribution was the result of two successive versions. The first version focused on augmenting the resource-specific behavior of web servers, specifically the Jigsaw web server, with object service functionality. While it was successful at extending the functionality of the server, the implementation of the IA was tied to Jigsaw. Therefore, the architecture didn't scale and was not reusable. Because of this, two primary objectives for the second version included reuse and scalability. To meet these goals we felt that the resulting architecture had to be extremely extensible to be able to adapt to meet the needs of most any environment or application.

While the first version was dynamic, in that it could load and execute services at the granularity of an event-target pair, its underlying architecture was not extensible. That is, each of the components of the IA had specific capabilities that could not be changed to adapt to different environments or applications. This severely limited its ability to be reused. What we felt was required was an architecture that would allow component-wise substitution. This would allow new components to be designed and used at anytime without requiring a re-architecting of the IA.  This solved the problem of reuse. However, given the diversity of uses of the WWW and kinds of data being accessed, it was important that each implementation of the IA was not tied into a particular configuration thereby constraining what it could actually do. For this reason we made the IA dynamically reconfigurable at the level of an event-target pair. What this meant was that the components of the IA would be assembled on the fly, and more importantly, that different implementations of those components could be used on each invocation.

This approach allowed us to define an infrastructure for augmenting the functionality of any application without tying it to any specific (static) implementation. Furthermore, the components of the IA could themselves be object services retrieved and loaded at runtime from the WWW. Note, too, that reuse was also partially enabled by the use of the di-parametric approach as described above.

In the second version of the IA, the primary components and invocation process are shown in the following figure (Figure 3).
 

   Figure 3. IA Components and High-level Component Interaction

Primary Components

The above graphic (Figure 3) outlines the high-level interaction of components. It also illustrates at a high-level how this architecture interfaces to an application. The entire generalized process from event interception to service execution supported by this design will be discussed.

The Point Of Interception

At some point within an application (an intercept point), using some built-in architecture/framework support, language support, ad-hoc code instrumentation, or by intercepting communication pathways external to the application, the IA can be used to augment to application's normal functionality with additional behaviors. Depending upon the interception mechanism that is used, this normal functionality may take any form in the local application, such as a method, an application-defined event, or even a line of code. To operate in the context of the IA the application's normal actions must be mapped to some event identifier(s), minimally so that the services we want to insert at this intercept point can be identified, retrieved, and performed. The manner in which an event's identity is defined can be application-specific, but there must be some means given an event, to locate and return the appropriate services to perform.  How the event is detected and intercepted is an application-specific detail we do not address. It is also the application's responsibility to map its internal representation of an event into an event object understood by the architecture.

Note that in the graphic above, the even-numbered arrows are pointing to component object types, and perhaps not more appropriately to instances of these types. This is because we are mixing dynamic and type models in one illustration... hopefully this will not be confusing. In addition, this discussion pertains to an abstract implementation, and therefore refers to abstract IA classes when appropriate. Actual implementations, in most cases, may use the default implementations of these classes which are provided in the distribution.

Event Objects (arrows 1/2)

The first step, once (the intercept mechanism's interpretation of) an "event" occurs at the intercept point, is to create an Event Object. An event object is an architecture-consumable representation of some application functionality we want to augment. To create an event object, the binding code must invoke a creation method on the Event Object Factory. This invocation, with supplied parameters, causes the Event Object Factory to return an appropriate (new) instance of an event object subtype to the int


Objects
Home
IA Infrastructure
Metadata
Objects
Object Models
Object Models II
Object Models III
Object Models IV
Reports
Survivability
Webtrader