Archive for 3月, 2015
JSF の相関項目チェック by Bean Validation
このエントリは、半分以上お遊びですのであまり詳しくは説明しません。ご興味ある方はお試しください。JSF で複数入力項目がある場合の相関チェックをする場合、通常は JSF のコンポーネントにバインドさせてバリデーション・フェーズで検証を行っていましたが(ご参照:本エントリの一番下に例を記載してますがそちらの方が楽です)、今回、Bean Validation、カスタム Bean Validation さらに CDI と PhasesListner を駆使して複数入力項目がある場合の相関チェックをしてみました。
JSF では直接 Bean Validation のクラスレベルの検証ができないので、無理矢理クラスレベルでチェクをするために、PhasesListener 内で ValidatorFactory をインジェクトして、validateメソッドに渡す事で Person クラスをValidate しています。おもしろいのが、JSF のライフサイクルで「モデル値の適用」フェーズ後は Person に代入された値が @Inject Instance<Person> でインジェクトできるようになるので、直接 Person としていじくれるようになる事です。
JSF の Facelets の内容
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form prependId="false">
<h:outputLabel id="birthDay" value="Input Birth Day"/>
<h:inputText id="inputYear" value="#{personManaged.person.birthYear}"/>/
<h:inputText id="inputMonth" value="#{personManaged.person.birthMonth}"/>/
<h:inputText id="inputDay" value="#{personManaged.person.birthDay}"/><br/>
<h:outputLabel id="age" value="Input Age"/>
<h:inputText id="inputAge" value="#{personManaged.person.age}"/>
<h:commandButton value="Check Multi Value" action="#{personManaged.execSubmitButton()}"/>
<h:messages/>
</h:form>
</h:body>
</html>
faces-config.xml の内容
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<lifecycle>
<phase-listener>jp.co.oracle.jdbcrealm.cdis.phaselisteners.ValiationPhaseListener</phase-listener>
</lifecycle>
</faces-config>
Facelets のバッキング・ビーン
package jp.co.oracle.jdbcrealm.cdis;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import jp.co.oracle.jdbcrealm.customvalidations.Person;
@Named(value = "personManaged")
@RequestScoped
public class PersonBirthDayValidatorBackingBean {
@Inject
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public void execSubmitButton() {
System.out.println(person.getBirthYear() + "/" + person.getBirthMonth() + "/" + person.getBirthDay() + "\t" + person.getAge());
}
}
Person クラス
package jp.co.oracle.jdbcrealm.customvalidations;
import java.math.BigDecimal;
import javax.enterprise.context.RequestScoped;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
@RequestScoped
@PersonClassLevelValidator // <---- カスタム・バリデータ
public class Person {
@NotNull
private Integer name;
@NotNull @Digits(integer = 3,fraction = 0)
private Integer age;
@NotNull @Digits(integer = 4,fraction = 0)
private Integer birthYear;
@NotNull @Digits(integer = 2,fraction = 0)
private Integer birthMonth;
@NotNull @Digits(integer = 2,fraction = 0)
private Integer birthDay;
/**
* @return the name
*/
public Integer getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(Integer name) {
this.name = name;
}
/**
* @return the age
*/
public Integer getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(Integer age) {
this.age = age;
}
/**
* @return the birthYear
*/
public Integer getBirthYear() {
return birthYear;
}
/**
* @param birthYear the birthYear to set
*/
public void setBirthYear(Integer birthYear) {
this.birthYear = birthYear;
}
/**
* @return the birthMonth
*/
public Integer getBirthMonth() {
return birthMonth;
}
/**
* @param birthMonth the birthMonth to set
*/
public void setBirthMonth(Integer birthMonth) {
this.birthMonth = birthMonth;
}
/**
* @return the birthDay
*/
public Integer getBirthDay() {
return birthDay;
}
/**
* @param birthDay the birthDay to set
*/
public void setBirthDay(Integer birthDay) {
this.birthDay = birthDay;
}
}
Person クラスのバリデータ(相関チェックの実装)
package jp.co.oracle.jdbcrealm.customvalidations;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.Period;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = PersonClassLevelValidator.PersonClassValidtor.class)
@Documented
public @interface PersonClassLevelValidator {
String message() default " Invalid input of the Birthday or Age";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
class PersonClassValidtor implements ConstraintValidator<PersonClassLevelValidator, Person> {
PersonClassLevelValidator constraintAnnotation;
@Override
public void initialize(PersonClassLevelValidator constraintAnnotation) {
this.constraintAnnotation = constraintAnnotation;
}
@Override
public boolean isValid(Person person, ConstraintValidatorContext context) {
if ( person == null) return true;
// Person として直接扱えるので色々楽
int age = person.getAge();
int birthYear = person.getBirthYear();
int birthMonth = person.getBirthMonth();
int birthDay = person.getBirthDay();
LocalDate birthDate;
try {
birthDate = LocalDate.of(birthYear, birthMonth, birthDay);
} catch (DateTimeException de) {
return false;
}
LocalDate today = LocalDate.now();
Period period = Period.between(birthDate, today);
return age == period.getYears();
}
}
}
JSF の PhasesListener でモデル値適用後に Person をバリデート
package jp.co.oracle.jdbcrealm.cdis.phaselisteners;
import java.util.Set;
import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;
import jp.co.oracle.jdbcrealm.customvalidations.Person;
import jp.co.oracle.jdbcrealm.customvalidations.PersonClassLevelValidator;
public class ValiationPhaseListener implements PhaseListener {
@Override
public PhaseId getPhaseId() {
return PhaseId.UPDATE_MODEL_VALUES;
}
@Inject
ValidatorFactory validFactory;
@Inject
javax.enterprise.inject.Instance<Person> instanceOfPerson;
@Override
public void afterPhase(PhaseEvent event) {
//どこからリクエストが来たかを検証 // ここの実装を綺麗にしたかったが難しかった。
FacesContext fContext = event.getFacesContext();
ExternalContext extContext = fContext.getExternalContext();
String url = ((HttpServletRequest) extContext.getRequest()).getRequestURL().toString();
if (!url.contains("PersonBirthDayValidate.xhtml")) {
return;
}
//モデルに値が適用された後(PhaseId.UPDATE_MODEL_VALUES)は、Person に値が代入されているためインジェクト可能
Person person = instanceOfPerson.get();
// Execute only PersonClassLevelValidator(Class-Constraint) Validation
Set<ConstraintViolation<Person>> violations = validFactory.getValidator().validate(person, Default.class);
//Bean Valiadtion の検証結果
violations.stream()
.filter(consts -> consts.getConstraintDescriptor().getAnnotation() instanceof PersonClassLevelValidator)
.map(violation -> violation.getMessage())
.findFirst()
.ifPresent((String errMsg) -> {
FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, errMsg, null);
fContext.addMessage("ERROR", facesMessage);
fContext.validationFailed();
fContext.renderResponse(); //SKIP lifecycle
});
}
@Override
public void beforePhase(PhaseEvent event) {
}
}
ちなみに、従来の JSF コンポーネントバインドを使った相関チェックのやり方はこんなかんじ
<f:view>
<f:metadata>
<f:viewAction action="#{indexManaged.checkArgument()}" onPostback="true" phase="PROCESS_VALIDATIONS" />
</f:metadata>
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<h:inputText id="text1" value="#{indexManaged.text1}" binding="#{indexManaged.bindComp1}"/>
<h:inputText id="text2" value="#{indexManaged.text2}" binding="#{indexManaged.bindComp2}"/>
<h:messages id="error_message" style="color:red;margin:8px;"/>
<h:commandButton value="OK" action="#{indexManaged.pushButton()}"/>
</h:form>
</h:body>
</f:view>
package jp.co.oracle.jsf22.multivalidate;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.component.html.HtmlInputText;
@Named(value = "indexManaged")
@RequestScoped
public class IndexManaged {
private String text1;
private String text2;
private HtmlInputText bindComp1;
private HtmlInputText bindComp2;
/**
* Creates a new instance of IndexManaged
*/
public IndexManaged() {
}
/**
* @return the text1
*/
public String getText1() {
return text1;
}
/**
* @param text1 the text1 to set
*/
public void setText1(String text1) {
this.text1 = text1;
}
/**
* @return the text2
*/
public String getText2() {
return text2;
}
/**
* @param text2 the text2 to set
*/
public void setText2(String text2) {
this.text2 = text2;
}
public String pushButton() {
return "";
}
/**
* @return the bindComp1
*/
public HtmlInputText getBindComp1() {
return bindComp1;
}
/**
* @param bindComp1 the bindComp1 to set
*/
public void setBindComp1(HtmlInputText bindComp1) {
this.bindComp1 = bindComp1;
}
/**
* @return the bindComp2
*/
public HtmlInputText getBindComp2() {
return bindComp2;
}
/**
* @param bindComp2 the bindComp2 to set
*/
public void setBindComp2(HtmlInputText bindComp2) {
this.bindComp2 = bindComp2;
}
public void checkArgument() {
String val1 = bindComp1.getValue() ;
String val2 = bindComp2.getValue() ;
if (!val1.equals(val2)){
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "入力内容が不正です。", "テキスト・フィールドに正しい値が入力されていません。");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
}
Java Day Tokyo 2015 まであと2週間
皆様、Java Day Tokyo 2015 まで、あと丁度2週間となりました。満席セッションもかなり多くなってきましたが、今年は会場が広いのでまだまだ魅力的なコンテンツをご選択・ご覧頂く事ができます。今からでもまだお申し込み可能なセッションを下記にご紹介します。(中には残りわずかなセッションも含まれますので)
● Java SE 系の情報を取得されたい方
【1-1】Lambdas and Streams: Taking the Hard Work Out of Bulk Operations in Java SE 8
【1-2】Date & Time API and other technologies of Java SE 8
【3-1】Javaの関数型言語への挑戦/ベターオブジェクト指向言語からの脱皮
【3-2】Java EEアプリケーションサーバの開発現場で見たJava SEの実際
【4-5】Java Flight Recorder のご紹介 (残りわずか)
【5-2】NetBeans IDE最新情報 (残りわずか)
● JavaFX 系の情報を取得されたい方
【6-1】FXML for Structure, CSS for Styling, and JavaFX as Standard GUI Library (残りわずか)
● Java EE 系の情報を取得されたい方
【2-1】Java EE 8 – directions and new features
【2-3】Applied Domain-Driven Design Blue Prints for Java EE 7
【2-4】CDI and EJB – Java EE alignment and strategy
【2-5】Reactive Java EE 7 – Let Me Count the Ways !
【4-1】Javaクラウド・サービスが実現する新しいエンタープライズJava (残りわずか)
【4-2】Oracle Developer Cloud Service, what can you do?
● Java ME/SE Embedded 系の情報を取得されたい方
【1-3】Java SE Embedded 8 / Java ME Embedded 8 Overview
【1-4】Programming the Real World
【1-5】Java and The Internet of Things For Automotive Applications
【7-1】IoT時代のエッジデバイス向け無線モジュールとJavaMEの取り組み
【7-3】Java Embedded で始める IoT
その他
● SDN(Software Defined Networking)
【7-2】OpenDaylightがやってきた!
●初心者向け
【9-1】Say Hello to Java
●Java 認定資格試験受講者向け
【9-5】最新 Java 認定資格ガイド
Java SE 8 や JavaFX、Java EE 7&8 、Embedded などの重要な情報を入手する事が今からでも間に合います。是非、皆様の周りの同僚やお友達、後輩にお声掛け頂けないでしょうか。現在「Java 20周年 盛り上げ大作戦!」を行っています。より多くの方へお誘い頂いた方にはここでしか入手できないレア・グッズをプレゼント予定でございます。
● Java 20周年 盛り上げ大作戦!の詳細はコチラ
◎ https://www.facebook.com/JavaDayTokyo/posts/674620795996755
◎ https://www.facebook.com/JavaDayTokyo/posts/687490008043167
最後に
先日もご案内さし上げましたが、Java Day Tokyo 終了後、東京国際フォーラムの近くで懇親会も実施します。こちらは、Java Day Tokyo に参加されていない方でも、今まで Java に携わられてこられた方や、Java の 20周年を一緒にお祝いしていただける方、どなたでもご参加頂ければたいへん嬉しく思います。
4/8(水) : 20th Anniversary Java コミュニティ・パーティの詳細
あと2週間でございますが、皆様のご参加を心よりお待ち申し上げます。
4/8(水) : 20th Anniversary Java コミュニティ・パーティ

既に、JJUG コミュニティのメーリング・リスト等ではご案内を差し上げておりますが、Java Day Tokyo の終了後、東京国際フォーラムの近くで、関係者(外人スピーカー、日本人スピーカーなど)を交えた懇親会を開きたいと思います。すでに、基調講演に登壇頂く Java SE の開発部門の責任者である George Saab さんや Cameron Purdy さん、Stephan Chin さんがくる事が決まっている他、Simon Ritter さん, Sharat Chandar さん Caicedo Angela なども参加すると言って頂いています。もちろん、日本人のご登壇者の皆様も多く参加していただきます。
このように直接、Java 業界で著名な方々と一同を介して交流を持てる機会は多くなく、基調な場になるかと思いますので是非多くの皆様にご参加いただけないでしょうか。
************************************************************************************
日付:2015/4/8
時間:PM 8:30 – 10:30
場所:Alice aqua garden
http://r.gnavi.co.jp/g105302/
参加費: 6,000 円
登録サイト URL:http://javadaytokyonightparty.peatix.com/
************************************************************************************
Java Day Tokyo の余韻もさめぬまま、4/8 (水) はJava 20 周年を祝う日として最後の最後まで盛り上がりませんか?お祝いという事でちょっとした催しも考えています。参加者数に限りがあるため、大変恐れ入りますが参加ご希望の方はどうぞお早めにご登録頂けますよう宜しくお願いします。
あと1ヶ月を切りましたが、皆様と当日お会いできる事を心より楽しみに致しております。
以上、今後ともどうぞ宜しくお願いします。
寺田
日本語 Java SE 8 API ドキュメント URL 変更につきまして
皆様
いつも大変お世話になっております。
現在、Java SE 8 に関連したドキュメントを大幅に更新を掛けております。
http://docs.oracle.com/javase/jp/8/
その過程で、Java SE 8 API ドキュメントへのリンクが下記に変更されました。
旧:http://docs.oracle.com/javase/jp/8/api/
新:http://docs.oracle.com/javase/jp/8/docs/api/
これは、USのオリジナルのドキュメントがディレクトリ構成を変更したためで、USに併せて日本でも今後、上記のURLが正しいURLとなります。つきましては、大変恐れ入りますが、上記に対して現在ブックマークされている場合は改めて御変更いただけませんでしょうか。
また、現在トップページにおいてもいくつかの不具合も確認しております。
http://docs.oracle.com/javase/jp/8/
●リンク切れ
Java チュートリアル・ラーニング・パス
Java言 語および仮想マシン仕様
●リンク間違い(英語サイトへ遷移)
Java SE 8について
新機能(機能および拡張機能)
商用機能アイコン: リンクは新しいウィンドウで開きます
互換性ガイド
既知の問題
●ページ内の画像リンク間違い
Java Platform, Standard Edition HotSpot Virtual Machineガベージ・コレクション・チューニング・ガイド
Java Platform, Standard Editionトラブルシューティング・ガイド
ダウンロー ドとインストールの方法
Java SEツール・リファレンス(UNIX)
Java SEツール・リファレンス(Windows)
上記は、Firefox で見た場合、画像が読み込めないためページの読み込みに非常に時間が掛かります。Chrome ならば、画像リンク切れを無視し表示できます。
上記の3点の不具合は既に認識しており修正中でございますので何れ正しく表示されます。
一方で、繰り返しますが、API ドキュメントに対するディレクトリ構成の変更は US 側で実施されている物であるため、新しいURLで再度ブックマーク等をして頂ければ誠に幸いです。
新しい Java SE 8 日本語 API Doc の URL
http://docs.oracle.com/javase/jp/8/docs/api/
以上、どうぞ宜しくお願いします。
寺田
Java Day Tokyo 2015 追加情報
先日、ご案内させて頂きましたように、4/8(水)に Java Day Tokyo 2015 を東京国際フォーラムで開催致します。
Java Day Tokyo 2015 Offcial Site :
http://www.oracle.co.jp/jdt2015/
既にご登録頂きました皆様におかれましては、ご登録頂きまして誠にありがとうございます。
登録サイトオープン後にセッションが追加されておりますので下記にご案内差し上げます。また今後も追加予定がございますので、セッション情報につきましては定期的にご確認頂ければ誠に幸いです。
※セッションは登録後も変更可能でございますので、必要に応じて登録ページよりセッションをご変更ください。
●15:10-16:00 実践的なJavaアプリケーションサーバの構築・運用~転ばぬ先の杖
山田 貴裕氏 伊藤忠テクノソリューションズ株式会社
●16:15-17:05 アジャイル時代のモデリング
平鍋 健児氏 株式会社チェンジビジョン
最後に
改めまして皆様にお願いがございます。
ご登録者数の状況は昨年、一昨年よりも良好ですが、まだまだより多くの開発者の皆様にご登録頂く事ができます。今年の Java Day Tokyo は、Java の生誕 20 周年を記念して、昨年、一昨年に比べ、規模を大幅に拡大して実施します。そして、このように規模を拡大してイベントを実施できたのも、過去の Java イベントにご参加頂いた皆様のおかげです。
今、ご登録頂いている皆様におかれましては、大変恐れ入りますが是非周りのご友人や後輩にもお声掛け頂き、より多くの Java 開発者の皆様にご参加いただけるように、ご協力頂けませんでしょうか。Java Day Tokyo のオフィシャル・アカウントからも告知がございましたが、より多くの方をご招待頂いた方には、何か良い事があるとの事です。是非、まわりの Java 開発者の皆様にお声掛け頂けませんでしょうか。
オラクルユニバーシティが提供する、Java 初心者用のセッションにもまだ余裕がございますので Java 初心者の方にもどうぞお声掛け頂ければ幸いです。
皆様、お一人お一人のお力をどうぞお貸し頂けないでしょうか。
以上、どうぞ宜しくお願いします。
寺田


