Individually, Java, JavaScript, and Navigator plug-ins are powerful tools that allow developers to field network-centric solutions. Each of these tools has its own strengths and weaknesses. Netscape's LiveConnect technology allows you to put these tools together in a single, integrated application.
With LiveConnect, Netscape gives sophisticated end users a way to control complex components (plug-ins and Java applets) from a relatively simple language-JavaScript, bringing component architecture to the Web.
Microsoft's Object Linking and Embedding (OLE) technology is designed to allow users to build compound documents. With in-place activation (also known as Visual Editing) and OLE automation, the components of a compound document do more than just look good-they can be changed in place by the user or by other software. The distinction between "program" and "data" was never blurrier. This transition from a world in which end users have to think about which application they are using, to one in which end users just manipulate their data (and let the system figure out which application to use) is known as document-centric computing.
This focus on document-centric computing brings us back to the Web. Today, when a user wants to access the Web, he or she launches an application, such as Netscape. If the user wants to copy some content from a Web page to a word-processing document, he or she has to launch a word processor and copy (or drag) the contents from one window to the next. If Apple's Cyberdog is any indication, the next generation of software will enable the user to open a document, type in text content, draw graphics content, and place Web content by simply navigating to the desired Web site.
In this scenario, the embedded plug-in or Java applet becomes an agent, controlled in part by the end user (through interaction), and in part by the page into which it is embedded. When copied or dragged into a compound document, a plug-in or applet negotiates its content with this document, and either embeds its content or maintains a link through the browser back to the Internet.
Although it may take a professional programmer to write a plug-in or applet, the end user is fully able to assemble content into a compound document. So, it is the end user who builds the solution (which was once called an application), using these professionally developed components.
There is adequate precedent for the success of this approach. The designers of UNIX started with a vision of "small, sharp tools" that could be connected together as needed. Do you need to see how many copies of the HTTP daemon are running on your Web server? The UNIX command ps -ef lists all of the processes. The command grep httpd scans for the string "httpd"-the name of the Web-server software. The UNIX tool wc counts things-wc -l counts lines. The pipe symbol (|) passes the output of one program to the input of the next. So, the following line of code creates a one-time application where none existed before:
ps -ef | grep httpd | wc -l
Do you want to keep this little program around? Save this line in a file (named, say, daemonCount.ksh). Make the file an executable program by typing the following:
chmod +x daemonCount.ksh
Also, make sure that daemonCount.ksh is in the path that your computer searches when you issue commands. Now when you type "daemonCount.ksh," this little application runs and returns the number of Web-server processes. Figure 14.1 illustrates a typical document-centric, component-based environment.
Contrast this approach with, say, Microsoft Word. The flagship application of the world's largest software company has it all. Do you need an outline? It's right there on the View menu. Ready to spell-check? It's at the top of the list under Tools. Graphics? Spreadsheets? Databases? You can connect them all through Microsoft Office. Microsoft has tried to anticipate your every need. But if they haven't, until recently, you were out of luck. You can't add or replace components such as the drawing program or the outline editor-those components are "hardwired" into the application.
But only "until recently," Microsoft has more to lose
than smaller firms by encouraging component architectures because
components can be written by smaller companies, potentially carving
up part of Microsoft's pie. OLE 2 is a major step toward component
architecture, as are compound documents, especially with in-place
activation. Dynamic Link Libraries (DLLs) make it easier to add
or change functionality after a product is fielded. ActiveX seems
to be a big step toward components. Over time, perhaps driven
by market pressures from software initiatives like OpenDoc and
CommonPoint (and yes, even Netscape Navigator plug-ins and Java
applets), Microsoft will move in the direction of component architecture.
The Future of Component Architectures |
While Microsoft is certainly getting involved in component architectures, most of the initiative is coming from Microsoft's competitors. You can review some of the technologies that make component architectures possible in this and other Que books. Chapter 1 of this book contains a section entitled, "What About OpenDoc?" You can read about OLE in that same chapter in the section "Netscape ONE versus ActiveX." Chapter 4 contains more material on OLE and Opendoc in "Netscape Differences." Chapter 15, "ActiveX and Plug-Ins in Other Browsers," of Netscape Plug-Ins Developer's Kit (Que, 1996) is mostly about ActiveX, a Microsoft technology that allows small OLE automation objects to be used in Microsoft Internet Explorer and other applications. You can read more about ActiveX in Special Edition Using ActiveX (Que, 1996). Chapter 17, "Using Class Libraries and Frameworks," of Netscape Plug-Ins Developer's Kit describes how components (also known as objects) are used at the programming level. The last section in Chapter 17 describes CommonPoint, a huge collection of "small, sharp tools" in C++, ready to be used by the programmer. For the most part, the people behind the non-Microsoft solutions are all the same folks. Taligent's CommonPoint supports OpenDoc. Apple, a key member of the OpenDoc consortium, may bundle Navigator in their next release of the Mac OS, right alongside Cyberdog. The next release of Navigator may support OpenDoc on all major platforms, including Windows. Taligent, which was founded on Apple's vision of the "Pink" operating system, is now a subsidiary of IBM. As we enter the last years of this decade, lines are being drawn: Microsoft is promoting an all-Microsoft solution, with scripting done in VBScript and components added by using ActiveX controls. Microsoft's major competitors, including IBM and Apple, are promoting radical document-centric architectures, with a lot of room for OpenDoc components that are written by the smaller players. |
Figure 14.2 traces the flow of control through a monolithic application.
Although only experienced programmers are writing plug-ins and Java applets, almost anyone can learn to write JavaScript or HTML. As a programmer, you can give the user a certain amount of control over your plug-in by passing parameters through the <EMBED> tag. (You can do the same thing with Java applets, using parameters to the <APPLET> tag, but this section concentrates on plug-ins.) Recall from Chapter 11, "Step-by-Step Through the NPP Methods," that NPP_New() passes a description of the parameters in argc, argn, and argv. If the HTML author writes
<EMBED SRC="http://www.somemachine.com/myFile.tst" HEIGHT=100 WIDTH=100 AUTOSTART=True LOOP=False AUTOSCALE=Often>
a plug-in that handles the MIME media type associated with the ".tst" data stream can read the parameters HEIGHT, WIDTH, AUTOSTART, LOOP, and AUTOSCALE.
What it does with these parameters is, of course, determined by the programmer. Figure 14.3 illustrates the mapping from <EMBED> attributes to argc, argn, and argv.
Figure 14.3: The HTML author can control plug-in options through attributes of the <EMBED> tag.
As you read Chapter 11, you may have thought, "There must be a simpler way to get messages into the plug-in." You're right. The techniques described in Chapter 11 hook platform-native controls, such as menus and pushbuttons, into your plug-in. This approach has the advantage of looking familiar to the user. It also gives you a great deal of control over the appearance of your plug-in. (For example, a pop-up menu that appears in response to the user right-clicking the mouse may offer a dozen or more menu items, yet it doesn't clutter the screen when not in use.)
Sometimes, however, you will be happy to have a little HTML form with some buttons that tell the plug-in to "Start" or "Stop." Netscape provides all this flexibility, and more, through LiveConnect.
If you want the user to truly interact with the plug-in, your best approach may be to add user controls such as pop-up menus to the plug-in and send messages to the plug-in, as described in Chapter 11. If you only need to control the plug-in, you may find LiveConnect sufficient.
The distinction between "interact" and "control" is a fine line. Here's an example. If your plug-in downloads a drawing from the Web, then allows you to annotate it and post it back to the Web, you need to use native controls. For example, you probably want a pop-up menu (perhaps triggered by the right mouse button) that allows you to zoom, save, and revert.
If your plug-in is simpler, such as a video player, and you want to allow the HTML author to add buttons to the page, such as "Start" and "Stop," LiveConnect may be faster and easier to implement. You can do more complex tasks with LiveConnect. The tradeoff is between portable code (with LiveConnect) and native look and feel (with native controls and messages).
Many developers will want to use both native controls and LiveConnect. It's up to you to decide how to best design your plug-in.
LiveConnect, new with Navigator 3.0, integrates applets that are written in the platform-independent language Java (from Sun Microsystems) with Netscape's JavaScript and with your plug-ins. Figure 14.4 illustrates the communications paths between Java, JavaScript, and the plug-in.
Figure 14.4: LiveConnect is Netscape's approach to integrating Java, JavaScript, and plug-in.
Note that LiveConnect doesn't allow your plug-in to communicate directly with a script written in JavaScript. All communications go through Java. This design isn't a problem, however, as you will read in a moment.
Recall that LiveConnect doesn't give a JavaScript programmer direct access to the plug-in, or vice versa. If you're comfortable with C++ and object technology, you can write a proxy class in Java that sits between your plug-in and JavaScript, giving a JavaScript programmer access to your plug-in.
To get started in Java, see Chapter 7, "Overview of Java." To go further with Java, check out Special Edition Using Java (Que, 1996). Then read on in this section to learn about the Netscape-specific packages that you need to use to connect Java to JavaScript and to your plug-ins.
You can download the latest version of the JDK, along with the Netscape modifications, as part of the Netscape Plug-ins SDK, at http://home.netscape.com/eng/mozilla/3.0/handbook/plugins/index.html. The Netscape-specific packages are at http://home.netscape.com/eng/mozilla/3.0/handbook/plugins/wr3.htm.
You can also use commercial packages, such as Symantec's Café or Natural Intelligence's Roaster, but you'll need the classes from Netscape's version of the kit. You'll also need Netscape's special version of the javah utility (also part of the kit).
The first step in connecting a script in JavaScript to your plug-in is to connect JavaScript to Java. After that, you'll build a Java class that does its work by calling your plug-in. From the point of view of JavaScript, this Java class serves as a proxy for your plug-in.
Calling JavaScript From Java Methods In order for a Java class to talk to JavaScript, you must import the Netscape javascript package in the Java class, as follows:
import netscape.javascript.*
The netscape.javascript package, described in a subsequent section of this chapter, includes two important classes-JSObject, which represents the JavaScript object, and JSException, which is raised to pass JavaScript errors back to your Java class.
For a Java class to call JavaScript, the HTML author must explicitly set the MAYSCRIPT attribute in the APPLET tag. For example:
<APPLET NAME="hello" CODE="hello.class" WIDTH=100 HEIGHT=100 MAYSCRIPT>
Note that in this example, a name was assigned to the applet.
You will use the name when you begin talking to the applet from
JavaScript.
Caution |
If a Java applet attempts to run a page's JavaScript, and the HTML author hasn't set MAYSCRIPT, Navigator raises an exception. You should catch this exception and put up an appropriate message to the user. One good way to put up an error message is to use the Java console that is defined by Netscape. From inside Java, write the following: System.out.println("Error: Applet unable to access JavaScript. |
To access JavaScript, your Java class must get a handle to the Navigator window, as shown in Figure 14.5. Your class's init() member is a good place to do this:
Figure 14.5: Jave gets a pointer to the JavaScript document in order to access its members.
JSObject win; public void init() { win = JSObject.getWindow(this); }
If the HTML page has a form named theForm, which, in turn, has a check box named theCheckbox, you can access the status of this check box by following the membership hierarchy from the form to the check box, as follows:
JSObject win; public void init() { win = JSObject.getWindow(this); JSObject doc = (JSObject) win.getMember("document"); JSObject theForm = (JSObject) doc.getMember("theForm"); JSObject check = (JSObject) myForm.getMember("theCheckBox"); Boolean isChecked = (Boolean) check.getMember("checked"); }
Just as getMember() gives you access to the components of the JavaScript form, call() and eval() give you access to the JavaScript methods. You can write the following:
public void init() { win = JSObject.getWindow(this); } public boolean mouseUp(Event e, int x, int y) { win.eval("alert(\"Hello, world!\");"); return true; }
or, equivalently,
public void init() { win = JSObject.getWindow(this); } public boolean mouseUp(Event e, int x, int y) { win.call("alert(\"Hello, world!\");"); return true; }
Calling Java Methods from JavaScript You can communicate in the opposite direction, from JavaScript to the applet, by making sure that the applet has a name. Figure 14.6 illustrates this mechanism. Suppose that you had a Java class like count, shown in Listing 14.1.
Figure 14.6: The HTML author assigns the applet a name so that JavaScript can talk to it.
Listing 14.1 -A Simple Java Class that Accepts Two
Commands
import java.applet.*; import java.awt.*; public class count extends Applet { int i; public void init() { i = 0; } public void paint (Graphics g) { g.drawString("The count is" + i, 10, 50); } public void increment() { i++; repaint(); } public void decrement() { i-; repaint(); } }
You can install count on your Web page, as is shown in Listing 14.2.
Listing 14.2 -A Web Page with a Controllable Applet
<HTML> <HEAD> <TITLE>Test count/TITLE> </HEAD> <BODY> <H1>Test the Count Applet</H1> <APPLET NAME="count" CODE="count.class" WIDTH=100, HEIGHT=100></APPLET> <FORM> <INPUT TYPE="Button" VALUE="Increment" NAME="IncrementButton" onClick="document.count.increment()"> <INPUT TYPE="Button" VALUE="Decrement" NAME="DecrementButton" onClick="document.count.decrement()"> </FORM> </BODY> </HTML>
Now that you know how Java and JavaScript can talk, you just have to get the plug-in to talk to Java.
When you add plug-ins to an HTML page, JavaScript puts them into an array named embeds. For example, if the following is the first <EMBED> tag on your page, JavaScript shows the associated plug-in in document.embeds[0]:
<EMBED SRC="http://www.somemachine.com/myFile.tst HEIGHT=100 WIDTH=100>
From JavaScript, you can access document.embeds.length
to find out how many plug-ins are on the page.
Note |
Because full-page plug-ins are, by definition, on a page with no JavaScript (and no HTML!), it only makes sense to talk about controlling embedded plug-ins, not full-page plug-ins. |
Tip |
To increase readability, give your plug-ins names instead of referring to them as elements of the embeds array. |
To make a plug-in visible from inside Java, your Java class must use netscape.plugin.Plugin. Netscape provides a file java_30 with Netscape 3.0. This file contains three Java packages, java, sun, and netscape:
The java and sun packages are replacements to packages of the same name in the Sun 1.0.2 Java Development Kit (JDK). They include security enhancements that are necessary for LiveConnect. Netscape and Sun are working together to ensure that these new packages are included in a future release of the Sun JDK.
netscape.applet is Netscape's replacement to sun.applet. Similarly, netscape.net replaces sun.net.
netscape.javascript implements JSObject and JSException, which are described previously in this chapter.
netscape.plugin implements the Plugin
interface. As a Java programmer, you use methods of the Plugin
interface to communicate with the plug-in.
Note |
To use the Netscape-supplied packages with the JDK compiler, add the path of the java_30 and classes.zip to the compiler's classpath. You can either specify a CLASSPATH environment variable or use the -classpath command line option when you run javac. |
Tip |
As a plug-in programmer, you have a C++ development environment such as Microsoft Visual C++ handy. Don't waste time running javac from the command line. Put your plug-in's Java proxy class in the makefile, and automatically call javac each time your plug-in is rebuilt. If you use Visual C++, just add the javac command line (with the -classpath option) to the Custom Build settings. While setting up the makefile, add the call to javah described in the following section. It will save you time later. |
Calling Java Methods from the Plug-In The plug-in talks to Java through Netscape's Java Runtime Interface. Figure 14.7 illustrates the JRI.
Netscape has defined the Java Runtime Interface to allow native code (such as a plug-in) to call Java methods. The full specification of the Java Runtime Interface is available online (http:// home.netscape.com/eng/jri/). Netscape also supplies a new version of javah, named the JRI (Java Runtime Interface) version, which writes a C/C++ header file from a Java class. To control the count class previously described from your plug-in, start by typing (or including in the makefile) the following:
javah -jri -classpath pathTojava_30Andclasses.zip count
The result of running javah is a header file for class count. Recall that count has one public data member, i. javah produces in-line accessor functions
jint i(JRIEnv* env);
and
void i(JRI* env, jint);
to get and set this data member.
Note that javah has transformed the Java int
into a variable of type jint.
Table 14.1 shows the JRI definitions of the Java primitive types.
Netscape's version of javah transforms Java variables into C/C++
variables with a new Java-specific type.
Java Type | C/C++ Type | |
boolean | jbool | |
byte | jbyte | |
char | jchar | |
short | jshort | |
int | jint | |
long | jlong | |
float | jfloat | |
double | jdouble |
These sizes are defined through a series of #ifdefs
in the file jri_md.h, which is included in the header file jri.h.
Make sure that your compiler sets up the proper preprocessor symbols
for your target machine, so your code gets the right size types.
Caution |
Make sure that you use the JRI types described in Table 14.1 when talking to Java methods. If you use the compiler's types (in other words, int), you run the risk of a size mismatch when you move to a new compiler or a new platform. |
Caution |
Javah doesn't do a very good job of protecting the privacy of data members in Java classes. You will be able to access private members from inside your plug-in. Avoid this temptation and use accessor methods and other public methods exclusively. By restricting yourself to public methods, your plug-in is less likely to need maintenance when the implementation of the Java class changes. |
To call the increment() method of the Java class count, just write the following
count->increment(env);
Here, env is the result of the function NPN_GetJavaEnv(). NPN_GetJavaEnv() has the following specification:
JavaEnv* NPN_GetJavaEnv(void);
Typically, you call NPN_GetJavaEnv()
once, in NPP_Initialize().
Tip |
Netscape starts the Java Runtime Interpreter when you first call NPN_GetJavaEnv(). This first call can impose a delay on your plug-in. If you're sure that your plug-in needs to call Java, call NPN_GetJavaEnv() in NPP_Initialize() and get it out of the way. The user expects to wait a few seconds when he or she accesses the plug-in content, anyway. |
Tip |
The pointer to the Java environment is thread-specific. If you call it in NPP_Initialize(), you can use it in any instance, but if you spawn a new thread, you need to call NPN_GetJavaEnv() for this thread, and reserve the new JavaEnv pointer for use in this thread only. |
Recall that most object-oriented languages, including both C++
and Java, support overloaded methods. That is, two or more methods
can have the same name as long as they take different parameter
types. (Sets of parameters are called signatures.) In C++,
the compiler performs name mangling to make sure that the
internal names are unique. Netscape's javah appends an index to
all but the first occurrence of a name. If you have three functions
named myFunc, javah produces
myFunc, myFunc_1,
and myFunc_2. To find out
which name to call for which signature, just check the header
file output by javah. Without the use of an index on all but one
of the names, the function name myFunc
would be ambiguous. For this reason, the index is sometimes known
as a "disambiguating index."
Tip |
If you have overloaded methods, first declare the one you plan to use most frequently from your plug-in. In this way, the declared version of the method will not have an index. Similarly, if you have a Java-implemented version of a method and a native (such as C or C++) version of the same method, put the declaration of the native method first. In this way, you don't have to worry about index names when you write the native implementation. |
Tip |
Because javah uses the underscore followed by a number to disambiguate overloaded methods, it performs name-mangling on Java methods that contain an underscore in their name. Save yourself a headache-don't use underscores in Java method names. |
Calling the Plug-In from Java Methods You can define native methods in Java that are implemented in C or C++. These methods give your Java applet access to low-level library routines in the operating system and also can be more efficient than Java alone. Figure 14.8 illustrates a Java class calling its native method.
Figure 14.8: Java invokes a native method, allowing access to the operating system.
Just as calls to Java by the plug-in are made through the C or C++ header files produced by javah, Java methods call the plug-in by calling a Java peer of the plug-in. This peer is of type netscape.plugin.Plugin.
If you instantiate a Java object of type Plugin, your Java method will be able to refer to the plug-in, but will not be able to call any plug-in specific methods. If you derive a new Java class from Plugin, you can define native methods in the derived class that correspond to these plug-in methods you want to expose to Java.
Suppose that you want to write a plug-in that does the same work as count, the Java class shown previously. The new plug-in will be named npCount. For convenience, call the Java peer class npCount as well. Besides the increment() and decrement() methods defined for count, npCount has a new method, post(), which calls NPN_PostURL() and sends the current count back to a CGI script or LiveWire application on the Web site. The file shown in Listing 14.3 implements npCount.
Listing 14.3 -A Java Peer Class For the npCount
Plug-in
import netscape.plugin.Plugin; class npCount extends Plugin { public native void increment(); public native void decrement(); public native boolean post(); }
Compile npCount.java with javac, and run the JRI version of javah on it, as follows:
javah -jri -classpath pathTonpcount npCount javah -jri -stubs -classpath pathTonpcount npCount
The second call to javah builds C stubs for the native methods. These stubs include a special initializer for each Java class. The initializer sets up the interface between Java and your plug-in. Be sure to call the initializer before you use the class! (If you have other Java classes with native methods outside of your plug-in, call their initializers, too.)
For npCount, the initializer is specified by the following line:
extern java_lang_Class* init_npCount(JRIEnv* env);
You can add initialization code to this init_ method. You can add balancing code to the Java peer's destroy() method. This method is called just before NPP_Destroy() is called on the instance. You also can check (from Java) to see if the native instance is still active by calling isActive(), which returns a boolean.
For Navigator to set up your Java peer class, it calls your plug-in's implementation of NPP_GetJavaClass(). NPP_GetJavaClass() is specified as follows:
jref NPP_GetJavaClass(void);
For npCount, a reasonable implementation of NPP_GetJavaClass() is:
jref NPP_GetJavaClass() { return init_npCount(NPN_GetJavaEnv()); }
If your plug-in uses other Java classes that have native methods, call their initializers before you return to register their native methods with the JRI.
Make sure that you add the stub file (here, npCount.c) into your
project so that it will be compiled and linked into the plug-in.
Tip |
You should always include an implementation of NPP_GetJavaClass() in your plug-in, even if you don't plan to use it to connect to Java. Just write a stub implementation that returns NULL. |
Follow the javah naming conventions (rather than C++ name mangling) when implementing the native methods. Therefore, you might write the following:
jbool native_npCount_post( JRIEnv* env, npCount* self, JRIMethodThunk* methodThunk, other parameters );
You can use the self parameter
to access any other class members. The other
parameters in the last line are the parameters that
you declared for this method in your Java class. The env
parameter is supplied by the runtime in case you need to make
calls back to Java. You can ignore the methodThunk
parameter.
Tip |
Check the documentation that comes with your version of the Netscape LiveWire/Plugins SDK. The methodThunk parameter is a holdover from an older design, and is scheduled to go away. You may not need to pass it in your plug-in. |
From the native method, you can access the plug-in instance by writing the following:
NPP npp = (NPP)self->getPeer(env);
From the plug-in, you can access the Java peer by calling NPN_GetJavaPeer(). The prototype of that function is:
jref NPN_GetJavaPeer(NPP instance);
If your plug-in is embedded on a page with JavaScript, the HTML author can activate the plug-in by calling it through the embeds interface. The first time JavaScript needs the Java peer object, Navigator makes an internal call to NPN_GetJavaPeer(), which instantiates the Java peer object. As part of NPN_GetJavaPeer(), Navigator calls the init_ method.
JavaScript then actually communicates to the Java peer object, which does its work through the native methods.
Figure 14.9 shows the flow of control from JavaScript through npCount and back again.
Figure 14.9: The npCount plug-in communicates both to and from JavaScript.
When the plug-in is loaded, it reads the current count from the Internet-through NPP_Write()-and writes it to the window-through NPP_SetWindow(). When Navigator calls NPP_GetJavaPeer(), npCount returns its peer object, which is made available to JavaScript through embeds (or by an explicit name in the <EMBED> tag). When the user clicks a button, Navigator calls the method on the Java peer object pointed to by embeds[0] (or the named plug-in). If the method is add or decrease, the peer object talks to JavaScript to read the contents of the MoveBy field. All the remaining work of the Java class is done back in the plug-in. The plug-in runs the native method and calls InvalidateRect() to trigger an update of its window.
After a plug-in and its peer Java object have been set up, they can be called by an HTML author-who doesn't have to be a professional programmer. By building a few well-written general-purpose plug-ins, programmers allow HTML authors and other users to access the Web in powerful and sophisticated ways. As the demand for quality software continues to outstrip the supply of talented programmers, the use of component architectures such as LiveConnect plug-ins will continue to be important.
You can learn more about LiveConnect in the LiveConnect/Plug-in
SDK, on the Netscape ONE site.
ON THE WEB |
http://home.netscape.com/comprod/development_partners/plugin_api/index.html This page contains links to the platform-specific versions of the LiveWire/Plug-ins SDK, as well as documentation on the API. LiveWire technology relies on the Java Runtime Interface (JRI). Netscape documents the JRI online at http://home.netscape.com/eng/jri/. |