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

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 EosFrame­work, which provides basic application-level procedures. You can think of the Eos­Framework 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).

Figure 11 chatApp.h
// chatApp.h
class ChatApp: public EosFramework
{
public:
ChatApp();
ChatApp(const ChatApp& src);
virtual ~ChatApp();
ChatApp& operator =(const ChatApp& src);

      // overriden functions from the base application framework class
      virtual EosObject  *getFirstObject();
      virtual EosAtom  getClientViewName(EosClient *client);

private:
// storage for the chat strings based on messages created by remote clients
      EosPrimitiveArray<EosString> fChatList;
};

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.

Figure 12 chatApp.cpp
// chatApp.cpp
#include "stdeos.hpp"
#include <chatApp.h>
#include <chatperson.h>

// retrieves a chat person object for each client that attaches to the server .exe
EosObject *ChatApp::getFirstObject()
{
  return (EosObject *)new ChatPerson(&fChatList);
}
// allows the server app to control the object and dialog name that is to be used at the applet level
// of the remote presentation
EosAtom ChatApp::getClientViewName(EosClient *client)
{
  return EosAtom("ChatPerson.Chat");
}

// misc. member functions
ChatApp::ChatApp() : EosFramework(),
    fChatList()
{
}
ChatApp::ChatApp(const ChatApp& src) : EosFramework(src),
    fChatList(src.fChatList)
{
}
ChatApp::~ChatApp()
{
}
ChatApp& ChatApp::operator =(const ChatApp& src)
{
    if (this != &src)
    {
        EosFramework::operator=(src);
        fChatList = src.fChatList;
    }
    return *this;
}

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 Figures 13 and 14).

Figure 13 chatperson.hpp
// chatperson.hpp
class ChatPerson: public EosObject
{
public:
ChatPerson();
ChatPerson(const ChatPerson& src);
virtual ~ChatPerson();
ChatPerson& operator =(const ChatPerson& src);

     // constructor which initializes the shared chat list
    ChatPerson(EosPrimitiveArray<EosString> *chatList);

// adds the user message to the chat list
    void send(void);

protected: 
     // client message string for adding to the chat list
      EosString fSendString;
      // the client user name to be presented with the client's message in the chat list
    EosString fName;
      // a "reference" to the shared chat list initialized within the ChatPerson constructor
    EosPrimitiveArrayRef<EosString> fChatList;
};


Figure 14 chatperson.cpp
// chatperson.cpp
#include "stdeos.hpp"
#include <chatperson.hpp>

// performs the send which adds the client string to the chat list
void ChatPerson::send()
{
  // prepare the client string and add it to the chat list
  EosString theString("(" + fName + ") " + fSendString);
  fChatList->add(theString);

  // now clear the send string field on the client
  fSendString = "";
}

// the constructor that takes a reference to the global chat list
ChatPerson::ChatPerson(EosPrimitiveArray<EosString> *chatList) : EosObject(),
    fSendString(),
    fName(),
      // assign the shared chat list object with this client
      fChatList(chatList) {}

ChatPerson::ChatPerson() : EosObject(),
    fSendString(),
    fName(),
    fChatList() {}
ChatPerson::ChatPerson(const ChatPerson& src) : EosObject(src),
    fSendString(src.fSendString),
    fName(src.fName),
    fChatList(src.fChatList) {}
ChatPerson::~ChatPerson() {}
ChatPerson& ChatPerson::operator =(const ChatPerson& src)
{
    if (this != &src)
    {
        EosObject::operator=(src);
        fSendString = src.fSendString;
        fName = src.fName;
        fChatList = src.fChatList;
    }
    return *this;
}

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.