1. (C) CASAREAL, Inc. All rights reserved.
脱Struts&独自フレームワーク!
今からでも遅くない
Java EE Web開発入門ハンズオン!
株式会社カサレアル
多田真敏 (@suke_masa)
JJUG CCC 2015 Fall
2015/11/28(土)
1
2. (C) CASAREAL, Inc. All rights reserved.
このセッションについて
u ハッシュタグ
#ccc_m1
u Java EEのWebアプリケーション開発につ
いて、演習を交えながら学習します
2
3. (C) CASAREAL, Inc. All rights reserved.
自己紹介
u 多田真敏(ただまさとし)
u 大阪出身、香川育ちの0x1F歳
u (株)カサレアル所属
u Java EEを中心とした研修トレーナー
u JJUG CCCは4回連続4回目の出場
u Twitter:@suke_masa
3
4年前の写真なので実物はもうちょっとおっさん…
4. (C) CASAREAL, Inc. All rights reserved.
本資料について
u 著作権は(株)カサレアルに帰属します。
u 個人の学習、コミュニティや社内の勉強会など、
「無償の用途」にのみご利用ください。
u 勉強会の場合、会場代など「運営に最低限必要な費用」を参加者
の方から集金する事については「無償の用途」の範囲内とします。
u 本資料を使用して有償のサービスを提供することを禁止
します。また、有償サービスで利用する資料への内容の
転載も禁止します。
4
5. (C) CASAREAL, Inc. All rights reserved.
本日のスケジュール
5
u 13:00-13:10 Java EE全体像の講義
u 13:10-13:50 JSF講義・演習
u 13:50-14:00 10分休憩
u 14:00-14:40 JAX-RS講義・演習
u 14:40-14:50 Jersey MVC + MVC 1.0講義
7. (C) CASAREAL, Inc. All rights reserved.
背景:Struts 1とSeasar2の終焉
u 2013年 Struts 1.x EOL
u 開発ストップは2008年から
(7年前に終わっていた)
u 2016年9月26日 Seasar2 EOL予定
u 2015年9月のSeasar Conferenceで発表
http://d.hatena.ne.jp/higayasuo/
20150928/1443415547
7
8. (C) CASAREAL, Inc. All rights reserved.
背景:脱・独自フレームワーク
u 作った社員が異動・退職したら保守できない
u 一般に公表されないものなので、
開発者の教育や外部調達が難しい
u 長く使っていて古くなってきた
8
なるべく、世の中で広く使われている
「一般的な」フレームワークが求められている
9. (C) CASAREAL, Inc. All rights reserved.
現在のトレンド
Java EE or Spring Framework
u Java EE
u 標準仕様=長期的な安定性
u APサーバーも含めたベンダーサポート
u Spring Framework
u 進化の速さがすごい=Javaの最先端
u 高機能・高生産性
9
10. (C) CASAREAL, Inc. All rights reserved.
なぜJava EEなのか?
u 「標準」は「一般的」の最上級
u 最先端ではないが十分に現代的な
フレームワーク
u 最先端が「最善の選択」かは時と場合による
u 仕様面・サーバー面での長期的安定性
10
11. (C) CASAREAL, Inc. All rights reserved.
Java EE=フルスタックフレームワーク
11
JSF
JAX-RS
WebSocket
CDI/EJB
/JTA
JPA
Interceptor
Bean Validation
Servlet/JSP
Web ビジネスロジック DBアクセス
12. (C) CASAREAL, Inc. All rights reserved.
Java EE 7対応APサーバー
(2015年11月時点)
u GlassFish/Payara
u WildFly
u WebLogic ← 今年10月リリース!
u WebSphere
u Cosminexus
u JBossは来年かも?
12
13. (C) CASAREAL, Inc. All rights reserved.
Java EEは「仕様」
u Javadocを見ると、ほとんどインタフェー
ス・アノテーション・例外
u APサーバーは「実装」の集合体
u 細かな挙動は「実装」によって
異なる部分もあるので注意
13
14. (C) CASAREAL, Inc. All rights reserved.
今回の環境
u Payara Web ML 4.1.1.154
u GlassFish 4.1.1をベースに、英国C2B2社がバグ修正・
機能強化を行っているサーバー
u GlassFishはJava EEの参照実装 (Reference
Implementation : RI)
u JDK 8u66
u NetBeans 8.1 (8.0.2でもOK)
u JavaDB (Payara内包)
14
15. (C) CASAREAL, Inc. All rights reserved.
事前準備の確認
u NetBeans・Payara・curlはインストール
済み?
u プロジェクトはクローン・ビルド済み?
u exerciseブランチに切り替え済み?
u アクション項目は表示済み?
15
16. (C) CASAREAL, Inc. All rights reserved.
今回の課題
u 架空の社員管理システム
u 社員データの検索・追加・更新・削除を行う
u 同じシステムをJSF/JAX-RS/Jersey MVCと
いう違う技術で実装します
u これからデモします
16
18. (C) CASAREAL, Inc. All rights reserved.
JSF (JavaServer Faces)とは?
u HTMLを返すWebフレームワーク
u コンポーネントベース
u .NET、Android、Swing、JavaFXなどのGUIアプ
リ経験がある人は馴染みやすい
u Strutsなどのアクションベースフレムーワークか
ら移行する人は、最初は違和感があるかも
18
19. (C) CASAREAL, Inc. All rights reserved.
JSF実装
u Mojarra (RI)
u GlassFish/Payara/WebLogic/WildFly/JBoss内包
u 読み方は「モハラ」
u Apache MyFaces
u WebSphere内包
19
20. (C) CASAREAL, Inc. All rights reserved.
JSFアプリの最小構成
u Backing Bean(Javaクラス)
u Facelets(画面)
u web.xml(設定ファイル)
20
21. (C) CASAREAL, Inc. All rights reserved.
Backing Bean
u 画面に対応したJavaクラス
u 「Managed Bean(管理Bean)」と呼ばれることもありますが、
これは他の仕様でも使われる用語なので、
区別する為に本セッションでは「Backing Bean」と呼びます
21
@Named @ViewScoped
public class EmpBean {
private String name; // setter/getterも必要
public String findByName() { return "index.xhtml"; }
}
22. (C) CASAREAL, Inc. All rights reserved.
Facelets
u XHTML形式のビュー技術
u JSPのようなJavaコードは書けない
→ビューとロジックの完全な分離が可能
22
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head><title>社員一覧</title></h:head>
<h:body>
<h:form>
氏名キーワード:<h:inputText value="#{empBean.name}" />
<h:commandButton action="#{empBean.findByName()}" value="検索">
23. (C) CASAREAL, Inc. All rights reserved.
web.xml
u FacesServletの登録が必要
23
<web-app>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<web-app>
24. (C) CASAREAL, Inc. All rights reserved.
Backing Beanの作り方①
名前付けアノテーションをクラスに付加
u EL式でビューから
呼び出す際の名前
を付ける
u デフォルトはクラス
名の頭文字を小文字
にしたもの
24
// 下記の3つは全て同じ意味
@Named
public class EmpBean { … }
@Named("empBean")
public class EmpBean { … }
@Named(value = "empBean")
public class EmpBean { … }
25. (C) CASAREAL, Inc. All rights reserved.
Backing Beanの作り方②
スコープアノテーションをクラスに付加
u @ViewScoped
u 1つの画面が有効な限り
Backing Beanインスタンスも有効
u 最もよく利用する
u @ConversationScoped, @FlowScoped
u 開始・終了を明示的に指定して、その間はずっとBacking Beanイ
ンスタンスも生きている
u 複数画面で値を受け渡すときに便利
u @RequestScoped, @SessionScoped,
@ApplicationScoped, @Dependentはあまり使わない
25
@Named
@ViewScoped
public class EmpBean { … }
26. (C) CASAREAL, Inc. All rights reserved.
★演習★ Backing Beanの作成
u 演習1-1:Backing Beanクラスに@Named
と@ViewScopedを付加する
26
27. (C) CASAREAL, Inc. All rights reserved.
Backing Beanの作り方③
画面と値を受け渡すフィールド
u 画面からはEL式で紐付けられる
u setter/getter必須
27
@Named @ViewScoped
public class EmpBean {
private String name;
public String getName() { return name; }
public void setName(String name) {
this.name = name; }
}
28. (C) CASAREAL, Inc. All rights reserved.
Backing Beanの作り方④
アクションメソッド
u ボタンが押された際の処理
を記述する
u 画面からはEL式で紐付けら
れる
u Stringでフォワード先の
ビュー名を返す
u voidまたはnullの場合、現在
と同じ画面に遷移する
28
@Named @ViewScoped
public class EmpBean {
@Inject BizLogic logic;
public String find() {
logic.doSomething();
return "index.xhtml";
}
}
29. (C) CASAREAL, Inc. All rights reserved.
★演習★ Backing Beanの作成
u 演習1-2:アクションメソッドの中身を実
装する
u 検索のキーワードを受け取るnameフィー
ルド、そのsetter/getterは作成済み
29
30. (C) CASAREAL, Inc. All rights reserved.
画面の作り方①
JSFタグによる記述
u namespaceを指定
u hタグ→HTMLタグの生成
u fタグ→Ajaxや入力値の変換・検証など
30
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head><title>社員一覧</title></h:head>
<h:body>
<h:form>
氏名キーワード:<h:inputText value="#{empBean.name}" />
<h:commandButton action="#{empBean.findByName()}" value="検索">
31. (C) CASAREAL, Inc. All rights reserved.
画面の作り方②
EL式によるBacking Beanとの紐付け
u #{Bean名.プロパティ名}で指定
u Bean名は@Namedで付けたもの
31
氏名キーワード:
<h:inputText value="#{empBean.name}" />
<h:commandButton
action="#{empBean.findByName()}"
value="検索">
@Named @ViewScoped
public class EmpBean {
private String name;
public String findByName() {
// do something
return "index.xhtml";
}
}
32. (C) CASAREAL, Inc. All rights reserved.
★演習★ 社員一覧画面の作成
u 演習1-3:ボタンが押された際のアクションメソッドをEL式
で指定する
u 演習1-4:tableに社員リストを一覧表示する
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビルド]
u (2)プロジェクトを右クリック→[実行]
u Windows PCの方は、セキュリティ警告が出たら[許可]して
ください
32
※データベースが
DROP-CREATE-INSERT
されます
33. (C) CASAREAL, Inc. All rights reserved.
【補足】Converterによる型変換
u ビューの入出力値(String)と、
Javaの型を相互変換する
33
<html xmlns="…"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:inputText
value="#{emp.joinedDate}">
<f:convertDateTime
pattern="yyyy-MM-dd"
timeZone="Asia/Tokyo"/>
</h:inputText>
@Named @ViewScoped
public class EmpBean {
private java.util.Date joinedDate;
…
}
34. (C) CASAREAL, Inc. All rights reserved.
【参考】HTML5 Friendly Markup
u JSF 2.2 (Java EE 7)からの新機能
u 通常のHTML5タグのように記述できる
u とは言え、JSFタグの属性などの知識も必要
34
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsf="http://xmlns.jcp.org/jsf">
氏名キーワード:
<input type="text" jsf:value="#{empBean.name}" />
35. (C) CASAREAL, Inc. All rights reserved.
Bean Validation
u Backing Beanの
フィールドに
@NotNullなどの
アノテーション
を付ける
35
@Named @ViewScoped
public class EmpBean {
@NotNull
@Size(min = 1, max = 10)
@Pattern(regexp = "[a-zA-Z]*")
private String name;
…
}
36. (C) CASAREAL, Inc. All rights reserved.
検証アノテーション(一部)
u @NotNull
u nullの場合に検証エラー
u @Pattern
u 指定した正規表現と一致しなければエラー
u regexp属性に正規表現を指定
u @Size
u 指定した文字列長の最大値・最小値の範囲内でなければエラー
u min属性に最小値を指定(デフォルト値:0)
u max属性に最大値を指定(デフォルト値:Integer.MAX_VALUE)
36
37. (C) CASAREAL, Inc. All rights reserved.
エラーメッセージの指定
u クラスパスルートの
ValidationMessages.propertiesに記述する
u メッセージのキーはアノテーションのmessage
属性に{}付きで指定する
37
@Named @ViewScoped
public class EmpBean {
@NotNull(message = "{message.notnull}")
private String name;
…
38. (C) CASAREAL, Inc. All rights reserved.
★演習★ バリデーション
u 演習2-1:Backing Beanのnameプロパ
ティに検証アノテーションを追加
38
39. (C) CASAREAL, Inc. All rights reserved.
画面にエラーメッセージを表示
u h:messagesタグで表示する
39
<h:messages id="error" errorStyle="color:red" />
Facelets
ブラウザ
40. (C) CASAREAL, Inc. All rights reserved.
★演習★ バリデーション
u 演習2-2:画面にエラーメッセージ表示のタ
グを追加する
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビル
ド]
u (2)プロジェクトを右クリック→[実行]
40
※データベースが
DROP-CREATE-INSERT
されます
41. (C) CASAREAL, Inc. All rights reserved.
【補足】発展的なバリデーション
u http://yamkazu.hatenablog.com/entry/
20110206/1296985545 を読むと、Bean
Validationを一通り学べます
u 複数のアノテーションをまとめて1つのアノテーショ
ンにできる
u 独自の検証ロジックを持ったアノテーションを自作
できる
u 複数のアノテーションをグループ化できる
41
42. (C) CASAREAL, Inc. All rights reserved.
【補足】相関バリデーション
(複数の入力項目にまたがった検証)
u 一工夫が必要。やり方は様々
u http://yoshio3.com/2015/03/26/jsf-multiple-validation/
u http://d.hatena.ne.jp/Yosuke_Taka/20120131/1327983465
u http://den2sn.hatenablog.com/entry/2014/12/22/092738
u http://n-agetsuma.hatenablog.com/entry/2012/11/18/231527
u JSF 2.3(Java EE 8)で標準仕様に入る予定
42
43. (C) CASAREAL, Inc. All rights reserved.
Ajaxの利用
u JavaScriptコード無しでAjaxを利用できる
u f:ajaxタグ
u execute属性:リクエスト時に送信する値を指
定
u render属性:レスポンスで返ってきた情報を描
画する部分を指定
43
44. (C) CASAREAL, Inc. All rights reserved.
Ajaxの利用
u 通常
u Ajax
44
<h:commandButton action="#{empBean.findByName()}" value="検索" />
<h:commandButton action="#{empBean.findByName()}" value="検索">
<f:ajax execute="@form" render=":employeeList :error"/>
</h:commandButton>
45. (C) CASAREAL, Inc. All rights reserved.
★演習★ 検索のAjax化
u 演習3:<f:ajax>タグで、ボタンが押された
際の挙動をAjaxに変更する
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビル
ド]
u (2)プロジェクトを右クリック→[実行]
45
※データベースが
DROP-CREATE-INSERT
されます
46. (C) CASAREAL, Inc. All rights reserved.
GET時のクエリパラメータの受け取り
u JSFは基本的にすべてPOSTで遷移する
u GETでのクエリパラメータ取得はFaceletsに
下記の記述が必要
46
<h:body>
<f:metadata>
<f:viewParam id="empId" name="empId"
value="#{employeeEditBean.empId}"/>
</f:metadata>
…
クエリ
パラメータ名
Backing Bean
プロパティ名
47. (C) CASAREAL, Inc. All rights reserved.
★演習★ クエリパラメータの
受け取り
u 演習4-1:Faceletsにf:viewParamタグを
記述する
47
48. (C) CASAREAL, Inc. All rights reserved.
@ConversationScoped
(会話スコープ)
u 会話の開始から終了まで、そのBacking
Beanは生存している
u 登録→確認→完了のような、複数画面で
値を受け渡す際に有用
u 画面にhiddenタグを埋め込んで値をやりとりす
るなどの必要なし
48
49. (C) CASAREAL, Inc. All rights reserved.
@ConversationScoped
(会話スコープ)
49
@Named @ConversationScoped
public class EmpBean {
@Inject Conversation conversation;
public String one() {
conversation.setTimeout(60000);
conversation.begin();
return "one.xhtml";
}
public String two() {
return "two.xhtml";
}
public String three() {
conversation.end();
return "three.xhtml";
}
}
u Conversationを
@InjectでDI
u begin()で会話開
始
u end()で会話終了
50. (C) CASAREAL, Inc. All rights reserved.
★演習★ 会話スコープの利用
u 演習4-2:Backing Beanを会話スコープにする
u 演習4-3:会話を開始する
u 演習4-4:会話を終了する
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビルド]
u (2)プロジェクトを右クリック→[実行]
50
※データベースが
DROP-CREATE-INSERT
されます
51. (C) CASAREAL, Inc. All rights reserved.
日本語書籍について
u Begining Java EE 6
u Managed Beanの作り方が違う (@ManagedBeanを付
加する)
u スコープアノテーションが別パッケージの古いもの
u 例:
u EE 6 → javax.faces.bean.ViewScoped
u EE 7 → javax.faces.view.ViewScoped
u マスタリングJava EE 5
u ビューがJSP
u 内部の仕組みなどはほぼ変わらない
51
52. (C) CASAREAL, Inc. All rights reserved.
その他に知っておいたほうが良いこと
u まずは菊田さんのJava Day Tokyo 2015の資料
を確認しましょう
https://www.youtube.com/watch?v=yW_LAa9FXAo
u アプリケーションライフサイクル
u 例外処理
u リッチコンポーネントPrimeFaces
u ユーティリティライブラリOmniFaces
52
54. (C) CASAREAL, Inc. All rights reserved.
JAX-RSとは?
u JSONやXMLをレスポンスする
REST Webサービス開発のフレームワーク
u シンプルで分かりやすいAPIが特徴
54
55. (C) CASAREAL, Inc. All rights reserved.
REST Webサービス超概要
u 入力 = URL + HTTPリクエストメソッド
u 出力 = JSON + HTTPステータスコード
55
GET http://localhost:8080/jjug-jax-rs/api/employees/1
{
"id" : 1,
"name" : "Yumi Wakatsuki", …
}
56. (C) CASAREAL, Inc. All rights reserved.
なぜREST Webサービスなのか?
u クライアントの多様化
u PCブラウザ、スマホ・タブレット(ネイティブアプ
リ・ブラウザ)、別システムとの連携…
u サーバー側はJSONだけ返して、画面表示はク
ライアントに任せる
u ブラウザの場合は、AngularJSなどのJavaScript
フレームワークを併用することが一般的
56
57. (C) CASAREAL, Inc. All rights reserved.
JAX-RS実装
u Jersey
u GlassFish/Payara/WebLogic内包
u RESTEasy
u WildFly/JBoss内包
u Apache CXF
u WebSphere内包(JAX-WS兼用)
57
58. (C) CASAREAL, Inc. All rights reserved.
JAX-RSアプリの最小構成
u Applicationサブクラス(Javaクラス)
u リソースクラス(Javaクラス)
u 設定ファイルは必要なし
58
59. (C) CASAREAL, Inc. All rights reserved.
Applicationサブクラスの作成
u javax.ws.rs.core.Applicationのサブクラスを作成する
u @ApplicationPathアノテーションにパスを指定する
u 下記のコードの場合、
「http://localhost:8080/コンテキストルート/api/…」
59
@ApplicationPath("api")
public class MyApplication extends Application {
// 中身は空でOK
}
60. (C) CASAREAL, Inc. All rights reserved.
★演習★ Applicationサブクラス
の作成
u 演習1-1:Applicationサブクラスの作成
u クラス内にgetProperties()というメソッ
ドがありますが、意味は後ほど説明しま
す
60
61. (C) CASAREAL, Inc. All rights reserved.
リソースクラス
u @Path+@GETでURLマッピングを指定
u @PathParamでパス内のパラメータを取得
61
62. (C) CASAREAL, Inc. All rights reserved.
リソースクラスのコード例
62
@Path("employees")
public class EmployeeResource {
@GET @Path("{id}")
public Response find(@PathParam("id") Integer id) {
…
}
}
GET http://<中略>/api / employees / 1
63. (C) CASAREAL, Inc. All rights reserved.
生成するメディアタイプ
u @Producesアノテーションで、レスポンス
のContent-Typeを指定する
63
@Path("employees")
public class EmployeeResource {
@GET @Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response find(@PathParam("id") Integer id) {
…
64. (C) CASAREAL, Inc. All rights reserved.
★演習★ リソースクラスの作成
u 演習1-2:クラスに@Pathを付加する
u 演習1-3:メソッドに@GET、@Path、
@Producesを付加する
u 演習1-4:メソッドの引数に@PathParam
を付加する
64
65. (C) CASAREAL, Inc. All rights reserved.
リソースメソッドの戻り値
u HTTPステータスコードと、JSONに変換
したいオブジェクトを戻り値にする
65
@GET @Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response find(@PathParam("id") Integer id) {
…
return Response. ok(employeeDto) .build();
}
200 OK、employeeDtoをJSON化
66. (C) CASAREAL, Inc. All rights reserved.
★演習★ リソースクラスの作成
u 演習1-5:メソッドの戻り値を記述する
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビルド]
u (2)プロジェクトを右クリック→[実行]
u (3) コメント内のcurlコマンドをコマンドプロン
プトで実行
66
※データベースが
DROP-CREATE-INSERTされます
67. (C) CASAREAL, Inc. All rights reserved.
【補足】MessageBodyWriter
u リソースメソッドの戻り値に渡されたオブ
ジェクトを、JSONやXMLに変換する役割を
持つインターフェイス
u オブジェクトの型・@Producesで指定され
た形式・Acceptリクエストヘッダで、どの
MessageBodyWriter実装クラスが動くかが
自動的に判断される
67
68. (C) CASAREAL, Inc. All rights reserved.
【補足】JSONパーサー
u MessageBodyWriter内で使われる、オブ
ジェクトをJSONに変換するライブラリ
u Payara/GlassFishには「MOXy(デフォル
ト)」「Jackson」「Jettison」が内包
u 今回はMOXyを無効化してJacksonを有効
化しています
68
69. (C) CASAREAL, Inc. All rights reserved.
【補足】MOXyの無効化
u Applicationサブクラス内でgetProperties()をオー
バーライドし、下記の記述を追加
69
@ApplicationPath("api")
public class MyApplication extends Application {
@Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<>();
properties.put(CommonProperties.MOXY_JSON_FEATURE_DISABLE,
Boolean.TRUE);
return properties;
}
}
70. (C) CASAREAL, Inc. All rights reserved.
【補足】Jacksonの有効化
u 下記のクラスを作成して@Providerを付加する
70
@Provider
public class ObjectMapperResolver
implements ContextResolver<ObjectMapper> {
@Override
public ObjectMapper getContext(Class<?> type) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
return objectMapper;
}
}
71. (C) CASAREAL, Inc. All rights reserved.
【補足】JAX-RSの例外
u HTTPステータスコードを表す例外が用意され
ている(下記は一部)
u WebApplicationException
u ClientErrorException (4xx)
u BadRequestException (400)
u NotFoundException (404)
u ServerErrorException (5xx)
u InternalServerErrorException (500)
71
72. (C) CASAREAL, Inc. All rights reserved.
【補足】例外処理
u ExceptionMapperインターフェイスを実装した
クラスに、@Providerアノテーションを付加する
72
@Provider
public class WebApplicationExceptionMapper
implements javax.ws.rs.ext.ExceptionMapper<WebApplicationException> {
@Override
public Response toResponse(WebApplicationException exception) {
exception.printStackTrace(); // 本来はログを取る
ExceptionDto exceptionDto = createExceptionDto(exception);
return Response.status(exception.getResponse().getStatusInfo())
.entity(exceptionDto).build();
}
}
73. (C) CASAREAL, Inc. All rights reserved.
クエリパラメータの取得
u @QueryParamで取得
u @DefaultValueで、そのクエリパラメータが無
かった場合のデフォルト値を指定可能
73
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response findByName(
@QueryParam("name") @DefaultValue("")
String name) { … }
74. (C) CASAREAL, Inc. All rights reserved.
Bean Validation
u リソースメソッドの引数に検証アノテー
ションを指定
74
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response findByName(
@QueryParam("name") @DefaultValue("")
@Pattern(regexp = "[a-zA-Z]*") @Size(max = 10)
String name) { … }
75. (C) CASAREAL, Inc. All rights reserved.
★演習★ クエリパラメータ
による検索
u 演習2-1:クエリパラメータを取得する
u 演習2-2:検証アノテーションの追加
u 演習2-3:リソースメソッドの戻り値を記述する
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビルド]
u (2)プロジェクトを右クリック→[実行]
u (3) コメント内のcurlコマンドをコマンドプロンプトで実行
75
※データベースが
DROP-CREATE-INSERT
されます
76. (C) CASAREAL, Inc. All rights reserved.
【補足】検証エラー時は例外が発生
u ConstraintViolationExceptionが発生する
u この例外に対応したExceptionMapperを作って
おく必要がある
76
@Provider
public class ConstraintViolationExceptionMapper
implements javax.ws.rs.ext.ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(ConstraintViolationException exception) {
…
}
}
77. (C) CASAREAL, Inc. All rights reserved.
【補足】本セッションにおける
異常時のステータスコード
u ID検索→該当IDなしは404、
IDに数字以外が来ても404
u 複数件検索→検索結果0件の場合、
200で空のJSON配列を返す
u バリデーションエラー→400
77
78. (C) CASAREAL, Inc. All rights reserved.
CRUD操作とHTTPリクエストメソッドと
HTTPレスポンスステータスコード
78
HTTPリクエスト
メソッド
HTTPレスポンス
ステータスコード
検索 GET 200 OK
追加 POST 201 CREATED
更新 PUT 200 OK
削除 DELETE 204 NO CONTENT
79. (C) CASAREAL, Inc. All rights reserved.
POSTによる追加
u メソッドには@POSTを付加
u @ConsumesでリクエストのContent-Typeを指定
79
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response insert(
EmployeeForm form) {
…
80. (C) CASAREAL, Inc. All rights reserved.
リクエストで送られたJSONの取得
80
@POST
@Consumes(…) @Produces(…)
public Response insert(
EmployeeForm form) {
…
{
"name" : "Mai Shiraishi",
"joined_date" : "2015-11-28",
"department" : {
"dept_id" : 1
}
}
public class EmployeeForm {
private String name;
@JsonProperty("joined_date")
private String joinedDate;
@JsonProperty("department")
private DepartmentForm departmentForm
= new DepartmentForm();
}
public class DepartmentForm {
@JsonProperty("dept_id")
private String deptId;
}
u リソースメソッドの引数の型
のプロパティ名と、JSONのプ
ロパティ名が同じであればOK
81. (C) CASAREAL, Inc. All rights reserved.
★演習★ POSTによる追加
u 演習3-1:リクエストのContent-Typeを
MediaType.APPLICATION_JSONに指定する
u @POSTは付加済み
81
82. (C) CASAREAL, Inc. All rights reserved.
Bean Validation
u 前ページのFormク
ラスの各フィール
ドに検証アノテーショ
ンを付加する
u リソースメソッド
の引数に@Validアノ
テーションを付加
82
public class EmployeeForm {
@NotNull
@Size(min = 1, max = 40)
@Pattern(regexp = "[a-zA-Zs]*")
private String name;
@DatePattern(pattern = "yyyy-MM-dd")
@JsonProperty("joined_date")
private String joinedDate;
@Valid
@JsonProperty("department")
private DepartmentForm departmentForm
= new DepartmentForm();
@POST
@Consumes(…) @Produces(…)
public Response insert(
@Valid EmployeeForm form) {
…
83. (C) CASAREAL, Inc. All rights reserved.
レスポンス
u 201 CREATEDを返す
u 追加が完了したデータを表すURLを
locationレスポンスヘッダに追加する
83
@POST @Consumes(…) @Produces(…)
public Response insert(@Valid EmployeeForm form) {
…
URI location = uriInfo.getAbsolutePathBuilder()
.path(employee.getEmpId().toString()).build();
return Response.created(location)
.entity(employeeDto).build();
}
84. (C) CASAREAL, Inc. All rights reserved.
★演習★ POSTによる追加
u 演習3-2:リソースメソッドに引数に@Validを付加する
u 演習3-3:リソースメソッドの戻り値を記述する
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビルド]
u (2)プロジェクトを右クリック→[実行]
u (3) コメント内のcurlコマンドをコマンドプロンプトで実
行
84
※データベースが
DROP-CREATE-INSERT
されます
85. (C) CASAREAL, Inc. All rights reserved.
【補足】MessageBodyReader
u リクエストから受け取ったJSONやXML
を、リソースメソッドの引数の型に変換
する役割を持つインターフェイス
u 引数の型と@Consumesで指定された形式
で、どのMessageBodyReader実装クラス
が動くかが自動的に判断される
85
86. (C) CASAREAL, Inc. All rights reserved.
【補足】Formクラス・DTOクラス・
Entityクラスは分ける
u 持つべきフィールド・従うべき規約・付
加すべきアノテーションが異なるので、
似たクラスだが分けたほうが良い
u Form → Bean Validation
u DTO → JSON, JAXB
u Entity → JPA
86
87. (C) CASAREAL, Inc. All rights reserved.
PUTによる更新
u メソッドには@PUTと@Consumesを付加
u 更新対象の主キーは@Pathで指定
u 200 OKとlocationヘッダを返す
87
@PUT @Path("{id}") @Consumes(…) @Produces(…)
public Response update(@PathParam("id") Integer id,
EmployeeForm form) {
…
URI location = uriInfo.getAbsolutePath();
return Response.ok(employeeDto)
.location(location).build();
}
88. (C) CASAREAL, Inc. All rights reserved.
★演習★ 更新
u 演習4:更新リソースメソッドの戻り値を記述す
る
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビルド]
u (2)プロジェクトを右クリック→[実行]
u (3) コメント内のcurlコマンドをコマンドプロンプト
で実行
88
※データベースが
DROP-CREATE-INSERTされます
89. (C) CASAREAL, Inc. All rights reserved.
DELETEによる削除
u メソッドには@DELETEを付加
u 更新対象の主キーは@Pathで指定
u 204 NO CONTENTとlocationヘッダを返す
89
@DELETE @Path("{id}")
public Response delete(@PathParam("id") Integer id) {
…
URI location = uriInfo.getAbsolutePath();
return Response.noContent()
.location(location).build();
}
90. (C) CASAREAL, Inc. All rights reserved.
★演習★ 削除
u 演習5:削除リソースメソッドの戻り値を記述す
る
u ここまで出来たら実行してみましょう
u (1)プロジェクトを右クリック→[消去してビルド]
u (2)プロジェクトを右クリック→[実行]
u (3) コメント内のcurlコマンドをコマンドプロンプト
で実行
90
※データベースが
DROP-CREATE-INSERTされます
91. (C) CASAREAL, Inc. All rights reserved.
日本語書籍について
u JavaによるRESTfulシステム構築
u 日本語書籍はJAX-RS 1.0 (Java EE 6)
対応版のみ
u JAX-RS 2.0 (Java EE 7)対応版は、
出版されているが英語版のみ
u 日本語版で基礎を身につけた後、
EE 7対応の英語版を読むとよい
91
92. (C) CASAREAL, Inc. All rights reserved.
日本語書籍について
u Web API : The Good Parts
u JAX-RSの本ではないが、RESTの基礎
として知っておいたほうが良いこと
が満載
u URLをどのように設計するか、異常
時にどのようなレスポンスを返すか
など
92
93. (C) CASAREAL, Inc. All rights reserved.
その他に知っておいたほうが良いこと
u 本日のうらがみさんの資料を確認しましょう
u 特にセキュリティ周りは実装独自機能を多用
するので、使用する実装のドキュメントも確
認しておきましょう
u Jersey:https://jersey.java.net/documentation/latest/
user-guide.html
u RESTEasy:http://resteasy.jboss.org/docs.html
u Apache CXF:http://cxf.apache.org/docs/index.html
93
95. (C) CASAREAL, Inc. All rights reserved.
JSF・JAX-RSはStrutsから移行しづらい?
u JSFはコンポーネントベース
u JAX-RSでブラウザの画面を作るには
JavaScriptフレームワークが必要
u どちらにしても、それなりの学習コスト・
移行コストがある
95
96. (C) CASAREAL, Inc. All rights reserved.
Model-View-Controller API (MVC 1.0)
u Java EE 8で導入予定の、
HTMLを返すアクションベースフレームワーク
u JAX-RSベース
u URLマッピング、バリデーション、例外処理な
ど、ほとんどJAX-RSの機能を流用している
u 詳細は下記の拙著をご参照ください
http://www.slideshare.net/masatoshitada7/java-ee-8mvc-10
96
97. (C) CASAREAL, Inc. All rights reserved.
しかし…
u Java EE 8は2017年上半期リリース予定
u Java EE 7時代の「今」はどうするか?
97
98. (C) CASAREAL, Inc. All rights reserved.
Jersey MVCとは?
u Jersey独自機能として作られている、
HTMLを返すアクションベースフレーム
ワーク
u MVC 1.0に近い
u もちろん相違点は存在する
98
99. (C) CASAREAL, Inc. All rights reserved.
Java EE 7時代のWeb開発
「第3の選択肢」
u Java EE 7時代の今はJersey MVCを使う
u EE 8対応のAPサーバーがリリースされた
ら、MVC 1.0に移行する
u GlassFishは2017年リリース予定(EE 8と同時)
u 有償サーバーは2019∼2020年?
99
100. (C) CASAREAL, Inc. All rights reserved.
Jersey MVCのライブラリ追加
u GlassFish/Payaraには内包されている
u WebLogicにはJersey MVCの追加が必要
u その他のAPサーバーでは、内包されているJAX-RS実
装を無効化して、Jersey + Jersey MVCを追加する
u RESTEasy(WildFly/JBoss内包)には、「REST Easy
HTML Provider」というJersey MVCに似た機能も存
在する
100
101. (C) CASAREAL, Inc. All rights reserved.
Jersey MVCの注意点
u MVC 1.0にはあるが、Jersey MVCには無い
機能がある
u 逆もまた然り
u 作り込み過ぎてMVC 1.0の再発明にならな
いように注意
u MVC 1.0は今後も仕様が変更される可能性が高い
101
102. (C) CASAREAL, Inc. All rights reserved.
設定クラス
u ResourceConfigを継承し、JspMvcFeature
を登録する
102
@ApplicationPath("api")
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(JspMvcFeature.class);
register(MvcBeanValidationFeature.class);
property(JspMvcFeature.TEMPLATE_BASE_PATH, "/WEB-INF/views/");
packages(true, this.getClass().getPackage().getName());
}
}
103. (C) CASAREAL, Inc. All rights reserved.
コントローラークラス
u リソースメソッドでViewableを返す
103
@Path("employees") @RequestScoped
public class EmployeeIndexResource {
@GET @Path("index")
public Viewable index() throws Exception {
...
return new Viewable("index");
}
}
104. (C) CASAREAL, Inc. All rights reserved.
JSPファイルの保存場所
(絶対パス指定)
u 上記の場合、WEB-INF/views/hoge/fuga/
index.jspにフォワードする
u WEB-INF/viewsは、ResourceConfigサブクラスで
JspMvcFeature.TEMPLATE_BASE_PATHで指定した場所
104
package com.example.rest.resource;
public class EmployeeIndexResource {
@GET @Path("index") public Viewable index() {
return new Viewable("/hoge/fuga/index");
}
}
105. (C) CASAREAL, Inc. All rights reserved.
JSPファイルの保存場所
(相対パス指定)
u 上記の場合、WEB-INF/views/com/example/
rest/resource/EmployeeIndexResource/
index.jspにフォワードする
105
package com.example.rest.resource;
public class EmployeeIndexResource {
@GET @Path("index") public Viewable index() {
return new Viewable("index");
}
}
106. (C) CASAREAL, Inc. All rights reserved.
MVC 1.0のビューの保存場所
u 現時点のMVC 1.0の仕様では、
Jersey MVCの絶対パス指定が最も近い
u 今のところ、デフォルトでは/WEB-INF/views/
配下に置くことしか決まっていない
106
107. (C) CASAREAL, Inc. All rights reserved.
コントローラーからビューへの
値の受け渡し
u MVC 1.0・Jersey MVC両方で利用できることから、
CDI Beanを利用する
107
@Inject EmployeeListDto employeeListDto;
@GET @Path("index")
public Viewable index() {
employeeListDto.setEmployeeList(employeeList);
return new Viewable("index");
}
<c:forEach items="${employeeListDto.employeeList}" var="emp">
…
108. (C) CASAREAL, Inc. All rights reserved.
例外処理はExceptionMapper
u Jersey MVCとMVC 1.0でほぼ同じ
u Viewableのパッケージ名が違うだけ
108
@Provider @ApplicationScoped
public class ExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<Exception> {
@Inject private ExceptionDto exceptionDto;
@Override
public Response toResponse(Exception exception) {
...
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new Viewable("/error/exception")).build();
}
}
109. (C) CASAREAL, Inc. All rights reserved.
バリデーション
u MVC 1.0 → BindingResult
u Jersey MVC → @ErrorTemplate
u だいぶ違っていてまだ悩み中なので、
近日中にブログ書きます
u 2015 Java EE アドベントカレンダーかも?
109
110. (C) CASAREAL, Inc. All rights reserved.
日本語情報
u Jersey MVCの日本語書籍は無いがブログ情報は多い
u http://qiita.com/opengl-8080/items/
f4c25ad671e8a6dac743 など
u ブログ情報はやや古いものもあるので(EE 6など)、
Jersey公式ドキュメントも読んで最新情報を確認し
たほうがよい
u https://jersey.java.net/documentation/latest/user-
guide.html#mvc
110
112. (C) CASAREAL, Inc. All rights reserved.
Java EE 7時代の「今」は何を選ぶか?
u Java EE 7標準にこだわりたい → JSF
u JavaScriptフレームワークやモバイル連携
にこだわりたい → JAX-RS
u アクションベースにこだわりたい
→ Jersey MVCからMVC 1.0に移行
112
113. (C) CASAREAL, Inc. All rights reserved.
Enjoy & let’s start Java EE !!
u ご参加ありがとうございました!
113