: config tutorial (v3)

Lesson 4: Configuration Hooks

Introduction

Configuration hooks are probably the hardest to understand method of configuring your application but they can be invaluable if you ship proxy libraries that wrap around Java types.

Let's take the following scenario: you are a Java middleware company and you want to ship a proxy library that allows C++ developers to use your Java types with as little configuration as possible. How can you do that?

What if you could bundle the configuration code into your proxy library? Clearly that does not work for everything that might need to be configured, but if you combine configuration hooks with some conventions, you can pretty much create auto-configuring libraries.

Let's start with the classpath. Your Java types will have to be deployed with the native application. A common application layout would be to to have all your native code modules (shared libraries and executables) in a bin directory and all your Java libraries in a sibling lib directory.

Let's continue with the JRE. You could bundle a JRE into your application that resides in a jre directory.

If you can just figure out where you're installed at runtime, you can configure both JVM and classpath relative to that location becuase you know where the JRE and the Java libraries can be found. You can do that explicitly in your application's main() method but you can also do it from a configuration hook that lives in your proxy type library.

Then you just need to tell your users to follow the prescribed file layout (or whatever policy you decide to use) and everything will work automagically.

How Configuration Hooks Work

A configuration hook is basically a callback that gets invoked multiple times by the xmog_jvm_loader. You register the hook by calling xmog_jvm_loader::registerConfigurationHook(), usually from a global variable's constructor. Global variables are initialized when the library loads, so hook registration takes place at library load time. The following snippet, which would be part of your proxy library, illustrates this pattern.

#include "xmog_java_client.h"

// a class whose only purpose is the registration of a callback hook
class config_hook
{
public:

    // the constructor registers the hook
    config_hook()
    {
        xmog_jvm_loader::registerConfigurationHook(myConfigHook);
    }

private:

    // the configuration hook callback
    static void XMOG_CALLING_CONVENTION myConfigHook(void * pl, int when)
    {
    	// let's ignore what goes into a hook for now
        ...
    }
};

// when this object is initialized, the hook is registered
static config_hook     hooker;

The key here is the declaration of hooker at file scope. When its constructor is invoked it will register myConfigHook() with the Codemesh runtime, which will then proceed to invoke the callback multiple times.

The first invocation will occur right after the xmog_jvm_loader instance has been created. This is the time to configure application defaults that can be overridden by anyone who has access to the xmog_jvm_loader singleton. In particular, whatever defaults you configure here can be overridden from your application's main() method.

The second invocation will occur right before the xmog_jvm_loader tries to load the JVM. This is the time to check whatever configuration might have been done in main() and to override it or amend it with your own settings. You could for example allow users to set a classpath programmatically and then you just append your jar files to it during this callback.

The third invocation will occur right after the xmog_jvm_loader successfully loaded the JVM. This is the time when you can perform some sanity checks to make sure required Java types are available or even call Java code to perform application initialization tasks.

Let's now take a look at what goes into a configuration hook callback (we ignored that in the snippet above). The following snippet contains the skeleton that you would flesh out for your own application. A full example is available as part of the code generator distribution (see location below).

// the configuration hook callback
void XMOG_CALLING_CONVENTION myConfigHook(void * pl, int when)
{
    xmog_jvm_loader * pLoader = (xmog_jvm_loader*)pl;
    if(when == XMOG_AFTER_INITIALIZATION)
    {
        // set defaults
    }
    else if(when == XMOG_BEFORE_LOADING)
    {
        // override or amend user configuration
    }
    else if(when == XMOG_AFTER_LOADING)
    {
        // check or perform Java application initialization
    }
}

Example

You can find an example in the code generator distribution at examples/cpp/v3/configuration/confighook. The readme.html file in that directory contains a description and the instructions on how to build and run the example.