User Tools

Site Tools


jsf-ru:faq:request_lifecycle

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
jsf-ru:faq:request_lifecycle [2006/01/14 20:47] – добавлено описание фазы Update Model Values slonopotamusjsf-ru:faq:request_lifecycle [2006/07/08 20:13] slonopotamus
Line 1: Line 1:
-[[jsf-ru:faq|Назад к FAQ]] 
-====== JSF Request Lifecycle ====== 
  
-===== В теории ===== 
- 
-Как нам подсказывает [[google>jsf request lifecycle|Google]] или та же спецификация по JSF, жизненный цикл JSF-запроса выглядит так: 
- 
-{{.:jsf-request-lifecycle.gif}} 
- 
-И всё бы хорошо, но. Эта модель **не работает**. А конкретно, она не работает тогда, когда к JSF-странице обращаются напрямую по ссылке, а не когда на неё переходят с другой JSF-страницы. В связи с этим, например, приходится жутко извращаться при [[with_jaas|интеграции JSF с JAAS]]. А когда обращаются напрямую, то **все** стадии, помеченные красным (application-level phases), на которых приложению проще всего взаимодействовать с JSF, **пропускаются**. Вас это радует? Меня это откровенно бесит. 
- 
-===== Описание стадий ===== 
- 
- 
- 
-==== Restore View ==== 
-На стадии //Restore View// реализация JSF должна выполнить следующие действия: 
-  * Проверить FacesContext для текущего запроса. Если он содержит UIViewRoot, то 
-    * Выставить ''UIViewRoot'''у локаль, полученную из метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/ExternalContext.html#getRequestLocale()|ExternalContext.html#getRequestLocale()]] для текущего запроса. 
-    * FIXME For each component in the component tree, determine if a ''ValueBinding'' for "binding" is present. If so, call the setValue() method on this ValueBinding, passing the component instance on which it was found. 
-    * Не предпринимать дальнейших действий на этой стадии. 
-  * Определить идентификатор вида (//view identifier//) для текущего запроса: 
-    * Если для ''FacesServlet'''а используется маппинг с префиксом (например, "''/faces/*''"), то в качестве ''viewId'' использовать то, что на месте ''*''((По-английски это называется //extra path information of the request URI//, но ХЗ как это перевести на русский.)). 
-    * Если используется маппинг с суффиксом (например, "''*.faces''"), то в качестве ''viewId'' берётся путь, по которому был сделан запрос, в котором суффикс заменяется на указанный в параметре инициализации контекста с именем равным //символической константе//((Объясните мне, что это за фигня.)) ''ViewHandler.DEFAULT_SUFFIX_NAME'' (если этот параметр не присутствует, использовать значение символической константы ''ViewHandler.DEFAULT_SUFFIX'' в качестве суффикса для замены.((Кажется я понял. Это тот суффикс, который задаётся в ''web.xml'' с помощью <code xml><context-param> 
-    <param-name>javax.faces.DEFAULT_SUFFIX</param-name> 
-    <param-value>.jspx</param-value> 
-  </context-param></code>)) 
-    * Если идентификатор вида определить не удаётся, бросить исключение((Какое - непонятно. Видимо любое.)). 
-  * Вызвать метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/ViewHandler.html#restoreView(javax.faces.context.FacesContext, java.lang.String)|ViewHandler#restoreView()]], передав ему полученный ''FacesContext'' и идентификатор вида в качестве аргументов и получив (возможно) ''UIViewRoot'' в качестве результата. 
-    * Если ''restoreView()'' вернул ''null'', то вызвать [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/ViewHandler.html#createView(javax.faces.context.FacesContext, java.lang.String)|ViewHandler#createView()]] и затем [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]]. 
-    * Если запрос не содержит ''POST''-данных или параметров в запросе (//query parameters//), то вызвать [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]]. 
-  * Сохранить созданный или восстановленный ''UIViewRoot'' в ''FacesContext'''е. 
-  * FIXME For each component in the component tree, determine if a ''ValueBinding'' for "binding" is present. If so, call the setValue() method on this ValueBinding, passing the component instance on which it was found. 
- 
-В конце этой стадии свойство ''viewRoot'' у ''FacesContext'''а для текущего запроса будет содержать сохранённое состояние вида, которое было в предыдущем запросе или новый вид, созданный с помощью [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/ViewHandler.html#createView(javax.faces.context.FacesContext, java.lang.String)|ViewHandler#createView()]] для указанного идентификатора вида. 
- 
- 
- 
-==== Apply Request Values ==== 
-Назначение этой стадии - дать каждому компоненту возможность обновить своё состояние на основании информации в текущем запросе (параметры, заголовки, куки и т.д.). 
- 
-В течение этой стадии реализация JSF должна вызвать метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIViewRoot.html#processDecodes(javax.faces.context.FacesContext)|UIViewRoot#processDecodes]]. По-нормальному это повлечёт за собой вызовы метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIComponent.html#processDecodes(javax.faces.context.FacesContext)|UIComponent#processDecodes]] для всех компонентов в дереве компонентов, как описано в явадоке к методу ''UIComponent.processDecodes()''. Для ''UIInput''-компонентов, преобразование данных должно быть проведено в соответствии с явадоком к [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIInput.html|UIInput]]. 
- 
-В процессе декодирования данных из запроса, компоненты могут производить хитрые действия, включающие в себя: 
-  * Компоненты, которые реализуют интерфейс [http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/ActionSource.html|ActionSource]] (например, [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UICommand.html|UICommand]]), которые определят, что они были активированы, добавят [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/event/ActionEvent.html|ActionEvent]] в очередь событий. Это событие будет вызвано в конце фазы [[#apply.request.values|Apply Request Values]] или в конце фазы [[#invoke.application|Invoke Application]], в зависимости от состояния свойства ''immediate'' у активированного компонента. 
-  * Компоненты, которые реализуют интерфейс [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/EditableValueHolder.html|EditableValueHolder]] (например, [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIInput.html|UIInput]]), у которых свойство ''immediate'' выставлено в ''true'', вызовут преобразование и валидацию (включая потенциально запуск событий [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/event/ValueChangeEvent.html|ValueChangeEvent]], которые по-нормальному происходят в фазе [[#process.validations|Process Validations]], вместо этого будут вызваны в фазе [[#apply.request.values|Apply Request Values]]. 
- 
-В конце этой фазы все компоненты, которые реализуют интерфейс [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/EditableValueHolder.html|EditableValueHolder]], будут обновлены в соответствии с данными, переданными в запросе (или с достаточным количеством данных чтобы вопроизвести некорректный ввод, если были ошибки преобразования). В дополнение к этому, для этих компонентов, у которых свойство ''immediate'' выставлено в ''true'', будут выполнены преобразование и валидация. Преобразования и валидации, которые не удались, с помощью метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#addMessage(java.lang.String, javax.faces.application.FacesMessage)|FacesContext#addMessage]] могут сигнализировать об ошибках для соответствующих компонентов и их свойство ''valid'' будет выставлено в ''false''((Вопрос: кем выставлено? Самим компонентом или реализацией JSF?)). 
- 
-Если какой-либо из вызванных методов ''decode()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#responseComplete()|FacesContext#responseComplete()]], то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов ''decode()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]], то управление должно немедленно перейти к фазе [[#render.response|Render Response]]. Иначе управление должно перейти к фазе [[#process.validations|Process Validations]]. 
- 
- 
- 
-==== Process Validations ==== 
-В процессе создания вида (''view'') для текущего запроса, к компонентам могут быть добавлены [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/validator/Validator.html|валидаторы]]. Помимо этого, компоненты могут сами реализовывать логику валидации в их методах ''validate()''((Найти явадок.))((Внимание! В следующей версии JSF этот метод будет помечен как ''deprecated'', поэтому избегайте его использования.)). 
- 
-На стадии [[#process.validations|Process Validations]] жизненного цикла запроса реализация JSF должна вызвать метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIViewRoot.html#processValidators(javax.faces.context.FacesContext)|UIViewRoot#processValidators()]]. По-нормальному это повлечёт за собой рекурсивный вызов метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIComponent.html#processValidators(javax.faces.context.FacesContext)|UIComponent#processValidators()]] для каждого компонента в дереве компонентов, как написано в документации к этому методу. Обратите внимание, что компоненты, реализующие [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/EditableValueHolder.html|EditableValueHolder]], у которых свойство ''immediate'' выставлено в ''true'', уже выполнили свою валидацию на стадии [[#apply.request.values|Apply Request Values]]. 
- 
-В процессе валидации, события могут добавляться в очередь компонентами и/или валидаторами, у которых вызывается метод ''validate()''. После этого они (события) будут распространены заинтересованным слушателям событий. 
- 
-В конце фазы все преобразования и настроенные валидации будут выполнены. Те преобразования и валидации, которые провалились((FIXME В оригинале ''failed''. Заменить на более русское слово.)), сигнализируют об этом посредством добавления сообщений вызывая метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#addMessage(java.lang.String, javax.faces.application.FacesMessage)|FacesContext#addMessage()]] у контекста для текущего запроса, и их свойство ''valid'' выставлено в ''false''((Опять же, кем? Реализацией JSF или самим компонентом?)). 
- 
-Если какой-либо из вызванных методов ''validate()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#responseComplete()|FacesContext#responseComplete()]], то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов ''validate()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]], то управление должно немедленно перейти к фазе [[#render.response|Render Response]]. Иначе управление должно перейти к фазе [[#update.model.values|Update Model Values]]. 
- 
- 
-==== Update Model Values ==== 
-Если обрабока запроса дошла до этой стадии, то предполагается, что запрос синтаксически и семантически((Не спрашивайте меня, что это означает.)) корректен, что локальное значение каждого компонента в дереве компонентов было обновлено, и что теперь можно обновлять данные в модели приложения для подготовки к выполнению событий приложения, которые находятся в очереди. 
- 
-В течение фазы [[#update.model.values|Update Model Values]], реализация JSF должна вызвать метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIViewRoot.html#processUpdates(javax.faces.context.FacesContext)|UIViewRoot#processUpdates()]]. По-нормальному это повлечёт за собой вызов метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIComponent.html#processUpdates(javax.faces.context.FacesContext)|UIComponent#processUpdates()]] для каждого компонента в дереве, как описано в документации к этому методу. Обновление модели выполняется в методе ''updateModel()''((Найти явадок.)) компонента. 
- 
-В процессе обновления модели, события могут добавляться в очередь компонентами, у которых вызывается метод ''updateModel()''. После этого они (события) будут распространены заинтересованным слушателям событий. В конце этой фазы у всех подходящих((В оригинале ''appropriate''.)) объектов данных будут обновлены их значения до значений, содержащихся в соответствующих компонентах, а локальные значения в компонентах будут очищены. 
- 
-Если какой-либо из вызванных методов ''updateModel()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#responseComplete()|FacesContext#responseComplete()]], то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов ''updateModel()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]], то управление должно немедленно перейти к фазе [[#render.response|Render Response]]. Иначе управление должно перейти к фазе [[#invoke.application|Invoke Application]]. 
- 
-==== Invoke Application ==== 
-FIXME 
-==== Render Response ==== 
-FIXME 
-===== На практике ===== 
-Делаем простенький [[.:PhaseListener]]: 
- 
-<code java> 
-import javax.faces.event.PhaseEvent; 
-import javax.faces.event.PhaseId; 
-import javax.faces.event.PhaseListener; 
- 
-public class MyPhaseListener implements PhaseListener { 
-  public void afterPhase(PhaseEvent event) { 
-  } 
- 
-  public void beforePhase(PhaseEvent event) { 
-    if (event.getPhaseId().equals(PhaseId.RESTORE_VIEW)) { 
-      System.err.println("----"); 
-    } 
-    System.err.println(event.getPhaseId().toString()); 
-  } 
- 
-  public PhaseId getPhaseId() { 
-    return PhaseId.ANY_PHASE; 
-  } 
-} 
-</code> 
- 
-И наблюдаем следующую картину: 
-  * Прямое обращение к странице: 
- 
-  RESTORE_VIEW 1 
-  RENDER_RESPONSE 6 
- 
-Вот то самое, о чём я говорил чуть выше. 
- 
-  * Сабмит формы с последующим редиректом: 
- 
-  RESTORE_VIEW 1 
-  APPLY_REQUEST_VALUES 2 
-  PROCESS_VALIDATIONS 3 
-  UPDATE_MODEL_VALUES 4 
-  INVOKE_APPLICATION 5 
- 
-  RESTORE_VIEW 1 
-  RENDER_RESPONSE 6 
- 
-Дамы и господа, обратите внимание. Запроса //два//. Причём оба какие-то неполноценные. В первом нет фазы ''RENDER_RESPONSE'', во втором всё как при простом обращении к странице. 
- 
-Вывод: если вы хотите, чтобы какой-то кусок кода вызывался при каждом обращении к JSF-приложению (например, вы хотите [[.:with_jaas|защитить]] какие-то страницы от просмотра, то необходимо либо писать фильтр, чтобы запросы к JSF-сервлету проходили сначала через него, либо писать [[.:PhaseListener]], который будет висеть на фазе ''RESTORE_VIEW''. 
jsf-ru/faq/request_lifecycle.txt · Last modified: 2019/06/12 16:08 by 127.0.0.1