Calling Plug-in Native Methods from Java

Not only can you call Java methods from a plug-in, but you can also define native methods that Java (and JavaScript) programs can call. "Native methods" are the name given to Java methods written in C code rather than in the Java language.

Why would you want to write native methods? Primarily to export lower-level functionality that could not otherwise be written directly in Java (such as functionality implemented by a plug-in). Native methods can also be used when greater performance is required.

To Java programs, plugins appear as instances of the class netscape.plugin.Plugin. The Plugin class is described in the Netscape Packages documentation.

Actually, there are three choices a plugin designer can make with respect to supplying a Java class for a plugin:

Defining a Plug-in Class

Plugins that wish to export additional functionality must do so by subclassing the class netscape.plugin.Plugin. Let's walk through a simple example where a plugin defines a new native method, playMovie.

We'll begin by creating a MoviePlugin.java file:

	import netscape.plugin.Plugin;

	class MoviePlugin extends Plugin {
		public native void playMovie(int from, int to);
	}

That's it! All you have to do is define the subclass and declare the new native method.

Next you'll need to compile the class with the Java compiler, javac, and then generate both headers and stubs with the new javah stub compiler. We've already seen how to create the header files in the previous section. To create the stubs you run javah once again with the -stubs option:

	% javah -jri MoviePlugin
	% javah -jri -stubs MoviePlugin

Be sure to set up your CLASSPATH environment variable to include the directory that the MoviePlugin.class file resides in (or use the -classpath option).

The call to javah -jri will produce a .h file containing macros for accessing the public fields and methods of the class. (When the DEBUG flag is defined, these are functions instead of macros for easier debugging.) Include the .h files of any classes that you intend to use in your plugin. And if you intend to implement any native methods that require protected or private access to fields and methods of a class, first define the IMPLEMENT_ClassName symbol before including the header:

	#include "java_lang_String.h"
	#define IMPLEMENT_MoviePlugin
	#include "MoviePlugin.h"

The call to javah -jri -stubs will produce a .c file with a special initializer for each class that it generates stub code for. It is essential that you call this initializer before using your class. The initializer routine performs the linkage that allows you to access fields of objects of that class, and registers your native method implementations so that Java can call them.

You need to call the initializer routine for all classes for which you provide native methods, not just your Plugin subclass. The initializer routine need not be called if you're just invoking methods or accessing fields.

For the MoviePlugin example, the file "MoviePlugin.c" will be generated containing the initializer routine init_MoviePlugin:

	extern java_lang_Class* init_MoviePlugin(JRIEnv* env);

If the MoviePlugin class had been declared to be in a Java package, the package name would also become part of the class name, separated by underscores.

Warning: Don't create initializer routines for system classes in order to access their fields. Most of these are private and may change in the future. Call the public accessor routines instead.

Associating a Class with your Plug-in

With Navigator Plug-ins version 0.8, you'll need to add another entry-point to your plug-in that tells the Navigator the Java class of your plug-in. This entry point is NPP_GetJavaClass:
	extern java_lang_Class* NPP_GetJavaClass(void);

For the MoviePlugin example, we can write:
	java_lang_Class* NPP_GetJavaClass() {
		return init_MoviePlugin(NPN_GetJavaEnv());
	}

As mentioned previously, you may also choose to not associate a Java class with your plug-in. This can be done by simply returning NULL from the NPP_GetJavaClass callback.

If you have any other classes that you'll be using from your plugin, you may need to call their initializer routine too. Do this if you have native methods that you're implementing for that class (calling the initializer will register them with the Java runtime). Just call all the initializer routines in your NPP_GetJavaClass routine, returning the one that is the class of your plugin.

You'll need to compile and link the stub file (e.g. "MoviePlugin.c") into your application to include the initializer. You'll also need to compile and link the stub file if you're compiling with the DEBUG preprocessor symbol defined. This causes functions to be used for field access and method invocation instead of macros which gives better type checking, and makes debugging a little easier.

Implementing Native Methods

The remaining task is to implement the body of the native method that we defined in the MoviePlugin example. The initializer generated by the javah stub compiler expects you to implement C functions with a name that corresponds to the class and method name, and a signature that corresponds to the signature of the declared native method. The function name is again the concatenation of the package and class name with the method name, separated by underscores and prefixed by the word native_:
	ResultType
	native_ClassName_methodName(
		JRIEnv* env,
		ClassName* self, JRIMethodThunk* methodThunk,
		other args...);

The env parameter is the current execution environment. It is passed to the native method by the runtime when it is called. The self parameter is the object on which the method is being invoked. For static methods, this will be the class on which the the method is invoked, rather than an instance. The methodThunk parameter is an artifact of how the native methods are called and can otherwise be ignored. Any remaining arguments (according to the signature of the method defined in Java) follow. So for the movie plug-in, we'll define:
	void
	native_MoviePlugin_playMovie(JRIEnv* env, MoviePlugin* self,
	                             JRIMethodThunk* methodThunk,
	                             jint from, jint to)
	{
		...bla, bla, bla...
	}

Associating the Native Instance and Java Object

Most likely, you'll need to access the native plugin instance (the NPP object) from the Java Plugin object. You can do this by fetching the peer field of the Plugin object:
	NPP npp = (NPP)netscape_plugin_Plugin_getPeer(env, self);

or in C++:
	NPP npp = (NPP)self->getPeer(env);

Be sure to include netscape_plugin_Plugin.h to have access to this accessor (you'll need to generate it by running javah -jri netscape.plugin.Plugin). Don't forget to also generate, compile and link the stub file (by running javah -jri -stubs netscape.plugin.Plugin) to get the initializer method and any accessor functions in DEBUG mode.

You can also access the Java object associated with your plugin at any time by calling the NPN_GetJavaPeer routine:

	extern netscape_plugin_Plugin* NPN_GetJavaPeer(NPP instance);

The Java instance is actually not created until this call is made the first time (thereby avoiding starting up the interpreter if you never need an instance). Note however that JavaScript will also call this routine if it needs to talk to a plug-in on the page.

When your Java Plugin instance gets created, its init method will be called. You can specialize this method to perform special initialization work after the instance is created. Also, right before the native plugin instance (the NPP) is destroyed, the Java Java Plugin instance will get its destroy method called. You can specialize this method to perform special cleanup work before the native instance is destroyed. At any time you can test if the native instance is still active (not destroyed) by calling isActive:

	public void init();
	public void destroy();
	public boolean isActive();

To access JavaScript from a plugin, you can call the getWindow method:
	public JSObject getWindow();

The getWindow method returns a JSObject (a JavaScript object reflected into Java) representing the window on which your plugin is running. From the window you can access other plugin instances, and JavaScript operations. You can also access the window object from native plugin code via:
	netscape_javascript_JSObject* window =
		netscape_plugin_Plugin_getWindow(env,
			NPN_GetJavaPeer(instance));

See the section Calling JavaScript from Java Methods or the Netscape Packages documentation for more details.

Garbage Collection Considerations

You may have noticed that you never have to free a Java object. That's because they get garbage collected, i.e. freed by the system when it can determine that the objects are no longer needed. However, with this privilege come a few caveats when dealing with Java objects from less fortunate languages like C and C++.

First, pointers to Java objects are dangerous. This includes jref, the primary type of pointers to Java objects (also referred to as references to Java objects), and pointers to types and structs fabricated by javah (for instance, java_lang_String* or struct java_lang_String*). Pointers to these types and structures are jrefs which have been cast.

Basically, the lifetime of Java references is the same as the stack frame in which they're used. You can think of them as automatic variables in C (pointers to structures that are allocated on the stack). Consequently you must never store a jref in the heap, or in a global variable. You can put them in local variables, pass them as parameters and return them as results, but don't use them anywhere else.

But what about globals?

Storing references to Java objects in global variables or in the heap is sometimes an essential part of life, so we've invented a way of for you to work with the garbage collector when you need them. The JRIGlobalRef data type is just for this purpose. JRIGlobalRefs can be used just like malloc'd data in C and C++ -- you explicitly obtain one, and you have to explicitly free them. Once you have one, you can store it anywhere.

To create a global reference, use the JRI_NewGlobalRef JRI call:

	extern JRIGlobalRef JRI_NewGlobalRef(JRIEnv* env, jref initialValue);

This call takes a jref with which to initialize the value of the new global reference. Global references must be disposed of by calling JRI_DisposeGlobalRef:
	extern void JRI_DisposeGlobalRef(JRIEnv* env, JRIGlobalRef globalRef);

To get or set a global reference, use the following calls:
	extern jref JRI_GetGlobalRef(JRIEnv* env, JRIGlobalRef globalRef);
	extern void JRI_SetGlobalRef(JRIEnv* env, JRIGlobalRef globalRef,
	                             jref newValue);

Global references are scoped to the execution environment which is passed as the first argument to all the global reference calls. This means that different execution envionments cannot access each other's globals (although jrefs may be freely exchanged between different execution environments). It also means that if the execution environment is destroyed, all the globals will be automatically disposed.

The Navigator assigns a unique execution environment to each class of plug-in. This means that if multiple instances of the same plug-in class (i.e. the same library) are currently active, they will share the same execution environment and consequently the same global references. Plug-ins of a different class will have their own globals references. When a plug-in library gets unloaded, the global references will be automatically disposed. [Warning: This is not implemented in Atlas Release beta4, so be sure to explicitly dispose any globals when your plug-in is unloaded.]