Calling Java Methods from Plug-ins


Calling Java methods from plug-ins is simple. You'll need to run the new JRI version of javah from Netscape on the class files whose methods you want to call. This will generate the header files you'll need for C and C++ to call Java.

For example, say you want to call methods from the java.lang.String class. All you have to do is call:

	% javah -jri java.lang.String

(Be sure to have your classpath set properly, or use the -classpath option to javah.) This will generate the file java_lang_String.h with accessors macros defined for all methods and fields that are accessible.

Accessing Fields

The JRI version of javah uses a naming convention to create accessor macros for all fields defined for a Java class. This convention prepends get_ or set_ followed by the package and class name to the field name to define a getter or setter. When the preprocessor symbol DEBUG is defined, these names declare functions (for better type checking) with signatures of the form:
	ResultType
	get_ClassName_fieldName(JRIEnv* env, ClassName* self);

	void
	set_ClassName_fieldName(JRIEnv* env, ClassName* self,
	                        ResultType value);

Note that the class name itself denotes the type of the object whose field is to be accessed (the self parameter). The name of the struct may also be used to avoid including additional header files, e.g. struct ClassName*.

In C++, the type denoting the Java class becomes an actual C++ class with the same name. Fields can be accessed via inlined accessor methods of the form:

	class ClassName : ... {
		ResultType	// getter method
		fieldName(JRIEnv* env) { ... }

		void		// setter method
		fieldName(JRIEnv* env, ResultType value) { ... }
		...
	}

So, for example, you can get the count field of the String class in C via:
	jint cnt = get_java_lang_String_count(env, myString);

or in C++:
	jint cnt = myString->count(env);

and set the count field in C via:
	set_java_lang_String_count(env, myString, cnt);

or in C++:
	myString->count(env, cnt);

The env parameter is passed to all Java methods and represents the current execution environment (the state of the Java interpreter).

Security Considerations

Note that the count field of the String is private although we are able to access it anyway. This is because the C and C++ interface to Java objects has unrestricted access privileges. Although we could have made you jump through more hoops to get private and protected information from Java, we didn't because you can always cast and find it anyway. However, it is not advised to access private fields or invoke private methods because other Java implementations may not provide these members (as they are not part of the contract published in the Java interface).

Static Fields

Static fields can be accessed in almost the same way as non-static fields. Rather than accessing them from an instance, we access them from a class object. So in Java where one might write:
	PrintStream o = System.out;

from C you would instead write:
	java_lang_Class* systemClass = class_java_lang_System(env);
	java_io_PrintStream* o = java_lang_System_out(env, systemClass);

or in C++:
	java_lang_Class* systemClass = java_lang_System::_class(env);
	java_io_PrintStream* o = java_lang_System::out(env, systemClass);

Note that although in C++ static Java object fields are accessed by static member functions, these member functions still have to be passed the Java class object. Although the class object could be looked up by the member function, passing it in eliminates possibile multiple lookups.

Accessing Methods

Methods can be accessed in a very similar fashion. For example, the substring method of the String class can be called as follows:
	java_lang_String* sub = java_lang_String_substring(env, str, 5, 9);

This call gets the substring of the string str from characters 5 through 9 and assigns the result to the sub variable.

The above example also can be expressed in C++ as:

	java_lang_String* sub = str->substring(env, 5, 9);

Overloaded Methods

The JRI version of javah attempts to deal with overloaded methods in Java in a more graceful way than fully mangling the name. Mangled names such as the way C++ member function names appear to C are not mnemonic and are difficult to deal with.

Whenever javah encounters more than one method with the same name, it generates symbols in C (and C++) that have an index appended to them for all but the first occurrance. For example, in the String class there are several methods with the name indexOf:

	public int indexOf(int ch);
	public int indexOf(int ch, int fromIndex);
	public int indexOf(String str);
	public int indexOf(String str, int fromIndex);

In C these are given the names (and signatures):
	extern jint java_lang_String_indexOf(JRIEnv* env,
			java_lang_String* self, jint a);

	extern jint java_lang_String_indexOf_1(JRIEnv* env,
			java_lang_String* self, jint a, jint b);

	extern jint java_lang_String_indexOf_2(JRIEnv* env,
			java_lang_String* self, java_lang_String *a);

	extern jint java_lang_String_indexOf_3(JRIEnv* env,
			java_lang_String* self, java_lang_String *a, jint b);

and in C++:
	jint indexOf(JRIEnv* env, jint a);
	jint indexOf_1(JRIEnv* env, jint a, jint b);
	jint indexOf_2(JRIEnv* env, java_lang_String *a);
	jint indexOf_3(JRIEnv* env, java_lang_String *a, jint b);

Here's a helpful hint: Put the overloaded method you want to use most from C or C++ before the other overloaded methods in the class definition. This will cause it's name to not have an index appended to it. Also, if you have a method implemented in Java and an overloaded declaration of a native method, put the native method first. It's the one you'll have to implement in your C code, and again you can avoid the appended index.

But what if I define a method called indexOf_1? To prevent this sort of collision, javah mangles method names containing underscores. All underscores get replaced by the hexidecimal value 00005f. Other Unicode characters also get replaced in this way with their hexidecimal equivalent. This means although the second overloaded version of indexOf would become indexOf_1, a method explicitly named indexOf_1 would become indexOf_00005f1. Bottom line: avoid underscores and you'll be much happier.

Note that although you can call methods which are overloaded in this way, you cannot define native methods with overloaded names. This is currently a limitation of the Java 1.0 runtime.

Java Names for Primitive Types

What's a jint? A jint is the JRI way of expressing a 32-bit signed integer value that works across all platforms. The JRI defines similar C type names for all of Java's scalar types. The following table shows the correspondence:

Java Type C/C++ Type Size
boolean jbool 1 byte
byte jbyte 1 byte
char jchar 2 bytes
short jshort 2 bytes
int jint 4 bytes
long jlong 8 bytes
float jfloat 4 bytes
double jdouble 8 bytes

You should always use these JRI types when dealing with Java from C and C++ rather than standard types like int or long. This is the first step to ensuring that your code will work across multiple platforms.

These JRI types are defined in the file jri_md.h which is included by the main JRI header file, jri.h. These types are conditionally defined for each architecture and compiler by controlling numerous preprocessor symbols (including type sizes, alignment, byte ordering, whether the compiler supports long long values, etc.).

You must set the right preprocessor symbols for your architecture and compiler before including jri.h. Look at jri_md.h to see what you'll need. The jri_md.h header also defines a set of cross-platform macros for manipulating jlong (long long) values.

What's this env parameter?

The env parameter represents the execution environment -- a JRIEnv* that's passed to all JRI operations (and consequently operations generated by javah). It's your handle on the Java interpreter. It serves two purposes: You needn't be concerned with it for the most part except to know that you need to pass it back to all JRI calls you make, and you shouldn't allow any other threads to use it except the one it was delivered to you on.

So how do you get your hands on one of these execution environments in the first place? The Navigator Plug-in API defines a new call, NPN_GetJavaEnv, that delivers one to you:

	extern JRIEnv* NPN_GetJavaEnv(void);

You can call this whenever you need to access the Java interpreter. The first time you call this, the interpreter may have to initialize itself if the browser hasn't already run a Java applet (so be patient).