1212-1214

Previous Table of Contents Next

Page 1212

automates the testing of interfaces and methods based on a configuration file, and logs output. The individual test suites are implemented as separate packages and classes designed to test each interface and method individually.

In general, you should fully implement all the interfaces defined in the java.sql package. If JDBC compliance is a high priority, the JDBC test suite is highly recommended.

Many commercial drivers ( especially pure Java drivers) are not JDBC compliant, strictly speaking. Some methods might be unsupported due to RDBMS limitations; additional methods might be needed to handle communication with a server component. By definition, RDBMS-specific methods and optimizations are not JDBC compliant. Conforming implementations must also support the JDBC SQL escape syntax for outer joins, stored procedure calls, date formats, and other highly RDBMS-specific features. This requires additional parsing and transformations within the driver to convert escape syntax to the native syntax of the RDBMS. If a driver is developed for in-house use, strict conformance is probably not necessary.

Using JNI

The Java Native Interface (JNI) provides two-way interprocess communication between the Java VM and native code libraries and applications. In the context of JDBC driver development, it is used to allow the Java portion of the driver to communicate with the native portion of the driver. In this section, it is assumed that the native portion of the driver is implemented in C/C++ for purposes of discussion.

The design of exported functions in the native library should be expressed in terms of Java datatypes, and the implementation of the exported functions should be the last step in developing the native library. The C declarations for the exported functions can be generated from their Java definitions.

Before you can access a native library from Java, you must load it. This is typically handled by the Java implementation of the driver interface in a static method:

 public class MyOracleDriver implements java.sql.Driver {     static {         System.loadLibrary("mylib");     }     .     .     . 

You must declare native methods in the Java classes that access them. These declarations are used to generate the C stubs for the native library, so you must consider the design of the native library. For example, the implementation of the ResultSet interface might contain a declaration such as

 protected native int fetch(int ResultSetID, Vector ColVals); 

In this example, the native portion of the driver accesses a specific instance of a C++ result set object based on an assigned identifier and populates a java.util.Vector with the results. The

Page 1213

specific ResultSet Java class might wrap this call as its implementation of next(), storing the resulting Vector as an instance variable so that the get() methods can be implemented in pure Java.

You can generate C declarations for the exported native methods using javah.exe, which is included in the JDK. For example, C declarations for exported functions to be accessed by the ResultSet implementation might be generated from the command line as follows :

 javah -jni mydriverpackage.myresultset 

This produces a C header file in the current directory named mydriverpackage_myresultset.h. Be sure to use the _jni option to generate JNI-style headers (older versions of the JDK used a different native interface). The C declaration for the fetch() native function described earlier is generated as follows:

 JNIEXPORT jint JNICALL Java_mydriverpackage_myresultset_fetch   (JNIEnv *, jobject, jint, jobject); 

JNI allows Java objects to be constructed and manipulated within C code. The native implementation for this method in C might look like

 #include <jni.h> #include "mydriverpackage_myresultset.h" JNIEXPORT jint JNICALL Java_mydriverpackage_myresultset_fetch( JNIEnv     *JNIenv, jobject      Jthis, jint    ID, jobject    VectOut) { jint                ret = 0;           jint                i = 0;           MyCppResultSet             *RS = (MyCppResultSet *)0; const char  *SetMethodName = "addElement";           const char  *SetMethodSig = "(Ljava/lang/Object;)V";           jclass        VectorClass = JNIenv->GetObjectClass(VectOut);           jmethodID SetMethod = JNIenv->GetMethodID                         (VectorClass, SetMethodName, SetMethodSig);     // locate the specific C++ object instance RS = myInstanceManager->getResultSetRef(ID);           if (RS != (AISResultSet *)0) {                  if (RS->NumBindVars == 0)                      ret = RS->BindAll();                  else                      ;                  if (ret == 0)             ret = RS->fetch();         else                      ;                  if (ret == 0) {             for (i = 0; i < RS->NumBindVars; i++) {                            if (!RS->BindVars[i]->NullInd) { JNIenv->CallObjectMethod(VectOut, SetMethod, JNIenv->NewStringUTF((char *)RS->BindVars[i]->Buffer));                 }                            else { 

Page 1214

 JNIenv->CallObjectMethod(VectOut, SetMethod, JNIenv->NewStringUTF((char *)""));                 }             }         }                  else                      ;           }           else         ret = -1; // invalid object ID return(ret); } 

A full discussion of JNI is beyond the scope of this chapter, so this sample implementation was contrived to demonstrate some of the key features of JNI. The header jni.h includes other JNI and platform-specific headers that define JNI datatypes, macros, and functions. For example, JNIEnv is used to represent a Java virtual machine, jobject represents a Java object reference, jint represents a Java integer, and so on.

Note that the native method is passed a reference to the Java virtual machine, and a reference to the invoking Java object is passed as the first two parameters. Additional parameters are method specific. In the native library, all JNI methods are invoked through a reference to the Java VM.

One set of JNI functions is dedicated to string operations. NewStringUTF(), for example, is used to construct a java.lang.String from a const char * in C. Another set of methods is used to determine the class of a Java object reference, locate method IDs, invoke methods on Java objects, and so on. In the preceding example, notice how a few of these routines are used to add elements to the java.util.Vector reference passed as the VectOut parameter. For the sake of simplicity, the example treats all column values as strings.

This simple example barely scratches the surface of JNI. It is provided to demonstrate the importance of understanding JNI before designing the interface between the Java portions of the driver and the native portions of the driver. Remember that only Java objects can be passed by reference and that there is some overhead in constructing and accessing Java objects from native code. You should understand a subset of the JNI prior to design, including JNI datatypes, function signatures, string methods, and basic object manipulation functions. As with other Java- related topics discussed in this chapter, you can refer to the JavaSoft JDK documentation for additional details on JNI.

The Future of JDBC

JDBC has already gained acceptance throughout the industry as the standard low-level API for connecting Java to databases. As JDBC continues to grow, additional tools, preprocessors, and higher-level APIs based on JDBC are being developed.

Oracle's J/SQL, for example, allows applications to embed SQL, DML, transaction control, stored procedure calls, and DDL directly in Java source code. The J/SQL preprocessor converts the

Previous Table of Contents Next


Oracle Unleashed
Oracle Development Unleashed (3rd Edition)
ISBN: 0672315750
EAN: 2147483647
Year: 1997
Pages: 391

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net