# How to write a new driver wrapper for JOnAS

## Introduction

The goal of this document is to describe how to write a new driver based on the work made for the new database manager. This document is divided into 3 parts. The first part describes how to write a driver with the defined framework. The second section describes how to extend the current driver wrapper for other databases. And the last section describes the current extensions made with the driver wrapper.

The contents of this guide are the following:

1. Introduction
2. Architecture
3. A driver with the framework
4. How to extend the driver wrapper
5. Current extensions

- Informix
- Sybase
- Oracle (for Jonas)
- InstantDB

6. Jonas[newDriver]XADataSource

- Common interface for XADataSource
- i.e. InstantDB
- Changes in Jonas

7. Appendix A : Informix implementation

- InformixConnectionHandle.java
- InformixConnectionPoolDataSource.java
- InformixPooledConnection.java
- InformixXAConnection.java
- InformixXADataSource.java
- InformixXAStatefulConnection.java
- JonasInformixXADataSource.java

## Architecture

There are many possibilities to use the new database manager from Jonas. All the possibilities work with a pool of connection which is an extension of the framework.

The first possibility is the default, the database manager uses the Standard class which uses the standard wrapper. In this case, we need to have a JDBC 1.0 driver. This way is the common way to work with the new database Manager. But you can use the second possibility to extend the driver wrapper for special works with some database. It is the case with Informix and Sybase, these databases need some hacks to work. In these cases, the database manager uses a special class (Informix or Sybase) which uses the standard wrapper.

The third possibility is to extend the driver wrapper for the InstantDB database. In this case, the database manager uses an InstantDB hat to extend the standard wrapper. We obtain, then, a JDBC 2.0 driver when we uses the InstantDB database.

The last possibility is to use a JDBC 2.0 driver. Oracle has a JDBC 2.0 driver and you can use it with the database manager from Jonas. In this case, the database manager needs to work with some special extensions (hats), the XADataSource, XAConnection, XAPreparedStatement and XAStatement are extended to do the glue between the database manager from Jonas and the JDBC 2.0 driver from Oracle.

The next section explains what is a framework.

The "How to extend the driver wrapper" section explains the cases of Informix and Sybase, and describes the Jonas hats, need to do the glue between the database manager from Jonas and the driver wrapper or a JDBC 2.0 driver.

## A driver with the framework

The core package is defined as the framework of the Jdbc Service. The core virtual driver is made of a set of classes implementing jdbc2 classes. Most of those classes implement the interface by delegation. Other classes are abstract. It makes the foundation classes for all virtual drivers including virtual drivers implementing standard extensions, implementations of a pool, wrappers for instrumentation of existing drivers. There are no optimizations in this package.

- org.enhydra.jdbc.core.CoreDataSource implements javax.sql.DataSource. It provides a Data Source which can be used to generate JDBC connections. This class is generic in the sense that it does not rely upon anything other than standard Java APIs.
- org.enhydra.jdbc.core.CoreConnection implements java.sql.Connection. This is an implementation of java.sql.Connection which simply delegates everything to an underlying physical implemention of the same interface.
- org.enhydra.jdbc.core.CorePreparedStatement implements java.sql.PreparedStatement. When created it is supplied with another PreparedStatement to which all of its class' methods delegate their work.

These classes have to be extented to write the corresponding driver.

## How to extend the driver wrapper

Some classes have to be extended to support an extension of the wrapper :

- org.enhydra.jdbc.standard.StandardConnectionHandle (implementation of java.sql.Connection) sub-class is [newDriver]ConnectionHandle. This class will be used to do additionnal work on the connection (see informix and sybase implementation)
- org.enhydra.jdbc.standard.StandardConnectionPoolDataSource (implementation of javax.sql.ConnectionPoolDataSource) sub-class is [newDriver]ConnectionPoolDataSource. The getPooledConnection method has to return a [newDriver]PooledConnection.
- org.enhydra.jdbc.standard.StandardPooledConnection (implementation of javax.sql.PooledConnection) sub-class is [newDriver]PooledConnection. This class has to define the newConnectionHandle method to set up a [newDriver]ConnectionHandle object.
- org.enhydra.jdbc.standard.StandardXADataSource (implementation of javax.sql.XADataSource) sub-class is [newDriver]XADataSource. The getXAConnection method has to return a [newDriver]XAConnection object. getPooledConnection method (2 methods, with and without parameters) have to be copied here because there is no connection between the [newDriver]ConnectionPoolDataSource class and the [newDriver]XADataSource class.
- org.enhydra.jdbc.standard.StandardXAConnection (implementation of javax.sql.XAConnection and javax.sql.XAResource) sub-class is [newDriver]XAConnection. The constructor of this class has to use the [newDriver]XAStatefulConnection object.
- org.enhydra.jdbc.standard.StandardXAStatefulConnection sub-class is [newDriver]XAStatefulConnection. the constructor works on [newDriver]XADataSource object.

See Appendix A for the implementation of the Informix extension.

## Current extensions

The current extensions of the driver wrapper are the following :

- extension for Informix and Sybase database
- extension for Oracle Jdbc 2.0 driver
- xa extension for instantDB

### Informix

The driver wrapper is extended to add a method about transaction and query timeout.

Transaction and Query timeout are set with the same execute statement but we want to be able to set different values for query and transaction timeout.

- QueryTimeout - The amount of time (in seconds) that a query will block before throwing an exception. If <0 then the query will block indefinitly. Optional, if not specified, then the value defaults to 0.
- TransactionTimeout - The amount of time (in seconds) that a transaction will block before throwing an exception. If < 0 then the transaction will block indefinitly. Optional, if not specified, then the value defaults to 0.

This new method is integrated into the InformixConnectionHandle class, which extends StandardConnectionHandle class. The package is org.enhydra.jdbc.informix.

### Sybase

The driver wrapper is extended to modify the setAutoCommit method in the StandardConnectionHandle. The process is the same. We extend this class in SybaseConnectionHandle to overwrite the method :

        package org.enhydra.jdbc.sybase;

        import org.enhydra.jdbc.standard.*;
        import java.sql.*;
        import java.util.*;

        public class SybaseConnectionHandle extends StandardConnectionHandle {

            public SybaseConnectionHandle (SybasePooledConnection pooledCon, Hashtable preparedStatementCache) {
              super (pooledCon, preparedStatementCache);
            } // constructor

            synchronized public void setAutoCommit(boolean autoCommit) throws SQLException {
              preInvoke();
              try {
                con.commit();
                con.setAutoCommit(autoCommit);
              } catch (SQLException e) {
                catchInvoke(e);
              }
            } // method setAutoCommit
        } // class SybaseConnectionHandle

### Oracle (for Jonas)

The Oracle extension is made to set up some properties which can not be set by the *XAPoolDataSource and their factories. We have to define in this class, the getReference and getObjectInstance methods for JNDI lookup in the *XAPoolDataSource object. Here, we call the setURL method instead of the setUrl method defined in the Standard wrapper.

This extension only works with the Oracle 8.1.6 version and the classes12.zip file which contains the JDBC 2.0 driver from Oracle.

        package org.objectweb.jonas.dbm;

        import oracle.jdbc.xa.client.OracleXADataSource;
        import org.objectweb.jonas.dbm.CommonDataSource;
        import java.sql.*;
        import javax.sql.*;
        import javax.naming.*;
        import javax.naming.spi.*;
        import java.util.Hashtable;

        public class JonasOracleXADataSource extends OracleXADataSource implements Referenceable, ObjectFactory,CommonDataSource {

            public JonasOracleXADataSource () throws SQLException{
                super();
            } // constructor

            public void setProperties(String url, String classname, String user, String password) throws SQLException {
                setURL(url);
                setUser(user);
                setPassword(password);
            } // method setProperties

            public Reference getReference() throws NamingException {
                // Note that we use getClass().getName() to provide the factory
                // class name. It is assumed that this class, and all of its
                // descendants are their own factories.

                Reference ref = new Reference(getClass().getName(), getClass().getName(), null);
                try {
                    ref.add(new StringRefAddr("url", getURL()));

                } catch (SQLException ef) {
                    throw new NamingException ("Exception JonasOracleXADataSource:getReference");
                }
                ref.add(new StringRefAddr("user", getUser()));
                ref.add(new StringRefAddr("password", getPassword()));

                return ref;
            } // method getReference

            /**
             * Methods inherited from ObjectFactory
             */
            public Object getObjectInstance(Object refObj, Name name, Context nameCtx, Hashtable env) throws Exception {
                Reference ref = (Reference)refObj;
                this.setURL((String)ref.get("url").getContent());
                this.setUser((String)ref.get("user").getContent());
                this.setPassword((String)ref.get("password").getContent());

                return this;
            } // method getObjectInstance

            public void setTransactionManager(TransactionManager tm) {}

            public void setDebug(boolean debug) {}

            public void setLogWriter(PrintWriter logWriter) {}
        } // class JonasOracleXADataSource

The JonasOracleXADataSource class is used in Jonas (Oracle1.properties) with the datasource.factory section :

        datasource.factory     org.objectweb.jonas.dbm.JonasOracleXADataSource

### InstantDB

The InstantDB extension is needed to work with true XA driver for InstantDB database. 2 classes are extended. IdbXADataSource extends StandardXADataSource, and is going to work with IdbXAConnection object which is an extension of StandardXAConnection object. With these 2 classes, the wrapper becomes now a driver.

## Jonas[newDriver]XADataSource

### Common interface for XADataSource

This new object is used to implement an interface which defines a setProperties method to set up the following properties :

- url
- classname (i.e. oracle.jdbc.driver.OracleDriver)
- user (user name)
- password

        package org.objectweb.jonas.dbm;
        import java.sql.*;

        public interface CommonDataSource {

            public void setProperties(String url, String classname, String user, String password) throws SQLException;
            public void setTransactionManager(TransactionManager tm);
            public void setDebug(boolean debug);
            public void setLogWriter(PrintWriter logWriter);
            public void setMinCon(int min);
            public void setMaxCon(int max);
        } // interface CommonDataSource

The setTransactionManager method is used to set up the transaction manager into the datasource. The setDebug method is used to set up if or not, the debug information will be displayed on the stream. The setLogWriter method is used to set up the logwriter object. The setMinCon method is used to set up the minimum of physical connection, and the setMaxCon method is used to set up the maximum of physical connection.

### i.e. InstantDB

        package org.objectweb.jonas.dbm.instantdb;

        import org.objectweb.jonas.dbm.CommonDataSource;
        import org.enhydra.jdbc.instantdb.IdbXADataSource;
        import java.sql.*;

        public class JonasIdbXADataSource extends IdbXADataSource implements CommonDataSource {

            public void setProperties(String url, String classname, String user, String password) throws SQLException {
                setUrl(url);
                try {
                    setDriverName(classname);
                } catch (Exception e) {
                    log("Exception JonasIdbXADataSource:setProperties "+e);
                    throw new SQLException("Exception JonasIdbXADataSource:setProperties ");
                }
                setUser(user);
                setPassword(password);

            } // method setProperties
        } // class JonasIdbXADataSource

### Changes in Jonas

The [driver].properties file is used to specify the factory for the data source :

        datasource.factory     org.objectweb.jonas.dbm.Jonas[Driver]XADataSource

The previous class must implement the CommonDataSource interface. This interface is used in the DataBaseServiceImpl class to bind into JNDI the factory of the data source :

        try {
            CommonDataSource connect = (CommonDataSource)(Class.forName(xaFactory).newInstance());
            connect.setProperties(url,
                                  dsd.getProperty(CLASS_NAME),
                                  dsd.getProperty(USER_NAME),
                                  dsd.getProperty(PASSWORD));
            connect.setTransactionManager(TransactionServiceImpl.getInstance().getTransactionManager());
            connect.setMinCon((new Integer(dsd.getProperty(MINCON, DEFAULT_MINCON))).intValue());
            connect.setMaxCon((new Integer(dsd.getProperty(MAXCON, DEFAULT_MAXCON))).intValue());
            connect.setDebug(debug);

            ds.setLogWriter(Trace.getLogWriter());
            connect.setLogWriter(Trace.getLogWriter());
            ds.std = (XADataSource)connect;
            ictx.rebind(xaName, connect);
            xadsList.put(xaName, connect);
        } catch ...

## Appendix A : Informix implementation

### InformixConnectionHandle.java

        package org.enhydra.jdbc.informix;

        import org.enhydra.jdbc.standard.*;
        import java.sql.*;
        import java.util.*;

        public class InformixConnectionHandle extends StandardConnectionHandle {

            private int lockModeWait = 0;   // The amound time to wait on a query or transaction

            public InformixConnectionHandle (InformixPooledConnection pooledCon, Hashtable preparedStatementCache) {
                super (pooledCon, preparedStatementCache);
            } // constructor

            public synchronized void setLockModeToWait(int seconds) throws SQLException {
                if (lockModeWait != seconds) {
                    if (seconds >0) {
                        execute("SET LOCK MODE TO WAIT " + seconds);
                    } else if (seconds == 0) {
                        execute("SET LOCK MODE TO NOT WAIT ");
                    } else {
                        execute("SET LOCK MODE TO WAIT ");
                    }
                    lockModeWait = seconds;
                }
            } // method setLockModeToWait

            public synchronized void execute (String sql) throws SQLException {
                Statement stat = createStatement();
                stat.execute(sql);
            } // method execute

        } // class InformixConnectionHandle

### InformixConnectionPoolDataSource.java

        package org.enhydra.jdbc.informix;

        import org.enhydra.jdbc.standard.*;
        import java.sql.*;
        import javax.sql.*;

        public class InformixConnectionPoolDataSource extends StandardConnectionPoolDataSource {

            /**
             * Create a pooled connection using the default username and password.
             */
            public PooledConnection getPooledConnection () throws SQLException {
                log("InformixConnectionPoolDataSource:getPooledConnection(0) return a pooled connection");
                return getPooledConnection (user, password);
            } // method getPooledConnection

            /**
             * Create a informix pooled connection using the supplied username and password.
             */
            public PooledConnection getPooledConnection (String user, String password) throws SQLException {
                log("InformixConnectionPoolDataSource:getPooledConnection(2) return a pooled connection");
                return new InformixPooledConnection ((ConnectionPoolDataSource)this, user, password);
            } // method getPooledConnection

        } // class InformixConnectionPoolDataSource

### InformixPooledConnection.java

        package org.enhydra.jdbc.informix;

        import org.enhydra.jdbc.standard.*;
        import java.sql.*;
        import javax.sql.*;

        public class InformixPooledConnection extends StandardPooledConnection {

            public InformixPooledConnection (ConnectionPoolDataSource dataSource, String user, String password) throws SQLException {
                super((StandardConnectionPoolDataSource)dataSource, user, password);
            } // constructor

            protected void newConnectionHandle() {
                connectionHandle = new InformixConnectionHandle (this, preparedStatements);
            } // method newConnectionHandle

        } // class InformixPooledConnection

### InformixXAConnection.java

        package org.enhydra.jdbc.informix;

        import org.enhydra.jdbc.standard.*;
        import java.sql.*;

        public class InformixXAConnection extends StandardXAConnection {

            /**
             * Creates the first free connection.
             */
            public InformixXAConnection (InformixXADataSource dataSource, String user, String password) throws SQLException {
                super (dataSource, user, password); // creates the first Connection object

                // Save the constructor parameters.
                this.dataSource = dataSource;
                curCon = new InformixXAStatefulConnection (dataSource, con);// wrap connection as a stateful connection

                // NOTE - the current connection is not made known to the data source
                // so it is not eligible for re-use. It only goes on the data source list
                // if it ever becomes associated with a global transaction.
                timerThread = new Thread (this);    // create the backgroup thread to check for timeouts

                timerThread.start();                        // start the timer thread
                timerThread.suspend();                      // and suspend until some timeouts get set up
                dataSource.log("InformixXAConnection created");
            } // constructor

        } // class InformixXAConnection

### InformixXADataSource.java

        package org.enhydra.jdbc.informix;

        import org.enhydra.jdbc.standard.*;
        import java.sql.*;
        import javax.sql.*;

        public class InformixXADataSource extends StandardXADataSource {

            /**
             * Creates an XA connection using the default username and password.
             */
            public XAConnection getXAConnection () throws SQLException {
                log("InformixXADataSource:getXAConnection(0) XA connection returned");
                return getXAConnection (user, password);
            } // method getPooledConnection

            /**
             * Creates an XA connection using the supplied username and password.
             */
            public XAConnection getXAConnection (String user, String password) throws SQLException {
                InformixXAConnection xac = new InformixXAConnection (this, user, password);
                connectionCount++;
                log("InformixXADataSource:getXAConnection(2) XA connection returned");
                return xac;
            } // method getPooledConnection

            /**
             * Create a pooled connection using the default username and password.
             */
            public PooledConnection getPooledConnection () throws SQLException {
                log("InformixConnectionPoolDataSource:getPooledConnection(0) return a pooled connection");
                return getPooledConnection (user, password);
            } // method getPooledConnection

            /**
             * Create a informix pooled connection using the supplied username and password.
             */
            public PooledConnection getPooledConnection (String user, String password) throws SQLException {
                log("InformixConnectionPoolDataSource:getPooledConnection(2) return a pooled connection");
                return new InformixPooledConnection (this, user, password);
            } // method getPooledConnection

        } // class InformixXADataSource

### InformixXAStatefulConnection.java

        package org.enhydra.jdbc.informix;

        import org.enhydra.jdbc.standard.*;
        import java.sql.*;
        import javax.transaction.Status;

        public class InformixXAStatefulConnection extends StandardXAStatefulConnection {

            /**
             * Creates a new stateful connection in the FREE state (NO_TRANSACTION)
             */
            InformixXAStatefulConnection (InformixXADataSource dataSource, Connection con) {
                super((StandardXADataSource)dataSource, con);
            } // method InformixXAStatefulConnection

        } // class InformixXAStatefulConnection

### JonasInformixXADataSource.java

        package org.objectweb.jonas.dbm;

        import org.objectweb.jonas.dbm.CommonDataSource;
        import org.enhydra.jdbc.standard.StandardXADataSource;
        import java.sql.*;
        import javax.sql.*;
        import javax.naming.*;
        import javax.naming.spi.*;
        import java.util.Hashtable;

        public class JonasInformixXADataSource extends StandardXADataSource implements CommonDataSource {

            public StandardInformixXADataSource () throws SQLException {
                super();
            } // constructor

            public void setProperties(String url, String classname, String user, String password) throws SQLException {
                setUrl(url);
                setUser(user);
                setPassword(password);
                try {
                    setDriverName(classname);
                } catch (SQLException e) {
                    log("Exception JonasInformixXADataSource:setProperties "+e);
                    throw new SQLException("Exception JonasInformixXADataSource:setProperties ");
                }
            } // method setProperties

        } // class JonasInformixXADataSource