В этой главе рассматривается использование технологии LiveConnect для взаимодействия кодов, написанных на Java и JavaScript. Предполагается, что Вы уже знакомы с программированием на Java.
В главе имеются следующие разделы:
Дополнительно об использовании LiveConnect см. JavaScript technical notes на сайте DevEdge, а также ищите соответствующую информацию на сайтах mozilla.org и developer.netscape.com.
LiveConnect даёт возможность подключать приложения серверного JavaScript к Java-компонентам и классам на сервере.
Вашему приложению JavaScript может понадобиться соединиться с кодом, написанным на других языках, таких как Java или C. Для подключения к Java-коду Вы используете функциональность LiveConnect. Для взаимодействия с кодом, написанным на других языках, у Вас есть несколько вариантов:
В этой главе обсуждается использование LiveConnect для доступа к не-JavaScript коду из приложений JavaScript.
Наконец, LiveConnect даёт возможность объектам JavaScript
Вашего приложения взаимодействовать с Java-объектами. Эти Java-объекты являются экземплярами классов в серверном
CLASSPATH
. Об установке CLASSPATH
см. книгу
Programmer's Guide to Servlets
. LiveConnect работает и с клиентским,
и серверным JavaScript, но имеет для каждой среды разные возможности.
Предполагается, что Вы уже знакомы с программированием на Java. Об использовании Java с Netscape-серверами см. Enterprise Server 3.5.1/3.6: Notes for Java Programmers 1 . О LiveConnect см. DevEdge Library 2 .
Во всех доступных Java-классах Вы можете иметь доступ к статичным public-свойствам
или методам или создавать экземпляры класса и получать доступ к public-свойствам и методам этих экземпляров.
В отличие от клиентского, однако, Вы можете иметь доступ только к тем Java-объектам,
которые были созданы Вашим приложением или другим приложением JavaScript, а
затем сохранены как свойство объекта
server
.
Если Java-объект был создан серверным приложением,
отличным от приложения на серверном JavaScript, Вы не можете иметь доступ к
этому Java-объекту.
Например, Вы не можете получить доступ к Java-объекту, созданному WAI plug-in,
расширением NSAPI или HTTP-аплетом.
Когда Вы вызываете метод Java-объекта, Вы можете передать объекты JavaScript в этот метод. Код Java может установить свойства и вызвать методы этих объектов JavaScript. Таким образом, Вы можете иметь и код JavaScript, вызывающий код Java, и код Java, вызывающий код JavaScript.
Код Java может иметь доступ к приложению JavaScript только таким способом. То есть Java-объект не может вызывать приложение JavaScript, если это приложение JavaScript (или другое приложение JavaScript) не имеет само доступа к соответствующему Java-объекту и не вызывает один из его методов.
В JavaScript wrapper\оболочка это объект типа данных целевого языка,
который содержит в себе объект исходного языка. На стороне JavaScript Вы можете
использовать объект-оболочку для доступа к методам и полям Java-объекта; вызывая
метод или получая доступ к свойству в оболочке даёт в результате вызов Java-объекта.
На стороне Java объекты JavaScript оборачиваются в экземпляры класса
netscape.javascript.JSObject
и передаются в Java.
Когда JavaScript-объект высылается в Java, машина выполнения создаёт Java-оболочку типа
JSObject
; когда JSObject
высылается из Java в JavaScript,
машина выполнения разворачивает его в объект оригинального типа JavaScript.
Класс JSObject
предоставляет интерфейс для вызова методов JavaScript и проверки свойств JavaScript.
Когда Вы обращаетесь к пакету или классу Java или работаете с объектом или массивом Java, Вы используете один из специальных объектов LiveConnect. Доступ JavaScript к Java имеет место в объектах, резюме по которым дано в следующей таблице.
Объект | Описание |
---|---|
Обёрнутый/wrapped массив Java, доступ к которому выполняется в коде JavaScript. | |
Обёрнутый объект Java, доступ к которому выполняется в коде JavaScript. | |
ПРИМЕЧАНИЕ:
Поскольку Java является строго типизированным, а JavaScript -слабо типизированным языком, машина выполнения JavaScript конвертирует значения аргументов в соответствующие типы данных тех языков, которые Вы используете с LiveConnect. См. "Конвертация Типов Данных".
Иногда существование объекта LiveConnect прозрачно,
поскольку Вы работаете с Java интуитивно. Например, Вы можете создать в Java
String
-объект и присвоить его переменной JavaScript myString
с помощью оператора new
в конструкторе Java таким образом:
var myString = new java.lang.String("Hello world")
В предыдущем примере переменная myString
это JavaObject
,
потому что она содержит экземпляр Java String
-объекта. Как JavaObject
,
myString
имеет доступ к методам public-экземпляра java.lang.String
и его суперкласса java.lang.Object
. Эти методы Java доступны в JavaScript
как методы JavaObject
, и Вы можете вызывать их так:
myString.length() // возвращает 11
Вы имеете доступ к конструкторам, полям и методам класса через тот же синтаксис,
который используете в Java.
Например, следующий код JavaScript использует свойства объекта request
для создания нового экземпляра класса Bug
и присвоения затем этого
нового экземпляра переменной JavaScript bug
. Поскольку Java-класс
требует целочисленного значения для первого поля, этот код конвертирует
строковое свойство объекта request
к целому числу, перед тем как
передать его конструктору.
var bug = new Packages.bugbase.Bug(
parseInt(request.bugId),
request.bugPriority,
request);
Если класс Java не является частью пакетов java
, sun
или netscape
,
Вы имеете к нему доступ через объект Packages
. Например, корпорация Redwood
использует пакет Java под названием redwood
как контейнер различных Java-классов,
которые ею реализованы. Для создания экземпляра класса HelloWorld
в redwood
Вы вводите конструктор класса:
var red = new Packages.redwood.HelloWorld()
Вы можете также получить доступ к классам в пакете по умолчанию (то есть классам,
которые не указывают пакет явно).
Например, если класс HelloWorld находится непосредственно в CLASSPATH
,
а не в пакете,
Вы можете получить к нему доступ так:
var red = new Packages.HelloWorld()
Объекты LiveConnect java
, sun
и netscape
являются сокращениями для обычно используемых пакетов Java. Например, можно
записать так:
var myString = new java.lang.String("Hello world")
var myString = new Packages.java.lang.String("Hello world")
По умолчанию директория $NSHOME\js\samples
, где $NSHOME
это директория, в которой установлен сервер, находится в CLASSPATH
сервера. Вы можете поместить Ваш пакет в эту директорию. Альтернативно Вы можете
поместить Ваши пакеты и классы Java в другую директорию.
Если Вы это делаете, убедитесь, что директория находится в Вашем CLASSPATH
.
Если какой-нибудь метод Java создаёт массив и Вы обращаетесь к этому массиву в JavaScript,
Вы работаете с JavaArray
. Например, следующий код создаёт
JavaArray
x
из 10 элементов типа int:
theInt = java.lang.Class.forName("java.lang.Integer")
x = java.lang.reflect.Array.newInstance(theInt, 10)
Подобно объекту JavaScript Array
, JavaArray
имеет
свойство length
, возвращающее количество элементов массива. В
отличие от Array.length
,
JavaArray.length
является свойством только для чтения/read-only,
поскольку количество элементов в Java-массиве фиксируется в момент создания.
Простые ссылки на пакеты и классы Java из JavaScript создают объекты JavaPackage
и JavaClass
. В одном из предыдущих примеров о корпорации Redwood,
например, обращение Packages.redwood
это JavaPackage
-объект. Аналогично обращение java.lang.String
это JavaClass
-объект.
В большинстве случаев Вам не нужно беспокоиться об
объектах JavaPackage
и JavaClass
: Вы просто
работаете с пакетами и классами Java, а LiveConnect прозрачно создаёт эти
объекты.
JavaClass
-объекты не конвертируются автоматически в экземпляры
java.lang.Class
при передаче их в качестве параметров Java-методам --
Вы обязаны создать оболочку/wrapper вокруг экземпляра java.lang.Class
.
В следующем примере метод
forName
создаёт объект-оболочку theClass
, который затем
передаётся методу newInstance
для создания нового массива.
theClass = java.lang.Class.forName("java.lang.String")
theArray = java.lang.reflect.Array.newInstance(theClass, 5)
Вы не можете передать односимвольную строку Java-методу, требующему аргумента
типа
char
. Вы обязаны передать таким методам целое число,
соответствующее значению Unicode для данного символа. Например, следующий код
присваивает значение "H" переменной c
:
c = new java.lang.Character(72)
Директория $NSHOME\js\samples\bugbase
содержит простое приложение,
иллюстрирующее использование LiveConnect. В этом разделе описан код JavaScript
этого приложения-образца. См. в разделе
"Пример Вызывающего JavaScript" описание кода Java этого
приложения.
Приложение bugbase
представляет собой простую БД "жучков". Вы
вводите bug, заполняя клиентскую форму номером жучка, приоритетом, продуктом, в
котором он обнаружен, и кратким описанием. Другая форма позволяет просмотреть
существующий bug.
Следующий JavaScript обрабатывает акцию входа:
// Шаг 1. проверить, что ID был введён.
if (request.bugId != "") {
// Шаг 2. Создаётся Bug-экземпляр и присваивается переменной.
var bug = new Packages.bugbase.Bug(parseInt(request.bugId),
request.bugPriority, request);
// Шаг 3. Получить доступ к массиву и сохранить экземпляр в нём.
project.bugsLock.lock();
project.bugs[parseInt(request.bugId)] = bug;
project.bugsLock.unlock();
// Шаг 4. Отобразить информацию.
write("<P><b><I>====>Committed bug: </I></b>");
write(bug, "<BR>");
}
// Шаг 5. Если ID не был введён, предупредить пользователя.
else {
write("<P><b><I>====>Couldn't
commit bug: please complete
all fields.</I></b>");
}
Bug
и присвоить его переменной bug
.
Конструктор класса Bug
принимает три параметра: два из них
являются свойствами объекта request
; третий это сам объект
JavaScript request
. Поскольку они являются элементами формы, эти свойства объекта
request
являются строками JavaScript. Код изменяет ID на целое
число перед передачей его Java-конструктору. После передачи request
-объекта Java-конструктору этот конструктор может затем вызывать его методы.
Этот процесс обсуждается в разделе "Пример Вызывающего
Серверного JavaScript".project.bugsLock
для получения исключительного доступа к массиву project.bugs
и
сохранить затем новый Bug
-экземпляр в этом массиве,
индексированным по номеру bug'а, специфицированному
в форме. Заметьте, что этот код сохраняет ссылку на Java-объект как значение
свойства JavaScript-объекта. О блокировке см. "Безопасное
Совместное Использование Объектов с Блокировкой"
Если Вам нужно использовать объекты JavaScript в Java, Вы обязаны импортировать
пакет netscape.javascript
в Ваш Java-файл. Этот пакет определяет
следующие классы:
netscape.javascript.JSObject
даёт Java-коду доступ к методам и
свойствам JavaScript.netscape.javascript.JSException
позволяет Java-коду обрабатывать ошибки JavaScript.Эти классы поставляются в .jar или .zip-файле. См. в книге Серверный JavaScript. Справочник. дополнительную информацию об этих классах.
Например, в Navigator 4. 0 для Windows NT классы
поставляются в файле java40.jar
в директории Program\Java\Classes
ниже директории Navigator'а. Вы можете специфицировать
переменную окружения Windows NT, войдя в Control Panel и создав пользовательскую
переменную окружения с названием CLASSPATH
со значением типа такого:
D:\Navigator\Program\Java\Classes\java40.jar
Дополнительно о CLASSPATH
см.
Administrator's Guide
.
Поскольку Java является строго типизированным языком, а JavaScript - слабо типизированным, машина выполнения JavaScript конвертирует значения аргументов в подходящие типы данных другого языка, при использовании LiveConnect. См. "Конвертация Типов Данных".
Все объекты JavaScript появляются в коде Java как экземпляры netscape.javascript.JSObject
.
Когда Вы вызываете метод в Вашем Java-коде, Вы можете передать ему JavaScript-объект
как один из аргументов.
Чтобы сделать это, Вы обязаны определить соответствующий формальный параметр
метода как имеющий тип
JSObject
.
Таким образом, всегда, когда Вы используете JavaScript-объекты
в коде Java, Вы должны помещать вызов JavaScript-объекта внутри блока try...catch
,
который обрабатывает исключения netscape.javascript.JSException
.
Это позволяет Вашему Java-коду обрабатывать ошибки при выполнении кода JavaScript,
которые появляются в Java как исключения типа JSException
.
Предположим, к примеру, что вы работаете с Java-классом JavaDog
.
Как показано в следующем коде, конструктор JavaDog
constructor
принимает JavaScript-объект jsDog
,
который определён как имеющий тип JSObject
, в качестве аргумента:
import netscape.javascript.*;
public class JavaDog
{
public String dogBreed;
public String dogColor;
public String dogSex;
// определить конструктор класса
public JavaDog(JSObject jsDog)
{
// использовать try...catch для обработки JSExceptions
this.dogBreed = (String)jsDog.getMember("breed");
this.dogColor = (String)jsDog.getMember("color");
this.dogSex = (String)jsDog.getMember("sex");
}
}
Обратите внимание, что метод getMember
объекта JSObject
используется для доступа к свойствам объекта JavaScript. Предыдущий пример
использует getMember
для присвоения значения JavaScript-свойства jsDog.breed
члену данных Java JavaDog.dogBreed
.
ПРИМЕЧАНИЕ: Более жизненный пример помещает вызов
getMember
в блокtry...catch
для обработки ошибок типаJSException
. См. "Обработка Ошибок JavaScript в Java".
Чтобы лучше понять, как работает getMember
,
посмотрим на определение специального JavaScript-объекта Dog
:
function Dog(breed,color,sex) {
this.breed = breed
this.color = color
this.sex = sex
}
Вы можете создать JavaScript-экземпляр Dog
под названием gabby
:
gabby = new Dog("lab","chocolate","female")
Если Вы вычислите свойство gabby.color
, Вы увидите, что оно имеет
значение "chocolate". Теперь предположим, что Вы создаёте экземпляр JavaDog
в вашем JavaScript-коде, передавая объект
gabby
-конструктору:
javaDog = new Packages.JavaDog(gabby)
Если Вы вычислите javaDog.dogColor
, Вы увидите, что оно также имеет
значение "chocolate", поскольку метод getMember
в Java-конструкторе
присваивает dogColor
значение gabby.color
.
Если JavaScript-код, вызванный из Java, потерпел неудачу на этапе
прогона/выполнения, он вызывает исключение. Если Вы вызываете
JavaScript-код из Java, Вы можете отловить/catch это исключение в блоке
операторов try...catch
.
Исключение JavaScript доступно коду Java как экземпляр
netscape.javascript
.JSException.
JSException
это Java-оболочка вокруг исключения любого типа, вызываемого в JavaScript, так
же как и экземпляры
JSObject
являются оболочками для JavaScript-объектов.
Используйте JSException
при вычислении JavaScript-кода в Java. Если JavaScript-код
не вычисляется из-за ошибки компиляции JavaScript или из-за какой-то другой
ошибки времени прогона, интерпретатор JavaScript генерирует сообщение об ошибке,
которое конвертируется в экземпляр JSException
.
Например, можно использовать блок
try...catch
для обработки исключений LiveConnect:
try {
global.eval("foo.bar = 999;");
} catch (Exception e) {
if (e instanceof JSException) {
jsCodeFailed()";
} else {
otherCodeFailed();
}
}
В этом примере оператор eval
терпит неудачу, если foo
не определён.
Блок catch
выполняет метод jsCodeFailed
, если оператор eval
в блоке try
вызывает JSException
; метод
otherCodeFailed
выполняется, если блок try
вызывает
какую-то иную ошибку.
Рассмотрим теперь использование Java для доступа к серверному JavaScript. Для того чтобы метод Java получил доступ к объектам JavaScript, он обязан быть вызван из приложения, написанном на серверном JavaScript. В клиентском JavaScript, Java может инициировать взаимодействие с JavaScript. На сервере Java не может инициировать это взаимодействие.
ПРИМЕЧАНИЕ:
Когда Вы рекомпилируете Java-класс, который используется в приложении JavaScript, новое определение может не иметь немедленного эффекта. Если любое приложение JavaScript, запущенное на web-сервере, имеет "живую" ссылку на объект, созданный из старого определения класса, все приложения продолжают использовать старое определение. Поэтому при рекомпилировании Java-класса Вы должны рестартовать любые приложения JavaScript, имеющие доступ к этому классу.
Java даёт вам возможность создавать раздельные потоки выполнения. Вы должны осторожно использовать эту возможность, если Ваш Java-код взаимодействует с JavaScript-кодом.
Каждый запрос серверного JavaScript обрабатывается в потоке, известном как request thread/поток запроса. Этот поток запроса ассоциируется с информацией о статусе, такой как контекст JavaScript, используемый для процессинга информации HTTP-запроса, и HTTP-буфер ответа.
Когда Вы вызываете Java-код из приложения JavaScript, этот код Java работает в том же самом потоке запроса, что и оригинальное приложение JavaScript. Java-код в этом потоке может взаимодействовать с приложением JavaScript и иметь гарантию, что оно является таким, как он ожидает. Точнее, он может полагаться на ассоциированную информацию статуса.
Однако Вы можете создать новый поток из Java-кода. Если Вы это сделаете, этот
новый поток не сможет взаимодействовать с приложением JavaScript и не
сможет опираться на информацию о статусе, ассоциированную с оригинальным
потоком запроса. Если он попытается это сделать, поведение будет неопределённым.
Например, создаваемый Вами Java-поток не может ни инициировать выполнение JavaScript-кода
через использование JSObject
, ни использовать writeHttpOutput
,
поскольку этот метод требует доступа к HTTP-буферу ответа.
В директории $NSHOME\js\samples\bugbase
содержится простое приложение, которое иллюстрирует использование LiveConnect. В
это разделе описывается приложение-образец Java-кода.
См. в разделе "Пример JavaScript, Вызывающего Java"
описание основ работы этого приложения и его JavaScript-кода.
// Шаг 1. Импортировать необходимые Java-объекты.
package Bugbase;
import netscape.javascript.*;
import netscape.server.serverenv.*;
// Шаг 2. Создать класс Bug.
public class Bug {
int id;
String priority;
String product;
String description;
String submitter;
// Шаг 3. Определить конструктор класса.
public Bug(int id, String priority, JSObject req)
throws java.io.IOException
{
// Записать часть http-ответа.
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");
}
// Шаг 4. Возвратить строковое представление объекта.
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();
} }
Многие шаги в этом коде не являются специфичными для взаимодействия с JavaScript. Только шаги 1 и 3 имеют отношение к JavaScript.
netscape.javascript
и netscape.server.serverenv
.
Если Вы пропустите этот шаг, Вы не сможете использовать объекты JavaScript.Bug
, специфицировать его поля.JSObject
. Этот
последний параметр является представлением JavaScript-объекта в Java. Через
методы этого объекта конструктор может получить доступ к свойствам и вызвать
методы объекта JavaScript. В этом случае он использует метод
getMember
объекта JSObject
для получения значений свойств JavaScript-объекта.
Также этот метод использует метод writeHttpOutput
предопределённого объекта
NetscapeServerEnv
(из пакета netscape.server.serverenv
)
для вывода информации в процессе конструирования объекта. Этот метод
записывает массив байтов в тот же поток вывода, который используется JavaScript-функцией write
.toString
. Это стандартный метод для Java-объекта,
возвращающий строковое представление полей объекта.Поскольку Java является строго типизированным, а JavaScript - слабо типизированным языком, машина выполнения JavaScript конвертирует значения аргументов в подходящие типы данных других языков, если Вы пользуетесь LiveConnect. Эта конвертация описана в следующих разделах:
Если Вы вызываете Java-метод и передаёте ему параметры из JavaScript, типы данных передаваемых параметров конвертируются в соответствии с правилами, описанными в следующих разделах:
Return-значения методов netscape.javascript.JSObject
всегда конвертируются в экземпляры java.lang.Object
. Правила
конвертации этих return-значений также описаны в этих разделах.
Например, если JSObject.eval
возвращает число JavaScript, Вы можете
найти правило конвертации этого числа в экземпляр java.lang.Object
в разделе
"Числовые Значения".
Если Вы передаёте числовые типы JavaScript в качестве параметров Java-методам, Java конвертирует эти значения в соответствии с правилами из данной таблицы:
Когда число JavaScript передаётся как параметр в Java-метод,
ожидающий экземпляр java.lang.String
, это число конвертируется в
строку. Используйте операцию == для сравнения результата этой конвертации с другими строковыми значениями.
Когда Вы передаёте Булев тип JavaScript в качестве параметра Java-методам, Java конвертирует эти значения в соответствии с правилами, описанными в таблице:
Когда JavaScript Boolean передаётся в качестве параметра Java-методу,
ожидающему экземпляр java.lang.String
, Boolean конвертируется в строку.
Используйте операцию == для сравнения результата конвертации с другими строковыми значениями.
Когда Вы передаёте строковые типы JavaScript в качестве параметра Java-методам, Java конвертирует эти значения в соответствии с правилами, описанными в таблице:
Тип Java-параметра | Правила конвертации |
---|---|
Строка JavaScript конвертируется в экземпляр | |
Все значения конвертируются в числа, как описано в ECMA-262. | |
Когда Вы передаёте null JavaScript в качестве параметра Java-методам, Java конвертирует это значение в соответствии с правилами, описанными в таблице:
Тип Java-параметра | Правила конвертации |
---|---|
В большинстве случаев, когда Вы передаёте объект JavaScript JavaArray
или JavaObject
как параметр Java-методу, Java просто разворачивает
этот объект (снимает оболочку); иногда объект переводится в другой тип данных по
правилам из таблицы:
Интерфейс или класс совместимы для присвоения с развёрнутым объектом, если развёрнутый объект является экземпляром типа Java-параметра. То есть, следующий оператор обязан возвращать true:
развёрнутыйОбъект instanceof типПараметра
Если вы передаёте JavaScript-объект JavaClass
в качестве параметра Java-методу, Java
конвертирует объект в соответствии с правилами из таблицы:
Если вы передаёте любой другой объект JavaScript в качестве параметра Java-методу, Java конвертирует объект в соответствии с правилами из таблицы:
Тип Java-параметра | Правила конвертации |
---|---|
Оболочка с объекта снимается, вызывается метод | |
Объект конвертируется в значение с использованием логики оператора | |
Оболочка с объекта снимается, и возникает одна из следующих ситуаций: |
Значения, переданные из Java в JavaScript конвертируются так:
netscape.javascript.JSObject
конвертируется в оригинальный JavaScript-объект.Array
: Вы можете иметь к нему доступ с синтаксисом
arrayName[index]
(где index
это целое число) и
определять его размер с помощью arrayName.length
.toString
объекта-оригинала.doubleValue
, если возможно, иначе - терпит неудачу.booleanValue
, если
возможно, иначе - терпит неудачу.Обратите внимание, что экземпляры java.lang.Double и java.lang.Integer конвертируются в объекты JavaScript, а не в числа JavaScript. Аналогично, экземпляры java.lang.String также конвертируются в JavaScript-объекты, а не в строки JavaScript.
Java String
-объекты также соответствуют
оболочкам JavaScript. Если Вы вызываете JavaScript-метод, который требует строки JavaScript,
и передаёте его данной оболочке, Вы получите ошибку. Вместо этого конвертируйте
оболочку в строку JavaScript, присоединяя к ней пустую строку, как показано
здесь:
var JavaString = JavaObj.methodThatReturnsAString();
var JavaScriptString = JavaString + "";
1 http://developer.netscape.com/docs/manuals/enterprise/javanote/index35.html
2 http://developer.netscape.com/docs/manuals/index.html?content=javascript.html
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.