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>