The explosion of the Internet is being discussed within the business community almost as much as it is in public. Companies are struggling with the meaning of the Internet, how it may be exploited for new business opportunities and what external and internal threats may arise as a consequence of its exploitation by others. One thing is certain, the Internet has fundamentally and irreversibly altered the rules for constructing and delivering business information systems. This chapter explores these issues and provides some insight regarding the construction of Web enabled business systems that can extend a company's reach. In this chapter, you will learn:
Traditionally, only very large corporations had the resources to develop and deploy on-line business systems that reached beyond a campus. Generally, available solutions for wide area communications infrastructure did not exist. Companies were limited to in-house design, construction, and administration of private networks. The cost of these solutions was prohibitively high for all but the very largest and wealthiest corporations.
From a business point of view, the Internet represents a low cost, worldwide data communications utility that has unmatched geographic coverage and an explosively growing population of users. This is the Internet's primary value to businesses. The barriers of entry to worldwide transaction processing are so low that essentially every business can afford to exploit it. With proper designs and a robust security infrastructure, there is now an opportunity to extend existing online business systems directly to consumers. As with any major environmental change, businesses that understand the meaning of the Internet, Web technology and how to exploit it will be more likely to survive. Those that ignore it will find themselves at a serious competitive disadvantage.
To understand how a business might exploit the Internet and the World Wide Web, it's important to understand the objectives and limitations of the WWW infrastructure from a business system point of view.
The World Wide Web was conceived of and designed as a mechanism for publishing information in electronic form. HTML is the media of the Web. The fundamental characteristics of the Web are:
HyperText Markup Language is the medium used to construct documents for this library. The advantages to a business for using HTML as an electronic document format are:
If publishing a library of HTML documents addresses some portion of your business system requirements, then the Web and HTML documents are the answer for you. It is a great, low-cost, ubiquitous electronic document library system.
Adobe Acrobat |
Adobe has extended the Postscript technology to address electronic publishing requirements. Their suite of Acrobat products enables publishers to use high-end publishing tools, such as FrameMaker, Premier, and Illustrator to create magazine- quality publications and deliver them over the Internet as Postscript Display Format (PDF) files. The freely available Acrobat Reader, which operates much like a Web browser, can be used to view these files. A Netscape Navigator Plug-in called Amber is available; it enables you to view PDF files within the Netscape Navigator window, much as any HTML file. |
Even as a document library system, HTML files have some significant disadvantages.
Other than as an electronic document library, this technology does not support many traditional business system requirements. It is, in fact, too restrictive for most applications:
SELECT ShipToName, ShipToAddress FROM Library WHERE ShipToZip IS BETWEEN 32256 AND 32779;
Most business applications manage the creation, update, delivery, and destruction of data. As can be seen in the preceding section, this capability is not native to Web technology. It must be added.
Relational Databases are the defacto standard for managing business information today. Their success is no accident. Relational databases have been designed to efficiently process high volumes of short transactions involving simple alpha/numeric data. Relational databases have the flexibility to handle ad hoc queries and new data relationships, and they pay a lot of attention to data integrity, availability, and multiuser data sharing (concurrency control) requirements. They provide a robust set of functions for administrating, distributing, accessing, and updating business information. Microsoft provides two low cost, highly capable relational database products: Microsoft Access and Microsoft SQL Server. IBM also delivers its excellent DB2 database product for all IBM Operating Systems and Microsoft NT.
Relational databases and normalization rules provide a well reasoned structure for analyzing and designing the data aspects of business applications. Once data is structured into these well formed tables, SQL enables applications and end-users reliable access to business information.
Relational databases are today's standard for managing business information and are the preferred database technology choice when extending on-line business systems to the Internet.
WWW server software is fairly simple in concept. It receives a request (GET) for a named resource, finds the named resource, and sends the retrieved resource to the requester. Most WWW server software assumes the resource is a file, and relies on the operating system's file system to resolve the name (path) to the file so that it can be read. A major drawback of this implementation is that the name of the resource is also its address. It cannot be moved without changing its name.
Servers, however, don't need to be implemented this way. For example, the server could assume the name is constructed of three parts:
and use a relational database engine to find and deliver the record. For example, HTML could be stored in a MEMO column in an Access database. This implementation would provide a mechanism for recording additional information (properties) about the document.
Let's use the Microsoft Access/95 Orders sample database as an example. This database consists of eight tables as listed in figure 16.1.
We will use the Products table to illustrate how material could be served from a table instead of a file. Figure 16.2 shows how the Products table has been designed.
One could publish Product information by accessing this database table instead of an HTML file. An anchor could then be constructed as follows:
Information about our <A HREF="Orders/Products/ProductId/Aniseed_Syrup"> Aniseed Syrup</A> product.
The server software would connect to the Orders database and issue the following SQL statement to select the desired record:
SELECT * FROM Products WHERE ProductName='Aniseed_Syrup';
The question is why would you create a Web server that is implemented on top of a database engine instead of a file system. Several vendors have developed servers that resolve URL's to database tables and rows; you can retrieve inventory information, create guestbooks and dynamically update Web pages. The Web browser becomes a front-end to a relational database.
A primary advantage to using a relational database engine, instead of a file system, is the ability to attach structured data (properties) to each document, such as the Author, Version, Subject,etc. This enables you to understand more about the document and to deliver documents based upon the values of the document's properties. For example, a CGI script can easily deliver a list of all documents written by a specific author within a specified time period. No external indexing or search engine is required.
A database can also provide a richer set of functions managing this information. Various levels of authorization, versioning, distribution, replication, and locking can be applied to tables for better control and to ensure data security and integrity.
An immediate drawback of this approach is that the tools used to create electronic documents are designed to run on top of file systems. When you create an HTML file, image, video clip, etc.,with any existing tool, it will be saved as a file. Therefore, additional work will be imposed on authors to get their material into and out of the database.
Second, database engines have much more limited text indexing and search capability than the WAIS search engines that provide access to HTML files. WAIS indexing utilities are run periodically to analyze the library's text and create compact index files. The WAIS search engine provides support for wildcard characters, Boolean operators and nearness tests. These algorithms do not run against the library files themselves. Rather, they run against the set of indexes built by the off-line utilities. For example, when you search the Yahoo library for "good places to surf," it replies with a list of home pages with ratings of how good they match your search criteria. It rates the results through complex algorithms.
Some of these text search engines enable you to specify even more powerful queries. The Dialog search engines let you specify the distance between key words. Some examples might be:
FIND BUDGET(10N)SENATE
which would find all documents with the word BUDGET within ten words of the word SENATE;
FIND (FEMALE or WOMEN) and (WEIGHT TRAIN? OR WEIGHT LIFT? OR WEIGHTLIFT?)/ENG
which would find English language documents regarding female weightlifting articles. None of these capabilities are offered in Relational database engines.
In contrast, SQL does very well at finding records based upon specific values in small character or numeric fields. However, documents are stored as large variable length character fields in relational database systems. Wildcard characters are very limited and boolean operators are not even available for these types of fields. In fact, you cannot search for anything beyond the first 255 characters of a document stored in a Microsoft Access memo field.
Finally, database engines are optimized to store large numbers of (relatively) small records, while file systems are designed to store (relatively) small numbers of large files. A database implementation could end up being slower than a file system implementation depending on the nature of the information.
Because the use of ODBC as the API between your application to the relational database engine enables you to pick virtually any database product and change database products with minimal impact to your application, we will assume you have made this choice. The steps for accessing a database from a Web page is:
CAUTION |
Microsoft provides a set of object classes (MFC) for implementing ODBC applications. This set of object classes are not thread safe and must not be used to implement Web applications via the ISAP interface. Also, ISAPI is a multithreaded interface. While the Microsoft Access product can be used as the database system for CGI applications, it can not be used for ISAPI applications. MS SQL Server 6.0 or newer must be used when implementing ISAPI database applications. |
An ODBC Software Development Kit is available from the Microsoft Developer's Network (MSDN) at a Level 2 subscription. A Level 2 MSDN membership provides you with the latest versions of Windows and Windows NT Workstation, including prerelease versions. It also provides you with the Development Platform-a set of CDs updated quarterly that contains all the software development kits (SDKs), device driver kits (DDKs), and Windows and Windows NT Workstation operating systems from Microsoft, both domestic and international versions. For information about subscribing to the MSDN, see http://www.microsoft.com/msdn.
The ODBC SDK provides you with the necessary header files, help files, and DLL's to implement ODBC applications. It also provides you with test utilities, trace utilities, administration and setup utilities, and sample application source code.
Use the ODBC/32 administration utility provided by the ODBC/SDK or the ODBC Service Applet on the control panel to set up and identify your database as an ODBC data source. In either case, you will see the ODBC Administration window as shown in figure 16.3. Each driver provides a different setup application. If you're using the Microsoft ODBC driver to connect to an MS Access or SQL Server database, make your database a System Database. This means that any user logged into this machine is able to see and potentially use this data source. Because the data source has its own logon ID and password, it is still protected.
Figure 16.3 : The ODBC applet in the control pane must be used to specify an ODBC data source.
With the Data Source dialog box on the screen, click System Database, then Add, Microsoft Access (.mdb) driver, and OK. This brings you to the Setup dialog box. Use the Select button to find the database on your hard drive (the database must be on a hard drive on the local system of the Web server). Enter the Data Source name. Accuracy is very important because ODBC is case-sensitive. When connecting to ODBC, you define the name of the datasource, not the name of the database. Click Advanced and set the password to simple with the Login name admin. Again this is important, ODBC will not connect to a datasource unless this information is accurate.
This is a more pertinent question. The Internet offers companies a low cost network utility. Some aspects of the World Wide Web technology enable companies to exploit this utility. What's important is reaching a broader population of potential customers over a wider geographic area. While HTML enables you to construct electronic documents and publish them on the Web, most business applications have nothing to do with publishing. They manage the processing of data to conduct business.
Business applications process structured information. The fields in an HTML FORM structure data values into named fields. The values entered into an HTML FORM can be extracted and stored into a relational table's rows and columns.
When information is named and structured, it can be processed. An application can verify that a field contains numbers when it should and that the numbers are entered in a valid format. Once validated, the value of a field can be added, subtracted, and averaged. These are the kinds of things business applications do.
It may take a very complex query to locate the precise information a person needs for a business activity; for example, "Find all the people who entered orders for black wingtip shoes in the month of December last year." If the information is stored in an SQL database, the above request might be satisfied by the following SQL select statement:
SELECT Name, Address FROM CustomerOrders WHERE Item = 'SHOE' and Style = 'WINGTIP' and OrderDate >= '#12/01/1995' and OrderDate < '#1/1/1996';
NOTE |
Indexing engines and Web crawlers do not have any visibility to information stored in a database that is reached this way. |
By its nature, a business transaction changes the state of information in the business system. An order is placed, a payment is made, and the order is shipped. HTML and file systems do not provide the necessary structure, concurrency controls, or transaction support to meet these requirements.
Traditional business transactions were implemented using dumb terminals, such as IBM 3270's, 5250's, 3101's, DEC VT100's, VT220's and VT320's. These character-based devices were all that was necessary to enable people to retrieve, enter, update, and delete business information. Business transactions were designed and coded specifically to the company's dumb terminal type. An application coded to run on an IBM 3270 could not be delivered to an operator on a DEC VT100.
HTML forms provide an operator interface and level of function similar to the IBM 3270. The primary difference is that an application can be designed and coded to interact with an HTML form rather than any specific piece of hardware. Web browsers interpret the form and present it to the operator regardless of whether it is running on a UNIX workstation, an IBM PC, or an Apple Macintosh. This Web technology isolates the business application developer from hardware implementation concerns. The application is developed to a form abstraction rather than to a hardware implementation.
This makes Web technology attractive to business application developers regardless of whether or not they ever expect to deliver their applications across the Internet. Web browsers and HTML forms provide a low-cost, high-function delivery mechanism that isolates their investment in application code from workstation, operating system, and hardware changes.
Before the advent of Web technology (specifically graphical browsers), a company wanting to present a business application to PC users had only two choices:
For the most part, the first option isolates applications from the chronic migration to new hardware and operating system levels. PC users, upgrading to Windows 95 or NT, could upgrade or replace the emulator code if necessary, without impact to the business applications at all. The drawback is that users have become unimpressed with the old, dumb terminal user interface. Its "un-usability" and "un-friendliness" are contrasted with ever improving windows based Graphical User Interfaces (GUIs).
The second option brought MIS into a new world of complexity. Programming, configuration management, and control (keeping the distributed application code current and in sync with hardware and OS changes) were significantly more complex.
Web technology is a middle ground that enables a company to present an acceptable user interface to a wide population of users, who have an unknown configuration, in a cost effective way. HTML and the browser provide some elements of a GUI interface. No company application code needs to be installed on the user's configuration and programming to an HTML interface is relatively easy. With extensions like the Java Abstract Windows Toolkit (AWT), user interfaces can be created which approach the sophistication of those created using the Microsoft Windows Foundation Classes.
Some business information is graphical in nature (X-rays) and some is best understood when rendered as a graphic (trend charts). Web technology enables applications to deliver these forms of information simply and effectively, something that dumb terminal emulators cannot do.
Web technology enables the use of the Internet as a low-cost, wide-area network utility. It provides an acceptable user interface to a broad population of users. It isolates the company's application code from distribution and configuration issues. Are there any drawbacks or inhibitors? To find out, let's explore the nature of a business transaction.
A computerized business transaction is a dialog between a human and one or more applications; it results in a change to the state of the business system.
An atomic transaction consists of a single exchange of information between an operator and an application:
On the other hand, a conversational transaction consists of multiple exchanges of information between an operator and one or more applications:
An example might be an ever-narrowing search for information where the operator enters some broad parameters about lease agreements, the application responds with a list of hits, the operator enters additional search constraints to be applied against the already qualified results, and so on. The operator finally finds the set of interesting lease agreements and wants them deleted.
The application for the atomic transaction does not need to know anything beyond the information contained in the single form it received. It contains the necessary and sufficient information for the application to completely perform its function. In contrast, the application in the conversational transaction is much more complex. It needs to know what state of the conversation it is in and the information it gathered earlier in the conversation before it can determine what it should do next. Additionally, it can be conversing with several users at the same time. In order to hold a conversation with an operator, an application needs three pieces of information when it receives a form to process:
While HyperText Transport Protocol (HTTP) and CGI can easily support atomic transactions, it does not directly provide the necessary support for conversational transactions.
Maintaining the State of the Conversation. HTTP coupled with CGI scripts, is a sessionless, stateless protocol. When an application completes processing a form and sends a new form to the operator in reply, the program's database connection is closed and the application terminates. When the new form is posted to the application, a new application (it may or may not be the same program) is started and the new form is processed. The second application has no memory of any prior processing activity or state and neither the server nor the browser provides sufficient information to the application for it to identify which conversation a form belongs to, or even whether or not the form is part of a conversation. Without these vital pieces of information, the application does not have a way to hold a conversation.
Logical Units of Work. A logical unit of work is a set of changes to the state of business information stored in the database that must be completed entirely, or not completed at all. If the set of changes is only partially completed, the business information in question has lost its integrity and may lead to unpredictable business and business system behaviors, which is clearly an undesirable situation.
Database systems support the concept of a logical unit of work. They provide applications with three operations to manage logical units of work:
Begin Transaction marks the start of the logical unit of work. All changes to the state of information from that point forward belong to one logical unit of work. When an application completes a transaction, it can use the Commit operation to commit the state changes to the business system, or the Rollback operation to empty the set, as if the application had never run.
NOTE |
An application must issue one of these closing commands before it ends. |
Atomic transactions can exploit these operations in a simple and straightforward manner. Conversational transactions cannot. The problem for conversational transactions is a consequence of the stateless nature of CGI and the implementation of relational database engines. When a CGI application has completed the processing of a form and has replied to the operator, it ends. What happens to data changes it has made during that processing? Relational database engines require an application to either commit or rollback its work before it ends. Let's look at a vehicle purchase application that must update both customer information and mark a vehicle as sold.
What happens if the customer backs out of the sale between steps 2 and 3, the customer's credit check fails, the sold vehicle is not on file, or the network connection is lost? If anything unusual occurs between steps 2 and step 4, the state of the business information is incomplete. We have a customer record indicating he bought a car for a specific price, but we do not know what car he bought. This is because the application did not have a way to define a logical unit of work to the database that spanned the changes in steps 2 and 4. From the database's point of view, step 2 was one logical unit of work and so was step 4.
Also, because the application was terminated and restarted, any pending updates from the first interaction with the operator must have been committed or rolled back independent of the current interaction. This is a severe limitation on the design and implementation of business applications.
Suppose an account manager was looking over delinquent lease customers and came to the conclusion that a car should be repossessed based upon a customer's account history. While the account manager was reviewing the history, someone else was applying payments. The account manager repossesses the car based upon the information he/she reviewed over the past 20 minutes. However, at the time the repossession transaction completed, the customer's account had been posted with sufficient funds to make it current! This company will have one very irate customer on their hands when the car is towed away!
In order to guarantee that the repossession transaction was correct from a business point of view, the transaction should make sure that the information that the operator used during the life of the conversation to base her/his decisions on is still true at the end of the transaction.
Concurrency controls are needed when multiple people want to share the ability to update a set of resources. These controls either prevent the actions of one operator from invalidating the actions of another or they ensure that when an operator's actions have been invalidated, he/she is notified of the fact. There are two concurrency control strategies:
The earlier problems are now partially solved with the new Internet Server Application Programming Interface (ISAPI). ISAPI enables an application to maintain state information and a database connection between interactions with a browser. Therefore, business applications should always be implemented using ISAPI instead of CGI.
An ISAPI application can create a persistent thread for each concurrent conversation. It can also create a reliable conversation identifier using three pieces of information:
By passing the conversation identifier to the browser as a hidden input field, the reply from the browser can be matched with a conversational thread in the application. The application root can then pass the input stream to the appropriate waiting thread to continue the conversation. Since the ISAPI application never terminates, the relationship between a conversation identifier and an application thread can be maintained across interactions with the browser.
Also, the state of the application's database connection, locks and uncommitted updates are maintained. This enables the application to commit or rollback the entire logical unit of work defined by the conversation.
Internet Draft proposals |
There is an Internet Draft proposal to the HTTP Working Group from Bell Laboratories and Netscape Communications Corporation which improves upon the HIDDEN field approach. The proposal specifies how a business application can send a piece of state information in the HTTP response header, which the client browser will store. Included in that state object is a description of the range of URLs for which that state is valid. Any future HTTP requests made by the client, which fall in that range will include a transmittal of the current value of the state object from the client back to the server. The state object is called a cookie. Cookies are a general mechanism which server side connections can use to both store and retrieve information on the client side of the connection. Simple forms of cookie support have been implemented in Netscape 3.0 and Internet Explorer 3.0. |
The heavily annotated pseudo code provided in listings 16.1, 2, and 3 illustrates how a conversational transaction could be implemented based upon an overall OO code framework. First, we will look at the ISAPI entry point code, which uses a context class and a transaction class. Next, we will review the context class, which establishes the processing context for the transaction and an implementation of a transaction. The transaction class relies on an overall application framework which we will discuss last.
The code shown in listing 16.1 is provided to illustrate how to set up the ISAPI entry points. Comments are provided for explanation.
Listing 16.1
// Simple ISAPI Test Program. #include <windows.h> #include "httpext.h" #include "transact.hpp" BOOL WINAPI _export GetExtensionVersion (HSE_VERSION_INFO *pVer) { pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR); lstrcpyn(pVer->lpszExtensionDesc,"www.ipworld.com ISAPI sample code.", HSE_MAX_EXT_DLL_NAME_LEN); return TRUE; } DWORD WINAPI _export HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpEcb) { string *transactionId; TCtransaction *workTransaction; TCtransaction *transaction; TCcontext *context; string *workstationAddress; context = new TCcontext(lpEcb); transactionId = context->IdentifyTransaction(); workstationAddress = context->IdentifyWorkstation(); if (workstationAddress == NULL) { context->ReportError ("500 HttpExtensionProc unable to determine the client address."); return HSE_STATUS_ERROR; } if (transactionId == NULL) transaction = new TCtransaction(workstationAddress->c_str()); else { workTransaction = new TCtransaction(); transaction = workTransaction->GetTransaction(transactionId->c_str()); delete workTransaction; } if (transaction == NULL) { context->ReportError("500 Unknown transaction."); return HSE_STATUS_ERROR; } int rtnCode; rtnCode = transaction->Run(context); delete context; if (rtnCode == 99) { delete transaction; return HSE_STATUS_SUCCESS_AND_KEEP_CONN; } return HSE_STATUS_SUCCESS; }
The code shown in listing 16.1 relies on methods defined for the Context and Transaction classes. The Context class shown in listing 16.2 encapsulates the ISAPI interfaces and hides them from the rest of the classes. Normally, there would be a pure virtual class definition to define the interfaces and the following code would be an ISAPI context subclass implementation. This code also illustrates how to obtain environmental information provided by the http.
Listing 16.2
The Context Class Header File #include <cstring.h> #include "httpext.h" class TCcontext { private: LPEXTENSION_CONTROL_BLOCK lpEcb; public: TCcontext(LPEXTENSION_CONTROL_BLOCK lpE); void DebugLog(char *msg); void ReportError(CHAR szErrMsg[]); string *IdentifyTransaction(); string *IdentifyWorkstation(); int WriteClient(CHAR szOutBuff[]); string *ReadClient(); BOOL EchoStream(char *outBuffer); }; TCcontext::TCcontext(LPEXTENSION_CONTROL_BLOCK lpE) { lpEcb = lpE; return; } void TCcontext::DebugLog(char *msg) { fstream ErrLog("isapi.log",ios::app); ErrLog << msg << "\n"; ErrLog.close(); } void TCcontext::ReportError(CHAR szErrMsg[]) { HCONN hConn; DWORD dwSize; LPDWORD lpdwSize; DebugLog(szErrMsg); dwSize = strlen(szErrMsg); lpdwSize = &dwSize; hConn = lpEcb->ConnID; lpEcb->ServerSupportFunction(hConn,HSE_REQ_SEND_RESPONSE_HEADER,szErrMsg, lpdwSize, (LPDWORD)szErrMsg); return; } int TCcontext::WriteClient(CHAR szOutBuff[]) { BOOL rtnCode; HCONN hConn; DWORD dwSize; LPDWORD lpdwSize; int iSize; char szHeadBuff[1024]; char szHeadForm[] = "Content-Type: text/html\r\nContent-Length: %06i\r\nConnection: Keep Alive\r\n\r\n"; lpdwSize = &dwSize; hConn = lpEcb->ConnID; iSize = strlen(szOutBuff); sprintf(szHeadBuff, szHeadForm, iSize); dwSize = strlen(szHeadBuff); rtnCode = lpEcb->ServerSupportFunction(hConn,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK", lpdwSize, (LPDWORD)szHeadBuff); if (!rtnCode) { ReportError("500 ContextWriteClient ServerSupportFunction error."); return HSE_STATUS_ERROR; } DWORD dwReserved; dwReserved = 0; dwSize = strlen(szOutBuff); return lpEcb->WriteClient(hConn, szOutBuff, lpdwSize, dwReserved); } string *TCcontext::ReadClient() { HCONN hConn; DWORD dwSize; LPDWORD lpdwSize; lpdwSize = &dwSize; // Fill the buffer with new inbound data. // Request it via ReadClient if necessary. // // Copy the inbound data to our buffer DWORD nBytesToCopy; string *inBuffer; inBuffer = new string((char *)lpEcb->lpbData); // Calculate how much is left and go get it. nBytes = lpEcb->cbTotalBytes - lpEcb->cbAvailable; if (nBytes > 0) { // Let's go get the rest of the data work = new char[nBytes]; if (!lpEcb->ReadClient(lpEcb->ConnID, work, (LPDWORD) &nBytes)) { delete work; return NULL; } inBuffer->append(work); } return inBuffer; } string *TCcontext::IdentifyTransaction() { char szInBuff[4096]; char *workptr1; char *workptr2; strcpy(szInBuff, (char *)lpEcb->lpbData); workptr1 = strstr(szInBuff,"SESSIONID"); if (workptr1 == NULL) return NULL; workptr2 = strtok(workptr1,"="); if (workptr2 == NULL) return NULL; workptr1 = strtok(NULL,"&"); if (workptr1 == NULL) return NULL; urlDecode(workptr1); strcvrt(workptr1, '+', ' '); return new string(workptr1); } string *TCcontext::IdentifyWorkstation() { CHAR szInBuff[256]; DWORD dwSize; BOOL rtnCode; LPDWORD lpdwSize; dwSize = sizeof(szInBuff); lpdwSize = &dwSize; rtnCode = lpEcb->GetServerVariable(lpEcb->ConnID, "REMOTE_ADDR", szInBuff, lpdwSize); if (rtnCode) return new string(szInBuff); return NULL; } BOOL TCcontext::EchoStream(char *szOutBuff) { BOOL rtnCode; char szWorkBuff[256]; CHAR szInBuff[8192]; HCONN hConn; DWORD dwSize; LPDWORD lpdwSize; lpdwSize = &dwSize; hConn = lpEcb->ConnID; strcat(szOutBuff,"<H1 align=center>Form Echo</H1>"); strcat(szOutBuff,"<PRE>"); sprintf(szWorkBuff,"Connection handle = %d<BR>", hConn); strcat(szOutBuff,szWorkBuff); strcat(szOutBuff,"Request Method = "); strcat(szOutBuff,lpEcb->lpszMethod); strcat(szOutBuff,"<BR>"); strcat(szOutBuff,"Query String = "); strcat(szOutBuff,lpEcb->lpszQueryString); strcat(szOutBuff,"<BR>"); strcat(szOutBuff,"Path Information = "); strcat(szOutBuff,lpEcb->lpszPathInfo); strcat(szOutBuff,"<BR>"); strcat(szOutBuff,"Path Translated = "); strcat(szOutBuff,lpEcb->lpszPathTranslated); strcat(szOutBuff,"<BR>"); dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "CONTENT_TYPE", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"Content type = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } rtnCode = lpEcb->GetServerVariable(hConn, "SERVER_PROTOCOL", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"Server protocol = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "HTTP_ACCEPT", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"HTTP Accept = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "REMOTE_ADDR", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"Remote address = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "REMOTE_HOST", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"Remote host = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "REMOTE_USER", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"Remote user = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "SERVER_NAME", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"Server name = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "SCRIPT_NAME", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"Script name = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "CONTENT_LENGTH", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"Content length = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } dwSize = sizeof(szInBuff); rtnCode = lpEcb->GetServerVariable(hConn, "HTTP_CONNECTION", szInBuff, lpdwSize); if (rtnCode) { strcat(szOutBuff,"HTTP connection = "); strcat(szOutBuff,szInBuff); strcat(szOutBuff,"<BR>"); } strcat(szOutBuff,"<BR>"); strncpy(szInBuff,(char *)lpEcb->lpbData,8192); char *p; p = strtok(szInBuff,"&"); while (p!=NULL) { urlDecode(p); strcvrt(p, '+', ' '); strcat(szOutBuff,p); strcat(szOutBuff,"<BR>"); p = strtok(NULL,"&"); } strcat(szOutBuff,"</PRE>"); return TRUE; }
The Context class encapsulates the ISAPI interfaces so the transaction code can be written without knowledge of them.
The Transaction class shown in listing 16.3 encapsulates the logic of the application program. When the ISAPI entrypoint gets control, it determines whether the input is a continuation of an existing conversation, or the start of a new conversation. If it is the start of a new conversation, it creates a new instance of the transaction class. There would normally be an abstract class definition for transactions which would implement all Transaction methods except the Run() method. Each application transaction would be a subclass of the abstract transactions and would implement its specific Run() method logic.
Listing 16.3
The Transaction Class Header File class TCtransaction { private: static TCtransaction* head; TCtransaction *next; string *transactionId; int state; public: TCtransaction(const char *ipAddress = "000.000.000.000"); ~TCtransaction(); void Purge(); void SetState(int newState); int GetState(const char *sid); int State() const; TCtransaction *GetTransaction(const char *sid); const char *TransactionId() const; int Run(TCcontext *context); }; TCtransaction* TCtransaction::head =0; // Transaction constructor. TCtransaction::TCtransaction(const char *ipAddress) { TTime now; string sep("^"); if (0 == strcmp("000.000.000.000",ipAddress)) return; transactionId = new string(ipAddress); transactionId->append(sep); transactionId->append(now.AsString()); TCtransaction *cursor = head; if (cursor == 0) { // If the list is empty, then set the head to head = this; // point to this transaction. } else { // Otherwise, run the list and add this one to while(cursor->next != 0) cursor = cursor->next; cursor->next = this; // the end. } state = 0; // Initialize the state to zero and next = 0; // the next pointer to zero. return; } TCtransaction::~TCtransaction() { // Remove the transaction from the list. // Delete the transaction. return; } // Return the state of the transaction. int TCtransaction::State() const { return state; } // Return the transaction ID. const char *TCtransaction::TransactionId() const { return transactionId->c_str(); } // Get a transaction pointer by specifying it's ID. TCtransaction *TCtransaction::GetTransaction(const char *sid) { TCtransaction *cursor = head; // Start at the head. if (cursor == 0) return NULL; // Nothing on the list! if (*cursor->transactionId == sid) return cursor; // Found it at the head. while(cursor->next != 0) { // Run down the list looking for it. cursor = cursor->next; if (*cursor->transactionId == sid) return cursor; // Found it. } return NULL; // No such transaction was found. } // Set the state of the transaction. void TCtransaction::SetState(int newState) { state = newState; return; } // ****************************************************************** // Normally TCtransaction::Run() would be defined as a pure virtual function // and this example would be defined as a subclass. The ISAPI entry // point would create a new instance of the subclass. But for brevity // we defined the example logic right here. // This code is dependent on the resto of the www.ipworld.com framework. int TCtransaction::Run(TCcontext *context) { string *inStream; // Borland string class. // Send the HTML prolog out. ClassifiedProlog(context, szOutBuff); // Read in the data from the client inStream = context->ReadClient(); switch(state) { case 0: // Manufacture and send a record entry form to the client. // Create an instance of the Classified Record entry form which // is defined as a subclass of HTML forms. TCclassifiedEntryForm entryForm(context, "Classified Entry"); // There maybe something of interest in the input stream that should // go back out. entryForm.Input(inStream->c_str()); // Manufacture the entry form html based upon it's definition. entryForm.SendEntryForm(szOutBuff); // Send the epilog. ClassifiedEpilog(szOutBuff); // Write it back to the client. if (!context->WriteClient(szOutBuff)) return HSE_STATUS_ERROR; SetState(1); // Set the next state of this transaction. return 1; case 1: // The entry form should now be filled out and full of values. // Read in the data from the client inStream = context->ReadClient(); // Create an instance of the Classified Record entry form which // is defined as a subclass of HTML forms. TCclassifiedEntryForm entryForm(context, "Classified Entry"); TCattribute *workAttribute; // Ask the entry form to load itself from the stream. entryForm.Input(inStream->c_str()); // Process the form. // If requested, perform the action. workAttribute = entryForm.FindAttribute("ACTIONCODE"); if (workAttribute == 0) context->ReportError( "Internal error. No action code specified in this form."); if (*workAttribute == "Store") { // Create an instance of the classified record object class whic // is defined as an odbc subclass of record objects. TCclassifiedRecord classifiedRec(context, "ipworld", "Public","Open", "Classified"); // Ask the form to copy its values to the record. entryForm.CopyTo(classifiedRec); // Ask the record to create itself (inserts a record into the database). if (classifiedRec.Create()) { workAttribute = classifiedRec.FindAttribute("OID"); if (!workAttribute) context->ReportError("Internal Error. No OID for this record."); strcat(szOutBuff,"<p> Your event has been successfully added to the database.\n"); strcat(szOutBuff,"Thank you.\n"); strcat(szOutBuff,"<b>Your record ID is: "); strcat(szOutBuff,workAttribute->Value()); strcat(szOutBuff,"</b>.\n"); strcat(szOutBuff,"Please record this number and the password you supplied in case\n"); strcat(szOutBuff,"you want to update or delete this item in the future.\n"); strcat(szOutBuff,"Thank you.\n"); } else { strcat(szOutBuff,"Oops... Something bad happened.\n"); } } else { // Assume the operator wants to preview the entry. // Create an instance of the classified display form. TCclassifiedDisplayForm displayForm("Display Classified"); // Ask the entry form to copy its values to the display form. entryForm.CopyTo(displayForm); // Ask the display form to construct HTML based upon it's definition. displayForm.SendDisplayForm(szOutBuff); } // Send the epilog. ClassifiedEpilog(szOutBuff); // Write it back to the client. if (!context->WriteClient(szOutBuff)) return HSE_STATUS_ERROR; SetState(0); return HSE_STATUS_SUCCESS; default: context->ReportError("500 Unrecognized state in main routine."); return HSE_STATUS_ERROR; } return 99; }
The Transaction class implements the application logic. Multiple instances of the transaction could be running concurrently, each with its own thread.
The Browser's Back Control |
When it saves a page to its cache, some browsers identify the HTML "page" by its URL. This enables it to retrieve a page out of its cache when referenced again. However, responses generated by applications do not have a complete URL. There is no file name. Some browsers use the application's name in lieu of a file name. This leads to a design problem for conversational transactions. Suppose you create one application that manages the entire conversation. Each form in the conversation is posted to the same application and each reply comes from the same application. Most browsers overlay the prior reply with the current one. If part of your application relies on the browser's "Back" control to allow the operator to return to earlier generated HTML pages, you are in trouble. It will not work. For these browsers, each generated reply must come from a different application in order to keep the browser from overlaying earlier generated pages. The Netscape Navigator browser does not have this design flaw. |
There is nothing so dynamic in the computer industry today as the explosion of Internet technologies. If it is true that designing business applications for change is a primary objective of software development, then it is especially true for designing business applications that use Internet technologies.
Object technology promises you the ability to deliver high quality, low cost, low maintenance applications that are resilient to change. This promise, however, is conditional upon the "proper" exploitation of the technology.
Many developers use case tools that enable them to exploit technology objects (such as buttons, frames, and so on) to create nice GUI interfaces. They then write procedures that run when certain events occur. These procedures are variously called callbacks, or event handlers. The entire application is designed within the framework provided by the case tool's technology objects. This, however, is not constructive object-oriented application development. It is procedural application development that is designed to operate within a specific object-oriented GUI framework.
The Object Management Group (OMG-the largest software consortium in the world) believes that by exploiting the constructive capabilities of the technology one can achieve its promises. That is, the characteristics and behavior of objects should be abstracted to provide a general capability over a wide range of specialized sub-classes. The abstract interface can then be used by all clients, regardless of specialized implementations.
This paradigm is generally accepted for technology objects. That is, it's accepted that there should be a general interface to buttons that enables you to put a label on them or re-size them, regardless of whether they surface as a Motif button or a Microsoft Windows button. For the most part, this philosophy has not been internalized to the point of affecting how you design business objects.
Suppose you want to create a business application that uses HTML forms for its user interface. Suppose you want your application to be able to dynamically determine if the browser is Java enabled, and use a Java form instead of an HTML form. When a posted Java form is received, it will require a different kind of processing. You want this technology change to occur without changing more than one or two lines of code.
In order to make this a reality, you need to write your application to an abstract object class called something like TCform. One specialized type of form might be an HTML form class called TChtmlForm, and another might be a Java form class called TCjavaForm. If the application is written to the abstract TCform interface, it has the ability to dynamically switch between an HTML user interface and a Java user interface. The code might look like:
rtnCode = lpEcb->GetServerVariable(hConn, "HTTP_ACCEPT", szInBuff, lpdwSize); if (strloc(szInBuff,"Java")) myForm = new TCjavaForm; else myForm = new TChtmlForm;
From that point on, all the code is the same.
Under the OMG design concept, developers first design the basic building blocks of an application and then they use these building blocks to construct applications. Using Legos as an analogy, first you need to design the Legos pieces, and then you can use the pieces to construct things. When Legos first came out, they were designed to be used to construct houses and buildings. You had about six basic pieces which enabled the construction of any building design (except round ones).
Embracing the OMG concept of objects and exploiting this technology successfully means you need to create a set of abstract classes that can be assembled to construct any business application. You must ask the questions, what is the nature of a business application, and what building blocks are essential to its construction?
The following is a generalized model of business applications:
Characteristic | Definition |
Static Model | The state or characteristics of a business object. |
Algorithms | The business algorithms used to perform calculations. |
Constraint Logic | The business logic employed to regulate state changes in the object space; sometimes known as integrity rules. |
Operations | The business object's methods and interfaces to those methods. |
Transactions | A controller that prescribes a dialog with an operator to accomplish a logical unit of work. |
Workflows | A process for completing a set of transactions. |
This chart corresponds roughly to understanding that an abstract
house is composed of a roof, a floor, walls, doorways, and window
openings. It does not necessarily tell you what fundamental blocks
are needed, but it does outline the abstract requirements you
are trying to satisfy. The next step is to identify a set of abstract
classes that can be used to construct this abstract application.
They might include:
Component | Definition |
Domains | Responsible for verifying values, rendering values in various formats and units of measure, relational operations, algebraic operations, and so on. |
Attributes | Responsible for its state or value. |
Objects | Responsible for knowing characteristics or attributes of some real world entity or event and for the persistent storage of their state. |
Rules | Enforces constraints. |
Events | Can be used to trigger the execution of rules. |
Forms | Responsible for rendering themselves to operators and for verifying their validity. |
Transactions | Responsible for controlling the operator dialog and completing or rolling back a logical unit of work. |
Workflows | Responsible for managing the process to complete a set of transactions. |
This object analysis has shown that these are a sufficient set of basic abstract classes that enable the construction of Web-based business transactions.
The next step is to take this analysis into object design. You used the patterns described in Design Patterns, Elements of Reusable Object-Oriented Software by Gamma et. al. to help design a highly reusable abstract application model.