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:
netscape.net
is a replacement for the Sun JDK package sun.net
.
java
and sun
packages replace packages in the Sun 1.1 Java Development Kit (JDK) classes.zip
.
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
int
, float
, and bool
data types correspond to JavaScript data types of the same name.
toString
method on the original object to be called. Converting to a number causes the floatValue
method to be called, if possible, and fails otherwise. Similarly, converting to a Boolean value causes the booleanValue
method to be called, if possible. If you pass the wrapper back to Java as an argument to a method or the value of a property, the runtime engine unwraps it to the original Java object.
String
objects also correspond to JavaScript wrappers. If you call a JavaScript method that requires a JavaScript string and pass it this wrapper, you'll get an error. Instead, convert the wrapper to a JavaScript string by appending the empty string to it, as shown here:var JavaString = JavaObj.methodThatReturnsAString();
var JavaScriptString = JavaString + "";
String
objects. When you pass a JavaScript string to Java, it is converted to a Java String
object. When passed by to JavaScript, the String
object is converted back to a JavaScript string.PackagesIn JavaScript, to refer to a Java class named javaClassName, where javaClassName is either a simple or a qualified class name, use the following syntax:
sun
netscape
java
Packages.javaClassNameTo refer to the constructors, static methods, or static properties of this class, use this syntax:
Packages.javaClasName(arguments)For example, consider the qualified Java class name
Packages.javaClasName.staticMethod(arguments)
Packages.javaClasName.staticProperty
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.SystemYou 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
java.lang.System
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(By default,
parseInt(request.bugId),
request.bugPriority,
request);
$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.javaClassNameHere, javaClassName is a simple (non-qualified) class name.
$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.
The steps in this code are:
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>");
}
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".
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".
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.
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.
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.
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();
} }
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
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:
flexi
. These implement the CORBA client and service.
Figure 6.3 The
flexi
sample application
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:
...
In these variables,
$NSHOME\js\samples\flexi
$NSHOME\wai\java\nisb.zip
$JDK\lib\classes.zip $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
You should see a message such as the following:
java FlexiServer 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:
flexi
.Table 6.2 Flexi files and directories
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) {
This code initializes the ORB and creates an instance of the
System.err.println(e);
}
}
}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(
This code calls the
parseFloat(request.claimAmount),
request.serviceDate,
request.providerName,
request.details);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:
Figure 6.4 A JavaScript application as a CORBA server
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.
Last Updated: 10/30/97 12:19:25