Web-Database Connectivity

Dr. Dobb's Sourcebook May/June 1997

Developing and maintaining web sites efficiently

By Z. Peter Lazar

Peter is a senior engineer with BTG Inc. and the designer of many web sites. He can be contacted at http:// www.btg.com/~plazar/.

Developing sophisticated web applications that interact with databases is a complex task. In the past year alone, numerous toolsets have appeared that try to make this process easier. The tools typically take one of two approaches: They either require programming or generate code. In this article, I describe why I believe the programmatic approach to developing complex web sites is preferable to the code-generation approach.

Traditional comparisons of software packages weigh issues such as execution speed, transaction time, executable size, and the like. The basis of my comparison is more pragmatic, however, focusing instead on the cost-effectiveness of web-site implementation and maintenance.

Maintainable web systems are simple and modular, and their construction costs are minimized in both the short and long term. In the short term, these characteristics improve maintainability and save money by reducing complexity. Simplicity and modularity become even more critical when new programmers must understand and change the system without input from the original developers.

Scalable systems reduce up-front hardware and database infrastructure costs and facilitate future growth. In a scalable system, porting from low-cost hardware to powerful, expensive hardware is virtually risk free because it involves no additional software development. In scalable systems, the same is true when porting from an inexpensive relational database to an expensive, industrial-strength database. Thus, scalable systems allow you to start with an inexpensive hardware and database solution and upgrade to a more powerful infrastructure when additional horsepower is necessary. To be truly scalable, a web site must also be extendable. That is, even though it may start small with simple functions, more complicated functions can be incorporated as the system grows. A truly scalable system is also, by definition, a high-performance system because it is able to scale up to powerful hardware and databases.

Toolset Overview

Among the more popular web-database middleware packages are Bluestone Sapphire/Web 2.1, NeXT WebObjects 3.0, Netscape LiveWire 1.0, and Allaire Cold Fusion 2.0. The first two take the code-generation approach; the last two take the programming-language approach.

Web development with Sapphire/Web and WebObjects involves automatic code generation. Rather than write code, you specify appearance and functionality via GUI screens. The system then translates that specification to template files, query definition files, binding files, and HTML-generation code.

The templates files are ordinary, static HTML files, except that they contain placeholders where HTML is generated at run time. The query definition files are in a proprietary format and capture graphically created database queries. The binding files, also in a proprietary format, map the template files to the HTML-generation code. (This code is in C in Sapphire/Web, and in a proprietary scripting language called "WebScript" in WebObjects.) During web-site run time, the code generates HTML that fills in the placeholders in the template files.

In most cases, the user interface generates template files, query definition files, binding files, and HTML-generation code. Developers can also define these components directly in the underlying language: However, this forfeits the ability to automatically generate them in the future.

Development with LiveWire and Cold Fusion, on the other hand, involve embedding scripting-language statements in HTML. Any visual web-page design tool can be used to generate the initial HTML, and any text editor can be used to embed the scripting-language statements. LiveWire and Cold Fusion require no special user interface for specifying web-site appearance or functionality.

Both the HTML and scripting-language code are typically combined in the same file. The HTML defines the appearance. The scripting-language statements specify functionality and indicate, via SQL statements, how to retrieve and format database data. At run time, the system converts the scripting statements and retrieved data into HTML and sends it to the user.

Cold Fusion uses a proprietary scripting language called "Cold Fusion Markup Language" (CFML). LiveWire uses JavaScript, a scripting language defined by Netscape and Sun. LiveWire's JavaScript is the same as that used in Netscape and Microsoft browsers, but it is server-side oriented, rather than client side. It also contains functions for accessing databases.

Figure 1 provides an overview of the LiveWire architecture. The LiveWire Manager is a component of the Netscape Enterprise Server. It translates server-side JavaScript and database data into HTML, then sends it to the user. Multiple database connections are established per application. Each connection can be shared among multiple users.

Figure 2 describes the Cold Fusion architecture, in which the Cold Fusion Application Server is a background process that translates Cold Fusion Markup Language and database data to HTML, then sends it to the web server. A single ODBC connection is maintained per application. It is shared among multiple users.

Figure 3 shows the Sapphire/Web architecture. Sapphire/Web applications can run either as traditional CGIs or as Application Servers. Application Servers generate one or more web pages. New Application Server processes are spawned for each concurrent user accessing the system. They shut down after a configured amount of time or number of web transactions. The Application Server Manager is a background process that schedules and manages Application Servers. The Web Server Extension is a background process that interfaces the web server with the Application Server Manager and Application Servers. Database connections are maintained for the life of a CGI or Application Server.

Finally, Figure 4 presents the WebObjects architecture. The WebObjects Adapter Server is a CGI that interfaces the web server with application executables. As it is a CGI and not memory resident, it lives for the duration of one web transaction. Application executables generate one or more web pages. New Application Server processes are created by the Adapter Server for each concurrent user accessing the system. They shut down after a configured amount of time or number of web transactions. Database connections are maintained for the life of an application executable.

Toolset Comparison

Figure 5 depicts how the cost effectiveness of a system design is based on maintainability and scalability and, likewise, how maintainability and scalability are based on design characteristics such as simplicity, modularity, portability, extendability, and performance. As Figure 5 illustrates, these are also desirable characteristics in web-site design.

Simplicity. Simplicity makes life easier for the developer -- the person who must maintain or extend a system built by someone else. To be simple, a web-database site should have few components, few concepts, and code that is easy to follow.

LiveWire and Cold Fusion contain few components. In LiveWire and Cold Fusion, HTML statements and functional logic are located in the same HTML file. This is simpler than the multiple files and user-interface screens used by Sapphire/Web and WebObjects. LiveWire and Cold Fusion not only have minimal internal components, they also do not rely on external compilers, special libraries, and other products. (Sapphire/Web relies on a separate C++ compiler on Windows NT and UNIX. New versions of these external products could bring about new bugs or incompatibilities, and may cause portability problems. WebObjects has a built-in compiler that reduces this problem, but adds a different kind of complexity by supporting two styles of its proprietary WebScript language and, in a future release, JavaScript.)

LiveWire and Cold Fusion involve few concepts, and the basic concepts necessary to develop a system are all specified in the same file. In Sapphire/Web and WebObjects, HTML templates, binding files, and C or WebScript functionality are manipulated with a profusion of screens and menus.

Embedded scripting-language code is easier to follow than automatically generated code. Developers unfamiliar with a particular web system built with LiveWire or Cold Fusion need only look in one file to determine how a particular web page operates. Formatting, control flow, and data access are all specified in the same place. With Sapphire/Web or WebObjects, the developer must look in a series of user-interface screens or try to decipher machine-generated source code saved in a collection of different files. Machine-generated code, which is inherently difficult to read, is especially hard to follow when it is in a low-level language such as C, as is the case for Sapphire/Web. In contrast, LiveWire and Cold Fusion involve human-generated code.

The driving force behind using an application generator is to simplify development by reducing the need to manually write code. In practice, however, code generators simply transfer the complexity of specifying a system from the code to the code generator's user interface. Also, coding will still be necessary for the parts of the system that cannot be generated automatically. Ultimately, you will have to become expert at both a complex, proprietary user interface and at reading and modifying machine-generated code.

This is not the case for programming-language-based web development, since there is no special user interface to learn and the code is high level and human generated.

Modularity. Being able to compartmentalize the underlying code and data for a web system makes the code easier to understand and facilitates reuse. The most modular web-database systems minimize code and data replication and separate look-and-feel from functionality.

LiveWire minimizes code replication by allowing you to define JavaScript functions. Functions in JavaScript, as in most modern programming languages, can be simple or complex and can be used throughout the system. Sapphire/Web and WebObjects allow C or WebScript functions to be reused from different HTML templates, but some code replication is unavoidable unless the programmer manually edits the system-generated code. Cold Fusion does not contain functions or similar constructs to prevent code replication.

Data replication occurs in web-database systems that copy database data to temporary HTML files. This could become a maintenance nightmare, because data duplication occurs in many places and data could become unsynchronized. LiveWire, Cold Fusion, Sapphire/Web, and WebObjects all prevent data replication by dynamically generating HTML from the database during run time.

When look-and-feel is distinct from functionality, a graphic designer can modify page layout and graphics without having to use a compiler or code in C. This is a powerful characteristic because it allows splitting web-site development work naturally between two very different specialists. LiveWire and Cold Fusion support this capability because a graphic designer can modify and extend HTML components using the visual editing tool of his or her choice without touching the uninterpreted scripting code. In Sapphire/Web and WebObjects, graphic artists cannot effectively modify HTML template files outside the scope of the development environment without having to recompile (hyperlinks in Sapphire/Web and WebObjects refer to executables rather than other HTML files). Consequently, the graphic artist would ultimately need to learn how to use the same tools as the code developer and would not be able to use his or her favorite visual HTML editor.

In Livewire and Cold Fusion, the HTML page itself, rather than another concept, is the basic component. This contributes to a more readable and, therefore, maintainable system by more closely equating the code structure with the structure and control flow seen by the web user.

Portability. The majority of web sites are hosted on UNIX or Windows NT and most web-related software is geared toward these operating systems. Consequently, sophisticated web-database products should include the ability to port between Windows NT and UNIX.

LiveWire and WebObjects are completely portable between NT and UNIX. Sapphire/Web is also portable between NT and UNIX, but relies on a different C compiler for each platform. Cold Fusion operates on Windows NT but not on UNIX.

Sophisticated web-database systems should also support ODBC and should be portable between the major industry-standard database systems. All four of the toolsets discussed here interface with Oracle, Informix, Sybase, and Microsoft SQL Server, either directly or via ODBC.

Extendability. The ability to scale up functionality is just as important as the ability to scale up hardware, operating systems and databases. In other words, an existing site, however simple, should always be extendable to new, sophisticated features.

Web-database software can go a long way toward extendability if it can display database data in any format allowed by HTML. LiveWire and Cold Fusion exhibit this capability, but this really isn't the case for Sapphire/Web and WebObjects. Sapphire/Web and WebObjects HTML templates do not contain conditional logic or looping constructs that would allow flexible data display. Rather, they contain only placeholders that are filled in at run time by a C or WebScript function. If an automatically generated function does not exist to display data in a given manner, then it must be written by hand. This is not simple because it requires understanding how to interface with the existing machine-generated code.

Automatic code generators cannot possibly be designed to predict and handle all types of functionality that might be required. Only human code generators can do that.

Performance. If a web-database system is portable, then it is usually possible to improve performance by "throwing hardware" at the system. Nevertheless, it is cheaper and obviously preferable to improve performance by using more efficient software. Two performance-enhancing tricks used by web-database software are to integrate with the web-server process and to maintain database connections.

Integrating web-database run-time managers with web servers reduces overhead. The LiveWire run-time manager runs as a thread in the Netscape Enterprise Server process. The Cold Fusion management process interfaces with the Netscape Enterprise Server and Microsoft Information Server using NSAPI and ISAPI. So, although there is an additional process, it runs continually and is shared for multiple web accesses. Sapphire/Web and WebObjects, in contrast, involve executables that are repeatedly invoked for each user session (sessions consist of one or more web accesses). This can become expensive when many users access the site at the same time.

Maintaining database connections is more efficient than reestablishing them with each access. LiveWire illustrates this performance difference because it supports both maintained and reestablished connections. In the maintained-connection mode, a definable number of database connections are opened when the web server starts. Data access speed improves substantially because there is no connect and disconnect overhead. The downside to the maintained-connection mode is that the same database account must be shared by all users. Systems that require different database accounts for different users must use the serial approach of reestablishing the connection for each access.

Cold Fusion also supports the ability to maintain database connections across multiple user sessions. Sapphire/Web and WebObjects do not.

Sample Application

To illustrate the differences between Sapphire/Web 3.0, WebObjects 3.0, LiveWire 1.0, and Cold Fusion 2.0, I've implemented the same three-page dynamic web site using each of the tools. The first page of this web site is static and contains a hyperlink. This hyperlink leads to the second page, which is a list of hyperlinked topics generated dynamically from a Microsoft Access database table. Topics are general descriptions such as "Sports," "News," or "Education." Clicking on any of these topics leads to the third page, which is a corresponding list of subtopics from the database (for example, "Football," "Golf," or "Rock Climbing"). Because of space limitations, I've included only the source code for the first two pages.

Listings One through Three implement the project using LiveWire. LiveWire.html is the first page of the Interest application implemented in LiveWire. It is a static HTML page that references the dynamic page, lw_topic.html. Because the LiveWire Manager (which tracks dynamic HTML files) is part of the web server, the server can distinguish static pages from dynamic pages even though both have the same ".html" suffix.

Listing Two is a LiveWire "Initial Page" that initializes the system and establishes database connections when a user first accesses the application. The <SERVER> and </SERVER> tags delimit server-side JavaScript to be stripped out and processed by the LiveWire manager.

At run time, the LiveWire Manager translates the JavaScript SQL statement in lw_topic.html to an HTML list of values from the database. Note that a cursor is used to return the query results row by row. Consequently, the resulting HTML could be formatted so that each row differs depending on the data returned for that row. In the Interest application, however, all rows have the same format.

Listings Four and Five implement the application with Cold Fusion. Cold Fusion's Interest application also starts with a static HTML page. The hyperlink references the Cold Fusion markup file cf_topic.CFM. The web server knows how to apply cf_topic.CFM to the Cold Fusion Application Server because .CFM was set up as an internal web-server MIME type.

cf_topic.CFM is a mixture of HTML and Cold Fusion Markup Language (CFML) tags. The Cold Fusion Application Server processes the CFML tags and generates HTML. The <CFQUERY> tag establishes an ODBC database connection (if necessary) and passes the SQL query to the ODBC data source. A database connection operation is performed only for the first user to access the project after application-server startup. Subsequent users share the same database connection. Database results are returned in result sets rather than by rows in Cold Fusion. The <CFTABLE> tag outputs the result set in an HTML table.

Listings Six through Ten implement the Sapphire/Web web site. Sapphire.html -- the initial page for the Sapphire/Web Interest applications -- is a static HTML file with a hyperlink to the Interest application's CGI, interest.exe. The argument to interest.exe denotes the Activator function for this hyperlink. This activator function is defined in the automatically generated C file Sapphire.c, which is linked with interest.c at project build time.

sw_topic.html is a template file that is processed by the Sapphire_html activator in the interest.exe CGI. It contains a hyperlink that references another activator function. Additionally, it contains a placeholder, ##Sa_MasDetHotListSite##, for the data that is dynamically generated by the R_Sapphire_html activator function.

Listing Eight contains initialization and project definition code. A Sapphire/Web developer would normally not need to understand or modify this code, or any of the automatically generated code in this project.

Listing Nine shows the automatically generated code for the Sapphire_html activator. It shows data from the TOPIC table bound to the #MasDetHotListSite# placeholder, and formatted as a list of hyperlinks.

Listing Ten contains the automatically generated code for the sw_topic_html activator. It defines how data from the SUBTOPIC table is displayed as a simple list of items.

Finally, Listings Eleven through Seventeen implement the application using WebObjects. Main.html is a template used by the WebObjects application executable to dynamically generate HTML. The object, NEXTPAGE, is defined in the declarations file, Main.wod. Main.wod (Listing Twelve) is a declarations file that maps the object NEXTPAGE to the action WOHyperlink, which references the next dynamic page, wo_topic. The template file, wo_topic.html, references two objects and two variables used by those objects. The objects are defined in wo_topic.wod and the variables are defined in wo_topic.wos.

In the declarations file (Listing Fourteen, wo_topic.wod), TopicList is defined as the list "topics" containing the elements "topic." The SubtopicPage object defines an attribute of topic to be a hyperlink that references the next page, wo_subtopic.

.wos files (see Listing Fifteen) usually contain WebScript functions that interact with objects defined in the .wod file. For this application, though, the .wos file simply contains variable declarations. In Listing Sixteen, wo_topic.woo defines the relationship between the topic and topics objects and the corresponding database table. Lastly, Listing Seventeen defines the code interface to the INTEREST database's TOPIC table. WebObjects automatically generates a separate .plist file for each table in each referenced database.

Conclusion

Although often overlooked, the short-term cost effectiveness of web-site implementation and long-term cost effectiveness of web-site maintenance are key to the successful application of web-based technology. In my experience, the programmatic approach to web development has generally enabled me to build sites that are more simple, modular, portable, extendable, and of higher performance than the code-generation approach. This isn't to say that toolsets such as LiveWire and Cold Fusion are in every instance superior to Sapphire/Web or WebObjects. Still, from the perspective of cost effectiveness, programmatic tools have been more successful for me.

DDJ

Listing One

<HTML><HEAD><!--
///////////////////////////////////////////////////////////////////////////
// LiveWire.html
//
// Initial page for Interest application.
///////////////////////////////////////////////////////////////////////////
-->
<TITLE> Initial LiveWire Page </TITLE></HEAD>
<BODY>
<H2>List Generated Dynamically from Database Using LiveWire</H2>
<P>Click <A HREF=lw_topic.html>here</A> to generate a list of 
hyperlinks from the TOPIC table.
</BODY>
</HTML>

Back to Article

Listing Two

<HTML><HEAD><SERVER>
///////////////////////////////////////////////////////////////////////////
//  lw_topic.htm - LiveWire Implementation
//
//  This page displays displays a list of interests from the TOPIC table.
//  The descriptions are hyperlinked. If you click on an interest, the
//  following page will display an associated list of sub-interests taken
//  from the SUBTOPIC table.
///////////////////////////////////////////////////////////////////////////
</SERVER>
<TITLE>Your Interests</TITLE>
</HEAD><BODY>
<P> <B>Select a Topic Which Interests You:</B>
<CENTER><UL>
<SERVER>
if ( database.connected() ) {
   database.beginTransaction();
   qs = "SELECT topic_id, topic_desc from TOPIC";
   results = database.cursor( qs );
   while(results.next())
   {
write( "<LI><A HREF=\"lw_subtopic.html?topic_id="+results.topic_id+
    "\">"+results.topic_desc+"</A>\n");
   }
   results.close();
   database.commitTransaction();
}
else {
   write("<p>Database connection failed");
}
</SERVER></UL>
</CENTER>
</BODY></HTML>

Back to Article

Listing Three

<SERVER>///////////////////////////////////////////////////////////////////////////
// project.html - LiveWire Implementation
//
//    Project file for INTEREST application
///////////////////////////////////////////////////////////////////////////

// Establish a Connection to the INTEREST Database for ALL Clients if ( !database.connected() ) { project.lock(); database.connect("ODBC","INTEREST","admin","",""); if(!database.connected()) { write("<p>Database connection failed"); } project.unlock(); } </SERVER>

Back to Article

Listing Four

<HTML><HEAD><!--
///////////////////////////////////////////////////////////////////////////
// ColdFusion.html
//
// Initial page for Interest application.
///////////////////////////////////////////////////////////////////////////
-->
<TITLE> Initial Cold Fusion Page </TITLE></HEAD>
<BODY>
<H2>List Generated Dynamically from Database Using Cold Fusion</H2>
<P>Click <A HREF=cf_topic.CFM>here</A> to generate a list of hyperlinks 
from the TOPIC table.
</BODY>
</HTML>

Back to Article

Listing Five

<HTML><HEAD><!--
///////////////////////////////////////////////////////////////////////////
//  cf_topic.htm - Cold Fusion Implementation
//
//  This page displays displays a list of interests from the TOPIC table.
//  The descriptions are hyperlinked. If you click on an interest, the
//  following page will display an associated list of sub-interests taken
//  from the SUBTOPIC table.
///////////////////////////////////////////////////////////////////////////
-->
<TITLE>Your Interests</TITLE>
</HEAD><BODY>
<P> <B>Select a Topic Which Interests You:</B>
<CENTER>
<UL>
<CFQUERY NAME="GetResults" DATASOURCE="INTEREST">
    SELECT TOPIC.topic_id,TOPIC.topic_desc,TOPIC.topic_id
 FROM TOPIC
</CFQUERY>
<CFTABLE QUERY="GetResults">
<CFCOL WIDTH="30" TEXT="<A HREF=""cf_subtopic.CFM?TOPIC__topic_id=
 #topic_id#"">#topic_desc#</A>">
</CFTABLE>
</UL>
</CENTER>
</BODY>
</HTML>

Back to Article

Listing Six

<!--///////////////////////////////////////////////////////////////////////////
// Sapphire.html - Sapphire/Web Implementation
//
// Initial page for Interest application.
///////////////////////////////////////////////////////////////////////////
--><HTML>
<HEAD><TITLE> Initial Sapphire Page </TITLE></HEAD>
<BODY>
<H2>List Generated Dynamically from Database Using Sapphire/Web</H2>
<P>Click <A HREF="http://ORDSYS2:80/cgi-bin/interest/interest.exe?FNC=
GO__ASapphire_html" >here</A> to generate a list of hyperlinks from 
the TOPIC table.
</BODY>
</HTML>

Back to Article

Listing Seven

<HTML><HEAD><!--
///////////////////////////////////////////////////////////////////////////
//  sw_topic.htm - Sapphire/Web Implementation
//
//  This page displays displays a list of interests from the TOPIC table.
//  The descriptions are hyperlinked. If you click on an interest, the
//  following page will display an associated list of sub-interests taken
//  from the SUBTOPIC table.
///////////////////////////////////////////////////////////////////////////
-->
<TITLE>Your Interests</TITLE>
</HEAD><BODY>
<P> <B>Select a Topic Which Interests You:</B>
<CENTER>
<UL>
##Sa_MasDetHotListSite##<A HREF="http://ORDSYS2:80/cgi-bin/interest/
  interest.exe?FNC=GO__Asw_topic_html">TEMP</A>
</UL>
</CENTER>
</BODY>
</HTML>

Back to Article

Listing Eight

#include "SaRnHtml.h"#include
/* Sapphire Start Of User Includes */
/* Sapphire End Of User Includes */
/* Sapphire Start Of Callback Declarations */
extern void R_Sapphire_html ();
extern void R_sw_topic_html ();
/* Sapphire End Of Callback Declarations */
#ifdef _NO_PROTO
int SaCartInit(argc, argv)
int argc;
char *argv[];
#else
int  SaCartInit(int argc, char *argv[])
#endif
{
   /* Sapphire Start Of Initial Code */
   /* Sapphire End Of Initial Code */
   SaInitClient(&argc, argv);
   /* Sapphire Start Of Callback Registration */
   R_Sapphire_html ();
   R_sw_topic_html ();
   SaInitHeaders();
   SaRelocatableProject(0);
   SaSetDefaults("","");
   SaSetStartupHtml("home.html");
   SaSetHtmlURL("http://ORDSYS2:80/interest");
   SaSetApiMaxNum(250);
   SaAllowShutDown(1);
   SaSetCacheNumConn(2);
   SaSetSessTimeOut(1200);
   SaSetGifURL("");
   SaSetDefaultVendor(SGENODEFAULT);
   SaSetOrbHost("");
   SaSetKOption(223046);
   SaSetDefaultUser("admin");
   SaSetDefaultPass("");
   SaAllowSQL(0);
   SaAllowConfigFile(0);
   SaSetConfigFile("/Sapphire/config/interest.cfg");
   SaSetHtmlDir("C:\\www\\data\\interest");
   SaSetPrintArgs(0);
   SaSetPrintEnv(0);
   SaProcessLogin(1);
   SaSetDoCommonLog(0);
   SaSetUseAppLogin(0);
   SaSetStateHost("");
   /* Sapphire End Of Callback Registration */
   /* Sapphire Start Of Final Code */
   /* Sapphire End Of Final Code */
   SaProcessInputStream();
   return 0;
}
#ifdef _NO_PROTO
int SAMAIN_DECL main(argc, argv)
int argc;
char *argv[];
#else
int SAMAIN_DECL main(int argc, char *argv[])
#endif
{
    SaRegisterReq(SaCartInit);
   SaParseCommmandLine(argc, argv);
    return 0;
}
#ifdef WRB
#ifdef _WINDOWS
#undef u_long
#include
#ifdef __cplusplus
extern "C" {
#endif
BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, 
   LPVOID lpReserved)
{
    return 1;
   UNREFERENCED_PARAMETER(hInst);
   UNREFERENCED_PARAMETER(ul_reason_being_called);
   UNREFERENCED_PARAMETER(lpReserved);
}
extern int SaInitCartNT(void* WRBCalls);
_declspec (dllexport)
int SaInitCart (void* WRBCalls)
{
   return SaInitCartNT(WRBCalls);
}
#ifdef __cplusplus
}
#endif
#endif
#endif

Back to Article

Listing Nine

/* Sapphire Module File V1.0B *//* Sapphire Start Of Tool Includes */
#include "SaRnHtml.h"
/* Sapphire Start Of user Includes */
static char _Sa_HTML_Filename[] = "Sapphire.html";
/*Sapphire Start of Function */
static void AGO ()
/* start of initial code */
{
/* start of func Code */
{
   /**  sapphire versn 1.00  **/
   XtPointer SaClient=NULL;
{
   pSgeWorkCallData pSgeWCD;
   extern void SaPopulateHotList SUTPROTO((Widget,
XtPointer, SaCallbackStruct*));
   /********************************************************/
   /********* users initial code is inserted here **********/
   /********************************************************/
/***** Begin Site Registry Code  *****/
/***** End of Site Registry Code *****/
   pSgeWCD = getSgeWorkCallData(UxWidget,
 -1,
 "INTEREST",
 "INTEREST",
 "TOPIC", NO_WP);
   SgeSetCallDetail(pSgeWCD, NO_CACHE,
 (XtPointer)0x0,
 ROW_INDEX, (XtPointer)0x0,
 (XtPointer)0x0,
 NO_QUEUE_FILE);
   SgeSetColBind(pSgeWCD, "sw_topic.html|MasDetHotListSite", 1, 0, 4,
 (XtPointer)SaPopulateHotList,
 (void *) 0);
   SgeSetColDetail(pSgeWCD, 4, 0, 0, 0,
 "LONG",
 "topic_id");
   SgeSetColBind(pSgeWCD, "sw_topic.html|MasDetHotListSite", 1, 0, 220,
 (XtPointer)0x0,
 (XtPointer)0x0);
   SgeSetColDetail(pSgeWCD, 12, 0, 0, 0,
 "TEXT",
 "topic_desc");
   SgeVaSetFrags(pSgeWCD,
 "SELECT  \
   [TOPIC].[topic_id], \
   [TOPIC].[topic_desc] \
FROM  \
   [TOPIC]",
 VULL);
   /* db-UIM/X Bind Name : sw_topicdhtml1 */
   DoWorkProc(pSgeWCD, PROCESSBRSREQUEST_XSQL);
   /*******************************************************/
   /********** users final code is inserted here **********/
   /*******************************************************/
   /*************** END OF SAPPHIRE FINAL CODE  **********************/
}
}/* end of func code */
/* start of final code */
}/* end of final code */
void R_Sapphire_html()
{
   SaRegisterHtmlActivator("GO__ASapphire_html",AGO);
}/* Sapphire End of Module */

Back to Article

Listing Ten

/* Sapphire Module File V1.0B *//* Sapphire Start Of Tool Includes */
#include "SaRnHtml.h"
/* Sapphire Start Of user Includes */
static char _Sa_HTML_Filename[] = "sw_topic.html";
/*Sapphire Start of Function */
static void AGO ()
/* start of initial code */
{
/* start of func Code */
{
   /**  sapphire versn 1.00  **/
   XtPointer SaClient=NULL;
{
   pSgeWorkCallData pSgeWCD;
   extern void SaPopulateListItem SUTPROTO((Widget,
XtPointer, SaCallbackStruct*));
   /********************************************************/
   /********* users initial code is inserted here **********/
   /********************************************************/
/***** Begin Site Registry Code  *****/
/***** End of Site Registry Code *****/
   pSgeWCD = getSgeWorkCallData(UxWidget,
 -1,
 "INTEREST",
 "INTEREST",
 "SUBTOPIC", NO_WP);
   SgeSetCallDetail(pSgeWCD, NO_CACHE,
 (XtPointer)0x0,
 ROW_INDEX, (XtPointer)0x0,
 (XtPointer)0x0,
 NO_QUEUE_FILE);
   SgeSetColBind(pSgeWCD, (XtPointer)0x0, 1, 0, 220,
 (XtPointer)SaPopulateListItem,
 (void *) 0);
   SgeSetColDetail(pSgeWCD, 12, 0, 0, 0,
 "TEXT",
 "subtopic_desc");
   SgeVaSetFrags(pSgeWCD,
 "SELECT  \
   [SUBTOPIC].[subtopic_desc] \
FROM  \
   [SUBTOPIC]",
 VULL);
   /* db-UIM/X Bind Name : sw_subtopicdhtml1 */

DoWorkProc(pSgeWCD, PROCESSBRSREQUEST_XSQL);

/*******************************************************/ /********** users final code is inserted here **********/ /*******************************************************/ /*************** END OF SAPPHIRE FINAL CODE ************************/ } }/* end of func code */ /* start of final code */ }/* end of final code */ void R_sw_topic_html() { SaRegisterHtmlActivator("GO__Asw_topic_html",AGO); }/* Sapphire End of Module */

Back to Article

Listing Eleven

<HTML><HEAD>
<TITLE>Initial WebObjects Page </TITLE>
</HEAD>
<BODY>
   <H2>List Generated Dynamically from Database Using WebObjects</H2>
   <P>Click <WEBOBJECT NAME=NEXTPAGE></WEBOBJECT> to generate a 
 list of hyperlinks from the TOPIC table.</P>
</BODY>
</HTML>

Back to Article

Listing Twelve

NEXTPAGE: WOHyperlink {string = "here"; pageName = "wo_topic"; };

Back to Article

Listing Thirteen

<HTML><HEAD><!--
///////////////////////////////////////////////////////////////////////////
//  wo_topic.html - WebObjects Implementation
//
//  This page displays displays a list of interests from the TOPIC table.
//  The descriptions are hyperlinked. If you click on an interest, the
//  following page will display an associated list of sub-interests taken
//  from the SUBTOPIC table.
///////////////////////////////////////////////////////////////////////////
-->
<TITLE>Your Interests</TITLE>
</HEAD><BODY>
<P> <B>Select a Topic Which Interests You:</B>
<CENTER>
<UL>
   <WEBOBJECT item=topic list=topics.allObjects name=TopicList 
   wbwebobjectclass=WORepetition>
   <WEBOBJECT name=SubTopicPage value=topic.topicId 
 wbwebobjectclass=WOString></WEBOBJECT><BR>
   </WEBOBJECT>
</UL>
</CENTER>
</BODY>
</HTML>

Back to Article

Listing Fourteen

TopicList: WORepetition {item = topic; list = topics.allObjects; };SubTopicPage: WOHyperlink {string = topic.topicDesc; 
    pageName = "wo_subtopic"; };

Back to Article

Listing Fifteen

id topic;id topics;

Back to Article

Listing Sixteen

{    NameCounts = {WORepetition = 2; WOString = 4; };
    Types = {};
    encoding = NSWindowsCP1252StringEncoding;
    variables = {
   topic = {TypeName = Topic; };
   topics = {
  AutoInitialized = 1;
  TypeName = Topics;
  initialValue = {
 class = WODisplayGroup;
 dataSource = {
class = EODatabaseDataSource;
editingContext = session.defaultEditingContext;
fetchSpecification = {class = EOFetchSpecification; 
   entityName = Topic; isDeep = YES; };
 };
 fetchesOnLoad = YES;
 formatForLikeQualifier = "%@*";
 numberOfObjectsPerBatch = 0;
 selectsFirstObjectAfterFetch = YES;
  };
   };
    };
}

Back to Article

Listing Seventeen

{    attributes = (
   {
  allowsNull = Y;
  columnName = topic_desc;
  externalType = TEXT;
  name = topicDesc;
  valueClassName = NSString;
  width = 220;
   },
   {
  allowsNull = Y;
  columnName = topic_id;
  externalType = COUNTER;
  name = topicId;
  precision = 10;
  valueClassName = NSNumber;
  valueType = i;
    );
    attributesUsedForLocking=topicDesc;
    classProperties = (topicId, topicDesc,);
    externalName = TOPIC;
    name = Topic;
    primaryKeyAttributes = (topicId);
}

Back to Article