Java is considered slow when compared to compiled languages such as RPG, COBOL, and C++. Yet, when you set about to performance tune your Java application, youll find that the best way to improve your Javas execution speed may have nothing to do with how it was created, but rather, how it is used.
There are two major reasons why Java applications are slower than traditional languages: Java apps are interpreted, and theyre object-oriented. If you are a Java expert who fully understands how interpreted, object-oriented applications execute, then you will be able to optimize your code for performance. But many AS/400 shops that have recently acquired the responsibility for maintaining Java applications simply do not have that level of expertise. No matter, because it turns out that the best way to improve your Java applications performance is not to be found from laborious code optimization but from making a few simple modifications to your data access strategies.
The simplest change you can make to your Java application garners the highest yield. That change is to start using connection pools. Think about it: What are the two most processor-intensive things you do with OS/400 applications? The answer is, number one, starting, or initializing, a job, and number two, opening a file (database). Your Java applications get at data first by creating a connection object and then accessing an OS/400 database file. Yet most Java applications create these connection objects with little regard for their performance penalty. Connection pools are the solution to that problem. This article introduces you to the concept of connection pools and then provides a tutorial for their use with your OS/400 Java applications.
OS/400 Connection Pools
A myriad of connection pools are available as open-source on the Internet, but I was reticent to suggest oneuntil now. The good people of the JTOpen team at IBM, with JTOpen version 2.0, have seen fit to supply us AS/400 Java coders with a connection pool for not only the JDBC Connection object but also the AS400 object. (The AS400 connection object is required when using the AS/400 Toolbox for Java classes that enable such AS/400-specific things as record-level access, program call, and data queue support.)
You can download the latest version of JTOpen from www.ibm.com/servers/eserver/ iseries/toolbox, but note that youll also need the JDBC 2.0 Optional Package Binary, available at www.java.sun.com/products/jdbc/ download.html.
When you create a connection pool, it prestarts a half-dozen or so connection objects. Then, in your Java apps, whenever you need a connection object, instead of creating one yourself, you can simply request one from the pool with the getConnection method. When youre done with the connection object, you put it back in the pool with a call to the close method for a JDBC Connection or the returnConnectionToPool method for an AS400 connection object. As a result of using pooled connections, those half-dozen or so prestarted connection objects may effectively replace the need to create hundreds or thousands of connection objects over the course of the day, thus substantially improving performance.
The JDBC Connection Pool
In this article, Im going to provide code examples that use the JDBC connection pool class (AS400JDBCConnectionPool). Note, however, that the same principles apply for the AS400 connection pool class (AS400ConnectionPool).
There are two strategies for creating a connection pool. One way is to create the AS400JDBCConnectionPool object and then make that object accessible to all clients. However, to be able to access the connection pool, the clients must be in the same Java Virtual Machine context (job). The other way is to create the AS400JDBCConnectionPool and assign it a Java Naming and Directory Interface (JNDI) name. Setting up JNDI is a pain worth enduring, because with it, Java clients can access your connection pool from anywhere on the Internet. Besides, JNDI is an integral part of Suns J2EE framework, so its a good idea to become familiar with it.
With JNDI, you can assign a virtual name that allows users at remote sites to access the AS400JDBCConnectionPool object that youve created on your host machine. JNDI requires some facilities under its wrappers to service the remote requests. Now you can download reference implementations of JNDI that use Lightweight Directory Access Protocol (LDAP), Common Object Services (COS), and Remote Method Invocation (RMI) from Sun. I suggest using the RMI implementation to enable JNDI because it is the easiest to install and configure.
To download the RMI implementation, visit www.java.sun.com/products/jndi and select the JNDI 1.2.1 download button, then off the next panel, select JNDI 1.2.1 class libraries and sample code and RMI registry service provider, 1.2.1 release. Those download options will place zip files on your machine that you will then have to extract to obtain the jndi.jar, rmiregistry.jar, and providerutil.jar files that you will need to add to your classpath. To be able to use the RMI implementation with JNDI, youll need to start Javas RMIRegistry utility. To do that from an NT environment, use the command START RMIREGISTRY. From a UNIX or OS/400 shell environment, use rmiregistry &. The ampersand tells QShell (or one of the other UNIX shell derivations) to run the rmiregistry server as a background process. As do all Internet application servers, the RMI server requires a port number; the default here is 1099.
With the RMI server started, you can create an AS400JDBCConnectionPoolDataSource/object and associate it with a JNDI context, as shown in Figure 1 (page 35) by my simple ConnPoolStart utility class. Note that, beside the toolbox package, ConnPoolStart requires the hash table class, from the java.util package; Suns ConnectionPool class, from Java optional extensions package; and the JNDI package, javax.naming. After ConnPoolStart creates the AS400JDBCConnectionPoolDataSource-object and sets its appropriate attributes, a hash table is created to place JNDI context information. Hash tables hold name/value pairs where the name is indexable. JNDI uses a hash table object to find your settings for a JNDI service provider, indexed by INITIAL_ CONTEXT_FACTORY, and the URL for your
provider, indexed by PROVIDER_URL. ConnPoolStart uses Suns reference implementation for the JNDI service and a local RMI server that sits on port 1099.
After my ConnPoolStart class completes execution, the AS400JDBCConnectionPoolDataSource-object is remotely available through a JNDI context. But using a JNDI context requires that you build a hash table. And putting all that extra nonsense in each of the Java classes that require an SQL connection is a little complex and would duplicate a lot of code. The ConnPool400 class in Figure 2 shows a simple way to encapsulate access to the connection pool. ConnPool400 is an example of what is known as a Singleton (no, it was not named after the editor of MC magazine) class because all of its fields and methods are static. Static fields and methods can be accessed without creating an object of that class. For instance, to invoke the getConnection methodwhich is the method that our Java clients will be usingyou simply qualify it with the class name. Figure 3 is a simple application that gives an example of how easily the ConnPool400 Singleton class getConnection method can be used to list the records in QIWS/QCUSTCDT.
ConnPool400 also has a special method called the static method. This method is a little weird because it has no name. I used it to call the startConnPool and setDataSource methods, which initialize the state of the ConnPool400 class. The static method is special not just because it has no name, but because the first time the class is accessedin this case, with the getConnection methodthe static method is automatically called. After that, the static method will never again be called, regardless of how many other times that class is accessed. This makes sense, because you only need to initialize the ConnPool400 class once.
Web Access
My quest for a good connection pool started out because I wanted to improve performance of servlets and JavaServer Pages (JSPs) from Apaches open-source Web application server, Tomcat. Now that the jtOpen team has added the AS400JDBCConnection pool and AS400ConnectionPool classes, my JSPs can easily retrieve connections from a shared connection pool. I did have to change the Tomcat startup script so the CLASSPATH environment variable had the appropriate JARs. I also had to start the RMI registry service. (Two JSP examples that use my ConnPool400 class to retrieve an SQL connection object are available for download at www.midrangecomputing.com/mc.)
For best results, release connection objects retrieved from the pool as soon as you are done with them. To release a connection, simply invoke the SQL Connection objects close method. (It really doesnt close; the connection is actually returned to the free pool.) But if your JSP or servlet must maintain a transaction context through multiple Web- browsing user requests, youll have to put the connection object in the session object:
session.putValue(Connection, con);
Then retrieve it within the next user access with:
con = (Connection)session.getValue
(Connection);
But, again, when you are done with that connection object, return it to the free pool by invoking the close method.
Article.close()
The AS400JDBCConnectionPool class is database-specific, but if you create a utility class to access it, you can plug in other connection pools that work with other
databases. Most connection pool classes have a method called getConnection, so your client application code will not require modificationonly the utility class.
I didnt go into any detail about the AS400JDBCConnectionPool class and its methods. Mostly because I didnt have room for that here. For best results, read the Java doc for AS400JDBCConnectionPool and the AS400JDBCConnectionPoolDataSource classes. These documents contain information in them about settings such as the number of connections that are prestarted and placed in the free pool and the maximum number of connections allowed in the pool. And perhaps most important of all, the best way to learn about connection pools is to just try them out for yourself. Nothing beats hands-on experience.
The last thing I would like to point out is that everything I presented in the article will also work with the AS400ConnectionPool class. If you use AS/400-specific facilities, such as data queues, to communicate with RPG applications or if you use record-level access, the use of the AS400ConnectionPool class will improve your applications performance by cutting down the number of AS400 connections in your Web applications.
import com.ibm.as400.access.AS400JDBCConnectionPoolDataSource;
import java.util.Hashtable;
import javax.sql.ConnectionPoolDataSource;
import javax.naming.*;
public class ConnPoolStart {
public ConnPoolStart() {
AS400JDBCConnectionPoolDataSource dtaSrc =
new AS400JDBCConnectionPoolDataSource();
dtaSrc.setServerName(207.212.90.50);
dtaSrc.setUser(denoncourt);
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
com.sun.jndi.rmi.registry.RegistryContextFactory);
env.put(Context.PROVIDER_URL, rmi://127.0.0.1:1099);
try {
Context ctx = new InitialContext(env);
ctx.rebind(jdbc/MC, dtaSrc);
} catch (NamingException e) {
e.printStackTrace();
}
Figure 1: Once you have the RMI server started, you can create an AS400JDBCConnectionPool object and associate it with a JNDI context.
import com.ibm.as400.access.*;
import javax.naming.*;
import java.sql.*;
import java.util.Hashtable;
public class ConnPool400 {
private static AS400JDBCConnectionPool connPool = null;
static {
startConnPool();
setDataSource();
}
private static void setDataSource () {
Context ctx = getContext();
AS400JDBCConnectionPoolDataSource dtaSrc = null;
try {
dtaSrc = (AS400JDBCConnectionPoolDataSource) ctx.lookup(jdbc/MC);
} catch (NamingException e) {
e.printStackTrace();
}
dtaSrc.setPassword(vo2max);
connPool = new AS400JDBCConnectionPool(dtaSrc);
}
public static Connection getConnection() {
try {
Connection conn = connPool.getConnection();
return conn;
} catch (ConnectionPoolException e) {
e.printStackTrace();
return null;
}
}
private static void startConnPool() {
ConnPoolStart startPool = new ConnPoolStart();
}
public static Context getContext() {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
com.sun.jndi.rmi.registry.RegistryContextFactory);
env.put(Context.PROVIDER_URL, rmi://127.0.0.1:1099);
Context ctx = null;
try {
return new InitialContext(env);
} catch (NamingException e) {
e.printStackTrace();
return null;
}
}
}
Figure 2: This code shows a simple way to encapsulate access to the connection pool.
import java.sql.*;
public class ConnPoolTest {
public static void main (String[] pList) {
Connection conn = ConnPool400.getConnection();
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(select * from qiws.qcustcdt);
while (rs.next()) {
System.out.println(rs.getString(1)+ +rs.getString(2));
}
conn.close();
} catch (SQLException e) {
e.printStackTrace();
return;
}
System.exit(0);
}
}
Figure 3: This simple example application shows how easily the ConnPool400 Singleton class getConnection method can be used to list the records in QIWS/QCUSTCDT.
LATEST COMMENTS
MC Press Online