В этой главе рассматриваются объекты службы 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. Все Права Зарезервированы.