В этой книге рассматривается использование ядра и серверного JavaScript
версии 1.4. JavaScript это созданный фирмой Netscape межплатформенный
объектно-ориентированный язык скриптов (сценариев) для клиентских и серверных приложений.
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
клиент
клиент-серверное взаимодействие 112-12
клиентские скрипты
клиентский JavaScript 12, 24, 8
кодирование клиентского URL 103, 107, 112, 117, 147, 149-151
курсоры
методы
объекты
подтверждение, промпт, конфигурирование 14
строки
Дата последнего обновления: 29 сентября 1999 г.
╘ Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассматривается использование LiveConnect для доступа к распределённым объектам CORBA. С помощью LiveConnect Вы можете получить доступ к Java; через Java - соединяться с CORBA-объектами, используя Netscape Internet Service Broker for Java.
В главе имеются следующие разделы:
Netscape Internet Service Broker for Java (ISB for Java) это брокер запроса объектов фирмы Netscape. ISB for Java устанавливает соединения внутри себя и с другими брокерами запроса объектов/object request brokers (ORB) путём использования протокола Internet InterORB Protocol (IIOP).
ISB for Java даёт Вашим приложениям JavaScript доступ к распределённой объектной
модели CORBA, опубликованной в ORB, работающем на основе IIOP (включая сам ISB for Java).
Эти объекты могут быть частью распределённого приложения. Чтобы получить доступ
к такому распределённому объекту, Вы обязаны иметь Java-stub/основу, и этот stub-класс
обязан быть в пути Вашей CLASSPATH
. В свою очередь, Вы можете
использовать Java и LiveConnect для экспонирования частей Вашего серверного
приложения JavaScript как распределённых объектов CORBA.
За пределами данного учебника остаётся описание создания CORBA-исполняемых распределённых объектов с использованием ISB for Java, а также то, как делать Java-стабы для таких объектов. Об этом см. учебник Netscape Internet Service Broker for Java Programmer's Guide.
Приложения серверного JavaScript могут получать доступ к распределённому объекту в зависимости от того как он опубликован. Простейшей альтернативой является создание и запуск распределённого объекта как отдельного процесса, что показано на следующем рисунке.
Как видно на рисунке, среды запуска Java и JavaScript находятся вместе на одном web-сервере. Они взаимодействуют путём использования LiveConnect стандартным способом, описанным ранее в этой главе. Методы, вызываемые в оболочке стаба в JavaScript, дают в результате вызов методов объекта Java-стаба в Java. Стаб использует Java ORB для взаимодействия с удалённым сервисом. В данной архитектуре серверный процесс объекта может проходить только на машине, которая имеет ORB, и может быть написан на любом языке.
Приложение-образец flexi
иллюстрирует это. В нём FlexiServer
это отдельное приложение Java, содержащее реализации нескольких распределённых
объектов. Этот пример обсуждается в разделе "Приложение-Образец Flexi".
После того как поработаете с flexi
, прочтите в разделе "Альтернативные
Публикации" обсуждение более сложных альтернатив публикации.
Приложение flexi
иллюстрирует использование серверного JavaScript
для доступа к удалённому сервису, запущенному на ORB с включённым протоколом IIOP,
а также показывает удалённый сервис, написанный целиком на Java с использованием ISB for Java.
И исходные файлы, и исполняемые файлы приложения flexi
установлены
в директории $NSHOME\js\samples\flexi
.
A flexible spending account (FSA)/гибко расходуемый счёт это счёт, на котором служащие могут хранить доллары предоплаты, используемые для медицинских расходов. Служащие обычно подписываются на этот план через администратора плана и выбирают сумму в долларах, которую они хотят хранить на своих счетах. Если служащий осуществляет затраты на медицинские цели, он отправляет запрос, который, если одобрен, вызывает снятие суммы со счёта и перевод её служащему.
Приложение flexi
предоставляет поддержку обслуживания
FSA. В этом приложении администратор имеет следующие
опции:
Для служащего имеются следующие опции:
Рисунок 22.2 показывает две основные части flexi
.
Они реализуют клиента и сервис CORBA.
flexi
Клиент CORBA это приложение на серверном JavaScript, известное как flexi
.
Это приложение реализует пользовательские интерфейсы администратора и служащего,
описанные ранее. Оно соединяется с объектом FSA-Admin (описанном далее) в
отдельном процессе или даже на отдельном компьютере. Приложение затем использует
этот и другие объекты, возвращаемые из FSA-Admin, для выполнения большинства своих операций.
Сервер CORBA это отдельное Java-приложение, работающее из оболочки/shell. Оно
содержит реализации всех интерфейсов, определённых в IDL-файле Flexi.idl
.
Это приложение под названием FlexiServer
реализует основную
функциональность системы FSA. После старта это приложение создаёт экземпляр
объекта, реализующий интерфейс ::FSA::Admin
, и регистрирует его под
именем "FSA-Admin." Клиенты этого сервиса (такие как приложение flexi
на JavaScript) получают доступ к этому объекту, разрешая сначала своё имя.
Клиенты используют этот объект для создания других объектов и для получения
удалённых ссылок на них.
FlexiServer
это отдельное приложение Java. Оно может работать
только на машине, имеющей JDK 1.0.2. В Enterprise Server 3.01 и в FastTrack Server
3.01 Вы можете также запускать его на машине с JDK 1.1.2. Прежде чем запустить FlexiServer
,
Вы должны убедиться, что среда работы корректна.
Из оболочки, где Вы запускаете FlexiServer
, убедитесь, что Ваша
переменная окружения PATH
содержит $JDK\bin
и что CLASSPATH
включает следующее:
...
$NSHOME\js\samples\flexi
$NSHOME\wai\java\nisb.zip
$JDK\lib\classes.zip
Здесь $JDK
это директория, в которой установлен JDK, а $NSHOME
это директория, в которой установлен Ваш web-сервер.
Если среда корректна, Вы можете стартовать FlexiServer
так:
cd $NSHOME\js\samples\flexi\impl
java FlexiServer
Вы должны увидеть такое сообщение:
Started FSA Admin: Admin[Server,oid=PersistentId[repId=IDL:Flexi/Admin:1.0,objectName=FSA-Admin]]
С этого момента FlexiServer
стартовал как сервис CORBA и
зарегистрировал в ORB объект с интерфейсом ::FSA::Admin
и именем FSA-Admin. FlexiServer
работает в фоновом режиме, ожидая запросов на обслуживание.
Вы обязаны стартовать FlexiServer
до старта flexi
,
поскольку стартовая страница flexi
пытается соединиться с FlexiServer
.
Добавьте $NSHOME\js\samples\flexi
в CLASSPATH
Вашего web-сервера.
О том, как это сделать, см. "Установка LiveConnect".
Используя Application Manager, установите приложение flexi
JavaScript,
как описано в разделе "Установка Нового Приложения". Параметры, устанавливаемые
Вами для flexi
, показаны в следующей таблице.
Установка | Значение |
---|---|
flexi | |
$NSHOME\js\samples\flexi\flexi.web | |
fsa.html | |
start.html | |
client-cookie |
Чтобы стартовать flexi
, Вы можете запустить его из Application Manager
или ввести следующий URL:
http://server-name/flexi
Страница по умолчанию позволяет пользователю идентифицировать себя как администратора или как служащего. Чтобы быстрее прочувствовать то, как работает это приложение, следуйте этому сценарию:
Система может обрабатывать только один запрос служащего в единицу времени. После удаления запроса может быть отправлен новый запрос.
В таблице показаны первичные файлы и директории для flexi
.
Просмотрите эти файлы, чтобы добиться полной ясности в вопросах работы приложения. Здесь обсуждаются лишь некоторые детали.
Функция main
отдельного Java-приложения
реализована в flexi\impl\FlexiServer.java
. Его код таков:
import org.omg.CORBA.*;
class FlexiServer {
public static void main(String[] args) {
try {
// Инициализируются orb и boa.
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
org.omg.CORBA.BOA boa = orb.BOA_init();
// Создаётся серверный объект.
Admin __admin = new Admin();
// Информирует boa, что серверный объект готов.
boa.obj_is_ready(__admin);
// Регистрируется имя объекта с именем сервиса.
// Сначала определяется хост имени сервиса, по умолчанию это <localhost>:80.
String _nameServiceHost = null;
if (args.length > 0) {
// Принимается, что первый arg это имя хоста имени
// сервиса. Ожидаемый формат: <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);
// Регистрируется серверный объект.
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);
}
}
}
Этот код инициализирует ORB и создаёт экземпляр класса Admin
. Затем
экземпляр регистрируется как распределённый объект с URL в форме http://
host
:
port
/FSA-Admin
. По умолчанию
host
это имя хоста, на котором запущен FlexiServer
, а
port
равен 80. Вы можете поставить Ваши собственные значения для
host:port
путём передачи их как аргументов для FlexiServer
при его старте.
Чтобы использовать локальный хост, но другой номер порта, Вам нужно изменить
исходный код и перекомпилировать его. Если код имеет соответствующее имя, он
регистрирует объект, используя метод
register
объекта netscape.WAI.Naming
. Дополнительно
см. книгу
Netscape Internet Service Broker for Java Reference Guide.
Наконец, код печатает сообщение на консоль и ожидает запросы от
CORBA-клиентов. В данном случае единственным CORBA-клиентом, который знает о
нём, является приложения flexi
на языке
JavaScript.
Файл start.html
это начальная страница приложения JavaScript flexi
.
Эта страница использует LiveConnect для инициализации ISB for Java и
установления соединения с FSA-Admin.
<server>
// Инициализируется orb.
project.orb = Packages.org.omg.CORBA.ORB.init();
// Устанавливается соединение с сервисом "FSA-Admin".
// По умолчанию принимается, что name service запущен на текущем сервере.
nameHost = "http://" + server.hostname;
serviceName = "/FSA-Admin";
serviceURL = nameHost + serviceName;
// Разрешается имя и получается ссылка на стаб Admin.
project.fsa_admin = Packages.Flexi.AdminHelper.narrow(
netscape.WAI.Naming.resolve(serviceURL));
</server>
Первый оператор инициализирует ISB for Java, вызывая static-метод init
Java-класса org.omg.CORBA.ORB
.
Он сохраняет возвращённый объект как свойство объекта project
, так
что он доступен для всего приложения.
Второй набор операторов определяет URL, который использовался для регистрации
объекта FSA-Admin
.
Если Вы использовали при регистрации этого объекта другой URL (как описано в
последнем разделе), Вам нужно сделать соответствующие изменения в этих
операторах. URL, используемый в CORBA-сервере, обязан быть точно тем же, что и URL,
используемый в CORBA-клиенте.
Код затем вызывает метод resolve
объекта netscape.WAI.Naming
для установления соединения с объектом Admin
, который был
зарегистрирован FlexiServer
как
FSA-Admin. Наконец, он вызывает метод narrow
объекта AdminHelper
для приведения полученного объекта к соответствующему типу Java-объекта. Этот Java-метод
возвращает Java-объект, соответствующий распределённому объекту. Машина
выполнения JavaScript обёртывает этот Java-объект как JavaScript-объект и
сохраняет его как свойство объекта project
. Теперь Вы можете
вызывать методы и получать доступ к свойствам этого возвращённого объекта, как и
любого другого Java-объекта. Другие страницы flexi
работают через
этот объект.
И ещё раз - о том, как работают CORBA-объекты, см. книгу Netscape Internet Service Broker for Java Reference Guide.
Код во flexi
создаёт другие объекты, кроме объекта Admin
,
и осуществляет к ним доступ во FlexiServer
. Эти объекты создаются
путём вызовов метода объекта Admin
. Например, если служащий
отправляет запрос, новый запрос создаётся в account-empl.html
следующим оператором:
__claim = __account.submitClaim(
parseFloat(request.claimAmount),
request.serviceDate,
request.providerName,
request.details);
Этот код вызывает метод submitClaim
объекта Account
для создания нового запроса служащего. Реализация этого метода в файле impl\Account.java
создаёт новый Claim
-объект, который регистрируется в ORB и
возвращается:
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;
};
Имеются две альтернативы для публикации распределённых CORBA-объектов, представляющие интерес при работе с серверным JavaScript:
В этих альтернативных вариантах CORBA-клиент и CORBA-сервер оба работают в одном процессе web-сервера.
С этой точки зрения, если CORBA-клиент не является приложением JavaScript, первый вариант пригоден для тех же целей, что и в случае, когда CORBA-сервер запущен как отдельный процесс.
Однако второй вариант, создающий распределённый объект в приложении JavaScript, реально делает это приложение сервисом CORBA. Рисунок 22.3 иллюстрирует эти альтернативы.
Ещё раз: среды выполнения Java и JavaScript находятся вместе на одном web-сервере.
Они взаимодействуют через использование LiveConnect стандартным способом,
описанным ранее в этой главе.
В это случае, однако, процессы Java и JavaScript действуют вместе как CORBA-сервис.
Этот сервис затем взаимодействует с CORBA-клинтом через ISB for Java стандартным способом.
Образец-приложение bank
это пример приложения JavaScript,
реализующего CORBA-сервис.
В данном случае CORBA-клиент может находиться на любой машине, имеющей ORB с IIOP, и может быть написан на любом языке. Интересно то, что CORBA-клиент может быть клиентским приложением Java (и через LiveConnect на клиенте - клиентским приложением JavaScript). Это даёт совершенно другой способ взаимодействия клиентского приложения JavaScript с серверным приложением JavaScript.
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассматривается, как машина выполнения JavaScript на сервере конвертирует более сложные типы данных, используемые в реляционных БД, и простые типы.
В главе имеются следующие разделы:
В БД имеется богатый набор типов данных. Машина выполнения JavaScript на сервере
конвертирует эти типы данных в значения JavaScript, строки или числа. Число в JavaScript
хранится как значение двойной точности с плавающей точкой. Вообще машина
выполнения конвертирует символьные типы данных в строки, числовые типы данных -
в числа, а даты - в Date
-объекты JavaScript. Она также
конвертирует null-значения в JavaScript null.
Поскольку JavaScript не поддерживает фиксированные или упакованные десятеричные числа, возможна некоторая потеря точности при чтении и записи типов упакованных десятеричных данных. Проверьте результаты до вставки их обратно в БД и используйте соответствующие математические функции для коррекции потери точности.
Значения даты, запрошенные из базы данных, конвертируются в Date
-объекты JavaScript. Чтобы вставить значение даты в БД, используйте Date
-объект JavaScript так:
cursorName.dateColumn = dateObj
Здесь cursorName
это курсор, dateColumn
это столбец,
соответствующий дате, а dateObj
это Date
-объект JavaScript.
Вы создаёте Date
-объект, используя оператор new
и Date
-конструктор:
dateObj = new Date(dateString)
где dateString
это строка, представляющая дату. Если dateString
- пустая строка, создаётся Date
-объект для текущей даты. Например:
custs.orderDate = new Date("Jan 27, 1997")
Базы Данных
DB2 имеют типы данных time
и timestamp
. Эти типы
конвертируются в тип Date
в JavaScript.
ПРЕДУПРЕЖДЕНИЕ!
LiveWire Database Service не может обрабатывать даты после 5 февраля 2037 года.
Дополнительно о работе с датами в JavaScript см. "Объект Date."
В следующей таблице показана конвертация, выполняемая машиной выполнения JavaScript для баз данных DB2.
Тип Данных DB2 | Тип Данных JavaScript |
---|---|
В следующей таблице дана конвертация БД Informix.
Тип Данных Informix | Тип Данных JavaScript |
---|---|
| |
| |
ODBC транслирует типы данных продавца в типы данных ODBC. Например, в Microsoft SQL Server
тип данных varchar
конвертируется в ODBC-тип SQL_VARCHAR
.
Дополнительно см. документацию ODBC SDK.
В следующей таблице показана конвертация, выполняемая машиной JavaScript для баз
данных ODBC.
Тип Данных ODBC | Тип Данных JavaScript |
---|---|
| |
В следующей таблице показана конвертация, выполняемая машиной выполнения JavaScript для баз данных Oracle.
Тип Данных Oracle | Тип Данных JavaScript |
---|---|
В следующей таблице показана конвертация, выполняемая машиной выполнения JavaScript для баз данных Sybase.
Тип Данных Sybase | Тип Данных JavaScript |
---|---|
|
|
1
Sybase-клиент ограничивает числовые типы данных 33 цифрами. Если Вы вставляете число JavaScript с большим количеством цифр в БД Sybase, Вы можете получить ошибку. |
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе обсуждается использование службы LiveWire Database Service для соединения Вашего приложения с реляционными базами данных DB2, Informix, ODBC, Oracle или Sybase. Показано, как выбрать наилучшую методологию для Вашего приложения.
В главе имеются следующие разделы:
Ваше JavaScript-приложение, запущенное на сервере Netscape Enterprise Server, может использовать LiveWire Database Service для доступа к БД серверов Informix, Oracle, Sybase и DB2 и серверов, использующих стандарт Open Database Connectivity (ODBC). Ваше приложение, запущенное на сервере Netscape FastTrack Server, может получать доступ только к БД на серверах, использующих ODBC.
В последующих обсуждениях предполагается, что Вы уже знакомы реляционными БД и Structured Query Language (SQL).
Прежде чем создать приложение JavaScript с использованием LiveWire, база или базы данных, к которым Вы планируете подключаться, должны уже существовать на сервере БД. Также Вы должны знать их структуру. Если Вы создаёте совершенно новое приложение, включающее БД, необходимо создать БД и заполнить её данными (как минимум в форме прототипа) до создания приложения.
До того как Вы попытаетесь использовать LiveWire, убедитесь, что Ваша рабочая
среда сконфигурирована соответственно. О том, как конфигурировать, см.
Главу 10, "Конфигурирование Вашей Базы Данных." Вы можете также использовать
приложение-образец videoapp
, описанное в
Главе 13, "Приложения-Образцы Videoapp и Oldvideo," для изучения некоторых
возможностей LiveWire.
Обычно для того чтобы взаимодействовать с БД, необходимо выполнить следующие общие действия:
database
или создать объект DbPool
для установки пула соединений БД. Это обычно выполняется на начальной странице
приложения, если только оно не требует установления специального соединения.database
или явно - при использовании метода connection
объекта DbPool
.Connection
).В этой главе обсуждаются первые три пункта действий. В Главе 9, "Работа с БД", обсуждаются остальные шаги.
Есть два основных способа соединения с БД с помощью сервиса LiveWire Database Service.
Это объекты DbPool
и Connection
, либо объект database
.
При этом подходе Вы создаёте пул соединений для работы
с реляционной БД. Вы создаёте экземпляр класса DbPool
,а затем
получаете доступ к объектам Connection
через этот объект DbPool
.
Объекты DbPool
и Connection
распределяют между собой
работу по соединению с БД и обслуживанию набора соединений и доступ к БД через соединение.
Это весьма гибкий подход. Ваше приложение может иметь несколько пулов соединений,
каждый со своей собственной конфигурацией БД и пользователя. Каждый пул может
иметь несколько соединений при такой конфигурации. Это даёт одновременный доступ
к нескольким БД или к одной БД из нескольких бюджетов. Вы можете также
ассоциировать пул непосредственно с приложением, а не с отдельным клиентским
запросом, и иметь таким образом транзакции, захватывающие несколько клиентских
запросов. Это ассоциирование выполняется путём присвоения пула свойству
объекта project
и удаления этого присвоения после окончания работы с пулом.
При этом подходе Вы используете предопределённый объект database
для соединения с БД при наличии единственной конфигурации соединения БД и
пользователя. Объект database
выполняет все действия по работе с БД.
Можно представить этот объект как database
единый пул соединений с БД.
Этот подход несколько проще, так как используется только один объект database
,
а не несколько объектов DbPool
и Connection
. Однако
при этом теряется гибкость первого подхода. Если Вы используете только объект database
и хотите соединиться с разными БД или разными бюджетами, Вы обязаны отключиться
от одной конфигурации, для того чтобы подключиться к другой. Также, при
использовании объекта database
, одна транзакция не может захватить
несколько клиентских запросов, а соединения с несколькими БД-источниками не могут быть установлены одновременно.
Как описано в последующих разделах, Вы должны ответить на два основных вопроса, когда решаете, как устанавливать соединения с БД:
В таблице резюмируется, как ответ на эти вопросы влияет на установку и обслуживание пула соединений с БД и отдельных соединений. В последующих разделах обсуждаются детали этих вариантов.
Сколько конфигураций БД? | Где соединение с пулом? | Где отключение пула? | Какой объект(ы) содержит пул? | Должен ли Ваш код хранить пул и соединение? |
Как Ваш код хранит пул и соединение в объекте project ? |
---|---|---|---|---|---|
Зависит от 1 |
Если Вы хотите использовать объект database
, Вам не нужно создавать
его. Это предопределённый объект, предоставляемый машиной выполнения JavaScript.
Если Вам нужны дополнительные возможности класса DbPool
, Вы
создаёте экземпляр класса DbPool
и соединяете этот объект с
конкретной БД, которая создаёт пул соединений.
Вы можете создать общий DbPool
-объект и специфицировать позднее
информацию соединения (используя метод connect
), или можете
специфицировать информацию соединения при создании пула. Общий DbPool
-объект
не имеет никаких доступных соединений в момент его создания. Исходя из этого,
Вам может понадобиться установить соединение при создании этого объекта. Если Вы
используете объект database
, Вы всегда обязаны устанавливать
соединение путём вызова метода database.connect
.
connect (dbtype, serverName, userName, password,
databaseName, maxConnections, commitFlag);
При создании соединения Вы можете специфицировать следующую информацию, либо при
создании DbPool
-объекта, либо при вызове метода connect
объекта DbPool
или database
:
dbtype
: Тип БД. Обязан быть "DB2"
, "INFORMIX"
, "ODBC"
, "ORACLE"
или "SYBASE"
. (Для приложений, запущенных на сервере Netscape FastTrack Server,
обязан быть "ODBC"
.)serverName
: Имя сервера БД, с которым устанавливается соединение.
Имя сервера обычно назначается при установке БД. В случае сомнений, узнайте у
администратора БД или системы.username
: Имя пользователя, соединяющегося с БД.password
:
Пароль пользователя.databaseName
: Имя БД на данном сервере, с которой устанавливается
соединение. Если Ваш сервер баз данных поддерживает множество БД на одном
сервере, предоставьте имя используемой БД. Если предоставлена пустая строка,
производится соединение с БД по умолчанию. Для Oracle, ODBC и DB2 Вы всегда
обязаны предоставлять пустую строку.maxConnections
: (Optional/Необязательный Параметр) Допустимое
количество соединений в пуле БД. Помните, что клиентская лицензия Вашей БД,
возможно, специфицирует максимальное количество соединений. Не устанавливайте
этот параметр в число, превышающее количество, допустимое по лицензии. Если Вы
не предоставляете этот параметр для объекта DbPool
, он имеет
значение 1. Если Вы не предоставляете этот параметр для объекта database
,
его значением будет то, что Вы специфицировали в Application Manager как
значение для Built-in Maximum Database Connections/Встроенное Значение
Максимального Количества Соединений с БД, когда устанавливали приложение. (См. "Установка
Нового Приложения"). См. в разделе "Однопоточные и
Многопоточные БД" о том, что Вы должны предусмотреть при установке этого
параметра.commitflag
: (Необязательный Параметр) Булево значение,
указывающее, подтвердить ли открытую транзакцию или выполнить её откат при
закрытии соединения. Специфицируйте true
для подтверждения
открытой транзакции и false
- для выполнения отката. Если этот
параметр Вами не предоставлен для объекта DbPool
, его значением
будет false
. Если этот параметр не предоставлен для объекта database
,
значением параметра будет true
.Например, следующий оператор создаёт новый пул БД из 5 соединений с БД Oracle. В этом пуле неподтверждённые транзакции откатываются:
pool = new DbPool ("ORACLE", "myserver1", "ENG", "pwd1", "", 5);
Приложение-образец dbadmin
позволяет Вам экспериментировать с
соединениями с различными БД как разным пользователям.
Для многих приложений Вы, возможно, захотите выполнять совместное использование набора соединений несколькими клиентами, или чтобы соединение захватывало несколько клиентских запросов. В этих случаях Вы должны устанавливать соединение на начальной странице Вашего приложения. Это позволит исключить возможные проблемы в тех случаях, когда отдельные клиенты выполняют совместные соединения с БД.
Однако для некоторых приложений каждый клиент должен выполнять своё собственное соединение. Как сказано в разделе "Совместное Использование Массива Пулов Соединений," клиенты могут совместно использовать объекты. Если это так, убедитесь, что блокировки используются для управления совместным использованием данных, как указано в разделе "Безопасное Использование Объектов с Помощью Блокировки".
В следующей таблице показаны методы объектов DbPool
и database
для обслуживания пула соединений. (Объект database
использует
другие методы, рассмотренные ранее, для работы с соединением с БД.)
DbPool
и database
для
обслуживания пулов соединенийLiveWire поддерживает многопоточный доступ к БД. То есть она поддерживает наличие более чем одного потока доступа к одной БД в единицу времени. Отсюда ясно, для чего нужен пул соединений с более чем одним соединением. Однако библиотеки БД некоторых производителей не являются многопоточными. Для таких БД не имеет значения, сколько соединений имеется в Вашем пуле, так как только одно соединение может устанавливаться с БД в единицу времени.
В этой таблице дан список клиентских библиотек баз данных, которые являются многопоточными на указанных платформах.
Sybase | Informix | Oracle | DB2 | ODBC 1 | |
---|---|---|---|---|---|
1
Все многопоточные тесты для ODBC были сделаны на MS SQL Server. Если Вы используете другой драйвер ODBC, узнайте у производителя, является ли драйвер многопоточным. |
Эти указания являются критичными для однопоточного доступа. Однако Вы должны
думать об этом даже тогда, когда используете БД с многопоточным доступом.
Однопоточная библиотека БД может иметь существенные
ограничения производительности. Поскольку только один поток имеет доступ к БД в
единицу времени, все прочие потоки обязаны ждать, когда первый поток освободит
соединение с БД, прежде чем один из них сможет получить доступ к БД. Если
доступа ожидают одновременно несколько потоков, каждому придётся ожидать довольно долго.
При разработке доступа к БД Вы должны предусмотреть следующее:
Каждый поток обязан ждать окончания работы другого потока. Чем короче время взаимодействия с БД, тем меньше ожидание.
Ограничения на использование транзакций для клиентских библиотек БД, которые не являются многопоточными:
database()
или DbPool(), Вы обязаны
установить максимальное допустимое количество соединений с БД в Вашем
приложении большим, чем количество клиентов, которые, как Вы предполагаете,
будут использовать Ваше приложение. Иначе некоторые клиенты не смогут
установить соединение с БД, и их приложения зависнут.
В любой данный момент времени соединённый объект DbPool
или database
и все соединения пула ассоциированы с определённой конфигурацией базы данных. То
есть всё, что находится в пуле, соединено с определённым сервером БД как
отдельный пользователь с отдельным паролем и с определённой БД.
Если Ваше приложение всегда использует одну конфигурацию, то можно использовать
единственный объект DbPool
или использовать объект database
и соединяться однократно. В этом случае Вы должны выполнить соединение на
начальной странице Вашего приложения.
Если Вашему приложению требуется несколько конфигураций, или потому что оно обязано соединяться с несколькими БД, или с одной БД, но с разными пользователями, или и то, и другое, Вам необходимо определить, как обслуживать эти конфигурации.
Если Вы используете объект database
и несколько соединений, то
выбора у Вас нет. Вы обязаны соединяться, отсоединяться и повторно соединяться с
объектом database
каждый раз, когда Вам нужно изменить что-либо в
конфигурации. Вы делаете это под управлением клиентских запросов. В этой
ситуации убедитесь, что используются блокировки, как указано в разделе "Совместное
Использование Объектов с Блокировкой," чтобы получать исключительный доступ
к объекту database
. Иначе другой клиентский запрос может отключить
объект до того, как текущий клиентский запрос закончит с ним работу. Хотя Вы и
можете использовать объект database
таким образом, лучше будет
всё-таки использовать объекты DbPool
.
Если Вы используете объекты DbPool
и несколько конфигураций, Вы
также должны соединяться, отсоединяться и повторно соединяться с объектом DbPool
.
Однако с объектами DbPool
у Вас появится больше возможностей. Вы
можете создавать столько пулов, сколько нужно, и ставить их под контроль объекта project
. (См.
в Главе 6, "Обслуживание Сессий" информацию
об объекте project
.) Использование нескольких пулов более
эффективно и обычно надёжнее, чем многократное использование единственного пула (как
с объектом database
, так и с единственным объектом DbPool
).
При определении того, как обслуживать пулы, Вы обязаны учитывать два фактора: ко скольки конфигурациям будут иметь доступ пулы и нужно ли будет одному соединению захватывать несколько клиентских запросов. Если у Вас небольшое количество возможных конфигураций, Вы можете создать отдельные пулы для каждой. В разделе "Совместное Использование Фиксированного Набора Пулов Соединений" обсуждается этот подход.
Если у Вас имеется очень большое или заранее не известное количество конфигураций (например, если все пользователи БД получают свои индивидуальные ID), нужно предусмотреть два варианта. Если одного соединения достаточно для выполнения одного клиентского запроса, Вы можете создавать отдельные пулы на клиентской странице.
Однако иногда соединение должно захватывать несколько клиентских запросов (например, если одна транзакция в БД захватывает несколько клиентских запросов). Возможно также, что Вы просто не хотите повторно соединяться с БД на каждой странице приложения. Если это так, Вы можете создать массив пулов, который используется совместно. В разделе "Совместное Использование Массива Пулов Соединений" обсуждается этот подход.
Независимо от используемого подхода, если Вам больше не нужно отдельное
соединение в пуле, зачистите ресурсы, используемые этим соединением, чтобы оно
стало доступным для других пользователей. Чтобы выполнить это, закройте все
открытые курсоры, хранимые процедуры и результирующие наборы. Верните соединение
обратно в пул. (Вы не должны освобождать соединение, если используете объект database
.)
Если Вы не освободили соединение, то при попытке отсоединить пул система будет ожидать, перед тем как реально отсоединиться, возникновения одного из двух условий:
Если Вы создаёте отдельные пулы БД для каждого пользователя, убедитесь, что пул отсоединён, до того как закончить с ним работу. О курсорах см "Манипуляции с Результатами Выполнения Запросов с Помощью Курсоров". О хранимых процедурах и результирующих наборах см. "Вызов Хранимых Процедур".
Часто в приложении небольшой набор пулов соединений используется всеми
пользователями данного приложения. Например, Вашему приложению нужно соединяться
с тремя различными БД или с одной БД, которая использует 4 пользовательских ID,
соответствующих 4 разным департаментам. Если у вас имеется небольшой набор
возможных конфигураций соединения, Вы можете создать отдельный пул для каждой
конфигурации. Для этого используйте объекты DbPool
.
Тогда необходимо, чтобы пул работал в течение всего периода существования
приложения, а не просто в течение периода существования клиента или отдельного
клиентского запроса. Вы можете реализовать это, создав каждый пул БД как
свойство объекта project
. Например, начальная страница приложения
может содержать эти операторы:
project.engpool = new DbPool ("ORACLE", "myserver1", "ENG",
"pwd1", "", 5, true);
project.salespool = new DbPool ("INFORMIX", "myserver2", "SALES",
"pwd2", "salsmktg", 2);
project.supppool = new DbPool ("SYBASE","myserver3","SUPPORT",
"pwd3", "suppdb", 3, false);
Эти операторы создают три пула для различных групп пользователей приложения.
Пул project.eng
содержит 5 соединений Oracle и подтверждает любую
неподтверждённую транзакцию при высвобождении соединения обратно в пул.
Пул project.sales
имеет два соединения Informix и откатывает любую
неподтверждённую транзакцию при окончании соединения.
Пул project.supp
имеет три соединения Sybase и откатывает любую
неподтверждённую транзакцию при окончании соединения.
Вы должны создавать такой пул как часть начальной страницы приложения. Эта
страница выполняется только при старте приложения. На страницах, доступных
пользователям, Вы не создаёте пул и не изменяете соединение. Вместо этого эти
страницы определяют, к какой группе принадлежит текущий пользователь, и
используют уже установленное соединение из соответствующего пула. Например, в
следующем коде определяется, какую БД использовать (на основе значения свойства userGroup
объекта request
), в БД ищется некоторая информация, которая
выводится пользователю, а затем соединение освобождается:
if (request.userGroup == "SALES") {
salesconn = project.salespool.connection("A sales connection");
salesconn.SQLTable ("select * from dept");
salesconn.release();
}
Вы можете также создать пул и изменить соединение на странице, доступной пользователю. В этом случае Вы должны быть осторожны, так как несколько пользователей, осуществляющих одновременный доступ к этой странице, не должны мешать друг другу. Например, только один пользователь должен иметь возможность создавать пул, используемый всеми остальными пользователями. О надёжности совместного использования информации см. раздел "Надёжное Совместное Использование Объектов с Помощью Блокировки".
В разделе
"Совместное Использование Фиксированного Набора Пулов
Соединений" описано, как Вы можете применить свойства объекта project
для совместного использования фиксированного набора пулов соединений. Этот
подход используется, если Вам в процессе разработки уже известно количество
необходимых пулов соединений и Вам нужно только небольшое количество соединений.
Часто нельзя предугадать заранее количество необходимых пулов соединений. В других случаях это возможно, но это количество недопустимо велико. Например, предположим, что для каждого потребителя, имеющего доступ к Вашему приложению, приложение проверяет пользовательский профиль на предмет определения того, какую информацию из БД вывести. Вы можете дать каждому потребителю уникальный пользовательский идентификатор ID для БД. Такое приложение требует, чтобы каждый пользователь имел свой набор параметров соединений (соответствующий различным пользовательским ID в БД) и, соответственно, разные пулы соединений.
Вы можете создать объект DbPool
и соединять и отсоединять его на
каждой странице приложения. Это будет работать, только если одно соединение не
должно захватывать несколько клиентских запросов. Иначе эта ситуация должна
обрабатываться по-разному.
Для данного приложения, вместо создания фиксированного набора пулов соединений
на начальной странице приложения или пула на каждой клиентской странице, Вы
создаёте одно свойство объекта project
, которое будет содержать
массив пулов соединений. Доступ к элементам этого массива осуществляется по
ключу на базе определённого пользователя.
Во время инициализации Вы создаёте массив, но не помещаете в него элементы (поскольку
никто ещё не пытался использовать приложение), как показано здесь:
project.sharedPools = new Object();
Когда пользователь впервые стартует приложение, оно получает идентифицирующий
пользователя ключ. На основе этого ключа приложение создаёт объект пула DbPool
и сохраняет его в массиве пулов. Имея данный пул соединений, оно может либо
соединяться на каждой странице, либо устанавливать соединение так, как описано в
разделе "Обслуживание Соединения по Запросам." Следующий
код создаёт пул либо получает уже созданный, проверяет его соединение и работает
затем с БД:
// Генерируется уникальный индекс для обращения к данному клиенту, если это
// ещё не было сделано на другой странице. О функции ssjs_generateClientID см.
// "Уникальное Обращение к Объекту client". if client.id == null {
client.id = ssjs_generateClientID();
}
// Если пула для данного клиента ещё нет, он создаётся
// и производится его соединение с БД. project.lock();
if (project.sharedPools[client.id] == null) {
project.sharedPools[client.id] = new DbPool ("ORACLE",
"myserver", user, password, "", 5, false);
}
project.unlock();
// Для удобства устанавливается переменная для этого пула.
var clientPool = project.sharedPools[client.id];
// Теперь у Вас есть пул: посмотрим, соединён ли он. Если нет, попытаемся соединить его.
// Если это не удаётся, перенаправляем на специальную страницу,
// чтобы проинформировать пользователя.
project.lock();
if (!clientPool.connected()) {
clientPool.connect("ORACLE", "myserver", user, password,
"", 5, false);
if (!clientPool.connected()) {
delete project.sharedPools[client.id];
project.unlock();
redirect("noconnection.html");
}
}
project.unlock();
// Если Вы дошли до этого места, Вы успешно соединились и
// можете работать с БД.
clientConn = clientPool.connection();
clientConn.SQLTable("select * from customers");
// ... другие операции с БД ...
// Всегда освобождайте соединение, если оно Вам больше не нужно.
clientConn.release();
}
Когда пользователь в следующий раз войдёт в приложение (например, с другой
страницы приложения), он использует тот же самый код и получит сохранённый пул
соединений и (возможно, сохранённый,) объект Connection
из объекта project
.
Если Вы используете ssjs_generateClientID
и сохраняете ID в объекте client
,
Вам может понадобиться защита от вторжения через доступ к этому ID и,
следовательно, к закрытой информации.
Объект sharedConns
, использованный в этом примере кода, не является
предопределённым объектом
JavaScript. Он просто создан в этом примере и может иметь другое имя по Вашему
выбору.
Как только Вы создали пул соединений, клиентская страница может получить доступ
к индивидуальному соединению из пула. Если Вы используете объект database
,
соединение в данном объекте является неявным; то есть Вы используете методы
объекта database
для доступа к соединению. Если, однако, Вы
используете объекты DbPool
, соединение инкапсулируется в объекте Connection
,
который Вы получаете через вызов метода объекта DbPool
. Например, в
следующем пуле:
project.eng = new DbPool ("ORACLE", "myserver", "ENG", "pwd1", "", 5);
Вы можете получить соединение из пула с помощью такого вызова метода:
myconn = project.eng.connection ("My Connection", 60);
Оба параметра метода являются необязательными. Первый это имя соединения (используется
при отладке); второй это целое число, обозначающее таймаут в секундах. В этом
примере, если пул имеет доступное соединение или если оно становится доступным в
течение 60 секунд, это соединение присваивается переменной myconn
.
Если соединение не становится доступным в течение указанного периода, этот метод
возвращается без соединения. Дополнительно об ожидании получения соединения из
пула см. раздел "Ожидание Соединения". О том, что делать,
если соединение не получено, см. "Запрашивание Незанятого
Соединения".
Если Вы закончили использование соединения, возвратите его в пул путём вызова
метода release
объекта Connection
. (Если Вы
используете объект database
, Вам не нужно самостоятельно
освобождать соединение). Прежде чем вызвать метод release
, закройте
все открытые курсоры, хранимые процедуры и результирующие наборы. Если Вы
вызываете метод release
, система ожидает, когда всё закроется, и
возвращает затем соединение в пул базы данных. После этого соединение доступно
следующему пользователю. Об использовании курсоров см. "Манипуляции
с Результатами Запросов с Помощью Курсоров". О хранимых процедурах и
результирующих наборах см. "Вызов Хранимых Процедур".
После получения соединения (через объект database
или объект Connection
),
Вы можете работать с БД. В таблице резюмированы методы объектов database
и connection
для работы с единственным соединением. Объект database
имеет и другие методы для обслуживания пула соединений, рассмотренные в разделе "Обслуживание
Пулов Соединений."
database
и Connection
для Работы с Единственным СоединениемВ некоторых случаях может понадобиться, чтобы единственное соединение захватывало несколько клиентских запросов. То есть Вы сможете использовать одно соединение на нескольких страницах HTML.
Обычно вы используете свойства объекта client
для информации,
захватывающего клиентские запросы. Однако значение свойства объекта client
не может быть объектом. Исходя из этого, Вы не можете сохранять пул соединений
БД в объекте client
. Вместо этого Вы используете пул соединений,
хранимый в объекте project
, обслуживая их так, как описано в данном
разделе. Если вы используете этот подход, Вам может понадобиться кодирование
пользовательской информации, по соображениям безопасности.
Будьте особенно осторожны при использовании такого подхода, поскольку сохранение соединения таким способом делает его недоступным для других пользователей. Если все соединения окажутся недоступны, новые запросы будут ожидать явного освобождения соединения или таймаута соединения. Это особенно проблематично для однопоточных библиотек БД. (Об установлении соединений так, что они будут запрашиваться, если не заняты в течение продолжительного времени, см. "Запрашивание Незанятого Соединения").
В следующем примере соединение и транзакция
захватывают несколько клиентских запросов. Код сохраняет соединение как свойство
объекта sharedConns
, который сам является свойством объекта project
.
Объект sharedConns
не является предопределённым объектом JavaScript.
Он просто создан в данном примере и может иметь другое имя, по Вашему выбору.
Поскольку один пул используется всеми клиентами, Вы должны создавать объект sharedConns
и создавать и соединять сам пул на начальной странице приложения примерно таким
кодом:
project.sharedConns = new Object();
project.sharedConns.conns = new Object();
project.sharedConns.pool = new DbPool ("SYBASE", "sybaseserver",
"user", "password", "sybdb", 10, false);
Затем на первой клиентской странице, получающей доступ к пулу, следуйте такой стратегии:
// Генерируется уникальный индекс для обращения к данному клиенту, если он ещё
// не сгенерирован на другой странице.
if client.id == null {
client.id = ssjs_generateClientID();
}
// Для удобства устанавливается переменная для данного пула.
var clientPool = project.sharedConns.pool;
// Проверяется, соединён ли пул. Если нет, перенаправляется
// на специальную страницу для информирования пользователя.
project.lock();
if (!clientPool.connected()) {
delete project.sharedConns.pool;
project.unlock();
redirect("noconnection.html");
}
project.unlock();
// Соединение получается из пула и сохраняется в объекте project.
project.sharedConns.conns[client.id] = clientPool.connection();
var clientConn = project.sharedConns.conns[client.id];
clientConn.beginTransaction();
cursor = clientConn.cursor("select * from customers", true");
// ... другие операции с БД ...
cursor.close();
}
Заметьте, что эта страница не выполняет откат или подтверждение транзакции.
Соединение остаётся открытым, и транзакция продолжается. (Транзакции
рассматриваются в разделе "Обслуживание Транзакций").
Вторая HTML-страница запрашивает соединение, базируясь на значении client.id
,
и продолжает работать с БД так:
// Запрашивается соединение.
var clientConn = project.sharedConns.conns[client.id];
// ... Выполняются ещё какие-нибудь операции с БД ...
// Здесь, если операции с БД успешно прошли, okay устанавливается в 1.
// Если была ошибка при работе с БД, okay устанавливается в 0. В конце
// подтверждается или откатывается транзакция на основе этого значения.
if (okay)
clientConn.commitTransaction();
else
clientConn.rollbackTransaction();
// Соединение возвращается в пул.
clientConn.release();
// Избавляемся от значения свойства объекта. Оно Вам больше не нужно.
delete project.sharedConns.conns[client.id];
В этом примере объект sharedConns
сохраняет единственный объект DbPool
и соединения для данного пула, которые используются в данный момент. Ситуация
может быть значительно сложнее. Если у Вас имеется фиксированный набор пулов БД,
Вы можете определить отдельный объект для хранения соединений каждого пула.
Если у вас имеется массив пулов и каждому пулу необходимы соединения,
захватывающие несколько запросов, Вам необходимо создать массив объектов, каждый
из которых сохраняет пул и массив его соединений. Как ответвление, вместо
немедленного перенаправления в том случае, если пул не соединён, клиентская
страница может сделать новую попытку установить соединение.
Если Вы используете ssjs_generateClientID
и храните ID в объекте client
,
Вам понадобится защита от вторжения и получения доступа к ID и, следовательно, к
закрытой информации.
В пуле, созданном объектом DbPool
, соединений имеется фиксированное
количество соединений. Если в момент попытки доступа все соединения заняты, Ваше
приложение ожидает освобождения соединения в течение специфицированного периода
таймаута. Вы можете управлять периодом ожидания.
Предположим, Вы определили следующий пул из 3 соединений:
pool = new DbPool ("ORACLE", "myserv", "user", "password", "", 3);
Предположим далее, что три клиента одновременно получают доступ к приложению и каждый использует одно из трёх соединений. Четвёртый клиент теперь запрашивает соединение через следующий вызов:
myconnection = pool.connection();
Этот клиент обязан ждать, пока один из трёх клиентов не освободит соединение. В
данном случае, поскольку вызов connection
не специфицирует таймаут,
клиент ждёт освобождения соединения неопределённо долго, а затем возвращает это
соединение.
Вы можете специфицировать различные периоды таймаута, задавая аргументы метода connection
.
Второй аргумент метода connection
это период таймаута в секундах.
Если Вы специфицируете таймаут 0, система ждёт бесконечно долго. Например,
следующий код ожидает соединения только 30 секунд перед таймаутом:
myconnection = pool.connection ("Name of Connection", 30);
Если в течение специфицированного периода соединение не освобождается, метод
возвращает null, и в сообщение об ошибке устанавливается сообщение о наименьшей
ошибке. Вы можете получить это сообщение, вызвав метод minorErrorMessage
объекта pool
. Если Вы вызываете таймаут из connection
,
Вам может понадобиться освободить соединение, отключив одно из уже установленных.
Дополнительно см. "Запрашивание Свободного Соединения".
Если Ваше приложение запрашивает соединение из объекта DbPool
, оно
может не получить его. Доступные опции в этот момент зависят от архитектуры
Вашего приложения.
Если каждое соединение существует только в период существования отдельного клиентского запроса, недоступность соединений невозможна из-за того, что пользователь оставил приложение в бездействии на значительный период времени. Это может произойти только из-за того, что весь код страницы JavaScript не закончил выполняться. В этом случае Вы не должны пытаться разорвать используемое соединение, чтобы воспользоваться им. Если Вы разорвёте соединение в этот момент, Вы рискуете оставить этот поток выполнения в несоответствующем состоянии. Вместо этого Вы должны удостовериться, что Ваше приложение освобождает каждое соединение по мере завершения его использования. Если Вы не хотите ожидать соединения, Вы должны предоставить пользователю возможность выбора другого варианта.
Если, наоборот, соединение захватывает несколько клиентских запросов, Вы можете захотеть запросить свободные соединения. В этой ситуации соединение может освободиться, поскольку пользователь не завершил транзакцию. Например, предположим, что пользователь отправляет данные на первой странице приложения и эти данные начинают многостраничную транзакцию БД. Вместо отправки данных для продолжения транзакции на следующей странице, пользователь переходит на другой сайт и никогда не вернётся к данному приложению. По умолчанию соединение остаётся открытым и не может использоваться другими клиентами.
Вы можете вручную запросить соединение, зачистив его и освободив в пул БД. Чтобы сделать это, напишите функции типа нижеследующих:
Bucket
: Определяет тип объекта (в данном примере - с названием bucket
)
для содержания соединения и штампа времени.MarkBucket
: Помечает объект bucket
штампом текущего
времени.RetrieveConnections
: Проходит по массиву соединений в поисках
объекта Connection
, к которому не было доступа в течение
установленного лимита времени, и использует CleanBucket
(описан
далее) для запрашивания этого объекта.CleanBucket
: Закрывает курсоры (и возможные хранимые процедуры и
результирующие наборы), откатывает или подтверждает каждую открытую транзакцию
и возвращает соединение в пул.Ваше приложение может использовать эти функции так:
Bucket
для
создания объекта bucket
.MarkBucket
для обновления штампа времени.RetrieveConnection
для поиска незанятых соединений,
закройте все открытые курсоры, подтвердите или откатите работающие транзакции
и верните незанятые соединения обратно в пул.Также на каждой странице, где Ваше приложение использует соединение, необходимо убедиться, что другой поток освободил соединение, до того как данная страница будет достигнута данным клиентом.
Функция bucket содержит соединение и штамп времени. Этот образец конструктора функции принимает соединение в качестве единственного параметра:
// Конструктор для Bucket
function Bucket(c)
{
this.connection = c;
this.lastModified = new Date();
}
Вы можете вызвать эту функцию для создания bucket для соединения, когда Вы получаете соединение из пула соединений. Вы можете добавить другие свойства для соединения bucket. Например, Ваше приложение может содержать курсор, который захватывает клиентские запросы. Тогда Вы можете использовать свойство для добавления курсора в bucket, так чтобы можно было закрыть открытый курсор при запрашивании соединения. Вы сохраняете курсор в bucket во время его создания, как видно из следующего оператора:
myBucket.openCursor =
myBucket.connection.cursor("select * from customer", true);
Функция MarkBucket
принимает объект Bucket
в качестве параметра и устанавливает в поле lastModified
текущее
время.
function MarkBucket(bucket)
{
bucket.lastModified = new Date();
}
Вызывайте MarkBucket
на каждой странице приложения, которая
использует соединение, содержащееся в bucket. Это восстанавливает в lastModified
значение текущей даты и предотвращает появление незанятых соединений.
RetrieveConnections
сканирует массив объектов Bucket
,
ищет buckets соединения, штамп времени которых установлен ранее некоторого
определённого времени. Если соединение найдено, функция вызывает CleanBucket
(описан
далее) для возвращения соединения в пул БД.
// Запрашиваются соединения, не занятые в течение специфицированного количества минут.
function RetrieveConnections(BucketArray, timeout)
{
var i;
var count = 0;
var now;
now = new Date();
// Этот цикл выполняется для каждого bucket в массиве.
for (i in BucketArray) {
// Вычисляется разница во времени между текущей/now и последней/last
// модифицированной датой. Эта разница выражается в миллисекундах.
// Если она больше значения timeout, вызывается функция зачистки.
if ((now - i.lastModified)/60000) > timeout) {
CleanBucket(i);
// Избавляется от bucket, поскольку он больше не используется.
delete i;
count = count + 1;
}
} return count;
}
После того как определено, что соединение должно быть
запрошено (с помощью функции RetrieveConnections
), Вам понадобится
функция для зачистки подробностей соединения и возврата его обратно в пул базы
данных.
Данная функция-образец закрывает открытые курсоры, откатывает открытые
транзакции и освобождает соединение.
function CleanBucket(bucket)
{
bucket.openCursor.close();
bucket.connection.rollbackTransaction();
bucket.connection.release();
}
CleanBucket
принимает, что данный bucket содержит открытый курсор
и его соединение имеет открытую транзакцию. Принимается также, что отсутствуют
хранимые процедуры и результирующие наборы. В Вашем приложении может
понадобиться и какая-нибудь другая проверка.
Следующий пример кода использует уже определённые функции для запрашивания соединений, к которым не обращались в течение 10 минут. Сначала создаётся совместно используемый массив соединений и пул БД с 5 соединениями:
if ( project.sharedConns == null ) {
project.sharedConns = new Object();
project.sharedConns.pool = new DbPool ("ORACLE", "mydb",
"user", "password", "", 5, false);
if ( project.sharedConns.pool.connected() ) {
project.sharedConns.connections = new Object();
}
else {
delete project.sharedConns;
}
}
Теперь используем следующий код для попытки получения соединения. После зачистки
пула генерируется клиентский ID, который затем используется как индекс в массиве
соединений. Далее пытаемся получить соединение. Если возникает таймаут, вызываем RetrieveConnections
для возвращения старого соединения в пул.
Если RetrieveConnections
возвращает соединение в пул, пытаемся
получить соединение вновь. Если всё ещё не можем получить соединение,
выполняется перенаправление на другую страницу с информацией, что свободных
соединений нет. Если запрашивается соединение, сохраняем его в новом bucket
соединения и сохраняем этот bucket соединения в совместно используемом массиве
соединений.
if ( project.sharedConns != null ) {
var pool = project.sharedConns.pool;
// Этот код запускается, только если пул уже соединён.
// Если нет, возможно, Вам нужен код для соединения.
if ( pool.connected() == true ) {
// Генерируется клиентский ID.
client.id = ssjs_generateClientID();
// Попытка получить соединение.
var connection = pool.connection("my connection", 30);
// Если соединение null, тогда ничего не будет доступно // в течение специфицированного лимита времени. Пытаемся запросить старые соединения.
if (connection == null) {
// Запрашиваются соединения, не используемые в течение последних 10 минут.
var count = RetrieveConnections(project.sharedConns.connections, 10);
// Если count не равен 0, делаем какое-нибудь соединение доступным.
if (count != 0){
connection = pool.connection("my connection", 30);
// Если connection всё ещё null, отказываемся.
if (connection == null)
redirect("nofreeconnections.html");
}
else {
// Отказываемся.
redirect("nofreeconnections.html");
}}
// Если Вы не дошли досюда, Вы получили соединение и можете продолжить работу.
// Поместите это connection в новый bucket, стартуйте транзакцию,
// получайте курсор, сохраняйте его в bucket и продолжайте работу.
project.sharedConns.connections[client.id] = new Bucket(connection);
connection.beginTransaction();
project.sharedConns.connections[client.id].cursor =
connection.cursor("select * from customer", true);
// Помечаем bucket соединения как использованный.
MarkBucket(project.sharedConns.connections[client.id]);
// Операторы Базы Данных.
...
}
На следующей странице многостраничной транзакции выполняются операции БД по этому соединению. После последней операции БД по соединению помечается bucket соединения:
var Bucket = project.sharedConns.connections[client.id];
if ( Bucket == null) {
// Повторное соединение.
}
else {
// Взаимодействие с БД.
...
// Последняя операция БД на странице.
row = Bucket.cursor.next();
row.customerid = 666;
Bucket.openCursor.insertRow("customer");
// Помечается bucket соединения как использованный на данной странице.
MarkBucket(Bucket);
}
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе обсуждается работа с реляционными базами данных DB2, Informix, ODBC, Oracle и Sybase. Рассматривается, как запрашивать информацию из БД и использовать её в приложении, как работать с транзакциями и как выполнять хранимые процедуры.
Не забывайте, что, если Ваше приложение работает на Netscape FastTrack Server, а не на Netscape Enterprise Server, оно может иметь доступ только к серверам БД, использующим стандарт ODBC.
В главе имеются следующие разделы:
Сервис LiveWire Database Service даёт возможность взаимодействовать с
реляционной БД различными способами.
Вы можете:
Об установке и обслуживании соединений БД см. Главу 8, "Соединение с Базой Данных".
Наиболее простым и быстрым способом отобразить результаты выполнения запросов к
БД является использование метода
SQLTable
объекта database
или объекта Connection
.
Метод SQLTable
принимает оператор SQL SELECT
и
возвращает HTML-таблицу.
Каждый ряд и столбец в запросе это ряд и столбец таблицы. Таблица HTML имеет
также заголовочную ячейку для каждого столбца таблицы БД.
Метод SQLTable
не даёт Вам возможности управлять форматированием
вывода. Кроме того,
если вывод содержит объект Blob
, этот объект не выводится как
изображение.
(Об использовании blob см. раздел "Работа с Бинарными
Данными"). если Вы хотите специализировать вывод, используйте курсор БД для
создания Вашей собственной функции вывода/отображения. См. также "Манипуляции
с Результатами Выполнения Запросов с Помощью Курсоров".
В качестве примера: если myconn
это Connection
-объект,
следующий оператор JavaScript отображает результат выполнения запроса к БД в
виде таблицы:
myconn.SQLTable("select * from videos");
Вот первая часть таблицы, которая могла бы быть сгенерирована этим оператором:
Метод execute
объекта database
или объекта Connection
даёт приложению возможность выполнять произвольный оператор SQL. Использование execute
называется выполнением передаваемого SQL, поскольку этот метод передаёт SQL
непосредственно на сервер.
Вы можете использовать execute
для выполнения оператора SQL любого
языка определения данных/data definition language (DDL) или языка
манипулирования данными/data manipulation language
(DML), поддерживаемого сервером БД. Примером могут служить операторы CREATE
,
ALTER
и DROP
. Хотя Вы можете использовать execute
для выполнения любого оператора SQL, Вы не можете с помощью этого метода
возвращать данные.
Обратите внимание, что execute
служит для выполнения стандартных
операторов SQL, а не для выполнения расширений SQL, поставляемых некоторыми
производителями БД. Например, Вы не можете вызвать функцию Oracle describe
или функцию Informix load
из метода execute
.
Для выполнения передаваемых операторов SQL просто задайте оператор SQL как
параметр метода
execute
. Например, Вам нужно удалить таблицу из БД, на которую
имеется ссылка в свойстве oldtable
объекта project
.
Чтобы выполнить это, Вы можете использовать такой вызов метода:
connobj.execute("DROP TABLE " + project.oldtable);
При использовании execute
Ваш SQL-оператор
обязан строго соответствовать требованиям синтаксиса SQL данного сервера БД.
Например, некоторые серверы требуют, чтобы каждый оператор SQL заканчивался
символом "точка с запятой". Дополнительно см. документацию к Вашему серверу БД.
Если Вы не стартуете транзакцию явно, единственный оператор подтверждается
автоматически.
Об управлении транзакциями см. "Обслуживание Транзакций".
Для выполнения некоторых операций, таких как создание и удаление таблиц, Вам могут понадобиться права доступа, предоставляемые администратором базы данных. См. документацию Вашего сервера БД и обратитесь к администратору сервера БД.
Часто Вам необходимо не просто отобразить таблицу с результатами выполнения
запроса, но изменить форматирование этих результатов или даже выполнить
какую-нибудь их обработку.
Для манипуляций результатами выполнения запроса Вы работаете с курсором БД,
возвращаемым запросом к БД. Для создания экземпляра класса Cursor
вызовите метод cursor
объекта database
или объекта
Connection
, передав ему оператор SQL SELECT
в качестве
параметра.
Вы можете представить курсор как виртуальную таблицу с рядами и столбцами, специфицированный по запросу. Курсор предполагает также понятие текущего ряда, который в реальности является указателем на ряд виртуальной таблицы. Когда Вы выполняете операции с курсором, они обычно воздействуют на текущий ряд.
По окончании работы, закройте курсор БД путём вызова его метода close
.
Соединение с БД не может быть освобождено, пока не закрыты все ассоциированные с
ним курсоры. Например, если Вы вызываете метод release
объекта
Connection
и это соединение имеет ассоциированный курсор, который
не был закрыт, соединение не будет освобождено, пока курсор не будет закрыт.
В таблице обобщены методы и свойства класса Cursor
.
Cursor
Метод или Свойство | Описание |
---|---|
Свойства, соответствующие каждому столбцу курсора. Имя каждого свойства
| |
Полную информацию об этих методах см. в описании класса Cursor
в
книге
"Серверный JavaScript.
Справочник"
.
Как только приложение установило соединение с БД, Вы можете создать курсор путём
вызова метода cursor
ассоциированного объекта database
или Connection
. Создание объекта
Cursor
также открывает курсор в БД. Вам не нужно выполнять
отдельную команду open.
Можно предоставить следующую информацию при создании объекта Cursor
:
SELECT
, поддерживаемый сервером БД. Чтобы
гарантировать независимость от вида БД,
используйте синтаксис SQL 89/92. Курсор создаётся как виртуальная таблица
результата выполнения этого оператора SQL.SELECT
будет таким:
"select count(*) from videos"
, Вы не сможете создать обновляемый
курсор.
Например, следующий оператор создаёт курсор для записей таблицы CUSTOMER
.
Записи содержат столбцы id
, name
и city
и
упорядочены по значениям столбца id
.
custs = connobj.cursor ("select id, name, city
from customer order by id");
Этот оператор устанавливает в переменную custs
объект Cursor
.
Запрос SQL может вернуть следующие ряды:
1 Sally Smith Suva
2 Jane Doe Cupertino
3 John Brown Harper's Ferry
Затем Вы можете получить доступ к этой информации через использование методов Cursor
-объекта custs
.
Этот объект имеет свойства id
, name
и city
,
соответствующие столбцам виртуальной таблицы.
Когда Вы первоначально создаёте Cursor
-объект, указатель
позиционируется сразу перед первым рядом виртуальной таблицы. В последующих
разделах рассматривается, как получить информацию из виртуальной таблицы.
Вы можете также использовать оператор конкатенации строк (+) и строковые
переменные (такие как значения свойств client
или
request
) при конструировании оператора SELECT
.
Например, следующий вызов использует ранее сохранённый customer ID для
последующей специализации запроса:
custs = connobj.cursor ("select * from customer where id = "
+ client.customerID);
При попытке создания Cursor
-объекта Вы можете столкнуться с
различными проблемами. Например, если оператор SELECT
в вызове
метода cursor
обращается к несуществующей таблице, БД возвращает
ошибку, и метод cursor
возвращает null вместо
Cursor
-объекта. В этой ситуации Вы должны использовать методы majorErrorCode
и
majorErrorMessage
для определения возникшей ошибки.
В качестве второго примера, предположим, что оператор SELECT
обращается к существующей таблице, в которой нет рядов. В этом случае БД может
не возвратить ошибку, а метод cursor
возвратит верный Cursor
-объект. Однако, поскольку этот объект не содержит рядов, при первой попытке
использования метода next
в этом объекте он возвратит false
.
Ваше приложение должно проверять возможность возникновения такой ситуации.
Когда Вы создаёте курсор, он получает свойство
colName
для каждого именованного столбца виртуальной таблицы (отличное от свойств,
соответствующих агрегатным функциям), как определено оператором SELECT
.
Вы можете получать доступ к значениям текущего ряда, используя эти свойства. В
вышеприведённом примере курсор имеет свойства для столбцов id
,
name
и city
. Вы можете вывести значения для первого
возвращённого ряда, используя следующие операторы:
// Создаётся Cursor-объект.
custs = connobj.cursor ("select id, name, city
from customer order by id");
// Прежде чем продолжить, убедитесь, что курсор действительно был возвращён
// и не было ошибки БД.
if ( custs && (connobj.majorErrorCode() == 0) ) {
// Получаем первый ряд.
custs.next();
// Отображаем значение.
write ("<B>Customer Name:</B>" + custs.name + "<BR>");
write ("<B>City:</B> " + custs.city + "<BR>");
write ("<B>Customer ID:</B> " + custs.id);
//Закрываем курсор.
custs.close();
}
Сначала текущим рядом является первый ряд таблицы.
Выполнение метода next
передвигает текущий ряд на первый ряд
таблицы.
Например, предположим, что это первый ряд курсора:
1 Sally Smith Suva
Customer Name: Sally Smith
City: Suva
Customer ID: 1
Вы можете обращаться к свойствам Cursor
-объекта (или, в
действительности, объекта JavaScript) как к элементам массива. Элемент массива с
индексом [0] соответствует первому столбцу, элемент
массива с индексом [1]
соответствует второму столбцу, и так далее.
Например, Вы можете использовать индекс для отображения тех же значений столбцов, что и в предыдущем примере:
write ("<B>Customer Name:</B> " + custs[1] + "<BR>");
write ("<B>City:</B> " + custs[2] + "<BR>");
write ("<B>Customer ID:</B> " + custs[0]);
Эта техника особенно применима внутри циклов. Например,
Вы можете создать Cursor
-объект с названием custs
и
вывести результат выполнения запроса в виде таблицы
HTML с помощью следующего кода:
// Создаётся Cursor-объект.
custs = connobj.cursor ("select id, name, city from customer order by id");
// Прежде чем продолжить, убедитесь, что курсор действительно был возвращён
// и не было ошибки БД.
if ( custs && (connobj.majorErrorCode() == 0) ) {
write ("<TABLE BORDER=1>");
// Отображаем имена столбцов как заголовки.
write("<TR>");
i = 0;
while ( i < custs.columns() ) {
write("<TH>", custs.columnName(i), "</TH>");
i++;
}
write("</TR>");
// Отображаем каждый ряд виртуальной таблицы.
while(custs.next()) {
write("<TR>");
i = 0;
while ( i < custs.columns() ) {
write("<TD>", custs[i], "</TD>");
i++;
}
write("</TR>");
}
write ("</TABLE>");
// Закрываем курсор.
custs.close();
}
Этот код может вывести примерно такую таблицу:
ID | NAME | CITY |
---|---|---|
В примере использованы методы, обсуждаемые в последующих разделах.
Операторы SELECT
могут запрашивать значения,
не являющиеся столбцами в БД, такие как агрегатные значения и выражения SQL. Для
таких значений Cursor
-объект не имеет именованного свойства. Вы
можете получить к ним доступ только через использование свойства индекса массива Cursor
-объекта для данного значения.
В следующем примере создаётся курсор с именем empData
, делается
переход к первому ряду этого курсора и затем отображается значение, запрошенное
агрегатной функцией MAX
. Выполняется также проверка того, что
результаты из БД верны, перед тем как использовать их:
empData = connobj.cursor ("select min(salary), avg(salary),
max(salary) from employees");
if ( empData && (connobj.majorErrorCode() == 0) ) {
rowexists = empData.next();
if (rowexists) { write("Highest salary is ", empData[2]); }
}
Во втором примере создаётся курсор с именем empRows
для подсчёта
количества рядов в таблице,
выполняется переход к ряду в этом курсоре, а затем отображается количество рядов
параллельно с выполнением проверки на верность данных:
empRows = connobj.cursor ("select count(*) from employees");
if ( empRows && (connobj.majorErrorCode() == 0) ) {
rowexists = empRows.next();
if (rowexists) { write ("Number of rows in table: ", empRows[0]); }
}
Сначала указатель курсора позиционируется перед первым рядом виртуальной таблицы.
Метод
next
используется для перемещения между записями виртуальной
таблицы. Этот метод передвигает указатель на следующий ряд и возвращает true
,
когда следующий ряд виртуальной таблицы найден. Если следующего ряда нет, next
возвращает false
.
Например, предположим, что виртуальная таблица имеет
столбцы с названиями title
,
rentalDate
и dueDate
. Следующий код использует next
для итерации по рядам и отображения значений столбцов в таблице:
// Создаём курсор.
custs = connobj.cursor ("select * from customer");
// Проверяем курсор и отсутствие ошибок БД.
if ( custs && (connobj.majorErrorCode() == 0) ) {
write ("<TABLE>");
// Итерация по рядам с отображением значений.
while (custs.next()) {
write ("<TR><TD>" + custs.title + "</TD>" +
"<TD>" + custs.rentalDate + "</TD>" +
"<TD>" + custs.dueDate + "</TD></TR>");
}
write ("</TABLE>");
// Всегда закрывайте курсоры по окончании работы!
custs.close();
}
Вы не всегда можете находиться в нужном месте в курсоре. Например, предположим, что Вы создаёте курсор и, пока Вы работаете с ним, кто-то добавляет ряд в таблицу. В зависимости от установок БД, этот ряд может появиться в Вашем курсоре. Исходя из этого, когда это удобно (как при обновлении рядов), Вы можете сделать так, чтобы Ваш код проверял, находится ли указатель в нужном ряду.
Метод columns
класса Cursor
возвращает количество столбцов в курсоре. Этот метод не принимает параметров:
custs.columns()
Вы можете использовать этот метод, если нужно итерировать по каждому столбцу курсора.
Метод columnName
класса Cursor
возвращает имя столбца
виртуальной таблицы. Этот метод принимает параметр - целое число,
специфицирующее порядковый номер столбца, начиная с 0. Первым столбцом
виртуальной таблицы является столбец 0, вторым - столбец 1, и так далее.
Например, следующее выражение присваивает имя первого столбца курсора custs
переменной header
:
header = custs.columnName(0)
Если Ваш оператор SELECT
использует шаблон (*) для выбора
всех столбцов таблицы,
метод columnName
не гарантирует, что порядок присвоения номеров
столбцам будет тем же. То есть, предположим, у Вас есть оператор:
custs = connobj.cursor ("select * from customer");
Если таблица customer имеет 3 столбца, ID, NAME и CITY, Вы не сможете заранее
предугадать, который из этих столбцов будет custs.columnName(0)
. (Конечно,
есть гарантия, что последовательные вызовы columnName
дадут
аналогичный результат). Если порядок для Вас важен, можно жёстко кодировать
имена в операторе выборки, как здесь:
custs = connobj.cursor ("select ID, NAME, CITY from customer");
В этом операторе, custs.columnName(0)
это ID, custs.columnName(1)
это NAME, а custs.columnName(2)
это CITY.
Вы можете использовать обновляемый курсор для модифицирования таблицы на основе
текущего ряда курсора. Чтобы запросить обновляемый курсор, добавьте
дополнительный параметр true
при создании курсора, как в этом
примере:
custs = connobj.cursor ("select id, name, city from customer", true)
Чтобы курсор был обновляемым, оператор SELECT
обязан быть
обновляемым запросом
(запросом, позволяющим производить обновление). Например, оператор не может
запрашивать ряды из более чем одной таблицы или содержать условие GROUP
BY
,
а также обычно он обязан запрашивать ключевые значения таблицы. Дополнительно о
конструировании обновляемых запросов см. документацию производителя БД.
Когда курсоры используются для внесения изменений в Вашу
БД, Вы всегда должны работать в рамках явной транзакции. Вы делаете это через
использование методов beginTransaction
,
commitTransaction
и rollbackTransaction
, как указано в
разделе "Обслуживание Транзакций." Если в таких ситуациях
Вы не используете явные транзакции, Вы можете получать ошибки из Вашей БД.
Например, Informix и Oracle возвращают сообщения об ошибке, если Вы используете
курсор без явной транзакции. Oracle возвращает Error ORA-01002: fetch out of sequence
;
Informix возвращает Error -206
: There is no current row for UPDATE/DELETE cursor
.
Как сказано в разделе "Навигация с Помощью Курсоров", Вы не обязательно привязаны к позиции в курсоре. Исходя из этого, при внесении изменений в БД не забывайте проверять, в каком ряду Вы работаете, прежде чем изменять его.
Также запомните, что при создании курсора указатель позиционируется перед рядом
курсора.
Так, чтобы обновить ряд, Вы обязаны вызвать метод next
как минимум
один раз для установки указателя на первый ряд таблицы. После этого Вы можете
присваивать значения столбцам курсора.
В следующем примере обновляемый курсор вычисляет премию для продавцов, выполнивших норму. Затем этой информацией обновляется БД:
connobj.beginTransaction ();
emps = connobj.cursor(
"select * from employees where dept='sales'", true);
// Прежде чем продолжить, убедитесь, что курсор действительно был возвращён
// и не было ошибки БД.
if ( emps && (connobj.majorErrorCode() == 0) ) {
// Производится итерация по рядам курсора с обновлением информации на базе // return-значения функции metQuota.
while ( emps.next() ) {
if (metQuota (request.quota, emps.sold)) {
emps.bonus = computeBonus (emps.sold);
}
else emps.bonus = 0;
emps.updateRow ("employees");
}
// После выполнения - закрывается курсор и подтверждается транзакция.
emps.close();
connobj.commitTransaction();
}
else {
// Если курсор для работы отсутствовал, транзакция откатывается.
connobj.rollbackTransaction();
}
Этот пример создаёт обновляемый курсор для всех employees/служащих департамента Sales.
Производится итерация по рядам курсора через использование определяемой
пользователем функции JavaScript metQuota
, для того чтобы
определить, выполнил ли служащий норму. Эта функция использует значение свойства quota
объекта request
(возможно, установленное в форме на клиентской
странице) и столбец sold
курсора для выполнения этого определения.
Код затем устанавливает соответствующую премию и вызывает updateRow
для модифицирования таблицы employees
. Когда пройдены все ряды
курсора, подтверждается транзакция. Если вызов метода cursor
не
вернул никакого курсора, транзакция откатывается.
Помимо метода updateRow
, Вы можете использовать методы insertRow
и
deleteRow
для вставки нового ряда или удаления текущего. При
использовании deleteRow
не нужно присваивать никакого значения,
поскольку этот метод просто удаляет весь ряд.
Если Вы используете insertRow
, значения,
присваиваемые столбцам, используются для нового ряда. Если перед этим Вы вызвали
метод next
курсора, то текущие значения в ряду используются для
столбцов без присвоенных значений; иначе столбцы будут иметь значения null.
Также, если некоторые столбцы таблицы не вошли в курсор, insertRow
вставляет null в эти столбцы. Место расположения вставляемого ряда зависит от
библиотеки производителя БД. Если Вам нужен доступ к ряду после вызова метода insertRow
,
Вы обязаны сначала закрыть имеющийся курсор, а затем открыть новый.
В
DB2 имеется тип данных Time
. JavaScript не имеет соответствующего
типа данных.
Поэтому Вы не можете обновлять ряды значениями, использующими тип данных DB2 Time
.
Транзакция это несколько действий с базой данных, выполняемых вместе. Либо все эти действия выполняются успешно, либо все вместе терпят неудачу. Если эти действия выполняют (окончательно) изменения БД, говорится, что транзакция подтверждена. Вы можете также выполнить откат /roll back транзакции, т.е. не подтвердить её; это отменяет все выполненные действия.
Транзакции важны для поддержания целостности и структуры данных. В то время как различные серверы БД реализуют механизм транзакций с некоторыми отличиями, сервис LiveWire Database Service предоставляет одни и те же методы для обслуживания транзакций на всех БД. Проверьте в документации продавца БД информацию о согласованности данных и уровнях изоляции транзакций.
Вы можете использовать явный контроль транзакции над выполнением любого набора
действий. Например, акции, модифицирующие БД, должны проходить под управлением
транзакций. Это акции, соответствующие операторам SQL INSERT
,
UPDATE
и DELETE
. Транзакции могут также использоваться
для контролирования согласованности данных.
Для большинства БД, если Вы не выполняете явного управления транзакциями, машина выполнения использует фоновый механизм БД - autocommit/автоподтверждение, когда каждый оператор в БД рассматривается как отдельная транзакция. Каждый оператор подтверждается или откатывается немедленно на основе успеха или неуспеха выполнения каждого отдельного оператора. Явное управление транзакциями переопределяет работу этого механизма по умолчанию.
В некоторых БД, таких как Oracle, autocommit это явный механизм, который LiveWire
включает для каждого отдельного оператора. В других БД, таких как Informix, autocommit
это поведение по умолчанию, если Вы не создаёте транзакцию. В общем, LiveWire
скрывает эти различия и переводит приложение в режим autocommit, если приложение
не использует beginTransaction
для явного старта транзакции.
Для ANSI-БД Informix, LiveWire не использует autocommit.
Для этих БД приложение всегда использует транзакции, даже если никогда явно не
вызывает beginTransaction
.
Приложение обязано использовать commitTransaction
или rollbackTransaction
для завершения транзакции.
Настоятельно советуем всегда использовать явный контроль транзакций при
выполнении изменений в БД. Это гарантирует, что изменения будут сделаны или
отменены вместе. Кроме того, при использовании обновляемых курсоров Вы также
всегда должны использовать явные транзакции для контроля за целостностью Ваших
данных в период между чтением данных (с помощью next
) и их
изменением (с помощью insertRow
,
updateRow
или deleteRow
).
Как указано в разделе "Изменение Информации Базы Данных",
использование явного контроля транзакций и обновляемых курсоров необходимо для
того, чтобы исключить ошибки в некоторых БД, таких как Oracle и Informix.
Используйте следующие методы объектов database
или Connection
для явного управления транзакциями:
beginTransaction
стартует новую транзакцию. Все действия,
модифицирующие базу данных, группируются в данную транзакцию, называемую
текущей транзакцией.commitTransaction
подтверждает текущую транзакцию. Этот метод
пытается подтвердить все действия, выполненные после последнего вызова метода beginTransaction
.rollbackTransaction
откатывает текущую транзакцию. Этот метод
отменяет все изменения, сделанные с момента последнего вызова метода beginTransaction
.NO
LOG
,
не поддерживает транзакции, и Вы получите ошибку при использовании данных
методов.
Сервис LiveWire Database Service не поддерживает вложение транзакций. Если Вы
вызовете beginTransaction
несколько раз до подтверждения или отката
первой открытой Вами транзакции, Вы получите ошибку.
Для объекта database
максимум области
видимости транзакции ограничен текущим клиентским запросом (HTML-страницей) в
приложении. Если приложение существует на странице до вызова метода commitTransaction
или rollbackTransaction
, то транзакция автоматически подтверждается
или откатывается на основе установок параметра
commitflag
, задаваемого при соединении с БД.
Для объектов Connection
область видимости транзакции ограничена
периодом существования этих объектов. Если Вы освобождаете соединение или
закрываете пул соединений до вызова методов
commitTransaction
или rollbackTransaction
, то
транзакция автоматически подтверждается или откатывается на основе установок
параметра
commitflag
, задаваемого при соединении с БД методом connect
или в конструкторе DbPool
.
Если текущая транзакция отсутствует (то есть, если приложение не вызывало
beginTransaction
), вызовы методов commitTransaction
и rollbackTransaction
могут привести к ошибке в БД.
Транзакция может работать с разными объёмами данных. Пример из раздела "Изменение Информации Базы Данных" создаёт одну транзакцию для модифицирования всех рядов курсора. Если в Вашем курсоре небольшое количество рядов, такой подход будет оправданным.
Если, однако, Ваш курсор возвращает тысячи рядов, Вы можете обработать этот курсор в нескольких транзакциях. Такой подход снизит размер транзакций и улучшит доступ к информации.
Если Вы разбиваете Ваш процесс на несколько транзакций, убедитесь, что вызов
next
и ассоциированный вызов updateRow
или deleteRow
происходят внутри одной транзакции. Если Вы получаете ряд в одной транзакции,
завершаете её, а затем пытаетесь обновить или удалить ряд, Вы можете
получить ошибку в БД.
Выбор способа обработки транзакции зависит от целей Вашего приложения. Нужно обратиться к документации создателя БД для получения информации о том, как использовать транзакции для данного типа БД.
Двоичные данные для мультимедиа-содержимого, такого как изображение или звуковой файл, хранятся в БД в виде большого двоичного объекта/binary large object (BLOb). Можно использовать технику двух видов для обработки бинарных данных в приложениях JavaScript:
Blob
.
Если Вам не нужно хранить BLOb-данные в БД,
Вы можете хранить в БД имена файлов и осуществлять доступ к этим файлам в Вашем
приложении с помощью стандартных тэгов HTML. Например, если Вы хотите вывести
изображение в каждом ряду таблицы БД, Вы можете создать в таблице столбец с
названием
imageFileName
, содержащий имя нужного файла изображения. Затем
можно использовать такое выражение HTML
для показа изображения в каждом ряду:
<IMG SRC=`mycursor.imageFileName`>
Когда курсор проходит по таблице, имя файла в тэге IMG
изменяется
на ссылку на соответствующий файл.
Для того чтобы Вы могли манипулировать реальными
двоичными данными в Вашей БД, машина выполнения JavaScript распознаёт значения
столбца, являющиеся BLOb-данными. То есть,
когда программа создаёт объект Cursor
, если один из столбцов
таблицы БД содержит
BLOb-данные, программа создаёт Blob
-объект для соответствующего
значения в объекте
Cursor
. Вы можете затем использовать методы Blob
-объектов для отображения этих данных. Также, если нужно вставить BLOb-данные в
БД, программа предоставляет Вам для использования глобальную функцию.
В таблице показаны методы и функции для работы с BLOb-данными.
Метод blobImage
вызывает BLOb из БД, создаёт временный файл
специфицированного формата и генерирует HTML-тэг IMG
, который
ссылается на временный файл.
Машина выполнения удаляет временный файл после генерации страницы и отправки её
клиенту.
Метод blobLink
вызывает BLOb-данные из БД,
создаёт временный файл и генерирует гипертекстовую ссылку HTML на этот временный
файл. Машина выполнения удаляет временный файл после того как пользователь
щёлкнет на ссылке или через 60 секунд после того как запрос будет выполнен.
Следующий пример иллюстрирует использование blobImage
и blobLink
для создания временных файлов. В данном случае таблица FISHTBL
содержит 4 столбца: ID(ентификатор), name/имя и два изображения. Одно из них
является уменьшенной копией/thumbnail изображения; другое - большим изображением.
Код записывает HTML для отображения имени, уменьшенной копии и ссылки на большое
изображение.
cursor = connobj.cursor ("select * from fishtbl");
if ( cursor && (connobj.majorErrorCode() == 0) ) {
while (cursor.next()) {
write (cursor.name);
write (cursor.picture.blobImage("gif"));
write (cursor.picture.blobLink("image\gif", "Link" + cursor.id));
write ("<BR>");
}
cursor.close();
}
Если FISHTBL
содержит ряды для 4 рыб, пример может дать на выходе
такой HTML:
Cod <IMG SRC="LIVEWIRE_TEMP9">
<A HREF="LIVEWIRE_TEMP10">Link1 </A> <BR>
Anthia <IMG SRC="LIVEWIRE_TEMP11">
<A HREF="LIVEWIRE_TEMP12">Link2 </A> <BR>
Scorpion <IMG SRC="LIVEWIRE_TEMP13">
<A HREF="LIVEWIRE_TEMP14">Link3 </A> <BR>
Surgeon <IMG SRC="LIVEWIRE_TEMP15">
<A HREF="LIVEWIRE_TEMP16">Link4 </A> <BR>
Если Вам нужно добавить BLOb-данные в БД, используйте глобальную функцию blob
.
Она вводит
BLOb-данные в столбец в обновляемом курсоре. В противоположность blobImage
и
blobLink
, функция blob
является функцией верхнего
уровня, а не методом.
Следующие операторы вставляют BLOb-данные в столбцы ряда, а затем обновляют этот
ряд таблицы
FISHTBL
в БД. В курсоре имеется единственный ряд.
// Начало транзакции.
database.beginTransaction();
// Создание курсора.
fishCursor = database.cursor ("select * from fishtbl where
name='Harlequin Ghost Pipefish'", true);
// Убедимся, что курсор создан.
if ( fishCursor && (database.majorErrorCode() == 0) ) {
// Позиционируем указатель на ряд.
rowexists = fishCursor.next();
if ( rowexists ) {
// Присваиваем/вставляем blob-данные.
fishCursor.picture = blob ("c:\\data\\fish\\photo\\pipe.gif");
// Обновляем ряд.
fishCursor.updateRow ("fishtbl");
// Закрываем курсор и подтверждаем изменения.
fishCursor.close();
database.commitTransaction();
}
else {
// Иначе закрываем курсор и выполняем откат транзакции.
fishCursor.close();
database.rollbackTransaction();
}
}
else {
// Иначе вообще не получаем курсор; откатываем транзакцию.
database.rollbackTransaction();
}
Помните, что backslash (\) это escape-символ в JavaScript. Исходя из этого, Вы обязаны использовать двойной обратный слэш в именах файлов NT, как было в данном примере.
Хранимые процедуры являются неотъемлемой частью работы с реляционной БД. Они являются средством автоматизации часто выполняемых процессов, но их использование даёт также и некоторые другие преимущества:
Сервис LiveWire Database Service
предоставляет два класса для работы с хранимыми процедурами: Stproc
и Resultset
. используя методы этих классов, Вы можете вызывать
хранимые процедуры и манипулировать результатами их работы.
Работа хранимых процедур имеет отличия на разных БД, поддерживаемых сервисом LiveWire Database Service. Самое важное для LiveWire - это отличия в передаче информации в и из хранимой процедуры в приложении на JavaScript. Вы всегда используете параметры ввода хранимой процедуры для передачи информации в хранимую процедуру.
Концептуально имеются несколько способов, которыми можно запросить информацию из хранимой процедуры. Не всегда производитель БД даёт возможность запрашивать информацию любым их этих способов.
Хранимая процедура может выполнять один или более операторов SELECT
,
запрашивая информацию из БД. Вы можете представить эту информацию как
виртуальную таблицу, очень похожую на курсор "только для чтения".
(О курсорах см. раздел "Манипулирование Результатами
Выполнения Запросов с Помощью Курсоров").
LiveWire использует класс Resultset
как
контейнер рядов, возвращаемых одним оператором SELECT
хранимой
процедуры. Если хранимая процедура допускает наличие нескольких операторов SELECT
,
Вы получите отдельные объекты Resultset
для каждого оператора SELECT
.
Метод resultSet
класса Stproc
используется для
получения результирующего набора объектов, а затем методы этих объектов
используются для манипулирования результирующим набором.
БД различных производителей возвращают результирующий набор по-разному:
SELECT
.RESUME
, хранимая процедура может иметь набор этих return-значений.
Это набор напоминает ряды таблицы. LiveWire создаёт один результирующий набор
для вмещения этой виртуальной таблицы.SELECT
. Вы можете открыть несколько ref-курсоров в
хранимой процедуре Oracle, чтобы вместить ряды , возвращаемые разными
операторами SELECT
. LiveWire создаёт отдельные
Resultset
-объекты для каждого ref-курсора.Помимо стандартных параметров ввода, некоторые производители БД разрешают вводить другие типы параметров для хранимых процедур. Параметры вывода хранят информацию при возвращении из хранимой процедуры и параметры ввода/вывода, передающие и возвращающие информацию.
Для большинства БД Вы используете методы outParamCount
и outParameters
класса Stproc
для доступа к параметрам вывода и ввода/вывода. Informix,
однако, не разрешает параметры вывода и ввода/вывода. Соответственно, Вы не
должны использовать методы outParamCount
и outParameters
с хранимыми процедурами Informix.
Как и вызов функции, хранимая процедура может иметь возвращаемое/return значение. Для Oracle и Sybase это return-значение является дополнением к возвращаемому результирующему набору.
Метод returnValue
класса Stproc
используется для
доступа к
return-значению. Однако return-значения для хранимой процедуры Informix
используются для генерации её результирующего набора. Поэтому returnValue
всегда возвращает null для хранимых процедур Informix. Помимо этого, return-значения
недоступны для хранимых процедур ODBC и DB2.
После установки соединения с БД этапы использования хранимой процедуры в Вашем приложении несколько различаются для разных БД:
resultSet
-объект и получаются данные из этого
объекта.Заметьте, что для разных БД Вы можете завершить выполнение Вашей хранимой процедуры получением return-значения или доступом к параметрам вывода. После того как одно их этих двух действий выполнено, Вы не можете больше работать с результирующим набором, созданным при выполнении хранимой процедуры.
В следующих разделах эти этапы рассматриваются по отдельности.
Этот этап выполняется только в DB2.
В DB2 имеются различные системные таблицы, в которые Вы можете записать Вашу хранимую процедуру. В общем, вставка хранимой процедуры в эти таблицы не обязательна. Однако, для того чтобы использовать Вашу хранимую процедуру с LiveWire, Вы обязаны создать вхождения в этих таблицах. Этот этап выполняется вне приложения JavaScript.
Для обычного DB2-сервера Вы обязаны создать системную
таблицу DB2CLI.PROCEDURES
и ввести в неё Ваши DB2-хранимые
процедуры. DB2CLI.PROCEDURES
это таблица-псевдокаталог.
Если Ваш DB2 предназначен для IBM MVS/EA версии 4.1 или
более поздней, Вы обязаны определить имена Ваших хранимых процедур в
таблице-каталоге SYSIBM.SYSPROCEDURES
.
Не забывайте, что Вы используете C, C++ или другой язык для написания DB2-хранимой
процедуры. Типы данных, которые Вы используете в этих языках, не соответствуют
типам данных, доступным в DB2. Следовательно, если Вы добавляете хранимую
процедуру в DB2CLI.PROCEDURES
или в SYSIBM.SYSPROCEDURES
,
убедитесь, что записаны соответствующие типы данных DB2 для параметров хранимой
процедуры, а не типы данных исходных языков.
Информацию о типах данных DB2 и о том, как сделать вхождения в таблицах, см. в документации по DB2.
Этот этап относится только к пользовательским и системным хранимым процедурам DB2, ODBC и Sybase. Вам не нужно определять прототип хранимых процедур БД Oracle или Informix.
Для DB2, ODBC и Sybase программа не может определить в процессе выполнения,
предназначен определённый параметр для ввода, вывода, или для того и другого.
Соответственно, после того как Вы подключились к БД, Вы обязаны создать
прототип, предоставляющий информацию о хранимой процедуре, которую Вы хотите
использовать, через метод storedProcArgs
объекта database
или DbPool
.
Вам нужно использовать по одному прототипу для каждой хранимой процедуры Вашего приложения. Программа игнорирует дополнительные прототипы для одной и той же процедуры.
В прототипе Вы предоставляете имя хранимой процедуры и тип каждого из её
параметров.
Параметр обязан быть: для ввода/input (IN
), вывода/output (OUT
),
а для ввода и вывода -
(INOUT
).
Например, чтобы создать прототип для хранимой процедуры
newhire
, имеющей два параметра ввода и один параметр вывода, можно
использовать такой вызов метода:
poolobj.storedProcArgs("newhire", "IN", "IN", "OUT");
Этот этап применяется ко всем хранимым процедурам.
Для выполнения хранимой процедуры Вы создаёте Stproc
-объект,
используя метод storedProc
объектов database
или Connection
.
Создание такого объекта автоматически вызывает хранимую процедуру. При создании
объекта хранимой процедуры Вы специфицируете имя процедуры и любые параметры
процедуры.
Например, у Вас есть хранимая процедура newhire
, принимающая
параметры - строку и целое число. Следующий вызов метода создаёт spObj
-объект хранимой процедуры и вызывает хранимую процедуру newhire
:
spObj = connobj.storedProc("newhire", "Fred Jones", 1996);
В общем, Вы обязаны предоставить значения для всех параметров ввода/вывода для
хранимой процедуры. Если хранимая процедура имеет значение по умолчанию,
определённое для одного из её параметров, Вы можете использовать директиву "/Default/"
для специфицирования этого значения по умолчанию. Аналогично, если хранимая
процедура может принимать null-значение одного из своих параметров, Вы можете
специфицировать это null-значение либо директивой
"/Null/"
, либо передав само null-значение.
Например, предположим, хранимая процедура demosp
принимает два
строковых параметра и один целочисленный. Вы можете предоставить эти параметры
таким образом:
spobj = connobj.storedProc("demosp", "Param_1", "Param_2", 1);
Альтернативно, чтобы передать null для второго параметра и использовать значение по умолчанию для третьего параметра, Вы можете использовать один из следующих операторов:
spobj = connobj.storedProc("demosp", "Param_1", "/Null/", "/Default/"); spobj = connobj.storedProc("demosp", "Param_1", null, "/Default/");
В Informix значения по умолчанию обязаны
появляться только после всех специфицированных значений. Например, Вы не можете
использовать /Default/
для второго параметра процедуры, а затем
специфицировать значение для третьего параметра.
Вы можете также использовать директивы "/Default/"
и "/Null/"
для параметров ввода/вывода.
В Oracle хранимая процедура может принимать ref -курсоры
как параметры input/output или как output-параметры. Например, у Вас имеется
хранимая процедура Oracle под названием proc1
, принимающая 4
параметра: ref -курсор, целочисленное значение, другой ref -курсор и другое
целочисленное значение.
Вызов этой хранимой процедуры из SQL Plus может выглядеть так:
execute proc1(refcursor1
, 3,refcursor2
, 5);
Однако, если Вы вызываете эту процедуру из приложения JavaScript, Вы не предоставляете ref -курсор-параметры. Вместо этого эквивалент может быть таким:
spobj = connobj.storedProc("proc1", 3, 5);
О параметрах вывода см. "Работа с Параметрами Вывода." Параметры вывода не могут быть null; однако Вы можете присвоить null-значение параметрам ввода или ввода/вывода.
В таблице дано резюме по методам объекта хранимой процедуры.
Stproc
- методыЭтот этап применяется ко всем хранимым процедурам.
Как указано в разделе "Результирующие наборы", разные БД
возвращают результирующие наборы разными способами. Например, у Вас имеется
таблица CUSTINFO
со столбцами
id
, city
и name
. В Sybase Вы можете
использовать такую процедуру для получения первых 200 рядов таблицы:
create proc getcusts as
begin
select id, name, city from custinfo where custno < 200
end
Если CUSTINFO
является таблицей Informix, эквивалентная процедура в Informix
может быть:
create procedure getcusts returning int, char(15), char(15);
define rcity, rname char (15);
define i int;
foreach
select id, name, city into i, rname, rcity
from custinfo
where id < 200;
return i, rname, rcity with resume;
end foreach;
end procedure;
Если CUSTINFO
- таблица Oracle, эквивалентная процедура Oracle
может быть:
create or replace package orapack as
type custcurtype is ref cursor return custinfo%rowtype
end orapack;
create or replace custresultset (custcursor inout orapack.custcurtype)
as begin
open custcursor for select id, name, city from custinfo
where id < 200
end custresultset;
Во всех случаях Вы создаёте resultSet
-объект для получения информации из хранимой процедуры. Вы делаете это через
использование метода resultSet
объекта хранимой процедуры так:
resObj = spObj.resultSet();
Как и для Cursor
-объектов, resultSet
-объекты
содержат текущий ряд, то есть ряд, на котором стоит указатель в результирующем
наборе. Вначале указатель позиционирован перед первым рядом результирующего
набора. Чтобы увидеть значения рядов результирующего набора, Вы используете
метод next
для перемещения указателя по рядам результирующего
набора, как показано в следующем примере:
spobj = connobj.storedProc("getcusts");
if ( spobj && (connobj.majorErrorCode() == 0) ) {
// Создаётся новыйresultSet
-объект.
resobj = spobj.resultSet();
// Перед тем как продолжить, убедитесь, что Вы получили результирующий набор.
if ( resobj && (connobj.majorErrorCode() == 0) ) {
// Сначала перемещает указательresultSet-объекта
к первому // ряду результирующего набора, а затем циклически проходит по рядам.
while (resObj.next())
{
write("<TR><TD>" + resObj.name + "</TD>");
write("<TD>" + resObj.city + "</TD>");
write("<TD>" + resObj.id + "</TD></TR>");
}
resobj.close();
}
}
До тех пор, пока в результирующем наборе имеется следующий ряд, метод next
возвращает
true
и перемещает указатель к следующему ряду. Если указатель
достиг последнего ряда результирующего набора, метод next
возвращает false
.
Предыдущий пример работает с хранимой процедурой Sybase. В этом случае resultSet
-объект содержит именованное свойство для каждого столбца результирующего набора.
Для процедур Informix и DB2, по контрасту, объект не содержит именованных
столбцов. В этом случае Вы можете получить значения, ссылаясь на позицию столбца.
Так, для Informix и DB2 Вы можете использовать такой код для вывода аналогичной
информации:
spobj = connobj.storedProc("getcusts");
if ( spobj && (connobj.majorErrorCode() == 0) ) {
// Создаётся новыйresultSet
-объект.
resobj = spobj.resultSet();
// Перед тем как продолжить, убедитесь, что Вы получили результирующий набор.
if ( resobj && (connobj.majorErrorCode() == 0) ) {
// Сначала перемещает указательresultSet-объекта
к первому // ряду результирующего набора, а затем циклически проходит по рядам.
while (resObj.next())
{
write("<TR><TD>" + resObj[1] + "</TD>");
write("<TD>" + resObj[2] + "</TD>");
write("<TD>" + resObj[0] + "</TD></TR>");
}
resobj.close();
}
}
Вы можете использовать позицию столбца для результирующих наборов любой БД, а не только с Informix и DB2. Вы можете использовать имя столбца для хранимой процедуры всех типов БД, а не только Informix или DB2.
Хранимая процедура Sybase, Oracle, DB2 или ODBC может
создавать несколько результирующих наборов. В этом случае хранимая процедура
предоставляет один resultSet
-объект для каждого набора.
Предположим, Ваша хранимая процедура выполняет такие операторы SQL:
select name from customers where id = 6767
select * from orders where id = 6767
Вы можете использовать несколько resultSet
-объектов, генерируемых
этими операторами, таким образом:
// Этот оператор нужен для DB2, ODBC и Sybase.
poolobj.storedProcArgs("GetCustOrderInfo","IN");
spobj = connobj.storedProc("GetCustOrderInfo",6767);
if ( spobj && (connobj.majorErrorCode() == 0) ) {
resobj1 = spobj.resultSet();
// Перед тем как продолжить, убедитесь, что результирующий набор существует.
if ( resobj1 && (connobj.majorErrorCode() == 0) ) {
// Первый результирующий набор возвращает только один ряд.
// Убедитесь, что ряд содержит данные.
rowexists = resobj1.next();
if ( rowexists )
write("<P>Customer " + resobj1.name +
" has the following orders:</P>");
resobj1.close();
// Второй результирующий набор возвращает один ряд для каждого заказа, // помещённого пользователем. Убедитесь, что ряды содержат данные.
resobj2 = spobj.resultSet();
var i = 0;
if ( resobj2 && (connobj.majorErrorCode() == 0) ) {
write("\nOrder# Quantity Total</P>");
while(resobj2.next()) {
write(resobj2.orderno + " " + resobj2.quantity
+ " " + resobj2.Totalamount + "</P>");
i++;
}
resobj2.close();
write("Customer has " + i + " orders.</P>");
}
else write("Customer has no orders.</P>");
}
}
spobj.close();
В качестве примера использования нескольких ref-курсоров Oracle
в хранимой процедуре см. описание класса Resultset
в книге
Серверный
JavaScript. Справочник
.
В таблице дано резюме по методам и свойствам класса Resultset
.
Resultset
- методы и свойства
resultSet
-объект является объектом "только
для чтения"/read-only, объектом последовательного стиля/sequential-style.
Исходя из этого, класс не имеет методов insertRow
, deleteRow
и
updateRow
, определённых для Cursor
-объектов.
Объект resultSet
не является бесконечно действующим. Вообще, когда
хранимая процедура стартует,
не допускается никакое взаимодействие между клиентом БД и сервером БД, пока
хранимая процедура не завершит выполнение. Есть три ситуации, когда
результирующий набор является недействующим:
database.beginTransaction();
spobj = database.storedProc("getcusts");
resobj = spobj.resultSet();
database.commitTransaction();
// Неверно! Результирующий набор больше не действует!
col1 = resobj[0];
resultSet
-объекты до
вызова методов returnValue
или outParameters
объекта
хранимой процедуры. Если Вы вызвали один из этих методов, Вы не сможете больше
получать данные из результирующего набора и не сможете получить дополнительные
результирующие наборы. См. о работе этих методов раздел "Работа
с Return-Значениями".spobj = database.storedProc("getcusts");
resobj = spobj.resultSet();
retval = spobj.returnValue();
// Неверно! Результирующий набор больше не действует!
col1 = resobj[0];
resultSet
-объекты до вызова
методов cursor
или SQLTable
ассоциированного
соединения. Как только Вы вызовете cursor
или
SQLTable
, результирующий набор станет недоступен. Например,
следующий код неверен:spobj = database.storedProc("getcusts");
resobj = spobj.resultSet();
curobj = database.cursor ("select * from orders");
// Неверно! Результирующий набор больше не доступен!
col1 = resobj[0];
resultSet
-объектами до вызова методов cursor
или SQLTable
ассоциированного соединения.
В ODBC, если Вы получаете курсор, выполняете доступ к результирующему набору,
а затем используете курсор,
Cursor
-объект становится недоступным. Например, следующий код
неверен:spbobj = database.storedProc("getcusts");
resobj = spobj.resulSet();
curobj = database.cursor ("select * from orders");
col1 = resobj[0]; // Неверно! Курсор больше не доступен.
curobj.next();
Этот этап относится к хранимым процедурам Sybase и Oracle. Для процедур Informix, ODBC
и DB2 метод returnValue
всегда возвращает null.
Если Ваша хранимая процедура возвращает значение (return value), Вы можете
получить к нему доступ с помощью метода returnValue
.
В DB2, ODBC и Sybase Вы обязаны использовать хранимые
процедуры и курсоры последовательно. Вы не можете их перемешивать. Исходя из
этого, Вы обязаны дать системе знать, что Вы закончили использование хранимой
процедуры, прежде чем сможете работать с курсором. Это выполняется через вызов
метода returnValue
объекта хранимой процедуры. Этот метод выдаёт return-значение
хранимой процедуры (если она его имеет) и завершает выполнение хранимой
процедуры. Вы должны также закрыть все объекты, относящиеся к хранимым
процедурам, когда завершаете их использование.
Для DB2, ODBC и Sybase Вы обязаны запросить resultSet
-объекты до вызова метода returnValue
. После того как Вы вызвали returnValue
,
Вы больше не сможете получить данные из результирующего набора и не сможете
получить какие-либо дополнительные результирующие наборы. Вы должны вызывать returnValue
после того, как обработали результирующий набор, но до запроса параметров вывода.
Этот этап касается хранимых процедур Sybase, Oracle, DB2 или ODBC. Для процедур Informix методы, обсуждаемые здесь, не применяются.
Чтобы определить количество параметров вывода процедуры (включая параметры и
вывода, и ввода/вывода), Вы используете метод outParamCount
. Вы
можете работать с параметрами вывода хранимой процедуры, используя метод outParameters
объекта. Если outParamCount
возвращает 0, хранимая процедура не
имеет параметров вывода. В этой ситуации не вызывайте
outParameters
.
Например, предположим, Вы создали хранимую процедуру, которая находит фамилию служащего по заданному ID. Если имеется фамилия служащего, ассоциированная с данным ID, процедура возвращает 1, и её output-параметр содержит фамилию служащего. Иначе параметр вывода является пустым. Следующий код выводит фамилию служащего или сообщение о том, что фамилия не найдена:
id = 100;
getNameProc = connobj.storedProc("getName", id);
returnValue = getNameProc.returnValue();
if (returnValue == 1)
write ("Name of employee is " + getNameProc.outParameters(0));
else
write ("No employee with id = " + id);
Предположим, хранимая процедура имеет один параметр ввода, один параметр ввода/вывода и один параметр вывода. Далее примем, что вызов хранимой процедуры отсылает значение параметра ввода и параметра ввода/вывода, как показано здесь:
spobj = connobj.storedProc("myinout", 34, 56);
Метод outParameters
возвращает любые параметры ввода/вывода до
того как возвратит первый параметр вывода.
В предыдущем примере, если Вызывается
outParameters(1)
, возвращается значение, возвращаемое хранимой
процедурой. И наоборот,
если вызывается outParameters(0)
, метод возвращает 56. Это
значение, переданное хранимой процедуре в позиции параметра ввода/вывода.
Параметры вывода не могут быть null; однако Вы можете присвоить null-значение
параметра ввода или ввода/вывода.
В DB2, ODBC и Sybase Вы обязаны запрашивать resultSet
-объекты и использовать метод returnValue
до того, как вызываете outParameters
.
После того как Вы вызвали returnValue
или outParameters
,
Вы больше не сможете получить данные из результирующего набора и не сможете
получить какие-либо дополнительные результирующие наборы. Вы должны вызывать outParameters
после обработки результирующего набора и любых return-значений.
Хранимые процедуры
Informix и Sybase могут возвращать коды ошибки, используя механизм исключений.
После того как Вы запустили процедуру на выполнение, Вы можете запрашивать эти
коды ошибок и сообщения об ошибках, используя методы majorErrorCode
и majorErrorMessage
ассоциированного объекта database
или
Connection
.
Например, у Вас имеется хранимая процедура Informix:
create procedure usercheck (user varchar(20))
if user = 'LiveWire' then
raise exception -746, 0, 'User not Allowed';
endif
end procedure
Если Вы запустите эту процедуру на выполнение, Вы сможете проверять, появилась ли ошибка, а затем получить доступ к коду ошибки и сообщению о ней:
spobj = connobj.storedProc("usercheck");
if ( connobj.majorErrorCode() ) {
write("The procedure returned this error code: " +
connobj.majorErrorCode());
write("The procedure returned this error message: " +
connobj.majorErrorMessage());
}
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассматривается настройка Вашей Базы Данных для запуска со службой LiveWire Database Service. Вы должны прочесть эту главу и раздел "Информация Конфигурации" перед тем как использовать LiveWire с Вашими JavaScript-приложениями.
Могут понадобиться изменения в клиентах БД. Дополнительно см. Enterprise Server 4.x Release Notes .
В этой главе имеются следующие разделы:
Одновременно с версиями 3.x и 4.x Netscape-серверов Вы обязаны установить клиентскую библиотеку БД (и к тому же - определённую версию), если хотите использовать LiveWire Database Service. Вы также обязаны сконфигурировать эту клиентскую библиотеку для работы с LiveWire.
Netscape-серверы не поставляются вместе клиентскими библиотеками БД. Вам нужно связаться с поставщиком Вашей БД на предмет получения соответствующей библиотеки. Вам необходимо установить и сконфигурировать клиентские библиотеки только тех БД, которые Вы будете использовать.
Если Вы устанавливаете БД на другую машину (не на ту, где установлен web-сервер), Вы обязаны иметь установленную клиентскую библиотеку БД на машине с web-сервером. Вы обязаны получить соответствующее лицензионное разрешение от поставщика Вашей БД. Netscape не сделает это за Вас.
Требования по конфигурированию Вашей БД могут отличаться в зависимости от того, находятся ли БД и web-сервер на одной или на разных машинах. Если они на одной машине, последующая информация будет касаться локальной конфигурации; если на разных машинах - удалённой конфигурации.
В этой главе рассмотрены только те аспекты установки клиента БД, которые имеют отношение к использованию с LiveWire. Для получения общей информации об установке клиента БД обратитесь к соответствующей документации поставщика БД.
После выполнения установки, описанной в данной главе, Вы можете использовать
приложение-образец dbadmin
для проверки правильности работы Вашего
соединения с БД. Вы используете это приложение
JavaScript для соединения с Вашим сервером БД и выполнения различных несложных
задач, таких как выполнение оператора SELECT
и отображение
результатов или отправки произвольных SQL-команд серверу.
Поскольку dbadmin
может использоваться для модификации и удаления
таблиц БД, доступ к ней автоматически ограничен, если Вы выбрали защищённый
режим Application Manager. Дополнительно об ограничении Application Manager см. "Управление
Доступом к Приложению".
Первое, что Вы обязаны сделать при использовании dbadmin
, это
соединиться с БД. Выберите
Connect to Database. Появится форма, показанная на Рисунке 10.1,
где вы можете ввести информацию о соединении. Введите параметры и щёлкните Connect,
чтобы попытаться установить соединение с сервером. О параметрах соединения см. "Пулы
Соединений Базы Данных", а также описание метода connect
в
книге
Серверный JavaScript.
Справочник
.
dbadmin
Если соединение установилось, появляется страница Execute Query. Это означает, что ваша БД соответствующим образом сконфигурирована для работы с LiveWire Database Service. Если соединиться не удалось, появится страница Database Connect Error. В этом случае убедитесь, что Вы следовали инструкциям по конфигурированию конкретной БД и "железа".
В следующей таблице дано резюме по конкретным поставщикам БД, поддерживаемых на каждой платформе
Netscape Enterprise Server 3.x (относительно Netscape Enterprise Server 4.0 см.
замечания Enterprise Server 4.0 Release Notes).
Данные поставщики БД не поддерживаются сервером
Netscape FastTrack Server.
Поставщик БД | AIX | DEC | Irix 6.2 | >HP-UX 11.0 | Solaris 2.5.1/2.6 | Windows NT 4.0 w/ SP4 |
---|---|---|---|---|---|---|
DB2 | CAE 2.1.2 | |||||
Informix | ||||||
Microsoft ODBC |
MS SQL Server 6.5 (драйвер 3.60) | |||||
Oracle 1 | ||||||
Sybase |
В следующей таблице дано резюме по поддержке ODBC в Windows NT для Netscape Enterprise Server и Netscape FastTrack Server.
ODBC-Компонент | Windows NT 4.0 SP4 |
---|---|
ODBC-Драйверы | |
Обратите внимание, что ODBC не поддерживается на платформах Unix.
Visigenic больше не обновляет свои существующие ODBC-драйверы или SDK. Вместо этого Visigenic избрал INTERSOLV для предоставления пути для обновления этих драйверов и продуктов. (См. http://www.intersolv.com/ newsstand/pr9711191.html). Visigenic предоставит документ, покрывающий различия между драйверами Visigenic и INTERSOLV ODBC. В таблице перечислены возможности поддерживаемых ODBC-драйверов для NT.
SQL-БД | Соединение | SQL passthrough/пропуск | Курсор "Только для чтения" | Обновляемый курсор | Хранимые процедуры |
---|---|---|---|---|---|
Обратите внимание, что ODBC не поддерживается на платформах Unix.
Для использования DB2-сервера у вас должен быть Netscape Enterprise Server. Вы не можете получить доступ к DB2 из Netscape FastTrack Server.
Все платформы: Установить DB2-клиент версии 2.1.2. Для Solaris
Вам нужен APAR #JR10150. См. DB2-документацию
http://www.software.ibm.com/data/db2/
.
Если серверы БД и web находятся на разных машинах: Чтобы определить возможность соединения с DB2-сервером, вы можете выполнить следующую команду из командной строки DB2:
DB2 TERMINATE # эта команда даёт возможность работы команды catalog
DB2 CONNECT TO имяБД USERID idпользователя USING пароль
Если вы используете в приложении типы данных BLOB
или CLOB
,
вы обязаны установить опцию longdatacompat
в Вашем файле $DB2PATH/db2cli.ini
в 1. Например:
[Database name]
longdatacompat=1
Если Вы сделали изменения в файле db2cli.ini
, Вы обязаны
рестартовать Ваш web-сервер, чтобы они начали действовать.
Только для Unix: Вы обязаны установить следующие переменные окружения/environment variables:
При использовании сервера Informix Вы обязаны иметь Netscape Enterprise Server. Вы не можете получить доступ к Informix из Netscape FastTrack Server.
Если серверы БД и web находятся на разных машинах, следуйте инструкциям раздела "Удалённый Informix."
Если серверы БД и web находятся на одной машине, следуйте инструкциям раздела "Локальный Informix."
Только для Unix: Установите Informix ESQL/C Runtime Client 7.22 (называемый также Informix I-Connect), а затем установите следующие переменные окружения:
Вы обязаны также изменить $INFORMIXDIR/etc/sqlhosts
,
чтобы оно соответствовал имени службы в файле /etc/services
. О том,
как это сделать, см. документацию Informix.
Только для NT: Установите Informix ESQL/C Runtime Client 7.20 (называемый также Informix I-Connect.) В процессе установки все необходимые переменные окружения также устанавливаются. Вы используете подходящую утилиту Informix для ввода нужной информации об удалённом сервере, с которым Вы хотите устанавливать соединение.
Если Ваш web-сервер запускается как System, убедитесь,
что у вас запущен regcopy.exe
.
Все платформы: В зависимости от используемой службы имён,
Вам может понадобиться также отредактировать соответствующий файл - добавить IP-адрес
удалённой хост-машины, с которой Вы соединяетесь.
В NT это файл winnt\system32\drivers\etc\hosts
ниже директории NT SystemRoot.
На Unix это файл /etc/hosts
.
В той же директории добавьте следующую строку к файлу services
:
ifmx_srvc
port
/tcp # Informix Online Server
где ifmx_srvc это имя Вашей службы, а
port - номер порта. Номер порта обязан совпадать с номером порта на
удалённой машине, который прослушивается Informix-сервером. Чтобы эта строка
начала действовать, Вы обязаны либо вставить как минимум один пробел после tcp
,
либо поместить комментарий в конце этой строки. Например, если Ваша служба
Informix называется ifmx1 и номер порта 1321, Вы добавляете следующую строку:
ifmx1 1321/tcp # Informix Online Server
Если вы устанавливаете Informix локально, Вы обязаны установить клиента Informix до установки Informix-сервера.
Только для Unix: Если Вы используете 7.22 Online Server
для Unix, процесс инсталяции создаёт соответствующую структуру директорий и файл sqlhosts
.
Вы обязаны установить переменные окружения, как для удалённого сервера.
Только для NT: Вы должны установить Online Server 7.20. Это установит также и клиента;
дополнительные шаги не нужны. Если Ваш web-сервер запускается как System,
убедитесь, что у вас запущен regcopy.exe
.
Все платформы: О возможностях и об используемых ODBC-драйверах см. "Поддерживаемые Клиенты БД и ODBC-Драйверы."
Вам необходимо иметь соответствующие ODBC-драйверы для БД, с которой Вы устанавливаете соединение. Также необходимо иметь дополнительные файлы ODBC-соединений/connectivity.
Большинство программных продуктов, предоставляющие ODBC connectivity, поддерживают ODBC-драйвер или драйверы и ODBC connectivity.
Только для NT: В настоящее время Netscape сертифицирован с ODBC Manager версии 2.5. Если у вас имеется доступ к ODBC-драйверу, но не к файлам ODBC connectivity, Вы можете получить их в MS ODBC SDK. Чтобы получить обновлённые файлы для Access, Foxpro и Excel, Вам может понадобиться патч WX1350 от Microsoft.
Только для Unix: Для ODBC на Unix вы можете использовать драйвер от Visigenic либо от OpenLink. Если Вы используете драйвер от Visigenic, следуйте инструкциям раздела "Visigenic ODBC-Драйвер (только Unix)." Если Вы используете драйвер от OpenLink, следуйте инструкциям раздела "OpenLink ODBC-Драйвер (только Solaris)."
Могут быть созданы два типа источников данных:
Источник данных описывает параметры соединения для каждой БД, необходимые для ODBC connectivity. Источник данных определяется чрез использование ODBC-администратора. Если ODBC установлен, администратор также устанавливается. Каждый ODBC-драйвер требует различной информации для определения источника данных.
Установите брокер запросов OpenLink Workgroup Edition ODBC Driver на сервер БД. Он должен быть запущен, до того как Вы сможете соединиться с БД с использованием агента запросов OpenLink.
Установите агента запросов (request agent) в клиенте OpenLink Generic ODBC версии 1.5 на клиентской машине БД.
Переименуйте или скопируйте файл драйвера менеджера агента
запросов из libiodbc.so
в libodbc.so
в директории $ODBCDIR/lib
,
где $ODBCDIR
это директория, в которой установлен ODBC.
Когда Вы устанавливаете Ваш сервер, Вы делаете так, чтобы он запускался как
некоторый пользователь: root, nobody или конкретный пользователь сервера.
Выбранный Вами пользователь должен иметь домашнюю директорию, которую, возможно,
понадобится создать. Например, по умолчанию домашняя директория для пользователя nobody
на Irix это /dev/null
. Если Вы устанавливаете Ваш сервер на Irix как nobody,
Вы обязаны создать для пользователя nobody другую домашнюю директорию.
В этой домашней директории Вы обязаны иметь файл .odbc.ini
.
Например, если сервер запускается как root, этот файл находится в директории (/).
Установите следующие переменные окружения:
LD_LIBRARY_PATH |
(Solaris и Irix) Добавляет размещение ODBC-библиотек к данной переменной. |
UDBCINI |
При установке сервера он запускается как пользователь: root, nobody
или определённый пользователь сервера. Выбранный вами пользователь должен иметь
существующую домашнюю директорию, которую, возможно, понадобится создать.
Например, домашняя директория по умолчанию для пользователя nobody в системе Irix
это /dev/null
. Если Вы устанавливаете Ваш сервер в Irix как nobody,
Вы обязаны дать пользователю nobody другую домашнюю директорию.
В этой домашней директории обязан находиться файл .odbc.ini
.
Например, если Вы запускаете сервер как root, этот файл находится в корневой директории (/).
Установите следующие переменные окружения:
Visigenic больше не обновляет свои существующие ODBC--драйверы и SDK. Вместо этого Visigenic избрал INTERSOLV для предоставления обновлённого пути для этих драйверов и продуктов. (См. http://www.intersolv.com/ newsstand/pr9711191.html). Visigenic будет предоставлять документ, проясняющий операционные различия между ODBC-драйверами Visigenic и INTERSOLV.
Чтобы использовать сервер Oracle, Вы обязаны иметь Netscape Enterprise Server. Вы не можете получить доступ к Oracle из Netscape FastTrack Server.
Если БД и web-сервер находятся на разных машинах, следуйте инструкциям раздела "Удалённый Oracle."
Если БД и web-сервер находятся на одной машине, следуйте инструкциям раздела "Oracle Local."
Только для Unix: Убедитесь, что Вы можете соединиться с Вашей БД Oracle через SQL*Net. Если
Вы закончили инсталяцию, Вы можете использовать loopback-тест для проверки
корректности соединения. Например,
из sqlplus
Вы можете попытаться соединиться с Вашей БД с помощью следующей команды:
connect username/password@service_name
sqlplus username/password@service_name
В этих командах Вы используете service_name из
Вашего файла tnsnames.ora
.
Только для NT: Вы обязаны установить клиентское программное обеспечение Oracle 7.3.2 для NT. Клиенты Oracle 7.1 и 7.2 не поддерживаются. Вы обязаны также создать файлы конфигурации Oracle, используемые соответствующей утилитой конфигурирования Oracle.
Только для Unix: Прежде чем Вы сможете подключиться к Oracle под Irix, Вы обязаны иметь соответствующие патчи для Irix. См. в Enterprise Server 3.x Release Notes информацию о необходимых патчах.
Вы обязаны установить клиентов Oracle 7.3.x для Unix. Клиенты Oracle 7.1 и 7.2 не поддерживаются.
Вы обязаны установить следующие переменные окружения:
Если эти переменные не установлены надлежащим образом, Oracle возвращает код ошибки ORA-1019 при первой попытке установления соединения. Об обработке ошибок см. Главу 12, "Обработка Ошибок для LiveWire."
Только для Unix: Прежде чем Вы сможете подключиться к Oracle под Irix, Вы обязаны иметь соответствующие патчи для Irix. См. в Enterprise Server 3.x Release Notes информацию о необходимых патчах.
Все платформы: Вы обязаны установить Oracle Workgroup Enterprise Server 7.3.2 (NT) или Enterprise Server 7.3.x (Unix). Клиенты Oracle 7.1 и 7.2 не поддерживаются. Уточните у вашего поставщика сервера, совместима ли Ваша версия Oracle-сервера с Oracle-клиентом.
Вы обязаны установить следующие переменные окружения:
ORACLE_HOME |
Специфицирует директорию верхнего уровня, в которой установлен Oracle. |
ORACLE_SID |
Специфицирует Oracle System Identifier (Системный Идентификатор). |
Если у Вас локальный сервер БД Oracle, Вы обязаны передавать пустую строку в
качестве второго аргумента методу connect
объектов database
или DbPool
или конструктору DbPool
. Таким образом, эти
методы используют значение переменной окружения ORACLE_SID
. Например:
database.connect ("ORACLE", "" "user", "password", "");
Об инсталяции Oracle см. дополнительно документацию по Oracle.
Чтобы использовать сервер Sybase, Вы обязаны иметь Netscape Enterprise Server. Вы не можете получить доступ к Sybase из Netscape FastTrack Server.
Если БД и web-сервер находятся на разных машинах, следуйте инструкциям раздела "Удалённый Sybase."
Если БД и web-сервер находятся на одной машине, следуйте инструкциям раздела "Локальный Sybase."
Кроме того, если Вы используете Unix-платформу, и Sybase имеет многопоточный драйвер для этой платформы, следуйте инструкциям раздела "Sybase (только для Unix)." См. в Enterprise Server 3.x Release Notes список Unix-платформ, на которых Sybase имеет многопоточный драйвер.
Только для Unix: Установите следующие переменные окружения:
SYBASE | Директория верхнего уровня, в которой установлен Sybase. |
LD_LIBRARY_PATH |
Для Solaris Вы обязаны также следовать инструкциям раздела "Sybase (только для Unix)."
Все платформы: Вы обязаны установить SYBASE Open Client/C. Поддерживаемые поставщики перечислены в разделе "Поддерживаемые Клиенты БД и ODBC-Драйверы."
Вы можете использовать подходящую утилиту Sybase для
ввода в файл sql.ini
(NT) и файл interfaces
(все
платформы) информации об удалённом сервере, с которым Вы хотите установить
соединение. Дополнительно см. документацию Sybase.
Только для Unix: Установите следующие переменные окружения:
SYBASE | Директория верхнего уровня, в которой установлен Sybase. |
LD_LIBRARY_PATH |
(DEC) Обязана содержать
|
Для Solaris Вы обязаны также следовать инструкциям раздела "Sybase (только для Unix)."
Все платформы: Установите Sybase SQL Server версии 11.1; клиентская часть устанавливается вместе с сервером. Поддерживаемые версии перечислены в разделе "Поддерживаемые Клиенты БД и ODBC-Драйверы."
Вы можете использовать подходящую утилиту Sybase для ввода в файл sql.ini
(NT)
и файл interfaces
(все платформы) информации об удалённом сервере,
с которым Вы хотите установить соединение. Дополнительно см. документацию Sybase.
На некоторых Unix-платформах Sybase имеет и однопоточный, и многопоточный драйверы. Если Sybase имеет многопоточный драйвер для конкретной Unix-машины, Вы обязаны использовать этот многопоточный драйвер с LiveWire. На этих платформах Ваш web-сервер будет вести себя непредсказуемо с однопоточным драйвером. Это требование применяется как для локальных, так и для удалённых соединений. оно не применяется к Windows-платформам.
См. в Enterprise Server 3.x Release Notes список Unix-платформ, на которых Sybase имеет многопоточный драйвер.
Чтобы убедиться в том, что Вы используете многопоточный
драйвер, вы обязаны отредактировать Ваш файл
$SYBASE/config/libtcl.cfg
. Он содержит пару строк, которые включают
однопоточный, либо многопоточный драйвер. Вы обязаны иметь одну из этих строк
закомментированную, а другую активную. Например, на Solaris найдите эти строки:
[DRIVERS]
;libtli.so=tcp unused ; Это беспоточный tli-драйвер.
libtli_r.so=tcp unused ; Это поточный tli-драйвер.
Удостоверьтесь, что строка для однопоточного драйвера закомментирована, а строка
многопоточного драйвера - не закомментирована. Имена файлов различаются на
разных платформах, но эти строки всегда расположены в разделе DRIVERS
и всегда закомментированы, чтобы различать однопоточный и многопоточный драйверы.
ПРИМЕЧАНИЕ:
Если вы хотите использовать утилиту Sybase
isql
, Вы обязаны использовать беспоточныйtli
-драйвер. В этом случае строка дляlibtli_r.so
обязана быть закомментирована. Об использовании этого драйвера см. документацию по Sybase.
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассмотрены типы ошибок, которые могут появиться при работе с реляционными БД.
В главе имеются следующие разделы:
При написании приложения на языке JavaScript Вы должны знать о различных ошибках. В тех случаях, когда Вы используете LiveWire Database Service для взаимодействия с реляционной БД, ошибки могут возникать по разным причинам. Например, SQL-операторы могут не выполняться из-за ограничений ссылочной целостности/referential integrity, потери привилегий доступа, блокировки записи или таблицы в многопользовательской БД и т.д. Если акция терпит неудачу, сервер БД возвращает сообщение об ошибке с указанием причины.
Ваш код должен проверять наличие ошибок и обрабатывать их соответствующим образом.
Return-значение методов объектов LiveWire может указывать, имеется ошибка или нет. Методы могут возвращать значения различных типов. В зависимости от типа Вы можете получать различную информацию о возможных ошибках.
Если метод возвращает число, return-значение может
представлять число либо статус-код. Например, Cursor.columns
возвращает количество столбцов в курсоре, а Cursor.updateRow
возвращает число, обозначающее наличие или отсутствие ошибки.
Методы Cursor.columns
и Resultset.columns
возвращают числовое значение.
Следующие методы возвращают число, обозначающее статус-код:
Connection.beginTransaction
Connection.commitTransaction
Connection.execute
Connection.majorErrorCode
Connection.minorErrorCode
Connection.release
Connection.rollbackTransaction
Connection.SQLTable
Cursor.close
Cursor.deleteRow
Cursor.insertRow
Cursor.updateRow
database.beginTransaction
database.connect
database.commitTransaction
database.disconnect
database.execute
database.majorErrorCode
database.minorErrorCode
database.rollbackTransaction
database.SQLTable
database.storedProcArgs
DbPool.connect
DbPool.disconnect
DbPool.majorErrorCode
DbPool.minorErrorCode
DbPool.storedProcArgs
Resultset.close
Stproc.close
Если числовое return-значение метода указывает статус-код, 0 означает успешное
выполнение, а ненулевое число указывает на ошибку. Если статус-код ненулевой, Вы
можете использовать методы
majorErrorCode
и majorErrorMessage
ассоциированного
объекта
Connection
, database
или DbPool
для того,
чтобы найти информацию об ошибке. В некоторых случаях методы minorErrorCode
и
minorErrorMessage
предоставляют дополнительную информацию об ошибке.
О return-значениях этих методов см. "Методы Работы с Ошибками".
Если метод возвращает объект, он может быть либо реальным объектом, либо null. Если метод возвращает null, возможно, возникла ошибка JavaScript. В большинстве случаев, если ошибка возникла в БД, метод возвращает верный объект, но программа устанавливает код ошибки.
Глобальная функция blob
возвращает объект.
Кроме того, следующие методы также возвращают объект:
Connection.cursor
Connection.storedProc
database.cursor
database.storedProc
DbPool (constructor)
DbPool.connection
Stproc.resultSet
Создаёте ли Вы курсор, результирующий набор или хранимую
процедуру, Вы должны проверять и существование созданного объекта, и возможное
наличие кода ошибки. Вы можете использовать методы
majorErrorCode
и majorErrorMessage
для тестирования
ошибки.
Например, Вы можете создать курсор и проверить его корректность кодом такого вида:
// Создаётся Cursor-объект.
custs = connobj.cursor ("select id, name, city
from customer order by id");
// Прежде чем продолжить, убедитесь, что возвращён реальный курсор // и что нет ошибки БД.
if ( custs && (connobj.majorErrorCode() == 0) ) {
// Получить первый ряд.
custs.next();
// ... процессинг рядов курсора ...
//Закрыть курсор.
custs.close();
}
else
// ... обработка ошибок ...
Следующие методы возвращают Булевы значения:
Connection.connected
Cursor.next
database.connected
DbPool.connected
Resultset.next
Если метод возвращает Boolean-значение, true
обычно указывает на успешное выполнение, в то время как false
указывает на некоторое другое состояние. Return-значение false
не
означает реальной ошибки; оно может означать и условие успешного завершения.
Например, Connection.connected
возвращает false
для указания на то, что Connection
-объект в данное время не
соединён. Это может означать, что ошибка возникла при создании Connection
-объекта или что ранее использованное соединение было намеренно разорвано. Эти
события не являются ошибками метода connected
. Если ошибка возникла
при создании объекта, Ваш код должен отлавливать ошибку в работе этого метода.
Если соединение было разорвано, Вы можете установить его вновь.
Второй пример - Cursor.next
возвращает false
, когда Вы
ряды курсора закончились. Если оператор SELECT
, используемый для
создания Cursor
-объекта, находит таблицу, но не находит столбцы,
соответствующие условию оператора SELECT
, создаётся пустой курсор.
При первой Вашей попытке вызова метода next
для этого курсора он
возвращает
false
. Ваш код должен учитывать такую возможность.
Если метод возвращает строку, Вы обычно не получаете сообщения об ошибке. Если, однако, метод возвращает null, проверьте ассоциированный error-метод.
Следующие методы возвращают строку:
Connection.majorErrorMessage
Connection.minorErrorMessage
Cursor.columnName
database.majorErrorMessage
database.minorErrorMessage
DbPool.majorErrorMessage
DbPool.minorErrorMessage
Resultset.columnName
Некоторые методы не возвращают значение. Вы не можете ничего сказать о возможных ошибках в работе этих методов. Следующие методы не возвращают значения:
Connection.release
Cursor.close
database.disconnect
DbPool.disconnect
Resulset.close
Как уже было сказано, многие методы возвращают числовой
статус-код. Если метод возвращает статус-код, может иметься и соответствующие
код ошибки и сообщение от сервера БД.
LiveWire имеет четыре метода для объектов Connection
, DbPool
и
database
для доступа к error-кодам и сообщениям БД.
Вот эти методы:
majorErrorMessage
: наивысшее
сообщение об ошибке, возвращённое базой данных.minorErrorMessage
: наименьшее сообщение об ошибке, возвращённое
базой данных.majorErrorCode
: наивысший error-код, возвращённый БД. Обычно он
соответствует SQLCODE сервера.minorErrorCode
: второй/secondary error-код, возвращённый базой
данных.Результаты, возвращаемые этими методами, зависят от используемого сервера БД и статус-кода БД. В большинстве случаев Вам нужно рассматривать только наивысший error-код или сообщение об ошибке, чтобы понять суть ошибки. Меньшие error-код и сообщение используются только в некоторых ситуациях.
ПРИМЕЧАНИЕ: Вызов другого метода объектов
Connection
,DbPool
илиdatabase
может зачистить/reset error-коды и сообщения. Чтобы исключить потерю информации, не забудьте проверить эти методы, перед тем как продолжить.
После получения сообщения об ошибке ваше приложение может
вывести сообщение пользователю. Ваше сообщение может включать строку,
возвращённую majorErrorMessage
или
minorErrorMessage
, или число, возвращённое majorErrorCode
или
minorErrorCode
. Дополнительно можно обработать число или строку,
перед тем как вывести их.
При обсчёте строки, возвращённой majorErrorMessage
и
minorErrorMessage
, LiveWire возвращает строку поставщика БД с
присоединённым дополнительным текстом.
Детальную информацию о возвращаемых значениях см. в описаниях этих методов в
книге
Серверный JavaScript. Справочник
.
В следующей таблице дан список статус-кодов,
возвращаемых различными методами. Netscape рекомендует не использовать эти
значения напрямую. Вместо этого, если метод возвращает ненулевое значение,
используйте ассоциированные методы majorErrorCode
и majorErrorMessage
для определения конкретной ошибки.
Статус-Код | Пояснение | Статус-Код |
Пояснение
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
---|
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе дан обзор типичного приложения на языке "Серверный JavaScript" и показано, как настроить систему для разработки серверных приложений.
В главе имеются следующие разделы:
Как уже говорилось, приложения JavaScript имею части, работающие на стороне клиента и на сервере. Кроме того, многие приложения JavaScript используют LiveWire Database Service для соединения приложения с реляционной базой данных. Исходя из этого, Вы можете представить приложения JavaScript как имеющие трёхслойную клиент-серверную архитектуру, как показано на Рисунке 2.1.
Клиентская среда JavaScript работает как часть WWW -клиентов, а серверная среда JavaScript работает как часть Netscape web-сервера с доступом к одному или более серверов БД. Рисунок 2.2 показывает более детально, как серверная среда JavaScript и приложения, созданные для неё, встраиваются в Netscape web-сервер.
Верхняя часть Рисунка 2.2
демонстрирует то, как серверный JavaScript
встраивается в Netscape web-сервер. Внутри web-сервера среда работы серверного JavaScript
состоит из трёх основных компонентов, перечисленных ниже. JavaScript Application Manager
работает поверх серверного JavaScript, как это делается в образце приложения,
предоставленном Netscape
(таком как приложение videoapp
), и в любом приложении, созданном
Вами.
Имеются три главных компонента среды работы JavaScript:
В целом приложение на JavaScript может содержать операторы, интерпретируемые клиентом (интерпретатором JavaScript, предоставляемым Netscape Navigator'ом или некоторыми другими web-браузерами) и сервером (машиной выполнения JavaScript, уже рассмотренной).
Когда вы запускаете приложение JavaScript, происходят различные действия: некоторые на сервере, и некоторые - на клиенте. Хотя конечному пользователю не обязательно знать все детали, Вам как разработчику приложения необходимо понимать, что происходит "под капотом."
При создании приложения Вы пишете HTML-страницы, которые могут содержать
операторы серверного и клиентского
JavaScript. В исходном коде HTML клиентский JavaScript находится в тэгах
SCRIPT
, а серверный JavaScript - в тэгах SERVER
.
Вы можете также писать файлы, содержащие только операторы JavaScript без тэгов HTML. Такой JavaScript-файл может содержать либо клиентский, либо серверный JavaScript; один файл не может содержать и клиентские, и серверные объекты или функции.
JavaScript VM (Виртуальная Машина), используемая в Netscape Enterprise Server 4.0,
реализует значительные улучшения для обработки локальных переменных (то есть
переменных, которые объявлены внутри функции), по сравнению с NES 3.6.
Соответственно, рекомендуется минимизировать использование глобальных переменных (переменных
между тэгами <server>
и </server>
) и
переписать приложения для использования функций там, где это возможно. Это
значительно повысит производительность приложения.
Если файлы HTML и JavaScript содержат серверный JavaScript, Вы затем
компилируете их в единый исполняемый файл приложения
JavaScript. Этот файл называется web-файлом и имеет расширение
.web
. Компилятор приложений JavaScript развёртывает HTML-код в
платформонезависимые байт-коды, разбирая и компилируя операторы серверного JavaScript.
Наконец, Вы публикуете Ваше приложение на Вашем web-сервере и используете JavaScript Application Manager для инсталяции и запуска приложения, чтобы пользователи могли получить к нему доступ.
На этапе прогона, когда клиент запрашивает страницу приложения серверного JavaScript, машина выполнения локализует представление этого файла в web-файле приложения. Она запускает весь найденный серверный код и создаёт HTML-страницу для отправки клиенту. Эта страница может содержать как обычные тэги HTML, так и операторы клиентского JavaScript. Весь серверный код запускается на сервере до отправки страницы клиенту и до выполнения любого HTML или клиентского JavaScript. Соответственно, Ваш серверный код не может использовать клиентские объекты, так же как и клиентский код не может использовать серверные объекты.
Дополнительно см. Главу 5, "Основы Серверного JavaScript."
Чтобы разрабатывать приложения JavaScript, использующие преимущества и клиентского, и серверного JavaScript, Вам нужна подходящая среда для разработки и публикации. В целом рекомендуется разрабатывать приложения на системе, отделённой от сервера публикации, поскольку разработка потребляет много ресурсов (порты соединений, пропускная способность, время процессора и память). При разработке также может быть нарушена работа уже опубликованных приложений конечного пользователя.
Среда разработки JavaScript состоит из:
Публикация и машины публикации требуют наличия следующего программного обеспечения:
Кроме того, если ваше приложение использует JavaScript-службу LiveWire Database Service, Вам понадобится:
В этом разделе рассматривается информация конфигурации для использования серверного JavaScript. Дополнительно о настройке БД для работы с сервисом LiveWire Database Service см. Главу 10, "Конфигурирование Базы Данных."
Чтобы запускать приложения JavaScript на Вашем сервере, Вы обязаны подключить машину выполнения JavaScript в вашем Server Manager, щёлкнув Programs, а затем выбрав серверный JavaScript. После появления промпта "Activate the JavaScript application environment/Активизировать среду приложений JavaScript ?" выберите Yes и щёлкните OK. У Вас спросят также об ограничении доступа к Application Manager. Дополнительно см. "Защита Application Manager'а."
ПРИМЕЧАНИЕ: Если Вы не подключите машину выполнения JavaScript, приложения JavaScript не смогут запускаться на этом сервере.
Чтобы использовать и сервлеты, и LiveWire, Вам необходимо
подключить серверный JavaScript
до подключения Java. Оба могут быть подключены через использование меню программ Administration Server'а.
Если Вы модифицируете путь к классам/classpath в obj.conf
, Ваши
изменения будут утеряны, если Вы подключите/отключите серверный JavaScript или Java
из программного меню Administration Server'а.
Альтернативой редактирования директивы classpath в obj.conf
является установка переменной окружения
CLASSPATH
в Unix или установка переменной CLASSPATH
в
установках System в Windows NT. Если Вам нужно редактировать obj.conf
непосредственно, сохраните первоначальный файл на всякий случай. В Enterprise Server
4.0 Вы должны добавить CLASSPATH
info в файлы конфигурации JVM (jvm12.conf
для Solaris и NT) через интерфейс Enterprise
Administration Server.
Как только Вы активируете среду приложений JavaScript, Вы обязаны остановить и рестартовать Ваш web-сервер, чтобы ассоциированные переменные окружения начали действовать. Если этого не сделать, приложения JavaScript, использующие службу LiveWire Database Service, работать не будут.
Application Manager предоставляет контроль над приложениями JavaScript. В связи с его особыми возможностями Вы должны защитить его от неавторизованного доступа. Если Вы не ограничиваете доступ к Application Manager'у, любой может добавлять, удалять, изменять, стартовать и останавливать приложения на Вашем сервере. Это, естественно, может привести к нежелательным последствиям.
Вы (разработчик приложений на JavaScript) должны иметь права доступа для использования Application Manager'а на сервере разработчика, так как Вы используете его для работы с приложением при разработке. Администратор Вашего web-сервера, однако, может не предоставить Вам таких прав на сервере разработчика.
Если Вы подключите машину выполнения JavaScript в Server Manager'е,
промпт спросит Вас, ограничивать ли доступ к Application Manager'у.
Выберите Yes и щёлкните OK.
(Yes - по умолчанию.) После этого любой, кто попытается получить доступ к Application Manager'у,
обязан будет ввести имя пользователя и пароль
Server Manager'а, чтобы получить возможность
использовать Application Manager и приложение-образец dbadmin
.
Дополнительно см. руководство администратора для Вашего web-сервера.
Если Ваш сервер не использует Secure Sockets Layer (SSL), имя пользователя и пароль для Application Manager'а передаются по сети в некодированном виде. Перехватив эти данные, можно получить доступ к Application Manager'у. Если Вы используете тот же самый пароль для Вашего сервера администратора, хакер получит также контроль и над этим сервером. Следовательно, можно рекомендовать не использовать Application Manager вне прокси-сервера, если Вы не используете SSL. О том, как подключить SSL к серверу, см. справочник администратора Вашего web-сервера.
Чтобы использовать Java-классы в LiveConnect, Вам нужно установить CLASSPATH сервера. Полное описание этой процедуры дано в Programmer's Guide to Servlets и в Netscape Enterprise Server Administrator's Guide.
Инсталяция Netscape-сервера не изменяет Вашу переменную окружения PATH
для включения директории, в которой установлен компилятор приложений JavaScript.
Если Вам необходим быстрый доступ к компилятору, Вы обязаны модифицировать эту
переменную.
В системах Unix у Вас есть выбор способов изменения переменной окружения PATH
.
Можно добавить $NSHOME/bin/https/lib
, где $NSHOME
это
директория, в которой установлен Ваш сервер. Поговорите с Вашим системным
администратором о том, как это сделать.
Чтобы изменить системный путь NT, запустите Control Panel,
найдите диалоговое окно System и установите переменную PATH
в
разделе Environment settings, включив в неё
%NSHOME%\bin\https\bin
, где NSHOME
это директория, в
которой установлен Ваш сервер.
Если вы переместили компилятор JavaScript-приложений в
другое место, Вы обязаны добавить эту новую директорию в переменную PATH
.
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
Здесь определены термины, используемые в приложениях JavaScript.
client
-объекта.
Этот метод хранит пары имя/значение (куки) на клиентской машине.client
-объекта. Этот метод присоединяет пары имя/значение к строке URL.Array
, Date
и
Math
, и основной набор элементов языка, таких как операции,
структуры управления и операторы. См. также client-side JavaScript, server-side JavaScript.Cursor
в JavaScript имеет
соответствующие свойства и методы.client
,
когда сервер использует клиентский IP-адрес для обращения к структуре данных,
содержащей значения свойств объекта
client
.undefined
, null
, Boolean
,
number
или string
.a=true // Boolean/Булево
b=42
// number/число
c="Hello world" // string/строка
if (x==undefined) {} // undefined
("не определено")
if (x==null) {} // null
client
, когда сервер генерирует уникальное имя для клиента,
сохраняемое в
cookie-файле на стороне клиента и позднее использует сохранённое имя для
обращения к структуре данных, содержащей значения свойств объекта
client
.client
,
когда сервер генерирует для клиента уникальное имя, присоединяет его к URL и
позднее использует сохранённое имя для обращения к структуре данных, содержащей
значения свойств объекта client
.request
,
client
, project
и server
- и один класс, Lock
,
предоставляющие основу для совместного использования данных запросами,
клиентами и приложениями.Date
. Некоторые методы
объекта Date
, такие как getHours
и setDate
,
являются также методами экземпляров объекта Date
. Другие методы
объекта Date
, такие как parse
и UTC
,
являются static/статическими, и экземпляры объекта Date
не имеют
этих методов.Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
Это введение в JavaScript и обсуждение некоторых фундаментальных понятий.
В главе имеются следующие разделы:
JavaScript это созданный фирмой Netscape
межплатформенный, объектно-ориентированный язык скриптинга (сценариев).
Ядро JavaScript содержит набор основных объектов, таких как Array
, Date
и
Math
, и основной набор элементов языка, таких как операции,
структуры управления и операторы. Ядро JavaScript может быть расширено для
различных целей путём дополнения новыми объектами; например:
JavaScript позволяет создавать приложения, работающие по всей сети Internet. Клиентские приложения работают в браузере, таком как Netscape Navigator, а серверные приложения - на сервере, таком как Netscape Enterprise Server. Используя JavaScript, Вы можете создавать динамические HTML-страницы, обрабатывающие пользовательский ввод и имеющиеся данные, используя специальные объекты, файлы и реляционные БД.
С помощью функциональности JavaScript LiveConnect Вы можете организовать взаимодействие кодов Java и JavaScript. Из JavaScript Вы можете инстанциировать объекты Java и получать доступ к их public-методам и полям. Из Java Вы можете получать доступ к объектам, свойствам и методам JavaScript.
Корпорация Netscape изобрела JavaScript, и JavaScript был впервые использован в браузерах Netscape.
Компоненты JavaScript показаны на рисунке:
В следующем разделе разбирается работа JavaScript на стороне клиента и на сервере.
Клиентский и серверный JavaScript имеют следующие общие элементы:
Array
, Date
и Math
Web-браузеры, такие как Navigator (2.0 и более поздние версии) могут интерпретировать операторы клиентского JavaScript, внедрённые в HTML-страницу. Когда браузер (или клиент) запрашивает такую страницу, сервер высылает клиенту по сети полное содержимое документа, включая HTML и операторы JavaScript. Браузер читает страницу сверху вниз, отображая результат работы HTML и выполняя операторы JavaScript по мере их обнаружения. Этот процесс, проиллюстрированный на рисунке, производит результат, который видит пользователь.
Операторы клиентского JavaScript, встроенного в HTML-страницу, могут реагировать на пользовательские события, такие как щелчок мыши, ввод данных в форму и навигация по страницам. Например, Вы можете написать функцию JavaScript для проверки ввода пользователем правильной информации в форму, запрашивающую телефонный номер или zip-код. Без передачи по сети внедрённый JavaScript на HTML-странице может проверить введённые данные и вывести диалоговое окно, если пользователь ввёл неверные данные.
Разные версии JavaScript работают со специфическими версиями Navigator'а. Например, JavaScript 1.2 работает с Navigator 4.0. Некоторые возможности JavaScript 1.2 недоступны в JavaScript 1.1 и поэтому недоступны в Navigator 3.0. Информацию о версиях JavaScript и Navigator см. в разделе "Версии JavaScript".
На сервере Вы также можете внедрять JavaScript в HTML-страницы. Серверные операторы могут соединяться с реляционными БД разных производителей, разделять информацию между пользователями приложения, получать доступ к файловой системе сервера или взаимодействовать с другими приложениями через LiveConnect и Java. HTML-страницы с серверным JavaScript могут содержать также клиентский JavaScript.
В отличие от страниц с чисто клиентским JavaScript, HTML-страницы, использующие серверный JavaScript, компилируются в байт-кодовые исполняемые файлы. Эти исполняемые приложения запускаются на выполнение web-сервером, имеющим машину времени выполнения JavaScript. Исходя из этого, создание приложений JavaScript это процесс из двух этапов.
На первом этапе, показанном на Рисунке 1.3, Вы создаёте HTML-страницы (которые могут содержать операторы как клиентского, так и серверного JavaScript) и файлы JavaScript. Затем Вы компилируете все эти файлы в единый исполняемый блок.
На втором этапе, показанном на Рисунке 1.4, страница приложения запрашивается клиентским браузером. Машина выполнения использует исполняемый блок для просмотра исходной страницы и динамической генерации HTML-страницы, возвращаемой клиенту. Она выполняет все найденные на странице операторы серверного JavaScript. Выполнение этих операторов может добавить новые операторы HTML или операторы клиентского JavaScript в HTML-страницу. Машина выполнения отсылает затем окончательный вариант страницы по сети Navigator-клиенту, который выполняет клиентский JavaScript и отображает результат.
В отличие от стандартных программ Common Gateway Interface (CGI), все исходники JavaScript интегрированы непосредственно в HTML-страницы, ускоряя разработку и облегчая обслуживание. Служба Session Management Service серверного JavaScript содержит объекты, которые Вы можете использовать для работы с данными, существующими между клиентскими запросами, у нескольких клиентов или нескольких приложений. Служба LiveWire Database Service серверного JavaScript предоставляет объекты для доступа к БД, служащие интерфейсом для серверов Structured Query Language (SQL).
JavaScript и Java напоминают друг друга, но имеют и фундаментальные отличия. JavaScript не имеет статической типизации и строгой проверки типов Java. JavaScript поддерживает большую часть синтаксиса выражений Java и базовые конструкции управления потоком.
В отличие от системы времени компиляции Java, построенной на объявлениях, JavaScript поддерживает систему времени выполнения, основанную на небольшом количестве типов данных: числовых, Булевых и строковых. JavaScript имеет объектную модель на базе прототипов вместо более общей объектной модели на базе классов. Модель на базе прототипов предоставляет возможность динамического наследования; то есть, то, что наследуется, может отличаться для разных объектов. JavaScript также поддерживает функции без специальных требований объявления. Функции могут быть свойствами объектов, исполняемыми как нетипизированные методы.
JavaScript это язык, свободный по форме, по сравнению с Java. Вы не должны объявлять все переменные, классы и методы. Вы не должны учитывать, являются ли методы public, private или protected, и не обязаны реализовывать интерфейсы. Return-типы переменных, параметров и функций не типизированы явно.
Java это язык на базе классов, разработанный для быстрого выполнения и строгой типизации. Строгая типизация означает, к примеру, что Вы не можете привести/cast целое число Java (integer) к ссылке на объект или получить доступ к private-памяти, нарушая байт-коды Java. Модель Java на базе классов означает, что программы состоят исключительно из классов и их методов. Наследование классов в Java и строгая типизация обычно требуют тесно выстроенной иерархий объектов. Эти требования делают программирование на Java более сложным, чем авторизация на JavaScript.
В противоположность этому, JavaScript ведёт своё начало от небольших динамически типизированных языков, таких как HyperTalk и dBASE. Эти языки сценариев предоставляют утилиты программирования для более широкой аудитории, поскольку имеют облегчённый синтаксис, специализированную встроенную функциональность и минимальные требования при создании объектов.
JavaScript позволяет создавать сложные компьютерные программы. Как и во всех других языках, Вы можете ошибаться при написании скриптов. Отладчик Netscape JavaScript Debugger даёт возможность отлаживать Ваши скрипты.
об использовании Отладчика см. Getting Started with Netscape JavaScript Debugger .
Netscape Visual JavaScript это утилита визуальной разработки на базе компонентов для платформы Netscape Open Network Environment (ONE). Он первоначально предназначался для использования разработчиками межплатформенных стандартизованных web-приложений из готовых компонентов с минимальными затратами на программирование. Эти приложения базируются на HTML, JavaScript и Java.
О Visual JavaScript см. Visual JavaScript Developer's Guide .
Netscape изобрела JavaScript, и JavaScript был впервые использован в браузерах Netscape. Одновременно Netscape работает с ECMA (European Computer Manufacturers Association) для создания стандартизованного международного языка программирования на базе ядра JavaScript. ECMA это международная ассоциация стандартов в области систем информации и коммуникаций. Эта стандартизованная версия JavaScript, называемая ECMAScript, ведёт себя совершенно одинаково во всех приложениях, поддерживающих этот стандарт. Компании могут использовать этот открытый стандартный язык для создания своих реализаций JavaScript. Первая версия стандарта ECMA документирована в спецификации ECMA-262.
Стандарт ECMA-262 одобрен также ISO (International Organization for Standards) как ISO-16262. Вы можете найти PDF-версию ECMA-262 на Netscape DevEdge Online. Вы также можете найти эту спецификацию на сайте ECMA. Спецификация ECMA не описывает Document Object Model (DOM), которая стандартизуется консорциумом World Wide Web Consortium (W3C). DOM определяет способ, которым объекты HTML-документа экспонируются в скрипте.
Netscape тесно сотрудничает с ECMA для создания спецификации ECMA.
Детальную информацию о соотношении версий спецификаций JavaScript и ECMA см. на сайте mozilla.org.
JavaScript всегда будет содержать возможности, не включённые в спецификацию ECMA; JavaScript совместим с ECMA, предоставляя дополнительные возможности.
Спецификация ECMA это набор требований по реализации ECMAScript; она применима, если Вам необходимо определить, поддерживается ли возможность из JavaScript в ECMA. Если Вы планируете написать код JavaScript, использующий только возможности, поддерживаемые ECMA, Вам может понадобиться просмотреть спецификацию ECMA.
Документ ECMA не предназначен для помощи программистам скриптов; для этого используйте документацию JavaScript.
Спецификация ECMA использует терминологию и синтаксис, которые могут быть незнакомы программистам JavaScript. Хотя описание языка может отличаться в ECMA, сам язык остаётся тем же самым. JavaScript поддерживает всю функциональность, данную в спецификации ECMA.
Документация по JavaScript описывает аспекты языка, необходимые программисту на JavaScript. Например:
Number
и String
не обсуждается в документации JavaScript, поскольку то, что генерируется, используется мало.
Number
-конструктор без аргументов возвращает +0, а String
-конструктор без аргументов возвращает "" (пустую строку).Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе даются основы серверной функциональности и различия между клиентским и серверным JavaScript. Здесь показано, как внедрять серверный JavaScript в HTML-файлы. Обсуждается, что происходит во время прогона программы на клиенте и на сервере, чтобы Вы могли разобраться во всём этом. В главе описано, как использовать JavaScript для изменения HTML-страницы, отправляемой клиенту, и, наконец, как распределить информацию между серверными и клиентскими процессами.
В главе имеются следующие разделы:
Серверный JavaScript имеет то же ядро языка, что и клиентский JavaScript, с которым Вы, возможно, уже знакомы. Задачи, выполняемые Вами при запуске JavaScript на сервере, несколько отличаются от задач, выполняемых при работе JavaScript на клиенте. Разные окружения и задачи обращаются к различным объектам.
Клиентская среда (браузер) является передним краем работы приложения. В этой среде, к примеру, Вы отображаете HTML-страницы в окне и обслуживаете истории сессий HTML-страниц, отображаемых в браузере в течение сессии. Объекты этой среды, следовательно, обязаны иметь возможность манипулировать страницами, окнами и историей.
По контрасту, в серверной среде Вы работаете с ресурсами сервера. Например, Вы можете установить соединение с реляционной базой данных, распределить информацию между пользователями приложения или манипулировать файловой системой сервера. Объекты этой среды обязаны иметь возможность манипулировать реляционной БД и файловой системой сервера.
Кроме того, HTML-страница не отображается на сервере. Она запрашивается на сервере для отображения на клиенте. Запрошенная страница может содержать клиентский JavaScript. Если запрошенная страница является частью приложения JavaScript, сервер может генерировать эту страницу "на лету".
При разработке приложения JavaScript помните о разнице между клиентской и серверной платформами. Различия показаны в следующей таблице.
Обычно имеются разные пути распределения приложения между сервером и клиентом. Некоторые задачи могут выполняться только на клиенте или только на сервере; другие могут выполняться на любом из них. Хотя нет какого-то определённого способа определить, что и где делать, Вы может следовать следующим общим правилам:
Основное правило - использовать клиентский процессинг (тэг SCRIPT
)
для следующих задач:
Использовать серверный процессинг (тэг SERVER
)
для следующих задач:
Служба JavaScript Session Management Service предоставляет объекты для сохранения информации, а клиентский JavaScript преходящ. Клиентские объекты существуют, пока пользователь имеет доступ к странице. Серверы могут объединять информацию от многих клиентов и многих приложений и могут сохранять большие объёмы данных в базе данных. Важно помнить обо всех этих характеристиках при распределении функциональности между клиентом и сервером.
После того как Вы установили и стартовали приложение JavaScript, пользователь
может получить к нему доступ.
Базовые процедуры таковы:
SERVER
, из
исходного кода HTML,
создавая HTML-страницу на основе этих байт-кодов и иного HTML, имеющегося в
оригинале. О том, как влиять на процесс конструирования страницы,
см. в разделе "Конструирование HTML-Страницы".Рисунок 5.1 иллюстрирует это процесс.
Конечно, пользователь обязан иметь Netscape Navigator (или иной браузер с возможностью выполнения JavaScript), чтобы клиент мог интерпретировать операторы клиентского JavaScript. Аналогично, если Вы создаёте страницу, содержащую серверный JavaScript, он должен быть установлен на Netscape-сервере, чтобы нормально функционировать.
Например, предположим, клиент запрашивает страницу с таким исходным кодом:
<html>
<head> <title> Add New Customer </title> </head>
<body text="#FFFF00" bgcolor="#C0C0C0" background="blue_marble.gif">
<img src="billlog2.gif">
<br>
<server>
if ( project.lock() ) {
project.lastID = 1 + project.lastID;
client.customerID = project.lastID;
project.unlock();
}
</server>
<h1>Add a New Customer </h1>
<p>Note: <b>All</b> fields are required for the new customer
<form method="post" action="add.html"></p>
<p>ID:
<br> <server>write("<STRONG><FONT COLOR=\"#00FF00\">" +
project.lastID + "</FONT></STRONG>");</server>
<!-- другие операторы html -->
</body>
</html>
При доступе к этой странице машина выполнения выполняет
на сервере код, ассоциированный с тэгами SERVER
. (Этот код выделен
жирным шрифтом.) Если ID нового потребителя 42, сервер высылает клиенту для
отображения такую HTML-страницу:
<html>
<head> <title> Add New Customer </title> </head>
<body text="#FFFF00" bgcolor="#C0C0C0" background="blue_marble.gif">
<img src="billlog2.gif">
<br>
<h1>Add a New Customer </h1>
<p>Note: <b>All</b> fields are required for the new customer
<form method="post" action="add.html"></p>
<p>ID:
<br><STRONG><FONT COLOR="#00FF00">42</FONT></STRONG>
<!-- другие операторы html -->
</body>
</html>
И клиентский, и серверный JavaScript реализуют язык JavaScript. Но каждый при
этом добавляет специфические объекты и функции для работы в клиентской или
серверной среде. Например, клиентский
JavaScript включает объект form
для представления формы на HTML-странице,
а серверный
JavaScript включает объект database
для соединения с внешней
реляционной БД.
В книге Клиентский JavaScript. Руководство. детально рассматривается ядро языка JavaScript и дополнительная специфика клиентского JavaScript.
ECMA, Европейская организация стандартизации систем информации и коммуникаций,
выпустила стандарт ECMA-262 языка JavaScript. Вы можете загрузить эту
спецификацию с сайта ECMA по адресу
http://www.ecma.ch
.
Как указано в книге
Серверный JavaScript. Справочник.
, Вы можете
использовать свойство prototype
многих классов, чтобы добавлять
новые свойства в класс и во все его экземпляры. Как описано в разделе "Классы
и Объекты", серверный JavaScript добавляет несколько классов и
предопределённых объектов. В новых классах, имеющих свойство prototype
,
оно работает в серверном JavaScript точно так же, как и в клиентском JavaScript.
Вы можете использовать свойство prototype
для добавления новых
свойств в классы Blob
,
Connection
, Cursor
, DbPool
, File
, Lock
,
Resultset
, SendMail
и Stproc
. Кроме того,
Вы можете использовать свойство prototype
класса DbBuiltin
для добавления свойств в предопределённый объект database
. Обратите
внимание, что Вы не можете создать экземпляр класса
DbBuiltin
; вместо этого Вы используете объект database
,
предоставляемый машиной выполнения JavaScript.
Вы не можете использовать prototype
с
объектами client
,
project
, request
и server
.
Так же, как и в клиентском JavaScript, Вы можете использовать свойство prototype
для любого класса, который Вы определяете в своём приложении.
Помните, что все приложения JavaScript на сервере
работают в одной среде. Вот почему Вы можете распределять информацию между
клиентами и приложениями.
Следствием этого, однако, является то, что при использовании свойства prototype
для добавления нового свойства в любой серверный класс, добавленный языком JavaScript,
новое свойство доступно всем приложениям, работающим на сервере, а не только
приложению, в котором это свойство было добавлено. Это даёт возможность легко
добавлять функциональность всем приложениям JavaScript
на Вашем сервере.
В противоположность этому, если Вы добавляете свойство в класс, который Вы определили в своём приложении, это свойство доступно только в том приложении, где оно было создано.
Вам необходимо знать, как компилятор приложений JavaScript распознаёт клиентский и серверный JavaScript в HTML-файле.
Операторы клиентского JavaScript могут появляться в следующих ситуациях:
SCRIPT
SCRIPT
О деталях см. книгу Клиентский JavaScript. Руководство .
Операторы серверного JavaScript могут появляться в следующих ситуациях:
SERVER
Заметьте, что Вы не можете специфицировать оператор серверного JavaScript как обработчик события. Дополнительно см. "Внедрение JavaScript в HTML".
Возможности LiveConnect ядра языка JavaScript работают на сервере иначе, чем на клиенте. См. дополнительно Главу 14, "LiveConnect. Обзор.".
JavaScript предоставляет дополнительную функциональность без использования объектов. Вы получаете доступ к этой функциональности через функции, не ассоциированные с каким-либо объектом (глобальные функции). Ядро языка JavaScript имеет глобальные функции, описанные в следующей таблице (также как и другие функции, описанные в документации Ядра JavaScript).
Серверный JavaScript добавляет глобальные функции, описанные в следующей таблице.
Функция | Описание |
---|---|
write |
Добавляет операторы к генерируемой клиентской HTML-странице. (См. "Генерация HTML".) |
flush |
Очищает буфер вывода. (См. "Очистка Буфера Вывода".) |
redirect |
Перенаправляет клиента по специфицированному URL. (См. "Переход к Новому Клиентскому Запросу".) |
getOptionValue |
Получает значения отдельных опций в элементе HTML-формы |
getOptionValueCount |
Получает количество опций в элементе HTML-формы |
debug |
Выводит значения выражений в окне (фрэйме) трассировки. (См. "Использование Функции debug".) |
addClient |
Присоединяет клиентскую информацию к URL. (См. "Присоединение client-Свойств к URL Вручную".) |
registerCFunction |
Регистрирует внешнюю функцию для использования в серверном JavaScript. (См. "Регистрация Внешних Функций/Native Functions".) |
callC |
Вызывает внешнюю функцию. (См. "Использование Внешних Функций в JavaScript".) |
deleteResponseHeader |
Удаляет информацию из "шапки" ответа, высылаемого клиенту. (См. "Манипуляции с Запросом и Ответом".) |
addResponseHeader |
Добавляет новую информацию в "шапку" ответа, высылаемого клиенту. (См. "Манипуляции с Запросом и Ответом".) |
ssjs_getClientID |
Возвращает идентификатор для |
ssjs_generateClientID |
Возвращает идентификатор, который Вы можете использовать для уникального
специфицирования объекта |
ssjs_getCGIVariable |
Возвращает значение специфицированной переменной окружения CGI. (См. "Доступ к Переменным CGI".) |
Для поддержания различных задач, выполняемых на каждой стороне, JavaScript имеет классы и предопределённые объекты, которые работают на клиенте, но не на сервере, и другие классы и предопределённые объекты, которые работают на сервере, но не на клиенте.
Важно!Названия этих объектов зарезервированы для JavaScript. Не создавайте Ваши собственные объекты, используя эти имена.
Ядро языка JavaScript предоставляет классы, описанные в следующей таблице. Детальную информацию обо всех этих объектах см. в книге Серверный JavaScript. Справочник .
Серверный JavaScript включает классы ядра, но не классы клиентского JavaScript. Серверный JavaScript имеет свой набор дополнительных классов для поддержки необходимой функциональности, как показано в следующей таблице.
Класс |
Описание
| Connection
| Одиночное соединение с БД из пула соединений. (См. "Отдельные Соединения с БД").
| Cursor
| Курсор БД. (См. "Манипуляции с Результатами Выполнения Запроса с Помощью Курсоров").
| DbPool
| Пул соединений с базами данных. (См. "Пулы Соединений с Базами Данных").
| Stproc
| Хранимая процедура БД. (См. "Вызов Хранимых Процедур").
| Resultset
| Представляет информацию, возвращаемую хранимой процедурой БД. (См. "Вызов Хранимых Процедур".)
| File
| Предоставляет доступ к файловой системе сервера. (См. "Служба Файловой Системы".)
| Lock
| Предоставляет функциональность для безопасного совместного использования/sharing данных запросами, клиентами и приложениями. (См. "Безопасное Совместное Использование Объектов с Блокировкой".)
| SendMail
| Предоставляет функциональность для отправки электронной почты из вашего приложения JavaScript. (См. "Почтовая Служба".) |
---|
Кроме того, серверный JavaScript имеет предопределённые объекты, описанные в следующей таблице. Эти объекты доступны для каждого HTTP-запроса. Вы не можете создать дополнительные экземпляры какого-либо из этих объектов.
Объект |
Описание
| database
| Соединение с базой данных. (См. "Соединение. Подходы.")
| client
| Инкапсулирует информацию о паре клиент/приложение, позволяя хранить эту информацию дольше, чем в течение одного HTTP-запроса. (См. "Объект client.")
| project
| Инкапсулирует информацию о приложении, которая хранится, пока приложение не прекратит работу на сервере. (См. "Объект project".)
| request
| Инкапсулирует информацию об отдельном HTTP-запросе. (См. "Объект request".)
| server
| Инкапсулирует глобальную информацию о сервере, которая хранится, пока сервер не остановится. (См. "Объект server".) |
---|
Есть два способа встроить операторы серверного JavaScript в HTML-страницу:
SERVER
.
Используйте этот тэг для размещения в нём одиночного оператора JavaScript
или нескольких операторов. Оператор расположен между тэгами <SERVER>
и </SERVER>
.
Вы можете перемежать
тэги SERVER
полными операторами HTML.
Никогда не помещайте тэг SERVER
между открывающей (<
)
и закрывающей
(>
) угловыми скобками тэга HTML. (См. "Тэг SERVER").
Также не используйте тэг <SCRIPT>
между тэгами <SERVER>
и </SERVER>
.
Используйте этот символ для включения выражений JavaScript внутрь тэгов HTML,
обычно для генерирования атрибута или значения атрибута
HTML на базе значений JavaScript. Эта техника используется внутри таких тэгов,
как якоря, изображения или тэги элементов формы, например, для предоставления
значения атрибута HREF
якоря.
Если Вы внедряете серверный JavaScript в HTML-страницу, машина выполнения JavaScript
на сервере выполняет обнаруженные ею операторы при процессинге (обработке)
страницы. Большинство операторов выполняют какие-либо операции на сервере, такие
как открытие соединения с БД или блокировка совместно используемого объекта.
Однако,
если Вы используете функцию write
в тэге SERVER
или
заключаете операторы в обратные кавычки, машина выполнения динамически
генерирует новый HTML для модифицирования страницы, высылаемой клиенту.
Тэг SERVER
это наиболее распространённый способ внедрения
серверного JavaScript в HTML-страницу.
Вы можете использовать тэг SERVER
в любой ситуации; обычно, однако,
вместо него используются обратные кавычки, если Вы генерируете имена или
значения атрибутов для HTML-страницы.
Большая часть операторов между тэгами <SERVER>
и </SERVER>
не появляется на HTML-странице, высылаемой клиенту. Эти операторы выполняются на
сервере. Однако вывод вызовов функции write
появляется в
результирующем HTML.
Следующая выдержка из приложения Hello World иллюстрирует эти варианты:
<P>This time you are
<SERVER>
write(request.newname);
client.oldname = request.newname;
</SERVER>
<h3>Enter your name</h3>
Получив данный участок кода, машина выполнения генерирует HTML
на базе значения request.newname
в операторе write
. Во
втором операторе она просто выполняет операцию JavaScript, присваивая значение request.newname
свойству client.oldname
. Она не генерирует никакого HTML. Итак,
если request.newname
будет "Mr. Ed," машина выполнения генерирует
из предыдущего отрывка следующий HTML:
<P>This time you are
Mr. Ed
<h3>Enter your name</h3>
Используйте обратные кавычки (`
) для выделения выражений серверного JavaScript
как заместителей для имён атрибутов или значений атрибутов HTML. JavaScript,
внедрённый в HTML с помощью обратных кавычек, автоматически генерирует
HTML; Вам не нужно использовать write
.
В целом тэги HTML имеют форму:
<TAG ATTRIB="value" [...ATTRIB="value"]>
где
ATTRIB
это атрибут, а "
value
"
это его значение. Значение в угловых скобках означает, что
допускается любое количество пар атрибут/значение.
Если в обратные кавычки заключено выражение JavaScript, используемое как значение атрибута, машина выполнения JavaScript автоматически добавляет знак кавычки вокруг всего значения. Вы сами не вводите знаки кавычек, хотя Вам это может понадобиться для разграничения строковых литералов выражения, как в следующем примере. Машина выполнения не делает это для имён атрибутов, поскольку не предполагается заключение имён атрибутов в кавычки.
Например, рассмотри следующую строку из образца-приложения Hangman:
<IMG SRC=`"images\hang" + client.num_misses + ".gif"`>
Эта строка динамически генерирует имя изображения на
основе значения client.num_misses
. Обратные кавычки обрамляют
выражение JavaScript, конкатенирующее строку "images\hang"
и
целочисленное значение client.num_misses
и строку ".gif"
,
давая строку типа "images\hang0.gif"
. Результатом будет HTML, такой
как
<IMG SRC="images\hang0.gif">
Порядок ввода кавычек является критичным. Сначала идёт обратная кавычка,
указывая, что следующее значение является выражением JavaScript, состоящим из
строки ("images\hang"
), соединяемой с целым числом (client.num_misses
)
и с другой строкой (".gif"
).
JavaScript конвертирует всё выражение до строки и добавляет необходимые знаки
кавычек вокруг значения атрибута.
Вы должны внимательно использовать знаки двойных
кавычек внутри обратных кавычек, поскольку содержащееся внутри них значение
интерпретируется как литерал. Поэтому не окружайте выражения JavaScript, которые
необходимо вычислить, знаками кавычек. Например, если значение свойства client.val
будет NetHead, то данный оператор:
<A NAME=`client.val`>
<A NAME="NetHead">
<A NAME=`"client.val"`>
<A NAME="client.val">
В качестве другого примера приведём два атрибута тэга ANCHOR
- HREF
и
NAME
. HREF
делает тэг гиперссылкой, а NAME
делает его именованным якорем. Следующие операторы используют переменную choice
для установки свойств
attrib
и val
объекта client
и создают
затем гиперссылку/hyperlink или цель/target, в зависимости от значений:
<SERVER>
if (choice == "link") {
client.attrib = "HREF";
client.val = "http://www.netscape.com";
}
if (choice == "target") {
client.attrib = "NAME";
client.val = "NetHead";
}
</SERVER>
<A `client.attrib`=`client.val`>Netscape Communications</A>
Если значением choice
будет "link"
, результат будет:
<A HREF="http://home.netscape.com">Netscape Communications</A>
Если значением choice
будет "target"
, результат будет:
<A NAME="NetHead">Netscape Communications</A>
В большинстве случаев ясно, когда использовать тэг SERVER
,
а когда - обратные кавычки. Иногда, однако можно достичь того же результата
другим путём ("Мы пойдём..."). В общем, лучше использовать обратные кавычки для
внедрения значений JavaScript в тэги HTML, а в других случаях - тэг SERVER
.
<IMG SRC=`"images\hang" + client.num_misses + ".gif"`>
<SERVER>
write("<IMG SRC=\"images\hang");
write(client.num_misses);
write(".gif\">");
</SERVER>
Обратите внимание, что обратная наклонная черта (backslash) даёт возможность ввести знак кавычки внутри литеральной строки. Хотя результирующий HTML - тот же самый, в этом случае обратные кавычки предпочтительнее, поскольку исходник легче читать и редактировать.
В разделе "Обзор Процессинга Времени Выполнения" показано, что происходит на этапе прогона, если пользователь выполняет доступ к странице приложения JavaScript. В данном разделе детально рассматриваются этапы этого процесса со 2 по 4, чтобы Вы смогли лучше понять, что происходит на каждом этапе. Это описание предоставляет контекст для понимания того, что Вам нужно делать на клиенте и сервере.
При работе с приложениями JavaScript важно помнить об асинхронной природе процессов Web. Приложения JavaScript разрабатываются для использования многими пользователями одновременно. Машина выполнения JavaScript на сервере обрабатывает запросы нескольких пользователей по мере их поступления и выполняет их в порядке поступления.
В отличие от традиционного приложения, которое запускается отдельным пользователем на отдельной машине, Ваше приложение обязано поддерживать одновременный доступ нескольких пользователей. Фактически, поскольку каждый фрэйм (кадр) HTML-документа из нескольких фрэймов генерирует свой собственный запрос/request, для машины выполнения может оказаться, что запрос одного пользователя является множественным запросом.
HTTP (Hypertext Transfer Protocol) это протокол, по
которому HTML-страница пересылается клиенту. Этот протокол является stateless\бесстатусным,
то есть информация не сохраняется в период между запросами.
В общем, любая информация, необходимая для обработки HTTP-запроса, должна
пересылаться вместе с этим запросом.
Это создаёт проблемы для многих приложений. Как использовать информацию
одновременно различными пользователями приложения или даже различными запросами
одного пользователя? Служба JavaScript Session Management
Service была разработана для того, чтобы помочь разрешить эту проблему. Детально
эта служба рассматривается в Главе 6, "Session Management Service."
В данный момент просто помните, что машина выполнения автоматически обслуживает
объекты client
, server
,
project
и request
.
Если Netscape-сервер получает клиентский запрос на страницу приложения, он сначала выполняет авторизацию (идентификацию). Этот шаг является частью базовых функций администрирования сервера. Если попытка авторизации запроса не удалась, последующие шаги не выполняются. Если запрос получил авторизацию сервера, машина выполнения JavaScript продолжает работу. Она выполняет следующие шаги, описанные в последующих разделах:
Инициализируются встроенные свойства объекта request
,
такие как IP-адрес и элементы формы, ассоциированные с данным request. Если URL
запроса специфицирует другие свойства, они инициализируются для объекта request
,
как описано в разделе "Кодирование Информации в URL".
Если объект client
уже существует, машина
выполнения запрашивает его на основе специфицированной техники обслуживания
клиента. (См. "Техника Обслуживания Объекта client").
Если объект client
не существует, машина выполнения конструирует
новый объект без свойств.
Вы не можете предвидеть, в каком порядке эти объекты конструируются.
Когда Вы компилировали исходный код JavaScript, исходник включал HTML-страницы с операторами серверного JavaScript. Главной задачей машины выполнения является конструирование из одной из этих исходных страниц HTML-страницы, содержащей только HTML и операторы клиентского JavaScript. При создании этой HTML-страницы машина выполнения сохраняет её части в области памяти, называемой буфером, пока не придёт время отправки буферизованного содержимого клиенту.
Этот шаг выполняется для каждого участка кода исходной страницы. Детали процесса выполнения различных серверных операторов рассматриваются далее в этом учебнике. Дополнительно см. "Конструирование HTML-Страницы".
Для данного запроса/request машина выполнения выполняет этот шаг, пока не произойдёт одно из следующих событий:
В этой ситуации машина выполняет шаги 4 и 5 и возвращается к выполнению шага 3 с очищенным буфером, продолжая выполнение того же запроса. (Шаг 4 исполняется только один раз, даже если шаги 3 и 5 повторяются).
flush
.В этой ситуации машина выполняет шаги 4 и 5 и возвращается к выполнению шага 3 с очищенным буфером, продолжая выполнять тот же самый запрос. (Шаг 4 исполняется только один раз, даже если шаги 3 и 5 повторяются).
redirect
.
В этой ситуации машина выполнения завершает данный запрос, выполняя шаги с 4
по 6. Она игнорирует любые вхождения после функции redirect
в
исходном файле и стартует новый запрос для страницы, специфицированной в
вызове redirect
.
В этой ситуации машина завершает данный запрос, выполняя шаги с 4 по 6.
Машина выполнения сохраняет свойства объекта client
непосредственно перед первой отправкой части HTML-страницы клиенту. Она
сохраняет эти свойства только один раз. Машина выполнения может повторять шаги 3
и 5, но не может повторить данный шаг.
Машина выполнения сохраняет свойства в этот момент, чтобы
поддерживать один из видов техники обслуживания объекта client
.
Например, схема кодирования клиентского URL высылает свойства client'
а
в шапке/header HTML-файла. Поскольку шапка высылается как первая часть файла,
свойства client'
а обязаны быть затем
высланы.
Следовательно, Вы должны учитывать, где в Вашем
файле-источнике устанавливаются свойства client'
а.
Вы всегда должны изменять свойства client'
а
в файле до любого вызова redirect
или flush
и до
генерирования 64KB HTML-вывода.
Если Вы изменяете значения свойств объекта client
в коде после того как HTML был выслан клиенту, эти изменения будут действовать
для оставшейся части клиентского запроса, но затем будут отменены. Отсюда:
следующий клиентский запрос не получит эти значения свойств; он получит
значения, действовавшие в тот момент, когда содержимое было в первый раз
отправлено клиенту. Например,
ваш код содержит такие операторы:
<HTML>
<P>The current customer is
<SERVER>
client.customerName = "Mr. Ed";
write(client.customerName);
client.customerName = "Mr. Bill";
</SERVER>
<P>The current customer really is
<SERVER>
write(client.customerName);
</SERVER>
</HTML>
Эта серия операторов даст в результате такой HTML, отправляемый клиенту:
<P>The current customer is Mr. Ed
<P>The current customer really is Mr. Bill
Теперь, когда появится следующий клиентский запрос,
значение свойства
client.customerName
будет "Mr. Bill". Этот очень похожий набор
операторов даст в результате тот же HTML:
<HTML>
<P>The current customer is
<SERVER>
client.customerName = "Mr. Ed";
write(client.customerName);
flush();
client.customerName = "Mr. Bill";
</SERVER>
<P>The current customer really is
<SERVER>
write(client.customerName);
</SERVER>
</HTML>
Однако при появлении следующего клиентского запроса значение client.customerName
будет "Mr. Ed"; а не "Mr. Bill".
Дополнительно см. "Техника Обслуживания Объекта client".
Сервер отсылает содержимое страницы клиенту. Для страниц без операторов серверного JavaScript сервер просто передаёт HTML клиенту. Для других страниц - машина выполнения реализует логику приложения для конструирования HTML и затем высылает сгенерированную страницу клиенту.
Машина выполнения разрушает объект request
,
сконструированный для данного клиентского запроса. Она сохраняет значения
объекта client
и разрушает физический объект
JavaScript. Она не разрушает объекты project
или server
.
Когда Вы компилируете приложение JavaScript, исходники состоят из HTML-страниц, содержащих операторы серверного JavaScript, и, возможно, HTML-страницы, не содержащие операторов серверного JavaScript. Если пользователь получает доступ к странице, которая не содержит серверных операторов, сервер высылает страницу обратно, как и любую другую HTML-страницу. Если пользователь получает доступ к странице, которая содержит серверные операторы, машина выполнения на сервере конструирует HTML-страницу для отправки в ответе, используя одну из исходных страниц Вашего приложения.
Машина выполнения сканирует исходную страницу. При обнаружении операторов HTML
клиентского JavaScript
она присоединяет их к создаваемой странице. При обнаружении операторов
серверного JavaScript она выполняет их. Хотя большинство операторов серверного JavaScript
выполняются на сервере,
некоторые влияют на конструируемую страницу. В следующих разделах
рассматриваются три функции: write
, flush
и redirect
,
которые влияют на обрабатываемую HTML-страницу.
Как уже было сказано ранее в этой главе, функция write
генерирует HTML
на основе значения выражения
JavaScript, заданного в качестве аргумента. Например, рассмотрим оператор:
write("<P>Customer Name is:" + project.custname + ".");
В ответ на этот оператор JavaScript генерирует HTML, вставляя тэг параграфа и
некоторый текст,
соединённый со значением свойства custname
объекта project
.
Например, если свойство будет иметь значение "Fred's software company", клиент
получит следующий HTML:
<P>Customer Name is: Fred's software company.
Как кажется клиенту, это нормальный HTML. Однако он в действительности динамически сгенерирован машиной выполнения JavaScript.
Для повышения производительности, JavaScript буферизует
конструируемую им
HTML-страницу. Функция flush
сразу высылает данные из внутреннего
буфера клиенту. Если Вы явно не вызываете функцию flush
, JavaScript
высылает данные клиенту после создания каждых 64KB содержимого конструируемой HTML-страницы.
Не путайте функцию flush
с методом flush
класса File
. (Об
использовании класса File
для ввода и вывода в файл см. "Служба
Файловой Системы").
Вы можете использовать flush
для управления временем переноса
данных клиенту. Например, Вы можете очищать буфер до операции, создающей
задержку/delay, такой как запрос к базе данных.
Также, если запрос к БД запрашивает большое количество рядов, очистка буфера
каждый раз после получения нескольких рядов предотвращает паузы при отображении
данных.
ПРИМЕЧАНИЕ: Если Вы используете клиентские куки для обслуживания свойств объекта
client
, Вы обязаны делать все изменения объектаclient
до очистки буфера. Дополнительно см. "Техника Обслуживания Объекта client".
Следующий фрагмент кода показывает, как используется flush
.
Предположим, Ваше приложение должно выполнить некоторые действия с каждым
потребителем/customer в Вашей БД потребителей. Если потребителей много, процесс
может затянуться. Поэтому, для того чтобы пользователь не ждал у застывшего
экрана, Ваше приложение может высылать вывод клиенту до начала обработки и затем
вновь - после конца обработки каждого ряда. Для этого Вы должны использовать
примерно такой код:
flush();
conn.beginTransaction();
cursor = conn.cursor ("SELECT * FROM CUSTOMER", true);
while ( cursor.next() ) {
// ... обработка ряда ...
flush();
}
conn.commitTransaction();
cursor.close();
Функция redirect
прерывает текущий клиентский запрос и стартует
новый по специфицированному URL. Например, у вас имеется оператор:
redirect("http://www.royalairways.com/apps/page2.html");
Когда машина выполняет это оператор, она прерывает текущий запрос. Машина
выполнения не продолжает обработку страницы-оригинала. Следовательно любые
операторы HTML или JavaScript, идущие в оригинальной странице после вызова redirect
,
будут утеряны. Клиент сразу загрузит указанную страницу, отбросив предыдущее
содержимое.
Параметром для redirect
может быть любой оператор серверного JavaScript,
вычисляемый до URL. Таким образом, Вы можете динамически генерировать URL,
используемый в redirect
. Например,
если страница определяет переменную choice
, Вы можете перенаправить
клиента на страницу в зависимости от значения choice
таким образом:
redirect ("http://www.royalairways.com/apps/page"
+ choice + ".html");
Если Вам нужно удостовериться, что текущие свойства client'а
доступны в новом запросе и что Вы используете один из видов техники обслуживания
объекта client
на основе URL,
Вы должны кодировать свойства в URL, передаваемом Вами в redirect
.
О том, как это сделать, см. "Присоединение
Свойств client'а к URL Вручную".
В целом свойства объекта request
и
переменные верхнего уровня JavaScript существуют только в течение одиночного
клиентского запроса. Если Вы выполняете перенаправление на новую страницу, Вам
может понадобиться сохранить некоторую информацию для нескольких запросов. Вы
можете сделать это, присоединив имена и значения свойств к URL, как описано в
разделе
"Кодирование Информации в URL".
Как и большинство других web-серверов, серверы Netscape устанавливают значения для определённого набора переменных окружения, называемых CGI-переменными, при установке контекста для запуска CGI-скрипта. Предполагается, что создатели CGI-скриптов могут использовать эти переменные в своих скриптах.
По контрасту, Netscape web-серверы не устанавливают отдельного окружения для приложений серверного JavaScript. Тем не менее, некоторая часть информации, обычно устанавливаемой в CGI-переменных, может использоваться в приложениях JavaScript. Машина выполнения предоставляет несколько механизмов для доступа к этой информации:
request
ssjs_getCGIVariable
для доступа к
некоторым CGI-переменным и другим переменным окруженияhttpHeader
объекта request
для доступа к свойствам шапки клиентского запроса
В следующей таблице перечислены свойства объекта
request
, соответствующие CGI-переменным. Дополнительно об этих
свойствах и об объекте request
в целом см.
"Объект request".
request
Серверная функция ssjs_getCGIVariable
даёт
Вам доступ к переменным окружения, установленным в серверном процессе, включая CGI-переменные,
перечисленные в следующей таблице.
ssjs_getCGIVariable
Здесь приведён синтаксис ssjs_getCGIVariable
:
value = ssjs_getCGIVariable("name");
Этот оператор устанавливает в переменную value
значение
CGI-переменной name
.
Если предоставленный вами аргумент не является одной из CGI-переменных,
перечисленных в
Таблице 5.8,
машина выполнения ищет переменную окружения с этим именем в среде сервера. Если
она найдена,
машина выполнения возвращает значение; иначе, возвращает null. Например,
следующий код присваивает значение стандартной переменной окружения CLASSPATH
переменной JavaScript
classpath
:
classpath = ssjs_getCGIVariable("CLASSPATH");
Метод httpHeader
объекта request
возвращает шапку/header
текущего клиентского запроса. Для CGI-скрипта Netscape web-серверы устанавливают CGI-переменные
для некоторой информации в шапке. Для приложений JavaScript Вы получаете эту
информацию непосредственно из шапки.
Таблица 5.9 показывает информацию, доступную как
переменные CGI в среде CGI,
но как свойства шапки - в серверном JavaScript. В свойствах шапки символы
подчёркивания в имени
CGI-переменной (_) заменяются на тире (-); например, CGI-переменная
CONTENT_LENGTH
соответствует свойству шапки content-length
.
Дополнительно о манипулировании клиентской "шапкой" см. "Манипуляции с Запросом и Ответом".
В таблице даны CGI-переменные, не поддерживаемые серверным JavaScript, которые не могут быть применены при запуске приложений JavaScript.
Переменная | Описание |
---|---|
GATEWAY_INTERFACE |
Версия CGI, запущенная на сервере. Не применяется к приложениям JavaScript. |
SERVER_SOFTWARE |
Тип запущенного сервера. Недоступна для приложений JavaScript. |
Нередко Вашим приложениям JavaScript нужно передать информацию либо с сервера
клиенту, либо с клиента на сервер. Например, когда пользователь в первый раз
выполняет доступ к приложению videoapp
, оно динамически генерирует
список категорий видео-файлов из текущего содержимого БД. Эта информация,
генерируемая на сервере, должна быть передана обратно клиенту. И наоборот, когда
пользователь щёлкает на категории из этого списка, выбор пользователя должен
быть передан обратно на сервер, чтобы он мог сгенерировать набор файлов.
Есть несколько способов отправки информации с клиента на сервер:
request
для каждого значения в HTML-форме.
(См. "Доступ к Значениям Формы")client
,
Вы можете модифицировать URL, отправленный серверу, чтобы включить значения
свойств объектов client
и request
. (См. "Кодирование
Информации в URL")client
и
request
. (См. "Использование Кук")httpHeader
объекта request
для манипулирования шапкой и, возможно, телом запроса. (См. "Манипуляции
с Запросом и Ответом").Формы это альфа и омега приложений на JavaScript. Вы используете такие элементы формы как текстовые поля и радио-кнопки в качестве первичного механизма переноса данных с клиента на сервер. Когда пользователь щёлкает на кнопке Submit, браузер отправляет значения, введённые форму, на сервер для обработки.
Атрибут ACTION
тэга FORM
определяет приложение, которому отправляются значения. Для того чтобы отправить
информацию приложению на сервер,
используйте URL приложения как значение атрибута ACTION
.
Если документ, содержащий форму, является скомпилированной
частью того же самого приложения,
Вы можете просто предоставить имя страницы вместо полного URL. Например, вот тэг FORM
из приложения-примера Hangman:
<FORM METHOD="post" ACTION="hangman.html">
Формы, отправляемые приложениям на серверном JavaScript, могут использовать get
или post
в качестве значения атрибута METHOD
.
ПРИМЕЧАНИЕ: Приложения на серверном JavaScript не поддерживают автоматическую выгрузку файлов. То есть, если специфицированная action это страница в приложении JavaScript, Вы отправляете элемент
INPUT
с типомTYPE="file"
, Ваше приложение обязано само обработать этот файл, как описано в разделе "Манипуляции с Запросом и Ответом".
Каждый элемент ввода HTML-формы соответствует свойству
объекта
request
. Имя свойства специфицируется атрибутом NAME
элемента формы. Например, следующий HTML создаёт свойство объекта request
под названием
guess
, которое принимает одиночный символ в текстовом поле. Вы
обращаетесь к этому свойству в серверном JavaScript как к request.guess
.
<FORM METHOD="post" ACTION="hangman.html">
<P>
What is your guess?
<INPUT TYPE="text" NAME="guess" SIZE="1">
Элемент SELECT
, дающий возможность множественного выбора, требует
особого обращения, поскольку это единственное свойство, которое может иметь
несколько значений. Вы можете использовать функцию getOptionValue
для получения значений выбранных опций в списке множественного выбора.
Дополнительно см. "Использование Списков Select".
Информацию об объекте request
см. в разделе "Объект
request".
Если Вы хотите обработать данные сначала на стороне клиента, Вы должны создать функцию клиентского JavaScript для выполнения обработки значений элементов формы и передать затем вывод клиентской функции элементу формы. Вы можете скрыть/hide этот элемент, чтобы он не отображался пользователю, если Вы хотите выполнить клиентский препроцессинг.
Например, у Вас имеется функция клиентского JavaScript под
названием
calc
, выполняющая расчёт на основе пользовательского ввода. Вы
хотите передать результат работы этой функции Вашему приложению для дальнейшей
обработки. Сначала Вам нужно определить скрытый элемент формы для результата:
<INPUT TYPE="hidden" NAME="result" SIZE=5>
Затем Вам нужно создать обработчик события onClick
для кнопки Submit,
который назначает вывод функции в скрытый элемент:
<INPUT TYPE="submit" VALUE="Submit"
onClick="this.form.result.value=calc(this.form)">
Значение элемента result
отправляется вместе с другими значениями
элементов формы. Обратиться к этому значению в приложении можно request.result
.
HTML-тэг SELECT
, используемый с атрибутом MULTIPLE
,
даёт возможность ассоциировать несколько значений с одним элементом формы. Если
Вашему приложению нужны списки с возможностью выбора нескольких опций, Вы
используете функцию getOptionValue
для получения значений в JavaScript.
Синтаксис getOptionValue
таков:
itemValue = getOptionValue(name, index)
Здесь name
это строка, специфицированная как атрибут NAME
тэга
SELECT
, а index
это порядковый индекс выбранной опции,
начиная с 0.
Функция getOptionValue
возвращает значение выбранного элемента, как
специфицировано ассоциированным тэгом OPTION
.
Функция getOptionValueCount
возвращает количество опций (специфицированных
тэгами
OPTION
) в списке выбора. Она требует только одного аргумента,
строки, содержащей имя тэга SELECT
.
Например, у Вас имеется следующий элемент:
<SELECT NAME="what-to-wear" MULTIPLE SIZE=8>
<OPTION SELECTED>Jeans
<OPTION>Wool Sweater
<OPTION SELECTED>Sweatshirt
<OPTION SELECTED>Socks
<OPTION>Leather Jacket
<OPTION>Boots
<OPTION>Running Shoes
<OPTION>Cape
</SELECT>
Вы можете обработать ввод из этого select-списка таким образом:
<SERVER>
var i = 0;
var howmany = getOptionValueCount("what-to-wear");
while ( i < howmany ) {
var optionValue =
getOptionValue("what-to-wear", i);
write ("<br>Item #" + i + ": " + optionValue + "\n");
i++;
}
</SERVER>
Если пользователь оставил выбор по умолчанию, скрипт возвратит:
Item #0: Jeans
Item #1: Sweatshirt
Item #2: Socks
Вы можете вручную кодировать свойства объекта request
в URL, по которому выполняется доступ к странице Вашего приложения. При создании URL
используется следующий синтаксис:
URL?varName1=value1[&varName2=value2...]
Здесь URL
это базовый URL, каждое varName
N
это имя свойства,
каждое value
N
это соответствующее
свойству значение (с мнемониками специальных символов). В этой схеме после
базового URL идёт знак вопроса (?
), после которого, в свою очередь,
идут пары имён свойств и их значений. Каждая пара отделяется амперсандом
(&
). Когда машина выполнения на сервере получает результирующий URL
в качестве клиентского запроса, она создаёт свойство объекта request
под названием varName
N
для каждой
переменной.
Например, следующий HTML определяет гиперссылку на страницу, инстанциирующую
свойства объекта request
i
и j
в 1 и 2,
соответственно. Операторы JavaScript в
refpage.html
могут затем обратиться к эти переменным request.i
и
request.j
.
<A HREF="refpage.html?i=1&j=2">Click Here</A>
Вместо использования статической URL-строки, как в предыдущем примере, Вы можете использовать операторы серверного или клиентского JavaScript для динамической генерации URL, кодирующего значения свойств. Например, Ваше приложение может включать страницу:
<HTML>
<HEAD>
<SCRIPT>
function compute () {
// ... заменить подходящими расчётами, // которые возвращают строку поиска ...
return "?num=25";
}
</SCRIPT>
</HEAD>
<BODY>
<a HREF="refpage.html" onClick="this.search=compute()">
Click here to submit a value.</a></p>
</BODY>
</HTML>
В данном случае, если пользователь щёлкнет на ссылке,
машина выполнения на клиенте запустит на выполнение обработчик события onClick
.
Этот обработчик устанавливает часть поиска из URL в ссылке в какую-нибудь
строку, возвращённую функцией compute
. Когда машина выполнения на
сервере получит этот запрос, она создаст свойство num
объекта request
и установит его значение в 25.
В качестве второго примера Вы можете добавить свойства объекта request
в URL, созданный в серверном скрипте. Это особенно применимо, если Вы перенаправляете клиентский запрос на новую страницу. Чтобы добавить свойства
объекта request
в серверный скрипт, Вы можете использовать такой
оператор:
<A HREF=`"refpage.html?i=" + escape(i) + "&j=" + escape(j)`>
Click Here</A>
Если вы создаёте URL в операторе серверного JavaScript, свойства объекта client
не добавляются автоматически. Если Вы используете технику на основе URL для
объекта client
, используйте функцию addClient
для
генерирования окончательного URL. В этом примере оператор может быть таким:
<A HREF=`addClient("refpage.html?i=" + escape(i)
+ "&j=" + escape(j))`>Click Here</A>
Об использовании addClient
см. "Присоединение
Свойств Объекта client к URL Вручную".
Функция escape
ядра JavaScript даёт возможность кодировать имена
или значения, присоединённые к
URL, включая специальные символы. Вообще, если приложению необходимо
генерировать имена и значения собственных свойств в URL запроса, Вы должны
использовать escape
, чтобы гарантировать корректную интерпретацию
всех значений. Дополнительно см. книгу
Серверный JavaScript. Справочник
.
Помните, что URL не изменяется, если пользователь перезагружает его, хотя
содержимое страницы может измениться.
Любые свойства, высланные в оригинальном URL, восстанавливают свои значения в URL,
имевшиеся на момент его первой отправки,
независимо от любых возможных изменений, сделанных при обработке. Например, если
пользователь щёлкнул кнопку
Reload для перезагрузки URL в предыдущем примере, i
и j
снова установятся в 1 и 2, соответственно.
Приложение JavaScript сообщается с клиентом через HTML и клиентский JavaScript. Если Вам нужно просто вывести информацию пользователю, нет никаких проблем: Вы создаёте HTML для форматирования этой информации так, как она будет отображаться.
Однако Вам может понадобиться выслать значения непосредственно клиентскому скрипту. Можно сделать это по-разному, включая такие способы:
SCRIPT
или обработчиках событий. (См. "Прямая
Замена")client
или других значений клиенту.
(См. "Использование Кук")deleteResponseHeader
и addResponseHeader
.
(См. "Манипуляции с Запросом и Ответом").
Чтобы отобразить HTML-форму с набором значений по
умолчанию в элементах формы, используйте тэг INPUT
для создания
необходимых элементов формы, замещая выражения серверного JavaScript
атрибутов VALUE
. Например, Вы можете использовать следующий
оператор для отображения элемента text и установки значения по умолчанию на
основе значения переменной client.custname
:
<INPUT TYPE="text" NAME="customerName" SIZE="30"
VALUE=`client.custname`>
Начальным значением этого текстового поля становится значение переменной client.custname
.
Так, если значением client.custname
является Victoria, клиенту
будет выслан такой оператор:
<INPUT TYPE="text" NAME="customerName" SIZE="30" VALUE="Victoria">
Вы может использовать аналогичную технику со скрытыми элементами формы, если не хотите показывать значения пользователю, как в следующем примере:
<INPUT TYPE="hidden" NAME="custID" SIZE=5 VALUE=`client.custID`>
В обоих случаях Вы можете использовать эти значения
клиентского JavaScript в значениях свойств объектов, доступных клиенту. Если эти
два элемента находятся в форме с именем entryForm
,
то значения станут свойствами JavaScript document.entryForm.customerName
и document.entryForm.custID
, соответственно. Вы можете затем
выполнять обработку этих значений на стороне клиента. См. также книгу
Клиентский JavaScript. Руководство
.
Вы можете также использовать серверный JavaScript для
генерирования клиентских скриптов. Эти значения могут использоваться в
последовательности операторов на клиенте. В качестве простого примера Вы можете
инициализировать клиентскую переменную по имени budget
на основе
значения client.amount
таким образом:
<p>The budget is:
<SCRIPT>
<SERVER>
write("var budget = " + client.amount);
</SERVER>
document.write(budget);
</SCRIPT>
Если значение client.amount
равно 50, это
сгенерирует такой JavaScript:
<p>The budget is:
<SCRIPT>
var budget = 50
document.write(budget);
</SCRIPT>
При запуске на клиенте это будет выглядеть так:
The budget is: 50
Куки это механизм, который Вы можете использовать на
клиенте для сохранения информации между запросами. эта информация находится в
файле с названием cookie.txt
(куки-файл), хранящемся на клиентской
машине. Протокол Netscape cookie детально описан в книге
Клиентский JavaScript. Руководство
.
Вы можете использовать куки для отправки информации в обоих направлениях, от
клиента серверу и с сервера клиенту. Куки, высылаемые с клиента, становятся
свойствами объекта
client
или объекта request
. Хотя Вы можете выслать с
сервера клиенту любое строковое значение в качестве куки, простейший метод будет
- отправить свойства объекта
client
.
Если приложение использует технику клиентских кук для работы с объектом client
,
машина выполнения на сервере сохраняет имена и значения свойств объекта client
как куки на клиенте. Об использовании кук для работы с объектом
client
см. раздел "Техника Работы с
Объектом client".
Для свойства объекта client
propName
машина выполнения автоматически создаёт куку с именем NETSCAPE_LIVEWIRE.
propName
,
принимая, что приложение использует технику работы с клиентскими куками. Машина
выполнения кодирует значения свойств так, как это требуется по протоколу кук Netscape
cookie protocol.
Для доступа к этим кукам в клиентском JavaScript Вы можете извлечь эту
информацию путём использования свойства
document.cookie
и такой функции как getSSCookie
, как
показано здесь:
function getSSCookie(name) {
var search = "NETSCAPE_LIVEWIRE." + name + "=";
var retstr = "";
var offset = 0;
var end = 0;
if (document.cookie.length > 0) {
offset = document.cookie.indexOf(search);
if (offset != -1) {
offset += search.length;
end = document.cookie.indexOf(";", offset);
if (end == -1)
end = document.cookie.length;
retstr = unescape(document.cookie.substring(offset, end));
}
}
return(retstr)
}
Функция getSSCookie
не является предопределённой функцией JavaScript.
Если Вам нужна похожая функциональность, Вы обязаны определить её (функцию) в
Вашем приложении.
Чтобы отправленная на сервер информация стала свойством объекта client
,
добавьте куку с именем, имеющим форму NETSCAPE_LIVEWIRE.
propName.
Предположим, Ваше приложение использует технику работы с клиентскими куками, а
машина выполнения на сервере создаёт свойство объекта client
под
названием
propName
для данной куки.
Тогда Вы можете использовать функцию типа следующей:
function setSSCookie (name, value, expire) {
document.cookie =
"NETSCAPE_LIVEWIRE." + name + "="
+ escape(value)
+ ((expire == null) ? "" : ("; expires=" + expire.toGMTString()));
}
Здесь функция setSSCookie
также не является предопределённой
функцией JavaScript. Если Вам необходима аналогичная функциональность, Вы
обязаны определить функцию в Вашем приложении.
Вы можете вызывать эти функции в клиентском JavaScript для получения набора
значений свойств объекта
client
, как в следующем примере:
var value = getSSCookie ("answer");
if (value == "") {
var expires = new Date();
expires.setDate(expires.getDate() + 7);
setSSCookie ("answer", "42", Expires);
}
else
document.write ("The answer is ", value);
Эта группа операторов проверяет, имеется ли свойство объекта client
с названием
answer
. Если нет, код создаёт его и устанавливает значение 42; если
найдено, выводится его значение.
Когда серверу высылается запрос на страницу приложения JavaScript, шапка/header
запроса включает все куки, установленные в данный момент для приложения. Можно
использовать метод request.httpHeader
для доступа к этим кукам из
серверного JavaScript и присвоения их значений серверным переменным.
И наоборот, Вы можете использовать функцию addResponseHeader
для
добавления новых кук в ответ, высылаемый обратно клиенту. Эта функциональность
описана в разделе
"Манипуляция с Запросом и Ответом".
На клиентской стороне Вы можете использовать функцию вроде приведённой здесь для доступа к куке:
function GetCookie (name) {
var arg = name + "=";
var alen = arg.length;
var clen = document.cookie.length;
var i = 0;
while (i < clen) {
var j = i + alen;
if (document.cookie.substring(i, j) == arg) {
var end = document.cookie.indexOf (";", j);
if (end == -1)
end = document.cookie.length;
return unescape(document.cookie.substring(j, end));
}
i = document.cookie.indexOf(" ", i) + 1;
if (i == 0) break;
}
return null;
}
И Вы может использовать функцию типа приведённой ниже для установки куки на клиенте:
function setCookie (name, value, expires, path, domain, secure) {
document.cookie =
name + "="
+ escape(value)
+ ((expires) ? "; expires=" + expires.toGMTString() : "")
+ ((path) ? "; path=" + path : "")
+ ((domain) ? "; domain=" + domain : "")
+ ((secure) ? "; secure" : "");
}
Если путь, специфицированный Вами для куки, находится в Вашем приложении JavaScript, то эта кука будет отправлена в ответ на любой запрос к приложению.
Вы можете использовать эту технику для передачи информации куки между клиентом и
сервером, независимо от вида используемой техники работы с объектом client
.
Серверный JavaScript имеет сборщика мусора, который автоматически освобождает память, выделенную для объекта, более не используемого. В большинстве случаев нет необходимости понимать досконально работу сборщика мусора. Этот раздел даёт обзор сборщика мусора и информацию о том, когда он вызывается.
Этот раздел даёт возможность продвинутым пользователям взглянуть на внутренние процессы JavaScript. Netscape не гарантирует, что эти алгоритмы останутся такими же в последующих релизах.
Пространство объекта в JavaScript состоит из арен. То есть машина выполнения JavaScript размещает набор арен, из которого затем размещает объекты. Если машина выполнения получает запрос на новый объект, она сначала просматривает список свободных арен. Если в списке свободных есть место, машина выделяет его. Иначе машина выполнения выделяет место в текущей используемой арене. Если используются все арены, машина размещает новую арену. Если все объекты арены - мусор, сборщик мусора освобождает арену.
Строка JavaScript обычно размещается как GC-объект. Строка имеет ссылку на байты строки, которая также размещается в куче/heap процесса. Если строковой объект подвергся сборке мусора, байты строки высвобождаются.
Работа сборщика мусора JavaScript основана на пометке и уничтожении. Сборщик не перемещает объекты. Он всегда обрабатывает корневой набор объектов. Этот корневой набор/root set включает в себя стэк JavaScript, объект global для контекста JavaScript и любые объекты JavaScript, которые были явно добавлены в корневой набор. В фазе разметки сборщик мусора помечает все объекты, достижимые из корневого набора. В конечной фазе все непомеченные объекты уничтожаются. Все убранные объекты собираются в список свободных.
Сборка мусора считается необходимой, если количество текущих используемых байтов в 1.5 раза превышает количество байтов, бывших в использовании в конце последней уборки мусора. Машина выполнения проверяет это условие в следующих точках и начинает уборку, если это необходимо:
if
, while
,
вызове функции и т.п.)lw_ForceGarbageCollection
.
Функция ssjs_onError
, если она определена в Вашем приложении,
вызывается в случае ошибки серверного JavaScript, такой как "undefined variable name/имя
переменной не определено". В функции ssjs_onError
Вы можете
делать всё,
что можно делать в функции серверного JavaScript, включая доступ к объектам server
, project
, client
и request
. Вы можете также выполнять перенаправление и вызывать другие функции.
Функция ssjs_onError
имеет следующий
синтаксис:
function ssjs_onError (<message>,<file>,<line number>)
<message>
текст сообщения об ошибке
<line number>
номер строки с ошибкой
Ошибка JavaScript в ходе выполнения функции onError
записывается в log-файл и трассируется (если активна). Функция ssjs_onError
,
однако, не вызывается рекурсивно. Ошибка в функции onError вызывает запись
сообщения в error log,
но не выполняет вызов onError.
function ssjs_onError(msg,file,line)
{
write("<br>\n<hr>")
write("error message: "+msg+"<br>\n")
write("file name: "+file+"<br>\n")
write("line number: "+line+"<br>\n")
write("<hr>")
}
ПРИМЕЧАНИЕ:
Чтобы дать каждой странице её собственную специальную функцию onError, добавьте присвоение ssjs_onError в начале кода страницы. Например:
ssjs_onError = custom_onError;
function custom_onError(msg,file,line)
{
// ...
}
Серверный JavaScript выполняет всё, что ssjs_onError
представляет
во время ошибки.
Вы можете использовать одну функцию ssjs_onError
, которая совместно
используется всеми страницами, либо можете динамически переключаться на другую
функцию onError в любое время, в том числе в начале каждой страницы.
Если два запроса выполняют одну и ту же функцию onError в один момент времени,
они имеют различные среды выполнения, как если бы Вы одновременно выполняли
какую-нибудь другую функцию.
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассматривается использование технологии 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. Все Права Зарезервированы.
В этой главе рассматривается дополнительная функциональность серверного JavaScript, которую Вы можете использовать для отправки e-mail из Вашего приложения, для доступа к файловой системе сервера, подключения внешних библиотек или непосредственного манипулирования клиентскими запросами и клиентскими ответами.
В главе имеются следующие разделы:
Вашему приложению может понадобиться отправить email-сообщение. Для этого Вы
используете экземпляры класса SendMail
. Единственным методом SendMail
является send
, для отправки сообщения,
а errorCode
и errorMessage
служат для интерпретации ошибок.
Например, следующий скрипт отсылает почту в vpg со специфицированной темой/subject и телом сообщения:
<server>
SMName = new SendMail();
SMName.To = "vpg@royalairways.com";
SMName.From = "thisapp@netscape.com";
SMName.Subject = "Here's the information you wanted";
SMName.Body = "sharm, maldives, phuket, coral sea, taveuni, maui,
cocos island, marathon cay, san salvador";
SMName.send();
</server>
В таблице даны свойства класса SendMail
.
Свойства To
и From
необходимы; все остальные свойства - по выбору/optional.
SendMail
Вы можете добавлять к этим свойствам любые другие.
Все свойства класса SendMail
включаются в шапку/header сообщения
при фактической отправке. Например, следующий код отсылает сообщение получателю bill
от vpg
, устанавливая в поле vpg
organization значение Royal Airways.
Отвечает на сообщение от vpgboss
.
mailObj["Reply-to"] = "vpgboss";
mailObj.Organization = "Royal Airways";
mailObj.From = "vpg";
mailObj.To = "bill";
mailObj.send();
Дополнительно о предопределённых полях шапки см. RFC 822, стандарт формата текстовых сообщений Internet.
Класс SendMail
позволяет отправлять простое текстовое почтовое
сообщение или сложное MIME-сообщение. Вы можете также добавить в сообщению
приложение/attachment. Для отправки MIME-сообщения добавьте свойство Content-type
к объекту SendMail
и укажите в нём MIME-тип сообщения.
Например, следующий участок кода отсылает изображение в формате GIF:
<server>
SMName = new SendMail();
SMName.To = "vpg@royalairways.com";
SMName.From = "thisapp@netscape.com";
SMName.Subject = "Here's the image file you wanted";
SMName["Content-type"] = "image/gif";
SMName["Content-Transfer-Encoding"] = "base64";
// В следующем операторе image2.gif обязан быть кодирован с базой 64/base 64.
// Если вы используете uuencode для кодирования GIF-файла, удалите header
// (например, "begin 644 image2.gif") и замыкающий ("end").
fileObj = new File("/usr/somebody/image2.gif");
openFlag = fileObj.open("r");
if ( openFlag ) {
len = fileObj.getLength();
SMName.Body = fileObj.read(len);
SMName.send();
}
</server>
Некоторые MIME-типы требуют больше информации. Например, если content type это multipart/mixed
,
Вы обязаны также специфицировать сепаратор границ для одного или более различных
наборов данных тела. Например, следующий код отсылает многочастное сообщение,
содержащее две части, каждая из которых является обычным текстом:
<server>
SMName = new SendMail();
SMName.To = "vpg@royalairways.com";
SMName.From = "thisapp@netscape.com";
SMName.Subject = "Here's the information you wanted";
SMName["Content-type"]
= "multipart/mixed; boundary=\"simple boundary\"";
fileObj = new File("/usr/vpg/multi.txt");
openFlag = fileObj.open("r");
if ( openFlag ) {
len = fileObj.getLength();
SMName.Body = fileObj.read(len);
SMName.send();
}
</server>
Вот файл multi.txt
, содержащий многочастное сообщение:
Это место для преамбулы.
Она игнорируется.
Это удобное место для комментария, предназначенного для читателей, не знакомых с MIME.
--простая граница
Это первая часть тела сообщения.
Это НЕ конец с символом обрыва строки.
--простая граница
Content-Type: text/plain; charset=us-ascii
Это вторая часть тела сообщения.
Это КОНЕЦ с символом обрыва строки.
--простая граница--
Это эпилог. Он также игнорируется.
Вы можете вкладывать друг в друга многочастные сообщения. То есть, если у Вас есть сообщение, content type которого многочастный, Вы можете включить другой многочастное сообщение в его тело. В таких случаях будьте внимательны и убедитесь, штаа каждый вложенный многочастный объект использует разные ограничители.
Детали о MIME-типах см. в RFC 1341 1 , MIME-стандарте. Об отправке почтовых сообщений в JavaScript см. также описание этого класса в книге Серверный JavaScript. Справочник .
JavaScript предоставляет класс File
, который даёт приложению
возможность записывать в файловой системе сервера. Это используется для
генерации постоянных HTML-файлов и хранения информации без использования сервера
БД. Одним из важнейших преимуществ хранения информации в файле вместо JavaScript-объектов
является то, что информация сохраняется даже при отказе сервера.
Соблюдайте осторожность при использовании класса File
. Приложение JavaScript
может читать или записывать файлы везде, где позволяет операционная система, в
том числе, возможно, и в системных файлах. Вы должны быть уверены, что Ваше
приложение не позволяет читать файлы паролей и другую закрытую информацию или
записывать в файлы. Делайте так, чтобы имена файлов, которые Вы передаёте в
методы, не могли быть изменены хакерами.
Например, не используйте свойства объектов client
или
request
в качестве имён файлов, поскольку эти значения могут стать
доступными хакеру через куки или URL. В таких случаях хакер сможет
модифицировать куки или URL, чтобы получить доступ к закрытым файлам.
Исходя из таких же соображений, Navigator не предоставляет автоматического доступа к файловой системе клиентской машины. Если необходимо, пользователь может сохранять информацию непосредственно в клиентской файловой системе, делая соответствующий выбор в меню Navigator'а.
Чтобы создать экземпляр класса File
, используйте стандартный
синтаксис JavaScript для создания объекта:
fileObjectName = new File("path");
Здесь fileObjectName
это имя, по которому Вы обращаетесь к файлу, а path
это полный путь к файлу. Этот path должен быть в формате серверной файловой
системы, а не URL.
Вы можете отобразить имя файла, используя функцию write
с File
-объектом в качестве аргумента. Например, следующий оператор выводит имя файла:
x = new File("\path\file.txt");
write(x);
После создания File
-объекта Вы можете
использовать метод open
для открытия файла и чтения и записи. Метод open
имеет следующий синтаксис:
result = fileObjectName.open("mode");
Это метод возвращает true
, если операция прошла успешно, и false
в ином случае.
Если файл уже открыт, операция терпит неудачу, и оригинальный файл остаётся открытым.
Параметр mode
это строка, специфицирующая режим открытия файла. В
таблице описаны эти режимы.
Когда приложение заканчивает использование файла, оно
может закрыть его, вызвав метод close
. Если файл не открыт, close
терпит неудачу. Это метод возвращает true
при успехе и false
- в противном случае.
Часто доступ ко многим приложениям могут выполнять одновременно многие пользователи. Вообще разные пользователи не должны пытаться одновременно вносить изменения в файлы, поскольку это может привести к непредсказуемым ошибкам.
Чтобы предотвратить модификацию файла одновременно
несколькими пользователями, используйте один из механизмов блокирования,
предоставляемых службой Session Management Service, как описано в разделе
"Безопасное Совместное Использование Объектов с
Помощью Блокировки". Если один пользователь блокировал файл,
другие пользователи приложения должны ждать, пока файл не будет разблокирован. В
общем это означает, что lock
(замок/блокировка) должна предшествовать всем
файловым операциям; после выполнения операций должно выполняться unlock
(разблокирование).
Если только одно приложение может модифицировать данный
файл, Вы можете получать блокировку в объекте project
. Если более
чем одно приложение может иметь доступ к одному и тому же файлу, получайте
блокировку в объекте server
.
Например, у Вас создан файл myFile
. Затем Вы может использовать его так:
if ( project.lock() ) {
myFile.open("r");
// ... файл используется ...
myFile.close();
project.unlock();
}
Таким образом, только один пользователь приложения может изменять файл в данный
момент времени. Для более тонкого управления блокировкой Вы можете создать Ваш
собственный экземпляр класса Lock
для управления доступом к данному
файлу. Это описано в разделе "Использование Lock-Экземпляров".
Класс File
имеет несколько методов, которые можно использовать
после открытия файла:
setPosition
,
getPosition
, eof
. Это методы для установки и получения
текущей позиции указателя в файле и для определения, не находится ли указатель
в конце файла.read
, readln
, readByte
.write
, writeln
,
writeByte
, flush
.byteToString
,
stringToByte
. Конвертируют одно число в символ и наоборот.getLength
, exists
,
error
, clearError
. Для получения информации о файле и
для получения и очистки error-статуса.Эти методы описаны в последующих разделах.
Физический файл, ассоциированный с File
-объектом, имеет указатель
текущей позиции в файле. Когда Вы открываете файл, указатель находится в начале
либо в конце файла, в зависимости от режима, использованного при открытии файла.
В пустом файле начало и конец файла это одна точка.
Метод setPosition
позиционирует указатель в файле,
возвращая true
при успехе и false
- в ином случае.
fileObj.setPosition(position);
fileObj.setPosition(position, reference);
Здесь fileObj
это File
-объект, position
это целое число, указывающее позицию указателя, а reference
указывает относительную точку для position
таким образом:
Метод getPosition
возвращает текущую позицию в файле, где первый
байт файла это всегда байт 0. Этот метод возвращает -1, если имеется ошибка.
fileObj.getPosition();
Метод eof
возвращает true
, если указатель находится в
конце файла, и
false
- в ином случае. Этот метод возвращает true
после первой операции чтения, которая пытается прочесть после конца файла.
fileObj.eof();
Используйте методы read
, readln
и readByte
для чтения из файла.
Метод read
читает специфицированное
количество байтов из файла и возвращает строку.
fileObj.read(count);
Здесь fileObj
это File
-объект, а count
это целое число, специфицирующее количество байтов для чтения. Если count
специфицирует больше байтов, чем осталось в файле,
метод читает до конца файла.
Метод readln
читает следующую строку файла и возвращает её как строку.
fileObj.readln();
Здесь fileObj
это File
-объект. Символы-разделители строк
(\r\n
в Windows или просто \n
в Unix или Macintosh) не
включаются в строку. Символ \r
пропускается; \n
определяет действительный конец строки.
Этот компромисс даёт осмысленное поведение на всех платформах.
Метод readByte
читает следующий байт из файла и возвращает числовое значение следующего байта или -1.
fileObj.readByte();
Для записи в файл имеются методы write
, writeln
,
writeByte
и flush
.
Метод write
записывает строку в файл. В случае успеха операции
возвращает true
и false
- в ином случае.
fileObj.write(string);
Здесь fileObj
это File
-объект, в string
это строка JavaScript.
Метод writeln
записывает сроку в файл и
вводит последующие \n
(\r\n
в текстовом режиме Windows).
Возвращает true
при успешном выполнении записи и false
- в ином случае.
fileObj.writeln(string);
Метод writeByte
записывает байт в файл. Возвращает true
в случае успеха и false
- в противном случае.
fileObj.writeByte(number);
Здесь fileObj
это File
-объект, а number
это число.
Когда Вы используете один из этих методов, содержимое
файла внутренне буферизуется.
Метод flush
записывает буфер в файл на диске. Этот метод возвращает true
в случае успеха и false
- в противном случае.
fileObj.flush();
Есть два основных формата файлов : ASCII-текст и
бинарный.
Методы byteToString
и stringToByte
класса File
конвертируют данные этих форматов.
Метод byteToString
конвертирует число в односимвольную строку. Это
static-метод. Вы можете использовать класс File
сам по себе, а не
его экземпляр, для вызова этого метода.
File.byteToString(number);
Если аргумент - не число, метод возвращает пустую строку.
Метод stringToByte
конвертирует первый символ своего аргумента,
строку, в число. Это также static-метод.
File.stringToByte(string);
Метод возвращает числовое значение первого символа или 0.
Вы можете использовать несколько методов класса File
для получения
информации о файлах и работы с error-статусом.
Метод getLength
возвращает число символов в
текстовом файле или количество байтов в любом другом файле. Возвращает -1, если
возникла ошибка.
fileObj.getLength();
Метод exists
возвращает true
, если файл существует, и false
- в ином случае.
fileObj.exists();
Метод error
возвращает статус ошибки или -1, если файл не открыт
или не может быть открыт. Статус ошибки/error status это ненулевое значение,
если ошибка возникла, и 0 в ином случае (нет ошибки). Коды статуса ошибки
зависят от платформы; обратитесь к документации по Вашей ОС.
fileObj.error();
Метод clearError
очищает error-статус (значение error
)
и значение eof
.
fileObj.clearError();
На сервере Netscape имеется приложение-образец Viewer. Поскольку это приложение даёт возможность просматривать файлы на сервере, оно не устанавливается автоматически.
Viewer это хороший пример использования класса File
.
Если Вы установили это приложение, позаботьтесь об ограничении доступа к нему,
чтобы неавторизованный пользователь не мог просматривать файлы на сервере.
Об ограничении доступа к приложению см. раздел "Публикация
Приложения".
Следующий код из приложения Viewer создаёт экземпляр класса File
,
открывает его для чтения и генерирует HTML, отражающий строки файла, с
разделительной линией после каждой строки.
x = new File("\tmp\names.txt");
fileIsOpen = x.open("r");
if (fileIsOpen) {
write("file name: " + x + "<BR>");
while (!x.eof()) {
line = x.readln();
if (!x.eof())
write(line+"<br>");
}
if (x.error() != 0)
write("error reading file" + "<BR>");
x.close();
}
Для взаимодействия с внешними приложениями рекомендуется использовать LiveConnect, как описано в Главе 14, "LiveConnect. Обзор.". Однако Вы можете также вызывать функции, написанные на других языках, таких как C, C++ или Pascal, и скомпилированных в библиотеки. Такие функции называются native-функции или внешние функции. Библиотеки внешних функций, называемые внешними библиотеками, являются библиотеками динамической компоновки/dll в ОС Windows и совместно используемыми объектами/shared objects - в ОС Unix.
Будьте осторожны при использовании внешних функций в Вашем приложении. Внешние функции могут нарушить защиту/безопасность, если внешняя программа выполняет команды пользователя в командной строке (например, программа, дающая возможность войти в ОС, или команды оболочки/shell). Эта функциональность опасна, так как хакер может присоединить дополнительные команды, используя точку с запятой для присоединения нескольких операторов. Лучше исключить использование ввода командной строки, если Вы не проверяете его достаточно жёстко.
Внешние функции используются в следующих случаях:
В директории примеров jsaccall
есть
несколько исходных и header-файлов, иллюстрирующих вызов функций внешних библиотек из приложения JavaScript.
В Application Manager Вы ассоциируете внешнюю библиотеку с определённым приложением. Но после того как библиотека ассоциирована с одним из приложений, она становится доступной всем другим установленным приложениям.
Выполните следующие действия для использования библиотеки внешних функций в приложении JavaScript:
registerCFunction
- для идентифицирования вызываемых функций
библиотеки - и callC
- для вызова этих функций. (См. "Регистрация
Внешних Функций" и
"Использование Внешних Функций в JavaScript")Важно!Вы обязаны рестартовать Ваш сервер, чтобы установить библиотеку для использования с приложениями. Вы обязаны рестартовать сервер каждый раз после добавления файлов новых библиотек или изменения имён файлов библиотек, используемых приложениями.
Хотя Вы можете написать внешние библиотеки на любом языке, JavaScript использует
соглашения языка C по вызову. Ваш код обязан подключать header-файл jsaccall.h
,
находящийся в директории js\samples\jsaccall\
.
Эта директория также содержит исходный код примеров
приложений, которые вызывают функции C, определённые в jsaccall.c
.
Просмотрите эти файлы, чтобы найти более конкретные рекомендации по написанию
функций C для использования с JavaScript.
Функции, вызываемые из JavaScript, обязаны быть экспортируемыми и обязаны соответствовать этому определению типа:
typedef void (*LivewireUserCFunction)
(int argc, struct LivewireCCallData argv[],
struct LivewireCCallData* result, pblock* pb,
Session* sn, Request* rq);
Прежде чем Вы сможете запустить приложение, использующее функции внешних библиотек, Вы обязаны идентифицировать файлы этих библиотек. Используя Application Manager, Вы можете идентифицировать библиотеки, когда Вы устанавливаете приложение (щёлкнув Add) или когда модифицируете параметры инсталяции приложения (щёлкнув Modify). Дополнительно об идентификации файлов библиотек с помощью Application Manager см. "Установка Нового Приложения".
После ввода пути к файлам библиотек в Application Manager Вы обязаны рестартовать сервер, чтобы изменения вступили в силу. Затем необходимо скомпилировать и рестартовать приложение.
После идентификации внешних библиотек с помощью Application Manager
все приложения, запущенные на данном сервере, могут вызывать функции этих
библиотек (используя
registerCFunction
и callC
).
Используйте JavaScript-функцию registerCFunction
для регистрации
внешней функции для использования с приложением JavaScript. Эта функция имеет
следующий синтаксис:
registerCFunction(JSFunctionName, libraryPath, CFunctionName);
Здесь JSFunctionName
это имя функции как она
будет вызываться в JavaScript функцией callC
. Параметр libraryPath
это полный путь к библиотеке, использующий соглашения Вашей ОС, а параметр
CFunctionName
это имя C-функции как она определена в библиотеке.
В вызове этого метода Вы обязаны вводить имя точно в указанном регистре,
указанном в Application Manager, даже в ОС NT.
ПРИМЕЧАНИЕ:
Backslash (\) это специальный символ в JavaScript, поэтому Вы обязаны использовать forward slash (/) или двойной backslash (\\) для отделения Windows-директории и имён файлов в
libraryPath
.
Данная функция возвращает true
, если функция зарегистрирована успешно, и
false
- в ином случае. Функция может потерпеть неудачу, если JavaScript
не сможет найти библиотеку по специфицированному пути или не найдёт
специфицированную функцию в библиотеке.
Приложение обязано использовать registerCFunction
для регистрации
функции, прежде чем сможет использовать
callC
для её вызова. После того как приложение зарегистрировало
функцию, оно может вызывать эту функцию любое число раз. Хорошим местом для
регистрации функций является начальная страница приложения.
После того как Ваше приложение зарегистрировало функцию, оно может использовать callC
для её вызова. Эта функция имеет следующий синтаксис:
callC(JSFunctionName, arguments);
Здесь JSFunctionName
это имя функции, как она была идентифицирована
с помощью
registerCFunction
, а arguments
это список разделённых
запятыми аргументов внешней функции. В качестве аргументов могут использоваться
любые значения JavaScript: строки, числа, булевы значения,
объекты или null. Количество аргументов обязано соответствовать количеству
необходимых аргументов внешней функции. Хотя Вы можете специфицировать объект JavaScript
в качестве аргумента, это используется редко,
поскольку объект конвертируется в строку перед передачей внешней функции.
Эта функция возвращает строковое значение, возвращённое внешней функцией.
Функция callC
может возвращать только строковые значения.
Приложение-образец jsaccall
иллюстрирует использование внешних
функций.
Директория jsaccall
содержит исходный код C (в jsaccall.c
),
определяющий C-функцию с именем mystuff_EchoCCallArguments
. Эта
функция принимает любое количество аргументов и возвращает строку, содержащую HTML
с перечислением аргументов. Это пример иллюстрирует вызов функций
C из приложения JavaScript и возвращаемые значения.
Чтобы запустить jsaccall
, Вы обязаны скомпилировать jsaccall.c
имеющимся у Вас компилятором C. Командные строки для нескольких распространённых
компиляторов даны в файле в виде комментариев.
Следующие операторы JavaScript (взятые из jsaccall.html
)
регистрируют C-функцию как
echoCCallArguments
в JavaScript, вызывают функцию echoCCallArguments
,
а затем генерируют HTML на основе значения, возвращённого данной функцией.
var isRegistered = registerCFunction("echoCCallArguments",
"c:\\mycode\\mystuff.dll", "mystuff_EchoCCallArguments");
if (isRegistered == true) {
var returnValue = callC("echoCCallArguments",
"first arg",
42,
true,
"last arg");
write(returnValue);
}
else {
write("registerCFunction() returned false, "
+ "check server error log for details")
}
Функция echoCCallArguments
создаёт
результирующую строку, содержащую HTML, который выводит тип и значение каждого
переданного ей аргумента JavaScript. Если
registerCFunction
возвращает true, вышеприведённый код генерирует
такой HTML:
argc = 4<BR>
argv[0].tag: string; value = first arg<BR>
argv[1].tag: double; value = 42<BR>
argv[2].tag: boolean; value = true<BR>
argv[3].tag: string; value = last arg<BR>
Обычный запрос, высылаемый клиентом на сервер, не имеет content type
(тип содержимого). Машина выполнения JavaScript автоматически обрабатывает такие запросы.
Однако, если пользователь отправляет форму, то клиент автоматически помещает content type
в шапку/header, чтобы сообщить серверу, как интерпретировать данные формы. Этот content type обычно
application/x-www-form-urlencoded
. Машина выполнения также
автоматически обрабатывает запросы с этим content type. В таких ситуациях Вам
редко нужен прямой доступ к шапкам/header запроса или ответа. Если, однако, Ваше
приложение использует иной content type, оно обязано уметь работать с шапкой запроса.
В свою очередь, типичный ответ сервера клиентку имеет тип содержимого text/html
.
Машина выполнения автоматически добавляет этот content type в свой ответ. Если
Вам в ответе нужен другой content type, Вы обязаны предоставить его сами.
Для поддержки этих потребностей машина выполнения JavaScript на сервере даёт
возможность Вашему приложению иметь доступ к
(1) шапке/header любого запроса и к (2) телу запроса, имеющего нестандартный content type.
Вы уже контролируете тело ответа тэгом SERVER
и тэгами HTML.
Функциональность, описанная в этом разделе, даёт также возможность управлять шапкой/header ответа.
Вы можете использовать эту функциональность в различных целях. Например, как описано в разделе "Использование Кук", можно осуществлять связь серверных и клиентских процессов с помощью кук. Также Вы можете использовать эту функциональность для поддержки выгрузки файлов.
World Wide Web Consortium публикует онлайновую информацию о протоколе HTTP и о том, что может пересылаться по этому протоколу. См., например, HTTP Specifications and Drafts .
Для доступа к парам имя/значение шапки клиентского запроса используйте метод httpHeader
объекта request
. Этот метод возвращает объект, чьи свойства и
значения соответствуют парам имя/значение шапки.
Например, если запрос содержит куки, header["cookie"]
или
header.cookie
будет его значением. Свойство cookie
,
содержащее все пары имя/значение этой куки (со значениями, кодированными так,
как описано в разделе "Использование Кук"),
обязано разбираться Вашим приложением.
Следующий код выводит свойства и значения шапки:
var header = request.httpHeader();
var count = 0;
var i;
for (i in header ) {
write(count + ". " + i + " " + header[i] + "<br>\n");
count++;
}
Если Вы отправили форму методом GET
, на
выходе получится примерно так:
0. connection Keep-Alive
1. user-agent Mozilla/4.0b1 (WinNT; I)
2. host piccolo:2020
3. accept image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
Если для отправки формы использован метод POST
,
вывод будет таким:
0. referer http://piccolo:2020/world/hello.html
1. connection Keep-Alive
2. user-agent Mozilla/4.0b1 (WinNT; I)
3. host piccolo:2020
4. accept image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
5. cookie NETSCAPE_LIVEWIRE.oldname=undefined; NETSCAPE_LIVEWIRE.number=0
6. content-type multipart/form-data; boundary=---------------------------79741602416605
7. content-length 208
Для нормальных HTML-запросов content type (тип содержимого) запроса будет application/x-www-form-urlencoded
.
После получения запроса с этим content type машина выполнения JavaScript на
сервере обрабатывает запрос, используя данные из тела запроса. В этой ситуации
Вы не можете получить прямой доступ к необработанным данным тела запроса. (Конечно,
Вы можете получить доступ к его содержимому через объекты
request
и client
, сконструированные машиной
выполнения).
Если, однако, запрос имеет любой иной content type, машина выполнения не обрабатывает автоматически тело запроса. В этом случае Ваше приложение должно определить, что делать с содержимым.
Возможно, другая страница Вашего приложения отправила запрос этой странице. Следовательно, Ваше приложение обязано предусматривать возможность получения необычного content type и должно знать, как его обработать.
Для получения доступа к телу запроса Вы используете
метод getPostData
объекта request
. Этот метод
принимает в качестве параметра количество символов тела. Если Вы специфицируете 0,
тело возвращается целиком. return-значение это строка, содержащая запрошенные
символы. Если доступных данных нет, метод возвращает пустую строку.
Можно использовать этот метод для получения всех символов за один раз или читать участки данных. Представляйте тело запроса как поток символов. При чтении Вы можете только продвигаться вперёд; Вы не можете читать одни и те же символы несколько раз.
Чтобы назначить всё тело запроса переменной postData
, можно
использовать следующий оператор:
postData = request.getPostData(0);
Если в качестве параметра специфицировано 0, метод
получает весь запрос. Вы можете точно определить, сколько символов содержится в
информации, используя свойство content-length
шапки таким образом:
length = parseInt(header["content-length"], 10);
Для получения тела запроса небольшими блоками Вы можете специфицировать иной параметр. Например, следующий код обрабатывает тело запроса блоками по 20 символов:
var length = parseInt(header["content-length"], 10);
var i = 0;
while (i < length) {
postData = request.getPostData(20);
// ...обработка postData...
i = i + 20;
}
Конечно, такой подход имеет смысл только тогда, когда Вы знаете, что блоки состоят из 20 символов информации.
Если отправляемый клиенту ответ/response использует специальный content type
(тип содержимого), Вы должны кодировать этот content type в шапке ответа. Машина
выполнения JavaScript автоматически добавляет content type по умолчанию
(text/html
) в шапку ответа/response header. Если вам необходима
специальная шапка, Вы обязаны сначала удалить из шапки старый content type по
умолчанию, а затем уже добавить новый. Это делается при помощи функций
addResponseHeader
и deleteResponseHeader
.
Например, если Ваш response использует royalairways-format
как специальный
content type, Вы можете специфицировать его так:
deleteResponseHeader("content-type");
addResponseHeader("content-type","royalairways-format");
Вы можете использовать функцию addResponseHeader
для добавления в
шапку ответа любой другой нужной информации.
Помните, что header отсылается с первой частью ответа/response. Следовательно, Вы должны вызывать эти функции раньше в скрипте на каждой странице. В общем, Вы должны быть уверены, что header ответа установлен до любого из следующих событий:
flush
для очистки буфера вывода.redirect
для изменения клиентских запросов.Дополнительно см. разделы "Очистка Буфера Вывода" и "Процессинг Времени Выполнения на Сервере".
1 http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1341.txt
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
Глава 4. Приложения-Образцы: Быстрый Старт
В главе рассматривается пример приложения серверного JavaScript, поставляемого вместе с Netscape web-серверами. Это введение в использование серверного JavaScript при помощи двух простых приложений-образцов.
Глава 5. Основы Серверного JavaScript
В этой главе даны основы серверного JavaScript, серверной функциональности и различий между клиентским и серверным JavaScript. Описано, как внедрить серверный JavaScript в HTML-файлы. Рассмотрено, что происходит во время прогона программы на клиенте и на сервере и как использовать JavaScript для изменения HTML-страницы, отправляемой клиенту, и, наконец, как совместно использовать информацию в клиентских и серверных процессах.
Глава 6. Служба Session Management Service
В этой главе рассмотрены объекты службы Session Management Service, введённые в серверном JavaScript для обеспечения возможности совместного использования данных несколькими клиентскими запросами к приложению, несколькими пользователями к одному приложению или даже несколькими приложениями на сервере.
Глава 7. Другая Функциональность JavaScript
В этой главе описана дополнительная функциональность серверного JavaScript, которую вы можете использовать для отправки сообщений e-mail из Вашего приложения, доступа к файловой системе сервера, подключения в приложение внешних библиотек или прямого манипулирования клиентскими запросами и ответами клиенту.
Дата последнего обновления: 29 сентября 1999 г.
╘ Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассматривается использование технологии LiveConnect для обеспечения возможности взаимодействия кодов, написанных на Java и JavaScript. Предполагается, что Вы уже знакомы с программированием на Java.
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
Глава 8. Соединение с Базой Данных
В главе обсуждается, как использовать LiveWire Database Service для соединения приложения с реляционными БД DB2, Informix, ODBC, Oracle и Sybase и как выбрать наилучшую методологию соединения с БД.
Глава 9. Работа с Базой Данных
В главе обсуждается работа с реляционными БД DB2, Informix, ODBC, Oracle и Sybase, как запросить информацию из БД и использовать её в Вашем приложении, как работать с транзакциями БД и как выполнять хранимые процедуры.
Глава 10. Конфигурирование Базы Данных
В главе описано, как настроить Вашу БД для работы с LiveWire Database Service. Вы должны прочесть эту главу и "Информацию Конфигурации", прежде чем начать использовать LiveWire с Вашими приложениями JavaScript.
Глава 11. Конвертация Типов Данных
В главе описано, как машина выполнения JavaScript на сервере конвертирует сложные типы данных из реляционных БД и простые типы JavaScript.
Глава 12. Обработка Ошибок LiveWire
В этой главе рассмотрены типы ошибок, которые могут возникнуть при работе с реляционными БД.
Глава 13. Приложения-Образцы Videoapp и Oldvideo
В главе описано приложение-образец videoapp, иллюстрирующее использование службы LiveWire Database Service. Показано, как конфигурировать окружение для запуска приложений videoapp и oldvideo.
Дата последнего обновления: 29 сентября 1999 г.
╘ Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой книге рассматривается создание приложений, написанных на языке "Серверный/Server-Side JavaScript" (SSJS). JavaScript это разработанный корпорацией Netscape платформонезависимый объектно-ориентированный язык скриптинга (сценариев) для клиентских и серверных приложений.
В данной главе имеются следующие разделы:
В релизах 3.x и 4.x версий сервера Netscape Enterprise Server служба LiveWire полностью интегрирована с сервером. Поскольку LiveWire database connectivity теперь интегрирована как LiveWire Database Service - часть серверного JavaScript, разработчикам не нужно устанавливать LiveWire как отдельный продукт. Просто включите поддержку JavaScript в Administration Server, чтобы сделать доступными необходимые компоненты.
ПРИМЕЧАНИЕ:
Отличие между SSJS в Netscape Enterprise Server (NES) 3.x и в NES 4.x состоит в том, что NES 4.x использует теперь JavaScript Runtime от Mozilla.org, которая поддерживает JavaScript 1.4. Предыдущие версии SSJS поддерживали JavaScript 1.0, JavaScript 1.1 и JavaScript 1.2.
В JavaScript версии 1.4 появились новые возможности и различные усовершенствования, которые обсуждаются в онлайновом учебнике Core JavaScript Reference v1.4:
http://developer.netscape.com/docs/manuals/js/core/jsref/index.htm
Также можно воспользоваться моим переводом на русский язык новой версии (Ядро JavaScript 1.5) этой книги .
В следующем списке резюмируются эти новые возможности:
throw
и
try...catch
.in
и instanceof
.in
возвращает true
,
если специфицированное свойство имеется в специфицированном объекте. Оператор instanceof
возвращает true
, если специфицированный объект имеет
специфицированный тип.java.lang.Object
наследуются в
JavaArray
. Дополнительно метод JavaArrary.toString
теперь вызывает метод java.lang.Object.toString
.JavaClass
в Java-метод, который требует
аргумента типа java.lang.Class
, вместо создания оболочки вокруг
экземпляра класса
java.lang.Class
.JSException
с детальным
сообщением.netscape.javascript.JSException
, поддерживавшего эту возможность,
не рекомендуются/deprecated.==
для сравнения двух экземпляров
JSObject
. Используйте JSObject.equals
.eval
:eval
не может вызываться неявно.
В предыдущих версиях лишь рекомендовалось, чтобы этот метод не вызывался неявно; начиная с
JavaScript 1.4, неявный вызов eval
может привести к ошибке времени
выполнения. Это изменение повысит производительность.eval
больше не доступен как метод класса Object
;
вместо него используйте функцию верхнего уровня eval
.arguments
; массив arguments
это переменная, он больше
не является свойством Function
-объектов. Это изменение улучшит
производительность.Function.arity
. Оно заменено свойством Function.length
.Функциональность менеджера JavaScript Application Manager не изменилась в Enterprise Server 4.0 по сравнению с Enterprise Server 3.x. Однако он теперь использует ту же новую схему цветов, что и Server Manager в Enteprise Server 4.0, и имеет некоторые изменения внешнего вида.
JavaScript Application Manager теперь имеет три вкладки:
Хотя названия и внешний вид вкладок и кнопок в JavaScript Application Manager в Enterprise Server 4.0 слегка отличается от Enterprise Server 3.6, их функциональность не изменилась.
Предполагается, что Вы уже имеете следующие базовые знания:
Каждая версия Netscape Enterprise Server поддерживает свою версию JavaScript. Чтобы помочь Вам при создании скриптов, совместимых с несколькими версиями Enterprise Server, в этом учебнике используются сокращённые обозначения версий сервера, в которых каждая возможность реализована.
Версия JavaScript | Версии Enterprise Server |
---|---|
Поскольку JavaScript может применяться на разных уровнях, документация по нему разделена на несколько книг. Набор онлайновых книг по JavaScript включает:
Документацию по JavaScript, состоящую из следующих книг:
Если Вы новичок в JavaScript, начните с Главы 1, "JavaScript. Обзор.". После того как Вы будете иметь твёрдое понимание основ, можете воспользоваться книгой Серверный JavaScript, Справочник. для получения долее детальной информации об объектах и операторах.
Используйте материал этой книги для знакомства с ядром и серверным JavaScript. Пользуйтесь книгами Клиентский JavaScript. Руководство. и Клиентский JavaScript. Справочник. для получения информации о создании сценариев на HTML-страницах.
Enterprise Server 4.x Release Notes содержат информацию о Enterprise Server 4.x, в том числе - касающуюся серверного JavaScript для Enterprise Server 4.x.
Книга Netscape Enterprise Server Programmer's Guide содержит резюме по разным интерфейсам программирования, доступным в версиях 4.x Netscape web-серверов. Используйте это учебник в качестве карты или отправной точки при изучении документации Enterprise Server для разработчиков.
Помимо этого, другие книги Netscape обсуждают отдельные аспекты JavaScript, особенно относящиеся к их области действия. Эти книги упоминаются в тексте данной книги там, где это возможно.
Сайт Netscape содержит большое количество информации, которая может понадобиться при создании приложений JavaScript. Вот некоторые URL, представляющие особый интерес:
http://help.netscape.com/products/tools/livewire
http://developer.netscape.com/one/javascript/ssjs/index.html
http://developer.netscape.com/news/viewsource/index.html
Если у вас имеется установленная версия предыдущего релиза Netscape web-сервера, Вы должны перенести установки сервера при инсталяции Enterprise Server 4.x. См. Enterprise Server 4.0 Installation and Migration Guide.
Если Вы ранее создавали приложения JavaScript, используя Серверный JavaScript 3.x, Вы должны знать об изменениях, выполняемых при обновлении до версии 4.x и переносе старых установок сервера:
Вы также должны знать об изменениях в поведении приложений серверного JavaScript по сравнению с Netscape Enterprise Server 3.x:
Приложения JavaScript работают на многих операционных системах; данная информация применима ко всем версиям. Пути файлов и директорий даны в формате Windows (с обратной наклонной чертой в качестве разделителя имён директорий). Для Unix-версий пути директорий - те же, за исключением того, что в качестве разделителей используются слэши вместо backslash.
В этой книге используются uniform resource locators (URLs) в форме:
http://server.domain
/path
/file
.html
В этих URL server это имя сервера, на котором запускается Ваше приложение,
как, например, research1
или www
; domain это
имя домена Internet, такое как netscape.com
или uiuc.edu
; path это
структура директорий на сервере; а
file
.html
это имя файла. В целом элементы, выделенные italic в URL, являются
заглушками, а элементы с нормальным моноширинным шрифтом являются литералами.
Если на Вашем сервере имеется Secure Sockets Layer (SSL), Вы должны использовать https
вместо http
в URL.
В книге использованы следующие соглашения по шрифтам:
Моноширинный шрифт
используется в
примерах кода, API и элементах языка (таких как имена функций и классов),
именах файлов, путях, именах директорий, тэгах HTML и в любом тексте, который должен выводиться на экран монитора.
(
Моноширинный italic
используется для заглушек,
вставленных в код.)Дата последнего обновления: 29 сентября 1999 г.
╘ Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассматриваются образцы приложений серверного JavaScript, поставляемые вместе с Netscape web-сервером. Это начало работы с JavaScript с использованием двух простейших приложений.
В данной главе имеются следующие разделы:
Если вы устанавливаете Netscape web-сервер, несколько примеров приложений JavaScript
устанавливаются одновременно.
Для получения представления о возможностях приложений JavaScript запустите их и
просмотрите исходный код. Вы можете также изменять эти приложения по мере
изучения возможностей JavaScript. Исходные и исполняемые файлы этих приложений
установлены в директории
$NSHOME\js\samples
, где $NSHOME
это директория, в
которой Вы установили сервер.
В таблице перечислены приложения-образцы.
1
Эти приложения работают только при наличии поддерживаемого
сервера БД, установленного в сети, и корректно сконфигурированного клиентского
программного обеспечения. См. Главу 10, "Конфигурирование
Базы Данных". Эти приложения обсуждаются в
Главе 13, "Приложения-Образцы Videoapp и Oldvideo". Перед началом
использования |
ПРИМЕЧАНИЕ:
Помимо указанных, в директории
$NSHOME\js\samples
имеется также приложениеmetadata
. Оно используется Visual JavaScript. Когда Вы будете просматривать исходный код, не изменяйте исполняемый файл.
Остаток этой главы посвящён двум простым приложениям, которые дадут Вам возможность поработать с приложениями JavaScript. Не беспокойтесь о деталях. Это обсуждение предназначается только для того, чтобы показать Вам возможности приложений JavaScript. Детали обсуждаются далее в этой главе.
В
Главе 13, "Приложения-Образцы Videoapp и Oldvideo",
приложение videoapp
рассматривается детально. Вы должны прочесть
эту главу перед началом работы с LiveWire Database Service.
В этом разделе Вы запустите приложение Hello World, пример простейшего приложения, и получите представление о следующих процедурах:
Чтобы начать работу с образцами приложений, Вам необходим доступ к JavaScript Application Manager. Вы можете получить этот доступ, введя следующий URL в Navigator:
http://
server.domain
/appmgr
В этом и других URL в данном учебнике server это имя сервера, на котором
Вы запускаете Ваше приложение, как, например research1
или www
, domain
это имя домена Internet, такое как netscape.com
или uiuc.edu
.
Если Ваш сервер имеет Secure Sockets Layer (SSL), используйте https
вместо
http
в URL.
В Application Manager'е выберите world
в
левом фрэйме и щёлкните кнопку Run.
Вы можете также ввести URL приложения в поле Location Navigator'а:
http://
server.domain
/world
Application Manager выведет страницу, показанную на Рисунке 4.1.
Об Application Manager см. "Технология Разработки Приложений JavaScript".
Это приложение демонстрирует две важные вещи: обслуживание различных клиентских статусов нескольких клиентов и обслуживание существующего статуса приложения. Оно выполняет следующие функции:
При первом посещении страницы пользователем значения обоих имён не определены. Количество предыдущих посещений пользователя равно 0; общее количество посещений также равно 0.
Введите своё имя и нажмите Enter. Страница теперь показывает введённое имя и текст "This time you are." Оба числа посещений увеличиваются на 1. Эта акция иллюстрирует простейшую обработку данных формы. Введите другое имя и нажмите Enter. Страница теперь показывает новое имя и текст "This time you are", а предыдущее имя с - текстом "Last time you were." И вновь оба числа посещений увеличиваются.
Если Вы выполните доступ к приложению из другого экземпляра Navigator'а (или с другого компьютера), страница покажет общее число посещений и количество посещений каждого экземпляра Navigator'а, а не только одного данного экземпляра.
Теперь посмотрим на исходный код JavaScript для данного приложения. Используйте
для этого ваш привычный текстовый редактор,
откройте файл $NSHOME\js\samples\world\hello.html
, где $NSHOME
это директория, в которой установлен ваш Netscape-сервер. Файл начинается
обычным HTML:
<html>
<head>
<title> Hello World </title> </head>
<body>
<h1> Hello World </h1>
<p>Your IP address is <server>write(request.ip);</server>
Тэги SERVER
в нижней строчке содержат код JavaScript, который
выполняется на сервере.
В данном случае оператор write(request.ip)
выводит свойство ip
объекта request
(IP-адрес клиента, выполнившего доступ к странице).
Функция
write
очень важна в приложениях JavaScript, поскольку используется
для дополнения значений выражений JavaScript в HTML-страницу, отправляемую
клиенту.
Объект request
является частью службы JavaScript Session Management Service.
Полное его описание см. в Главе 6, "Session Management Service".
Функция
write
это одна из функций JavaScript, которые не ассоциированы со
специфическим объектом. О функции write
см. раздел
"Конструирование HTML-Страницы".
Далее идут операторы, пока не представляющие для нас интереса. Затем - оператор:
<server>
client.oldname = request.newname;</server>
Этот оператор присваивает значение свойства newname
объекта request
свойству oldname
объекта client
. Объект
client
также является частью JavaScript Session Management Service.
Его полное описание см. в Главе 6, "Session Management Service".
Пока просто отметьте, что client
может содержать информацию о
приложении, специфичную для определённого браузера, запускающего это приложение.
Значение свойства request.newname
устанавливается, когда пользователь вводит значение в форме. Далее в файле можно
найти такие операторы:
<form method="post" action="hello.html">
<input type="text" name="newname" size="20">
Значением атрибута ACTION
формы является hello.html
(имя
текущего файла).
Это означает, что, когда пользователь отправляет форму, щёлкая кнопку Enter или
нажимая клавишу Enter, Navigator перезагружает текущую страницу. Вообще, ACTION
может быть любая страница приложения JavaScript.
Значением атрибута NAME
текстового поля
является
newname
. Когда страница отсылается, этот оператор присваивает то,
что пользователь ввёл в текстовом поле, свойству newname
объекта request
,
к которому в JavaScript можно обратиться request.newname.
Значения
элементов формы всегда соответствуют свойствам объекта request
.
Свойства объекта request
сохраняются только в течение одного
(текущего) клиентского запроса.
Несколькими строками ниже находится другой тэг
SERVER
, указывающий, что следующие строки являются операторами
серверного JavaScript.
Вот первая группа операторов:
if (client.number == null)
client.number = 0
else
client.number = 1 + parseInt (client.number, 10)
Данный условный оператор проверяет, инициализировано ли свойство number
объекта client
. Если нет, код инициализирует его в 0; иначе
number
увеличивается на 1 с использованием функции JavaScript parseInt
,
которая конвертирует строковое значение в число. Поскольку предопределённый
объект client
конвертирует все значения свойств в строки, Вы
обязаны использовать parseInt
или parseFloat
для
конвертации этих значений в числа.
Так как number
это свойство объекта client
,
оно отличается для каждого клиента, выполняющего доступ к приложению. Это
значение указывает количество раз, когда "you have been here/Вы здесь были."
Для отслеживания общего количества посещений Вы используете объект project
,
поскольку он совместно используется всеми клиентами, выполняющими доступ к
приложению. Свойства объекта project
существуют, пока приложение не
будет остановлено. Следующая группа операторов отслеживает посещения:
project.lock()
if (project.number == null)
project.number = 0
else
project.number = 1 + project.number
project.unlock()
Первый оператор использует метод lock
объекта project
. Это даёт клиенту временный исключительный доступ к
объекту
project
. Оператор if
проверяет, было ли определено
свойство project.number
. Если нет, код инициализирует его в 0;
иначе код увеличивает его на 1.
Наконец, метод unlock
освобождает объект project
, и
другие клиенты могут получить к нему доступ.
Последние операторы файла выводят значения свойств
client.number
и project.number
.
<p>You have been here <server>write(client.number);</server> times.
<br>This page has been accessed <server>write(project.number);
</server> times.
В этом разделе Вы модифицируете, рекомпилируете и
рестартуете приложение-образец. Для редактирования исходного файла Вы сначала
должны найти его. Как Вы помните, Application
Manager показывает путь к исполняемому файлу приложения (файлу с расширением .web
).
Исходный файл hello.html
должен находиться в той же директории.
Отредактируйте файл. HTML-файл начинается такими операторами:
<html>
<head> <title> Hello World </title> </head>
<body>
<h1> Hello World </h1>
<p>Your IP address is <server>write(request.ip);</server>
<server>
write ("<P>Last time you were " + client.oldname + ".");
</server>
<p>This time you are <server>write(request.newname);</server>
<server>client.oldname = request.newname; </server>
Добавьте строку, выводящую тип пользовательского браузера:
<p>You are using <server>write(request.agent)</server>
Если Вам нужно, можете также персонализировать шапку/head страницы; например, можете сделать такой заголовок: "Fred's Hello World."
Когда вы закончите изменение файла, запустите компилятор приложений JavaScript. В командной строке перейдите в директорию, содержащую исходный файл. Введите такую строку для компиляции приложения:
jsac -v -o hello.web hello.html
Альтернативно Вы можете из этой директории запустить скрипт build
(в Unix)
или
build.bat
(в NT). В этом случае компилятор стартует и выводит
сообщения.
Последнее сообщение должно быть "Compiled and linked successfully/Откомпилировано
и скомпоновано успешно".
Опубликуйте файлы на Вашем сервере разработки. Для рестарта войдите в Application Manager, выберите Hello World, затем выберите Restart. Эта операция загрузит на сервер новую откомпилированную версию приложения. Затем Вы можете запустить приложение, выбрав Run. Открывается окно с Hello World. Вы увидите внесённые Вами изменения в приложении.
В этом разделе Вы запустите и модифицируете приложение-образец Hangman и получите представление о том, как:
Hangman это классическая игра, в которой игроки пытаются угадать слово. Неизвестные буквы отображаются на экране звёздочками; звёздочка заменяется на букву, если игрок угадывает её. Если введённая буква некорректна, прорисовывается часть повешенного. Игра показывает также некорректные буквы, введённые Вами.
Если повешенный прорисуется полностью, игрок проиграл. Игрок выигрывает, угадав все буквы слова до окончания прорисовки повешенного. В этой упрощённой версии игры есть только три слова для угадывания. После окончания игры можно снова начать её снова (и использовать следующее слово) или закончить игру.
Запустите приложение Hangman, выбрав Hangman в Application Manager и щёлкнув Run. Можно также загрузить приложение в Navigator:
http://
server.domain
/hangman
В ответ Application Manager выведет страницу, показанную на следующем рисунке.
Поиграйте в игру, чтобы получить представление о работе программы.
В следующей таблице перечислены исходные файлы приложения Hangman.
Основная логика приложения содержится в файле hangman.html
. Базовая
логика проста, как три рубля:
Тело/body HTML-файла hangman.html
начинается кодом JavaScript в
тэге
SERVER
. Сначала идёт код для инициализации новой игры:
if (client.gameno == null) {
client.gameno = 1;
client.newgame = "true";
}
if (client.newgame == "true") {
if (client.gameno % 3 == 1)
client.word = "LIVEWIRE";
if (client.gameno % 3 == 2)
client.word = "NETSCAPE";
if (client.gameno % 3 == 0)
client.word = "JAVASCRIPT";
client.answer = InitAnswer(client.word);
client.used = "";
client.num_misses = 0;
}
client.newgame = "false";
Этот код использует объект client
для сохранения информации об
игроке. Поскольку нет необходимости сохранять статус игры между вызовами разных
клиентов, этот код не использует объекты project
или server
.
Первый оператор определяет, играл игрок до этого, или нет,
проверяя существование client.gameno
; если нет, код
инициализируется в 1 и client.newgame
устанавливается в true
.
Затем некоторая простая логика присваивает
"секретное слово" свойству client.word;
имеются только три
секретных слов, по которым игра проходит по циклу. Свойство client.gameno
хранит количество игр, сыгранных конкретным пользователем. Финальная часть
инициализации использует InitAnswer
, функцию, определённую в файле hangman.js
,
для инициализации свойства client.answer
строкой звёздочек.
Затем идёт блок операторов обработки действий игрока по угадыванию:
if (request.guess != null) {
request.guess = request.guess.toUpperCase().substring(0,1);
client.used = client.used + request.guess + " ";
request.old_answer = client.answer;
client.answer = Substitute (request.guess, client.word,
client.answer);
if (request.old_answer == client.answer)
client.num_misses = parseInt(client.num_misses) + 1;
}
Оператор if
определяет, угадал ли игрок (введя букву в форму). Если
да, код вызывает Substitute
(другую функцию, определённую в файле
hangman.js
) для замены угаданной буквы в client.answer
.
Это постепенно прорисовывает слово в client.answer
(например, "N*T**AP*").
Второй оператор if
проверяет, было ли изменено свойство client.answer
после последнего угадывания; если нет, код увеличивает client.num_misses
для отслеживания количества некорректных ответов. Вы обязаны всегда использовать parseInt
при работе с целочисленными значениями свойств предопределённого объекта client
.
Как видно из следующего кода, последний оператор if
в коде JavaScript
проверяет, победил игрок или проиграл, и перенаправляет клиента соответственно.
Функция redirect
открывает специфицированный HTML-файл и передаёт
управление в него.
if (client.answer == client.word)
redirect(addClient("youwon.html"));
else if (client.num_misses > 6)
redirect(addClient("youlost.html"));
Это конец начального тэга SERVER
. Начинается HTML,
дополненный выражениями JavaScript. Человек прорисовывается с использованием
закавыченных выражений JavaScript внутри HTML-тэга IMG
:
<IMG SRC=`"images\hang" + client.num_misses + ".gif"`>
Всё выражение между двумя обратными кавычками (`
) это строка JavaScript.
Она состоит из строкового литерала "images\hang
", соединяемого со
значением
client.num_misses
(которое является целым числом, но хранится как
строка), соединяемого со строковым литералом ".gif"
. Имеются шесть GIF-файлов
с изображениями повешенного человечка в различных стадиях: image0.gif
, image1.gif
и так далее. Выражение JavaScript
в кавычках генерирует HTML вида:
<IMG SRC="images\hang0.gif">
<PRE><SERVER>write(client.answer)</SERVER></PRE>
You have used the following letters so far:
<SERVER>write(client.used)</SERVER>
Они выводят значение client.answer
(слово,
содержащее все правильно угаданные буквы) и все угаданные буквы.
Оставшаяся часть файла это стандартный HTML. Важно отметить, что атрибут
ACTION
тэга FORM
специфицирует файл hangman.html
как URL, по которому отправляется форма. Это значит, что при отправке формы
страница перезагружается с новыми (специфицированными) значениями формы.
Посмотрим на hangman.js
, пример исходного
файла, написанного с использование только серверного JavaScript. В нём
определены две функции, InitAnswer
и
Substitute
, используемые приложением. Заметьте, что в файлах с
серверным JavaScript тэги SERVER
не используются.
Вы можете поэкспериментировать с JavaScript, чтобы получить представление о разработке приложений. Одна из важнейших задач - отладка. Выберите в Application Manager приложение Hangman и Debug/Отладить. Application Manager открывает окно с приложением в одном фрэйме и с отладочной информацией - в другом, слева, как показано на Рисунке 4.3.
Заметьте, что URL будет теперь
http://
server.domain
/appmgr/debug.html?name=hangman
Вы можете сделать закладку на этот URL для удобства работы с Hangman. После этого не нужно будет выходить в Application Manager.
Попытайтесь добавить в Hangman функцию, проверяющую,
является ли введённый пользователем символ буквой
(а не числом или знаком пунктуации). Можете использовать функцию InitAnswer
из hangman.js
для старта. После компиляции и рестарта приложения
используйте закладку для запуска приложения в режиме отладки.
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассматриваются объекты службы Session Management Service, доступные в серверном JavaScript и предназначенные для обеспечения совместного использования данных несколькими клиентскими запросами к приложению, несколькими пользователями одного приложения или даже несколькими приложениями на сервере.
Session Management Service это набор возможностей для
управления созданием и уничтожением различных предопределённых объектов в ходе
сессии сервера. Эти возможности предоставлены через объекты request
, client
,
project
и server
.
Помимо этого Вы можете конструировать экземпляры Lock
для
управления доступом к совместно используемой информации. Lock
-экземпляры дают возможность точно управлять использованием информации,
устанавливая исключительный доступ к специфицированным объектам.
В главе имеются следующие разделы:
Предопределённые объекты request
, client
, project
и server
содержат данные, существующие в течение различных периодов
времени и доступные различным клиентам и приложениям. Имеется единственный
объект server
, используемый всеми запущенными на сервере приложениями.
Имеется отдельный объект project
для каждого запущенного приложения.
Имеется один объект client
для каждого браузера (клиент),
выполняющего доступ к данному отдельному приложению.
Наконец, имеется отдельный объект request
для каждого клиентского
запроса с определённого клиента к определённому приложению. На
Рисунке 6.1 показаны возможности относительной
доступности различных объектов.
Машина выполнения JavaScript на сервере конструирует объекты обслуживания сессии в разные моменты времени. Эти объекты применяются для хранения различных данных. Вы можете определить специфические для приложения свойства для любого из этих объектов.
request
request
имеет предопределённые свойства, к
которым Вы можете получить доступ.request
считается объектом только для
чтения. Машина выполнения сохраняет текущие значения всех элементов формы как
свойства объекта request
.
Вы можете использовать его для хранения, информации, специфичной для
отдельного запроса, но для этого более эффективным будет использование переменных JavaScript.Машина выполнения конструирует объект
request
каждый раз, когда
сервер отвечает на клиентский запрос из web-браузера. Он уничтожает этот
объект в конце выполнения клиентского запроса. Машина выполнения не
сохраняет данные объекта request
. client
client
для каждой пары клиент/приложение. Все запросы от одного клиента к одному и
тому же приложению пользуются одним и тем же объектом client
.
Объект client
не имеет предопределённых свойств.client
для тех данных, которые должны
использоваться несколькими запросами от того же самого клиента (пользователя),
но они не должны использоваться разными клиентами приложения. Например,
Вы можете сохранять пользовательский ID потребителя как свойство объекта client
.client
для
каждого клиентского запроса, но свойства сохраняются в течение времени
существования клиентского соединения с приложением.
Следовательно, хотя физически объект client
существует только для одного клиентского запроса,
концептуально Вы можете представлять, что он конструируется при первом
соединении клиента с приложением и не разрушается, пока клиент не прервёт свой
доступ к приложению. Есть несколько подходов к работе со свойствами объекта client
при множественных запросах.
См. дополнительно раздел "Технология Работы с Объектом client".client
,
когда клиент заканчивает использование приложения. На практике машине JavaScript
непросто определить, когда объект client
и его свойства
должны быть уничтожены. О том, как выполняется это определение, см. раздел "Жизненный
Цикл Объекта client". См. также "Объект client". project
project
. Объект project
не имеет предопределённых свойств.project
для совместного использования
данных несколькими клиентами, работающими с одним приложением. Например, Вы
можете хранить следующий доступный ID потребителя как свойство объекта
project
. Когда объект project
применяется для
совместного использования данных, Вы должны быть внимательны относительно
обеспечения одновременного доступа к этим данным; см.
"Безопасное Совместное Использование Объектов с Помощью
Блокировки". Из-за ограничений, действующих для свойств объекта
client
, Вам иногда придётся использовать объект project
для хранения данных отдельного клиента.project
при старте приложения в Application Manager или при старте
сервера. Она разрушает объект при остановке приложения или сервера. server
server
. Объект server
имеет
предопределённые свойства.server
для совместного использования данных
несколькими приложениями сервера. Например,
Вы можете использовать объект server
для отслеживания
использования всех приложений сервера.
Когда объект server
применяется для совместного использования
данных, Вы должны побеспокоиться об обеспечении одновременного доступа к этим
данным; см. "Безопасное Совместное Использование Объектов с
Помощью Блокировки".server
при старте сервера и
разрушает его при остановке сервера.Рисунок 6.2 может помочь представить, как все эти объекты соответствуют URL страницы Вашего приложения.
На этом рисунке Joe запрашивает URL http://www.royalairways.com/videoapp/category.html
,
соответствующий странице приложения videoapp
. Когда машина
выполнения получает запрос, она использует уже существующий объект server
,
соответствующий www.royalairways.com
и уже существующий объект project
,
соответствующий приложению videoapp
. Машина создаёт объект client
,
соответствующий комбинации Joe и приложения videoapp
. Если Joe уже
получал доступ к другим страницам этого приложения, новый объект client
использует любые сохранённые свойства. Наконец, машина создаёт новый объект request
для конкретного запроса на страницу category.html
.
Объект request
содержит данные, специфичные для текущего
клиентского запроса. Они имеет самое короткое время существования из всех
объектов. JavaScript конструирует новый объект request
для каждого
получаемого клиентского запроса; например, объект создаётся, когда
document.location
или переходит к
странице, используя метод history
.redirect
.
Машина выполнения JavaScript на сервере уничтожает объект request
по окончании ответа на запрос (обычно предоставляя запрошенную страницу).
Следовательно, типичный период существования объекта request
- менее одной секунды.
ПРИМЕЧАНИЕ:
Вы не можете использовать объект
request
в начальной странице приложения. Эта страница запускается, когда приложение стартует на сервере. В этой точке нет объекта клиентского запроса, и поэтому нет доступного объектаrequest
. О начальных страницах см. раздел "Установка Нового Приложения".
Резюме по объекту request
см. а разделе
"Предопределённые Объекты. Обзор.".
В таблице перечислены предопределённые свойства объекта
request
. Некоторые из них соответствуют переменным окружения CGI.
Вы можете получить доступ к этим и другим переменным окружения CGI через
использование функции ssjs_getCGIVariable
, описанной в разделе
"Доступ к Переменным CGI".
request
Свойство | Описание | Пример значения |
---|---|---|
agent |
Имя и версия клиентского программного обеспечения. Используйте это свойство для адресного применения расширенных возможностей некоторых браузеров. |
Mozilla/1.1N (Windows; I; 32bit) |
auth_type |
Тип авторизации, если запрос защищён авторизацией какого-либо типа. Netscape web-серверы
поддерживают базовую авторизацию доступа по HTTP. Соответствует переменной
окружения CGI |
basic |
auth_user |
Имя локального HTTP-пользователя web-браузера, если
авторизация доступа HTTP была активирована для данного URL. Заметьте, что это не
способ определения имени пользователя, получающего доступ к Вашей программе.
Соответствует переменной окружения CGI |
vpg |
ip |
IP-адрес клиента. Может использоваться для авторизации или отслеживания доступа. |
198.95.251.30 |
method |
HTTP-метод, ассоциированный с запросом. Приложение
может использовать его для определения соответствующего ответа на запрос.
Соответствует переменной окружения CGI |
GET 1 |
protocol |
Уровень протокола HTTP, поддерживаемый клиентским программным обеспечением.
Соответствует переменной окружения CGI
|
HTTP/1.0 |
query |
Информация из запрашивающей HTML-страницы; это информация в URL, идущая после знака "?".
Соответствует переменной окружения CGI |
button1=on&button2=off |
imageX |
Горизонтальная позиция курсора, когда пользователь щёлкает на карте изображений/image map. Описано в разделе "Работа с Картами Изображений". |
45 |
imageY |
Вертикальная позиция курсора, когда пользователь щёлкает на карте изображений. Описано в разделе "Работа с Картами Изображений". |
132 |
uri |
Частичный URL запроса: с вырезанными протоколом, именем хоста и, возможно имеющимся, номером порта. |
videoapp/add.html |
1
Для HTTP 1.0 method
имеет одно из значений: GET
, POST
или HEAD
.
Если Вы объявляете переменные верхнего уровня в серверном JavaScript,
они имеют тот же период существования, что и свойства объекта request
.
Например, такое объявление существует только на протяжении текущего запроса:
var number = 42;
Помимо предопределённых свойств, Вы можете иметь в клиентском коде информацию,
которая будет становиться свойствами объекта request
. Это делается
через использование элементов формы и кодирование свойств в URL запроса, как
описано в разделе "Отправка Значений с Клиента на Сервер".
Хотя Вы можете создавать дополнительные свойства объекта
request
непосредственно операторами серверного JavaScript,
производительность будет выше, если использовать переменные JavaScript.
Создаваемые Вами свойства объекта request
могут быть любого
допустимого в JavaScript типа, включая ссылки на другие объекты JavaScript.
Помните, что период существования объекта request
и, следовательно,
его свойств, это период действия запроса. Если Вы сохраняете ссылку на другой
объект в объекте request
, то объект, на который ссылаются,
уничтожается вместе с объектом request
, если только объект, на
который ссылаются, не имеет на себя другой действующей ссылки, прямой или косвенной, с объекта
server
или project
.
Атрибут ISMAP
тэга IMG
указывает на серверную карту
изображений. Если пользователь щёлкает на карте, горизонтальная и вертикальная
позиции курсора высылаются на сервер.
Свойства imageX
и imageY
возвращают эти координаты.
Рассмотрим такой HTML:
<A HREF="mapchoice.html">
<IMG SRC="images\map.gif" HEIGHT=599 WIDTH=424 BORDER=0
ISMAP ALT="SANTA CRUZ COUNTY">
</A>
Страница mapchoice.html
получает свойства request.imageX
и request.imageY
на основе позиции курсора в момент щелчка мышью.
Несколько браузеров-клиентов могут иметь одновременный доступ к приложению JavaScript.
Объект client
предоставляет метод для работы отдельно с каждым
клиентом. Он также имеет технологию для отслеживания работы каждого
браузера-клиента с приложением при наличии нескольких запросов.
Машина выполнения JavaScript на сервере конструирует
объект client
для каждой пары клиент/приложение. Браузер-клиент,
соединённый с одним приложением, имеет другой объект
client
из того же самого браузера-клиента, соединённого с другим
приложением. Машина выполнения конструирует новый объект client
каждый раз, когда пользователь выполняет доступ к приложению; могут быть сотни и
тысячи объектов client
, активных одновременно.
ПРИМЕЧАНИЕ:
Вы не можете использовать объект
client
на начальной странице Вашего приложения. Эта страница начинает работу при старте приложения на сервере. В этот момент клиентского запроса нет, и поэтому нет также и доступного объектаclient
. Дополнительно см. раздел "Установка Нового Приложения".
Машина выполнения конструирует и уничтожает объект client
для каждого клиентского запроса. В то же время, при обработке запроса машина
выполнения сохраняет имена и значения свойств объекта client
. Таким
способом машина выполнения может конструировать новый объект
client
из сохранённых данных, если тот же самый пользователь вновь
использует приложение, сделав следующий запрос. Таким образом, концептуально Вы
можете представлять объект client
как объект, действующий в течение
сессии работы клиента с приложением.
JavaScript не сохраняет объекты client
, не имеющие значений свойств.
Поэтому, если приложению не нужны объекты client
и оно не
присваивает свойствам объекта
client
никаких значений, оно не выполняет никакой лишней работы.
У Вас имеются несколько различных опций: как и где машине
выполнения сохранять свойства объекта
client
. Эти опции обсуждаются в разделе "Технология
Обслуживания Объекта client".
Резюме по объекту client
см. в разделе "Предопределённые
Объекты. Обзор".
В объекте client
отсутствуют значения предопределённых свойств,
поскольку он предназначен для хранения специфических для приложения данных.
Операторы JavaScript могут присваивать специфичные для приложения свойства и
значения объекту client
. Хорошим примером свойства объекта client
является ID-номер потребителя. Когда пользователь в первый раз вызывает
приложение, оно обязано присвоить
customer ID, как в следующем примере:
client.custID = getNextCustID();
Здесь определяемая в приложении функция getNextCustID
используется
для вычисления customer ID.
Машина выполнения затем присваивает этот ID свойству custID
объекта client
.
После установки customer ID может оказаться неудобным
требовать от пользователя ввода ID на каждой странице приложения.
Однако без использования объекта client
нет иной возможности
ассоциировать корректный customer ID с последующими запросами клиента.
Из-за использования такой техники для обслуживания свойств
объекта client
при наличии нескольких клиентских запросов имеется
одно важное ограничение для значений свойств объекта client
. Машина выполнения
JavaScript не сервере конвертирует значения всех свойств объекта client
в строки.
Не присваивайте объект в качестве значения свойства объекта
client
. Если Вы это сделаете, машина выполнения конвертирует этот
объект в строку; после этого Вы не сможете больше работать с этим объектом. Если значение
клиентского свойства представляет другой тип данных, например, number, Вы
обязаны конвертировать строковое значение перед тем как его использовать.
Например, Вы можете создать целочисленное свойство объекта client
:
client.totalNumber = 17;
Затем Вы можете использовать parseInt
для инкремента значения totalNumber
:
client.totalNumber = parseInt(client.totalNumber) + 1;
Аналогично Вы можете создать Булево свойство объекта client
:
client.bool = true;
if (client.bool == "true")
write("It's true!");
else
write("It's false!");
Заметьте, что условное выражения сравнивает client.bool
со строкой "true"
.
Можно использовать эту технику для обработки Булева выражения. Например, для
отрицания Булева свойства используйте такой код:
client.bool = (client.bool == "true") ? false : true;
Хотя Вы можете работать непосредственно со свойствами объекта client
,
Вы выполняете таким образом лишнюю работу. Если Вы повторно используете значение
свойства объекта client
, предусмотрите использование переменных
верхнего уровня JavaScript.
Перед использованием свойства объекта client
, присвойте его
переменной. Когда Вы закончите работу с этой переменной, присвойте результат
обратно соответствующему свойству объекта client
.
Эта техника несколько повысит производительность среды.
Как отмечено ранее, Вы не можете сохранять ссылки на другие объекты в объекте client
.
Вы можете, однако, сохранять ссылки на объекты в объектах project
или server
. Если у вас имеется свойство, ассоциированное с
клиентом, принимающее значения объекта, создайте массив, индексированный по
клиентским ID, и храните ссылку на массив в объекте project
или server
.
Вы можете использовать этот массив для хранения значений объекта,
ассоциированного с клиентом. Рассмотрим следующий код:
if client.id == null
client.id = ssjs_generateClientID();
project.clientDates[client.id] = new Date();
Здесь использована функция ssjs_generateClientID
, описанная далее,
для создания уникального ID для данного объекта client
. Она
использует этот ID как индекс массива clientDates
в объекте project
и сохраняет новый объект Date
в этом массиве, ассоциированном с
текущим объектом client
.
Для некоторых приложений может понадобиться сохранять
информацию, специфическую для пары клиент/приложение, в объектах project
или
server
. Два обычных примера - сохранение соединения с БД между клиентскими запросами
(описано в Главе 8, "Соединение с Базой Данных")
или хранение специального объекта, имеющего такой же период существования, что и
предопределённый объект client
, и содержащего значения объектов (описано
в разделе "Создание Специального Объекта client").
В этих случаях Вам необходим способ уникально обратиться к паре клиент/приложение.
JavaScript имеет для этого две функции, ssjs_getClientID
и
ssjs_generateClientID
. Они не принимают аргументов; обе возвращают
уникальную строку, которую вы можете использовать для идентификации пары.
При каждом вызове
ssjs_generateClientID
машина выполнения возвращает новый
идентификатор. Исходя из этого,
если Вы используете эту функцию и Вам нужен идентификатор, существующий дольше,
чем отдельный клиентский запрос, Вам нужно сохранить этот идентификатор,
возможно, как свойство объекта client
. Пример использования этой
функции см. в разделе "Совместное Использование
Массива Пула Соединений".
Если Вы используете эту функцию и сохраняете ID в объекте client
,
нужно проявлять осторожность, чтобы хакер не смог получить доступ к этому ID и,
как следствие, к закрытой информации.
Альтернативой может стать использование функции ssjs_getClientID
.
Если Вы применяете одну из этих серверных технологий для объекта client
, машина JavaScript
генерирует и использует идентификатор для доступа к информации определённой пары
клиент/приложение. (О работе с объектом client
см. раздел "Технология
Работы с Объектом client").
При использовании этих технологий
ssjs_getClientID
возвращает идентификатор, используемый машиной
выполнения. Каждый раз при вызове этой функции из определённой пары
клиент/приложение Вы будете получать тот же самый идентификатор. Соответственно,
Вам нет необходимости сохранять идентификатор, возвращаемый функцией ssjs_getClientID
.
Однако, если Вы используете другую технику, эта функция возвращает "undefined";
тогда необходимо использовать функцию ssjs_generateClientID
.
Если Вам нужен идентификатор и Вы используете серверную технологию, возможно,
понадобится использовать функцию
ssjs_getClientID
. Тогда Вам не нужно будет самостоятельно
сохранять идентификатор и отслеживать его использование; машина выполнения
сделает это за Вас. При использовании клиентской техники, однако, Вы не сможете
применять функцию ssjs_getClientID
; тогда Вы обязаны использовать функцию
ssjs_generateClientID
.
Как уже было сказано ранее, свойства предопределённого
объекта client
могут иметь только строковые значения. Это
ограничение может представлять проблему для работы некоторых приложений.
Например, Вашему приложению нужен объект, который существует столько же, сколько
предопределённый объект client
, но который может принимать в
качестве значений свойств объекты или другие типы данных. В этом случае Вы
можете создать Ваш собственный объект и сохранить его как свойство объекта client
.
В этом разделе приведён пример создания такого объекта. Можете включить этот код как файл JavaScript в Ваше приложение. Затем в начале страницы, на которой нужно использовать этот объект, введите следующий оператор:
var customClient = getCustomClient()
(Разумеется, можно использовать другое имя переменной.) Если это первая
страница, запрашивающая данный объект, метод
getCustomClient
создаёт новый объект. На других страницах он будет
возвращать уже существующий объект.
Этот код сохраняет массив всех специальных объектов client
,
определённых в приложении как значения свойства customClients
предопределённого объекта
project
. Он сохраняет индекс в этом массиве и строковое значение свойства
customClientID
предопределённого объекта client
. Кроме
того, этот код использует блокировку/lock, хранимую в свойстве customClientLock
объекта project
, чтобы гарантировать надёжность доступа к этому
массиву. О блокировании см. раздел "Безопасное Совместное
Использование Объектов с Блокировкой".
Переменная timeout
в функции
getCustomClient
жёстко кодирует период окончания действия этого
объекта. Если Вам нужен другой период окончания действия, специфицируйте другое
значение для этой переменной. Независимо от используемого периода действия,
Вы должны вызывать метод expiration
предопределённого объекта client
для установки его срока окончания действия в то же значение, какое
специфицировано Вами для специального объекта. О работе этого метода см. раздел "Период
Существования Объекта client".
Для удаления всех закончивших работу специальных объектов приложения вызовите следующую функцию:
expireCustomClients()
Это всё, что нужно сделать! Если Вы используете этот код, предопределённые
объекты client
и
project
имеют следующие дополнительные свойства, которые Вы не должны изменять:
Вы можете специализировать класс путём изменения его методов
onInit
и onDestroy
. Как показано здесь, эти методы - это просто основа.
Вы можете добавить код для изменения действий при создании и уничтожении объекта.
// Эта функция создаёт новый специальный объект client или запрашивает существующий. function getCustomClient()
{
// ==========> Измените жёстко кодированный период ожидания <==========
// Примечание: Не забудьте установить окончание обслуживания client-статуса
// в то же самое значение, что и использованное ниже в вызове
// client.expiration. Это даст возможность индексу отключать все предопределённые
// объекты client в то же время, которое содержится в объекте project.
var timeout = 600;
var customClient = null;
var deathRow = null;
var newObjectWasCreated = false;
var customClientLock = getCustomClientLock();
customClientLock.lock();
var customClientID = client.customClientID;
if ( customClientID == null ) {
customClient = new CustomClient(timeout);
newObjectWasCreated = true;
}
else {
var customClients = getCustomClients();
customClient = customClients[customClientID];
if ( customClient == null ) {
customClient = new CustomClient(timeout);
newObjectWasCreated = true;
}
else {
var now = (new Date()).getTime();
if ( customClient.expiration <= now ) {
delete customClients[customClientID];
deathRow = customClient;
customClient = new CustomClient(timeout);
newObjectWasCreated = true;
}
else {
customClient.expiration = (new Date()).getTime() +
timeout*1000;
}
}
}
if ( newObjectWasCreated )
customClient.onInit();
customClientLock.unlock();
if ( deathRow != null )
deathRow.onDestroy();
return customClient;
}
// Функция для удаления старых специальных объектов client.
function expireCustomClients()
{
var customClients = getCustomClients();
var now = (new Date()).getTime();
for ( var i in customClients ) {
var clientObj = customClients[i];
if ( clientObj.expiration <= now ) {
var customClientLock = getCustomClientLock();
customClientLock.lock();
if ( clientObj.expiration <= now ) {
delete customClients[i];
}
else {
clientObj = null;
}
customClientLock.unlock()
if ( clientObj != null )
clientObj.onDestroy();
} } }
// Не вызывайте эту функцию явно.
// Она используется методами getCustomClient и expireCustomClients.
function getCustomClientLock()
{
if ( project.customClientLock == null ) {
project.lock()
if ( project.customClientLock == null )
project.customClientLock = new Lock()
project.unlock()
}
return project.customClientLock
}
// Не вызывайте эту функцию явно.
// Она используется методами getCustomClient и expireCustomClients.
function getCustomClients()
{
if ( project.customClients == null ) {
project.lock()
if ( project.customClients == null )
project.customClients = new Object()
project.unlock()
}
return project.customClients
}
// Конструктор класса CustomClient. Не вызывайте его явно.
// Используйте вместо него функцию getCustomClient.
function CustomClient(seconds)
{
var customClients = getCustomClients();
var customClientID = ssjs_generateClientID();
this.onInit = CustomClientMethod_onInit;
this.onDestroy = CustomClientMethod_onDestroy;
this.expiration = (new Date()).getTime() + seconds*1000;>
client.customClientID = customClientID;
customClients[customClientID] = this;
}
// Если нужно специализировать, переопределите следующие две функции.
function CustomClientMethod_onInit()
{
// ==========> Добавьте код инициализации Вашего объекта <==========
// Этот метод вызывается при блокировке.
}
function CustomClientMethod_onDestroy()
{
// ==========> Добавьте код очистки Вашего объекта <==========
// Этот метод не вызывается из блокировки.
}
Объект project
содержит глобальные данные приложения и
предоставляет метод для совместного использования информации клиентами,
выполняющими доступ к приложению. JavaScript конструирует новый объект
project
, когда приложение стартует при использовании Application Manager.
Каждый клиент, получающий доступ к приложению, использует один и тот же объект project
.
Резюме по объекту project
см. в разделе "Предопределённые Объекты. Обзор.".
В отличие от предыдущих релизов, в этом релизе машина
выполнения JavaScript не создаёт и не уничтожает объект project
для
каждого запроса. Если Вы остановили работу приложения,
объект project
этого приложения уничтожается. Новый объект project
создаётся для приложения, когда оно стартует снова. Типичный период
существования объекта project
- дни или недели.
JavaScript конструирует набор объектов project
для каждого Netscape HTTP-процесса, запущенного на сервере. JavaScript
конструирует объект project
для каждого приложения, запущенного на
каждом отдельном сервере. Например, если один сервер запущен на порте 80, а
другой - на порте 142 на той же самой машине, JavaScript конструирует отдельный набор объектов
project
для каждого процесса.
У объекта project
нет предопределённых
свойств, поскольку он предназначен для содержания специфических для приложений
данных, доступных для многих клиентов. Вы можете создавать свойства любого
верного типа JavaScript, включая ссылки на другие JavaScript-объекты.
Если Вы храните ссылку на другой объект в объекте project
, машина
выполнения не уничтожает объект, на который ссылаются, по окончании клиентского
запроса, в котором объект создаётся. Объект доступен и в течение последующих запросов.
Хороший пример свойства объекта project
-
следующий доступный ID потребителя. Приложение может использовать это свойство
для отслеживания последующих присваиваемых IDs. Любому клиенту, получающему
доступ к приложению и не имеющему ID потребителя, присваивается этот ID, и его
значение будет увеличиваться для каждого первоначального доступа.
Помните, что объект project
существует только
в течение периода работы приложения на сервере. Когда приложение останавливается,
объект project
уничтожается вместе со всеми значениями его свойств.
Поэтому, если у Вас имеются данные приложения, которые нужно сохранять постоянно,
их необходимо сохранять в БД
(см. Часть 3, "LiveWire Database Service") или в
файле на сервере (см. "Служба Файловой Системы").
Для каждого приложения имеется только один объект project
. Таким
образом, код, исполняемый в любом запросе к данному приложению, может получить
доступ к тому же объекту project
. Поскольку сервер является многопоточным,
могут иметься несколько активных запросов в данный момент времени, либо от
одного и того же клиента, либо от разных клиентов.
Для поддержания целостности данных Вы обязаны
гарантировать исключительный доступ к свойству объекта project
при
изменении значения свойства. Неявной блокировки, как это было в предыдущих
релизах, больше нет; Вы обязаны запрашивать исключительный доступ. Легче всего
сделать это через использование методов lock
и unlock
объекта project
. См. раздел "Безопасное
Совместное Использование Объектов с Помощью Блокировки.".
Объект server
содержит глобальные данные для всего сервера в целом
и предоставляет метод для обеспечения совместного использования информации
разными приложениями, работающими на сервере. Объект server
также
автоматически инициализируется информацией о сервере. Резюме по объекту
server
см. в разделе "Предопределённые Объекты. Обзор.".
Машина выполнения JavaScript конструирует новый объект server
,
когда сервер стартует, и уничтожает объект server
, когда сервер
останавливается. Каждое приложение, запущенное на сервере, использует один и тот
же объект server
.
JavaScript конструирует объект server
для
каждого Netscape HTTPD-процесса (на сервере), запущенного на машине. Например,
может иметься серверный процесс на порте
80 и другой - на порте 8080. Это совершенно отдельные серверные процессы, и JavaScript конструирует объект
server
для каждого из них.
В следующей таблице описаны свойства объекта server
.
server
Свойство | Описание | Пример |
---|---|---|
hostname |
www.netscape.com:85 | |
host |
www.netscape.com | |
protocol |
http: | |
port |
85 | |
jsVersion |
3.0 WindowsNT |
Например, Вы можете использовать свойство jsVersion
для обусловливания возможностей на базе серверной платформы (или версии), на
которой приложение работает, как показано здесь:
if (server.jsVersion == "3.0 WindowsNT")
write ("Application is running on a Windows NT server.");
Помимо этих автоматически инициализируемых свойств, Вы можете создавать свойства
для хранения данных, совместно используемых многими приложениями. Свойства могут
иметь любой допустимый в JavaScript тип, включая ссылки на другие JavaScript-объекты.
Если Вы сохраняете ссылку на другой объект в объекте server
,
машина выполнения не разрушает объект, на который ссылаются, по окончании
запроса, в ходе которого он (объект server
) был создан. Объект остаётся
доступным для последующих запросов.
Как и случае с объектом project
, объект server
имеет
ограниченный период существования. Когда
web-сервер останавливается, объект server
разрушается вместе со
всеми значениями свойств. Поэтому, если у Вас имеются данные приложения, которые
нужно сохранять постоянно, их необходимо сохранять в БД (см. Часть 3, "LiveWire Database Service") или в
файле на сервере (см. "Служба Файловой Системы").
Для всего сервера имеется один объект server
. Таким образом, код,
выполняемый в любом запросе, в любом приложении, может иметь доступ к одному и
тому же объекту server
. Поскольку сервер является многопоточным,
могут иметься несколько запросов в данный момент времени. Для поддержания целостности данных Вы обязаны
гарантировать исключительный доступ к объекту server
при внесении изменений.
Также Вы обязаны гарантировать, что имеется исключительный доступ к свойству объекта
server
, когда изменяется значение этого свойства. Неявной
блокировки, как это было в предыдущих релизах, теперь нет; Вы обязаны
запрашивать исключительный доступ. Легче всего сделать это через использование
методов lock
и unlock
объекта server
. См. раздел "Безопасное
Совместное Использование Объектов с Помощью Блокировки".
Объект client
ассоциирован как с определённым приложением, так и с
определённым клиентом. Как было сказано в разделе "Объект client",
машина выполнения создаёт новый объект
client
всякий раз при поступлении нового запроса от клиента к
серверу. Однако целью является сохранение свойств объекта client
от
одного запроса до следующего. Для этого машине выполнения нужно сохранить
свойства объекта client
между запросами.
Есть два основных подхода при работе со свойствами объекта
client
: можно работать с ними на стороне клиента или на сервере.
Эти два вида клиентской техники либо сохраняют имена свойств и их значения
непосредственно в куках на клиенте, либо в URLs на генерируемой HTML-странице.
Все три вида серверной техники сохраняют имена свойств и их значения в структуре
данных в памяти сервера, но различаются по используемой для индексирования
структуры этих данных схеме.
Вид техники выбирается, когда Вы используете JavaScript Application Manager
для инсталяции или модификации приложения, как указано в разделе
"Установка Нового Приложения". Это даёт Вам
(или менеджеру сайта) возможность изменять технику обслуживания без
перекомпилирования приложения. Однако поведение Вашего приложения может меняться
в зависимости от действующей техники обслуживания объекта
client
, как описано в следующих разделах. Обязательно объясните
Вашему менеджеру сайта, от какого вида техники зависит работа Вашего приложения.
Иначе менеджер может изменить эти установки и нарушить работу Вашего приложения.
Поскольку некоторые виды этой техники сохраняют информацию в структуре данных на сервере или в куки-файле на клиенте, машина выполнения JavaScript дополнительно должна определять, когда избавиться от этих свойств. В разделе "Период Существования Объекта client" рассматривается, как машина выполнения определяет это, и описываются методы, которые можно использовать для изменения этого поведения.
Каждый вид техники имеет свои преимущества и недостатки, и то, что является недостатком в одной ситуации, может оказаться преимуществом в другой. Вам необходимо выбрать вид техники, наиболее подходящей для Вашего приложения. Виды техники описаны более детально в последующих разделах; в этом разделе даётся общее сравнение.
В таблице выполнено общее сравнение клиентской и серверной техники.
На Рисунке 6.3 и на Рисунке 6.4 видно, какая информация хранится при использовании каждого вида техники, где она хранится и передаётся ли по сети. На Рисунке 6.3 дана информация для клиентской техники.
На Рисунке 6.4 дана информация для серверной техники.
Имеются некоторые общие для видов (серверной и клиентской) техники вопросы. Для обоих типов техники, использующих куки, браузер обязан поддерживать протокол Netscape cookie protocol. В обоих случаях, когда браузер на клиентской машине закрывается, информация сохраняется в cookie-файле на клиентской машине. В других случаях ограничений нет.
Техника серверных кук создаёт единственную куку для
идентификации соответствующего объекта
client
. В противоположность этому, техника клиентских кук создаёт
отдельную куку для каждого свойства объекта client
. На технику
клиентских кук, следовательно, скорее повлияет ограничение в 20 кук на приложение.
В технике клиентских кук свойства объекта client
высылаются
клиенту, когда высылается первая часть HTML-страницы. Если Вы изменяете позднее
значения свойств объекта client
при выполнении действий на странице,
эти изменения не отсылаются клиенту и теряются. Это ограничение не действует для другой техники.
Для обеих техник, использующих кодирование в URL, если Ваше приложение
конструирует URL на этапе выполнения или использует функцию redirect
,
необходимо либо вручную присоединять свойства объекта client
,
которые должны быть сохранены, либо использовать addClient
, чтобы
машина выполнения присоединила эти свойства.
Хотя присоединение свойств не является обязательным для других техник, Вам может
понадобиться сделать это, чтобы изменение техники не нарушило работу Вашего приложения.
Кроме того, при использовании техник кодирования URL, как только браузер
перейдёт на страницу за пределами приложения или даже отправит форму приложению
с использованием метода GET
, все свойства объекта
client
будут утеряны. Свойства не теряются в такой ситуации для других видов техники.
Ваш выбор техники частично определяется тем, должны ли существовать свойства
объекта client
в такой ситуации.
Ваш выбор используемой техники опирается на требования Вашего приложения. Техника клиентских кук не использует дополнительной памяти сервера (как при серверной технике) и высылает информацию только один раз для страницы (в противоположность клиентской технике кодирования URL). Эти факты могут сделать использование техники клиентских кук предпочтительным для больших Internet-приложений. Однако в некоторых случаях другая техника может оказаться более подходящей. Например, серверный IP-адрес работает быстрее, не увеличивая сетевого трафика. Можно использовать это для приложений Вашей Intranet, для которых скорость работы является критичной.
Есть два вида клиентской техники:
Сравнение этих видов техники см. в разделе "Сравнение Видов Техники Обслуживания Объекта сlient".
Когда приложение использует клиентские виды техники,
машина выполнения кодирует свойства объекта client
в ответ на
клиентский запрос в шапке/header ответа (для клиентской куки) или в URLs в теле
ответа (для клиентского кодирования URL).
Поскольку реальные имена и значения свойств пересылаются между клиентом и сервером, рестарт сервера не вызывает потери клиентской информации. Однако отправка этой информации вызывает увеличение сетевого трафика.
В технике клиентских кук машина выполнения JavaScript
на сервере использует протокол Netscape cookie protocol для передачи клиенту свойств объекта
client
и их значений. Она создаёт по одной куке для каждого
свойства объекта client
. Свойства высылаются клиенту один раз в шапке/header ответа
генерируемой HTML-страницы. Netscape cookie protocol описан в книге
Клиентский JavaScript.
Руководство
.
Для исключения конфликтов с другими куками, которые Вы
можете создать в Вашем приложении, машина выполнения создаёт имя куки, добавляя
NETSCAPE_LIVEWIRE.
перед началом имени свойства объекта client
.
Например, если client
имеет свойство custID
, машина выполнения
создаёт куку под названием NETSCAPE_LIVEWIRE.custID
. Когда
информация куки высылается клиенту,
машина выполнения делает всё необходимое кодирование специальных символов в
значении свойства, как описано в книге
Клиентский JavaScript.
Руководство
.
Иногда Вашему приложению может понадобиться взаимодействие операторов JavaScript
на сервере и на стороне клиента. Поскольку это вид техники высылает клиенту
свойства объекта client
как куки, Вы можете использовать это как
способ облегчить это взаимодействие. См. дополнительно "Взаимодействие
Между Сервером и Клиентом".
При использовании этой техники машина выполнения сохраняет
свойства объекта client
, когда она в первый раз очищает внутренний
буфер, содержащий сгенерированную HTML-страницу. Исходя из этого,
для того чтобы предотвратить потерю любой информации, Вы должны как можно раньше
присвоить значения всем свойствам объекта client
в скриптах на
каждой странице. В особенности Вы должны гарантировать, что свойства объекта client
будут высылаться перед тем как (1) машина выполнения сгенерирует 64KB
содержимого HTML-страницы (она автоматически очищает буфер вывода в этой точке), (2)
Вы вызовете функцию flush
для очистки буфера вывода или (3) Вы
вызовете функцию redirect
для изменения клиентских запросов.
Дополнительно см. разделы "Очистка Буфера Вывода" и
"Процессинг Времени Выполнения на Сервере".
По умолчанию, когда Вы используете технику клиентских кук, машина выполнения не
устанавливает явно время окончания срока действия кук. В этом случае куки
заканчивают работать, когда пользователь закрывает браузер. (Это поведение по
умолчанию для всех кук.) Как указано в разделе "Период
Существования Объекта client", Вы можете использовать метод expiration
объекта client
для изменения срока окончания действия. Если Вы
используете client.expiration
, машина выполнения устанавливает
соответствующий срок окончания работы куки в cookie-файле.
При использовании техники клиентских кук метод client.destroy
уничтожает все значения свойств объекта client
, но не влияет на то,
что хранится в cookie-файле на клиентской машине. Не используйте для удаления
кук из cookie-файла или памяти браузера метод
client.destroy
; вместо него используйте client.expiration
с аргументом 0 секунд.
В целом Netscape-куки имеют нижеследующие ограничения. Эти
ограничения применяются тогда, когда Вы используете куки для хранения свойств объекта client
:
client
.client
, объект
client
может хранить максимум 20 свойств. Если Вы хотите
использовать в Вашем приложении также и другие куки, общее их количество всё
равно ограничено числом 20.
При использовании техники клиентского кодирования URL
машина выполнения на сервере пересылает клиенту свойства и значения объекта client
,
присоединяя их к каждому URL в генерируемой HTML-странице. Соответственно
свойства и их значения пересылаются столько раз, сколько имеется гиперссылок на
генерируемой HTML-странице, что приводит к значительному увеличению сетевого трафика.
Размер строки URL ограничен 4KB. Следовательно, когда Вы используете клиентское кодирование URL, общий размер имён свойств и их значений не может превышать 4KB. Любая информация свыше лимита 4KB будет усекаться.
Если Вы генерируете URLs динамически или используете функцию
redirect
, Вы можете добавлять свойства объекта client
или другие свойства к URL. Когда Вы вызываете redirect
или генерируете URL, компилятор не
присоединяет автоматически свойства объекта client
. Если
присоединение необходимо, используйте функцию addClient
. См. раздел
"Присоединение Свойств Объекта client к URL Вручную".
В технике клиентского кодирования URL значения свойств добавляются к URL по мере обработки этих URL. Нужно следить, чтобы Ваши URL имели одинаковые свойства и значения. Например, рассмотрим код:
<SERVER>
...
client.numwrites = 2;
write (addClient(
"<A HREF='page2.html'>Some link</A>"));
client.numwrites = 3;
write (addClient(
"<A HREF='page3.html'>Another link</A>"));
...
</SERVER>
Когда машина выполнения обрабатывает первый оператор
write
, она использует 2 как значение свойства numwrites
,
а при обработке второго оператора write
она использует в качестве значения 3.
Итак, если Вы используете метод client.
destroy
в
середине страницы, только ссылки, шедшие на странице до вызова этого метода
получат значения, присоединённые к URL. Те же, которые идут после вызова этого
метода, не имеют присоединённых значений. Следовательно, значения свойств
объекта client
передаются на некоторые страницы, но не на все. Это может быть нежелательно.
Если страница имеет ссылку на URL за пределами Вашего
приложения, Вам не понадобится присоединять клиентский статус. Тогда не
используйте статическую строку в качестве значения HREF
.
Вместо этого вычисляйте значение. Это предотвратит автоматическое присоединение
машиной выполнения клиентского статуса к URL. Например, у вас имеется ссылка:
<A HREF="mailto:me@royalairways.com">
Машина выполнения присоединяет свойства объекта client
. Чтобы этого
не происходило, используйте очень похожую ссылку:
<A HREF=`"mailto:me@royalairways.com"`>
При этой технике объект client
не перестаёт действовать,
поскольку существует только в URL-строке, находящейся на клиенте. Следовательно,
метод client.expiration
не производит никаких действий.
При клиентском кодировании URL Вы теряете все свойства
объекта client
, когда отправляете форму, используя метод GET
,
и когда выполняете доступ к другому приложению. Ещё раз - Вам может быть нужно
или не нужно терять эти свойства, в зависимости от потребностей Вашего приложения.
В отличие от техники клиентских кук, клиентское кодирование URL не требует ни поддержки web-браузером протокола Netscape cookie, ни записи информации на клиентской машине.
Есть три вида серверной техники:
Сравнение разных видов техники см. в разделе "Сравнение Видов Техники Обслуживания Объекта сlient".
При любом виде техники машина выполнения на сервере
сохраняет свойства объекта client
и их значения в структуре данных
в памяти сервера. Единая структура данных, сохраняемая в период между
клиентскими запросами, используется для всех приложений, работающих на сервере.
Виды техники различаются только в индексе, используемом для доступа к информации
в этой структуре данных, гарантируя, что каждая пара клиент/приложение получает
соответствующие свойства и значения для объекта client
.
Ни одна из этих техник не записывает информацию на жёсткий диск сервера. Только техника серверных кук позволяет записывать информацию на диск клиентской машины при окончании работы браузера.
Поскольку эти виды техники сохраняют информацию
объектов client
в памяти сервера в промежутке между клиентскими
запросами, нет или почти нет увеличения сетевого трафика. Имена и значения
свойств никогда не пересылаются клиенту. Кроме того нет ограничения на
количество свойств объекта client
и на размер свойства.
Недостатком является, разумеется, то, что эти виды техники используют память сервера в промежутке между клиентскими запросами. Для приложений, используемых большим количеством потребителей, это может иметь важное значение. Конечно, это можно также рассматривать и как преимущество, так как Вы можете сохранять столько информации, сколько необходимо.
Техника с использованием IP-адреса индексирует структуру данных на основе IP-адресов приложения и клиента. Эта простая техника является также и самой быстрой, поскольку вообще не требует отправки информации клиенту. Так как индекс базируется на IP-адресах приложения и клиента, эта техника создаёт отдельный индекс для каждой пары приложение/клиент, работающей на сервере.
Эта техника хорошо работает, когда все клиенты имеют фиксированные IP-адреса. Она работает ненадёжно, если клиент не имеет гарантированно фиксированного IP-адреса, например, если клиент использует протокол Dynamic Host Configuration Protocol (DHCP) или провайдера Internet, который динамически размещает IP-адреса. Эта техника также не работает у клиентов, использующих прокси-сервер, поскольку все пользователи прокси сообщают один и тот же IP-адрес. Поэтому данная техника используется в основном только для приложений Intranet.
Техника серверных кук использует длинное уникальное имя,
генерируемое машиной выполнения для индексации структуры данных на сервере.
Машина выполнения использует протокол Netscape cookie для хранения генерируемого
имени как куки/cookie на клиенте. Она не сохраняет имена и значения свойств как
куки. Поэтому данная техника создаёт одну куку, в то время как клиентская
техника кук создаёт отдельную куку для каждого свойства объекта client
.
Сгенерированное имя отсылается клиенту только один раз в
шапке/header HTML-страницы. Вы можете получить доступ к этому имени через
функцию ssjs_getClientID
, описанную в разделе
"Уникальное Обращение к Объекту client". Эта техника
использует тот же самый cookie-файл, что и техника клиентских кук; эти виды
техники отличаются тем, что информация сохраняется в cookie-файле. Протокол Netscape cookie protocol описан в книге
Клиентский JavaScript.
Руководство
.
Итак, поскольку клиенту отсылается только генерируемое
имя, а не реальные имена и значения свойств, не имеет значения, где на Вашей
странице изменяются свойства объекта client
. Это контрастирует с техникой клиентских кук.
По умолчанию машина выполнения устанавливает период
действия серверной структуры данных в 10 минут и не устанавливает срок действия
кук, отправляемых клиенту. Как указано в разделе "Период
Существования Объекта client", Вы можете использовать метод expiratio
объекта client
для изменения срока действия и для установки периода
действия куки.
При использовании серверной куки метод client.destroy
уничтожает все значения свойств объекта client
.
В общем, Netscape-куки имеют ограничения, перечисленные в разделе "Использование Клиентских Кук". Если Вы используете серверные куки, эти ограничения вряд ли будут достигнуты, так как создаётся только одна кука (содержащая индекс).
Это быстрая техника, не имеющая встроенных ограничений на количество и размер свойств и их значений. Вы больше ограничены тем, сколько пространства будете использовать на Вашем сервере для хранения этой информации.
Техника серверного кодирования URL использует длинное
уникальное имя, генерируемое машиной выполнения для индексации структуры данных
на сервере. В этом случае, вместо того чтобы сделать это генерируемое имя
клиентской кукой, сервер присоединяет имя к каждому URL на генерируемой HTML-странице.
Следовательно, имя высылается столько раз, сколько имеется ссылок на
генерируемой HTML-странице. (Имена и значения свойств не присоединяются к URLs, только генерируемое имя.)
Ещё раз: Вы можете получить доступ к этому генерируемому имени с помощью функции ssjs_getClientID
,
описанной в разделе "Уникальное Обращение к Объекту client".
Если Вы генерируете URLs динамически или используете функцию redirect
,
Вы можете добавлять свойства к
URL. Поэтому, когда Вы вызываете redirect
или генерируете URL,
компилятор не присоединяет индекс автоматически. Если Вы хотите оставить индекс
для свойств объекта client
, используйте функцию addClient
.
См. также "Присоединение Свойств Объекта client к URL Вручную".
Если Ваша страница имеет ссылку на URL вне Вашего приложения, Вам может и не
понадобиться присоединение клиентского индекса. Тогда не используйте статическую
строку как значение атрибута HREF
. Вместо этого вычисляйте это
значение. Это предотвратит автоматическое присоединение машиной выполнения
клиентского индекса к URL. Например, у Вас имеется ссылка:
<A HREF="mailto:me@royalairways.com">
В это случае машина выполнения присоединит индекс объекта client
.
Чтобы этого не происходило, используйте очень похожую ссылку:
<A HREF=`"mailto:me@royalairways.com"`>
При серверном кодировании URL вы теряете идентификатор объекта client
(и,
соответственно, свойства и их значения)
при отправке формы с методом GET
. Вы можете терять или не терять
эти свойств, в зависимости от потребностей Вашего приложения.
После того как клиент получил доступ к приложению, не
гарантируется, будет он далее запрашивать продолжение обработки или продолжит
выполнение до логического конца. В связи с этим объект client
не
имеет встроенного механизма окончания строка действия. Этот механизм позволяет
JavaScript периодически "зачищать" старые объекты client
, которые
больше не нужны. Каждый раз, при получении сервером запроса на страницу
приложения, JavaScript восстанавливает период существования объекта client
.
По умолчанию поведение механизма срока действия
значений варьируется и зависит от вида используемой техники работы с объектом client
,
как видно из таблицы.
client
на основе вида используемой техники
Приложение может управлять периодом ожидания JavaScript
перед зачисткой свойств объекта client
. Для изменения величины
этого периода используйте метод
expiration
, как в следующем примере:
client.expiration(30);
В ответ на это вызов машина выполнения зачищает свойства объекта client
по прошествии 30 секунд. Для серверной техники этот вызов заставит сервер удалить
свойства объекта из структур данных через 30 секунд. Для этих двух видов техники
такой вызов устанавливает окончание срока действия через 30 секунд.
Если объект client
перестаёт действовать, когда имеется активный
клиентский запрос с использованием этого объекта,
машина выполнения ждёт окончания этого запроса, прежде чем уничтожить объект client
.
Вы обязаны вызывать expiration
на каждой
странице, срок окончания действия которой хотите специфицировать. Страницы, не
специфицирующие срок действия, используют поведение по умолчанию.
Приложение может явно уничтожать объект client
методом destroy
:
client.destroy();
Когда приложение вызывает destroy
, JavaScript удаляет все свойства из объекта
client
.
Если Вы используете технику клиентских кук для работы с
объектом client
, метод destroy
уничтожает все значения
свойств объекта client
, но не влияет на то, что хранится в
клиентском cookie-файле. Чтобы удалить и значения свойств из этого cookie-файла,
не используйте метод destroy
; вместо него используйте expiration
с аргументом 0 секунд.
Если Вы используете технику клиентского кодирования URL для работы с объектом
client
, метод destroy
удаляет все свойства объекта client
.
Ссылки на странице до вызова destroy
оставляют свойства объекта client
в своих URL, а ссылки, расположенные после вызова метода, не имеют свойств.
Поскольку маловероятно, что Вам понадобится, чтобы только некоторые URL
страницы содержали свойства объекта client
, Вы, вероятно, должны
будете вызывать
destroy
либо вверху, либо внизу страницы, когда используете работу
с клиентскими URL. См. также "Использование Клиентского Кодирования URL".
При использовании кодирования URL на клиенте или на
сервере для работы с объектом client
машина выполнения обычно
должна сохранять соответствующую информацию (имена и значения свойств объекта client
или индекс серверной структуры данных) во всех
URL, высылаемых клиенту, вне зависимости от того, являются ли эти URL как
статический HTML или были сгенерированы операторами серверного JavaScript.
Машина выполнения автоматически присоединяет
соответствующую информацию к гиперссылкам HTML, находящимся вне тэгов SERVER
.
Так, например, предположим, что Ваша HTML-страница содержит следующие операторы:
<HTML>
For more information, contact
<A HREF="http://royalairways.com/contact_info.html">
Royal Airways</a>
...
</HTML>
Если приложение использует кодирование URL для объекта client
,
машина выполнения автоматически присоединит client
-информацию в
конец URL. Вы не должны ничего делать специально для поддержки этого поведения.
Однако ваше приложение может использовать функцию
write
для динамической генерации оператора HTML, содержащего URL.
Вы можете также использовать функцию redirect
для старта нового
запроса. Когда Вы используете операторы серверного JavaScript
для добавления URL к генерируемой HTML-странице, машина выполнения предполагает,
что Вы специфицировали полный URL для отправки в нужном Вам виде. Она не
присоединяет автоматически клиентскую информацию даже при использовании
кодирования URL для работы с объектом client
. Если Вам нужно
присоединить клиентскую информацию,
Вы обязаны сделать это сами.
Вы используете функцию addClient
для
добавления вручную соответствующей client
-информации. Эта функция
принимает URL и возвращает новый URL
с присоединённой информацией. Например, предположим, что контактный URL
варьируется в зависимости от значения свойства client.contact
.
Вместо вышеприведённого HTML Вы можете ввести следующее:
<HTML>
For more information, contact
<server>
if (client.contact == "VIP") {
write ("<A HREF='http://royalairways.com/vip_contact_info.html'>");
write ("Royal Airways VIP Contact</a>");
}
else {
write ("<A HREF='http://royalairways.com/contact_info.html'>");
write ("Royal Airways</a>");
}
</server>
...
</HTML>
Теперь машина выполнения не присоединяет свойства объекта client
к URL. Если Вы используете один из видов техники кодирования URL для работы с
объектом client
, может возникнуть проблема. Тогда, если Вы хотите
отправить свойства объекта client
с этим URL, используйте такой код:
<HTML>
For more information, contact
<server>
if (client.contact == "VIP") {
write (addClient(
"<A HREF='http://royalairways.com/vip_contact_info.html'>"));
write ("Royal Airways VIP Contact</a>");
}
else {
write (addClient(
"<A HREF='http://royalairways.com/contact_info.html'>"));
write ("Royal Airways</a>");
}
</server>
...
</HTML>
Также всякий раз, когда Вы применяете функцию redirect
для перенаправления клиентского запроса, Вы должны использовать addClient
для присоединения информации, как здесь:
redirect(addClient("mypage.html"));
В противоположность этому, если Ваша страница имеет ссылку на URL вне Вашего
приложения, Вам может не понадобиться присоединение клиентской информации.
Тогда не используйте статическую строку в значении атрибута HREF
.
Вместо этого вычисляйте значение. Это предотвратит автоматическое присоединение
машиной выполнения клиентского индекса или свойств к URL. Например, у вас имеется ссылка:
<A HREF="mailto:me@royalairways.com">
В этом случае машина выполнения присоединяет клиентскую информацию. Чтобы этого не было, используйте очень похожую ссылку:
<A HREF=`"mailto:me@royalairways.com"`>
Хотя приложение первоначально инсталировано для
использования техники без кодирования
URL для работы с client
, оно может быть позднее модифицировано для использования техники кодирования URL.
Следовательно, если Ваше приложение генерирует динамические URL или использует redirect
,
Вам всегда нужно будет использовать addClient
.
Рабочая среда для версии 3.x или 4.x Netscape-сервера является многопоточной; то есть она обрабатывает более одного запроса в единицу времени. Поскольку эти запросы могут требовать выполнения JavaScript, то более чем один поток выполнения JavaScript может быть активным в одно и то же время.
Если несколько потоков одновременно пытаются изменить свойство одного и того же объекта JavaScript, они могут привести этот объект в несоответствующее состояние. Участок кода, в котором необходимо выполнять один, и только один, поток выполнения в единицу времени, называется критическим разделом/сritical section.
Один объект server
используется совместно
всеми клиентами и всеми приложениями, работающими на сервере. Один объект project
используется всеми клиентами, получающими доступ к одному приложению
на сервере. Кроме того, Ваше приложение может создавать другие объекты, которые
оно предоставляет в совместное пользование клиентским запросам, или оно даже может
совместно с другими приложениями использовать объекты. Для поддержания
целостности данных в этих совместно используемых объектах Вы обязаны получить
исключительный доступ к объекту, прежде чем изменять любое его свойство.
В отличие от предыдущих релизов, неявная блокировка объектов
project
иserver
теперь отсутствует.
Чтобы лучше понять, что происходит, рассмотрим следующий
пример. Предположим, Вы создаёте совместно используемый объект project.orders
для отслеживания заказов пользователей. Вы обновляете
project.orders.count
каждый раз при получении нового заказа, используя следующий код:
var x = project.orders.count;
x = x + 1;
project.orders.count = x;
Предположим, что project.orders.count
первоначально установлено в 1 и что поступили два новых заказа в двух разных потоках.
Произойдёт следующее:
project.orders.count
в переменной x
.x
.x
.project.orders.count
в 2.project.orders.count
изменилось, и также устанавливает 2 в х.
Итак, конечное значение project.orders.count
будет 2, хотя корректным должно быть 3.
Чтобы избежать проблем такого рода, Вам нужно получить
исключительный доступ к свойствам совместно используемых объектов перед тем как
записывать в них. Для этих целей Вы можете конструировать Ваши собственные
экземпляры класса
Lock
, работающие с любым совместно используемым объектом. Кроме
того, объекты
server
и project
имеют методы lock
и unlock
,
которые Вы можете использовать для ограничения доступа к этим объектам.
Представьте lock (замок/блокировку) как именованный флаг, который Вы обязаны устанавливать перед входом в критичный раздел. Если Вы запрашиваете именованный флаг и кто-то уже имеет его, Вы будете ждать, пока этот второй не освободит флаг. В процессе ожидания Вы не сможете изменять то, что не должны изменять. После получения Вами флага кто-либо ещё будет ожидать и не сможет ничего изменить, пока Вы не освободите флаг. Если возникнет ошибка или таймаут закончится до того, как Вы получите флаг, Вы можете снова вернуться в режим ожидания, либо делать что-нибудь другое, как, например, дать Вашим пользователям знать, что приложение очень занято, чтобы выполнить данную операцию сейчас. Вы не должны вмешиваться в процесс ожидания (изменяя совместно используемую информацию)! Рисунок 6.5 иллюстрирует этот процесс.
В терминах программирования замок/lock представлен
экземпляром класса Lock
. Вы можете использовать экземпляр класса Lock
для получения исключительного доступа к любому совместно используемому объекту.
Обычно Вы создаёте экземпляры Lock
на начальной странице Вашего
приложения (по причинам, которые будет изложены позднее).
На других страницах, перед критичным для совместно используемого объекта разделом (например,
перед разделом, который запрашивает и изменяет значение свойства), Вы вызываете
метод lock
экземпляра Lock
. Если этот метод возвращает true
,
Вы получаете замок и можете продолжать. В конце критичного раздела Вы вызываете
метод unlock Lock
-экземпляра.
Когда клиентский запрос в одиночном потоке выполнения
вызывает метод lock
, любой другой запрос, вызывающий метод lock
для того же Lock
-экземпляра, ожидает, пока первый поток не вызовет метод unlock
,
пока не закончится таймаут или пока не возникнет ошибка. Это верно независимо от
того, находится второй запрос в другом потоке для того же клиента или в потоке для другого клиента.
Если все потоки вызывают метод lock
перед попыткой изменения
совместно используемого объекта, то лишь один поток в единицу времени может войти в критичный раздел.
Использование замков находится всецело под управлением разработчика и требует кооперации. Машина выполнения не заставляет Вас ни вызывать
lock
, ни учитывать блокировку, полученную кем-либо другим. Если Вы не спрашиваете, Вы можете изменять всё что захотите. Поэтому очень важно выработать привычку всегда вызыватьlock
иunlock
при входе и выходе из критичного раздела кода и проверять return-значение методаlock
, чтобы гарантировать, что блокировка получена. Можно представлять это в терминах флага: если Вы не запрашиваете флаг, вы не будете находиться в режиме ожидания. Если Вы не находитесь в режиме ожидания, Вы можете изменять то, что изменять нельзя.
Вы можете создать столько замков, сколько Вам необходимо. Один и тот же замок может использоваться для управления доступом к нескольким объектам, либо каждый объект (или даже каждое свойство) может иметь собственный замок.
Замок/lock сам по себе является просто объектом JavaScript;
Вы можете сохранить ссылку на него в любом другом объекте JavaScript. Таким
образом, например, обычной практикой является конструирование экземпляра Lock
и сохранение его в объекте project
.
ПРИМЕЧАНИЕ:
Поскольку использование замка блокирует доступ других пользователей к именованному флагу, потенциально задерживая выполнение их задач, хорошей практикой станет использование замков в течение возможно более короткого периода.
Следующий код показывает, как отследить заказы
потребителей в совместно используемом объекте project.orders
,
рассмотренном ранее, и как обновлять
project.orders.count
каждый раз при получении нового заказа.
Включите в начальную страницу приложения такой код:
// Создать новый Lock и сохранить в project.
project.ordersLock = new Lock();
if (! project.ordersLock.isValid()) {
// Невозможно создать Lock. Перенаправить на страницу обработки ошибок.
redirect ("sysfailure.html");
}
Этот код создаёт экземпляр класса Lock
и проверяет (вызовом метода isValid
),
не возвращено ли что-нибудь неправильное при его создании. Очень редко Ваш
экземпляр Lock
конструируется неправильно. Это случается только
тогда, когда машина выполнения запущена вне системных ресурсов при создании объекта.
Вы обычно создаёте экземпляры Lock
на
начальной странице, поэтому Вам не нужно получать замок перед созданием
экземпляров Lock
. Начальная страница запускается только один раз -
при старте приложения на сервере. Поэтому Вам гарантируется, что создаётся только один экземпляр каждого замка.
Если, однако, Ваше приложение создаёт замок на какой-либо иной странице,
множественные запросы могут вызывать эту страницу в это время. Один запрос может
проверять наличие замка и не обнаружить его, в то время как другой запрос
создаёт замок, а третий запрос создаёт второй замок. Тем временем первый запрос
вызывает метод lock
своего объекта. Затем второй запрос вызывает
метод lock
своего объекта. Оба запроса теперь "думают", что они
имеют безопасный доступ к критичному разделу кода и продолжают свою работу, нарушая работу другого.
После получения верного замка Ваше приложение может продолжать работу. На странице, требующей доступа к критичному разделу, можете ввести такой код:
// Начало критичного раздела -- получить замок.
if ( project.ordersLock.lock() ) {
var x = project.orders.count;
x = x + 1;
project.orders.count = x;
// Конец критичного раздела -- освободить замок.
project.ordersLock.unlock();
}
else
redirect("combacklater.html");
Этот код запрашивает замок. Если замок получен (то есть, если метод lock
возвратил
true
), выполняется вход в критичный раздел, вносятся изменения и, наконец, замок освобождается.
Если метод lock
возвращает false
, то данный код не
получает замка. В этом случае приложение перенаправляет на страницу, которая
сообщает, что приложение в данный момент не может выполнить запрос.
Каждый из объектов project
и server
имеет методы lock
и unlock
. Вы можете использовать эти методы для
получения исключительного доступа к свойствам этих объектов.
В этих методах ничего нового нет.
Вам также необходима кооперация с другими участками кода. Вы можете представлять
эти методы как имеющие флаги: один флаг с именем "project", а другой - флаг с
именем "server." Если другой раздел кода не вызывает
project.lock
, первый может изменять любые свойства объекта project
.
В отличие от метода lock
класса Lock
,
Вы не можете специфицировать таймаут для метода lock
объектов project
и server
. То есть, когда Вы вызываете project.lock
,
система ожидает бесконечно долго освобождения замка. Если Вы хотите ожидать
только в течение определённого периода, используйте экземпляр класса Lock
.
В примере использованы методы
lock
и unlock
для получения исключительного доступа к
объекту project
для модификации свойства ID потребителя:
project.lock()
project.next_id = 1 + project.next_id;
client.id = project.next_id;
project.unlock();
Вы используете замки для защиты критичных участков кода. На практике это означает, что один запрос ожидает, пока другой выполняет критичный код. Вы обязаны соблюдать осторожность при использовании замков для защиты критичных разделов. Если один запрос ожидает освобождения замка, полученного другим запросом, а этот второй запрос ожидает освобождения замка, полученного первым запросом, ни один из запросов не сможет продолжить работу. Эта ситуация называется deadlock/тупик/мертвая блокировка.
Рассмотрим предыдущий пример обработки заказов потребителей. Предположим, что приложение разрешает два действия. В одном - пользователь вводит нового потребителя; в другом - пользователь вводит новый заказ. Как часть создания нового потребителя приложение также создаёт новый заказ потребителя. Это действие выполняется на одной странице приложения, давая примерно такой код:
// Создать нового потребителя (customer).
if ( project.customersLock.lock() ) {
var id = project.customers.ID;
id = id + 1;
project.customers.ID = id;
// Стартовать новый заказ (order) для этого нового потребителя.
if ( project.ordersLock.lock() ) {
var c = project.orders.count;
c = c + 1;
project.orders.count = c;
project.ordersLock.unlock();
}
project.customersLock.unlock();
}
Во втором типе действия пользователь вводит новый заказ потребителя. Как часть процесса ввода нового заказа: если потребитель ещё не является зарегистрированным потребителем, приложение создаёт нового потребителя. Это действие выполняется на другой странице приложения, где может быть примерно такой код:
// Стартовать новый заказ.
if ( project.ordersLock.lock() ) {
var c = project.orders.count;
c = c + 1;
project.orders.count = c;
if (...код определения неизвестного потребителя...) {
// Создать нового потребителя.
// Этот внутренний замок может вызвать проблемы!
if ( project.customersLock.lock() ) {
var id = project.customers.ID;
id = id + 1;
project.customers.ID = id;
project.customersLock.unlock();
}
}
project.ordersLock.unlock();
}
Заметьте, что каждый из этих фрагментов кода пытается получить второй замок, уже
получив один. Это может вызвать проблемы. Предположим, что один поток начинает
создание нового потребителя; он получает замок
customersLock
. В это же самое время другой поток начинает создание
нового заказа; он получает замок ordersLock
. Теперь первый поток
запрашивает замок ordersLock
. Поскольку второй поток уже получил
этот замок, первый поток должен ждать. Предположим, однако, что второй поток
теперь запрашивает замок customersLock
. Первый поток уже имеет этот
замок, поэтому второй поток должен ждать. Теперь потоки ждут друг друга.
Поскольку никто их них не специфицировал таймаут, оба они будут ждать бесконечно.
В данном случае проблему можно легко устранить. Поскольку значения ID потребителя и номер заказа не зависят один от другого, нет никакого смысла вкладывать замки друг в друга. Вы можете избежать возможных тупиков, переписав оба фрагмента кода. Перепишите первый фрагмент так:
// Создать нового потребителя.
if ( project.customersLock.lock() ) {
var id = project.customers.ID;
id = id + 1;
project.customers.ID = id;
project.customersLock.unlock();
}
// Стартовать новый заказ для этого нового потребителя.
if ( project.ordersLock.lock() ) {
var c = project.orders.count;
c = c + 1;
project.orders.count = c;
project.ordersLock.unlock();
}
Второй фрагмент будет примерно таким:
// Стартовать новый заказ.
if ( project.ordersLock.lock() ) {
var c = project.orders.count;
c = c + 1;
project.orders.count = c;
project.ordersLock.unlock();
}
if (...код для определения неизвестного потребителя...) {
// Создать нового потребителя.
if ( project.customersLock.lock() ) {
var id = project.customers.ID;
id = id + 1;
project.customers.ID = id;
project.customersLock.unlock();
}
}
Хотя это и надуманная ситуация, тупики это совершенно реальная проблема, и они могут произойти во многих случаях. Для этого даже не понадобится более одного замка или более одного запроса. Рассмотрим код, в котором две функции запрашивают один и тот же замок:
function fn1 () {
if ( project.lock() ) {
// ... какие-то действия ...
project.unlock();
}
}
function fn2 () {
if ( project.lock() ) {
// ... какие-то другие действия ...
project.unlock();
}
}
Сам по себе этот код не содержит проблем. Позднее слегка измените его, чтобы fn1
вызывала fn2
, уже имея замок, как показано далее:
function fn1 () {
if ( project.lock() ) {
// ... какие-то действия ...
fn2();
project.unlock();
}
}
Вот вы и получили тупик/deadlock. Это, конечно, немного смешно, когда единственный запрос ожидает от самого себя освобождения флага!
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе описано приложение-образец videoapp, иллюстрирующее использование службы LiveWire Database Service. Рассмотрено, как сконфигурировать рабочую среду для запуска приложений videoapp и oldvideo.
В главе имеются следующие разделы:
Netscape-серверы поставляются с двумя приложениями-образцами для работы с базами
данных, videoapp
и
oldvideo
, которые иллюстрируют работу LiveWire Database Service.
Эти приложения весьма похожи; они отслеживают прокат видео в воображаемом
видео-салоне. Приложение videoapp
демонстрирует использование
объектов DbPool
и Connection
.
Приложение oldvideo
демонстрирует использование предопределённого
объекта database
.
Есть несколько небольших ограничений на использование этих приложений:
videoapp
не может
базироваться на БД Informix. Приложение oldvideo
может
использоваться с Informix.videoapp
использует курсоры, захватывающие несколько HTML-страниц.
Если ваш драйвер БД - однопоточный, эти курсоры могут иметь блокировки для БД
и препятствовать доступу к ним других пользователей.Прежде чем Вы сможете запустить эти приложения, необходимо сделать незначительные изменения в исходных файлах и создать базу данных для видео. В данном разделе указывается, какие файлы Вы обязаны изменить и какие процедуры выполнить, чтобы внести эти изменения и создать БД для каждого из поддерживаемых серверов БД. См. конкретную информацию в разделе, посвящённом Вашему серверу БД.
ПРИМЕЧАНИЕ:
Ваш сервер БД обязан быть настроен и запущен перед началом создания видео-БД, и Вы обязаны сконфигурировать сервер и клиента БД так, как указано в Главе 10, "Конфигурирование Базы Данных".
Кроме того, скрипты создания БД используют утилиты БД, поставляемые с Вашими БД. Вы уже должны знать, как пользоваться этими утилитами.
Приложение videoapp
находится в директории
$NSHOME\js\samples\videoapp
, где $NSHOME
это
директория установки Netscape-сервера. Приложение oldvideo
находится в директории
$NSHOME\js\samples\oldvideo
.
Для каждого приложения Вы обязаны изменить строку с информацией о соединении в
исходном HTML-файле start.html
,
чтобы настроиться на среду окружения Вашей БД. О параметрах соединения см. раздел
"Пул Соединений Базы Данных"; дополнительно см.
также описание метода connect
в книге
Серверный JavaScript. Справочник
.
Для приложения videoapp
измените эту строку:
project.sharedConnections.pool =
new DbPool ("<Server Type>", "<Server Identifier>", "<User>", "<Password>", "<Database>", 2, false)
Для приложения oldvideo
измените эту строку:
database.connect ("INFORMIX", "yourserver", "informix", "informix", "lw_video")
Сохраните изменения и рекомпилируйте приложение. Для рекомпиляции приложения из
командной строки запустите его build-файл, расположенный в директории приложения.
Убедитесь, что переменная окружения PATH
содержит путь к
компилятору (обычно это $NSHOME\bin\https
).
Рестартуйте приложения в JavaScript Application Manager'е.
Имеются два набора скриптов создания БД для videoapp
и oldvideo
,
находящиеся в соответствующих этим приложениям директориях. Наборы скриптов
идентичны. Если Вы запустите один набор, оба приложения смогут использовать БД.
При первом запуске скриптов Вы можете получать ошибки, касающиеся стирания БД или таблиц, которые ещё не существуют. Это нормально; можно спокойно игнорировать эти сообщения.
Прежде чем использовать нижеуказанные инструкции, Вы
обязаны сконфигурировать Ваш Informix-клиент, как указано в разделе "Informix."
Кроме того, убедитесь, что переменная окружения PATH
содержит путь
к $INFORMIXDIR\bin
и что Ваш клиент сконфигурирован для использования утилит Informix.
SQL-файлы для создания видео-БД (lw_video
)
в Informix находятся в двух директориях:
$NSHOME\js\samples\videoapp\ifx
$NSHOME\js\samples\oldvideo\ifx
ПРИМЕЧАНИЕ:
Не забудьте, что пути в этом учебнике, если они относятся и к NT, и к Unix, даются в формате NT. В Unix Вы используете
$NSHOME/js/samples/videoapp/ifx.
ifx_load.csh
для videoapp
и для oldvideo
.cd c:\netscape\server\js\samples\videoapp\ifxВы можете также запустить эти команды из директории
ifx_load.bat
oldvideo\ifx
:
Прежде чем использовать нижеуказанные инструкции, Вы обязаны сконфигурировать
Ваш Oracle-клиент, как указано в разделе
"Oracle." Помимо этого, Ваш клиент обязан
быть сконфигурирован для запуска утилит Oracle. Чтобы запускать SQL Plus, Вам
может понадобиться установить переменную окружения ORACLE_SID
.
SQL-файлы для создания видео-БД в Oracle, находятся в двух директориях:
$NSHOME\js\samples\videoapp\ora
$NSHOME\js\samples\oldvideo\ora
SQL>
введите
такую команду:Start $NSHOME\js\samples\videoapp\ora\ora_video.sqlВы можете также запустить этот скрипт из директории
oldvideo
. Этот SQL-скрипт
не создаёт новую БД. Он создаёт таблицы Oracle в текущем экземпляре.ora_load
для загрузки видео-таблиц с
данными. В NT: запустите пакетный файл
ora_load.bat
для загрузки видео-таблиц с данными. Вы обязаны
отредактировать соответствующий файл для подключения к Вашему серверу;
инструкции об этом содержатся в файле.
Прежде чем использовать нижеуказанные инструкции, Вы обязаны сконфигурировать
Ваш Sybase-клиент, как указано в разделе
"Sybase." Кроме того, в Unix убедитесь, что
переменная окружения PATH
содержит $SYBASE\bin
и DSQUERY
указывает на ваш сервер.
SQL-файлы для создания видео-БД в Sybase, находятся в двух директориях:
$NSHOME\js\samples\videoapp\syb
$NSHOME\js\samples\oldvideo\syb
syb_video.csh
userid password
Например:$NSHOME\js\samples\videoapp\syb\syb_load.csh saВ NT это скрипт:
syb_load
userid
password
Например:c:\netscape\server\js\samples\videoapp\syb\syb_load saМожно также запустить скрипт из директории
oldvideo
.ПРИМЕЧАНИЕ:
Если у Вас на машине установлены и Sybase, и MS SQL Server или DB2, потенциально может возникнуть конфликт имён. Эти производители поставляют утилиты с одинаковыми названиями (
bcp
иisql
). При запуске этих скриптов убедитесь, что переменная окружения настроена для запуска корректной утилиты.
Прежде чем использовать нижеуказанные инструкции, Вы обязаны сконфигурировать
Ваш Sybase-клиент, как указано в разделе
"ODBC." Кроме того, в Unix установите DSQUERY
так, чтобы она указывала на Ваш сервер.
SQL-файлы для создания видео-БД в MS SQL Server, находятся в двух директориях:
$NSHOME\js\samples\videoapp\mss
$NSHOME\js\samples\oldvideo\mss
mss_load
userid password
Например:c:\netscape\server\js\samples\videoapp\mss\mss_load sa
ПРИМЕЧАНИЕ:
Если у Вас на машине установлены и Sybase, и MS SQL Server или DB2, потенциально может возникнуть конфликт имён. Эти производители поставляют утилиты с одинаковыми названиями (
bcp
иisql
). При запуске этих скриптов убедитесь, что переменная окружения настроена для запуска корректной утилиты.
SQL-файлы для создания видео-БД в DB2, находятся в двух директориях:
$NSHOME\js\samples\videoapp\db2
$NSHOME\js\samples\oldvideo\db2
PATH
обязана содержать директории $DB2PATH/bin
, $DB2PATH/misc
и $DB2PATH/adm
.db2_load.csh
; в NT это скрипт db2_load.bat
.
Отредактируйте соответствующий файл db2_load
и измените следующие
параметры, отражающие Ваше окружение:<nodename>
:
псевдоним имени узла/node name alias<hostname>
: имя хоста узла, где находится целевая БД<service-name>
:
имя службы или имя экземпляра из файлов службы<database-name>
:
имя БД<user>
: авторизованный
пользователь<password>
: пароль пользователя/etc/services
имеют вхождения для экземпляра или имени службы, если Вы создаёте БД на удалённом DB2-сервере.db2_load
запускает скрипты db2_video.sql
и import.sql
.
Эти вспомогательные скрипты создают видео-таблицы и загружают их данными из
файлов *.del
.
Они не создают новую БД. Они создают таблицы DB2 в псевдониме локальной БД,
специфицированном в скрипте db2_load
.ПРИМЕЧАНИЕ:
Если у Вас на машине установлены и DB2, и Sybase или MS SQL Server, потенциально может возникнуть конфликт имён. Эти производители поставляют утилиты с одинаковыми названиями (
bcp
иisql
). При запуске этих скриптов убедитесь, что переменная окружения настроена для запуска корректной утилиты.
В этом разделе говорится о запуске приложения videoapp
. Это
значительно более сложный пример, нежели образцы, рассмотренные в
Главе 4, "Быстрое Начало с Примерами Приложений". В этой главе дан только его
обзор. Вы должны просмотреть также некоторые файлы, чтобы получше познакомиться с приложением.
После создания видео-БД и изменения параметров соединения с БД Вы можете запустить приложение:
http://
server.domain
/videoapp
После соединения с БД менеджер Application Manager выводит домашнюю страницу videoapp
,
как показано на Рисунке 13.1.
Если соединение с БД невозможно установить, Вы увидите сообщение об ошибке. Убедитесь, что введены корректные параметры соединения с БД, как указано в разделе "Соединение с Базой Данных и Рекомпиляция", приложение рекомпилировано и рестартовало.
Первое, что нужно сделать при установлении соединения, это создать нового потребителя/customer. Пока Вы этого не сделаете, нет ни одного потребителя, который может использовать другие функции приложения.
Вы можете использовать videoapp
как customer или как administrator.
Как customer Вы можете:
Запустите приложения и сделайте несколько выборов для выполнения различных действий.
Исходные HTML-файлы videoapp
,
перечисленные в следующей таблице, обильно комментируются.
videoapp
Этот раздел ориентирует Вас на реализацию некоторой функциональности в videoapp
.
Он описывает, как приложение работает с БД и детали процедуры аренды видео.
Другие задачи похожи.
Когда пользователь инициирует сессию с приложением videoapp
,
входя на начальную страницу (home.html
), videoapp
проверяет, имеется ли уже соединение с БД. Если это так, videoapp
предполагает, что не только приложение, но и пользователь уже соединены с БД, и продолжает работу.
Если соединение не установлено, videoapp
перенаправляет на страницу start.html
.
На ней приложение создаёт единый пул соединений с БД, используемый всеми
потребителями, получает соединение для пользователя и стартует транзакцию с БД
для этого соединения. Затем перенаправляет обратно на home.html
для
продолжения. Пользователь никогда не видит перенаправления.
Транзакция базы данных стартует на странице start.html
и остаётся открытой, пока пользователь явно не сохранит или не отменит изменения,
щёлкнув кнопку Save Changes или Abort Changes. При этом запускается
save.html
или abort.html
. Эти страницы подтверждают или
откатывают изменения открытой транзакции, а затем немедленно начинают новую
транзакцию. Поэтому соединение потребителя всегда остаётся открытым.
Получив соединение с БД, videoapp
выводит
пользователю главную страницу. С неё пользователь может сделать выбор - такой
как аренда клипа или добавление нового потребителя. Каждая из этих опций выводит
разные страницы, которые содержат операторы серверного
JavaScript. Многие страницы имеют операторы, использующие соединение для
взаимодействия с БД, выводя информацию или внося изменения в БД.
Первое, что Вы обязаны сделать при первом соединении, это добавить нового потребителя. Пока Вы этого не сделаете, отсутствуют потребители для выполнения других действий программы.
Страница pick.html
содержит набор фрэймов,
где пользователь может взять клип напрокат. Набор фрэймов состоит из страниц
category.html
, videos.html
и pickmenu.html
.
Страница category.html
запрашивает в БД список известных категорий
клипов. Затем выводит эти категории в виде списка ссылок в таблице левого фрэйма.
Если пользователь щёлкает на одной из ссылок,
videoapp
выводит video.html
в правом фрэйме. Есть
несколько интересных моментов, касающихся серверного кода, выполняющего эти
задачи. На странице Вы можете найти такие строки:
var userId = unscramble(client.userId)
var bucket = project.sharedConnections.connections[userId]
var connection = bucket.connection
Эти операторы имеются на большинстве страниц videoapp
.
Они запрашивают соединение из того места, где оно хранится в объекте project
.
Следующая строка получает новый курсор, применимый для данной задачи:
cursor = connection.cursor("select * from categories");
Вариант этого оператора имеется в начале большинства задач.
Вот следующий интересный набор операторов:
<SERVER>
...
while (cursor.next()) {
catstr = escape(cursor.category)
</SERVER>
<TR><TD><A HREF=`"videos.html?category=" + catstr` TARGET="myright">
<SERVER>write(cursor.category);</SERVER></A>
</TD>
</TR>
<SERVER>
} // окончание цикла while
Этот цикл создаёт в курсоре ссылку для каждой категории. Обратите особое внимание на оператор:
<A HREF=`"videos.html?category=" + catstr` TARGET="myright">
Эта строка создаёт ссылку на videos.html
. Она содержит в URL имя категории.
Предположим, это категория Comedy. Оператор создаёт такую ссылку:
<A HREF="videos.html?category=Comedy" TARGET="myright">
Когда пользователь щёлкает на ссылке, сервер переходит на
страницу videos.html
и устанавливает значение свойства category
объекта request
в Comedy
.
Страница videos.html
может обрабатываться
из pick.html
или из category.html
. В первом случае
свойство category
не устанавливается, поэтому страница выводит сообщение,
предлагающее пользователю выбрать категорию. Если category
правильно установлено, videos.html
входит в БД для вывода информации
обо всех клипах данной категории. Эта страница использует ту же технику, что и category.html
,
для конструирования информации и создания ссылок на странице
rent.html
.
Страница rent.html
фактически записывает
взятые пользователем напрокат клипы. Она получает информацию из запроса и
обновляет таблицу в БД для отражения новой аренды. Эта страница выполняет
обновление, но не подтверждает изменения, пока пользователь не
выберет Save Changes или Abort Changes.
Страница pickmenu.html
просто выводит кнопки для возврата на
домашнюю страницу или на страницу для добавления нового потребителя.
Как способ применения функциональности LiveWire
рассмотрим пример модернизации videoapp
. Вот что можно сделать:
sharedConnections
предполагает наличие соединения у пользователя.
Вы можете изменить start.html
, чтобы выполнять проверку наличия ID у данного
пользователя в этом массиве и проверить, хранится ли соединение в нужном месте.
См. раздел "Совместное Использование Массива Пула Соединений".Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой книге рассматривается использование ядра и серверного JavaScript
версии 1.4. JavaScript это созданный фирмой Netscape межплатформенный
объектно-ориентированный язык скриптов (сценариев) для клиентских и серверных приложений.
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
В этой главе рассматриваются вопросы процесса разработки Вашего приложения,
такие как использование компилятора приложений JavaScript и Менеджера Приложений Netscape-серверов
для инсталяции или отладки Вашего приложения.
Информацию об использовании только клиентского JavaScript см. в книге
Клиентский JavaScript 1.3. Руководство.
В этой главе имеются следующие разделы:
Обычно HTML статичен: после того как Вы написали HTML-страницу, её содержимое фиксируется. Фиксированное содержимое пересылается с сервера клиенту, когда клиент выполняет доступ к странице по её URL. С помощью JavaScript Вы можете создавать HTML-страницы, которые могут изменяться вслед за изменением данных или в ответ на действия пользователя. На Рисунке 3.1 показана базовая процедура создания и запуска приложения JavaScript.
Для создания приложения JavaScript Вы должны выполнить следующие шаги:
<server>
и </server>
) и переписать приложения так, чтобы максимально
использовать функции. Это значительно увеличит скорость работы приложения. .web
). (См. "Компиляция
Приложения".) Скомпилировать исходные файлы Java в файлы классов. http://
server.domain
/world/
.
Вы можете выполнить также отладку приложения, щёлкнув Debug в Менеджере
Приложений. (См. "Отладка Приложения".)Прежде чем Вы сможете разрабатывать приложения на JavaScript, Вам нужно будет запустить машину выполнения на сервере и, возможно, защитить Менеджер Приложений JavaScript от неавторизованного доступа. Дополнительно см. разделы "Информация Конфигурации" и Глава 2, "Введение.".
Прежде учиться создавать приложения JavaScript, Вы должны ознакомиться с Менеджером Приложений JavaScript. Вы можете использовать Application Manager для выполнения следующих задач:
Добавления нового JavaScript-приложения.
Модификации любых атрибутов уже установленного приложения.
Останова, старта и рестарта установленного приложения.
Запуска и отладки активного приложения.
Удаления установленного приложения.
Application Manager сам является приложением на JavaScript, демонстрирующим мощь и гибкость JavaScript. Вы запускаете JavaScript Application Manager по следующему URL в Navigator'е:
http://
server.domain
/appmgr
В ответ Application Manager отобразит страницу, показанную на Рисунке 3.2 для Netscape Enterprise Server 3.x и на Рисунке 3.3 - для Enterprise Server 4.0.
Application Manager отображает в прокручиваемом левом фрэйме (кадре/окне) все приложения JavaScript, установленные в данный момент на сервере. Выберите нужное приложение, щёлкнув на его имени в прокручиваемом списке.
Правый фрэйм отображает для выбранного приложения следующую информацию:
database
client
Описание этих полей см. в разделе "Инсталяция Нового Приложения".
Для добавления нового приложения щёлкните вверху кнопку Add Application.
Щёлкните Configure (в Enteprise Server 3.x) или Preferences (в Enterprise Server 4.0)
для конфигурирования установок по умолчанию для Application Manager.
Щёлкните Documentation, чтобы перейти на страницу технической поддержки Netscape JavaScript,
где имеются ссылки на разнообразную документацию. Щёлкните Help для получения
дополнительных инструкций об использовании Application Manager.
Первым шагом на пути создания приложения JavaScript будет создание и редактирование исходных файлов с кодом. Файл с расширением web для приложения JavaScript может быть исходным файлом одного из двух видов:
html
или .htm
.js
.Если Вы используете JavaScript в HTML-файле, Вы обязаны следовать правилам, указанным в разделе "Внедрение JavaScript в HTML".
Не используйте никаких специальных тэгов в файлах .js
; компилятор
приложений JavaScript на сервере и интерпретатор JavaScript на стороне клиента
считают такие файлы написанными на JavaScript. Поскольку HTML-файл используется
и на клиенте, и на сервере, один файл JavaScript обязан использоваться либо на
сервере, либо на клиенте; он не может использоваться и там, и там. Следовательно,
файл JavaScript может содержать либо клиентский, либо серверный JavaScript, но
один файл не может содержать и клиентские, и серверные объекты или функции.
Компилятор приложений JavaScript компилирует и связывает HTML- и JavaScript-файлы,
содержащие серверный JavaScript, в единый платформонезависимый байт-код web-файла
(имеющего расширение .web)
, как описано в разделе
"Компиляция Приложения".
Вы инсталируете web-файл, запускаемый машиной выполнения JavaScript, как описано в разделе "Инсталяция Нового Приложения".
Вы компилируете приложение JavaScript, используя компилятор приложений JavaScript, jsac
.
Компилятор создаёт web-файл из исходных файлов на языках HTML и JavaScript.
Netscape Enterprise Server 4.0 поддерживает компилятор JavaScript Application Compiler версии 24.13. Заметьте, что приложение, скомпилированное с использованием "=" в качестве операции Equal, не сможет использовать новый компилятор. Вы обязаны использовать "==" в качестве операции Equal (==).
Для облегчения доступа к компилятору Вам может понадобиться добавить директорию,
в которой он установлен, в переменную окружения PATH
. От том, как
это сделать, см. раздел "Локализация Компилятора"
в Главе 3, "Технология Разработки Приложений JavaScript."
Вам нужно также добавить директорию <server_root>/bin/httpd/lib
в LD_LIBRARY_PATH
(или LIBPATH
, или SHLIB_PATH
на Unix-платформах), либо <server_root>/bin/https/bin
в PATH
на Windows NT-платформах.
Вам нужно компилировать только те страницы, которые содержат серверный JavaScript или оба - клиентский и серверный JavaScript. Вам не нужно компилировать страницы, содержащие только клиентский JavaScript. Вы, конечно, можете сделать это, но скорость работы будет выше, если Вы оставите страницы с клиентским JavaScript без компиляции.
Компилятор доступен из командной строки. Используйте следующий синтаксис командной строки для компиляции и компоновки приложений JavaScript на сервере:
jsac [-h] [-c] [-v] [-d] [-l]
[-o outfile.web]
[-i inputFile]
[-p pathName]
[-f includeFile]
[-r errorFile]
[-a 1.2]
script1.html [...scriptN.html] [funct1.js ... functN.js]
Элементы в квадратных скобках не обязательны. Синтаксис дан на нескольких
строках для большей ясности. Файлы script
N
.html
и funct
N
.js
это входные файлы для
компилятора. Должен быть как минимум один HTML-файл. По умолчанию файлы HTML и JavaScript
ищутся в текущей директории. Специфицируемые Вами файлы обязаны быть JavaScript-
или HTML-файлы; Вы не можете специфицировать другие файлы, такие, например, как .GIF.
На всех платформах Вы можете использовать тире (-
) или слэш (/
)
для обозначения опций командной строки. То есть, следующие строки эквивалентны:
jsac -h
jsac /h
Заметьте, что, поскольку слэш обозначает опции командной строки, входной файл не может начинаться с символа / (слэш) для обозначения абсолютного пути (в Unix). Таким образом, следующий вызов неверен:
jsac -o myapp.web /usr/vpg/myapp.html
Это ограничение не распространяется на путь, который Вы предоставляете как
аргумент командной строки; только на входные файлы. В NT Вы можете использовать
обратный слэш (\
) для указания абсолютного пути во входном файле,
как в следующем вызове:
jsac -o myapp.web \usr\vpg\myapp.html
В Unix Вы обязаны использовать опцию командной строки -i
для
специфицирования абсолютного пути, как описано ниже.
Доступны следующие опции командной строки:
-h
: Помощь по синтаксису компилятора. Если Вы используете эту опцию,
не указывайте никаких других.-c
: Синтаксис только проверяется; web-файл не генерируется. Если
эта опция указана, опцию -o
указывать не нужно.-v
: (Verbose) Выводит информацию о работе компилятора.-d
: Выводит генерируемое JavaScript-содержимое.-l
: Специфицирует набор символов, используемый при компиляции (такой
как iso-8859-1
, x-sjis
или euc-kr
)-o
outfile
: Создаёт web-файл в формате
байт-кода с именем outfile.web
. Если эта опция не указана,
компилятор не генерирует web-файл. (Опускайте эту опцию только тогда, когда
используете опцию -c
для проверки синтаксиса или -h
для получения помощи.)-i
inputFile
: Позволяет специфицировать входной
файл с полным путём, а не с относительным путём. Для этой опции может быть
задано только имя файла. Если Вам нужно специфицировать несколько файлов с
полным путём, используйте опцию -f
.-p
pathName
: Специфицирует корневую директорию
для всех относительных путей, используемых при компиляции. (Используйте перед
опцией -f
.) Для этой опции можно указать только один путь.-f
includeFile
: Специфицирует файл, который
является списком входных файлов, что позволяет обойти ограничение на
количество символов, вводимых в командной строке. Для этой опции можно указать
только одно имя файла. Файлы в списке входных файлов в
includeFile
разделены пробелами. Если имя файла содержит пробел, Вы обязаны заключить имя
файла в двойные кавычки.-r
errorFile
: Перенаправляет стандартный
вывод (включая сообщения об ошибках) в специфицированный файл. В этой опции
можно указать только одно имя файла.-a 1.2
: Устанавливает версию в 1.2 для обеспечения обратной совместимости.
Например, следующая команда компилирует и компонует две HTML -страницы с
JavaScript, main.html
и hello.html
, и файл серверного JavaScript, support.js
,
создавая двоичный исполняемый файл с именем myapp.web
. Кроме того,
в процессе компиляции компилятор печатает в командной строке информацию о ходе компиляции.
jsac -v -o myapp.web main.html hello.html support.js
В следующем примере команда компилирует файлы, перечисленные в файле looksee.txt
,
в бинарный исполняемый файл под названием looksee.web
:
jsac -f looksee.txt -o looksee.web
Здесь looksee.txt
может содержать:
looksee1.html
looksee2.html
\myapps\jsplace\common.js
looksee3.html
Вы не можете запустить приложение, и клиенты не могут получить к нему доступ, пока Вы не установите его. Инсталяция приложения идентифицирует его на сервере. После установки Вы можете перестроить/rebuild и запустить приложение любое число раз. Приложение нужно переустановить только тогда, когда Вы его удалили. Вы можете установить до 120 приложений JavaScript на один сервер.
Прежде чем устанавливать, Вы обязаны поместить все файлы, имеющие отношение к приложению, в соответствующую директорию путём публикации файлов. Иначе Вы получите сообщение об ошибке при попытке инсталяции. Из соображений безопасности, Вы можете не захотеть публиковать свои исходные файлы JavaScript на сервер публикации. См. в разделе "URL Приложения" об ограничениях на размещение файлов.
Чтобы установить новое приложение с помощью Application Manager, щёлкните Add Application. В ответ Application Manager выведет в правом фрэйме форму, показанную на Рисунке 3.4. (Цветовая схема в Enterprise Server 4.0 будет другой).
Заполните поля в форме Add Application следующим образом:
http://
server.domain
/world
.
Это необходимое поле, и имя, которое Вы запишете, обязано отличаться от имён
других приложений на данном сервере. См. "URL Приложения".c:\nshome
,
путь web-файла для приложения Hello World будет
c:\nshome\js\samples\world\hello.web
.
index.html
для стандартного URL.request
или client
. Это необязательное поле.database
.
Код JavaScript может переопределить это значение путём вызова метода database.connect
.client
. Это
может быть клиентская "кука"/cookie, URL клиента, IP сервера, серверная кука
или URL сервера. См. "Техника Работы с Объектом client".После того как Вы предоставили всю требуемую информацию, нажмите Enter для инсталяции приложения, Reset для очистки всех полей или Cancel для отмены операции.
Вы обязаны остановить и рестартовать сервер после добавления или изменения внешних библиотек. Вы можете рестартовать сервер из Server Manager; см. руководство администратора сервера.
При установке приложения Вы обязаны задать ему имя. Это имя определяет базовый URL
приложения: тот URL, который клиенты используют для доступа к странице по
умолчанию данного приложения JavaScript.
Базовый URL приложения имеет форму:
http://server.domain/appName
Здесь server это имя HTTP-сервера, domain это домен Internet (включая субдомены), а appName это имя приложения, которое Вы ему дали при инсталяции. Отдельные страницы приложения достигаются через URL приложения в форме:
http://server.domain/appName/page.html
Здесь page это имя страницы приложения. Например, если Ваш сервер
называется coyote
, а домен имеет имя royalairways.com
,
базовый URL для приложения hangman
будет:
http://coyote.royalairways.com/hangman
Если клиент запрашивает этот URL, сервер генерирует HTML для страницы по умолчанию приложения и высылает её клиенту. URL приложения страницы winning в этом приложении:
http://coyote.royalairways.com/hangman/youwon.html
Прежде чем инсталировать приложение, убедитесь, что выбранное Вами имя
приложения не присвоило существующий URL на Вашем сервере. Машина выполнения JavaScript
направляет все клиентские запросы по URL, соответствующим URL приложения, в
директорию, специфицированную для web-файла. Таким образом можно "обмануть"
нормальную корневую директорию.
Например, предположим, клиент запрашивает URL, который начинается с префикса
из предыдущего примера:
http://coyote.royalairways.com/hangman
В этом случае машина выполнения на сервере ищет документ в директории samples\hangman
,
а не в нормальной корневой директории сервера. Сервер обслуживает в этой
директории страницы, которые не компилируются в приложение.
Вы можете поместить Ваши исходные (неоткомпилированные) файлы серверного JavaScript в ту же директорию, что и web-файл; однако Вы должны делать это только для отладки. Если Вы публикуете Ваше приложение для общего пользования, Вы, по соображениям безопасности, не должны публиковать неоткомпилированные файлы серверного JavaScript.
При инсталяции приложения Вам может понадобиться ограничить к нему доступ пользователей, особенно если приложение предоставляет доступ к закрытой информации.
Если Вы работаете на рабочем сервере за брандмауэром/firewall, то можете не беспокоиться об ограничении доступа в процессе разработки приложения. Удобно не иметь ограничений доступа в процессе разработки приложения, так как Вы можете быть уверены в защищённости приложения от атак за стеной firewall. Если Вы используете учебные данные на стадии разработки, то риск ещё меньше. Однако, если Вы публикуете Ваше приложение, Вы должны быть уверены, что любой посетитель Вашего приложения может им пользоваться.
Если Вы закончили разработку и готовы опубликовать приложение, необходимо предусмотреть его защиту. Вы можете ограничить доступ, применив к приложению стиль конфигурации сервера. О стилях конфигурации см. руководство администратора Вашего web-сервера.
Чтобы модифицировать приложение, выберите имя приложения в списке приложений и щёлкните Modify.
Вы можете изменить любое поле, определённое при инсталяции приложения, за исключением имени приложения. Чтобы изменить имя приложения, необходимо удалить приложение, а затем реинсталировать его.
Если Вы модифицируете поля остановленного приложения, Application Manager автоматически запускает его. Если Вы модифицируете поля активного приложения, Application Manager автоматически останавливает и запускает его.
Чтобы удалить приложение, выберите его в списке приложений и щёлкните Remove. Application Manager удалит приложение, и оно больше не сможет быть запущено на сервере. Клиенты не смогут больше получить доступ к приложению. Если Вы удалите приложение, а после этого захотите запустить его, нужно будет инсталировать его заново.
Хотя клиенты потеряют доступ к приложению, удаление его с помощью Application Manager не удаляет физически файлы приложения с сервера. Если Вы хотите полностью удалить файлы, сделайте это вручную.
После инсталяции приложения его можно запустить на выполнение. Выберите приложение в списке приложений и щёлкните Start. Если приложение запущено успешно, его статус изменяется со Stopped на Active.
Вы можете запустить приложение, загрузив его URL:
http://server.domain/appmgr/control.html?name=appName&cmd=start
Здесь appName это имя приложения. Вы не можете изменить этот URL, если не имеете привилегий доступа к Application Manager.
Чтобы остановить приложение и сделать его таким образом недоступным для пользователей, выберите имя приложения в списке приложений и щёлкните Stop. Статус приложения изменится на Stopped, и клиенты не смогут запускать его. Вы обязаны остановить приложение, если хотите переместить web-файл или обновить приложение с рабочего сервера на конечном сервере публикаций.
Вы можете также остановить приложение, загрузив следующий URL:
http://server.domain/appmgr/control.html?name=appName&cmd=stop
Здесь appName это имя приложения. Вы не можете изменить этот URL, если не имеете привилегий доступа к Application Manager.
Вы обязаны рестартовать приложение каждый раз после его перестроения/rebuild. Чтобы рестартовать активное приложение, выберите его в списке приложений и щёлкните Restart. Фактически рестарт реинсталирует приложение; программа ищет специфицированный web-файл. Если верного web-файла нет, Application Manager генерирует ошибку.
Вы можете также рестартовать приложение, загрузив следующий URL:
http://server.domain/appmgr/control.html?name=appName&cmd=restart
Здесь appName это имя приложения. Вы не можете изменить этот URL, если не имеете привилегий доступа к Application Manager.
После инсталяции и компиляции приложения Вы можете запустить его двумя способами:
Сервер сгенерирует HTML для специфицированной страницы и вышлет его клиенту.
Для отладки приложения сделайте следующее:
Можно использовать функцию debug
для вывода отладочной информации,
как описано в разделе "Использование Функции debug".
После запуска отладки приложения JavaScript Вы не сможете остановить или рестартовать его. В этих случаях Application Manager выдаст сообщение "Trace is active/Трассировка включена". Если это произойдёт, сделайте следующее:
Теперь Вы можете остановить и рестартовать это приложение.
Для отладки приложения выберите его в списке приложений и щёлкните Debug. Application Manager откроет новое окно Navigator, в котором будет запущено приложение. Запустится также утилита трассировки в отдельном фрэйме, в окне, содержащем приложение, или вообще в другом окне. (Вы можете определить появление отладочного окна при конфигурировании установок по умолчанию для Application Manager, как описано в разделе "Конфигурирование Установок по Умолчанию").
Утилита трассировки выведет следующую отладочную информацию:
request
и client
до и
после генерации HTML для страницыproject
и server
На Рисунке 3.5 показано то, что Вы можете увидеть при отладке приложения Hangman.
Вместо Application Manager вам может больше подойти использование URL отладки приложения. Для отображения утилиты трассировки приложения в отдельном окне введите следующий URL:
http://server.domain/appmgr/trace.html?name=appName
Здесь appName это имя приложения. Для отображения утилиты трассировки в том же самом окне, что и окно приложения (но в отдельном кадре/фрэйме), введите URL в форме:
http://server.domain/appmgr/debug.html?name=appName
Вы не сможете воспользоваться двумя вышеуказанными URL, если не имеете прав для запуска Application Manager. Для удобства можно сделать закладку на URL отладки.
Вы можете использовать функцию debug
в Вашем приложении JavaScript
для помощи при отслеживании/трассировке проблем в приложении. Функция debug
отображает значения для утилиты трассировки. Например, следующий оператор
выводит значение свойства guess
объекта request
в окне
трассировки вместе с некоторым идентифицирующим текстом:
debug ("Current Guess is ", request.guess);
После завершения разработки и тестирования приложения Вы можете опубликовать его и сделать доступным для предполагаемых пользователей. Это требует выполнения двух шагов:
Вы должны скопировать web-файл приложения на сервер публикаций вместе с изображениям и неоткомпилированными файлами HTML и JavaScript, которые необходимы для работы приложения. О публикации файлов приложений см. дополнительно Netshare and Web Publisher User's Guide.
В целом, по соображениям безопасности, Вы не должны публиковать файлы с исходным кодом.
В зависимости от приложения, Вы можете ограничить доступ для определённых
пользователей или групп пользователей. В некоторых случаях любой пользователь
может запускать приложение; тогда ограничений можно вообще не применять. Если
приложение показывает специальную информацию или даёт доступ к файловой системе
сервера, Вы должны предоставить доступ только авторизованным пользователям,
имеющим соответствующие имена и пароли для входа.
Вы ограничиваете доступ к приложению, применяя стиль конфигурации сервера из Вашего Server Manager. Об использовании Server Manager и стилей конфигурации см. Enterprise Server 4.0 Administrator's Guide.
В этом разделе показано, как изменять установки по умолчанию для Application Manager. Кроме того, говорится о формате файла, в котором Application Manager хранит информацию.
Чтобы конфигурировать установки по умолчанию для Application Manager, щёлкните Configure (в Enteprise Server 3.x) или Preferences (в Enterprise Server 4.0) в верхнем фрэйме Application Manager'а. В ответ Application Manager отобразит форму, показанную на Рисунке 3.6.
Вы можете специфицировать следующие значения по умолчанию:
database
.client
.Если Вы устанавливаете новое приложение, используются значения полей по умолчанию в качестве начальных значений.
Дополнительно Вы можете специфицировать следующие установки:
Application Manager является удобным интерфейсом для модифицирования файла
конфигурации $NSHOME\https-
serverID
\config\jsa.conf
,
где $NSHOME
это директория, в которой установлен сервер, а serverID
это идентификатор ID сервера. В случае фатальной ошибки Вам может потребоваться
отредактировать этот файл самостоятельно. Вообще-то это не рекомендуется, но
здесь мы даём информацию для такого случая.
Каждая строка файла jsa.conf
соответствует приложению. Первый
элемент каждой строки это имя приложения. Остальные элементы имеют формат name=value
,
где name
это имя поля установки, а value
это значение
поля. Возможные значения name
:
uri
: часть - имя приложения в базовом URL приложенияobject
: путь к web-файлу приложенияhome
: страница по умолчаниюstart
: начальная страница приложенияmaxdbconnect
: максимальное количество соединений с БД, разрешённое
для предопределённого объекта database
library
: пути к внешним библиотекам, разделённые запятыми или
точкой с запятойclient-mode
: техника обслуживания объекта client
Файл jsa.conf
имеет ограничение размера в 1024 строки, каждая
строка может иметь не более 1024 символов. Если поля, введённые в Application Manager,
превосходят этот лимит, строка усекается. Это ведёт обычно к потере последнего
элемента, файлов внешних библиотек. Если это произошло, уменьшите количество
используемых внешних библиотек и добавьте библиотеки в другие приложения.
Поскольку установленные библиотеки доступны всем приложениям, любое приложение может использовать их.
Строка, начинающаяся с #, обозначает комментарий. Такая строка игнорируется. Можно также включать в файл пустые строки.
Не записывайте несколько строк, специфицируя одно и то же имя приложения. Это вызовет ошибки в работе Application Manager'а.
Дата последнего обновления: 29 сентября 1999 г.
╘ Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.
Глава 2. Введение
В этой главе дан обзор типичного приложения, написанного на серверном JavaScript, и показано, как настроить систему для разработки серверных приложений.
Глава 3. Технология Разработки Приложений JavaScript
В этой главе описан процесс разработки Вашего приложения: как использовать компилятор приложений JavaScript и менеждер Application Manager серверов Netscape для установки или отладки Вашего приложения. Об использовании только клиентского JavaScript см. книгу Клиентский JavaScript. Руководство .
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.