: config tutorial (v3)

Lesson 2: Explicit Configuration

Introduction

Explicit configuration is the programmatic configuration of your combined Java and C++ application through API calls. It lies at the heart of all other configuration mechanisms because the other mechanisms can be implemented in terms of the epxlicit configuration API. As already mentioned in the introductory lesson, the configuration API is split into three categories. We will largely focus on the pre-launch category in this lesson because that is the most important one for 99% of our users.

The xmog_options and xmog_jvm_options types declare the public configuration APIs that are implemented by the xmog_jvm_loader type, through which you usually access them.

You typically acquire a reference to the singleton xmog_jvm_loader instance in your program's main() or in a custom initialization method that you invoke from your program's main(). The simplest way to do it is by calling the static get_jvm_loader() method without any arguments and then start calling configuration API functions declared by these two types:

xmog_jvm_loader & loader = xmog_jvm_loader::get_jvm_loader();

// start making configuration API calls
loader.setJvmPath( ... );
loader.setDashDOption( ... );

...

try {
    xmog_jvm * pJvm = loader.load();
}
catch( xmog_exception & e ) {
    ...
}

When to Configure

As you can see in the above code snippet, we explicitly try to load the JVM after the configuration is complete. While this is not strictly necessary (the JunC++ion runtime supports on-demand loading when Java is required) it is highly recommended. Doing it this way gives you one and only one point at which the JVM initialization could fail, making the debugging of configuration issues much easier.

You want to make sure that the configuration is complete before you make your first Java call or the JunC++ion runtime will simply use its default settings to load a JVM when asked to do something in Java. Most JVM settings are "frozen" in place once the JVM has been loaded and cannot be modified after the fact. Therefore, particularly if you have a large codebase, it is important to perform the configuration as early as possible. We recommend that you do it right at the beginning of your program's main().

To the novice C++ developer it might seem that this will guarantee that the runtime configuration has happened when the first call to Java needs to be made. Unfortunately, that is not always the case. C++ has to perform a host of initialization tasks before it can call main(). For example, the constructors of all globally declared objects have to be called prior to calling main() or your program code might be using uninitialized objects. Thankfully, the C++ infrastructure takes care of this for you. It is exactly this invisible initialization that can cause problems though.

Do not declare a global proxy type instance somewhere in your code and initialize it in such a way that Java code has to be executed, for example by initializating a proxy java::lang::String with a string literal:

#include "java_lang_pkg.h"

// DO NOT DO THIS
// a global String instace we use over and over
static const java::lang::String   SECRET = "shared secret";

Because the SECRET object requires that the JVM be loaded in order to convert the string literal to a Java String instance, the JunC++ion runtime will have no choice but to load a JVM during the String instance's constructor call. Crucially, this happens before your configuration code in main() is executed. It is safe to initialize global proxy instances to null or to use their default constructor, which does not result in a Java call.

For a more advanced, yet more complex technique you can see the the lesson on Configuration Hooks. It should be noted though that even with configuration hooks you would still depend on the order of object initialization for the proper outcome to be guaranteed.

Configuration API Overview

A brief overview of the configuration API follows below.

Property JVM Switch API Description
Jvm Path n/a setJvmPath(const char*)
getJvmPath()

The fully qualified name of the JVM library to be used to execute the application's Java portions.

This is usually a file called jvm.dll on Windows, libjvm.so on Linux/Unix, and libjvm.dylib on MacOS. Take care that it is platform-compatible with your application: you can only load 32-bit libraries into 32-bit processes and 64-bit libraries into 64-bit processes.