JavaEE Advent Calendar 2012 1日目の記事です。
Java EE6 ハンズオンラボ
Oracleの@arunguptaさんが公開しているJavaEE6ハンズオンラボはご存知でしょうか? このハンズオンでは簡単なアプリケーションを作成することでJavaEE6の主要技術を一通り学ぶことができます。 扱っている技術は
- JPA 2 (MetaModelを使ったCriteriaQuery含む)
- Sevlet 3
- EJB 3.1 (@Schedule含む)
- JSF 2 (Ajax含む)
- JAX-RS
- CDI (Interceptor含む)
- Bean Validation
です。46ページのドキュメントですが要領よく、かなり網羅されています。
これからJavaEE6を始める人の最初の一歩にぴったりではないでしょうか。
金魚本についている付録のハンズオンはこの資料の前のバージョンの翻訳です(あれを書いたのも実は自分なのですが。。色々すいません)。
そして今回のバージョンの資料は細々と翻訳しています。 https://github.com/making/javaee6-hol-glassfish-doc
翻訳バージョンはhttps://javaee6-hol-glassfish-doc.readthedocs.org/から参照可能です(随時更新中)
訳がいまいちなところも多々あるので、翻訳に協力してくれる方はpull requestください! よろしくお願いします!!
JBoss Forgeでチュートリアルを進めてみる
ではこのチュートリアルをJBoss Forgeで作ってみましょう。 本家チュートリアルではNetBeansを使ってJavaEE6アプリケーションを作成していますが、 折角なのであえてJBoss Forgeでもやってみます。
元々次の回に書くつもりでしたが、JavaEE Advent Calendarが埋まっちゃったので。。
JBoss Forgeって何?
JBoss ForgeはJava EE用のRADツールで、プロジェクト(+ソース)雛形生成ツールという色が強いです。コマンドラインでMavenベースドなプロジェクトを構築して、ソースコードを生成していきます。 JBossプロダクトですが、APサーバーはJBoss ASに限定しているわけではなく、Glass Fishでも全然OKです。 似たようなプロジェクトでSpring Rooがありますが、Rooは生成されたコードがAspectJの黒魔術すぎて、生成した後実際にどうプロジェクトを進めて良いかわからん!というのに対して、Forgeはplainなソースコードを吐いてくれるので、出力したあと自分で作り込んでいくというのが容易です。
JBoss Forgeをインストール
Macユーザーはbrewでインストールできます。非Macユーザーは自分で調べてください。Mac買えや。
$ brew install jboss-forge
==> Downloading https://repository.jboss.org/nexus/service/local/artifact/maven/redirect?r=releases&g=org.jboss.forge&a=forge-distribution&v=1.1.1.Final&e=zi
######################################################################## 100.0%
/usr/local/Cellar/jboss-forge/1.1.1.Final: 209 files, 29M, built in 107 seconds
JBoss Forgeを実行
forge
コマンドで起動します。環境変数JAVA_HOME
が設定されている必要があります。
Macユーザーは
$ export JAVA_HOME=/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/
な感じ。
$ forge
Using Forge at /usr/local/Cellar/jboss-forge/1.1.1.Final/libexec
_____
| ___|__ _ __ __ _ ___
| |_ / _ \| `__/ _` |/ _ \ \\
| _| (_) | | | (_| | __/ //
|_| \___/|_| \__, |\___|
|___/
JBoss Forge, version [ 1.1.1.Final ] - JBoss, by Red Hat, Inc. [ http://jboss.org/forge ]
プロジェクト作成
new-project
コマンドでプロジェクトを作成します。名前とトップレベルのパッケージを指定します(Rooとそっくり)。チュートリアルにしたがってプロジェクト名を"JavaEE6SampleApp"、トップレベルのパッケージを"org.glassfish.samples"
[no project] forge $ new-project --named JavaEE6SampleApp --topLevelPackage org.glassfish.samples
? Use [/Users/maki/forge/JavaEE6SampleApp] as project directory? [Y/n]
***SUCCESS*** Created project [JavaEE6SampleApp] in new working directory [/Users/maki/forge/JavaEE6SampleApp]
Wrote /Users/maki/forge/JavaEE6SampleApp
Wrote /Users/maki/forge/JavaEE6SampleApp/pom.xml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/java
Wrote /Users/maki/forge/JavaEE6SampleApp/src/test/java
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/resources
Wrote /Users/maki/forge/JavaEE6SampleApp/src/test/resources
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples
Scaffoldの設定
RADツール定番のScaffoldです。まずはセットアップでViewテクノロジーを選択します。当然JSFを指定。scaffold
コマンドを実行します。設定はデフォルトでいいのでEnterを押しまくってください。
[JavaEE6SampleApp] JavaEE6SampleApp $ scaffold setup --scaffoldType faces
? Scaffold provider [faces] is not installed. Install it? [Y/n]
? Facet [forge.maven.WebResourceFacet] requires packaging type(s) [war], but is currently [jar]. Update packaging? (Note: this could deactivate other plugins in your project.) [Y/n]
***SUCCESS*** Installed [forge.maven.WebResourceFacet] successfully.
Use which version of 'jboss-javaee-6.0' ?
1 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Beta4]
2 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Beta5]
3 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Beta6]
4 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Beta7]
5 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.CR1]
6 - [org.jboss.spec:jboss-javaee-6.0:pom::1.0.0.Final]
7 - [org.jboss.spec:jboss-javaee-6.0:pom::2.0.0.Beta1]
8 - [org.jboss.spec:jboss-javaee-6.0:pom::2.0.0.CR1]
9 - [org.jboss.spec:jboss-javaee-6.0:pom::2.0.0.Final]
10 - [org.jboss.spec:jboss-javaee-6.0:pom::2.1.0.Beta1]
11 - [org.jboss.spec:jboss-javaee-6.0:pom::3.0.0.Beta1]
12 - [org.jboss.spec:jboss-javaee-6.0:pom::3.0.0.Final]
13 - [org.jboss.spec:jboss-javaee-6.0:pom::3.0.1.Final]*
? Choose an option by typing the number of the selection [*-default] [0]
***SUCCESS*** Installed [forge.spec.jpa] successfully.
***SUCCESS*** Installed [forge.spec.ejb] successfully.
***SUCCESS*** Installed [forge.spec.cdi] successfully.
***SUCCESS*** Installed [forge.spec.servlet] successfully.
***SUCCESS*** Installed [forge.spec.jsf.api] successfully.
***SUCCESS*** Installed [faces] successfully.
? Create scaffold in which sub-directory of web-root? (e.g. http://localhost:8080/JavaEE6SampleApp/DIR) [/]
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp
Wrote /Users/maki/forge/JavaEE6SampleApp/pom.xml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/resources/META-INF/persistence.xml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/WEB-INF/beans.xml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/WEB-INF/faces-config.xml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/favicon.ico
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/scaffold/paginator.xhtml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/scaffold/pageTemplate.xhtml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/index.html
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/index.xhtml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/error.xhtml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/add.png
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/bootstrap.css
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/false.png
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/favicon.ico
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/forge-logo.png
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/forge-style.css
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/remove.png
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/search.png
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/true.png
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/jboss-community.png
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/WEB-INF/web.xml
実はデフォルトでJSFしか選べません。プラグインをインストールすることでViewを追加することが出来ます。なんとSpring MVC版も作れたりします!
JPAの設定
JPAの設定をします。Hibernate、JBoss ASを使わず、あえてEclipseLink、GlassFishを選択します。データソースのJNDI名もチュートリアルにあわせてjdbc/sampleに。
[JavaEE6SampleApp] JavaEE6SampleApp $ persistence setup --provider ECLIPSELINK --container GLASSFISH_3 --jndiDataSource jdbc/sample
***INFO*** Setting transaction-type="JTA"
***INFO*** Overriding example datasource with [jdbc/sample]
? Do you want to install a JPA 2 metamodel generator? [y/N]
? The JPA provider [ECLIPSELINK], also supplies extended APIs. Install these as well? [y/N]
***SUCCESS*** Persistence (JPA) is installed.
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/resources/META-INF/persistence.xml
次のようなpersistence.xmlが出力されます。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="forge-default" transaction-type="JTA">
<description>Forge Persistence Unit</description>
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/sample</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>
Entityを作成
チュートリアルでは既存でDBテーブルからリバースエンジニアリングでEntityを生成していますが、今回はまずは先にEntityを作成してみます。連絡先Eメールアドレスと名前を持ったContactエンティティを作成してみます。パッケージ名はorg.glassfish.samples.entities
で。
[JavaEE6SampleApp] JavaEE6SampleApp $ entity --named Contact --package org.glassfish.samples.entities --idStrategy AUTO
Created @Entity [org.glassfish.samples.entities.Contact]
Picked up type <JavaResource>: org.glassfish.samples.entities.Contact
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Contact.java
[JavaEE6SampleApp] Contact.java $ field string --named email
Added field to org.glassfish.samples.entities.Contact: @Column private String email;
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Contact.java
[JavaEE6SampleApp] Contact.java $ field string --named name
Added field to org.glassfish.samples.entities.Contact: @Column private String name;
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Contact.java
ls
コマンドでフィールド、メソッド一覧を表示できます。id、versionは勝手に付与されます。
[JavaEE6SampleApp] Contact.java $ ls
[fields]
private::Long::id; private::String::email; private::String::name;
private::int::version;
[methods]
public::equals(Object that)::boolean public::getEmail()::String
public::getId()::Long public::getName()::String
public::getVersion()::int public::hashCode()::int
public::setEmail(final String email)::void public::setId(final Long id)::void
public::setName(final String name)::void public::setVersion(final int version)::void
public::toString()::String
実際には次のようなソースが出力されています。
package org.glassfish.samples.entities;
import javax.persistence.Entity;
import java.io.Serializable;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Column;
import javax.persistence.Version;
import java.lang.Override;
@Entity
public class Contact implements Serializable
{
@Id
private @GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
Long id = null;
@Version
private @Column(name = "version")
int version = 0;
@Column
private String email;
@Column
private String name;
public Long getId()
{
return this.id;
}
public void setId(final Long id)
{
this.id = id;
}
public int getVersion()
{
return this.version;
}
public void setVersion(final int version)
{
this.version = version;
}
@Override
public boolean equals(Object that)
{
if (this == that)
{
return true;
}
if (that == null)
{
return false;
}
if (getClass() != that.getClass())
{
return false;
}
if (id != null)
{
return id.equals(((Contact) that).id);
}
return super.equals(that);
}
@Override
public int hashCode()
{
if (id != null)
{
return id.hashCode();
}
return super.hashCode();
}
public String getEmail()
{
return this.email;
}
public void setEmail(final String email)
{
this.email = email;
}
public String getName()
{
return this.name;
}
public void setName(final String name)
{
this.name = name;
}
public String toString()
{
String result = "";
if (email != null && !email.trim().isEmpty())
result += email;
if (name != null && !name.trim().isEmpty())
result += " " + name;
return result;
}
}
EntityからScaffold生成
さっきセットアップしたScaffoldを実際に生成しています。
[JavaEE6SampleApp] Contact.java $ scaffold from-entity
***INFO*** Using currently installed scaffold [faces]
? [/Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/scaffold/pageTemplate.xhtml] File exists, overwrite? [Y/n]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.Contact]
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/view/ContactBean.java
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/contact/create.xhtml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/contact/view.xhtml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/contact/search.xhtml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/resources/scaffold/pageTemplate.xhtml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/view/ViewUtils.java
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/webapp/WEB-INF/classes/META-INF/forge.taglib.xml
Wrote /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Contact.java
パッケージ名を指定しないとviewパッケージに出力されます。まあいいか。
NetBeanでプロジェクトを開く
ForgeでできたプロジェクトはMavenプロジェクトなのでNetBeansでそのまま開けます。開く前にJTAのセットアップをしておきます。これがないとNetBeansでエラーになります。
[JavaEE6SampleApp] Contact.java $ jta setup
***SUCCESS*** Installed [forge.spec.jta] successfully.
***SUCCESS*** Java Transaction API (JTA) is installed.
Wrote /Users/maki/forge/JavaEE6SampleApp/pom.xml
NetBeansを開いて「プロジェクトを開く」で"JavaEE6SampleApp"を選択し開きます。
こんな感じのプロジェクトになります。
一時的にsrc/main/resources/META-INF/persistence.xmlの"eclipselink.ddl-generation"の値を"create-tables"に変えておきます。
<property name="eclipselink.ddl-generation" value="create-tables"/>
GlassFishを起動した後、プロジェクト名を右クリックして「実行」をクリック。サーバーを選択。
ブラウザが立ち上がり次のような画面が表示されます。
左側の"Contact"をクリックするとContactエンティティのCRUD画面へ遷移します。
既存のテーブルからEntityをリバースエンジニアリングする
チュートリアル同様、APPデスキーマのテーブルからEntityを自動生成してみます。 実はリバースエンジニアリング機能は標準ではなくて、hibernate-toolsプラグインをインストールする必要があります。
[JavaEE6SampleApp] Contact.java $ forge install-plugin hibernate-tools
NetBeansで立ち上げているDerbyに接続して自動生成させます。derbyclient.jarの位置は適宜修正してください。
[JavaEE6SampleApp] entities $ generate-entities --datasource jdbc/sample --url jdbc:derby://localhost:1527/sample --schema APP --user app --dialect org.hibernate.dialect.DerbyDialect --driver org.apache.derby.jdbc.ClientDriver --pathToDriver /Applications/NetBeans/glassfish-3.1.2.2/javadb/lib/derbyclient.jar --entityPackage org.glassfish.samples.entities
? Enter the password for JDBC connection. ***
Found 9 tables in datasource
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Customer.java
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Sequence.java
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Product.java
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Manufacturer.java
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/PurchaseOrder.java
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/MicroMarket.java
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/ProductCode.java
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/Contact.java
Generated java at /Users/maki/forge/JavaEE6SampleApp/src/main/java/org/glassfish/samples/entities/DiscountCode.java
Generated 9 java files.
エンティティが生成されました。"eclipselink.ddl-generation"を"none"に変更します。
<property name="eclipselink.ddl-generation" value="none"/>
そしてScaffoldを再実行。
[JavaEE6SampleApp] entities $ scaffold from-entity org.glassfish.samples.entities.* --overwrite
***INFO*** Using currently installed scaffold [faces]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.Contact]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.Customer]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.DiscountCode]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.Manufacturer]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.MicroMarket]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.Product]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.ProductCode]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.PurchaseOrder]
***SUCCESS*** Generated UI for [org.glassfish.samples.entities.Sequence]
Wrote (略..)
再実行すると次のようにエンティティの選択肢が増えます。
さてここからチュートリアルではServletを作ったり、EJBを作ったりするわけですが、 本記事ではこの辺で終了しておきます。。というかNetBeansで直接作っちゃってOKです。 (ちなみに↑くらいのScaffoldならNetBeans単体で実現できます。。出力されるManagedBeanのソースもあまり奇麗でない。。)
src/main/webapp/resources/scaffold/pageTemplate.xhtmlにFaceletsのテンプレートができているので、 これをベースに新規ページを作っていくのが良いのではないでしょうか。
(ちなみに次のGlassFish勉強会でもこのネタ発表します。。ネタ少なくてすいません)
2日目は@megascusさんお願いします!