Глава JSP.8

JSP-Контейнер


В этой главе рассматриваются контракты между JSP-контейнером и JSP-страницей.

Протокол прекомпиляции (см. Раздел JSP.8.4) также представлен здесь.


Информация, представленная здесь, не зависит от Языка Скриптинга, используемого на JSP-странице.

Глава JSP.6 описывает информацию для случая, когда атрибут language директивы page установлен в "java".
 

Классы реализации JSP-страницы должны использовать классы JspFactory и PageContext для получения преимуществ платформозависимых реализаций.

JSP.8.1 Модель JSP-Страницы

JSP-страница представлена на этапе выполнения объектом реализации JSP-страницы и выполняется JSP-контейнером. Объект реализации JSP-страницы - это servlet/сервлет. JSP-контейнер направляет запросы от клиента объекту реализации JSP-страницы и ответы объекта реализации JSP-страницы - клиенту.


JSP-страница описывает создание объекта response из объекта request для данного протокола, возможно, создавая и/или используя в ходе этого процесса некоторые другие объекты. JSP-страница может также указывать, как должны обрабатываться некоторые события.

В JSP 1.2 только события init и destroy являются допустимыми событиями.

JSP.8.1.1 Протокол, Видимый Web-Сервером

JSP-контейнер локализует соответствующий экземпляр класса реализации JSP-страницы и направляет ему запросы, используя протокол Servlet. JSP-контейнеру может потребоваться создать такой класс динамически из исходной JSP-страницы до направления ей объектов request и response.


Класс Servlet определяет контракт между JSP-контейнером и классом реализации JSP-страницы.
Если используется протокол HTTP, контракт описывается классом HttpServlet. Большинство JSP-страниц используют протокол HTTP, но другие протоколы также разрешены  данной спецификацией.


JSP-контейнер автоматически делает несколько серверных/server-side объектов доступными объекту реализации JSP-страницы. См. Раздел JSP.2.8.3.

JSP.8.1.1.1 Протокол, Видимый Автором JSP-Страницы

JSP-спецификация определяет контракт между JSP-контейнером и автором JSP-страницы. Этот контракт определяет обязательства, которые автор может установить для акций, описанных на JSP-странице.
Главной частью этого контракта является метод _jspService(), который генерируется автоматически JSP-контейнером из JSP-страницы.

Детальная информация об этом контракте дана в Главе JSP.6. Контракт описывает также, как автор JSP может обозначить, какие акции будут предприниматься, когда будут вызываться методы реализации страницы init() и destroy(). В спецификации JSP 1.2 это делается через определение методов с именами jspInit() и jspDestroy() в объявлении элемента скриптинга в JSP-странице.

Метод jspInit(), если он имеется, будет вызываться для подготовки страницы перед направлением первого запроса. Аналогично, JSP-контейнер может затребовать ресурсы, используемые JSP-страницей, если запрос не обслуживается JSP-страницей, через вызов её метода jspDestroy(), если он имеется.


Автор JSP-страниц не может (пере)определять методы Servlet через объявление элемента скриптинга.
 

JSP-спецификация резервирует имена методов и переменных, начинающиеся с jsp, _jsp, jspx и _jspx, в любом сочетании регистров.

JSP.8.1.1.2 Интерфейс HttpJspPage

Выполнению контракта между JSP-контейнером и автором JSP-страницы помогает требование о том, что класс Servlet, соответствующий JSP-странице, обязан реализовывать интерфейс HttpJspPage (или интерфейс JspPage, если протокол - не HTTP).

Рисунок J2EE.8.1 Контракт между JSP-Страницей и JSP-Контейнером.
JSP-Контейнер
JSP-Страница

На Рисунке J2EE.8.1 показаны включённые контракты. Теперь мы рассмотрим этот процесс более детально.

JSP.8.2 Класс Реализации JSP-Страницы/Page Implementation Class

JSP-контейнер создаёт класс реализации JSP-страницы для каждой JSP-страницы. Имя класса реализации JSP-страницы зависит от особенностей реализации. Объект реализации JSP-страницы принадлежит к зависящему от реализации именованному архиву. Этот архив может отличаться от одной JSP-страницы к другой. Неименованный архив не должен использоваться без явного "импортирования" класса.


JSP-контейнер может создавать для JSP-страницы класс реализации, либо суперкласс может быть предоставлен автором JSP-страницы с помощью атрибута extends директивы page.
 

Механизм extends предназначен для опытных пользователей. Он должен использоваться предельно осторожно, так как он ограничивает возможность принятия решений JSP-контейнером. Он может, к примеру, свести на нет усилия по повышению производительности.


Класс реализации JSP-страницы будет реализовывать Servlet, а протокол Servlet будет использоваться для направления запросов классу.


Класс реализации JSP-страницы может зависеть от поддержки других классов. Если класс реализации JSP-страницы упакован в WAR, все связанные классы должны будут быть включены, так что пакет будет переносим на все JSP-контейнеры.


Автор JSP-страницы пишет JSP-страницу, ожидая, что клиент и сервер будут взаимодействовать по определённому протоколу. JSP-контейнер обязан гарантировать, штаа запросы и ответы для страницы используют нужный протокол. Большинство JSP-страниц используют протокол HTTP, и их классы реализаций обязаны реализовать интерфейс HttpJspPage, который расширяет JspPage. Если это не HTTP-протокол, тогда класс реализует интерфейс, расширяющий JspPage.

JSP.8.2.1 Контракты API

Контракт между JSP-контейнером и Java-классом, реализующим JSP-страницу, соответствует интерфейсу Servlet. См. детали в спецификации Servlet 2.3.


Ответственность за выполнение этого контракта лежит на реализации JSP-контейнера, если JSP-страница не использует атрибут extends директивы jsp.

Если атрибут extends директивы jsp используется, автор JSP-страниц обязан гарантировать, что суперкласс, заданный в атрибуте extends, поддерживает этот контракт.

Таблица JSP.8-1  Как JSP-Контейнер Обрабатывает JSP-Страницы
Комментарии Методы, вызываемые JSP-Контейнером
Метод может по выбору/optionally быть определён в JSP-странице.
Метод вызывается при инициализации JSP-страницы.
Если метод вызывается, доступны все методы сервлета, включая getServletConfig().
void jspInit()
Метод по выбору определяется в JSP-странице.
Метод вызывается при уничтожении страницы.
void jspDestroy()
Метод не может быть определён в JSP-странице.
JSP-контейнер автоматически генерирует этот метод, базируясь на содержимом JSP-страницы.
Метод вызывается при каждом клиентском запросе.
void
_jspService(<ServletRequestSubtype>, <ServletResponseSubtype>) throws IOException,
ServletException

JSP.8.2.2 Параметры Запроса и Ответа

Как видно из Таблицы JSP.8-1, методы контракта между JSP-контейнером и JSP-страницей требуют наличия параметров запроса и ответа.


Формальным типом параметра запроса (который в этой спецификации называется <ServletRequestSubtype>) является интерфейс, расширяющий javax.servlet.ServletRequest. Этот интерфейс обязан определять зависящий от используемого протокола запроса контракт между JSP-контейнером и классом, реализующим JSP-страницу.


Аналогично и формальный тип параметра ответа (называемый в этой спецификации <ServletResponseSubtype>) является интерфейсом, расширяющим javax.servlet.Servlet-Response. Этот интерфейс обязан определять зависящий от используемого протокола ответа контракт между JSP-контейнером и классом, реализующим JSP-страницу. Интерфейсы запроса и ответа вместе описывают зависящий от протокола контракт между JSP-контейнером и классом, реализующим эту JSP-страницу.


HTTP-контракт определяется интерфейсами javax.servlet.http.HttpServletRequest и javax.servlet.http.HttpServletResponse. Интерфейс JspPage ссылается на эти методы, но не может синтаксически описать методы, вызывающие подтипы Servlet(Request,Response). Однако интерфейсы для специфических протоколов, которые расширяют JspPage, могут делать это, так же, как HttpJspPage описывает их для протокола HTTP.


JSP-контейнеры, соответствующие этой спецификации (классами реализации JSP-страницы и работой JSP-контейнера), обязаны реализовывать интерфейсы запроса и ответа (request и response) для HTTP-протокола, как описано в этом разделе.

JSP.8.2.3 Опущение Атрибута extends

Если атрибут extends директивы page (см. Раздел 2.10.1) в JSP-странице не используется, JSP-контейнер может генерировать любой класс, удовлетворяющий контракту, описанному в Таблице JSP.8-1, если он трансформирует JSP-страницу.
 

В следующих примерах Пример Кода 8.1 иллюстрирует общий/родовой HTTP-суперкласс, названный ExampleHttpSuper. В Примере Кода 8.2 показан подкласс, названный _jsp1344, который расширяет ExampleHttpSuper и является классом, сгенерированным из JSP-страницы.

Используя отдельные классы _jsp1344 и ExampleHttpSuper, транслятор JSP-страницы не нуждается в поиске информации, содержит ли JSP-страница объявления с jspInit() или jspDestroy(). Это значительно упрощает реализацию.

Пример Кода 8.1 Общий HTTP-Суперкласс


imports javax.servlet.*;

imports javax.servlet.http.*;

imports javax.servlet.jsp.*;

/**

* Пример суперкласса для HTTP JSP-класса

*/

abstract class ExampleHttpSuper implements HttpJspPage {

private ServletConfig config;

final public void init(ServletConfig config) throws ServletException {

this.config = config;

jspInit();

public void jspInit() {

}

public void jspDestroy() {

}

}

final public ServletConfig getServletConfig() {

return config;

}

// Этот метод не является final, поэтому он может быть переопределён более точным методом

public String getServletInfo() {

return "Суперкласс для HTTP JSP"; // можно и получше?

}

final public void destroy() {

jspDestroy();

}

/**

* Точка входа в сервис.

*/

final public void service(ServletRequest req, ServletResponse res)

throws ServletException, IOException {

// количество отловленных исключений увеличится при наличии внутренней ошибки.

HttpServletRequest request = (HttpServletRequest) req;

HttpServletResponse response = (HttpServletResponse) res;

_jspService(request, resonse);

/**

* Абстрактный метод, предоставляемый JSP-процессором в подклассе,

* обязан быть определён в подклассе.

*/

abstract public void _jspService(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException;

}

Пример Кода 8.2 Java-Класс, Генерируемый из JSP-страницы


imports javax.servlet.*;

imports javax.servlet.http.*;

imports javax.servlet.jsp.*;

/**

* Пример класса, генерируемого для JSP.

*

* Имя класса непредсказуемо.

* Мы принимаем, что это пакет HTTP JSP (как чаще всего и бывает)

*/

class _jsp1344 extends ExampleHttpSuper {

// Следующий код вставлен непосредственно через объявления.

// Любые следующие части могут или могут не иметься;

// если они не определены здесь, будут использоваться

// методы суперкласса.

public void jspInit() {....}

public void jspDestroy() {....}

// Следующий метод генерируется автоматически

// JSP-процессором.

// Тело/body JSP-страницы

public void _jspService(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

// инициализация неявных переменных

HttpSession session = request.getSession();

ServletContext context =

getServletConfig().getServletContext();

// для этого примера мы принимаем, что директива буферизована

JSPBufferedWriter out = new

JSPBufferedWriter(response.getWriter());

// далее идёт код из скриптлетов, выражений и статического текста.

}

}

JSP.8.2.4 Использование Атрибута extends

Если автор JSP-страниц использует extends, генерируемый класс идентичен классу из Примера Кода 8.2, за исключением того, что имя этого класса это имя, специфицированное в атрибуте extends.


Контракт класса реализации JSP-страницы не изменяется. JSP-контейнер должен проверить (обычно через отражение/reflection), что предоставленный суперкласс:

JSP-контейнер может выдавать фатальную ошибку трансляции, если обнаружит, что предоставленный суперкласс не удовлетворяет этим требованиям, но большинство JSP-контейнеров эту проверку не будут выполнять.

JSP.8.3  Буферизация

JSP-контейнер буферизует данные (если директива jsp специфицирует это, используя атрибут buffer), когда они высылаются от сервера клиенту.

Headers/"Шапки" клиенту не высылаются, пока не вызван первый метод flush. Следовательно, ни одна из операций, имеющих отношение к шапкам, таких как методы setContentType, redirect или error, не является верной до тех пор, пока метод flush не начнёт выполняться и шапки не начнут высылаться.


Класс javax.servlet.jsp.JspWriter буферизует вывод и высылает его. Класс JspWriter используется в методе _jspService, как в следующем примере:


import javax.servlet.jsp.JspWriter;

static JspFactory _jspFactory = JspFactory.getDefaultFactory();

_jspService(<SRequest> request, <SResponse> response) {

// инициализация неявных переменных ...

PageContext pageContext = _jspFactory.createPageContext(

this,
request,
response,
false,
PageContext.DEFAULT_BUFFER,
false

);

JSPWriter out = pageContext.getOut();

// ....

// .... тело идёт здесь через "out"

// ....

out.flush();

}


Вы можете найти полный листинг javax.servlet.jsp.JspWriter в Главе JSP.9.


При включённой буферизации, Вы можете всё ещё использовать метод redirect скриптлета в файле .jsp, вызывая response.redirect(какой-то URL) напрямую.

JSP.8.4 Прекомпиляция

JSP-страница, использующая протокол HTTP, будет получать HTTP-запросы.

Контейнеры, соответствующие JSP 1.2, обязаны поддерживать простой протокол прекомпиляции, а также некоторые базовые зарезервированные имена параметров. Заметьте, что протокол прекомпиляции это понятие близкое, но не то же самое, что компиляция JSP-страницы в Servlet-класс (Приложение JSP.A).

JSP.8.4.1 Имена Параметров Запроса

Все имена параметров запроса, начинающиеся с префикса "jsp", зарезервированы спецификацией JSP и не должны использоваться иначе, чем установлено данной спецификацией.


Все JSP-страницы должны игнорировать (и не зависеть от) параметры, начинающиеся с "jsp_"

JSP.8.4.2 Протокол Прекомпиляции

Запрос к JSP-странице, содержащий параметр запроса с именем "jsp_precompile", является запросом прекомпиляции. Параметр "jsp_precompile" может не содержать значения или может содержать значения "true" или "false". В любом случае, такой запрос не должен отправляться JSP-странице.


Назначение запроса прекомпиляции в том, чтобы указать JSP-контейнеру, что нужно прекомпилировать (предварительно откомпилировать) JSP-страницу в класс реализации JSP-страницы. Это указание переправляется путём задания параметру значения "true", или не задавая никакого значения, но учтите, что запрос может быть проигнорирован.


Например:


1.   ?jsp_precompile

2. ?jsp_precompile="true"

3. ?jsp_precompile="false"

4. ?foobar="foobaz"&jsp_precompile="true"

5. ?foobar="foobaz"&jsp_precompile="false"


1, 2 и 4 - действуют; запросы не будут направлены странице.

3 и 5 - верны; запрос не будет направлен странице.


6. ?jsp_precompile="foo"  - это неверно, и будет генерироваться ошибка HTTP error; 500 (Server error).