: code tutorial (v3)

Lesson 1: Project Setup

The topic of this lesson is how to set up your C++ project to use Java types. As described in the Technology overview, your application code usually deals with C++ proxy types, but the C++ proxy types internally rely on the C++ runtime library.

We glossed over this a little bit, but you also deal with the C++ runtime library directly when you configure the Java runtime environment for your C++ application. In fact, you could use just the C++ runtime library without any proxy types to load a JVM into your process.

Therefore, the simplest Java/C++ integration project does not involve any proxy types. It only uses some of the framework APIs from the runtime library to load a JVM. Such a program might look like this:

#include <stdio.h>
// this includes all the "xmog_" framework types that you
// might normally have to deal with
#include "xmog_java_client.h"

int main()
{
    // acquire a reference to the singleton JVM loader, configured to
    // - allow overrides of settings by environment variables
    // - allow a default JVM if none is specified
    // - trace all facilities at the most verbose level
    xmog_jvm_loader & loader = xmog_jvm_loader::get_jvm_loader( true, true, TraceAll, TraceVerbose);

    try
    {
        // have the loader attempt to load a JVM.
        // this could fail for example if no JVM is installed or if the loader
        // cannot locate an installed JVM
        // The JVM instance is owned by the loader and does not need to be deleted.
        xmog_jvm * pJvm = loader.load();
    }
    catch( xmog_exception xe )
    {
        // xmog_exception is the root exception class for anything
        // related to the Java side.
        // Any proxy exception types present are derived from this type as well
        // but they offer a much richer API for of dealing with exception message
        // and other exception properties.

        // we ask the exception for the Java message string. We are given ownership
        // and need to release the string when we're done with it.
        char *   msg = xe.get_message_chars();

        fprintf( stderr, msg );

        xmog_java_string::free( msg );
    }

    return 0;
}

This program uses the JMS Courier runtime library's configuration API to acquire a JVM loader instance and then attempts to load the default Java Virtual Machine using that instance. In a real integration project, you would follow the JVM loading by using some C++ proxy types before returning from main().

All JMS Courier runtime library type names start with xmog_ as a tribute to Calvin and Hobbes' "transmogrifier gun." C++ namespaces were still not reliable in all C++ compilers when we designed the product and we picked this prefix as unlikely to clash with anything else.

Compilation

Were you to try to compile this code with g++, for example by typing:

g++ -o loadjvm minimal.cpp

you would receive several compilation errors related to missing include files and missing types. Clearly you need to tell the compiler where your xmog_ header files are located so all the xmog_ type declarations can be found. Thus you add JMS Courier's include directory to the compiler's include path:

g++ -I "<installdir>/cpp/v3/include" -o loadjvm minimal.cpp

Now your compilation succeeds with g++ but you are getting linker errors. With other compilers you might have to make a few more adjustments:

Linking

At a minimum you will receive a link error due to the xmog_ type definitions not being available. You added the header files but now you need to also add the library that contains the actual type implementations.

The days when there was one C++ compiler per platform are long gone. Most commercial platforms support at least a "native" compiler, often supplied and supported by the platform's manufacturer, as well as the GNU C++ compiler, g++. In the case of Linux, g++ is the native compiler. The picture is further complicated by different compiler versions and the presence of 32 and 64-bit versions. Normally, your goal will be to link with a runtime library that was built for your target platform and processor architecture, using the same compiler type and version that you are using to build your project.

We're saying "normally" because that is not always true. For example:

Some combinations never make sense though. For example:

Where to Find the Right Runtime Library

JMS Courier supplies you with a range of different runtime libraries and hopefully one of them matches your build environment. If not, you can always talk to us and we can build a runtime library for you.

The runtime library is always called xmogrt. Depending on your platform, the full name of the file will be:

The libraries can be located in subfolders of the cpp/v3/lib directory.

Directory hierarchy screenshotThe screenshot to the right shows a typical directory hierarchy of runtime library directories.

As you can see, the first level after cpp/v3/lib is an operating system identifier.

The second level is a processor architecture identifier. The processor architecture is used to identify both the family of processors and the bittedness. 64-bit versions of a processor architecture are represented by their own code. For example, the 32-bit version of Intel X86 compatible processors is represented by the family code x86 whereas the 64-bit version is represented by the family code amd64. We use commonly known identifiers, so you should not have any trouble locating the proper directory.

The third level is a combined compiler type and compiler version identifier. As mentioned above, you don't necessarily have to match the type and version exactly, but you should try to use the best match that you can find.

Finally, there are debug and release versions of the runtime library. The fourth level distinguishes between these two flavors.