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 routine that sets up the class for
use from native code (use_ClassName
). It is
essential that you call this routine before using your class. The
"use
" 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.
For the MoviePlugin example, the file "MoviePlugin.c" will be
generated containing the "
Warning: Don't generate "
If you have any other classes that you'll be using from your plugin,
you may need to call their "
You can also access the Java object associated with your plugin at any
time by calling the
When your Java
First, pointers to Java objects are dangerous. This includes
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
To create a global reference, use the
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.]
For more information, consult the Java Runtime
Interface (JRI) specification, particularly the overview, the
native method
API description and enhancements to
javah.
use
" routine
for all classes whose methods will be invoked or fields will be
accessed -- not just your Plugin
subclass that defines
native methods.use
" routine
use_MoviePlugin
:
extern java_lang_Class* use_MoviePlugin(JRIEnv* env); /* C */
java_lang_Class* MoviePlugin::_use(JRIEnv* env); /* C++ */
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. use
" 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.unuse
" routine:
extern void unuse_MoviePlugin(JRIEnv* env); /* C */
void MoviePlugin::_unuse(JRIEnv* env); /* C++ */
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 use_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.use
" routine too. Do this if
you have native methods that you're implementing for that class
(calling the "use
" will register them with the Java
runtime). Just call all the "use
" routines in your
NPP_GetJavaClass
routine, returning the one that is the
class of your plug-in. unuse
" routine so that your classes can be unloaded
when the plug-in shuts down:
NPError
NPP_Shutdown() {
JRIEnv* env = NPN_GetJavaEnv();
unuse_MoviePlugin(env);
}
You'll need to compile and link the stub file (e.g.
"MoviePlugin.c
") into your application to include the
"use
" and "unuse
" routines. 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
"use
" 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, 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. 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,
jint from, jint to)
{
...bla, bla, bla...
}
.c
file for
your class with the IMPLEMENT_ClassName
preprocessor
symbol defined. This will cause the native method stub routines to
be linked into your DLL. Without this you'll see "Unsatsified link
error" messages from Java when your Java code tries to call a native
method. Typically we define a dummy stub.c
file that
defines this symbol to prevent it from being defined globally:
/* stub.c: Compile this instead of MoviePlugin.c to get
the java stub routines pulled in. */
#define IMPLEMENT_MoviePlugin /* define symbol for stub routines */
#include "_stubs/MoviePlugin.c" /* and pull in the .c file */
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 "use
"
method and any accessor functions in DEBUG
mode.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.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++.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.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.
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.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.