Binding Java and Web Services to Native Code, Page 2
The patterns defined by NCA are designed to be implemented against a collection of metadata that describes the interfaces for the existing source (client) and the generated target API. When these are provided, and given a specific pattern to generate, the intermediate code can be generated according to the rules and semantics defined for a particular bridge. In modern computing, there are many potential systems that can provide descriptive information about APIs, the most powerful today being that of XML. To that end, NCA defines PLanML (Programming Language Markup Language), which is designed to represent a large number of the common languages used today.
PLanML is defined by its own schema, which defines the principle types, methods and enumerations required by NCA-compliant tools to generate code according to the principal patterns. A representative sample of the metadata generated for a simple C++ class may look like Example 1.
<typeDef aliasName="HelloWorld" byteSize="1" category="struct" name="HelloWorld" typeID="110"> <struct aliasName="HelloWorld" type="class"> <method access="public" methodID="144" name="value" scopeRef="110" typeRef="105"> <parm typeRef="87" name="arg"/> </method> </struct> </typeDef>
Given the existence of common metadata describing the APIs used for a set of common languages (PLanML), a natural extension is to use XSL/T (XSL Transformations) to allow for the generation of bridging code compliant with the Native Connector Architecture. As part of the transformation process, a compliant tool will be able to:
- Generate all of the bridging code required to create a Connector.
- Generate build instructions/rules (such as a Makefile useable by the make tool).
- Generate descriptor information describing the generated code as well as the output of the build for use in assembly and deployment of the Connector.
NCA defines some default patterns for the use of generating Java language to C++ connectors; however, one of the distinct advantages of the architecture is that the templates used are human readable (and thus modifiable). By invoking an XSL/T processor against different templates, one can generate different types of bridges and different bodies of code, resulting in a highly extensible bridging toolkit.
Resulting Component Anatomy
A given component that adheres to the Native Connector Architecture is modeled as shown in Figure 2:
- The original code is unchanged and may be composed of a single object, or a collection of objects or methods.
- The original code is "wrapped" in methods and objects, which both abstract the original code's APIs as well as provide any "plumbing" (such as marshalling, un-marshalling, or system calls) that may be needed to isolate the original code.
- A single, new interface is generated around the "plumbing" code, which provides an interface that is natural within the context of the client environment. This means, for example, that if the client is a Java application, the new interface is a pure Java class/object rather than a collection of native methods.
- The client interacts with the component in its natural environment, obeying the semantics and rules that govern it. The mechanisms and foreign semantics of the underlying code are encapsulated and hidden to the client application.
Figure 2: Native Connector Component Anatomy
The runtime architecture of a system based on Native Connectors presumes that the existing client code and existing legacy code remain unchanged by the addition of the Connector. The Native Connector Architecture defines the following elements:
- A Container, which logically encapsulates the APIs exposed by the legacy code. Functionality exposed by the legacy code is delegated to it by the Container methods
- A Bridge, which serves the function of mapping the Container methods to the Component (the point of integration for client code) as well as any marshalling, un-marshalling, or special system processing
- A Component or Accessor, which serves as the point of integration for client code
- A Runtime component, which handles any system behavior required, such as object lifetime management, exception handling, and so forth
The general runtime architecture for a Connector-based system can be described as in Figure 3:
Figure 3: Runtime Architecture Overview
Connector Generation Process
The process by which the Bridge, Container, and Component are generated is a mechanical, data-driven set of actions that forms a code generation process. The foundation of this generation is two-fold:
- PLanML, a specific schema of XML that describes the interfaces both consumed and emitted in a language- and platform-agnostic fashion
- XSL/T, a set of patterns embodied as XSL transformations that consume PLanML and emit code for later compilation. The overall process is shown in Figure 4:
Figure 4: Overall Connector Generation Process
Page 2 of 3