Tomcat 5 and DB2 for iSeries
May 12, 2004 Marc Logemann
Tomcat 5 was released several weeks ago, and already there has been a massive adoption by the industry. Tomcat 5 offers many new features, like Java Management Extension (JMX) monitoring, integrated session clustering, and improved taglib handling, and its performance and management capabilities are greatly improved. This release also reunited the Tomcat developer group, which had been split into two branches. Since my last article about DB2 for iSeries, many things have changed, so an update is important.
ACCESSING DB2 FOR ISERIES WITH DB2 UNIVERSAL DRIVER
Many people ask me about the JDBC setup with Tomcat and the JDBC driver included in the JTOpen package, but the forget there is another interesting choice to connect to a DB2 on the iSeries, by using IBM‘s Universal JDBC Driver. It is a Type 2 and a Type 4 JDBC driver, supporting connections via a DB2 database client on the application host (Type 2), or a direct JDBC connection to the database server without any DB2 client services (Type 4). The type being used by the driver is determined by the JDBC URL supplied by the application or, as I’ll explain later, by the Tomcat DataSource configuration.
But why use this driver instead of the one supplied by JTOpen, called IBM Toolbox for Java? Because, with this driver, you can connect to various DB2 platforms, like iSeries, zSeries, or Intel-based machines with Linux or Windows. Also, you get some of the new JDBC 3 features with the driver, such as getGeneratedKeys(), which will retrieve an auto-generated key of the last insert without requerying the database. For a complete list of supported JDBC 3 features, check out IBM’s Information Center.
But before you proceed, you should check the prerequisites for this driver on the iSeries. See the table below for a detailed list of PTFs that should be applied. The JDBC Driver uses Unicode, and these PTFs add support for it to the iSeries.
OS/400 Version | PTFs |
V5R3 | — |
V5R2 | SI06541, SI06796, SI07557, SI07564, SI07565, SI07566, SI07567 |
V5R1 | SI06308, SI06300, SI06301, SI06302, SI06305, SI06307, SI05872 |
USING THE DRIVER IN YOUR WEB APPLICATION
For declarative programming, you should avoid using the Class.forName() approach to load the JDBC driver, because changing the driver, or just the user name, password, or database name, would require code changes and recompiling. (Declarative programming is a way to externalize some parts of the application into XML or descriptor files in order to be able to make runtime changes without affecting the code. The Struts Framework is a perfect example of this. There you define most of the controller and dispatching behavior in XML files.) The DataSource classes were added to the JDBC API because of this. Together with JNDI (Java Naming and Directory Interface) to load the declaratively defined DataSource, you can change driver implementations and other settings without affecting the application code. In Tomcat there is more than one way to define a JNDI resource, and with the latest release of Tomcat, you can separate context configurations in different XML files, meaning that you don’t have a blown up server.xml anymore, since there is only one small server.xml and many context XML files.
But first you have to define the DataSource. Instead of defining the JNDI resource on the context level–like most Tomcat tutorials do–you place it in the server.xml as a global resource, thus making it easy to share across different contexts and Web applications. Here is the definition:
[..] <GlobalNamingResources> <Resource type="com.ibm.db2.jcc.DB2SimpleDataSource" name="jdbc/db2universal"/> <ResourceParams name="jdbc/db2universal"> <parameter> <name>factory</name><value>com.ibm.db2.jcc.DB2DataSourceFactory</value> </paramter> <parameter><name>username</name><value>db2admin</value></parameter> <parameter><name>database</name><value>mydatabase</value></parameter> <parameter><name>password</name><value>mypassword</value></parameter> <parameter><name>type</name><value>4</value></parameter> </ResourceParams> </GlobalNamingResources> [..]
As you can see, all resources that should have some global characteristics must be placed within the GlobalNamingResources tag. You define the type of the resource and the JNDI name for the lookup in the application. It’s important to know that even though DB2SimpleDataSource is of type javax.sql.DataSource, you should define the concrete type here, because otherwise the DB2DataSourceFactory doesn’t know what kind of IBM DataSource implementation to load. The DB2DataSourceFactory is one of the most important things to look out for. By supplying a “factory” parameter, Tomcat knows that it shouldn’t use its default factory for DataSources DbcpDataSourceFactory, but should instead use the IBM one. I left out the “server” parameter because the database runs on the same machine as the Tomcat daemon. Otherwise you would have to supply a valid DNS name or IP address. For a complete list of the DataSource properties, check the IBM DB2 site.
The DB2DataSourceFactory is capable of returning different kinds of DataSources, as shown in the table below. Remember that the resource type defines what DataSource will be returned by the factory.
- com.ibm.db2.jcc.DB2SimpleDataSource, which does not support connection pooling. You can use the implementation with the Universal Type 2 driver or the Type 4 driver.
- com.ibm.db2.jcc.DB2DataSource, which supports connections pooling. You can use this implementation only with the Type 2 driver. With this implementation, connection pooling is handled internally and is transparent to the application.
- com.ibm.db2.jdbc.DB2ConnectionPoolDataSource, which supports connection pooling. With this implementation, you must manage connection pooling yourself, either by writing custom code or by using a tool like IBM WebSphere.
- com.ibm.db2.jdbc.DB2XADataSource–same as DB2ConnectionPoolDataSource, but supports distributed transactions.
To use the JNDI resource in the Web application, you have to create a link in the context configuration file:
<?xml version="1.0" encoding="utf-8"> <Context path="/myApp" docbase="myApp"> <ResourceLink name="jdbc/db2universal" type="com.ibm.db2.jcc.DB2SimpleDataSource" global="jdbc/db2universal"> </Context>
The context configuration file should be named like the context docbase; in this case, the file should be created under:
$TOMCAT_ROOT$/conf/Catalina/$HOSTNAME$/myapp.xml.
Tomcat creates the folder structure on first startup, and if you deploy your Web application without manually creating a context configuration file, Tomcat will create it for you. You can modify the one you created afterward. The content of the XML file is not too spectacular; you just define the name that is made visible to the application and also the global name from the server.xml.
The following code describes very roughly how to retrieve objects via JNDI. If you want more details about JNDI, I suggest reading some Java books on the topic.
[..] try{ Context myContext = new InitialContext(); Context envContext = (Context)myContext.lookup("java:comp/env"); DataSource myDataSource = (DataSource)envContext.lookup("jdbc/db2universal"); } catch(NamingException e) { // do something } [..]
USING THE JTOPEN DRIVER AND DATASOURCE
Using the JDBC driver included in JTOpen is not much different from using the Universal Driver. One drawback when using the JTOpen package is the non-existence of an ObjectFactory, which will bootstrap the AS400JDBCDataSource. I developed a very simple ObjectFactory for this specific DataSource, which you can download and use. Inside the JAR file, I also provided the source, so you can easily see what kind of parameters are available for this DataSource.
A sample server.xml would look like this:
[..] <GlobalNamingResources> <Resource type="com.ibm.as400.access.AS400JDBCDataSource" name="jdbc/db2jtopen"/> <ResourceParams name="jdbc/db2jtopen"> <parameter> <name>factory</name> <value>de.logentis.jtopen.AS400DataSourceFactory</value> </paramter> <parameter><name>user</name><value>db2admin</value></parameter> <parameter><name>libraries</name><value>*libl</value></parameter> <parameter><name>password</name><value>mypassword</value></parameter> <parameter><name>serverName</name><value>myiSeries</value></parameter> </ResourceParams> </GlobalNamingResources> [..]
The ResourceLink inside your context XML file, described before, must be changed so the new name is reflected.
Again, the type of Resource will determine what kind of DataSource you get back. Right now the factory is capable of returning an AS400JDBCDataSource or an AS400JDBCConnectionPoolDataSource.
YOUR SOURCE OF DATA
Working with DataSources is essential these days, and Tomcat makes it easy to configure them. This article focused on handling DataSources with the IBM Universal JDBC Driver, not on performance issues. All connections you get back from the DataSources mentioned here are not pooled and will not perform as well as their pooled counterparts. In a future article, I will explain how to pool the different DataSources available.
Marc Logemann is founder of Logentis, a German consulting company that focuses on iSeries and Java services and development. E-mail: mlogemann@itjungle.com