JSF

Controller: Faces Servlet

View: XHTML(Facelets)

Model: Managed BeanまたはCDI

JSFのライフサイクル(他サイト参照)

★Struts vs. JSF

Struts: リクエスト/レスポンス指向 リクエストベースの開発

JSF: 画面指向 コンポーネントベースの開発

※他サイトを参照

★JSFベースのリッチUIコンポーネント

RichFaces

Demo http://livedemo.exadel.com/richfaces-demo/

OpenFaces

ICEfaces

MyFaces

ZK

vaadin

Oracle ADF Faces Rich Client

PrimeFaces

★メッセージの処理

void addMessage(String clientId, FaceMessage message);

FaceMessage(Severity severity, String summary, String detail)

public String doCreateBook() {

FacesContext ctx = FacesContext.getCurrentInstance();


if (...) {

ctx.addMessage("bookForm:title",

new FacesMessage(FacesMessage.SEVERITY_WARN, "Wrong title", "xxx"));

}


if (ctx.getMessageList().size() == 0) {

return null;

}


try {

...

ctx.addMessage(null,

new FacesMessage(FacesMessage.SEVERITY_INFO, "Book created", "xxx"));

} catch () {

ctx.addMessage(null,

new FacesMessage(

FacesMessage.SEVERITY_ERROR, "Fail", e.getMessage()));

}

return null;

}

★JSFの暗黙オブジェクト

★画面遷移

@ManagedBean

public class BookController {

@EJB

private BookEJB bookEJB;


public String doCreateBook() {

...

return "list.xhtml";

return "success"; // faces-config.xmlで定義

return null; //画面がリロード

}

}

faces-config.xml(グローバルリンクの場合)

<navigation-rule>

<from-view-id>*</from-view-id>

<navigation-case>

<from-outcome>logout</from-outcome>

<to-view-id>logout.xhtml</to-view-id>

</navigation-case>

</navigation-rule>

<navigation-rule>

<from-view-id>*</from-view-id>

<navigation-case>

<from-outcome>logout</from-outcome>

<to-view-id>logout_admin.xhtml</to-view-id>

<if>#{userController.isAdmin}</if>

</navigation-case>

</navigation-rule>

★フェーズと各処理

サンプル1

<h:form>

<h:messages/>

<h:inputText

value="#{hoge.text}"

valueChangeListener="#{hoge.valueChanged}"

validator="#{hoge.validate}"

converter="hogeConverter" />

<h:commandButton

actionListener="#{hoge.actionListen}"

action="#{hoge.action}"

value="LOGIN"/>

</h:form>

import javax.faces.application.FacesMessage;

import javax.faces.component.UIComponent;

import javax.faces.context.FacesContext;

import javax.faces.event.ActionEvent;

import javax.faces.event.ValueChangeEvent;

import javax.faces.validator.ValidatorException;

public class HogeBean {

public void validate(FacesContext context, UIComponent component, Object value){

System.out.println("<---validate");

String text = value.toString();

if(text.equals("xxx")){

throw new ValidatorException(new FacesMessage("..."));

}

}

public void valueChanged(ValueChangeEvent event){

System.out.println("<---valueChangeListener");

}

public void actionListen(ActionEvent e){

System.out.println("<---actionListener");

}

public String action(){

System.out.println("<---action");

return "success";

}

...

}

import javax.faces.component.UIComponent;

import javax.faces.context.FacesContext;

import javax.faces.convert.Converter;

public class HogeConverter implements Converter{

public Object getAsObject(FacesContext context, UIComponent component, String value) {

System.out.println("<---converter:getAsObject");

return value;

}

public String getAsString(FacesContext context, UIComponent component, Object value) {

System.out.println("<---converter:getAsString");

return value.toString();

}

}

出力

[BEFORE]RESTORE_VIEW 1

[AFTER]RESTORE_VIEW 1

[BEFORE]APPLY_REQUEST_VALUES 2

[AFTER]APPLY_REQUEST_VALUES 2

[BEFORE]PROCESS_VALIDATIONS 3

<---converter:getAsObject

<---validate

<---valueChangeListener

[AFTER]PROCESS_VALIDATIONS 3

[BEFORE]UPDATE_MODEL_VALUES 4

[AFTER]UPDATE_MODEL_VALUES 4

[BEFORE]INVOKE_APPLICATION 5

<---actionListener

<---action

[AFTER]INVOKE_APPLICATION 5

[BEFORE]RENDER_RESPONSE 6

<---converter:getAsString

[AFTER]RENDER_RESPONSE 6

サンプル2

JSF標準

<h:commandButton value="Btn1" action="#{myBean.action()}"

actionListener="#{myBean.actListener()}" >

<f:ajax execute="@this" render=":form:rlt"/>

<f:setPropertyActionListener

value="#{myBean.hogeG}" target="#{myBean.hogeS}"/>

</h:commandButton>

PrimeFaces

<p:commandButton value="Btn2" action="#{myBean.action()}"

process="@this" update=":form:rlt"

actionListener="#{myBean.actListener()}" >

<f:setPropertyActionListener

value="#{myBean.hogeG}" target="#{myBean.hogeS}"/>

</p:commandButton>

<h:outputText id="rlt" value="#{myBean.rlt}"/>

@Named

@RequestScoped

public class MyBean implements Serializable{

public void actListener(){

// step 1

}

public String getHogeG(){

// step 2

}

public void setHogeS(String hogeS){

// step 3

}

public void action(){

// step 4

}

public String getRlt(){

// step 5

}

}

★JSF 2.2の新機能

・HTML5のサポート

・Resource library contracts (Multi-Templating)

・Faces Flow

・ステートレス・モード

・ファイルアップロード機能の提供(Ajaxも可能)

・CSRF対策

・ViewActionの追加

・カスタム・コンポーネント・アノテーション対応

ステートレス・モード

サーバ側でコンポーネントツリーを保持しない

アクセスの度にコンポーネントツリーを作り直す

<f:view transient="true">

<h:body>...</h:body>

</f:view>

HTML5のサポート

書き方1

<html xmlns="http://www.w3.org/1999/xhtml"

xmlns:h="http://java.sun.com/jsf/html"

xmlns:p="http://java.sun.com/jsf/passthrough">

<h:form>

<h:inputText value="#{bean.value}" p:placeholder="xxx"/>

</h:form>

</html>

書き方2

<html xmlns="http://www.w3.org/1999/xhtml"

xmlns:h="http://java.sun.com/jsf/html"

xmlns:f="http://java.sun.com/jsf/core">

<h:form>

<h:inputText value="#{bean.value}" >

<f:passThroughAttribute name="placeholder" value="xxx" />

</h:outputText>

</h:form>

</html>

書き方3

<h:outputText value="#{bean.value}" >

<f:passThroughAttributes value="#{bean.attributes}" />

<f:passThroughAttributes value="{"one":1, "two":2, "three":3}" />

</h:outputText>

※Map<String, Object>

サーバ側の場合

UIComponent component = ...;

Map passThrough = component.getPassThroughAttributes();

passThrough.put("placeholder", "xxx");

★Avoid repeat expressions

<c:set var="man" value="#{someBean.isMan(person)}"/>

<c:set var="country" value="#{someBean.getCountry(person)}"/>

あるいは

<ui:param name="man" value="#{someBean.isMan(person)}"/>

<ui:param name="country" value="#{someBean.getCountry(person)}"/>

<ui:include src="/include/some-snippet.xhtml">

<ui:param name="money" value="#{man and country eq 'de' ? 1000 : 900}"/>

</ui:include>

★ActionEventから要素の値を取得

<p:commandButton value="Edit" actionListener="#{someBean.edit}" phone="#{phone}" />

private static final String PHONE_ATTRIBUTE = "phone";

public void edit(ActionEvent event) {

Phone phone = getAttribute(event, PHONE_ATTRIBUTE, Phone.class);

}

public static <T> T getAttribute(ActionEvent event, String key, Class<T> type) {

return type.cast(event.getComponent().getAttributes().get(key));

}

★改行

方法1

dialog.message=AAA<br />BBB

<h:outputFormat value="#{messages['dialog.message']}" escape="false" />

方法2

.pre {

display: block;

white-space: pre;

}

dialog.message=AAA\nBBB

<h:outputFormat value="#{messages['dialog.message']}" styleClass="pre" />

★preRenderView vs viewAction(JSF 2.2)

<f:metadata>

<!-- 画面表示の度に実行する -->

<f:event type="preRenderView" listener="#{myBean.initialize}"/>

</f:metadata>

Renderer Responseフェーズで実行する

<f:metadata>

<!-- 初期表示時に1回のみ実行する -->

<f:viewAction action="#{myBean.initialize}"/>

<!-- 画面表示の度に実行する -->

<f:viewAction action="#{myBean.initialize}" onPostback="true"/>

</f:metadata>

phase="APPLY_REQUEST_VALUES"

フェーズ2から5のAPPLY_REQUEST_VALUES, PROCESS_VALIDATIONS, UPDATE_MODEL_VALUES, INVOKE_APPLICATIONが指定可

<h:form id="bookForm">

<h:inputText id="title" value="#{bookController.book.title}" />

<h:message for="title">

</h:form>

★重要なオブジェクト

FacesContext javax.faces.context.FacesContext

取得方法

1.ページ側:暗黙のfacesContextオブジェクト

2.ManagedBean側:staticメソッドgetCurrentInstance()

メソッド

addMessage

getApplication

getAttributes

getCurrentInstance

getELContext

getMaximumSeverity

getMessages

getPartialViewContext

getViewRoot

release

renderResponse

responseComplete

Application

Application app = facesContext.getApplication();


RuntimeConfig

RuntimeConfig config = RuntimeConfig.getCurrentInstance();


ExternalContext javax.faces.context.ExternalContext

ExternalContext extCtx =

FacesContext.getCurrentInstance().getExternalContext();

HttpServletResponse response = (HttpServletResponse) extCtx.getResponse();

HttpServletRequest request = (HttpServletRequest) extCtx.getRequest();

ServletContext(<context-param>)の初期パラメータを取得

String value = extCtx.getInitParameter("name");

或いは

Map paramMap = extCtx.getInitParameterMap();

if (paramMap != null) {

String value = paramMap.get("name");

}

Requestのパラメータを取得

Map paramMap = extCtx.getRequestParameterMap();

if (paramMap != null) {

String id = paramMap.get("id");

}

Iterator paramNames = extCtx.getRequestParameterNames();

while (paramNames.hasNext()) {

String paramName = (String) paramNames.next();

String paramValue = (String) paramMap.get(paramName);

...

}

★Flashについて(ページ間)

public String nextPage() {

JsfUtils.flashScope().put("xxx", this);

return "page?faces_redirect=true";

}

public final class JsfUtils {

public static Flash flashScope() {

return FacesContext.getCurrentInstance().getExternalContext().getFlash();

}

}

<h3>#{flash.xxx.text}</h3>

<h3>#{flash['xxx'].text}</h3>

★自動生成されるidの区切り文字(コロン)を変える設定

<context-param>

<param-name>javax.faces.SEPARATOR_CHAR</param-name>

<param-value>-</param-value>

</context-param>

<h:form id="frm">

<h:commandButton id="btn" value="送信"/>

</h:form>

HTMLのソース

<input id="frm:btn" type="submit" ...>

<input id="frm-btn" type="submit" ...>

★f:paramとf:attribute

<h:commandButton id="submit"

value="送信" action="#{myBean.showData}">

<f:param name="username" value="xxx" />

</h:commandButton>

public String showData(){

FacesContext fc = FacesContext.getCurrentInstance();

Map<String,String> params =

fc.getExternalContext().getRequestParameterMap();

this.data = params.get("username");

return "result";

}

<h:commandButton id="submit"

actionListener="#{myBean.attributeListener}" action="result">

<f:attribute name="value" value="送信" />

<f:attribute name="username" value="xxx" />

</h:commandButton>

public void attributeListener(ActionEvent event){

this.data = (String)event.getComponent().getAttributes().get("username");

}

★faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd"

version="2.1">

<application>

<view-handler>

xxx.web.faces.CustomViewHandlerWrapper

</view-handler>

<navigation-handler>

xxx.web.faces.CustomConfigurableNavigationHandler

</navigation-handler>

<locale-config>

<default-locale>ja</default-locale>

<supported-locale>en</supported-locale>

<supported-locale>ja</supported-locale>

</locale-config>

</application>

<converter>

<converter-for-class>java.lang.String</converter-for-class>

<converter-class>

xxx.web.faces.converter.CustomConverter

</converter-class>

</converter>

<render-kit>

<renderer>

<component-family>org.primefaces.component</component-family>

<renderer-type>

org.primefaces.component.FileUploadRenderer

</renderer-type>

<renderer-class>

xxx.web.faces.renderkit.CustomFileUploadRenderer

</renderer-class>

</renderer>

<renderer>

<component-family>org.primefaces.component</component-family>

<renderer-type>

org.primefaces.component.MessagesRenderer

</renderer-type>

<renderer-class>

xxx.web.faces.renderkit.CustomMessagesRenderer

</renderer-class>

</renderer>

</render-kit>

<lifecycle>

<phase-listener>xxx.web.faces.CustomPhaseListener</phase-listener>

</lifecycle>

</faces-config>

import javax.faces.application.ViewHandlerWrapper;

public class CustomViewHandlerWrapper extends ViewHandlerWrapper { ... }

import javax.faces.application.ConfigurableNavigationHandler;

public class CustomConfigurableNavigationHandler

extends ConfigurableNavigationHandler { ... }

import org.primefaces.component.fileupload.FileUploadRenderer;

public class CustomFileUploadRenderer extends FileUploadRenderer { ... }

import org.primefaces.component.messages.MessagesRenderer;

public class CustomMessagesRenderer extends MessagesRenderer { ... }

★@PostConstructメソッドが画面表示時とSubmit時に2回呼ばれた問題

import javax.enterprise.context.RequestScoped;

@Named

@RequestScoped

public class HelloBean {

@PostConstruct

public void init() {

doInit();

}

public void submit() {

...

}

}

又は

import javax.enterprise.context.Conversation;

import javax.enterprise.context.ConversationScoped;

@Named

@ConversationScoped

public class HelloBean implements Serializable {

@Inject

private Conversation conversation;

@PostConstruct

public void init() {

doInit();

conversation.begin();

}

public void submit() {

...

conversation.end();

}

}

解決方法1

@Named

@RequestScoped

public class HelloBean {

@PostConstruct

public void init() {

if (FacesContext.getCurrentInstance().isPostback()) {

...

} else {

doInit();

}

}

...

}

解決方法2

import javax.faces.bean.ManagedBean;

import javax.faces.bean.ViewScoped;

@ManagedBean

@ViewScoped

public class HelloBean implements Serializable {

@PostConstruct

public void init() {

doInit();

}

...

}

解決方法3(JavaEE 7)

import javax.faces.view.ViewScoped;

@Name

@ViewScoped

public class HelloBean implements Serializable {

@PostConstruct

public void init() {

doInit();

}

...

}

★message-bundle vs resource-bundle

<message-bundle>

override JSF default warning/error messages used by validation/conversion

xxx.i18n.Messages_xx.properties

javax.faces.component.UIInput.REQUIRED = ...

faces-config.xml

<message-bundle>xxx.i18n.Messages</message-bundle>

<resource-bundle>

register a localized resource bundle which is available throughout UI files

xxx.i18n.Text_xx.properties

main.title = ...

faces-config.xml

<locale-config>

<default-locale>en</default-locale>

<supported-locale>en</supported-locale>

<supported-locale>jp</supported-locale>

<supported-locale>fr</supported-locale>

</locale-config>

<resource-bundle>

<base-name>xxx.i18n.Text</base-name>

<var>text</var>

</resource-bundle>

<title>#{text['main.title']}</title>