HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 
Implementing a Lightweight Web Server for Resource Pooling and Scalability, by Kieth Majkut and Vivek Sharma

On the Web it's about three things – speed, reliability and scalability. Does your Web site respond quickly? Does your Web site always respond quickly? Does your Web site always respond quickly when it's being used by tens or hundreds of thousands of users?

Hardware is part of the answer, but software may arguably be more of the answer. Poor use of limited resources can affect performance as much, or more, as too little hardware.

Over time your Web system may have become a combination of everything from static files to database applications, from CGI to servlets, from C to Perl to Java and on and on and on. At some point it's good to step back and examine each of these pieces and the resources they use.

Even if you do, you may only tweak each component independent of the rest. You may think the CGI and the servlet don't affect each other, or the database applications don't affect (the serving of) the static files, but they do. Just like any large piece of software, every piece of a Web system affects every other. It would be good to step back and examine each piece in regard to the system as a whole. Examining what system resources each piece is using can prove very beneficial.

Most Web applications depend on a database back end. A database is a common resource shared by different applications. An examination of database resource usage should answer a number of questions. How many connections are needed? How many SQL statements are being executed? And how many round-trips (from the Web server to the database) are being made? The answer, probably, is too many.

Just because a large system can support a huge amount of resource usage doesn't mean that having one will improve speed, reliability or scalability. Quite the opposite could be true – it may be causing undue bottlenecks. This is exactly what we found while developing large Web systems for Oracle. A resource bottleneck is one of the main reasons we implemented a lightweight Web server.

In this article we discuss our implementation of a lightweight server that pools database resources for disparate pieces of a Web system. We describe our initial system problems as well as the first and second (improved) versions of the server. After that we encourage you to examine your system and its limited resources to see if you need a server like this to drastically improve performance.

The Initial System
We begin with a brief description of our Web system and some of the applications that are part of the architecture. As with many Web systems, ours includes a membership database. After login, members are authorized to access (static) documents in the file system. The Apache Web server is used to serve static pages, and an Apache module, written in C, controls access. The Apache authentication module receives the username and password, queries the database and (dis)allows access as necessary. The module makes multiple database queries to extract different pieces of authorization information from different tables, which causes multiple round-trips to the database.

We also have a Java servlet (the level servlet) that's used to set membership levels. After payment is received (via another system), the servlet receives the username and updates the database. The servlet executes only one database update and causes one round-trip.

Finally another Java servlet receives a username and password, queries the database, sets cookies and (dis)allows access as necessary. The module makes multiple database queries and causes multiple round-trips. This servlet performs the same duties as the Apache module, but is used for application login, not file (system) protection.

Each of these three components makes one or more connections to the database. The authentication module is written in C and loaded with the Apache process. The level and authentication servlets are written in Java and basically operate stand-alone. The authentication module and the authentication servlet execute the same SQL statements; the level servlet executes a different statement. These three components are a tangled web of software, whose pieces only partially overlap (see Figure 1).

Figure 1
Figure 1

The Resulting Problem
The resource problems did not occur all at once, nor were they initially obvious. This was because each module was written independently of each other and implemented over a long period of time. As system usage grew, the fact that each component made an unrestrained number of database connections and round-trips caused the Web system in general to slow down. This is because opening and closing connections is a relatively expensive process. At peak times the system was being overloaded by connections and round-trips.

The first thought was to maintain connection pools in each of the components. This would definitely save the time that was spent opening connections. However, determining the optimal number of connections for each component was difficult as each component had different usage patterns (peak times). The authorization module is used constantly (with peaks); the authorization servlet has regular peaks and valleys; and the level servlet is used sparingly. Maintaining a fixed number of connections in each component would solve part of the problem, but not all of it. To illustrate this, let's say the authorization servlet needs to maintain 50 connections for peak usage, which occurs at 6 a.m. Pacific time. And let's say the authorization module needs 70 at 5 p.m. If each of them maintained their own connection pool, we'd have to establish 120 (70 + 50) connections throughout the day. However, realistically, we don't need that many connections around all the time (after all, established connections themselves are a drain on the system). It would be better if we could keep 90 (or some number greater than 70) open connections to serve peak usage for both the servlet and the module.

And the Solution
A centralized connection pool was needed. Each component could then retrieve connections from the pool, execute SQL and return connections to the pool. This immediately suggested an RMI- or CORBA-based system. The first problem is that Connection objects can't be transmitted over an RMI system.

This meant the core logic that performs SQL operations had to be coupled with the connection pool inside a server. This server can accept basic information, such as usernames, passwords and some action indicator, and perform tasks for different client applications. This again seems like a task that can be accomplished by an RMI/CORBA server. However, we didn't use RMI or CORBA due to a number of reasons:

  1. Distributed systems using RMI/CORBA are beneficial when objects need to be exchanged between applications. However, in our case several components just needed to transfer string data, so the overhead of RMI/CORBA was not justified.
  2. With RMI and CORBA you need to run special compilers to generate skeleton and stub files, then you must distribute them. This maintenance overhead was not desirable.
  3. The final disadvantage, unique to RMI, is that it can only work with components written in Java. In our Web system that would mean we can combine only two out of three components. The Apache authorization module would be excluded and it was the largest resource user.
Although the investigation into RMI and CORBA didn't provide a solution, it did help us conceptually. It reintroduced us to one of the underlying mechanisms – sockets. Both Java and C components can communicate over sockets.

What we needed now was a lightweight server that could maintain a database connection pool and efficiently execute SQL statements on behalf of multiple applications (see Figure 2). The usage of sockets makes it similar to a Web server because it can reside on any machine in the system.

Figure 2
Figure 2

A Simple (Lightweight) Protocol
To make this work, we needed to define a protocol for communication between the server and the different applications. The two things this protocol had to accomplish were:

  1. The server should be able to distinguish which client application is connected to it so it can execute the corresponding method that services the request.
  2. The server and the client should be able to exchange multiple pieces of data during one request.
The first exchange would be from the application to the server. It would send a string, which would contain a unique value that identifies the client component. Optionally, the application would also send any data required by the server to process the request. The Authentication application would send a username/password that it wants validated and the level servlet would send a username/password/level of a user for update.

Since multiple pieces of data need to be sent in one string, a predefined separator is required. This separator is a unique string of characters that doesn't normally exist in the kind of data exchanged between the client and the server. So the client-to-server portion of the protocol is:

[clientIdentifier][separator][data1][separator][data2][separator].........

After receiving the information the server would first identify which client application is connected to it. This information would be present in the first data element of the string sent by the client. Based on this, the server would invoke a method that serves requests for this client application. This method would process the remaining data and prepare a result string. For instance, the method that handles authentication requests would parse the input string, retrieve the username/password, validate it against the database and prepare a result string that contains a Success or Failure code. The level servlet might pass only a username/password and a new level. This protocol would be like:

[data1][separator][data2][separator].......
Here's an example of this communication:

The authentication module would establish a connection with the server. It would then write the following string on the data stream:

AuthModule[SEP]username1[SEP]password1
Here [SEP] is the separator string we want to use in the communication. The server would read this string and parse it. The first element in the string would indicate that the request came from the authorization module. From this the server would figure out that there should be a username and password in the string. It would then validate this username/password combination and send back a result such as:
username1[SEP]authorized

System Overview
Several pieces are required to build such a system. First we need a server that can listen on a port for requests from client applications. The server then needs to spawn a thread that can handle each request. The thread should be able to read the information sent by the client application, parse it and invoke the appropriate method. The method that it invokes would be specific to the client application.

Also, we need a connection pool that's maintained by the server. Each method that serves client applications should be able to request a connection from this pool. It should also be able to return the connection back to the pool so other methods could use it. A description of the classes follows.

The Connection Pool
TheConnPool
This is a class for pooling database connections. It contains a vector that can store instances of the ConnectionObject class shown in Listing 1. ConnectionObject is an abstraction around a JDBC connection. Each ConnectionObject instance can contain a JDBC Connection object. It can also contain other connection-related information such as whether it's valid and/or open.

TheConnPool class itself is shown in Listing 2. It contains a method, createConnectionObject(), that creates and returns a ConnectionObject.

The constructor of TheConnPool class creates a number of ConnectionObjects and stores them in the vector. This class also contains a variable called totalConnections that keeps count of the number of ConnectionObjects created.

TheConnPool contains a getConnection() and a putConnection() method in it. Whenever a method of the server needs a database connection, it calls the getConnection() method. This method removes the first object (ConnectionObject) in the vector and returns it. When the server method no longer needs the connection, it can return the ConnectionObject back to the pool using the putConnection() method.

Note that both getConnection() and putConnection() are synchronized methods. This ensures that no two methods of the server can be putting/getting connections from the pool simultaneously, saving them from possible memory corruption.

Lightweight (Java) Server
Listing 3 shows the implementation of our lightweight server. LightweightServer first creates a TheConnPool object, thereby causing MAX_CONNECTIONS number of connections to be established with the database.

It then creates an instance of the ServerSocket class (in java.net) that listens for client connections on port 10101. (Of course, this can be easily changed so that all parameters like port, max connections, etc., are read from a configuration file.)

The ServerSocket class has an accept() method in it. This method causes the server to wait till a client tries to establish a connection to the machine/port on which the server is running. Since we want to serve all client requests, we start "accept()"ing calls inside a while(true) loop. Also, we don't want to make clients wait until all previous clients' requests have been fully serviced. So we create a thread for handling each client's request and start it. Once the thread has been started, control will come back to the accept() method so the server can process other client requests waiting in the queue. Meanwhile, the thread can service the client for which it was started.

WorkThread class is the heart of our server – it reads data sent by the calling application, processes it and returns results. This thread figures out the client application that called it based on the first element defined in our protocol. It then calls the appropriate method to handle that client's request. TheConnPool object created in LightweightServer is passed to each thread. Note that the same instance of this class is being passed to each thread. By doing so we're ensuring that there's synchronization among threads that try to get/put connections in the pool object.

Client Connections
We've now seen how to develop a server that can use a common database connection pool for serving requests from one or more client applications. Next we'll see how applications written in C (like our authentication module) and those written in Java (like our level servlet) can exchange information with this server. Listing 4 shows a Java snippet for connecting to the server. Listing 5 shows a C program for doing the same. In both cases we assume the server is running on port 10101 on the machine "dummy.server.com".

Optimization
Monitoring the Size of the Pool

We now have an architecture in place that can provide centralized processing and database resource pooling for multiple applications. The next step is to optimize the usage of this pool. This can be achieved with a monitor thread that periodically checks the number of connections in the pool and compares it with the number actually being used by one of the server threads. The monitor thread can then add/remove connections from the pool depending on usage. In times of heavy usage, the monitor can create new ConnectionObjects and add them to the pool. At low traffic times it can remove some ConnectionObjects to free up database resources.

Also, we need to maintain the concept of minimum and maximum connections. When the server starts up it should bring up a few connections. As usage grows, the number of connections in the pool can increase. However, we want some restrictions to prevent a process from hogging the database. This can be done by restricting the number of connections to a prespecified hard limit.

To do this we need to add a couple of methods to TheConnPool class: addConnectionObjects() and removeConnectionObjects(). Also, the constructor of TheConnPool needs to start the monitoring thread. See Listing 6 for an outline of these changes.

For the sake of illustration let's assume that the site using this monitor expects traffic that varies from very low, to medium, to very high. In the first case we'd want no more than five connections. In the second we'd like to keep open connections at 10. Third, no matter how high the traffic goes, we don't want connections to go beyond 20. So, we have a SOFT_LIMIT of five, MID_LIMIT of 10 and a HARD_LIMIT of 20.

The MonitorThread class shown in Listing 7 attempts to optimize the number of open connections. It wakes up every minute and checks the total number of connections and compares it with the number of connections being used. If too many are being used, it reduces the number to MID_LIMIT or SOFT_LIMIT, whichever applies. Similarly, it increases the connections if it figures that more are required due to high traffic. Note: You can add a further level of granularity by adding limits between the SOFT, MID and HARD limits.

Logging Connection Usage Information
The next big question is how to determine good values for SOFT_LIMIT, MID_LIMIT and HARD_LIMIT. One easy way is by adding some code to the MonitorThread so it writes to a log file whenever it increases or decreases connections in the pool. You can put in time stamps that tell you how often the thread is activated. If the thread is activated too often, then the selected numbers aren't good. Another way is to write a LogThread along the same lines of the MonitorThread. This thread could also wake up every few minutes and find out the total number of connections and the connections being used. It could then write this information along with a time stamp to a log file.

If the log file is written in a Comma Separated Value (CSV) format, you can use something like Excel to see traffic patterns on your site graphically. So, if total connections are equal to the connections being used for most of the day, then you might consider raising your HARD_LIMIT. On the other hand, if the number of connections being used never reaches the total connections, then you can reduce the HARD_LIMIT number and save some database resources.

Other Improvements
Stored Procedures

We save time by preopening connections to the database in TheConnPool class. Making round-trips to the database is another expensive operation. If you're executing a large number of SQL statements, each causing a round-trip to the database, you'll see performance degradation. You should try to reduce these round-trips. One way is by creating stored procedures. Instead of having a number of JDBC calls executing one SQL statement at a time in your logic, you could bunch the statements together and create a stored procedure in the database.

Different vendors provide different ways to do this. With an Oracle database you can create a stored procedure using PL/SQL. This way you'd make just one call to the database over the connection, greatly improving your overall performance.

Statement Pooling
The ConnectionObject above is a wrapper class that contains a JDBC Connection object. We're not storing Connection objects directly in the vector that's maintained by TheConnPool because we want to store information about the connection, such as whether it should be discarded. Another piece that can be stored here is a set of precreated Statement objects. This is beneficial for statements that need to be executed repeatedly, since creating a PreparedStatement and keeping it open saves time.

Since we know which statements the server threads can execute, we can precreate the corresponding Statement objects. This can be done with minimal changes. The createConnectionObject() method in TheConnPool class can be enhanced so that after creating a connection, it creates a CallableStatement (for stored procedures) or a PreparedStatement object, gives it a name and stores it in the stmts hashtable. A method in the server thread can then retrieve a connection and the actual statement it needs to execute.

In Listing 8 we've slightly modified the ConnectionObject class by adding a hashtable called stmts.

We modify TheConnPool class so it creates the required statements and stores them in the ConnectionObject. In Listing 9 we're creating one CallableStatement and storing it with the name LEVEL_STMT, and creating a PreparedStatement and assigning AUTH_STMT as its name. Before we had statement pooling, the method that served authentication requests would extract a connection out of the ConnectionObject and create a statement from it. Now it can simply request the required Statement object and execute it. Listing 10 illustrates how the new method would look.

Making the Server More Generic (Version 2)
The lightweight server discussed above is fairly generic. You can easily add new methods to the WorkThread class and make it capable of handling requests from new applications. However, the drawback is that for every new application you'd have to recompile (and restart the server). In addition, the server code would start looking clumsy and become difficult to maintain as new applications are added to the system. To overcome this we decided to decouple the request handling functionality from the WorkThread class. So, instead of having the functionality inside methods defined in WorkThread, they would be contained in their own classes. By slightly changing our protocol, we can invoke handlers for any class without ever having to recompile the lightweight server.

To do this we defined an interface called ServerWorker. This interface has a method called execute that returns a string. The input parameters of this method are a ConnectionObject and the string that's communicated from the client application to the server (containing the username, password, etc.). The ServerWorker is shown below.

public interface ServerWorker
{
public String execute(ConnectionObject connObj, String inp);
}

Every client handler needs to reside in a class that implements this interface. An example implementation is shown in Listing 11.]

To make the server independent of client applications we need to make two changes:

  1. The protocol needs to be changed. Instead of passing a client identifier to the server exchange as the first element in the client, it should pass the name of a class that can handle the application's requests.
  2. After examining the first element in the protocol, the server should instantiate an object of the class specified in the exchange instead of calling a method in WorkThread.
As long as this class implements the ServerWorker interface, the server can invoke its execute method and have it process the client request. The benefit is that now new applications can be added to the system without changing the server. The changed portions of the server are shown in Listing 12.

To use this server, the following string is sent from the client to the server:

AuthWorker[SEP]username1[SEP]password1
Now look at Listing 12 to see how our server would respond to such a request. First it'll parse this string and determine that the class that handles requests for this client is named AuthWorker. It will use the forName method of class to load the class of this name (AuthWorker). It will then create an instance of this class using the newInstance() method of Class. newInstance() returns an object. However, our server deals only with handler classes that implement the ServerWorker interface. We can cast the object that results from newInstance() to a ServerWorker object. Since each class that implements ServerWorker needs to have an execute() method in it, our server will simply invoke this method and pass it a ConnectionObject and the string it received as input from the client. Now the execute() method in the AuthWorker class can parse the input string, figure out the username/password, use the ConnectionObject to authorize the user and send results back to the server.

Summary
Speed is a crucial factor in the success of any Web site. One of the common factors that impedes speed is improper utilization of resources. And database connections are a scarce and expensive resource that should be used very carefully. In this article we've looked at a centralized server that can help optimize usage of your database. This server can maintain a pool of connections that can be shared by any application.

This concept isn't limited to database connections; ideally, any resource that can affect performance should be moved into an architecture like this. By doing proper logging and monitoring you can figure out an optimal configuration and improve the response time of your applications.

Author Bios
Keith Majkut, a software development manager at Oracle Corporation, leads a team responsible for Web infrastructure projects. He's been at Oracle for over 10 years and has followed the technology from DOS to Web and from C to Java. He can be contacted at: kmajkut@us.oracle.com

Vivek Sharma is a software developer at Oracle Corporation. He has over seven years of industry experience. His areas of experience and interest include Web-based research and development. He also coauthored the book Developing e-Commerce Sites: An Integrated Approach Addison-Wesley. Vivek can be contacted at: vivek_sharma_99@yahoo.com

	


Listing 1

public class ConnectionObject
{
    Connection conn;
    boolean connectionOk;
    ConnectionObject(Connection c)
    {
          conn = c;
    }
    public Connection getConnection()
    {
         return conn;
    }
}

Listing 2

public class TheConnPool
{
  int totalConnections = 0;
  Vector pool;
  TheConnPool(int max)
  {
    pool = new Vector();
    for(int i=0; i<max; i++)
    {
      ConnectionObject connObj = createConnectionObject();
                            pool.addElement(connObj); 
      // Add connection to pool
                            totalConnections++;
                  }
           }
           public synchronized Connection getConnection()
           {
                    if(pool.size() == 0)  
	/* No more available connections in pool */
                          return null;
                    ConnectionObject connObj =
                       (ConnectionObject)pool.elementAt(0);
                    pool.removeElementAt(0);
                    return conn;
           }
           public synchronized void putConnection(
						ConnectionObject connObj)
           {
                      pool.addElement(connObj);
           }
           public ConnectionObject createConnectionObject()
           {
               Connection c = .... /* Create a JDBC connection 
                                  using the DriverManager     
                                  class as you normally do */
   return new ConnectionObject(c);
  }
}

Listing 3

public class LightweightServer
{
          public static void main(String[] args)
          {
                       ........
                   TheConnPool connPool = 
					new TheConnPool(MAX_CONNECTIONS);
                    ServerSocket serverSocket = 
					new ServerSocket(10101);
                    while( true )
                    {
                          Socket clientSocket = 
						serverSocket.accept();
                          WorkThread wt =
					   new WorkThread(clientSocket, connPool);
                          wt.start();
                    }
                         .........
            }
}
class WorkThread extends Thread
{
                Socket clientSocket = null;
                TheConnPool connPool;
                WorkThread(Socket cs, TheConnPool cp)
                {
                          clientSocket = cs;
                          connPool = cp;
                }
                public synchronized void run()
                {
                      // Get connection object from pool
                      ConnectionObject connObj =
                         connPool.getConnectionObject(); 
                      // Establish input/output streams for 
                      // communication with client
                      InputStream is = new
                    	 BufferedInputStream(
 					clientSocket.getInputStream());
                      java.io.PrintStream ps = new
                        java.io.PrintStream(
					clientSocket.getOutputStream());

                      // Read data sent by client
                      byte[] buffer = new byte[1024];
                      is.read(buffer, 0, 1024);
                      String inputString = new              
                                           String(buffer);

                   ...//Parse the input string and examine 
                      //the first element
                      // According to the protocol this 
                      // element should tell us which 
                      // application has made the request

                      if(firstElement.equals("AUTH"))   
                   /*    Called by the Apache or the Servlet 
                         authentication client */
                         processedResult =
                         executeAuthenticate(connObj,input-
                         String);
                      else
                      if(firstElement.equals("LEVEL"))
                         processedResult =
                         executeLevelUpdate(connObj,input-
                         String); 
                       	// Called by the level servlet
           // More else ifs for  handling other client types

connPool.putConnection(conn); // Return connection to pool

                      	// Send result to the client
                       ps.print(processedResult);
               }
               private synchronized String executeAuthenticate(
                         ConnectionObject connObj, String i)
               {
// Extract Connection from connObj and create required Statement
                      Connection conn = connObj.getConnection();

                    	/* Extract username/password
                           Verify this against database     
                           using username/password and 
                           Connection c Prepare result  
                           string and send it back
                     		*/
               }
               private synchronized String executeLevelUpdate(
                         ConnectiononnObj connObj, String i)
               {
                    		// Extract Connection from             
                       connObj and create required Statement
                      Connection conn = connObj.getConnection();
/* Extract username and level Connect to database and        
   set new level for this member Send a Success  or Failure code  */
               }
}

Listing 4

String HOST = "dummy.server.com";
                int PORT = 10101;
                String sep = "[SEP]";

                Socket clientSocket = new Socket(HOST, PORT);
                os = new PrintWriter(
                            new OutputStreamWriter( 
                              clientSocket.getOutputStream()));
                is = new BufferedReader(
                             new InputStreamReader( 
                              clientSocket.getInputStream()));
                String sendVal = "LEVEL" + sep + username +   
                                sep + level;
                os.println(sendVal);
                os.flush();
                String recVal = is.readLine();
                os.close();
                is.close();
                clientSocket.close();

Listing 5

#include <arpa/inet.h>
#include <sys/socket.h>

 main()
 {
        struct hostent *host;
        unsigned long address;
        unsigned short port = 10101;
        char *hostName = "dummy.server.com";

        int sock;

        char buffer[60];
        int bufferlen;

        char reslt[2048];
        int RESLT_LEN = 2047;
        struct sockaddr_in addr;

/* Lookup the host, create a socket and establish connection on port 10101 */
    host = gethostbyname(hostName);
    address = ((struct in_addr *)host->h_addr_list[0])->s_addr;
    addr.sin_addr.s_addr = address;
    addr.sin_port = htons(port);
    addr.sin_family = AF_INET;
    sock=socket(AF_INET, SOCK_STREAM, 0);
    connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));

        /* Put data to be sent to server in variable 'buffer' */
    strcpy(buffer, "AUTH[SEP]username[SEP]password";

        /* Write 'buffer' to the output stream */
    bufferlen=strlen(buffer);
    write(sock, buffer,bufferlen);

        /* Read result sent by the server  */
   read(sock, reslt, RESLT_LEN);
}

Listing 6

TheConnPool(int max)
   {
         ....
         MonitorThread mt = new MonitorThread(this);
         mt.start();
   }
   public synchrnonized void addConnectionObjects(int n)
   {
         for(int i=0; i<n; i++)
         {
           ConnectionObject connObj = createConnectionObject();
             pool.addElement(connObj); // Add connection to pool
             totalConnections++;
         }
   }
   public synchronized void removeConnectionObjects(int n)
   {
         for(int i=0; i<n; i++)
         {
             ConnectionObject connObj = pool.elementAt(0);
             try{
                  Connection conn = connObj.getConnection();
                   conn.close();
             }catch(Exception ex){}
             pool.removeElementAt(0);
             totalConnections--;
         }
   }

Listing 7

class MonitorThread extends Thread
{
    int SOFT_LIMIT=5;
    int MID_LIMIT=10;
    int HARD_LIMIT=20;
    int SLEEP_TIME = 60000; /* One minute */
    TheConnPool tc;
    MonitorThread(TheConnPool t)
    {
         tc = t;
    }
    public void run()
    {
       while(true)
       {
          int connsAvailable = tc.pool.size();
          int totalConns = tc.totalConnections;
          int connsBeingUsed = totalConns - connsAvailable;
          boolean increaseConns = false, reduceConns = false;
          int increaseBy = 0, reduceBy = 0;
          if(connsBeingUsed < SOFT_LIMIT && totalConns > SOFT_LIMIT)
          {
                reduceConns = true;
                reducyBy = totalConns - SOFT_LIMIT;
          }
          else
          if(connsBeingUsed < MID_LIMIT && totalConns > MID_LIMIT)
          {
                reduceConns = true;
                reducyBy = totalConns - MID_LIMIT;
          }
          else
          if(connsAvailable == 0 && totalConns < MID_LIMIT)
          {
                increaseConns = true;
                increaseBy = MID_LIMIT - totalConns;
          }
          else
          if(connsAvailable == 0 && totalConns < HARD_LIMIT)
          {
                increaseConns = true;
                increaseBy = HARD_LIMIT - totalConns;
          }
          if(reduceConns)               
tc.removeConnectionObjects(reduceBy);
          else
          if(increaseConns)
               tc.increaseConnectionObjects(increaseBy);
          try{
                sleep(SLEEP_TIME);
          }catch(InterruptedException ie){}
      }
    }
}

Listing 8

public class ConnectionObject
{
    Connection conn;
    boolean connectionOk;
    Hashtable stmts;
    public setStatements(Hashtable h)
    {
         stmts = h;
    }
    public Statement getStatement(String nm)
    {
         return (Statement)stmts.get(nm);
    }
    ......

Listing 9

public class TheConnPool
{
           public ConnectionObject createConnectionObject()
           {
                    Connection c = .... 
/* Create a JDBC connection using the DriverManager class as you normally do */
                    ConnectionObject connObj = new ConnectionObject(c);
                    Hashtable stmts = new Hashtable();

                    PreparedStatement pstmt = 
                                   c.prepareStatement(
  "UPDATE member_table " +
                                      " SET level = ? WHERE username = ?");
                    stmts.put("LEVEL_STMT", pstmt);
                    CallableStatement cstmt = 
                          c.prepareCall(
      "{call Auth_Api.authenticate(?,?,?,?)}");
                    cstmt.registerOutParameter(3, Types.VARCHAR);
                    cstmt.registerOutParameter(4, Types.VARCHAR);

                    stmts.put("LEVEL_STMT", pstmt);
                    stmts.put("AUTH_STMT", cstmt);

                    connObj.setStatements(stmts);
                    return connObj;
           }

Listing 10

               private synchronized String executeAuthenticate
	   (
         ConnectionObject connObj, 
   String i)
               {
                    // Extract required Statement connObj 
CallableStatement cstmt = (CallableStatement)connObj.getStatement
("AUTH_STMT");
        // Execute the query using this pre-created statement
               }

Listing 11

public class AuthWorker implements ServerWorker
{
    public String execute(ConnectionObject connObj, String inp)
    {
           String result = "";
           .... // Parse the username/password
           try{
                 PreparedStatement pstmt = 
connObj.getStatement("AUTH_STMT");
                 .....
              
           }catch(Exception ex){}
           return result;
    }
}

Listing 12

                public synchronized void run()
                {
                      // Get connection object from pool
                     
                 ConnectionObject connObj =
                         connPool.getConnectionObject(); 
                      ....
                      // First element contains the name of   
                      // the class that can handle the request.

                      Class c = Class.forName(firstElement);
// Create an instance of this class and invoke the execute method

                      ServerWorker sw = (ServerWorker)c.newInstance();
                      String result = sw.execute(connObj, inputString);
                      ....



  
 
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.