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:
Plugin
-- This should be
done when the plugin must have a Java object associated with it, but
otherwise doesn't define any additional methods (either Java methods
or native methods).
Plugin
-- This should be done
when the plugin wishes to export additional functionality (usually
native methods). We'll see how to do that in the next section.
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 MoviePluginBe 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.
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.
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... }
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.
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
jref
s 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.
JRIGlobalRef
data type is just for this purpose.
JRIGlobalRef
s 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
jref
s 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.]