WL#2437: Create Java/C++ Adaptor for Federated Storage Handler

Affects: Server-7.1   —   Status: On-Hold

Create a JNI (Java Native Interface) Adaptor for the Federated storage handler.
Adaptor should allow the C++ code to make use of generic JDBC connections to
other types of database.
A java class (FederatedConnection) will create a JDBC connection to the remote
database. This FederatedConnection object will be construsted with a jdbc driver
class name, url, user name, and password. This object will expose methods to
allow for inserts, updates, as well as selects and iterating over the results of
selects.

A C++ class (Federated_connection) will wrap all of the JNI (Java Native
Interface) calls to the java object. 



Here are the three layers:

1) Business Logic
 - creates and uses Federated_connection(s)

2) Federated_connection
- encapsulates knowlege about a specific java class (FederatedConnection)
- holds a reference to an instance of a FederatedConnection
- uses the "Java_context" to do the JNI "hard work"

3) Java_context
- encapsulates all the knowledge of how to do the ugly JNI work, without knowing
about any particular java class.
There is a C++ class called "Federated_connection" with functions like
"execute_query", and "get_result_for_column".

It also supports prepared statements, but the simplest usage looks a little like
this:


// ------
Federated_connection con(jdbc_driver_name, url, username, password);

con.execute_query("SELECT version()");
while (con.next_result())
    printf("%s", con.get_result_bytes_for_column(1)->toString());

con.close_results();
// ------


The idea behind "Federated_connection" is to provide a nice clean set of
functions to easilly work with.


Federated_connection is really sort of a "C++ facade" to a java object. In fact
the Federated_connection has a reference to a "jobject" (java object) of type
"FederatedConnection.class" .... When we call "execute_query" on a
Federated_connection, the Federated_connection will evoke "executeQuery(String)"
on the java object.

void Federated_connection::execute_query(string sql)
{
	_java->call_void_method(_jcon, "executeQuery", sql);
}

In the above code snip, "_jcon" is the java object of type "FederatedConnection"
"_java" is a pointer to an object which encapsulates all the ugly JNI knowledge
and crufty JNI code. This keeps all that code localized in one place, prevents
duplication, and eliminates the need for all that ugly JNI cluttering up the
screen every time we construct a query.


The "_java" is a "Java_context" ... which is the best name I could think of for
a wrapper of a JNIEnv pointer. Now let's look at what
"_java->call_void_method(_jcon, "executeQuery", sql)" does:

void Java_context::call_void_method(jobject jobj, string method, string arg0)
{
  string sig= "(Ljava/lang/String;)V";

  jvalue args[1];
  args[0].l= to_jstring(arg0);

  call_void_method_a(jobj, method, sig, args);
}

Here we see the VERY ugly JNI starting to emerge. First we're creating the ugly
jni method signature for a void method which takes a single parameter of type
String. Next, we are creating the argument array. After that we call
"to_jstring" on the "select version()" string (which will do the UTF8 to
"java.lang.String" conversion) and then assign that to the object pointer of the
jvalue at element zero. Wheh! Now we are ready for the next bit of UGLY JNI stuff.

void Java_context::call_void_method_a(jobject obj,
                               string methodName,
                               string signature,
                               jvalue* args)
{
  jclass cls= env->GetObjectClass(obj);
  jmethodID methodId= get_method_id(cls, methodName, signature);
  env->CallVoidMethodA(obj, methodId, args);
}

in the above snip of code, "env" is a pointer of type "JNIEnv" as provided by
the JNI interface. Since you've done JNI before, this part will look pretty
familiar to you: first you need to have the class, then you can get the methodID
from the class, then you need to use the JNIEnv to envoke the method on java
object with the arguments. (And, if this were not a void method, we'd want to do
something with the result of the function call.)

So ... let's recap.
In our code, all we did was create a Federated_connection and call execute_query
and pass in a C string "select version()". Now we're calling:

//psudo code:
env  CallVoidMethodA( javaFederatedConnection, executeQuery, ["select version()"])

which will be just like we had a java program which did:
federatedConnection.executeQuery("select version()");


One more thing: lifecycle and instantiating the JVM. There is a class called
"JVMSingleton" which will instantiate the JVM the first time get() is called.
When we create a Federated_connection, it gets the JVM from JVMSingleton and
does the magic "JNI AttachCurrentThread" in order to create the JNIEnv* ...
which we wrap as a "Java_context". When Federated_connection is destroyed, it
does the magic "JNI detatch" ... but since the JVM may be shared from many
places, it does not destroy the JVM. That must be done separately via
JVMSingleton when we are sure that no one needs a JVM any longer and we wish to
free the memory.