How to build the JNI-based C interface to Oracle NoSQL Database from source.

The Oracle NoSQL Database C API is a JNI-based library used to build client applications capable of interacting with Oracle NoSQL Database. This C API is released in source form. This page describes how to build the library so that it can be used with your application code.

Prerequisites for build

  1. A Java JDK (Java 7 or later). You should make sure that the JAVA_HOME environment variable is properly set (for CMake)

  2. A C compiler.

  3. Installation of CMake. There are precompiled, ready-to-install binaries for various Linux distributions that can be installed directly. Download links are here, including dmg images for Mac OS X:

    http://www.cmake.org/cmake/resources/software.html

  4. Built and installed Avro C library (also requires CMake).

    1. Download source from http://avro.apache.org/releases.html (currently using 1.7.6). Following the download link you can download either the entire source (avro-src-1.7.6.tar.gz) or just the C api source (avro-c-1.7.6.tar.gz). This affects the paths below.

    2. Untar the package. If you downloaded the full source then cd lang/c. If you downloaded the C source just start at the top level directory. Read the INSTALL file, which discusses building from source. Before building, also edit CMakeLists.txt and uncomment the line that reads:

      add_definitions(-DAVRO_ALLOW_MISSING_FIELDS_IN_RESOLVED_WRITER)

    3. Build and install the library. If you use a non-standard prefix in -DCMAKE_INSTALL_PREFIX remember that for later. This definition tells CMake where to install the built library and header files. If you use a standard location such as /usr/local or /usr, the default build will find it.

Prerequisites to run and test

  1. Path to NoSQL Database (kvstore.jar for testing, kvclient.jar for general client applications).

  2. Java runtime (Java 6 or later).

  3. Avro C library installation.

To build and run

  1. Create a build directory. All build and CMake artifacts reside here for easy cleanup. The build directory can be anywhere as long as the cmake command is:

    cmake <path-to-top-level-directory>.

    This keeps the source tree clean of build artifacts.

    $mkdir build
    cd build

  2. Generate makefiles (from build). Make sure that JAVA_HOME is properly set to allow CMake to find the JNI library.

    $ cmake <options>

    These options are available on the cmake command line:

    This command will fail if it is unable to locate either the JNI runtime or Avro. It can be invoked using --trace (see cmake usage) to get additional information about failures.

  3. Once cmake has succeeded, build.

    $ make

    If there is a problem, some additional information, such as the specific compiler flags being used can be had by doing this:

    $ VERBOSE=1 make

    The build artifacts end up in build/build/{bin,lib,include} The libraries required to run the interface are lib/libkvstore* and libavro* (wherever that was built). The test suite and example binaries are built into bin but are not required for deployment.

  4. Tests can be run either by directly invoking kvsuite or if KV_PATH_TO_JAR was specified for cmake via make.

    $ make test

    Output of make test ends up in the file Testing/Temporary/LastTest.log. It can be examined for detailed success and failure information.

    Direct test invocation has this usage:

    $ build/bin/kvsuite -r <path-to-kvroot> -k <path-to-kvstore.jar> -v

    Note the following:

  5. The "hello" example is built by default. In order to run there must be a store available. KVLite will do on the local machine. For example if you have the Oracle NoSQL Database on your machine you can do this:

    1. java -jar <path-to-kvstore.jar> kvlite

      The default port is 5000 on host localhost, with store name kvstore.

    2. Run the program:

      build/bin/hello -store kvstore -port 5000 -host localhost \
      -jar <path-to-kvclient.jar>

      The program can be directed to other stores as well. The hello program stores a simple record, reads it, and outputs the value, which is "Big Data World!" It is a good example of a simple program to perform initialize, open, put, get, and close on a store from the C interface.

  6. Once the build is verified it can be installed to the location specified in the cmake arguments.

    $ make install

    If additional permissions are required for the installation directory you may need to use

    $ sudo make install

Building and running application code

Compilation requires kvstore.h and must link with libkvstore. For example, if you used -DCMAKE_INSTALL_PREFIX=$HOME/install, on linux you could compile a single file program this way

$ gcc -o myprog myprog.c -I $HOME/install/include -lkvstore -L$HOME/install/lib

Running can be more complicated because libkvstore depends on libavro as well as the the JNI libraries. On Linux LD_LIBRARY_PATH must include paths to all 3.

$ export LD_LIBRARY_PATH=$HOME/install/lib:$JAVA_HOME/
$ ./myprog ...

On MacOS use DYLD_LIBRARY_PATH instead of LD_LIBRARY_PATH and the path to the Java library in $JAVA_HOME is not required.

Application build alternative

The CMake framework can be leveraged for your own applications. The simplest way to do this is to add your code to the template provided in examples/CMakeLists.txt file. That file has instructions in its comments. The advantage to this approach is that it will hard-wire the paths to the dependent libraries into the executable (using -rpath in the link step).

Initializing the Java VM in C code

Any C program using this interface must have access to the kvclient.jar file and initialize the Java VM. The libraries required for the JVM will have been linked with libkvstore during the build.

There are two ways code that uses the C API can initialize the JVM.

  1. Supply the classpath to the kvclient.jar and use the simple API:

    kv_error_t kv_create_jni_impl(kv_impl_t **impl, const char *classpath);

    This interface initializes the JVM internally and is what the hello.c example does. It is simple but does not allow the caller to pass additional arguments to the JVM such as min/max heap size or other potentially useful parameters.

  2. Initialize the JVM externally and pass it into the API. This is a little more work but gives the application more flexibility in how the JVM is initialized. This interface is used to pass the JVM object:

    kv_error_t kv_create_jni_impl_from_jvm(kv_impl_t **impl, void *jvm);

    The void *jvm must be a JavaVM * that was created by a call to the JNI function JNI_CreateJavaVM(). The classpath used to create the JavaVM object must include kvclient.jar.