JavaEE 7をやってみよう。 Webデプロイメントアセンブリー
JPA編 その3の補足。
JPA編 その3で、
動的WebプロジェクトからEJBプロジェクトを参照できるようにするため、
動的Webプロジェクトの参照プロジェクトにEJBプロジェクトを加えていたが、
よくよくEclipseのマーカーを見ると警告が出ていることに気づいた。
クラスパス・エントリー /SampleEjb は、エクスポートまたは公開されません。実行時に ClassNotFoundExceptions が発生する可能性があります。
なんのこっちゃ、とは思ったが、
よくよく考えてみれば、ワークスペース上でビルドが通る様にしているだけなので、
デプロイした場合、モジュールが欠落している可能性も考え得る。
その警告と読み取れた。
それで、設定すべき別の方法があるのでは無いかと、
エラーメッセージを手がかりに調べてみた。
……が、よくわからず、プロジェクトプロパティをもう一度見なおして、
それっぽいものもを見つけた。
それが、Webデプロイメントアセンプリーの項目。
ここのManifest Entriesの追加ボタンを押すと、
追加対象にEJBプロジェクトが表示されたので、
試しに追加してみたら警告が消えた。
設定は正しいようなので、これはどういう事なのか改めて調べた。
この記事を読むと、Enterprizeアプリケーションプロジェクトでは、
グループ内のプロジェクトで依存関係が設定できるらしいことがわかった。
JPAプロジェクトはEnterpizeアプリケーションプロジェクトに加えるだけで参照可能になるので、
動的WebプロジェクトからEJBプロジェクトを参照可能とするために、
この操作が必要なのは多少解せないが、そういうものなのだろう。
といったところで、今回は終了。
JavaEE 7をやってみよう。 JSF テンプレートXML その2
その1のつづき。
前回、VIEWを作ったので、
今回は、DB検索を行うManaged Beanを作り、JSFで結果表示を行ってみる。
JSFのManaged BeanについてはCDIの回に少し書いた。
Managed Beanは画面アクションとBeanの処理を紐付けできるような、
いわゆるアクションクラス的な位置付けのクラス。
今回の要件で言えば、
画面表示時に艦艇テーブルから全件抽出できるような処理を持つJavaクラス、ということになる。
ま、JPA編のその8でRESTによるWebAPIを作ったので、
処理内容としてはそれと同じものを作ることになるんだけどね。
といったところで、ソース。
WarshipCatalogクラス
package app.manage; import java.util.List; import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.faces.model.ListDataModel; import javax.inject.Named; import model.Warship; import ejbModule.WarshipServiceLocal; @RequestScoped @Named(value="warShipCat") public class WarshipCatalog { @EJB private WarshipServiceLocal ejb; private ListDataModel<Warship> allList; @PostConstruct private void init() { List<Warship> warShipList = ejb.getAllList(); allList = new ListDataModel<Warship>(warShipList); } public ListDataModel<Warship> getAllList() { return allList; } public void setAllList(ListDataModel<Warship> allList) { this.allList = allList; } }
EJBの使い方はManaged Beanで使っても変わらないので、
難しいところは無いと思う。
@PostConstructアノテーションを付けたメソッドでDB参照を呼び出しているので、
このクラスのインスタンスが作られるたびにDB検索が行われメンバに格納される。
VIEWはそのメンバから値を読み出す、仕掛け的にはただそれだけ。
といったところで、完成。
動確、行ってみよう!
Wildflyを起動して、プロジェクトをデプロイ後、
下記のURLにアクセスするとDBに格納したデータが表示される。
http://localhost:8080/(Webプロジェクト名)/faces/jsf/view/warship.xhtml
安直ではあるが、これでVIEWも完成。
登録、削除機能を作る下地もできたので、
以後はこれを流用して、JPAの調査を続けましょ。
といったところで、次回へ続く。
注) 生成したHTMLについては、JSF テンプレートXML その3にも言及があるので、
そちらも参照のこと。
注その2) PostConstruction内にDB参照を行っているが、この処理はあんまり良くない。
JavaEE 7をやってみよう。 JSF テンプレートXML その1
JPAを使って登録、削除をやろうとした時、
どうしても入力が必要なので、
登録・削除用のVIEWを作ろうと思う。
どうせなら、JSFのテンプレート機能をを使おうと思って、
ちょっと調べてみた。
このへんの記事を読んで、わかったこと。
- XMLネームスペースに"http://java.sun.com/jsf/facelets"を追加することで、
テンプレート用(ってかFacelet用だけど)のタグが使える。 - faceletsカスタムタグのinsertタグをテンプレートに定義しておくと、
実態のVIEWからはめ込むhtmlを指定できる。
これだけで、一応テンプレートが作れそうなので、やってみた。
VIEWの形状をヘッダ、サイドメニュー、本文、フッタを持つ、よくある形式にしてみた。
構成
コンテンツは画面パーツ、レイアウトがひな形、
ビューがレイアウトに従い、パーツを組み合わせた画面と捉えるとわかりやすい。
WebContent + jsf + contents | + common | | footer.xhtml | | header.xhtml | | htmlHeader.xhtml | | menu.xhtml | | | + warship | contents.xhtml | + layout | + common | layout.xhtml | + view warship.xhtml
jsf/layout/common/layout.xml
画面の基礎となるレイアウト。
定義は単純で、画面構成のヘッダ、サイドメニュー、本文、フッタを定義している。
内、ヘッダ、サイドメニュー、フッタはincludeタグを使用してレイアウトで読み込むように定義して、
このレイアウトを使う限り、必ず付加されるようになっている。
加えて、metaタグなんかを含む共通のhead要素であるhtmlHeader.xhtmlも組み込んでいる。
唯一、本文だけはinsertタグを使ってレイアウトを使用するviewからhtmlを指定できる形になっている。
<!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:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <ui:include src="/jsf/contents/common/htmlHeader.xhtml"/> <ui:insert name="extHeader" /> <body> <div id="wrapper"> <div id="header"> <div id="header_body"> <ui:include src="/jsf/contents/common/header.xhtml"/> </div> </div> <div id="contents"> <ui:insert name="content" /> </div> <div id="left-sidebar"> <ui:include src="/jsf/contents/common/menu.xhtml"/> </div> <div id="footer"> <ui:include src="/jsf/contents/common/footer.xhtml"/> </div> </div> </body> </html>
jsf/contens/common/header.xhtml, htmlHeader.xhtml, footer.xhmtl, menu.xhtml
4ファイルともほぼスタブなのだが、特筆する箇所といえば、
htmlHeader.xhtmlに定義したHTMLタイトル。
タイトルタグを指定すると、ブラウザのタイトルバーにページタイトルが表示されるが、
あれを動的に設定できるようにしている。
動的に変えるコンテンツにはinsertタグを用いるのはこれまで通りだが、
タグの中にデフォルト文字列を書いておくことで、
設定時/未設定時の値を使い分けることができるってことらしい。
- htmlHeader.xhtml
<!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:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Cache-Control" content="no-cache" /> <title><ui:insert name="title">Default title</ui:insert></title> <h:outputStylesheet library="css" name="default.css" /> </h:head> </html>
[残り3ファイルはこちら](クリックで開く)
- header.xhtml
<!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:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <h:body> <h:outputLabel value="#{staticText['site-title']}" /> </h:body> </html>
- footer.xhtml
<!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:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <h:body> <h:outputLabel value="#{staticText['copyright']}" /> </h:body> </html>
- menu.xhtml
<!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://java.sun.com/jsf/html"> <h:body> <div>艦艇図鑑</div> <div>艦艇登録</div> <div>艦艇編集</div> <div>艦艇削除</div> </h:body> </html>
jsf/contents/warship/warship.xhtml
艦艇一覧を表示する画面パーツという位置付けのファイル。
以前、JSFの回に書いたテーブルタグによるリスト形式のhtmlと同じような内容となっている。
なんか、無駄にxmlnsをインクルードしているのはご愛嬌ってことで。。。
JPAの流れではJSF用のManaged Beanを作っていなけれど、
これに合わせて、後ほどManaged Beanを作ることになる。
<!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:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:fn="http://java.sun.com/jsp/jstl/functions" xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" xmlns:x="http://java.sun.com/jsp/jstl/xml"> <h:body> <h:form id="form1"> <h2><h:outputLabel value="艦艇図鑑" /></h2> <h:dataTable var="item" value="#{warShipCat.allList}" styleClass="table" headerClass="headerrow" rowClasses="oddrow,evenrow"> <h:column> <f:facet name="header">No.</f:facet> <h:outputText value="#{warShipCat.allList.rowIndex + 1}" /> </h:column> <h:column> <f:facet name="header">艦種</f:facet> <h:outputText value="#{item.warshipType.name}" /> </h:column> <h:column> <f:facet name="header">名前</f:facet> <h:outputText value="#{item.name}" /> </h:column> </h:dataTable> </h:form> </h:body> </html>
jsf/view/warship.xhtml
実際に呼び出す画面となるhtml。
注目すべきはcompositionタグ。
これを使うことでレイアウトを合成できる。
compositionタグの中にxmlnsの定義を書いているのが奇妙といえば奇妙だけれど、
そういうものなんだろうと、いうことにした。
テンプレート側でinsertタグを用いた箇所に値を設定するには、defineタグを用いる。
ここでは、HTMLタイトルと本文をdefineタグを使って値を設定している。
<!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"> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" template="/jsf/layout/common/layout.xhtml"> <ui:define name="title">艦艇図鑑</ui:define> <ui:define name="content"> <ui:include src="/jsf/contents/warship/content.xhtml"/> </ui:define> </ui:composition> </html>
と、JSFのVIEWについてはこれまで。
あと、header.xhtmlやfooter.xhtmlに固定文字列を設定するためのリソースファイルや、
画面デザインのためのCSSを作成した。
これらをすべてWebプロジェクトに組み込めばViewの完成。
あとはManaged Beanを作るだけ。
といったところで、次回へ続く。
注) 生成したHTMLについては、JSF テンプレートXML その3にも言及があるので、
そちらも参照のこと。
[残りファイルはこちら](クリックで開く)
- statictext.properties
site-title= 艦艇これくしょん copyright=(C) ひこぽん(nagamitsu1976)
@CHARSET "UTF-8"; #wrapper { text-align: left; width: 804px; margin: 0 auto; padding: 0; border: 2px solid black; } #header { height: 100px; margin: 0; padding: 0; border-color: black; border-width: 0px 0px 2px 0px; border-style: solid; background-color: #e6e6fa; } #header_body { text-align: center; position: relative; top: 40%; right: 0; bottom: 0; left: 0; } #contents { width: 580px; float: right; margin: 0; padding: 10px 10px 10px 10px; border-color: black; border-width: 0px 0px 0px 2px; border-style: solid; } #left-sidebar { height: 100%; width: 180px; float: left; margin: 0; padding: 10px 10px 10px 10px; border: 0px solid black; } #footer { text-align: center; clear: both; margin: 0; padding: 0.5em 0; border-color: black; border-width: 2px 0px 0px 0px; border-style: solid; background-color: #e6e6fa; } .headerrow { background-color:#e6e6fa; } .oddrow { background-color:#ffffff; } .evenrow { background-color:#ffffcc; } .table { width: 300px; background-color: #b0c4de; border: 1px solid #boc4de; }
- faces-config.xml
<?xml version="1.0" encoding="UTF-8"?> <faces-config 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" version="2.2"> <application> <locale-config> <default-locale>ja</default-locale> </locale-config> <resource-bundle> <base-name>resources.statictext</base-name> <var>staticText</var> </resource-bundle> </application> </faces-config>
JavaEE 7をやってみよう。 JPA その8
その7のつづき。
今度はEJBを呼び出す処理を実装する。
Webアプリ側なので画面とか必要なのだが、
まずは、ちゃんとDB参照できるか手軽に確認するために、
RESTのWebAPIとして機能を作ってみる。
作るクラスは2つ。
RestApplicationクラスとEJBを呼び出すResourceクラスの2つ。
では、ソース。
- RESTアプリケーションクラス
package app; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("/rest") public class RestApplication extends Application { }
- Warshipリソースクラス
package app.res; import java.util.List; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import model.Warship; import org.jboss.resteasy.annotations.providers.jaxb.Formatted; import ejbModule.WarshipServiceLocal; @RequestScoped @Path("/warship") public class WarshipResource { @EJB private WarshipServiceLocal ejb; @Path("/test") @GET @Produces("text/plain") public String getTest() { return "ALive!"; } @Path("/all") @POST @Produces({"application/json;charset=UTF-8"}) @Consumes({"application/json;charset=UTF-8"}) @Formatted public List<Warship> getAll() { List<Warship> allList = ejb.getAllList(); return allList; } }
RESTアプリケーションのアノテーションなんかについては、以前のRESTの項を見てもらうとして*1、
ここではWebアプリケーション→EJBの呼び出しについて補足する。
といっても前回作ったEJBのメソッドを呼んでいるだけ。
メンバにEJBのローカルインターフェイスを定義して、目的の箇所で使用するだけOKなので簡単。
ここではgetAllメソッド内で、EJBからDB呼び出しを行っている。
また、EJBのローカルインターフェイスに@EJBアノテーションを付けておけば、
インスタンスは自動的にインジェクションされる。
といったところで、ようやっと、
Webプロジェクト → EJB → JPA → DBの流れができたので、
動作確認をやってみよう!
デプロイ前に、Wildflyの状態を確認。
DBサーバが起動した状態でWildflyを起動して、
JDBCドライバがデプロイされているかを確認する。
手順についてはJDBCドライバをデプロイした回を参照のこと。
問題がなければ、Eclipseのサーバーのタブから、Wildflyを選択し、
コンテキストメニューから「追加および除去」を選ぶ。
ダイアログから今回作成したEnterprizeアプリケーションプロジェクトを追加して、完了を押す。
しばらく待つと、デプロイが始まってWildflyに配備される。
デプロイ後、ブラウザからテストメソッドを叩いて正しくデプロイで着ているか確認してみる。
URLはhttp://localhost:8080/(Webプロジェクト名)/rest/warship/test
問題なければ、"ALive!"と文字列が表示できる。
リソースクラスの配備にも問題が無いのがわかったところで、
JMeterから全件参照のメソッドを叩いてみる。
URLはhttp://localhost:8080/(Webプロジェクト名)/rest/warship/all
ちなみにWarshipResource.getAll()メソッドをPOSTで作ったので、
ブラウザからURLを直接叩いても値は取得できない。
あらかじめデータをDBに登録しておくと、こんな感じで抽出できる。
なんとかDB参照までこぎつけることが出来た。
あとは、追加、削除あたりをやってみないことには、
JPAの概要は得られないか。
といったところで、次回へ続く。
JavaEE 7をやってみよう。 JPA その7
その6のつづき。
今回はエンティティ取得について実装する。
エンティティの取得はEntityManagerというクラスを用いて行う。
EntityManagerにJPQLというJPA用のSQLのようなものを投げることによって、
DB参照ができるようになる。
なんのことはない、やっぱり問い合わせ文は書くのだ。
しかし、全件参照については自動生成したエンティティクラスにすでに定義されている。
今回はそれを呼び出すようにして、SQLを書かずにDB参照するものを作る。
といったところで、ソース。
package ejbModule; import java.util.List; import javax.ejb.LocalBean; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import model.Warship; /** * Session Bean implementation class WarshipService */ @Stateless @LocalBean public class WarshipService implements WarshipServiceLocal { @PersistenceContext private EntityManager entityManager; /** * Default constructor. */ public WarshipService() { // TODO Auto-generated constructor stub } @Override @TransactionAttribute(TransactionAttributeType.REQUIRED) public List<Warship> getAllList() { return entityManager.createNamedQuery("Warship.findAll", Warship.class).getResultList(); } }
まず、メンバにEntityManaerを定義。
@PersistenceContextを付けることで実行時にインスタンスがインジェクションされる。
getAllListメソッドの処理では、EntityManagerのcreateNamedQueryメソッドを呼んでいる。
引数は定義済みJPQLの名前とその型。
Warshipクラスはともかく、"Warship.findAll"ってどこから来たのか、と思うでしょ。
これ、自動生成したWarshipクラスをよく見ると、
クラスに付加されているアノテーションに妙なSQLと一緒に定義されている。
package model; import java.io.Serializable; import javax.persistence.*; /** * The persistent class for the WARSHIP database table. * */ @Entity @NamedQuery(name="Warship.findAll", query="SELECT w FROM Warship w") public class Warship implements Serializable { 〜〜 略 〜〜 }
createNamedQueryの引数はこれを示している。
ちなみにcreateNamedQueryの戻り値はTypeQuery
これはDBの取得結果ではない。
名前が示す通り、あくまでクエリクラス。
これが持つgetResultListメソッドを呼ぶことで、
ようやっと、DBの抽出結果が得られる、といった仕掛けのようだ。
あと、トランザクションの種類を指定するためにTransactionAttributeアノテーションをメソッドに付けてある。
指定するパラメータはTransactionAttributeType.REQUIREDを指定した。
この属性の種類はいくつかあって、用途によって使い分けるようだけれど、
小難しいことはまだわからないので、デフォルトらしいREQUIREDを指定した。
これは当該セッションがトランザクション開始前ならば新たにトランザクションを開始してから処理を実行するが、
すでにトランザクションが開始されている場合は、開始済みであるトランザクションで処理を実行する、
といった意味がある。
他の属性については、ここを見るといいかもしれない。
といったところで、EJBの実装は終わり。
次はビジネスロジックを呼び出すアプリ側の実装に入る。
次回へ続く。
JavaEE 7をやってみよう。 JPA その6
その5のつづき。
動作確認のためのEJB作りとなる。
WebアプリでなくてもJPAは使えるらしいけど、
せっかくJavaEEをやってるのだから、EJBを使っておこう、
というノリにしておこう。
EJBプロジェクトは、平たく言うとビジネスロジックを記述する層という認識で、
今回のようにDB参照して一覧取得を行う、とか、入力内容をDBに格納する、とか、
そのような処理をまとめとく印象。
EJBクラスの種類には大きくセッションBeanとメッセージBeanってのがあるらしく、
違いとしては前者はセッション同期、後者は非同期ってな感じらしい。
詳しくはOracleのサイトを見ると良いのかも。
というわけで、
先に作ったJPAプロジェクトのエンティティクラスを用いてDB参照を行うビジネスロジックを
EJBで作ってみる。
- 1. セッションBean作成
- 新規から「その他」を選び、
ウィザードから「EJB」→「セッションBean (EJB 3.x)」を選ぶ。 - 2. Bean本体とインターフェースの作成
- パッケージ名とクラス名を入力する。EJBを呼び出す側が使用するインターフェースが必要なのでビジネスインターフェースの作成も有効にする。
リモートとローカルがあるのだが、とりあえず、ローカルのみとする。*1 - 3. 完了
- 完了ボタンを押すとクラスとインターフェースが自動生成される。
- 4. メソッド追加
- コンストラクタ以外にメソッド一つもないので、
ここに機能としてWarshipテーブルから全レコード取得するメソッド定義する。
ローカルインタフェースを開いてメソッドを追加する。 - 5. メソッド実装
- インターフェースに追加したメソッドを実装する。
Eclipseではエラーから簡単にスタブが実装できるので楽だね。
といった具合に外枠だけは埋まった。
あとはDB参照を実装すればOK。
エンティティ呼び出しの実装は次回に続く。
*1:同アプリケーションサーバで稼働する場合はローカル、別アプリケーションサーバで稼働しているモジュールを呼ぶ場合はリモートとなるらしい。参照:ローカルインタフェースとリモートインタフェースの使用
JavaEE 7をやってみよう。 JPA その5
その4のつづき。
エンティティクラスをEJBで使用する際、
DBの接続先を解決する必要がある。
DBの接続先については、
アプリケーションサーバー上に定義したJNDIデータソースを使用する。
データソースについては、その2で設定したものを用いる。
JPAプロジェクトでは接続先の設定をpersistence.xmlに記述する。
xmlを直接編集しても問題ないが、EclipseのJPAプロジェクト(つまり、JBOSSツールを使っていると)ではGUIで編集ができる。
persistence.xmlの構造仕様がわからなくても、なんとかなっちゃう!
って喜ぶ自分は三流なのか。
- 1. persistence.xmlを開く
- プロジェクトのpersistence.xmlを開く。
"JPAコンテンツ"をドリルダウンするとわかりやすい。 - 2. 接続のタブを開く
- 接続のタブを開き、"JTAデータソース"の欄にJNDIデータソース名を記述する。
保存して終了。
今回はJNDIデータソースを使用しているが、
トランザクションタイプを"リソース・ローカル"にすると、
JDBCデータソースの欄が入力できる。
JNDIデータソースを使わない場合は、こちらを設定することになる。
といったところで、JPAプロジェクトの編集は終わり。
実際に動作確認をしてみるために、
以降のステップでEJBとWebプロジェクトを作成する。
次回へ続く。