Chapter 17

JavaScript on the Server


CONTENTS

So far the focus in this book has been on using JavaScript on the client side. This chapter looks at how JavaScript is becoming an integral part of the other side-the server.

JavaScript extends the capabilities of the server. By providing a scripting language the software can do more without calling an external program. This makes it easier for Webmasters to add features to their sites that browsers can take advantage of. And it can reduce the load on the server by keeping the processing within the server software.

This chapter looks at the LiveWire environment from Netscape. LiveWire itself is not a server, but works with Netscape servers to create applications that make pages come alive.

An Overview of LiveWire

LiveWire from Netscape is an integrated, visual environment for building client-server applications for Internet or enterprise networks. These applications can be for the public to access or limited to a specific audience such as an intranet.

LiveWire is actually a suite. It consists of the Netscape Navigator Gold, LiveWire Site Manager, LiveWire Server Extensions, LiveWire Server Front Panel, and JavaScript. LiveWire runs on the UNIX and Windows NT operating systems.

This chapter details the use of JavaScript in LiveWire. Given the nature of JavaScript, it is believed that other servers that incorporate JavaScript will work similarly.

NOTE
JavaScript on the server is nearly identical to JavaScript on the browser including the syntax and the statements. There are also several shared objects: string, math, and date objects. However, most objects are specific to the application. This chapter covers the objects unique to LiveWire.

Live Objects and Properties

LiveWire has four built-in objects: request, client, project, and server. Obviously these are related. Each server can run multiple projects. Any project can have multiple simultaneous clients using it. And most clients request more than one page. A graphical depiction of this relationship is shown in figure 17.1.

Figure 17.1 : While there can be only one server object, there can be several project objects, more client objects, and many more request objects. However, this picture is not static since request and client objects only exist for short periods of time.

Request Object

Each time a browser wants more information it sends a request to a server. Data about these requests are available on the server from the request object in JavaScript. If you have written CGI scripts, you should be familiar with the properties that are built into the request object.

The Built-In Properties  The request object is initialized with four properties: agent, ip, method, and protocol. A JavaScript function on the server can use this information to determine its response to the request. The following list provides details about each property:

Other Request Properties  A request may include additional information. Your browser's request may have resulted from submitting information from an HTML form. This information becomes a request property. It does not matter if the data is submitted with either the post or get method. The name of each element of the form becomes a property name.

For example, take a form with a text element with the name of answer. Complete the text box with a value of "59" and submit it to the server. The server receives the information in the form of "answer=59." The request object now has a property called request.answer which reflects the value of "59."

You can also store information in request properties. One property you might want to consider adding is a time and date stamp. Remember that the life of a request object is rather short, so values stored in these properties are not stored for long.

CAUTION
The request property in LiveWire reserves the names of ip, agent, method, and protocol for its own use. Therefore, if you plan to submit information from a form, make sure that your form does not use these names for its elements.

Application of Request Properties  These built-in properties allow the server to vary the response to the request. The agent property is commonly used to supply different HTML documents to different types of browsers. For example, an HTML page with tables might only be served to a limited number of browsers while others receive a different version of the page.

Servers may limit access to certain information to specific IP addresses. Using the ip property, the server might only provide sensitive company information to clients requesting it from known company computers. Usually you want additional security, just screening IP addresses is not enough.

If a property is added for the date and time, it might be used to serve different responses. Perhaps certain parts of your site are only accessible after hours; or perhaps the graphics change depending on the time of day; or you could send a holiday greeting.

Obviously, form data sent to a server should generate a response. Perhaps a simple "thank you" and this form information is stored on the server's hard drive. Many sites have guestbooks that work this way. Other applications process the information and return a calculated response. For example, the information can be a message to append to an existing Web page. The response might acknowledge the submission but also display the updated page.

Client Object

Each time a new client accesses the server application, a new client object is created. However, there are no built-in properties for the client object. If you need information retained about the client, then you create a property.

Client Properties  A very common use for a client property is a client identification number. When the client first accesses the server, he can receive a form. The server can then process this information and assign the client an identification number. This might be randomly generated or looked up from a list of previous customers.

As an example, a client can send a request. Part of the request can include a user supplied customer number in the text box named idnumber. You can then create a client property called custNo with the following line:

client.custNo = request.idnumber

Client Objects Expire  The server overflows with client objects unless they are properly deleted. Since client objects are automatically created for each and every client to access, then there must be a mechanism for deleting them.

LiveWire automatically deletes any client object with no property values. So if you don't use a client object, you don't have to worry about deleting it. In other words, you only have to clean up after yourself.

The default expiration is ten minutes of inactivity. If the client does not send another request to the server within ten minutes of the previous request, the object expires.

Obviously, in many cases ten minutes is insufficient time and, therefore, you might want to manually control this. You can simply use the expiration statement

client.expiration(seconds)

where seconds is the number of seconds before the client expires.

Another manual control is destroy. If you no longer need the client object, simply use the statement

client.destroy()

LiveWire looks at the client that sent the request and destroys its client object. This eliminates all of the client object's properties as well.

Cookies Store Information Between Sessions  Another technique for retaining client information is cookies. The browser must support the Netscape cookie protocol. If supported, the server sends the information to the client as name/value pairs. Obviously, this increases network traffic, but can offer substantial advantages to large access servers.

NOTE
For more information about cookies see http://home.netscape.com/newsref/std/cookie_spec.html.

There are several other techniques for maintaining this information. However, they all have limited application. Client URL encoding causes a large increase in network traffic. Using IP addresses on the server only works for clients using fixed IP addresses. This might work for some intranet applications; for general use, it is worthless. For more details, refer to the LiveWire documentation.

Project Objects

Each application, when started, creates project objects. This is global data for the entire application. Every client that accesses the application shares these objects.

Properties for Project Objects  The project object has no pre-defined properties. If you need to hold information for your project, you create the objects you need in the application.

Many projects need to store values. For example, in billing a customer you might need the next invoice number. This number is incremented when another invoice is generated.

Lock Project Objects  In any multi-user environment you must deal with cases of simultaneous access. On file servers, you lock a file while you are using it. You unlock it when you are finished with the operation. The other user must wait for you to finish.

If you do not lock files, the data can be corrupted. A simple example is with two people editing a document. If both are editing at the same time, then one saves his changes before the other. The problem is that the first set of changes are over written by the second.

Project objects should also be locked when in use. In your task, simply start with the line

project.lock();

Next, include your statements. For example,

invoiceno = invoiceno + 1

Then unlock the object with

project.unlock();

Server Objects

Global data for the entire server are in the server objects. These objects can be shared between applications. There are also a few objects that tell you about the server. Any request, client, or project can access these objects.

The Built-In Server Properties  The server object is initialized with the following two properties:

Adding Server Properties  As with most JavaScript objects, you can add properties to server objects. For example, you might want to add the time the server was last accessed. This might be read by a monitoring routine, as follows:

     today = new Date()
     server.accesstime = today.getTime()

As with project objects, server objects should be locked. Since you can have more than one process accessing the object at one time, you should use the same locking procedure as discussed for project objects.

External Process Communications: JavaScript and CGIs

Undoubtedly with your active interest in building Web sites, you have dealt with Common Gateway Interface (CGI) scripts. Prior to JavaScript, this was the primary means of creating interactive applications. Libraries of CGI scripts include counters, e-mailers, message boards, and many other functions.

LiveWire can replace CGI programming. Instead of calling external programs, the server software runs applications that are closely integrated to it. JavaScript is the language of these applications.

Applications are developed with three tools. You build these applications using LiveWire's Site Manager. The source files for the applications are developed using the same HTML editors used to build browser JavaScript pages. The applications run in response to requests from Netscape Navigator.

Steps to Building a LiveWire Application

Building a LiveWire application is not unlike other development procedures. The process can be done using either a command line compiler or a graphic interface.

The following are the steps to create a LiveWire application:

  1. Create the source files (see the section "LiveWire Source Files" later in this chapter).
  2. Using the graphic interface of Site Manager (see fig. 17.2), you must bring the files under site management. You do this by selecting the application directory on the screen and choosing Site, Manage. (For the command-line compiler, you can skip this step.)
    Figure 17.2 : LiveWire's Site Manager displays your site directory in a tree structure where you specify which part of the site you want to manage. The directories managed by LiveWire are highlighted with a red marker.
  3. Build the application by creating a compiled *.web file.
  4. Install the application using the LiveWire Application Manager (see fig. 17.3).
    Figure 17.3 : LiveWire's Application Manager installs the applications you develop. The Application Manager itself is a LiveWire application and is listed on the first line.
  5. If you rebuild the application, restart it using the Application Manager.
  6. Run the application by loading any of the pages with your browser.

Browsing into a LiveWire Application

Like any other Web site, a browser requests a Web page to access a LiveWire application. The browser can request any of the pages within an application. The server sees the request like any other request, though it is handled differently. In turn, the browser is not concerned if the HTML is a static page or from a dynamic LiveWire application. The form of the URL is as follows:

http://server.domain/application/page.html

In this case the domain is the Internet domain and the server is the name of the HTTP server. The next element, application, is the name you define when the application is installed with the Application Manager. The final part, page.html, is simply the name of any page in the application. Each application has a default page. So if page.html is the default for this application, the page.html at the end of the URL would be optional.

In the following example,

http://home.myserver.com/callhome/phonebook.html

the page phonebook.html is severed from an application named callhome. This application resides on the server called home at the domain of myserver.com.

LiveWire Source Files

To build a LiveWire application, you construct one or more source files. The following are three types of files you can build:

Using the SERVER Tag  The SERVER tag contains JavaScript that either executes a statement or produces HTML with the write function. A JavaScript statement can be a rather simple routine or a more complex set of functions.

Using the SERVER tag with JavaScript to produce HTML is very common. As a very simple example, you might create a document that returns the IP address to the browser, as follows:

<P>Your request came from ip address: 
<SERVER> write(request.ip) </SERVER>

When using the SERVER tag, the result is sent in response to the request. The source code that contains the SERVER tag and your logic stays secure on the server. This is different than HTML pages that contain JavaScript where the browser gets all of the source code.

Using the Backquote  A shorthand method of putting JavaScript into the HTML document is to use the backquote ('). Using the backquote, the HTML is automatically generated without having to use the write statement.

Our preceding example would simply become

<P>Your request came from ip address:  'request.ip' </P>

The backquote is especially useful when the JavaScript produces an attribute value. Generally, HTML tags have the form of

<TAG ATTRIB="value" [...ATTRIB=VAL]>

where ATTRIB is the attribute and value is its value. Backquoted JavaScript can substitute for any attribute or value in this syntax.

CAUTION
Mixing backquotes with double quotes can confuse the compiler and you. The simple rule is that the backquote comes first. Everything inside the backquotes is treated as a JavaScript expression. If you place the backquote inside the double quote, then it is interpreted literally.

The JavaScript Balancing Act

When interactivity was only done with a CGI, all of the processing was done on the server. Because JavaScript can run on both the server and the browser, writing a successful application requires you to properly allocate the processing between the two.

Let the Browser Handle Much of the Work  Previous chapters detailed how JavaScript on the browser side can handle a great deal of processing. In general, let the browser code "polish" the information before sending it to the server. The following are several different tasks best handled by the browser:

More Work for the Server  Though the browser code can take much of the burden off the server, the server still must perform. Given the increasingly interactive nature of Web applications, the demands on the server are increasing. The following types of tasks should be performed by the server:

Obviously, the server is a busy fellow. And this load is expected to grow dramatically as servers provide multimedia material and more interactivity.

Exchanging Information between the Browser and Server

In making your application come alive, the server and browser must exchange information. The client, or browser, typically sends user responses. These can first be "polished" by JavaScript routines on the browser side. The server in turn sends data back to the browser as HTML pages.

From Client to Server  User responses are submitted just as you currently handle forms. The user completes the form and clicks the submit button. The data from the radio buttons, checkboxes, textboxes, and textarea are sent to the server. The server then places this data into the request object. Each element of the form has a corresponding property.

NOTE
Consider adding hidden files to your forms. Then, prior to submitting the data, have a JavaScript routine that processes the user input and puts the result into the hidden field's value property. Practical uses are totals, averages, word counts, and other mathematical results. This takes some of the load off the server.

From Server to Client  Usually a server only returns a static page in response to a browser request. In LiveWire, the response is still a page, but the contents of the page vary. User input can result in changes to default form values, new values of hidden form elements, or direct substitutions.

A server-side JavaScript can dynamically build in the HTML code for a form element that is part of the page. As an example, you can have the following statement in a source document:

<INPUT TYPE="text"  NAME="example" VALUE='request.agent'>

In this case the default value of the text is the browser agent information.

You use an identical procedure for hidden form elements. The only difference is that the type is hidden instead of text. Your client-side JavaScript can then use this value as part of any function.

Another means of making your pages come alive is to change part of the JavaScript code. When you send a page to the browser it can contain JavaScript code for the browser to execute as part of the page. There is no reason that this code has to be static.

The server can modify the page being sent to change the JavaScript code embedded in the page. The page on the server side might include

<SERVER>
write ("<SCRIPT>var luck = " + client.winnings + "</SCRIPT>")
</SERVER>

Assuming the value of client.winnings is 1000, the browser sees a line of

<SCRIPT>var luck =  1000 </SCRIPT>

External Files and Databases with JavaScript

In building a non-trivial application, you need to be able to read and write data from a file. It can be customer information, data about merchandise, or student grades. This is a basic procedure in almost every application.

LiveWire provides a file object. This allows your application to write to the server's file system. As a security measure, JavaScript on the browser does not permit saving data to the file system.

NOTE
LiveWire Pro adds support for Structured Query Language (SQL) databases. It supports Informix, Oracle, Sybase, and Microsoft databases.

CAUTION
Don't give your users an open invitation to fill up your hard drive or otherwise abuse your system. Check the volume. Consider disallowing repetitive entries from the same IP address.

Create the New File Object

Like other JavaScript operations, file handling is also done using objects. LiveWire provides a file object and you create new objects for each file you want to use. If you need to use file files, then create a new file object for each one.

Use the standard syntax in creating file objects:

fileObjectName = new File("path")

In this case "path" is the file path relative to the application directory. This is not a URL, but uses the server's file system format-for example, /mydirectory/sample.txt.

Each file object has numerous methods that you can use. However, you must first open the file.

Open the File

After you create the file object, you then need to open the file before you can do anything else with it. To open the file, you use the open method, as follows:

result=fileObjectName.open ("mode")

The result is true if the file was opened successfully; otherwise it is false.

CAUTION
If the file is already opened, this operation fails. The original file object remains unchanged and open. You should test for this possibility since simultaneous requests can be sent to any LiveWire application.

A file is opened for reading and/or writing. You might want to create a new file, replace an old file, or just append to the end of an existing file. Using one of the following modes, you can open a file in any of these modes:

You may have noticed that each of these methods deals only with text files. LiveWire provides you with an option to save the file in a binary format. Just append a b to any of these modes.

NOTE
Forgot the filename? Simply use the write method to display it. For example, the following results in the display of the filename:
write ( fileObjectName )

File Positioning

When dealing with data stored in a file, you must consider where in the file the desired data is stored or where you intend to store it. You may not want to read the first three items, but you do want to read the next two items. The file object allows you to read the current position, change the position, or check if you are at the end of the file.

When you open a file, the current position depends on the mode you use to open it. Generally it starts at the beginning of a file, except for modes a+ and a where data is appended at the end of an existing file. For empty or new files, the end of the file and the beginning of the file are the same.

The current position in the file is available using the getPosition method. The first byte in a file is 0 and any other position is a positive number. An error is indicated by returning a -1. The syntax is

x =  fileObj.getPosition()

The setPosition method changes or sets the current position. You can change the position relative to the beginning of the file, relative to the end of the file, or relative to the current position. This is called the reference, and is an optional parameter. The default reference is the beginning of the file.

The syntax for the setPosition method is

fileObj.setPosition(position [,reference])

where reference is a numeric value-0 relative to the beginning of the file, 1 relative to the current position, and 2 relative to the end of the file. This method returns true if successful; otherwise, false.

For example, if the current position was 10 with the end file at 20, the following would be the results:

fileObject.setPosition ( 3)     /...new position is 3
fileObject.setPosition(2,0)     /...new position is 2
fileObject.setPosition(-2,1)     /...new position is 8
fileObject.setPosition(-2,2)     /...new position is 18

In reading a file you often want to read through the entire thing, but to do so you need to know when you have reached the end. So you test for the end of the file (eof). The file object has the eof method that returns a true after the first read operation that attempts to read past the end of the file.

fileObj.eof()

Writing with the File Object

The file object provides three methods of writing data to a file. These methods allow you to write a string, write a string followed by a \n (see the following note), or write a single byte to a file. Each method returns true if successful; otherwise it returns false.

The syntax is

fileObj.write(string)
fileObj.writeln(string)
fileObj.writeByte(number)

Like most languages, when data is sent to a file it is stored in a buffer to increase efficiencies. This internal buffer stores the data until the buffer is full, until the file is closed, or when flushed. (Flushed means the code forces the data in the buffer to write to the file.) Then it physically writes the data into the file.

To ensure that your data is properly saved, you can force a flush with the flush method. The syntax is

fileObj.flush()

Reading with the File Object

Just as there are three methods of writing to a file, so there are three methods to reading a file. You can read a specific number of bytes, read in the entire next line, or read in a single byte. Each method returns true if successful, otherwise it returns false. The syntax is

fileObj.read(count)
fileObj.readln()
fileObj.readByte()

NOTE
On Windows systems, text files typically end a line of text with a carriage return and an end of line character (\r\n). On UNIX systems, the line ends with a single end of line character. To accommodate both formats, when using the writeln method, JavaScript adds one or both characters at the end of the line depending on the server platform. When reading a file, the line separator characters are not included when using the readln method.

Converting Data

The data in any of your files is stored in either ASCII text format or binary. The file object has two methods for converting from one format to the other. Both methods, which follow, are static, so no object is required.

The syntax of these methods is

File.byteToString(number)
File.StringToByte(string)

Error Checking and Getting Information

Often you just want basic information about a file. Does the file exist? How long is the file?

The exists method returns a simple true if the file exists, otherwise it returns false. For example,

fileObj.exists()

The getLength method returns the number of bytes in a file. For a text file, it returns the number of characters. In case of an error, it returns a -1. For example,

fileObj.getLength()

LiveWire also allows you to check on the error status of a file or clear the error status. Since error status codes are platform dependent, you must check your operating system documentation for the actual codes. The syntax for these methods is

fileObj.error()
fileObj.clearError()

Example Using the File Object

Let's do a very simple example. Let's take an existing text file and copy it into a new file. The example in listing 17.1 does not include error checking. In a real application you would want to add this to the code. For example, you might want to check if anyone else is already using the file.


Listing 17.1  Copying a File on LiveWire

oldFile = new File ("oldtext.txt")     /...create object for existing text file
newFile = new File ("newtext.txt")     /...create object for new file

result1 = oldFile.open("r")          /...open file for reading only
result2 = newFile.open("w")
     /...open file, initially empty, for writing

until oldFile.eof() {          
     /...until the end of the file is reached
     result3 = newFile.writeln(oldFile.readln())
     /...read a line from the old file and write it to the new file
}
result4 = oldFile.close()     /...close each file
result5 = newFile.close()