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.
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.