Java / C++ runtime configuration

Introduction

 

What exactly is it that needs to be configured? Any integration solution requires some configuration. In our case, we have a Java/C++ integration solution, which means we have to configure some of the glue in between. When you run a Java application, you typically have to specify all kinds of command line options, for example:

    java -cp "..\lib\myapp.jar" -Xmx256m -Dmy.key=value MyApp

Maybe someone conveniently put this into a batch file for you, so this complexity is hidden from you, but it is there nevertheless and every Java developer expects such a command line to be present.

In the most common use case for our product, a C++ application is using Java classes under the hood. How do you tell that embedded JVM all the Java configuration settings that it needs to successfully run your code? We did not want to impose a requirement on your C++ process that it has to parse Java command line options.

This is what runtime configuration is all about. In addition to setting JVM options, there are framework configuration options that have to do with questions like:

  • How are Java exceptions translated to C++?
  • How are Java strings translated to C++ and vice versa?
  • Should the application use a Shared JVM instead of an in-process JVM?
  • How and where should the framework trace debug messages?

A mature integration solution requires a mature configuration framework. The runtime API provides you with several different alternative configuration techniques and this page provides you with the basic instructions on how to use each of these techniques. The different techniques are explicit, XML file-based, via environment variables, and via configuration hooks.

The product also contains several examples specificly demonstrating the different configuration mechanisms in the examples' rtconfig subfolder.

Explicit Configuration API

In some ways, configuring your application explicitly in code is the simplest solution to the configuration problem. There is no external, user-editable configuration file that could be edited, accidentally deleted or messed up, which is of course exactly why it might not be the right mechanism for you. The nice thing about explicit configuration is that it is easy to debug and you have full power over every aspect of configuration.

It should be mentioned that the mere fact that you're using the explicit configuration API does not mean that you're hard-coding all configuration settings in your application. It is totally conceivable that you would use the API to implement your own, flexible configuration scheme, for example one that is based on your own proprietary configuration files.

Usage of the configuration API always starts out with the acquisition of a reference to a JVM loader instance. This is a singleton instance in the runtime library and you can use one of several factory methods to acquire a preconfigured instance. Consider the following snippet:

xmog_jvm_loader & loader = xmog_jvm_loader::get_jvm_loader();


loader.appendToClassPath( "." ); 

In this example, we use the "default" factory method to acquire a reference to the loader. That loader instance will be essentially empty of configuration information. We then proceed to append the current directory as a classpath root.

There are several different factory methods you can choose from. Each one allows you to take control over all sorts of initialization activities. The following snippet configures a number of basic options before appending to the classpath:

xmog_jvm_loader & loader = 
   xmog_jvm_loader::get_jvm_loader( true, true, TraceAll, TraceInfo);

loader.appendToClassPath( "." ); 

The four arguments passed to the factory method have the following meaning:

  • Allow environment variables to override configuration settings.
  • Search for a default JVM to use if no JvmPath is specified.
  • Apply the TraceInfo level to all trace categories for more debugging feedback.

Please check out the Runtime Configuration section of the runtime API documentation for more information and a complete list of configuration functions.

XML Configuration File

XML file-based configuration relies on the presence of a file containing configuration data in XML format. For the application to know about that configuration file, you need to tell the framework which file to use.

You can use the static method xmog_jvm_loader::setConfigFile() to tell the framework about the configuration file. When you use this method, you can later use any of the JVM loader factory methods, even the ones that don't take a file name as an argument, and your file will be read. Alternatively, you can use a factory method that takes the filename as an argument. The xmog_jvm_loader class also declares a read() method that takes a filename as an argument. You could explicitly call that method to several times to aggregate the options from more than one config file.

The format of XML configuration files is based on .NET configuration files but it does not require section declarations to be present. A typical configuration file would look like this:

<configuration>
  <codemesh>
    <runtime>
	
      <Loader name="Default" />
	  
      <Default.JvmSettings>
        <add key="ClassPath" value="..\.." />
        <add key="TraceLevel" value="TraceInfo" />
        <add key="InitialHeapSize" value="256" />
        <add key="MaximumHeapSize" value="256" />
      </Default.JvmSettings>
	  
      <Default.Options>
        <add key="test" value="value" />
        <add key="novalue" value="" />
        <add key="com.codemesh.test" value="456" />
      </Default.Options>
	  
      <Default.XOptions>
        <add key="X:+UseParallelGC" value="" />
      </Default.XOptions>
	  
    </runtime>
  </codemesh>
</configuration>

You can see three sections of interest in this example file:

  • Default.JvmSettings
    This section contains key/value pairs standing for framework or JVM configuration settings that we make available via convenient names.
  • Default.Options
    This section contains key value pairs that represent the so-called -D options of the JVM. Notice that there is no leading -D in the key or value.
  • Default.XOptions
    This section contains key value pairs that represent the so-called -X options of the JVM. Notice that there is not leading -X in the key or value.

Notice how these sections are "selected" by the name attribute in the Loader element. Basically, you could have multiple, alternate named configurations (for example for production, QA, debug) in one configuration file and then quickly switch between them by simply changing the value of the name attribute. Whether or not you take advantage of this feature is totally up to you.

Within one arbitrarily named group (in this case, the "Default" group), there is some overlap between the three section because many of the options in the JvmSettings section are really synonymous with -D or -X options. The MaximumHeapSize option for example really stands for the -Xmx option. So both of the following snippets would have identical effects:

<Default.JvmSettings>
  <add key="MaximumHeapSize" value="256" />
</Default.JvmSettings> 
<Default.XOptions>
  <add key="mx" value="256m" />
</Default.XOptions>

Don't use both alternative forms of representation in one config file; the runtime behavior would be undefined because one configuration setting would simply override the other. The same is true for -D options that have more abstract synonyms.

You can probably see the benefit of using the first alternative: you don't have to memorize the sometimes cryptic codes used to represent the options; instead you can use descriptive names. Unfortunately, there are so many -X options that we can't possibly make all of them available via convenient names; we have simply focussed on the most commonly used ones.

The Options section provides you with the ability to define arbitrary -D options as key/value pairs.

The XOptions section gives you an escape hatch for such rarely used options. There is one more thing that you should understand about XOptions: the distinction between key and value. Under the hood, the key and the value become concatenated to a "-X" string to form the JVM option that is being passed to the JVM initialization API. The option above is turned into:

"-X" + "mx" + "256m"

to form the complete option "-Xmx256m". If we simply concatenate the two strings, why do we have a separate key and value? And would the following option have worked too?

<Default.XOptions>
  <add key="mx2" value="56m" />
</Default.XOptions>

Yes, it would have, but it would be very hard to understand and it would break some of the built-in safeguards against misconfiguration. Above, we said that having both the MaximumHeapSize and the "mx" option in one file would cause undefined behavior. That is true because MaximumHeapSize is internally implemented in terms of setting the "mx" option. This makes sure that there are no duplicates of one setting in the configuration. If you used "mx2" as the name, this safeguard would fail and you would end up with a duplicate setting for the "mx" option, possibly causing a load failure.

Please remember that all JVM initialization settings will be disregarded if you're connecting to a Shared JVM server. You can still specify them, but they will mostly be ignored (unless the framework is configured to fall back to in-process initialization in case the server is unavailable).

There is not much else to say about configuration files, except for the complete list of configuration options that you can use in the JvmSettings section. We typically only comment on the options that do not correspond directly with a JVM initialization option.
 

Option Name Corresponding
JVM setting
Comments
AppendBootClassPath -Xbootclasspath/a:  
AppendToClassPath n/a Usually preferable to setting the ClassPath directly because it uses platform portable path separators and can be used on top of defaults that stem from config files or config hooks.
Append_To_LibraryPath n/a Append a directory to the native library search path. Usually preferable to setting the LibraryPath directly because it uses platform portable path separators and can be used on top of defaults that stem from config files or config hooks.
BatchMode -Xbatch  
BootClassPath -Xbootclasspath:  
CheckJni -Xcheck:jni  
ClassPath -Djava.class.path  
Connection n/a The connection string for the Shared JVM.
Debug -Xdebug  
DefaultEncoding n/a The name of the Java encoding used to convert between Java strings and native strings.

Default is [unspecified]. Use "UTF-8" for best performance if your strings are either ASCII or UTF-8.
EnableClassGC -Xnoclassgc Setting is logically reverse of JVM setting.
EnableVerboseGC -verbose:gc  
ExceptionPolicy n/a Governs how Java exceptions are translated into native exceptions. Default value is "TypedExceptions" and that rarely needs to be changed.
Future -Xfuture  
IgnoreUnrecognized n/a A JNI configuration option. Causes JVM initialization failure if value is false and unknown option is used.
IncrementalGC -Xincgc  
InterpretedMode -Xint  
InitialHeapSize -Xms In MB.
JniFallback n/a Set to true if using remote (Shared JVM) mode but allowing fallback to in-process mode if server is not available.

Requires Remote=true and configuration of both remote and in-process options to make sense.
JvmPath n/a The full name (including path) of a JVM. This is a file usually called jvm.dll on Windows or libjvm.so on most Unixes.
 
Only required if operation with a default JVM is not acceptable and if in-process mode is used.
LibraryPath -Djava.library.path  
LogGCFile -Xloggc:  
MaximumHeapSize -Xmx  In MB.
MixedMode -Xmixed  
NoAgent -Xnoagent  
NoShutdown n/a Works around on-exit crashes if set to true. Rarely needed.
PrependBootClassPath -Xbootclasspath/p:  
PrependClassPath n/a Prefix the configured classpath with the specified file or directory.
Profile -Xprof  
ReduceSignals -Xrs  
Remote n/a Set to true if you wish to use the Shared JVM instead of in-process JNI integration.

Combine with JniFallback if in-process execution should be used as a fallback option.

Typically requires specification of Connection string.
Run -Xrun  
SecurityManager -Djava.security.manager  
SecurityPolicy -Djava.security.policy  
TraceFile n/a The name of a file into which all trace messages are written.
TraceLevel n/a The minimum necessary level for a trace message to be written.

One of TraceErrors, TraceWarning, TraceInfo, and TraceVerbose.
RuntimeLib n/a Synonym for JvmPath.
UseCodemeshSecurityManager n/a When you specify a custom security manager via SecurityManager, your proxy application might not work. That's because the proxy application requries certain privileges that you typically don't grant.

Setting this option to true enables the required privileges while delegating all other privilege checks to your specified security manager.

Environment Variables

You can control almost all configuration options via environment variables. In order to do this, your application must support environment variable overrides. For application security purposes, you can control whether or not environment overrides are allowed.

To enable environment overrides, you simple pass a boolean value of true as the first argument of your xmog_jvm_loader factory method. Allowing configuration overrides is a great way for testing your application with different runtime environments. For example, if you need to make sure that your application works with multiple versions of a JVM, you can easily switch the JVM that is being used by defining the XMOG_JVM_PATH environment variable.

Another use case for environment variable overrides would be the specification of debugging-related overrides.

The following table contains the complete list of supported variables.
 

Environment Variable Corresponding
JVM setting
Comments
XMOG_APPEND_BOOT_CLASS_PATH -Xbootclasspath/a:  
XMOG_BATCH -Xbatch  
XMOG_BOOT_CLASS_PATH -Xbootclasspath:  
XMOG_CHECK_JNI -Xcheck:jni  
XMOG_CLASSPATH -Djava.class.path  
XMOG_CONN n/a The connection string for the Shared JVM.
XMOG_DEBUG -Xdebug  
XMOG_DEFAULT_ENCODING n/a The name of the Java encoding used to convert between Java strings and native strings.

Default is [unspecified]. Use "UTF-8" for best performance if your strings are either ASCII or UTF-8.
XMOG_EXCEPTION_POLICY n/a Governs how Java exceptions are translated into native exceptions. Default value is "TypedExceptions" and that rarely needs to be changed.
XMOG_FUTURE -Xfuture  
XMOG_INCREMENTAL_GC -Xincgc  
XMOG_INTERPRETED -Xint  
XMOG_INITIAL_HEAPSIZE -Xms In MB.
XMOG_JNI_FALLBACK n/a Set to true if using remote (Shared JVM) mode but allowing fallback to in-process mode if server is not available.

Requires Remote=true and configuration of both remote and in-process options to make sense.
XMOG_JVM_PATH n/a The full name (including path) of a JVM. This is a file usually called jvm.dll on Windows or libjvm.so on most Unixes.
 
Only required if operation with a default JVM is not acceptable and if in-process mode is used.
XMOG_LIBPATH -Djava.library.path  
XMOG_LOGGCFILE -Xloggc:  
XMOG_MAXIMUM_HEAPSIZE -Xmx  In MB.
XMOG_MIXED -Xmixed  
XMOG_NOAGENT -Xnoagent  
XMOG_NO_SHUT n/a Works around on-exit crashes if set to true. Rarely needed.
XMOG_PREPEND_BOOT_CLASS_PATH -Xbootclasspath/p:  
XMOG_PROF -Xprof  
XMOG_RS -Xrs  
XMOG_REMOTE n/a Set to true if you wish to use the Shared JVM instead of in-process JNI integration.

Combine with JniFallback if in-process execution should be used as a fallback option.

Typically requires specification of Connection string.
XMOG_RUN -Xrun  
XMOG_SECURITY_MANAGER -Djava.security.manager  
XMOG_SECURITY_POLICY -Djava.security.policy  
XMOG_TRACE_FILE n/a The name of a file into which all trace messages are written.
XMOG_TRACE_LEVEL n/a The minimum necessary level for a trace message to be written.

One of TraceErrors, TraceWarning, TraceInfo, and TraceVerbose.
XMOG_USE_CODEMESH_SECURITY_MANAGER n/a When you specify a custom security manager via SecurityManager, your proxy application might not work. That's because the proxy application requries certain privileges that you typically don't grant.

Setting this option to true enables the required privileges while delegating all other privilege checks to your specified security manager.

Configuration Hooks

Configuration hooks are the most sophisticated of the built-in configuration mechanisms. A configuration hook is essentially a callback that is invoked by the runtime library at various points in the initialization sequence. You can register a configuration hook via the runtime framework method xmog_jvm_loader::registerConfigurationHook(). Your registered configuration hook(s) are called

  • right after the JVM loader singleton has been instantiated.
    This is before the application has a chance to execute any user-written configuration code and is the perfect time to set defaults that can be customized or overridden by user-written configuration logic.
  • right before the JVM loader is loading the JVM.
    This is after the application has had a chance to execute user-written configuration code and is the perfect time to check user-supplied options for validity and possibly override user choices.
    This is also a great point to set a breakpoint to see when exactly the JVM is being loaded.
  • right after the JVM has been successfully loaded.
    This gives you a chance to try to execute some application-specific Java functionality and provide some application-specific error messages on failure.

Configuration hooks make the most sense if you build them into a shared library that's used by your application. The general idea is this:

  1. Write a configuration hook implementation that handles one or more of the callback events.
  2. Add it to a shared library that is being used by your application(s).
  3. Declare a static variable whose constructor registers a configuration hook and buld that into your DLL/shared object as well.
  4. When your application that is linked with the DLL/shared object starts up, the C++ runtime initializes all static variables in the DLL and that invokes the config hook registration code.
  5. Finally, when the application creates a JVM loader or triggers JVM loading, your callback gets invoked and sets up the runtime environment according to your shared object's requirements.

There's an excellent config hook overview in the generated runtime API.

 


Copyright 2006-2011 by Codemesh, Inc., ALL RIGHTS RESERVED

:
runtime configuration
home products support customers partners newsroom about us contact us