Category: Java/C++ Runtime
What do I need to do to run my application on Unix/Linux?
Each platform comes with its own challenges and peculiarities. We manage to hide many of these peculiarities at the C++ compiler level, but we cannot hide the issues that exist at the OS level. If you started out on Windows or classic Mac OS, the challenges that are added by Unix/Linux can seem quite formidable.
This page collects some hard-earned bits of wisdom about running mixed C++/Java application on Unix/Linux systems.
Issues common to most Unix/Linux platforms
Your mixed language application usually consists of an application binary, one or more shared libraries (DLLs in Windows parlance) and a JVM. The JVM itself is a set of shared libraries and resource files. On Windows, the operating system provides an implicit search path for shared libraries that includes the current working directory and the directory in which the application's main binary module resides. This is not the case on Unix/Linux. Even if you have all shared libraries and your application colocated in one directory, your application will usually not run because the shared libraries cannot be found.
There are different solution approaches to this problem. The most systematic solution is to build a so-called runpath into your modules. A runpath is essentially a library search path that is built into your executable. The runpath is typically specified at link time using a compiler/linker-specific option string. While a runpath is great for finding your own modules, it doesn't much help for modules that are dynamically configurable, i.e. the JVM. The Codemesh runtime configuration framework allows you to dynamically configure the JVM that is to be used by the C++ application. On most platforms it would be catastrophic to have one JVM configured by the runtime API and a runpath refering to another JVM.
Because of this issue, you usually cannot avoid having to deal with the platform's library search path. This path is specified via an environment variable that has different names on different platforms. The following table provides the environment variable names by platform:
|Platform||Library search path variable|
|Windows||typically not required|
You can specify the library search path for your application by using one of the above variables. The question then is: which directories should be on the library search path? The answer is: the directory containing the Codemesh runtime library (oftentimes the current working directory) and the directories required for successfully loading the configured JVM.
On Windows, you don't need to worry about the JVM at all; on most Unix/Linux platforms, you need to configure several directories to satisfy a particular JVM's runtime requirements. You typically find the JVM library in a directory following this naming policy:
You would probably think that adding the directory in which libjvm.sl resides to the search path would be sufficient to run the application. Unfortunately, this is not the case. On HP-UX, you need to add at least the parent directory of the JVM directory in addition to the JVM directory. On other platforms, you might also have to add a sibling directory that contains a thread model-specific library. This directory is often called native_threads.
These requirements taken together often yield a search path containing:
- the current working directory (.)
- the JVM directory (contains libjvm, subsequently called <jvmdir>)
- the JVM's parent directory (<jvmdir>/..)
- the thread model directory (<jvmdir>/../native_threads)
One question remains though: if the JVM is configured dynamically, how are we going to be able to configure the environment for the process? The answer lies in a particular feature of the Codemesh runtime API. The Codemesh runtime allows you to query the JVM that is configured and it even has some built-in expert knowledge of the search path that should be configured for this JVM. The methods supporting this functionality are:
If you call one of these methods after your application has configured itself but before it has loaded a JVM, you can get all the information that is necessary to successfully load a JVM in a subsequent run. The key to successful execution becomes double (or triple) execution of the application: first you query one piece of information, then the next, and finally you run the application a third time with the queried information used to configure the environment. This is the pattern that we use in our ANT-based examples. A shell script doing the same thing might look like this:
HP-UX cd /myapp/bin export SHLIB_PATH=. myjvm=`./myapp -jvm` mypath=`./myapp -info` export SHLIB_PATH=$mypath LD_PRELOAD=$myjvm ./myapp
In this example, we have built our application to understand two command line arguments: -jvm and -info, each of which is tied to one of the above printAndExit() methods. You can name the options anything you want: -dumpjvm, -printjvm, etc. The only thing that matters is that your application has a way of answering a question about its configuration. You then use the output from the query as input for the real program execution.
You can see the LD_PRELOAD statement in the above example. This is a peculiarity of HP-UX and will be discussed below.
On AIX, you might bump into default memory size restrictions if your JVM is configured to ask for more heap than is normally allocated. In this case, use the LDR_CNTRL environment variable with the MAXDATA option, for example:
There is more information available on this topic at IBM.
On HP-UX, you need to make sure that your exceutable queries the search path and you need to preload the JVM and possibly other shared objects as well. Use the chatr command to enable search path processing:
chatr +s enable <executable>
Use LD_PRELOAD when launching your application: