Summary

Introduction

Engine for Web Applications is an application framework for client-side development and Web applications. It provides an environment in which to develop and run JavaScript components and applications. Each Engine can be pre-built to user specifications, and components can be built into the engine itself, deployed as separate script packages, or deployed as application components. The Engine code is designed within a hierarchical structure, and can be used with most existing JavaScript toolkits and libraries. Engine is based on a structured API, a core set of background services, and an optional abstraction layer. It is a controlled environment for Web pages in which scripts may run or with which scripts may communicate, and a binding for custom APIs. The package structure protects it and other Engine components from namespace collisions and does not impede the use of existing toolkits and widgets. It is designed for the last-mile of a Web Application.

Development started in the Fall of 2002, and has been maintained and improved through the present. Engine for Web Applications has been helping developers make Web applications with JavaScript and XML (AJAX) for three years. It has been used in stand-alone, third-party widgets and products, and enterprise Web applications.

Engine for Web Applications encapsulates several services and utilities: managing application interdependencies and bootstrapping (tasking), messaging, transactions, configuration, dynamic runtime components, and monitoring. The engine service, from which the product gets its namesake, connects the other services and utilities to create an abstraction layer for associating script with content and exposing declarative scripting with XML.

Engine makes it easy to quickly develop and deploy scripts without the hassle of namespace collisions, managing object references and lifecycles, and bootstrapping loading sequences.

[ top ]

Stardard Features

  • Standard API for building and implementing components, services, and utilities.
  • Object Registry for storing and querying objects that conform to the standard API and have unique identifiers. Registered objects receive termination signals and destroy notifications when the Web page is closing or the framework is shutting down, helping to prevent memory leaks and improve overall performance and manageability.
  • XML Library for making synchronous and asynchronous GET and POST requests. Includes built in cache and connection pooling.
  • Wires for building methodologies using one or more unrelated functions.
  • Messaging for one-way communication using coded descriminator, and message subscription and publication.
  • Transactions for two-way multi-object communication.
  • Tasking for creating dependency driven task lists, such as bootstraping asynchronous sequences.
  • Application components for creating ubiquitous compiled, packaged, or XML-based modules. XML-based modules can be inserted into the component distribution to create a package, and that package can then be re-compiled into the main Engine library or included as a supplementary reference.
  • Engines for creating an abstraction layer over the execution of a tasks and processing inline or external declarative markup, and serving as a container to child objects and components.
  • XHTMLComponent super class for binding XHTML/HTML DOM nodes to the Engine framework.
  • XHTMLFormComponent static class that manages form elements (via XHTMLComponent references) within Engine contexts.
  • Application component class for optionally binding XHTLM/HTML DOM nodes or XHTMLComponents to custom code.
  • Psuedo thread class for starting background threads that can be automatically cleaned up by the application component.
  • Monitoring service for easily obtaining callbacks to various events handlers instrumented into the web page.

[ top ]

Comparison with AJAX Toolkits, Frameworks, Libraries, ETC

Engine for Web Applications was developed in 2002, and the same core tenets of its design still apply: protect scope, provide services for object communication and bootstrapping, and make it easy to add new features that benefit from the framework. Asynchronous XML requests (AJAX) happens to comprise a significant part.

Many frameworks, libraries, and widgets have arisen in 2005, and celebrating AJAX. The most significant difference that separates Engine for Web Applications from other AJAX toolkits, libraries, frameworks, et al, is that it was developed to solve a set of problems.

[ top ]

The Engine project is the result of detailed planning and is based on prior development projects. It is a set of core utilities and services that help in the implementation of multiple widgets and toolkits. This section describes the evolution of development and ultimate exchange of widgets for a framework.

The MDI Project was started at the end of 1998 as a set of behaviors for Internet Explorer, and evolved into a very complex and extensible DHTML Window system. The short-lived M3 Project began as a complete restructuring of the MDI Project. The fundamental design decisions of the Engine Project were based on what was learned from the previous projects.

The MDI project used a combined file structure allowing configurable deployments. However, namespace collisions and global namespace pollution was rampant. The blind package hierarchy was developed to address the namespace problem, and occurred in the MDI -> M3 redesign. Supporting arbitrary external script files seems extensible, but was not practical considering the various dependencies. Instead, it made more sense to provide a mechanism to build in the desired packages and clip out unused features from those packages.

Throughout the development cycles of the MDI project, one of the primary issues was making sure all of the supplementary files loaded. Tasking was one fundamental design flaw in both the MDI and M3 projects, and along with the Object Registry became one of the first design requirements of Engine. A convoluted routine initialization routine was used as there was no ability to create a logical set of instructions and dependencies.

For example, before a UI widget could be created, the StyleManager had to load an external XML file containing style definitions, and then hash-up those values to support tokens and be able to perform math upon style values. Before the StyleManager loaded, a global object had to be created with initialization values. The requirements placed upon the developer to initialize the MDI code was not very light. Once running it was much smoother, but the API was no less obtuse than most other widget toolkits.

Sequencing the order of load events was first addressed in early prototypes of the M3 project with an Application Driver object and a contorted event-hook system. The event-hook routines were quickly replaced with more bootstrap-like routines, and then task routines. Finally, in the M3 project, there was an object that handled generic tasks. And it was every-bit as convoluted as the original MDI initialization.

[ top ]

The need for a generic task service existed, and the basic prototype work had been done, but the system broke down the moment a task included dependencies. When a task is created to have a generic action definition, a generic handler definition, and zero or more dependencies, there is an expectation that the object representing the task will be handled by a robust yet arbitrary process. However, constantly polling the task lead to race conditions and invalid dependency checks, and running the task around a set of API hooks was just messy.

In the Engine paradigm, a task object is created as a transaction packet and whose only transaction participant is the Task Service. The Transaction Service defines the communication structure, and the required API for transaction participants gives the Task Service a means to track packet state. And what was a messy problem became a neat solution. The Application Driver was then only needed to provide basic dependencies.

While the Task Service defines an API that is much cleaner and more standardized than the MDI API, and supports XML-based definitions, there was a clear need to define a very-simple, non-script based approach to executing tasks. The original Engine Service abstracted the Task Service API by allowing developers to associate task names with HTML elements. At the time, UI widgets were still on the map for Engine, and it seemed logical to provide a way to define generic API invocations with XML.

Although the UI widgets were stripped out, the intelligent abstraction arose from the XML-based self-defining configuration used by the Engine Service. The Engine Service configuration provided a seamless and transparent implementation for the widgets. The ability to create broader definitions, such as XML transformations and static method connections (without changing the methods) was added at the same time the widgets were removed. With the addition of the XHTMLComponent and Application Component packages, the concept of intelligent abstraction solidified.

With the ability to create abstract definitions, the strength of Application Components and the concept of effective binding arose. The advantage of effective binding with an Application Component is that arbitrary objects make use of the core Engine services (eg: Transaction Service) and expose a simple API for immediate use. The API is: write a function and an object uses it (as applicable). When Application Components are grouped, objects have immediate access to a narrow channel of communication. If these objects are HTML form fields, for example, one action on one field could trigger an entire sequence of actions on other fields, all without any object specifically naming another. Most import, though, developers can implement these rich interactions without having to know more than a few simple principles of the Engine architecture.

It is very straightforward to create new Web Application paradigms based upon the standardized architecture. Using the SDK, custom Application Components can be compiled back into the framework, making the product all the more malleable.

With these concepts and services, downlevel content becomes its own application, giving developers access to the power of the Engine services but without having to work around one more API. This interface also makes it easier to prototype solutions with Engine because it reduces the amount of Engine-specific code.

[ top ]

The average Web site will most likely not see much benefit from Engine because - as has been so often remarked upon - it was designed for Web applications. Consider a Web application to consist of one or more Web pages that work with multiple tiers to support user interaction through some degree of business logic and data. Engine is more practical as the level of interaction increases between a user and the data increases, particularly when the user requirements are limited to specific clients. Many high-volume sites that need to support a broader set of user agents would represent a harder argument for using Engine. However, portal and ISV applications would experience tremendous development and deployment gains.

One strength of using Engine in an Enterprise deployment - meaning with non-public Engine SDK - is it uses a robust build and configuration system. With the SDK, Developers can prototype components on the fly via Application Components, compile those components into static Application Component-based deployments, design new components within the easy-to-learn architecture, and configure deployment of core features on a granular level. This subsequently allows developers to rapidly deploy new Web application frameworks based on the Engine architecture. More important, however, is that developers may choose how much of the Engine API they decide to implement by electing to use Application Components or creating their own components.

Another advantage is the careful preservation of the global namespace. Developers may continue using existing toolkits and widgets without worrying about collisions.

[ top ]

One common JavaScript library is a psuedo-thread implementation. However, these psuedo-threads are inherently unsafe because there is usually no notification that an object has been destroyed or marked itself for destruction but is still referenced elsewhere. The Engine architecture helps to make these psuedo-thread implementations safer. Requiring objects to conform to the architecture and be registered with the Object Registry, and exposing a destroy method that removes the object reference from the registry, provides a point juncture where a thread library may tap into the internal message publication that an object was removed. At that point, the thread can stop itself. Also, a thread could rely on a specific object architecture when enforcing conformance to the general Engine architecture.

The Thread implementation included in the Engine framework is one such implementation. While it may not be used for arbitrary objects, the implementation does demonstrate the scalability of the architecture. Consider what happens when an object starts a thread and is then destroyed. The Thread component automatically listens for publications from the Object Registry announcing that an object was removed. If the object is the thread's owner, the thread can stop itself. Or, if the object doesn't remove itself from the registry but it's ready state has shifted to a destroyed state, the thread can use that change to stop.

If the thread implementation does not consider the object state, then the thread will continue to run with the referenced pointer, or throw an error if the object reference is unknown.

[ top ]

The Engine Framework includes the Object Registry as a core component. The purpose of the Object Registry is to enforce a basic architecture, to store object pointers by unique identifiers, communicate basic instructions (eg: signal termination) to registered objects, and expose global access to that registry. A few disadvantages to the registry are over-using friendly names for unique identifiers can create namespace collisions, not every object that would partake in such a registry needs the Engine Framework API, and not every object should be responsible for removing itself from the registry when it is destroyed.

Enter another strength of Application Components. The central goal of the Application Component is to give developers fast-track access to the Engine API; a developer only needs to write the core script and be done. The Application Component base-class handles registering and unregistering the component with the Object Registry, exposes wrapper utilities for evaluating Wires, managing Transactions, and, removing any Token Stack references.

The Token Stack is similar to the Object Registry in that it stores object references by identifiers, and it requires an object owner that uses the Engine API. However, it is different in that the object added to the stack can be any object, the id does not have to be unique, and queries can be made for one or more matching key names, by the owner object, or the first object on the stack. When used from within an Application Component, any object references are removed when the Application Component is destroyed.

Where Application Components are used to easily add features and functionality to a Web Application built with Engine, and where the Object Registry, Transactions, the Engine Service, and the Message Service all provide methods of inter-object communication, it was desirable to have high-level discoverability without polluting the Web page DOM, and without compromising the stricter parameters of the Object Registry The motivation behind the Token Stack was to provide that exposure. For example, when an Application Component is initialized, some object may be added to the Token Stack. From that point, and from anywhere else in the application, that object reference is then accessible (as with the Object Registry). The primary difference from the Object Registry, multiple objects with the same name may be added to the stack, and any objects added to the Token Stack are removed for a particular owner object when the owner is destroyed.

[ top ]