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 JDBC Courier 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 JDBC Courier 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 JDBC Courier 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 |
---|