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 assemblies that wrap around Java types.
Let's take the following scenario: you are a Java middleware company and you want to ship a proxy type assembly that allows .NET 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 type assembly? 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 assemblies.
Let's start with the classpath. Your Java types will have to be deployed with the .NET application, otherwise
nothing will work.
A common application layout would be to to have all your assemblies 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 JvmLoader
.
You register the hook by calling JvmLoader.RegisterConfigurationHook()
from a method that the
JMS Courier runtime will call when your assembly gets loaded into the process. You can
make that happen by declaring a public static void Init()
method in a class that is annotated
with the JuggerNETInit
attribute. This is shown in the snippet below.
using Codemesh.JuggerNET; // a class whose only purpose is the registration of a callback hook [JuggerNETInit] public class ConfigHookRegistrar { public: // the constructor registers the hook public static void Init() { JvmLoader.RegisterConfigurationHook(Configure); } public static void Configure(IJvmLoader loader, int when) { // let's ignore what goes into a hook for now } };
The key here is the class annotation with the [JuggerNETInit]
attribute. This will
cause the runtime to invoke the Init()
method when the assembly is loaded into the
process. That causes the hook registration to be processed and that in turn causes the
registered callback to be invoked multiple times in the application's lifecycle.
The first invocation will occur right after the JvmLoader
instance has been
created. This is the time to configure application defaults that can be overridden by anyone
who has access to the JvmLoader
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 JvmLoader
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 JvmLoader
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 public static void Config(IJvmLoader loader, int when) { When w = (When)when; if(w == When.XMOG_AFTER_INITIALIZATION) { // set defaults } else if(when == When.XMOG_BEFORE_LOADING) { // override or amend user configuration } else if(when == When.XMOG_AFTER_LOADING) { // check or perform Java application initialization } }
Example
You can find an example in the code generator distribution at examples/dotnet/v3/configuration/confighook
.
The readme.html
file in that directory contains a description and the instructions on how to build and
run the example.