[Contents] [Previous] [Next] [Index]

Chapter 6
Working with Java and CORBA Objects Through LiveConnect

This chapter describes using LiveConnect to connect your server-side JavaScript application to Java components or classes on the server. Through Java you can connect to CORBA-compliant distributed objects using Netscape Internet Service Broker for Java.

Sections in this chapter:

Your JavaScript application may want to communicate with code written in other languages, such as Java or C. To communicate with Java code, you use JavaScript's LiveConnect functionality. To communicate with code written in other languages, you have several choices:

For information on including external libraries, see "Working with External Libraries". This chapter discusses using LiveConnect to access non-JavaScript code from JavaScript applications.

Ultimately, LiveConnect allows the JavaScript objects in your application to interact with Java objects. These Java objects are instances of classes on the server's CLASSPATH. See "Setting Up for LiveConnect" for information on setting CLASSPATH appropriately. LiveConnect works for both client-side and server-side JavaScript but has different capabilities appropriate to each environment.

If you have a CORBA service and you have the IDL for it, you can generate Java stubs. The Java stubs can then be accessed from JavaScript using LiveConnect, thus giving you access to your service from JavaScript. For the most part, connecting to CORBA services in this way is just like accessing any other Java code. For this reason, this chapter first talks about using LiveConnect to communicate between Java and JavaScript. Later, it describes what you need to do to access CORBA services.

This chapter assumes you are familiar with Java programming. For information on using LiveConnect with client-side JavaScript, see the JavaScript Guide. For information on using Java with Netscape servers, see Enterprise Server 3.0: Notes for Java Programmers. For other information on LiveConnect, see the DevEdge Library.

For all available Java classes, you can access static public properties or methods of the class, or create instances of the class and access public properties and methods of those instances. Unlike on the client, however, you can access only those Java objects that were created by your application or created by another JavaScript application and then stored as a property of the server object.

If a Java object was created by a server application other than a server-side JavaScript application, you cannot access that Java object. For example, you cannot access a Java object created by a WAI plug-in, NSAPI extension, or an HTTP applet.

When you call a method of a Java object, you can pass JavaScript objects to that method. Java code can set properties and call methods of those JavaScript objects. In this way, you can have both JavaScript code that calls Java code and Java code that calls JavaScript code.

Java code can access a JavaScript application only in this fashion. That is, a Java object cannot invoke a JavaScript application unless that JavaScript application (or another JavaScript application) has itself accessed an appropriate Java object and invoked one of its methods.

Predefined Java Classes

Netscape servers include a file of Java packages called serv3_0.zip. You can access these packages in your JavaScript application. These Java included packages can be used with JavaScript:

The netscape.javascript package is documented in the JavaScript Guide. The netscape.net package is not documented because it is implemented in the same way as the original Sun package.

Data Type Conversion

When JavaScript code calls Java or Java code calls JavaScript, the JavaScript runtime engine converts argument values into the appropriate data types for the other language. It performs the same conversions on the server as it does on the client. Figure 6.1 illustrates the data-type conversions.

Figure 6.1    Data-type conversion between JavaScript and Java

These conversions are as follows:

Calling Java from JavaScript

To access Java code from JavaScript, you either access a static method or property of the Java class or you create an instance of the Java class and then access its methods or properties. If a method of a Java object requires another Java object as a parameter, you must first get access to that Java object in your JavaScript environment. You can do so, for example, by creating the Java object or by having it returned by a Java method.

Referring to a Java Object in JavaScript

JavaScript has the following predefined top-level objects to provide access to Java packages from within JavaScript:

Packages
sun
netscape
java
In JavaScript, to refer to a Java class named javaClassName, where javaClassName is either a simple or a qualified class name, use the following syntax:

Packages.javaClassName
To refer to the constructors, static methods, or static properties of this class, use this syntax:

Packages.javaClasName(arguments)
Packages.javaClasName.staticMethod(arguments)
Packages.javaClasName.staticProperty
For example, consider the qualified Java class name bugbase.Bug. To access its constructor, use:

new Packages.bugbase.Bug(arguments);
If you access a class that belongs directly or indirectly to the java, sun, or netscape package, then the Packages prefix is optional. For example, you can refer to the java.lang.System class in either of the following ways:

Packages.java.lang.System
java.lang.System
You access constructors, fields, and methods in a class with the same syntax that you use in Java. For example, the following JavaScript code uses properties of the request object to create a new instance of the Bug class and then assigns that new instance to the JavaScript variable bug. Because the Java class requires an integer for its first field, this code first converts the request string property to an integer before passing it to the constructor.

var bug = new Packages.bugbase.Bug(
   parseInt(request.bugId),
   request.bugPriority,
   request);
By default, $NSHOME\js\samples directory, where $NSHOME is the directory in which the server was installed, is on the server's CLASSPATH. You can put your packages in this directory. Alternatively, you can choose to put your Java packages and classes in any other directory. If you do so, make sure the directory is on your CLASSPATH. For example, assume you have a Java class called MyClass in a package called MyPkg in the \MyDir directory. In this case, you would include \MyDir on your CLASSPATH and refer to the package as MyPkg.MyClass.

You can also access packages in the default package (that is, classes that don't explicitly name a package). Directories containing classes in the default package must be on the CLASSPATH. To refer to these classes, use this syntax:

Packages.javaClassName
Here, javaClassName is a simple (non-qualified) class name.

Example of JavaScript Calling Java

The $NSHOME\js\samples\bugbase directory includes a simple application illustrating the use of LiveConnect. This section describes the JavaScript code in that sample application. See "Example of Java Calling JavaScript" for a description of this application's Java code.

The bugbase application represents a simple bug database. You enter a bug by filling in a client-side form with the bug number, priority, affected product, and a short description. Another form allows you to view an existing bug.

The following JavaScript processes the enter action:

// Step 1. Verify that ID was entered.
if (request.bugId != "") {
   // Step 2. Create Bug instance and assign to variable.
   var bug = new Packages.bugbase.Bug(parseInt(request.bugId),
      request.bugPriority, request);
   // Step 3. Get access to shared array and store instance there.
   project.bugsLock.lock();
   project.bugs[parseInt(request.bugId)] = bug;
   project.bugsLock.unlock();
   // Step 4. Display information.
   write("<P><b><I>====>Committed bug: </I></b>");
   write(bug, "<BR>");
}
// Step 5. If no ID was entered, alert user.
else {
   write("<P><b><I>====>Couldn't commit bug: please complete
      all fields.</I></b>");
}
The steps in this code are:

  1. Verify that the user entered an ID for the bug. Enter the bug only in this case.

  2. Create an instance of the Java class Bug, and assign that instance to the bug variable. The Bug class constructor takes three parameters: two of them are properties of the request object; the third is the JavaScript request object itself. Because they are form elements, these request properties are both JavaScript strings. The code changes the ID to an integer before passing it to the Java constructor. Having passed the request object to the Java constructor, that constructor can then call its methods. This process is discussed in "Example of Java Calling JavaScript".

  3. Use project.bugsLock to get exclusive access to the shared project.bugs array and then store the new Bug instance in that array, indexed by the bug number specified in the form. Notice that this code stores a Java object reference as the value of a property of a JavaScript object. For information on locking, see "Sharing Objects Safely with Locking".

  4. Display information to the client about the bug you have just stored.

  5. If no bug ID was entered, display a message indicating that the bug couldn't be entered in the database.

Calling JavaScript from Java

For a Java method to access server-side JavaScript objects, it must have been called from a server-side JavaScript application. In client-side JavaScript, Java can initiate an interaction with JavaScript. On the server, Java cannot initiate this interaction.

NOTE: When you recompile a Java class that is used in a JavaScript application, the new definition may not take effect immediately. If any JavaScript application running on the web server has a live reference to an object created from the old class definition, all applications continue to use the old definition. For this reason, when you recompile a Java class, you should restart any JavaScript applications that accesses that class.

Referring to a JavaScript Object in Java

If you want to use JavaScript objects in Java, you must import the netscape.javascript package into your Java file. This package defines the JSObject and JSException classes to allow your Java code to access JavaScript methods and properties and to handle errors returned by the JavaScript code.

The JSObject and JSException classes are described in the JavaScript Guide. Methods of these classes work the same on the client and on the server, with one exception: the GetWindow method of the JSObject class is not available on the server.

When you call a Java method, you can pass a JavaScript object as one of its arguments. To do so, you must define the corresponding formal parameter of the method to be of type JSObject. Also, any time you use JavaScript objects in your Java code, you should put the call to the JavaScript object inside a JSException wrapper. This allows your Java code to handle errors in JavaScript code execution which appear in Java as exceptions of type JSException.

Threading

Java allows you to create separate threads of execution. You need to be careful using this feature when your Java code interacts with JavaScript code.

Every server-side JavaScript request is processed in a thread known as the request thread. This request thread is associated with state information such as the JavaScript context being used to process the request, the HTTP request information, and the HTTP response buffer.

When you call Java code from a JavaScript application, that Java code runs in the same request thread as the original JavaScript application. The Java code in that thread can interact with the JavaScript application and be guaranteed that the environment is as it expects. In particular, it can rely on the associated state information.

However, you can create a new thread from your Java code. If you do, that new thread cannot interact with the JavaScript application and cannot rely on the state information associated with the original request thread. If it attempts to do so, the behavior is undefined. For example, a Java thread you create cannot initiate any execution of JavaScript code using JSObject, nor can it use writeHttpOutput, because this method requires access to the HTTP response buffer.

Example of Java Calling JavaScript

The $NSHOME\js\samples\bugbase directory includes a simple application that illustrates the use of LiveConnect. This section describes the sample application's Java code. See "Example of JavaScript Calling Java" for a description of the basic workings of this application and of its JavaScript code.

// Step 1. Import the needed Java objects.
package Bugbase;
import netscape.javascript.*;
import netscape.server.serverenv.*;
// Step 2. Create the Bug class.
public class Bug {
   int id;
   String priority;
   String product;
   String description;
   String submitter;
   // Step 3. Define the class constructor.
   public Bug(int id, String priority, JSObject req)
   throws java.io.IOException
   {
      // write part of http response
      NetscapeServerEnv.writeHttpOutput("Java constructor: Creating
         a new bug.<br>");
      this.id = id;
      this.priority = priority;
      this.product = (String)req.getMember("bugProduct");
      this.description = (String)req.getMember("bugDesc");
   }
   // Step 4. Return a string representation of the object.
   public String toString()
   {
      StringBuffer result = new StringBuffer();
      result.append("\r\nId = " + this.id
         + "; \r\nPriority = " + this.priority
         + "; \r\nProduct = " + this.product
         + "; \r\nDescription = " + this.description);
      return result.toString();
   }   }
Most of the steps in this code are not specific to communicating with JavaScript. It is only in steps 1 and 3 that JavaScript is relevant.

  1. Specify the package being used in this file and import the netscape.javascript and netscape.server.serverenv packages. If you omit this step, you cannot use JavaScript objects.

  2. Create the Java Bug class, specifying its fields.

  3. Define the constructor for this class. This constructor takes three parameters: an integer, a string, and an object of type JSObject. This final parameter is the representation of a JavaScript object in Java. Through the methods of this object, the constructor can access properties and call methods of the JavaScript object. In this case, it uses the getMember method of JSObject to get property values from the JavaScript object. Also, this method uses the writeHttpOutput method of the predefined NetscapeServerEnv object (from the netscape.server.serverenv package) to print information during object construction. This method writes a byte array to the same output stream used by the JavaScript write function.

  4. Define the toString method. This is a standard method for a Java object that returns a string representation of the fields of the object.

Accessing CORBA Services

Netscape Internet Service Broker for Java (ISB for Java) is Netscape's object request broker. ISB for Java communicates with itself and with other object request brokers (ORBs) using the Internet InterORB Protocol (IIOP).

ISB for Java enables your JavaScript application to access CORBA-compliant distributed objects deployed in an IIOP-capable ORB (including ISB for Java itself). These objects may be part of a distributed application. To access such a distributed object, you must have a Java stub, and that stub class must be on your CLASSPATH. Conversely, you can use Java and LiveConnect to expose parts of your server-side JavaScript application as CORBA-compliant distributed objects.

It is beyond the scope of this manual to tell you how to create CORBA-compliant distributed objects using ISB for Java or how to make Java stubs for such objects. For this information, see the Netscape Internet Service Broker for Java Programmer's Guide.

Server-side JavaScript applications can access a distributed object regardless of how it is deployed. The simplest alternative to consider is that the distributed object is created and run as a separate process, as illustrated in Figure 6.2.

Figure 6.2    A JavaScript application as a CORBA client

As shown in this illustration, the Java and JavaScript runtime environments are together in the same web server. They communicate using LiveConnect in the standard way described earlier in this chapter. Methods called on the stub wrapper in JavaScript result in method calls on the Java stub object in Java. The stub uses the Java ORB to communicate with the remote service. With this architecture, the object server process can be on any machine that has an ORB and can be written in any language.

The flexi sample application illustrates this. In this sample, FlexiServer is a stand-alone Java application that has implementations of a number of distributed objects. This example is discussed in "Flexi Sample Application".

After you have worked with flexi, read "Deployment Alternatives" for a discussion of more complicated deployment alternatives.

Flexi Sample Application

The flexi sample application illustrates using server-side JavaScript to access remote services running on an IIOP-enabled ORB and also illustrates a remote service written entirely in Java using ISB for Java. Both the source files and the application executables for the flexi sample application are installed in the $NSHOME\js\samples\flexi directory.

A flexible spending account (FSA) is an account in which employees may deposit pretax dollars to be used for medical expenses. Employees typically elect to sign up for this plan with the administrator of the plan and select a dollar amount that they want deposited into their account. When an employee incurs a medical expense, the employee submits a claim which, if approved, results in a withdrawal from the account and the remittance of the approved amount to the employee.

The flexi sample application provides support for managing flexible spending accounts. With this application, an administrator has these options:

The employee has these options:

CORBA Client and Server Processes

Figure 6.3 shows the two primary parts of flexi. These implement the CORBA client and service.

Figure 6.3    The flexi sample application

The CORBA client is the server-side JavaScript application known as flexi. This application implements the administrator and employee user interfaces described earlier. This application connects with the FSA-Admin object (described next) in a separate process or even on a separate machine. The application then uses that object, and other objects returned by FSA-Admin, to perform most of its operations.

The CORBA server is a stand-alone Java application run from the shell. It contains implementations for all the interfaces defined in the IDL file Flexi.idl. This stand-alone application, called FlexiServer, implements the primary functionality of the FSA system. Upon startup, this application creates an instance of an object implementing the interface ::FSA::Admin and registers it with the name "FSA-Admin." Clients of this service (such as the flexi JavaScript application) obtain access to this object first by resolving its name. Clients use this object to create other objects and to get remote references to them.

Starting FlexiServer

FlexiServer is a stand-alone Java application. You can run it on any machine that has JDK 1.0.2. In Enterprise Server 3.01 and FastTrack Server 3.01, you can also run it on a machine that has JDK 1.1.2. Before running FlexiServer, you need to ensure that your environment is correct.

From the shell where you're going to start FlexiServer, make sure that your PATH environment variable includes $JDK\bin and that CLASSPATH includes the following:

... 
$NSHOME\js\samples\flexi
$NSHOME\wai\java\nisb.zip
$JDK\lib\classes.zip
In these variables, $JDK is the directory in which the JDK is installed and $NSHOME is the directory in which your web server is installed.

Once the environment is correct, you can start FlexiServer as follows:

cd $NSHOME\js\samples\flexi\impl 
java FlexiServer
You should see a message such as the following:

Started FSA Admin: Admin[Server,oid=PersistentId[repId=IDL:Flexi/Admin:1.0,objectName=FSA-Admin]]
At this point, FlexiServer has started as a CORBA service and registered with the ORB an object with interface ::FSA::Admin and name FSA-Admin. FlexiServer runs in the background, waiting for service requests.

Starting Flexi

You must start FlexiServer before you start flexi, because flexi's start page attempts to connect to FlexiServer.

Add $NSHOME\js\samples\flexi to the CLASSPATH for your web server. For information on how to do so, see "Setting Up for LiveConnect".

Using the Application Manager, install the flexi JavaScript application as described in "Installing a New Application". The parameters you set for flexi are shown in Table 6.1.

Table 6.1 Flexi application settings  
Setting Value
Name

flexi
Web File Path

$NSHOME\js\samples\flexi\flexi.web
Default Page

fsa.html
Initial Page

start.html
Client Object Maintenance

client-cookie

Using Flexi

To start flexi, you can run it from the Application Manager or enter the following URL:

http://server-name/flexi
The default page lets the user be identified as an administrator or an employee. To get a quick feel for the application, follow this scenario:

  1. Administrator creates an account for a user with a certain balance.

  2. Employee selects the account.

  3. Employee submits a claim.

  4. Administrator selects employee's account.

  5. Administrator accepts claim, which results in a reduction in the employee's account balance and a remittance of a check for the claim amount.

  6. Employee selects the account.

  7. Employee views the status of the account.

  8. Administrator selects employee's account.

  9. Administrator deletes claim.
The system can handle only one claim per employee at any time. Once the claim has been deleted, a new claim may be submitted.

Looking at the Source Files

Table 6.2 shows the primary files and directories for flexi.

Table 6.2 Flexi files and directories  
flexi.idl
File defining the interface to the remote service, including Admin, Account, Claim.

Flexi\
Directory containing code generated from Flexi.idl by the idl2java program. This directory includes the skeletons and stubs for the interfaces.

impl\
Directory containing implementations in Java for all the interfaces defined in Flexi.idl. It also contains the class FlexiServer which implements the main program for the Java application that runs the service.

*.html
Files implementing the server-side JavaScript application. It also includes the application's web file, flexi.web.

Browse through these files to get a clear understanding of this application. Only a few highlights are discussed here.

Setting Up FlexiServer as a CORBA Server: The main routine of the stand-alone Java application is implemented in flexi\impl\FlexiServer.java. Its code is as follows:

import org.omg.CORBA.*;
class FlexiServer {
   public static void main(String[] args) {
   try {
      // Initialize the orb and boa.
      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
      org.omg.CORBA.BOA boa = orb.BOA_init();
      // Create the server object.
      Admin __admin = new Admin();
      // Inform boa that the server object is ready.
      boa.obj_is_ready(__admin);
      // Register the name of the object with the name service.
      // First, determine the name service host;
      // by default use <localhost>:80.
      String _nameServiceHost = null;
      if (args.length > 0) {
         // Assume the first arg is the hostname of the name
         // service host. Expected format: <hostname>:<port>
         _nameServiceHost = args[0];
      }
      else {
         String _localHostName = null;
         try {
            _localHostName=
               java.net.InetAddress.getLocalHost().getHostName();
            _nameServiceHost = _localHostName + ":80";
         }
         catch (java.net.UnknownHostException e) {
            System.out.println("Couldn't determine local host;
            can't register name.");
         }
      }
      String _regURL = "http://" + _nameServiceHost + "/FSA-Admin";
      System.out.println("Registering Admin object at URL: " + _regURL);
      // Register the server object.
      netscape.WAI.Naming.register(_regURL, __admin);
      System.out.println("Started FSA Admin: " + __admin);
      boa.impl_is_ready();
   }
   catch (org.omg.CORBA.SystemException e) {
      System.err.println(e);
      }
   }
}
This code initializes the ORB and creates an instance of the Admin class. It then registers the instance as a distributed object, with a URL of the form http://host:port/FSA-Admin. By default, host is the name of the host on which FlexiServer is run and port is 80. You can supply your own value for host:port by passing it as an argument to FlexiServer when you start it. To use the local host but a different port number, you need to change the sample code and recompile. Once the code has an appropriate name, it registers the object using the register method of the netscape.WAI.Naming object. For more information, see Netscape Internet Service Broker for Java Reference Guide.

Finally, if successful the code prints a message to the console and then waits for requests from CORBA clients. In this case, the only CORBA client that knows about it is the flexi JavaScript application.

Setting up flexi as a CORBA client: The file start.html is the initial page of the JavaScript flexi application. This page uses LiveConnect to initialize ISB for Java and establish the connection to FSA-Admin.

<server>
// Initialize the orb.
project.orb = Packages.org.omg.CORBA.ORB.init();
// Establish connection to the "FSA-Admin" service.
// By default, assume name service is running on this server.
nameHost = "http://" + server.hostname;
serviceName = "/FSA-Admin";
serviceURL = nameHost + serviceName;
// Resolve name and obtain reference to Admin stub.
project.fsa_admin = Packages.Flexi.AdminHelper.narrow(
   netscape.WAI.Naming.resolve(serviceURL));
</server>
The first statement initializes ISB for Java by calling the static init method of the Java class org.omg.CORBA.ORB. It stores the returned object as a property on the project object, so that it lasts for the entire application.

The second set of statements determine the URL that was used to register the FSA-Admin object. If you used a different URL when you registered this object (as described in the last section), you need to make appropriate changes to these statements. The URL used in the CORBA server must be exactly the same as the URL used in the CORBA client.

The code then calls the resolve method of the netscape.WAI.Naming object to establish the connection to the Admin object that was registered by FlexiServer as FSA-Admin. Finally, it calls the narrow method of AdminHelper to cast the returned object to the appropriate Java object type. That Java method returns a Java object corresponding to the distributed object. The JavaScript runtime engine wraps the Java object as a JavaScript object and then stores that object as a property on the project object. At this point, you can call methods and access properties of that returned object as you would any other Java object. The other pages in flexi work through this object.

Once again, for more details on how the CORBA objects work, see Netscape Internet Service Broker for Java Reference Guide.

Using the Admin Object to Administer and View Accounts: Other code in flexi creates and then accesses objects in FlexiServer other than the Admin object. These other objects are created by calls to methods of the Admin object. For example, if the employee chooses to submit a claim, a new claim is created in the account-empl.html with the following statement:

__claim = __account.submitClaim(
   parseFloat(request.claimAmount),
   request.serviceDate,
   request.providerName,
   request.details);
This code calls the submitClaim method of the Account object to create a new employee claim. The implementation of that method, in the file impl\Account.java, creates a new Claim object, which the code registers with the ORB and then returns, as follows:

public Flexi.Claim submitClaim(float amount, String serviceDate,
   String providerName, String details)
{
   Claim __clm = new Claim(this, amount, serviceDate,
      providerName, details);
   org.omg.CORBA.ORB.init().BOA_init().obj_is_ready(__clm);
   _current_clm = __clm;
   System.out.println("***Created a new claim: " + __clm);
   return __clm;
};

Deployment Alternatives

There are two other alternatives for deployment of a CORBA-compliant distributed object that are of interest when working with server-side JavaScript:

In these alternatives, the CORBA client and the CORBA server both run in the same web server process.

From the point of view of JavaScript, if the CORBA client is not a JavaScript application, the first alternative is for all practical purposes the same as having the CORBA server run as a separate process.

However, the second alternative, creating a distributed object in a JavaScript application, in effect makes that application the CORBA service. Figure 6.4 illustrates this alternative.

Figure 6.4    A JavaScript application as a CORBA server

Once again, the Java and JavaScript runtime environments are together in the same web server. They communicate using LiveConnect in the standard way described earlier in this chapter. In this case, however, the Java and JavaScript processes act together to be the CORBA service. This service then communicates with a CORBA client through ISB for Java in its standard way. The bank sample application is an example of a JavaScript application implementing a CORBA service.

Here, the CORBA client can be on any machine that has an IIOP-capable ORB and can be written in any language. One interesting possibility is that the CORBA client can be a client-side Java application (and through LiveConnect on the client, a client-side JavaScript application). This provides a completely different way for a client-side JavaScript application to communicate with a server-side JavaScript application.


[Contents] [Previous] [Next] [Index]

Last Updated: 10/30/97 12:19:25


Copyright © 1997 Netscape Communications Corporation