The JVM Fanboy Blog 

Oracle Nashorn + Oracle NoSQL: First steps

by vincent


Posted on Tuesday Oct 20, 2015 at 11:12PM in NoSQL Database


Learning to work with Oracle NoSQL was a high entry on my to-do list. I always figured Oracle Nashorn (the new JavaScript engine that runs on JVM and is shipped for the first time with Java 8) is an ideal language to learn and try JVM-based APIs, mainly because of the interactivity of the JavaScript language. So, I thought this would be a cool opportunity to test the Oracle NoSQL API with Nashorn.

I was absolutely not disappointed. Nashorn is, in my opinion, very suitable for this.

About Oracle Nashorn

First of all, I am a huge Nashorn fan. Although not as fast as Node.js (which is powered by Google's mighty V8 engine), bringing the flexibility of a modern JavaScript implementation to the JVM world was a very good idea. I love that it is part of Java 8 SE, so every Java developer that uses Oracle-based JREs or JDKs has full access to it.

The Nashorn team added extensions to the JavaScript language so that JavaScript code can make full use of existing JVM classes. Also, Nashorn is compatible with the existing JSR-223 "Scripting for Java Platform" standard, which means that the Nashorn engine can be embedded into any JVM program. Using this technique, you can embed JavaScript code in your Java 8 (and up) projects, that communicate with your objects while Nashorn executes the JavaScript code dynamically. Mind blowing IMO... :-)

Nashorn is not the first JavaScript implementation witten for the JVM. Mozilla had Rhino, which was originally written by Netscape in the late '90s. Sun and Oracle provided a slightly modified version of Rhino with their Java 6/7 SE platforms. While the project is - at the time of this writing - still alive, it is no longer distributed with Oracle's JDK/JRE releases since Nashorn is its successor. See the Wikipedia article for more Rhino background information.

One area where Nashorn greatly improved on Rhino is execution speed of JavaScript code, this is because Nashorn makes use of the newest features that were recently introduced in the JVM itself (InvokeDynamic calls especially).

About Oracle NoSQL

When so-called "No SQL" databases became popular (nowadays, it stands more for "Not Only SQL"), Oracle released Oracle NoSQL. Both an open-source version and an enterprise version are available. Note that the license of the open-source version seems to be somewhat restricted, so double-check if you can live with the rules before committing to a project using Oracle NoSQL. The Enterprise Edition mainly adds features to integrate Oracle NoSQL with other Oracle products and some security enhancements.

Oracle NoSQL is written in Java, so also very easy to use on the Java platform. Note that drivers for Node.JS, Python (both experimental at this time) and C are also available from the Downloads section of the Oracle's NoSQL product page.

Oracle NoSQL is very much developed with "big data" utilization in mind. It is meant to be used in a high-performance distributed environment (many Oracle NoSQL nodes linked to each other) and contains several fault tolerance features. It is really meant for the high requirements in large enterprise environments.

It offers storing of classic Key/Value pairs. Unlike many other NoSQL databases, Oracle NoSQL requires the use of a pre-defined schema (similar to database tables in traditional relational databases). These schema's can use features that traditional DBMS usually do not offer as easily, for example:

  • A complete map (key/value pairs, where key is always String and the value can be of a pre-defined type) can be stored into into a single field
  • A field can be divided into fixed subfields (for example a "name" field that contains both a "first_name" and "last_name" field).

Also, another nice feature is that it can convert fields to valid (even formatted) JSON automatically.

Oracle released version 3 recently released. This new version offers a new Table API to write and read data, the documentation states that the previous APIs will probably be deprecated in a future release.

Installing Oracle NoSQL

Oracle NoSQL is complex software. In real-world usage you would usually have multiple Oracle NoSQL nodes on different servers that are linked to each other. Configuring a real world scenario is out of scope of this blog post (although I plan to write more posts about Oracle NoSQL when I have more experience with it).

Luckily, Oracle cleverly added a "KVLite" runtime option that sets up a single, fully working, but completely non-authenticated Oracle NoSQL node that is ready for action with one command. This option was provided so that developers could get up to speed with Oracle NoSQL ASAP. Oracle stresses several times in their documentation that this option must not be used to measure Oracle NoSQL's runtime performance and should not to be used in a production environment.

The only officially supported operating systems are Linux and Solaris. I used Windows at the time of this writing, KVLite seemed to work fine with it in my limited tests. But as things stand, I'd never ever recommend using Windows in a production scenario.

Now let's download and install Oracle NoSQL. It's very easy.

  • Ensure that you have successfully installed Oracle Java 1.8 JDK on your system by typing:
    "java -version"
    in a terminal screen (Command Prompt on Windows). Oracle NoSQL works with Java 7 and up, but we'll be using Nashorn that is only delivered with Oracle's Java 8.
  • From the official site, head over to the Downloads section and download the most recent open-source Community Edition version (3.4.7 at the time of this writing).
  • Extract the zip or tar file in a directory of your choice. This directory will be used to store your database as well

Running an Oracle NoSQL instance using KVLite

  • Open a new terminal screen (Command Prompt on Windows) and navigate to the directory you just created using the "cd" command
  • To run KVLite with the default settings (listen on localhost using port 5000 and store database in current directory with the hardcoded name "kvstore", type:
    "java -Xmx256m -Xms256m -jar ./lib/kvstore.jar kvlite"
  • You should see a screen like the following. Note that the message may differ if you already ran the command previously and a database's available.

  • If you require other options, you should refer to the documentation for the available command-line options. I did not have a need to try them myself.
  • You can stop KVLite simply by pressing CTRL+C on your terminal screen. For now keep it running.

Starting Nashorn and let it point to the JVM Oracle SQL driver classes

Before Nashorn can run a JavaScript script file that references the Oracle NoSQL JVM driver, you should set a command-line option when starting Nashorn that points Nashorn to the JAR file that contains the Oracle NoSQL driver. This is necessary because the driver is not supplied with the Oracle Java JDK.

This driver is stored in a file called "kvclient.jar" that is put in the "lib" subdirectory of the Oracle NoSQL main directory.

Before doing a full script, let's do a simple test in the interactive Nashorn console, to see if Nashorn can open the driver successfully:

  • Open a new console screen (I'll not repeat myself that this is called Command Prompt on Windows ;) ), keep the console that runs KVLite alone.
  • Type "jjs -cp PATH-TO-ORACLE-NOSQL/lib/kvclient.jar"
  • (obviously replace PATH-TO-ORACLE-NOSQL with the full path to your Oracle NoSQL main directory)
  • If all goes well, you will see a jjs> prompt. If Nashorn can not find the file, it will complain and you should try again.
  • At this prompt, enter the following command "var KVStore = Java.type("oracle.kv.KVStore");". If all goes well, Nashorn can load the KVStore class from the kvclient.jar file and will simply acknowledge this by displaying a new jjs> prompt.
  • Now type "exit()" and press Enter to exit the interactive Nashorn console.
  • Here's a screenshot of my session

Now let's explore the Table API with a Nashorn script

While the interactive console is a very cool Nashorn feature, and one that is very handy when learnig a new API, Nashorn can also run files that contains JavaScript code.

I usually use NetBeans IDE to develop my programs and scripts. Unfortunately, while NetBeans has a very cool feature where it can run JavaScript scripts that are stored in existing web development projects via Nashorn, NetBeans does - AFAIK - not have a dedicated standalone Nashorn project type yet. I haven't find a nice way to write and run standalone Nashorn scripts in NetBeans at this time. Therefore, for now, I write my Nashorn scripts in a simple text editor. I hope I will find a way to do this in NetBeans in the future, as I hate writing scripts in any other programs than NetBeans... I love NB too much :-)

I opened my editor with two windows. On the left window I opened various Java examples that Oracle provides in the examples directory. On the right Window I wrote Nashorn code that used the techniques demonstrated in the examples. Regularly I ran the script in Nashorn to see if it worked and to fix errors.

I came up with this simple script that demonstrate some Table API techniques: creating a table with the DDL statements (that is very similar to DDL in SQL databases), dropping it prior to that if it already exists (for testing purposes), and then add two records (notable of the used schema is that a complete map stored in a single field), then reads back those records.

While not an extensive test suite by any means, I think it's a nice, small introduction to using Oracle NoSQL's Table API in a Nashorn script

Here's the script:

// Change this to your Oracle NoSQL configuration

var config = {
    'store': 'kvstore',
    'host': 'localhost',
    'port': '5000'
}

// Java type imports

var KVStore = Java.type("oracle.kv.KVStore");
var KVStoreConfig = Java.type("oracle.kv.KVStoreConfig");
var KVStoreFactory = Java.type("oracle.kv.KVStoreFactory");


// Used Queries

var DDL_CREATE = "\
    CREATE TABLE IF NOT EXISTS movies ( \
        movieID INTEGER, \
        name STRING, \
        properties MAP(STRING), \
        PRIMARY KEY (movieID) \
    )";

var DDL_DROP = "DROP TABLE IF EXISTS movies";


// Some helper functions

function getStore(store, host, port) {
    // Connect to existing store and return it
    var storeConfig = new KVStoreConfig(store, host + ":" + port);
    return KVStoreFactory.getStore(storeConfig);    
}


function createMovieRow(table, movie) {
    // Create a new row with the content of the specified movie JSON object
    row = table.createRow();
    row.put("movieID", movie["id"]);
    row.put("name", movie["name"]);
    
    var movieProperties = movie["properties"];    

    var mapProperties = row.putMap("properties");
    for (var property in movieProperties) {
        mapProperties.put(property, String(movieProperties[property]));
    }
    return row
}


function readMovieRow(row) {
    // Read row that contains movie and return JSON movie object
    var propertiesString = row.get("properties").asMap().toJsonString(false);
    
    return {
        "id": row.get("movieID"), 
        "name": row.get("name"), 
        "properties": JSON.parse(propertiesString)
    }    
}



// Main script logic

function main() {
    var movie1 = {"id": 1, "name": "Birdman", "properties": {"media": "bluray", "rating": "very good"}};
    var movie2 = {"id": 2, "name": "Mega Shark vs Mecha Shark", "properties": {"media": "dvd", "rating": "unbelievable"}};
    var movies = [movie1, movie2];

    //==========================================================================
    // Connect to NoSQL database    
    //==========================================================================
    
    print('Connecting...');
    store = getStore(config["store"], config["host"], config["port"]);
    
    print('Dropping table (when it exists)...');
    store.executeSync(DDL_DROP);
        
    print('Creating table...');
    store.executeSync(DDL_CREATE);
    
    //==========================================================================
    // Adding movies to NoSQL database    
    //==========================================================================
    
    print("\nRetrieving movies table...");
    var tableAPI = store.getTableAPI();
    var table = tableAPI.getTable("movies");
    
    for (var i = 0; i < movies.length; i++) {
        print("\nCreating movie '" + movies[i]["name"] + "' row...");
        var rowMovie = createMovieRow(table, movies[i]);
        
        print("Storing movie: '" + rowMovie + "'...");
        tableAPI.put(rowMovie, null, null);
    }
    
    //==========================================================================
    // Reading movies from NoSQL database    
    //==========================================================================
    
    print();
    movieIds = [1, 2]
    for (var i = 0; i < movieIds.length; i++) {
        var movieId = movieIds[i];
        print("Generating key for id=" + movieId  + "...");
        var key = table.createPrimaryKey();
        key.put("movieID", movieId);
        
        print("Obtaining movie with primary key: " + key);
        var row = tableAPI.get(key, null);
        
        var movie = readMovieRow(row);
        print("\nMovie id=" + movie["id"]);
        print("Movie name=" + movie["name"]);
        for (var p in movie["properties"]) {
            print("Property '" + p + "': '" + movie["properties"][p] + "'");
        }
        print();
    }
    
    print("Closing store...");
    store.close()
}
 
main();

Some notes about the code:

  • In this script I could rely on standard JavaScript conversion functions, like String() to convert am integer to a String. While JavaScript is not a typed language, when using existing Java classes in Nashorn, the JVM still has to find a (perhaps overloaded version of the) method that matches with the specified values, otherwise an error occurs. Since Nashorn internally uses standard Java classes like java.lang.Integer and java.lang.Double to store variables, you can use standard methods, like intValue() or toString() in your JavaScript code. I just wouldn't advise it when a JavaScript equivalent exists.
  • The Table API generally seems easy to use, but I found it a bit disappointing that it seems it can not work with common existing Java classes, like HashMaps to store maps in a Map field. As far as I can tell, you really need to use the oracle.kv.table.MapValue class and manually convert your HashMaps. I am very new to the Table API, so take this with a grain of salt :-)
  • I wouldn't be surprised if the Table API offers much more JSON compatability than I have demonstrated in this script. I'll research this and update this post accordingly.
  • One of the differences with writing Nashorn code opposed to Java code is that you need to write a lot less import oracle.kv.XXX statements, because you do not need to specify your types when declaring a variable
  • Deleting a schema from KVLite seems to take quite a lot of time
  • Of course read the documentation and refer to the JavaDocs for much more detailed information about the Table API.

Store the script in a "test_oracle_nosql.js" file and add the filename to the jjs -cp PATH/lib/kvclient.jar test_oracle_nosql.js command you used previously, to run it. Here's my output:

That's it for now! Thanks for reading! You can now press CTRL+C in the window running your KVLite instance :)



No one has commented yet.
Comments are closed for this entry.