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. (

______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState