Implementing a replacement for the non-functional
java.lang.System.getenv using JNI is quite straightforward. The
constraints of what we're trying to do and how JNI works pretty much
determine what the code will look like. The code here is released into the
public domain with no licensing restrictions: do with it what you will.
First you need a class to contain the new method. Because I couldn't think
of anything better I've called mine uk.co.tigress.System:
package uk.co.tigress ; /** * TheOnce you've compiled that you can useSystemclass provides an implementation of *getenvthat actually works, unlike the one in *java.lang.System. The class cannot be instantiated. * * @author R M Yorston * @version 1.0 */ public class System { static { java.lang.System.loadLibrary("getenv") ; } private System() { } /** * Gets an environment variable. An environment variable is a * system-dependent external variable that has a string value. * * @param name name of the environment variable * @return the value of the variable, ornullif the * variable is not defined. */ public static native String getenv(String name) ; }
javah to generate an
include file, which basically just has a declaration of the required
native function. Then you can write the native function. The desired
functionality and the way JNI works means that you'll end up with something
not unlike:
#include "uk_co_tigress_System.h" #includeIf you use a Java 2 development environment you might also need these two functions:JNIEXPORT jstring JNICALL Java_uk_co_tigress_System_getenv (JNIEnv *env, jclass c, jstring jname) { const char *name, *value ; if ( jname == NULL ) { return NULL ; } name = (*env)->GetStringUTFChars(env, jname, (jboolean *)NULL) ; value = getenv(name) ; (*env)->ReleaseStringUTFChars(env, jname, name) ; return value ? (*env)->NewStringUTF(env, value) : NULL ; }
EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
/*
* getenv.dll doesn't use any features of JNI beyond 1.1, so
* returning JNI_VERSION_1_1 is fine.
*/
return JNI_VERSION_1_1 ;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved)
{
return ;
}
Compile that and put it into a shared library, put the Java class into a
jar file and install them where the runtime can get at them. In Java 1.2
and above this is simply achieved using the Java Extension Mechanism for
optional packages. See the documentation that comes with the J2SDK. In
Java 1.1 you'll need to add the jar file to your classpath and put the
shared library in the right place. This is left as an exercise for the
reader. Java 1.0 has a different native interface. That is even more
emphatically left as an exercise for the reader.
Using the method is straightforward. It's probably best to refer to the
class using its full name rather than importing it, as this will only lead
to confusion with java.lang.System.
public class test {
public static void main(String args[]) {
System.out.println(uk.co.tigress.System.getenv("HOME")) ;
}
}
For your convenience pre-compiled versions of the jar file and native
library for certain platforms are available for download from this site.
To download, right-click on the link and choose 'Save link as...'. None
of the files is bigger than 4 Kbytes.
To install the files go to the top directory of a JRE installation, or
the jre directory of an SDK installation and unpack the
archive file.
bin
directory, so there's a separate archive for that. The files are the same,
they just need to be in a different place.
bin. I'd appreciate knowing if that's
correct.
As an alternative approach, Pierre-Charles David has pointed out that on some systems it's possible to access the environment variables for a process through the /proc filesystem. This opens the way to a pure Java implementation of getenv on those systems.
If you have enjoyed this code you might also care to read my
rant about why I think Sun are wrong to disable
java.lang.System.getenv.