JavaEnterprise JavaMoving data to the browser using Java and JavaScript Part 1: From...

Moving data to the browser using Java and JavaScript Part 1: From database to Web server

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.



Sometimes it seems that the components of a four-tier architecture perceive data the way the four blind men perceived the elephant: each in a different way. The database returns a result set, traversed by a cursor. The middle tier thinks in terms of data structures, like vectors. The Web server wants a fixed array of values. And all the browser wants is an HTML table!

Fortunately, the principles of object-oriented programming can solve this problem, because each object tells each of the other objects it communicates with how it wants to deal with data. In this article, I will outline a simple way to transfer data from Result Sets to Vectors, so that the Vectors can be distributed to a Web server. The Web server, in turn, can access the values in the Vector using Server-Side JavaScript.

From ResultSet to a Vector of Vectors

The secret of my solution is to extend the Java Vector object, so that it can contain all the values in the ResultSet returned from a database. This new object I call a Table, and you can think of it as a Vector of Vectors. A Vector is a Java data structure that works much like a one-dimensional Array, except that it has a variable size. A Vector of Vectors, therefore, works like a two-dimensional Array, especially if all the component Vectors are the same length. Because the component Vectors in the Table correspond to rows returned from the database, I will refer to them as “rows.”

My Table object also has a special Vector, which I will place in the first row in the Table. This special Vector will contain only one element, which is a String that will contain the text of any SQL exception thrown when we make our database call. In practice, this special Vector would contain several elements, each serving a special role.

First, I will define my Table object:

Listing 1


import java.util.*;	// contains the Vector object

public class Table extends Vector
	{
	/* 
	* Object-oriented programming lesson #1: access an object's 
	* properties by calling its methods!
	*/

	// The Table's constructor goes here; you can put custom code inside
	
	public int getWidth () 
		{
		/*
		* Returns the size of a typical component row of the Table.
		* This only works if every row of the Table has the same size 
		* (except for the special row). 
		*/
		
		int width = 0;
		if (this.size == 1)	// Database returned no rows
			return 1;
		try 	
			{
			Vector thisrow = (Vector) this.elementAt (1);
			width = thisrow.size ();
			}
		catch (Throwable e) {}
		return width;
		}

	public int getLength () 
		{
		/*
		* Returns the number of rows returned by the database.
		* Subtract 1 for the special row.
		*/
		
		return this.size () - 1;
		}

	public Object getElement (int i, int j) 
		{
		/*
		* Returns the object at row i, position j (similar to an Array).
		*/
		
		Object obj = new Object ();
		try 	
			{
			Vector thisrow = (Vector) this.elementAt (i + 1);
			obj = thisrow.elementAt (j);
			}
		catch (Throwable e) {}
		return obj;
		}
	
	public String getErrorMessage () 
		{
		/*
		* Returns the error message in the special row.
		*/

		String s = new String ();
		try 	{
				Vector thisrow = (Vector) this.elementAt (0);
				s = thisrow.elementAt (0).toString ();
				}
		catch (Throwable e) {}
		return s;
		}
	}


Now that I have defined a Table, let’s access the database and instantiate the Table object:

Listing 1a


import java.sql.*;	// This is where Java Database Connectivity (JDBC) lives
import java.util.*;	// for the Vector object, again

public class DatabaseHandler 
	{
	
	// Constructor goes here
	
	public Table getTable (String query)
		{
		// JDBC stuff
		Statement statement = null;
		ResultSet results = null;
		ResultSetMetaData meta = null;
		
		// Table stuff
		Vector thisrow = new Vector ();
    	Table newtable = new Table ();
    	
    	// More JDBC stuff
    	connect ();		
    	/*
    	* Handles connecting to the database 
    	* (you write this part)
    	*/
    	
		statement = connection.createStatement();
		try
			{
    		results = statement.executeQuery(lookupSql (query));
    		/* "query" is a String that is passed from the Web server to 
			* indicate what data are desired; lookupSql () is a method that 
			* returns the appropriate SQL statement as a String.
			*/
			
			thisrow.addElement ("OK");	// indicates no SQL exception
			newtable.addElement (thisrow);
			}
		catch (SQLException exception)
			{
			thisrow.addElement (exception);
			newtable.addElement (thisrow);
			}
			
		meta = results.getMetaData (); // so we can get the column count
		while (results.next ())		// here's where we move through the data
			{
			thisrow = new Vector ();		
			for (int i = 1; i <= meta.getColumnCount (); i++)
				thisrow.addElement (results.getObject (i));
			newtable.addElement (thisrow);	// add one Vector to the Table
			}		
		statement.close ();
		return newtable;
		}
	}

The database returns a Result Set, which is a peculiar kind of object that you must traverse row by row. We put the contents of that object into the Table. Each element in the Table can be any kind of Java object. If your database can’t return Java objects, you may want to cast the contents of the Table to strings.

The Web server code

Our Web server must now be able to call the getTable method of an instance of the DatabaseHandler object. Where you put this class depends on how your Web server’s Java Virtual Machine works.

Listing 2


public class WebServerPlugin
	{
	DatabaseHandler handler;
	
	public WebServerPlugin
		{
		/*
		* Code for connecting to the DatabaseHandler object goes here 
		* in the constructor. You might use RMI, or some Web server specific
		* code.
		*/
		
		handler = new DatabaseHandler ();
		}
	
	public synchronized Table getTable (String query) 
		{
		Table t = new Table ();
		try
			{
			t =  handler.getTable (query);
			}
		catch (Throwable e) {}
		}
	return t;
	}


The Server-Side JavaScript

I will now use Server-Side JavaScript (SSJS) to access the WebServerPlugin. The Server-Side JavaScript-to-Java communication is called Server-Side LiveConnect, pioneered by Netscape Communications.

First, we must call a function that will instantiate the WebServerPlugin and store it in the project object, so every Web page in the SSJS application can access the connection.

Snippet 1


function connectToJava ()
	{
	if (project.connection == null)
		project.connection = new Packages.WebServerPlugin ();
	}


Next, I will define a custom object, which can be instantiated by calling the following function. Instantiating objects by calling functions is what makes JavaScript an “object-based” language.

Listing 3


function tableObject (query) 
	{
	var thistable = project.connection.getTable (query);
	/*
	* This is how I call the Java getTable method of the 
	* WebServerPlugin object.
	*/
	
	this.width = thistable.getWidth ();
	this.length = thistable.getLength ();
	this.element = new Array (this.length);
	
	this.error = '' + thistable.getError ();
	/*
	* I must "cast" the error into a string by adding it to the null string,
	* or JavaScript won't know what to make of it!
	*/

	for (i = 0; i < this.length; i++)
		{
		/*
		* Here is where I build a JavaScript array object that I can access
		* when I build by HTML page.
		*/
		
		this.element[i] = new Array (this.width);
		for (j = 0; j < this.width; j++)
			this.element[i][j] = "" + thistable.getElement (i, j);
		}
	return this;
	}


Putting it on the page

Now comes the fun part. Here’s all you have to do on your HTML page to make a database call and access the data returned:

Snippet 2


myTable = new tableObject ('myquery');	// actually performs a database call!
write (myTable.element [1][2]);	// writes the second element of the first row


Remember, the String ‘myquery’ represents whatever parameters you want your Web page to send to the database to indicate which query you want to perform. Once myTable is instantiated, you can think of

myTable.element
as a simple JavaScript array.

Conclusion

The power of distributed object-oriented architectures lies in the flexibility of their interfaces. As long as each component knows how to communicate with those other components it is supposed to communicate with, it doesn’t matter whether data take different forms in different components. It’s like the old story of the four men and the elephant, only without the monolithic elephant of a legacy system. It’s okay that the four men see things in different ways, as long as they know how to communicate with each other.


About the author

Jason Bloomberg has been coding, scripting, and programming Web sites since early 1995. He is now director of Web technology at TransNexus LLC, an Internet Telephony company, but is best known for his JavaScript games at The Rhodes Arcade. His book, Web Page Scripting Techniques, was published by Hayden Books in 1996. He has two children and lives in Atlanta, Ga.


Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories