Java and Postgres95

First in a series of articles detailing the creation of a Java interface to Postgres95.
Using Native Methods in Java

Java methods (class functions) that have been declared “native” allow programmers to access code in a shared library. Theoretically, this code can be written in any language that will “link with C” (but in general, you'll probably want to stick to C, or perhaps C++).

When a Java class is loaded, it can explicitly tell the Java system to load any shared library (.sos in Linux) into the system. Java uses the environment variable LD_LIBRARY_PATH (and ldconfig) to search for the library, and will then use that library to resolve any methods that have been declared “native”.

The general procedure for writing native code is as follows:

  • Write the .java file, declaring all native methods as “native” (The .java file must compile cleanly at this point, so insert dummy methods if you need to)

  • Add the loadLibrary() command to your .java files to tell Java to load the shared library

  • Compile the class:

            javac [-g] classname.java
    
  • Generate the headers and stubs:

            javah classname (no extension)
    
            javah -stubs classname
    
  • Use the declarations in the classname.h file to write your C code (I use the file classnameNative.c, as it seems popular, and the stubs file uses classname.c)

  • Compile the .c files using the -fPIC (position independent) flag:

    gcc -c -fPIC -I/usr/local/java/include
    filename.c
    
  • Generate the shared lib (these flags are for gcc 2.7.0):

    gcc -shared -Wl,-soname,libFOO.so.1 -o
    libFOO.so.1.0 *.o -lotherlib
    
  • Put the .so file somewhere in your LD_LIBRARY_PATH (or add it to /etc/ld.so.conf).

An Example: The PGConnection Class

The PGConnection class is a wrapper for libpq's PGconn. A PGconn represents a connection to the backend Postgres95 process, and all operations on the database go through that connection. Each PGConnection will create a PGconn and keep a pointer to it for future use.

Let's walk through the steps above:

First, we write our PGConnection.java file (Listing 1). Remember that it must compile cleanly in order to generate our header and stubs, so if you refer to any Java methods that you haven't written, create dummy methods for them. We will need a constructor, a finalizer, and all of the operations that libpq allows on a PGconn. We declare most of these operations as native methods (see Listing 1exec() and getline() are special cases that we'll consider later).

Listing 1. PGConnection.java

The PGConnection Constructor

To get a PGconn, libpq provides the function:

PGConn *setDB(char *host, char *port, char *options, char *tty,
char *dbName)

Since this in effect “constructs” the connection to the database, we'll use this as a model for our constructor (See Listing 1, line 18). The constructor simply calls connectDB() (Listing 1, line 21; a native method that calls setdb()—we'll define it in a moment), and throws an exception if the connection is not made. Doing the error checking in the constructor guarantees that no connection will be returned if the call to setdb () fails.

Now let's look at our first native method, connectDB(). We declare it as native at line 70 in Listing 1. Note that no Java code is provided.

There are several important things to notice about this declaration. The “private” keyword makes this method accessible only from the PGConnection class itself (we want only our constructor calling it). The “native” keyword tells Java that code from a shared library should be loaded for this method at runtime. Since libpq is not “thread-save”, we want to make it impossible for two threads to be making calls to libpq at the same time. Making all of our native methods “synchronized” goes a long way towards this goal (we will return to this when we tackle the asynchronous notification system). Finally (Listing 1, lines 70-73), the declaration states that connectDB() takes five Java strings as arguments and doesn't return anything.

Figure 1. How Types Convert to and from Java and C

The remainder of the native calls follow this same pattern, with the exception of exec() and getline(). Again, we'll put these off a little longer.

Before we continue, let's add the loadLibrary call. We place it at the end of the class, in a block marked “static” (Listing 1, line 92) with no method name. Any blocks such as this are executed when the class is loaded (exactly once) and libraries that have already been loaded will not be duplicated. In our example, we'll name the library libJgres.so.1.0, so we need to use loadLibrary (“Jgres”) (See Listing 1, line 94).

With our .java file complete, we are ready to write the C code. First, we compile the .java file with:

javac PGConnection.java

Then, we create the “stubs” file and the .h file with:

javah PGConnection
javah -stubs PGConnection

At this point you should have PGConnection.h and PGConnection.c in your current directory. PGConnection.c is the “stubs” file, and should not be modified. For our purposes, the only thing you must do to the stubs file is to compile it and link it into your shared library.

PGConnection.h is a header file that must be included in any C file that accesses PGConnection objects. At line 14 (see Listing 2) you will find the declaration of a struct corresponding to the data for our object. Below that you will find prototypes for all of the native methods we declared. When writing the C code for native methods, you must match these signatures exactly. Listing 2. PGConnectionNative.c (includes PGConnection.h)

Now, let's (finally) write the C code.

The code for connectDB is very straightforward, and demonstrates the majority of the issues involved in writing native code. Notice that the first argument to connectDB is not listed in the PGConnection.java file. Java automatically passes a “handle” (a fancy pointer) to the object you are dealing with as the first parameter of every native method. In our case, this is a pointer to a struct HPGConnection (defined in PGConnection.h), which we name “this” (Listing 2, line 14. If you're working in C++, you may want to use “self” since “this” is a keyword). Any access to the object's data must go through this handle.

The remainder of the parameters are the Strings we passed in (see PGConnection.java). These are also passed as handles, or pointers to the struct Hjava_lang_String (defined in java_lang_string.h, included by native.h). We could access these structures like any other handles (see below), but Java provides several convenient functions that make it much easier to work with strings.

The most useful of these functions are makeCString and makeJavaString. These convert Java's Strings to char *s and vice versa, which use Java's garbage collector to handle memory allocation and recovery automatically. (

______________________

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix