This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


December 1996

Leveraging Your Visual C++‚ Experience on the Internet with Thin Client Technology

David C. Mitchell

In this article I’ll present a way to use Internet deployment standards offered by Java, yet maintain C++ development—including the code you're working on—and focus on why the thin client model is best suited for Internet apps. You’ll see why  traditional server/client models have limited flexibility.

This article assumes you’re familiar with C++, Visual C++, Java

David C. Mitchell is the founder and chief technical officer of ViewSoft, a provider of thin client Internet development technology. He can be reached at davem@viewsoft.com or www.viewsoft.com .

There you are, minding your own business, trying to get some real work done, while the rest of the world surfs the Net. You've got nothing against HTML or Web pages, and both Java and ActiveX look promising. But you've been so busy building giant programs using Visual C++®, you just haven't had time to sort through all the hype and figure out exactly which tools you're going to use to take these real applications to the Internet.
      Let's discuss what you as a developer can do to exploit your Visual C++ experience in the Internet environment. I'll present a way to use the Internet deployment standards offered by Java and still maintain C++ development (including the C++ code that you are currently working on). I'll also focus on the thin client model and why it is best suited for Internet applications. You'll see why traditional client/server models have limited flexibility in the context of the Internet. I'll develop a "litmus test" for determining how effective an application's architecture is at exploiting the Internet. Based on the litmus test, I'll define a framework that allows you to create thin client applications and deploy them over the Internet using Java's cross-platform toolkit, AWT. I'll include various code examples that demonstrate a framework that has already been developed.
      A pared-down version of this framework along with support tools to use the framework is available in the source code listing found here. The framework and tools supplied are sufficient to develop all of the samples in this article. For this article, I'll focus on the Java solution—in a future article, I'll show you how to use ActiveX to develop a thin client.

What's Interesting About the Internet?
      Before jumping in on the thin client application development model, let's review why everybody is so excited about using the Internet infrastructure for application deployment. The most obvious reason the Internet is so interesting is because it gives users remote access to information. More information is available than will ever fit on a client machine. The question then becomes, which model best enables on-demand, source-independent information browsing?
Figure 1 shows the basic client/server model of the Internet. The client provides the presentation context, typically a browser such as Microsoft Internet Explorer or Netscape Navigator that allows remote data to be viewed through the HTML presentation layer. This data represents a slice of the content of the Internet. The network moves the content to the presentation context via HTTP.


Figure 1  Client/Server Model
      Figure 1  Client/Server Model


      URLs further separate the location of content from the presentation context, allowing a browser to access multiple servers and their associated data. Actual access to the content is deferred until the user wants to see it. This deferral mechanism places minimal demands on the network and avoids the transfer of unnecessary data to the client machine. This approach facilitates the exposure of a maximum amount of remote resources with the minimum use of network and client-side resources (see Figure 2).


Figure 2  Remote Data
      Figure 2  Remote Data


      Remote data isn't the only reason why the Internet is interesting—users also want to access logic and services not on their local machines. URLs are as useful as locators for remote logic and services as they are for remote data. It's the combination of data and logic that forms the new content for the Internet. As illustrated in Figure 3, the structure is the same.


Figure 3  Remote Data and Logic
      Figure 3  Remote Data and Logic


      The new content for the Internet is objects—specifically, C++ classes that exist on the server. These are the regular kinds of C++ classes you create, edit, compile, and debug with the Visual C++ development environment. Although the current approach to Internet deployment is typically procedural, where the data operates separately from the logic, it can be enhanced with the object-based model you're used to.
      ActiveX and Java give application deployment more flexibility. The client becomes a mechanism for accessing remote objects on remote servers (see Figure 4). The server contains data and logical content in program structures such as C++ classes.


Figure 4  Remote Object Deployment
      Figure 4  Remote Object Deployment


      How can remote objects best be exposed to the client user over the network infrastructure? The Internet's greatest potential is realized when logic and services, together with content, are presented to multiple users in a context of transparent, real-time collaboration (see Figure 5). Collaboration in a network environment means that two or more users can communicate with one another by viewing or interacting with common data and services. The sessionless nature of the Internet lets multiple clients access an object's state concurrently.


Figure 5  Real-time Multiple User Collaboration
      Figure 5  Real-time Multiple User Collaboration


      Collaboration on the Internet removes the user's dependency on the location of information and services by allowing many users to interact with information freed from the constraints of monolithic applications. But collaboration in an Internet environment is a particularly difficult task.

The Application Building Litmus Test
      So you're convinced that you want to take advantage of the Internet for deploying applications. How can you be sure that the tools you select or the methods you employ for building and publishing your applications are going to yield the best results? Let's develop a litmus test to ensure that the solution you choose will meet your objectives. When building applications for the Internet, ask yourself the following questions: What architectural features mesh most logically with the structure of the Internet? What are the requirements for a development framework to create Internet object-based applications using Visual C++? Why didn't I become an accountant? How can I best use an Internet client/server architecture with the Visual C++ development environment I already use? What is a good Internet application development solution, not just for the Internet of today but into the future?
      Let's take a brief look at some of the features you need to get your C++ objects to the Web.

Just Enough Java
      An important part of the litmus test is the application-deployment mechanism. Applications should be able to run without needing client software or plug-ins to be installed prior to connection. This dynamic deployment mechanism enables all or part of the application to be downloaded to the client machine and run locally, allowing the application to benefit from local machine services such as the display, input devices, and local interactive controls in the user interface. Application performance and interactivity are made possible because code is downloaded and run on the local machine.
      What parts of the application really need to be downloaded and run? Certainly not the application logic. Application logic interacts with data not on the client machine (for example, corporate databases, data repositories, and shared information services). In addition, the application logic I'm talking about consists of C++ objects running on the server.
      The part of the application that gets downloaded and run on the client machine is the presentation layer. But how do you get the presentation onto the client machine? This presentation layer includes the GUI elements along with just enough application logic to convert user interface events into object state changes. These state changes, and only these state changes, are what your client will send across the Internet to the server.
      One way to do this is by using what I call "Just Enough Java." This use of Java is simply pragmatic. Besides providing a dynamic download of the presentation layer, Java offers platform independence, effectively making your Visual C++ applications cross-platform. In addition, as a client-side solution, Java takes advantage of its virtual-machine security model.

Thinner is Better
      Thinner is better. No, this isn't an infomercial for the Thighmaster. When it comes to Internet clients, thin is definitely in. The interesting thing about the Internet is that most of the data and logic that users want to access is not on their machines. What do you send down to the client machine to successfully access and deploy an application? Here, less is more. Why? Let's review the fat client problem.
      Practically all of the work done with Java to date uses a fat client model. That is, the application logic, not just the presentation layer, is downloaded to the client machine for execution. Why is this a bad approach? First of all, by downloading the application logic to the client, the logic is displaced from the content the user is trying to access. This violates the basic object-oriented model of data/logic encapsulation. The application has to be split, code has to be sent down to the client, and a network protocol has to be created and maintained by the developer. The whole process involves creating a distributed application, greatly increasing the complexity of writing an application simply to move it to the Internet.
      Another problem with fat clients is the burden that the application places on the client. If too much of the processing burden takes place on the client machine or device, fewer types of client applications and platforms will be able to access and run that application. This will become a problem when Internet appliances become popular. Cramming a large application into a narrow-bandwidth pipe and into a limited-performance client device inhibits your ability to move your application to the net. The fat client approach is not appropriate when you cannot rely on the power or capability of the client. So, if you've got visions of being able to access your Visual C++ application on a Sega Genesis machine, a cellular phone, or a PDA device, remember: the thinner the client, the fewer restrictions on deploying your application.
      Fat clients also suffer because of the bandwidth limitations of the Internet. Since the application logic is on the client in a fat client approach, large amounts of data must be moved to the client machine before the application is able to process it for the user. This movement of data puts an unnecessary bandwidth burden on the network connection. This is problematic because the Internet is largely a low-bandwidth, high-latency environment not conducive to large amounts of network traffic.
      Finally, the fat client inhibits collaboration. It becomes a burden to replicate a common state across multiple machines when the application object state is controlled in a distributed fashion. In other words, if the client contains logic that changes a shared object's state, that state must somehow be updated on other clients currently looking at or interacting with that object. This becomes a tremendous burden on the developer. The problem can be remedied somewhat through an RPC or object proxy solution, where multiple clients are accessing centralized data at the server. But client synchronization and data access control are typically left to the developer, since RPC-like mechanisms don't deal with the replication issues associated with collaborative applications.
      RPC approaches also include the burden of deciding where to split the application between the client and the server. The developer must write a distributed application where some of the code is Java, JavaScript, or VBScript and the rest is a server object written in C++. Doesn't sound like a very scalable development solution, does it? Besides having to decide which parts of the application are where, the developer has to make coding decisions about what data is changed, whether it is shared or not, and so on. Some technologies attempt to abstract the distributed nature of the application, often requiring the use of a proprietary language, generally resulting in a loss of control over performance issues.
      When it comes to deploying applications with the Internet architecture, thinner is better because it allows you to distribute the presentation, not the application. Rapid application access is possible since you do not have to download the entire application. A minimal burden is placed on client resources (or smaller dependency on hardware configuration), and development complexity is reduced because you don't have to write a distributed application. So the litmus test includes a check for thin client Internet application deployment.

Transparent Collaborative Support
      Another issue for the litmus test is in the area of collaboration. Collaboration should be a central theme when you talk about the Internet. A Web development effort should not be considered without taking into account how multiple simultaneous users will access that application. Any environment or framework should be able to abstract or automate collaborative capability at some level.
      I believe that there are two ways in which collaboration can be made possible with minimal developer involvement. The first would be through a codeless presentation model. By codeless I mean that the presentation layer of an application would contain as little application logic as possible (or none) that directly manipulates shared object states. What this means is that object state manipulation code shouldn't be downloaded to the client machine. In an ideal codeless presentation model, the presentation logic executed on the client machine deals only with user interaction (GUI events), transforming those events into data state change commands. Client data state changes should then be packaged and sent to the server to update its objects' states and, in turn, update any other interested clients currently looking at the state of those server objects. The logic that operates on the shared data would be an encapsulated C++ object running on the server machine. The presentation layer would represent a remote client to that object's services. The central shared object would be accessed by multiple simultaneous clients.
      Another collaboration technique allows many clients to have simultaneous dynamic access to a running application. An application framework with such a runtime binding capability could allow clients to attach or detach a particular application during program execution. Since application logic is just another kind of content that can be encountered on the Internet, the same should be the case for applications as well. Runtime binding on an application would allow a client to access an application object's state without involving the shared object directly. By dynamically allowing access to the C++ object, the details for client attachment to that object wouldn't have to be handled by the implementor of the class. This general lack of direct developer involvement in the control of collaboration access would greatly simplify the process of writing a multiuser-access Internet app.

State Change-based Client/Server Connection
      Available bandwidth has always been a limiting factor for effective deployment of applications over the network, so the litmus test should include an examination of the bandwidth impact.
      Where do you divide an application to ensure that the minimum amount of presentation logic is sent to the client and that the application runs optimally over the net? The division shouldn't take place at the event level, since many more events occur on the client than are necessary to maintain an application object's state. Passing events from one side to the other causes the application to create many data round trips before users see the results of their interaction on the screen.
      For example, many events happen with a scroll bar interactor, but the important piece of information is the thumb position produced through user interaction. If the scroll bar controls the value of a numeric data member of a C++ object, then only the final scroll bar thumb position value needs to be sent across the network to update the server-side object. This transfer of state information is the minimum information exchange necessary to maintain synchronized application state over a network. To minimize state-transfer bandwidth, you need to send only that part of an application object's semantic state that is currently relevant to the presentation. A core question of GUI research over the last decade has been how best to implement the state transfer.
      A model is needed that utilizes the state-based nature intrinsic to object-oriented development. Such a model is essential for optimal communication between the server application and the many clients that can simultaneously access that application. Ideally, the implementation of C++ objects need not be involved directly with this state-change notification scheme. Object state management would then remain external to the application object in the same way a client would access the services of an object without the object being concerned with the details of how it is being used. This approach is appealing because it is not cluttered with unnecessary dependencies such as how the object is used, deployed, accessed, and so on.

Thin Client Three-tier Architecture
      The litmus test just created effectively defines a new application architecture for the Internet. This model, which I call the thin client three-tier architecture, can be differentiated from the traditional three-tier client/server approach in two ways. First of all, in the thin client model, all the application-specific logic is running on the server. The logic could be distributed on the server side over many machines, but the approach is inherently server-side. Secondly, in the thin client model, only state changes are exchanged between the client and the server. In the traditional three-tier model the communication between the client and the server is based on remote procedure calls (RPCs). Implementing an application with RPCs requires application-specific logic on the client. Application partitioning is typically left to the developer. The problem with the traditional three-tier architecture with respect to the Internet is that it exhibits many fat client characteristics. It is important to note that a thin client approach allows objects to be accessed by multiple clients simultaneously and in real time.
      Traditional client/server approaches, allowing only a single client to access a server process, were OK for the single-user desktop application, but inadequate and too inflexible given the multiclient access capabilities of the Web. Traditionally, multiuser access to data was limited to the multiuser capabilities of the database engine. The thin client approach enables multiuser control of data and services of application objects. The traditional client/server approach controls data and services at the level of the database engine where the context of the data with respect to its associated logic is limited or nonexistent.
      Let's take a look at the implementation of the thin client three-tier architecture. Client, server, and database pieces can be interchangeable. The Java AWT toolkit provides the API for the user interface of an application. The presentation layer (the thin client) can actually be an applet downloaded from the server and run within an Internet browser. The applet contains user-interface widgets such as scroll bars and buttons and enough embedded network communication machinery to respond to and initiate state changes between the client and the server.
      A client-side visual element is associated with a specific server-side object. For example, an AWT edit box on the client can be "connected" to a data member of a C++ object running on the server. When a value is typed into the edit box on the client-side applet, a data state change message is sent to the server. In this example, an edit box is a Java applet with a "network" line connected to a server-side object's data member. Any other clients viewing that data member through their interfaces will also update automatically to reflect the object's new state.
      Although the client is small, it is also smart enough to know the kind of C++ objects running on the server to which it is connected. The client sends network messages when the user interacts with the AWT control, and receives state changes initiated by the server (for example, when the application logic changes the data member). The generated presentation layer then becomes a client to the server-side C++ objects. The entire client can be generated so that, by default, the developer isn't required to write any code (see Figure 6).


Figure 6 Thin Client Three-tier Architecture
      Figure 6 Thin Client Three-tier Architecture


      C++ objects are executed on the server. The client download initiates a connection to server objects through an object framework-based protocol. Clients can attach to an already running C++ application on the server or start a unique C++ process upon client-applet startup. Any database accessible through C++ can be accessed by the objects in the server-side application.

Meta Data
      Having defined the requirements of Internet applications and the architecture necessary to implement them, let me highlight the specific characteristics of a Visual C++ development framework that would enable the creation, deployment, and maintenance of such applications. The framework includes the following capabilities and features: meta data, state change notification, callbacks, runtime binding, cycle reduction, synchronization, and model-view separation. Now, let's take a closer look at each of these components.
      Meta data is a format for describing the structure of application objects. In the world of C++, meta data would include information about a class structure, for example, the name of a class, its functions and fields, their types, and so on. The Internet's protocol for discovering the structure of content on the server is HTML. The HTML format couples content and meta data for remote information access. HTTP makes requests on parts of the server content by performing a GET command. The argument of the GET command (the path to the HTML file of interest) is really information about the data the user is trying to access—the meta data. Ideally, application objects are viewed and manipulated over the Internet by accessing them through what I like to call a meta interface.
      An effective framework for accessing C++ objects over the Web should be inherently self-describing. In other words, clients are able to access objects' services and structures to attach to these objects. A protocol similar to HTTP incorporated into the framework would enable applet clients to perform application-level GET commands for accessing server-side C++ objects. This same protocol would also support server-side pushes that move C++ object state changes to the client when the object state changed. Meta information is the glue that enables remote presentation to attach to C++ objects on the server.

State Change Notification
      While meta information provides remote access, a physical network connection must exist to communicate state changes between the client and the server. State change notification capability must be supported at the framework level. A framework that requires the developer to explicitly deal with data change notification messages adds too much complexity to the application code and places too many dependencies on the details of remote presentation access into the application logic, making application objects inherently nonreusable.

Callbacks
      Callbacks are functions called when data changes. C++ provides hooks for allowing functions to be called when data changes in a way that is transparent to the implementor of a class. Suppose the following class represents a way to store and manipulate an x, y position:

class Point {

      public: Point(int x, int y) : xPos(x), yPos(y) { } void offset(int x, int y) { xPos += x; yPos += y; } void setXPos(int x) { xPos = x; } void setYPos(int y) { yPos = y; } int getXPos() { return xPos; } int getYPos() { return yPos; } private: int xPos; int yPos; };

      The xPos and yPos data members represent the actual information of the Point class. These data members can have "guarded" or write barrier access (an Eiffel language term) or they can be "computed fields" (as in Smalltalk). Each of these implementations fires a callback when data changes. Another technique, C++ operator overloading, allows code to be executed whenever an explicit assignment to the data member is performed.
      Let's first introduce a new class and then modify the Point class shown above to include write barrier capability. The following IntData class represents a partial encapsulation of the int type:
class IntData {
 public:
  Int(int d) { data = d; }
  int operator () { return data; }
  operator = (IntData &d) {}
  operator = (int d) { }
  
  addCallback(void f(int));
  removeCallback(void f(int));
 protected:
  dataChanged()
 private:
  int data;
 };
Now, let's modify the Point class to have callback-enabled data members:
class Point {
 public:
  Point(int x, int y) : xPos(x), yPos(y) { }
  void offset(int x, int y) { xPos += x; yPos += y; }
  void setXPos(int x) { xPos = x; }
  void setYPos(int y) { yPos = y; }
  int getXPos() { return xPos; }
  int getYPos() { return yPos; }
  
 private:
  IntData xPos;  // CHANGED
  IntData yPos;  // CHANGED
 };
      Notice that the only code in the class that needed to change (as indicated with comments) is the declaration representation of the Point data members, using IntData as the type. Using this mechanism, when a Point object's state changes, clients external to the class can be notified. You'll see a little later that client access to data members can be reasonably restricted to a limited set of clients, for example, clients that visually represent the object remotely. Not just anyone can access the internal state of an object.
      This capability allows callbacks to be associated with the state of objects. For brevity, many of the details of encapsulated access have been left out. This method can be applied to C++ object pointers, arrays, reference-counted objects, and so on. This approach to data change notification contributes to a dramatic reduction in the complexity associated with connecting a remote thin client user interface to the object itself. Data members, then, use a "plug and socket" access approach that allows the representation of the object to be external to the implementation of the class.

Runtime Binding
      Data change notification is not quite enough. Suppose that clients want to modify the object during program execution without generating code. Code generation complicates the task of attaching multiple heterogeneous clients to server objects. For maximum flexibility in attaching collaborative clients to an object over the Web, a runtime binding mechanism can be added to C++ object access. C++ runtime binding doesn't degrade the general performance of the running object itself. Remember the advantages of having meta data information available to describe the structure of classes? Well, it can also help automate the runtime binding of compiled C++ classes. The meta data of each class that is to be accessible over the Internet can be used to generate a C structure that enables lookup and access to data members of an object by name (for flexibility) and by index (for speed). Of course, the name can be resolved to an offset index at runtime as an optimization.
      In Figure 7 the class represents color, which contains red, green, and blue components. Runtime binding access on member functions is also possible, which can further separate the client presentation from the structure of the object. The runtime binding approach allows you to preserve the performance capability of the C++ object itself while allowing remote dynamic access to the object. All this is possible without introducing appreciable complexity for the class implementor (since the runtime binding structure can be automatically generated using the meta data of the class). This lets you focus on the business logic without concern for the details of making the object's state externally viewable.

Cycle Reduction
      When monitoring data changes on objects, cycles can occur that would cause infinite callback conditions. For example, the following doTransaction function gets called:

	void doTransaction()
 	{
  	A = 10;
 	}

      A callback function, AChanged(int a), was added to the callback list on the A member so that, when A is assigned to 10, the AChanged function is called:
	void AChanged(int a)
 	{
  	B = 20;
                        	}
A callback function, BChanged, was in turn added to the B data member, and so it is called as well.
void BChanged(int b)
 {
  doTransaction();
 }
Notice that the BChanged function calls the same function that started the chain of functions to be called in the first place. This creates an infinite cycle. Unless reduced, this sequence of changes and notifications will happen at a furious pace so long as the system stack will permit. The solution is to set a flag so that if the data change callback notification happens while that same data is being changed, the callback function isn't called again.

Synchronization
      Concurrency control always becomes an issue when dealing with multiple threads of execution. In the thin client three-tier model, multiple clients can update the state of shared objects on the server at the same time. How do you resolve synchronization problems as they occur without coding the concurrency control into the objects themselves? Keep in mind that simple proxy methodologies or RPC mechanisms don't handle concurrency issues automatically. A remote function call that changes an object's state can corrupt or break other clients making similar requests on the same object at the same time.
      Let's look at a fairly simple example. Suppose a virtual whiteboard object is running on the server. It contains a list of shapes that have been drawn on the whiteboard by several remote thin clients. The current state of the whiteboard includes a square (item 0), triangle (item 1), and a line of text (item 2). Three clients are currently viewing the whiteboard. Client 1 invokes a command to delete the text (item 2). At the same time client 2 invokes a command to delete the triangle shape (item 1). Also at the same time, client 3 sends a command to add a line. Without concurrency control, the object state will depend on the order in which the commands are handled by the server object.


Figure 8  Optimistic Concurrency Control
      Figure 8  Optimistic Concurrency Control


      Here's an example of how optimistic concurrency control resolves this problem (see Figure 8). Let's say the server handles or sees the "delete the triangle" (item 1) first. The delete command is performed successfully. Next, the server processes the command to delete the text (item 2). Of course, by now the previous delete of the 0th item could corrupt the intent of deleting item 2. The second delete command needs its index to be decreased by one to index 1 (a transformation) to preserve the intent of the command (deleting the text item). Finally, the third command to add a line at the end of the shape list needs no modification since the index of the line item depends on what is currently the last item index. The key to making this scenario work correctly is to use an optimistic algorithm that preserves object states. It is an optimistic approach because no client-to-server negotiation takes place to resolve state change commands. The algorithm assumes that commands can be transformed as they are encountered to preserve the object's integrity. In Figure 8, the immediate action on the whiteboard is translated into the final resolution by the optimistic algorithm.
      The concurrency control example given above is specific to array or list-based data structures. Each data type requires a unique set of rules appropriate to it. Typically, a temporal list is maintained to monitor and adjust operations on data structures based on the data type's rules. Commands from the clients can be transformed to preserve the integrity of the transaction. Again, this synchronization capability can exist outside of the regular implementation of application-specific C++ classes if runtime binding and data change callbacks are integral to the object structure or framework.

Complete Presentation Decoupling
      In the thin client architecture, user interfaces visually represent objects running on the server. In an ideal environment, the presentation is separated from the objects themselves. Figure 9 illustrates the point.


Figure 9 Presentation Decoupling
      Figure 9 Presentation Decoupling


      In Figure 9, a class (ChangeValue) contains a single data member (value) and two functions (addToValue and subtractFromValue). This ClassView tool is part of the framework package that can be found in the source code file available here. The ChangeValue class, when instantiated at runtime, will execute on the server. A dialog can be created that visually represents the entire class. The scroll bar and the edit box are both client controls attached to the class member int value. The buttons control the direct invocation of the member functions of the class. The lines shown between the client presentation and the server C++ object represent the state change management relationship that exists between the thin client and the server-based application object.
      In this example, the user would look at the presentation embedded in the HTML page as a Java applet. The object (the effective content for the client presentation) is manipulated as the user interacts with the thin client interface. For example, suppose the user presses the Add button. This causes the client to initiate a command to call the member function addToValue on the ChangeValue class. The following code is written for the addToValue function:

void ChangeValue::addToValue()
 {
  // change the value, this may create callbacks to occurs depending
  // on the number of clients currently interested in this value
  value += 10;
 }
The value change will produce a set of callbacks to be invoked, which will subsequently update any thin clients currently attached to the ChangeValue object, including the scroll bar and edit box shown on the page where the user pressed the button.

Sample Application
      My thin client Internet sample application uses C++ code on the server and a Java applet running in Internet Explorer on the client. This basic chat program comprises only a few pages of code but manages to show the collaboration capability inherent in server-centric architectures. The program allows users to add messages to a chat listing that all attached clients can see in real time. Clients add to the common list by typing in the message and invoking a send command. Users can identify themselves via an edit string. Clients are dynamically attached to and detached from a running server-based chat application. While all clients share the common chat list, other information is client-specific and is invisible to other users. The application structure along with the Java client presentation is first defined in the Internet-enabled app builder, part of my framework (see Figure 10).


Figure 10  Chat Applet in Development
      Figure 10  Chat Applet in Development


      The C++ class definitions are defined in the builder, which uses class schema information to build connections to the Java-based dialogs. As the class structure and the Java client dialogs are designed, the application developer invokes the commands to generate the basic pieces for deploying an Internet application. On the server side, basic C++ skeleton code is created by the builder (the .h and .cpp files for the classes defined in the builder). On the client side, a Java applet is generated and automatically compiled by the builder prior to deployment to the client in the context of an HTML page. As a convenience, a Visual C++ .mak file is generated so that the server side application compiles to an .exe. The application developer then implements the app-specific logic in the member functions of the classes and compiles the app directly. Code editing, debugging, and general development all take place in the familiar Visual C++ environment. On the client side, there is no more code to write. By default, an HTML file is generated for each project created in the application builder environment. The Java applet is simply referenced in an HTML file and automatically runs when the user double-clicks on the reference. Let's first look at the C++ code you would write.

Server Side C++ Code
      Two classes are defined in the builder: ChatApp and ChatPerson. ChatApp is derived from my EosFramework, which provides basic application-level procedures. You can think of the EosFramework class as the main() of the server side application. It has functions that control various program aspects: collaboration control, application startup and shutdown, and so on. An array of strings is defined in the ChatApp class that represents all the messages sent by the various clients since the application started (see ChatApp::fChatList in Figure 11). The EosPrimitiveArray class is templated for string types and uses the MFC CArray class for its storage and basic functionality. The fChatList data member of ChatApp defines the data that is to be shared among all attached clients. Two other functions, getFirstObject and getClientViewName, are used for controlling multiple collaborative clients (see Figure 11).
      The chat application is launched for the first time when a Java thin client applet requests access to the server-based application. Since this application is designed to be collaborative, each new client attaches to the same chat application process running on the server. The rules regarding client to server connections can be defined by the developer, including attaching a single server process to multiple clients or attaching a only one client to a server process. In the application example, I want each client that attaches to the chat process to receive its own private object. This object, ChatPerson, allows the user to view common data (the chat list) as well as its own uniquely viewed information. The function ChatApp::getFirstObject is called at runtime whenever a new client attaches to the server's running process. It returns the first object that will be presented to the user as a Java applet. The state of the data associated with the ChatPerson instance will be automatically reflected in the Java applet user interface based on the dialog that was designed for that object in the application builder. The function ChatApp::getClientViewName (see Figure 12) allows the developer to control exactly which dialog designed in the builder will be initially presented to the user on the client.
      The ChatPerson object will actually be presented to the client remotely in the Java applet. One instance of ChatPerson is created for each client that attaches to the chat application. When the client detaches from the server side application, its ChatPerson instance will be deleted as well.
      The basic framework defined here allows the developer to define C++ object pointer members with a reference-counted template wrapper class. This class is useful for determining when an object is no longer interesting to other parties. This is especially useful in the case when clients attach and detach at runtime. When a client detaches from the server process, and if that client held the only reference to the object, it will automatically go out of scope. This greatly reduces object pointer management often associated with C++ based applications. The ChatPerson member fChatList is another example of automatic reference counting. A reference to the global chat list is assigned to the client in order for the ChatPerson object to attach client-specific messages. Only when the actual application terminates does the global chat list go out of scope, since its count is the number of active clients (ChatPerson instances) plus the ChatApp ownership of the array.
      Additional members of ChatPerson include a string for creating the message to send (fSendString), the name of the client sending the message (fName), and a send function that prepares the string and adds it to the global chat list (see Figure 13 and 14).
      Just a few lines of code are required to perform the basic chat application functionality. Notice that in this object connection-based approach the code that deals with application-specific logic (that of manipulating class members, and so on) doesn't have to concern itself with network connections, sockets or RPC-based code, Java AWT client control settings, or communication. Those pieces are decoupled from the class structure itself and are automatically resolved by the connection technology enabled by the builder.

The Java Generated Client
      Although by default the details of the Java client applet (see Figure 15) are hidden by the builder's code generation, it is useful to look at the client code to see better what is happening there. Comments added to the generated code indicate the various pieces incorporated on the client in order to present and access remote C++ objects running on the server. It should be noted that some generated code has been omitted for brevity. A full example of the application can be inspected at by downloading it here.
      There are three main pieces to the Java applet that are generated to successfully deploy Internet thin-clients attached to C++ server executables. These are: socket communication, dialog resource description and creation, and remote C++ member attachment and connection protocols. These three components work in concert to create a Java applet that has minimal client-side resource requirements while still providing optimal communication performance over various Internet access speeds.


Figure 16 Chat applet
      Figure 16 Chat applet


Figure 16 demonstrates the client view of the chat application running in Internet Explorer.

Summary
      To summarize, the thin client three-tier model builds on your expertise as a Visual C++ developer for creating powerful applications for the Internet. The model lets you effectively use Java, giving your applications the dynamic cross-platform deployment mechanism needed for the Internet. It also provides the client side of your applications with just enough application logic to optimally process and communicate user interaction across the Internet to the server. And, best of all, you don't have to be a Java guru to build real Internet applications that solve real problems with Java.
      The excitement surrounding the Internet is here to stay. The challenge to you is to apply the litmus test as you evaluate the profusion of frameworks and tools available in the months ahead. Then, while everyone else is trying to figure out which way they're going, you can get some real work done.

 

From the December 1996 issue of Microsoft Systems Journal.