Seasarプロジェクト見学(その1 SAStruts)
概要
未だ未体験のSeasarプロジェクトを見学してみます。
まずは、DIがどのような作りか体験したっかのですが、
プレゼンテーションだけで12プロジェクトもあってどれを試せばいいのかいきなり迷います。
ググッてヒット件数を比較。
- Cubby の検索結果 約 1,840,000 件
- Mayaa の検索結果 約 153,000 件
- mobylet の検索結果 約 15,500 件
- S2BlazeDS の検索結果 約 6,230 件
- S2Flex の検索結果 約 54,600 件
- S2JSF の検索結果 約 13,300 件
- S2OpenAMF の検索結果 約 686 件
- S2Portlet の検索結果 約 1,850 件
- S2Struts の検索結果 約 22,500 件
- SAStruts の検索結果 約 68,300 件
- Teeda の検索結果 約 182,000 件
- Ymir の検索結果 約 349,000 件
CubbyがダントツですがSeaser以外の「何か」もヒットしています。Cubby seaserだと約 287,000 件でした。
S2Strutsが私の周りでは一番よく聞くような気がしますが、主流派では無いようです。
CubbyかYmirがシンプルそうで気になりますが、
なんとなくはStrutsの拡張っぽくて新しそうなSAStrutsを使ってみることにしました。
導入
公式サイトのセットアップに沿って進めていきます。
1.プラグイン
http://eclipse.seasar.org/updates/3.2/から「Tomcat Launcher」プラグインをEclipseにインストールしろとありますが、見つかりませんでした。「WebLauncher」というそれっぽいものがあるので、代わりに入れてみます。カンです。
http://eclipse.seasar.org/updates/3.3/から「ResourceSynchronizer」と「SAStrutsPlugin」は見つかったのでインストール。
2.チュートリアルプロジェクトの設定
チュートリアルのEclipseプロジェクトをダウンロード。
sa-struts-tutorial-1.0.4-sp8.zip
インポートしたら、Tomcatのjarがうまくパスが通らなかったので、適当に直します。
3.WebLauncherの設定
チュートリアルのプロジェクトはWTPのサーバから認識できませんでした。
先ほど入れたWebLauncherで試してみます。
「WebLauncherダイアログ」
プロジェクト右クリック>[プロジェクト]メニュー>[WebLauncher]を選択
・「Winstoneを使用する。」:チェックをつける
・コンテキスト名:""(空白)
・動作ディレクトリ:/sa-struts-tutorial/src/main/webapp
・ポート番号:8080
・設定ファイル:""(空白)
「SAStrutsダイアログ」
ついでに、SAStrutsの設定
・Webappルート:/src/main/webapp
・メインJavaソース・パス:/src/main/java
・convention.diconパス:/src/main/resources/convention.dicon
・Webサーバ:http://localhost:8080
・コンテキスト:
4.サーバ起動
プロジェクト右クリック>[WebLauncher]メニュー>[サーバを起動する]メニューを選択
http://localhost:8080/にアクセスすると、「Welcome to Super Agile Struts Tutorial」画面が開きました。
Wicketの認証機能
概要
前回、Wicketに認証機能を追加しようとしてSpring Securityを導入してみたのですが、そもそも方向性が間違っていました。Wicketには、wicket-auth-rolesという認証機能のサブプロジェクトがあります。
情報源
Wicketの公式サイトでwicket-auth-rolesの情報を探したのですが、見つかりませんでした。公式サイトからリンクされているWikiにあるかも知れませんが、現在(2010/04/13)メンテナンス中でアクセスできない状態です。
ソースは、Apachiのsvnにそれらしきリリースタグのブランチがありました。
mavenのセントラルリポジトリに見当たらないため、svnリポジトリから直接入手するしかなさそうです。
http://svn.apache.org/repos/asf/wicket/releases/wicket-1.4.7/wicket-auth-roles
また、examplesディレクトリに、wicket-auth-rolesのサンプルがあります。
http://svn.apache.org/repos/asf/wicket/releases/wicket-1.4.7/wicket-examples/src/main/java/org/apache/wicket/examples/authentication
さらに、下記サイトを参考にさせていただきました。
rio's blog
「Wicket の勉強 (4) wicket-auth-roles を使って認証/認可を実現する」
http://rio1218.blog26.fc2.com/blog-entry-82.html
サンプル
svnのexamplesを元に、既存のWicketアプリケーションに「最小限の認証機能」を分かる限り最短で導入する手順を紹介します。
1.pom.xmlにwicket-auth-rolesを追加
<dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket-auth-roles</artifactId> <version>1.4.7</version> </dependency>
2.AuthenticatedWebSessionの導入
AuthenticatedWebSessionを継承した新規クラスを追加します。
import org.apache.wicket.Request; import org.apache.wicket.authentication.AuthenticatedWebSession; import org.apache.wicket.authorization.strategies.role.Roles; public final class MyAuthenticatedWebSession extends AuthenticatedWebSession { private static final long serialVersionUID = 1L; public MyAuthenticatedWebSession(Request request) { super(request); } @Override public boolean authenticate(final String username, final String password) { return username.equals("wicket") && password.equals("wicket"); } @Override public Roles getRoles() { if (isSignedIn()) { return new Roles(Roles.ADMIN); } return null; } }
3.AuthenticatedWebApplicationの導入
Applicationクラスの継承元をWebApplicationから、AuthenticatedWebApplicationに変更
import 既存のインポートクラス・・・ import org.apache.wicket.authentication.AuthenticatedWebApplication; import org.apache.wicket.authentication.AuthenticatedWebSession; import org.apache.wicket.authentication.pages.SignInPage; public class FooApplication extends AuthenticatedWebApplication { @Override protected void init() { super.init(); 既存の実装・・・ } @Override public Class<? extends Page> getHomePage() { 既存の実装・・・ } @Override protected Class<? extends WebPage> getSignInPageClass() { return SignInPage.class; } @Override protected Class<? extends AuthenticatedWebSession> getWebSessionClass() { return MyAuthenticatedWebSession.class; } }
4.@AuthorizeInstantiationの導入
ログインしないと遷移できないページのクラスに@AuthorizeInstantiationアノテーションを追加
@AuthorizeInstantiation("ADMIN")
@AuthorizeInstantiation("ADMIN") public class AdminPage extends BasePage { ・・・
結果
上記例では、未ログインの状態でAdminPageに遷移すると、自動的にログイン画面に当たるSignInPageが開きます。
ここで、ユーザ名="wicket"、パスワード="wicket"を入力しないと先に進めません。
補足説明
- 今回はログイン画面として、組み込みのSignInPageを使用していますが、本来はこのクラスを継承した独自のPageを作成する必要があります。
- MyAuthenticatedWebSession#authenticate(String,String)では、オンコードで判定を行っていますが、実際はここでDB等からユーザ情報を取得する必要があります。
- MyAuthenticatedWebSession#getRoles()でisSignedIn()を使用してログイン判定をしていますが、スーパークラスのAuthenticatedWebApplicationでauthenticate()の戻り値をsignedInフィールドに保持しています。
public final boolean isSignedIn() { return signedIn; } public final boolean signIn(final String username, final String password) { return signedIn = authenticate(username, password); }
- 今回は、getRoles()でオンコーディングでRoles.ADMINを返していますが、ログインユーザによりロールを変える場合は、authenticate()でユーザ情報をセッション内に保持し、getRoles()内でユーザ情報から判定する必要があります。
iBATIS3
概要
iBatis3を導入する目的で評価用のサンプルコードを書いてみました。
参考にしたサイト
記載は書かれた日時が新しい順です。
Oboe吹きプログラマの黙示録
「iBATIS3 と Google guice」(2010/04/10の記事)
http://blogari.zaq.ne.jp/oboe2uran/
SqlSessionFactoryをシングルトンで使用することの問題についての考察があります。
「iBATIS3入門」(2010/03/14の記事)
http://sourceforge.jp/projects/artery/releases/46311
http://sourceforge.jp/projects/artery/downloads/46311/ibatis3.pdf/
ArteryというORマッパープロジェクト内にある、iBATIS3に関する日本語で書かれた入門書です。
分かりやすい内容でおすすめです。
・xmlのにSQLのIN句を指定する方法
・xmlにSQLを書かずにアノテーションで指定する方法
等々についても説明があります。
公式サイトhttp://ibatis.apache.org/index.html
「User Guide (English)」(2010/02/15づけ)
http://svn.apache.org/repos/asf/ibatis/java/ibatis-3/trunk/doc/en/iBATIS-3-User-Guide.pdf
本家サイトの英語で書かれたガイドです。
最新の内容と思われますが、サンプルコードが不親切です。
CodeZine
「iBATISを使ったO/RマッピングによるDBアクセスの実例」(2007/06/07の記事)
http://codezine.jp/article/detail/1289
バージョン2.3に対しての説明です。
サンプルの内容
DROP TABLE IF EXISTS users; CREATE TABLE users ( user_id INT NOT NULL AUTO_INCREMENT, user_name VARCHAR(10), version INT, PRIMARY KEY (user_id) ) ENGINE=InnoDB;
・idは自動採番
・更新時にはvarsionで楽観的排他制御をおこなう
・varsionはSQLで自動インクリメンタル(Javaでは意識しない)
pom.xml
下記を追加
<dependency> <groupId>org.apache.ibatis</groupId> <artifactId>ibatis-sqlmap</artifactId> <version>3.0-beta-10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.10</version> </dependency>
サンプルソース構成
src/test/ibatis /configuration /AppSqlSessionFactory.java /iBatisConfiguration.xml /entity /User.java /UserId.java /UserMapper.xml
サンプルソース
AppSqlSessionFactory.java
package test.ibatis.configuration; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class AppSqlSessionFactory { private final static String XML_PATH = "test/ibatis/configuration/iBatisConfiguration.xml"; public static SqlSessionFactory get() { try { Reader reader = Resources.getResourceAsReader(XML_PATH); return new SqlSessionFactoryBuilder().build(reader); } catch (IOException e) { throw new IllegalStateException(e); } } }
iBatisConfiguration.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/iBatisTest" /> <property name="username" value="root" /> <property name="password" value="ponyo" /> </dataSource> </environment> </environments> <mappers> <mapper resource="test/ibatis/entity/UserMapper.xml" /> </mappers> </configuration>
User.java
package test.ibatis.entity; public class User { private long id; private String name; private long version; // 新規追加の場合 public User(String name) { this(-1L, -1L); } // iBatisにより生成される場合 public User(Long id, Long version) { this.id = id; this.version = version; } public long getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } public long getVersion() { return version; } }
UserId.java
package test.ibatis.entity; public class UserId { private long id; public long get() { return id; } }
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd"> <mapper namespace="USERS"> <resultMap id="UserMapping" type="test.ibatis.entity.User"> <constructor> <idArg column="user_id" javaType="long" /> <idArg column="version" javaType="long" /> </constructor> <result property="name" column="user_name" /> </resultMap> <select id="findById" parameterType="long" resultType="test.ibatis.entity.User" resultMap="UserMapping" > SELECT user_id, user_name, version FROM users WHERE user_id = #{value} </select> <insert id="insert" parameterType="test.ibatis.entity.User"> INSERT INTO users (user_name, version) VALUES (#{name},1) </insert> <select id="getInsertedId" resultType="test.ibatis.entity.UserId" > SELECT LAST_INSERT_ID() id; </select> <update id="updateById" parameterType="test.ibatis.entity.User"> UPDATE users SET user_name = #{name}, version = version + 1 WHERE user_id = #{id} AND version = #{version} </update> <delete id="deleteById" parameterType="long"> DELETE FROM users WHERE user_id = #{id} </delete> </mapper>
テスト
検証のためさらにUnitTestを追加します。
pom.xmlに追加
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency>
UserTest.java
import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.Test; import test.ibatis.configuration.AppSqlSessionFactory; import test.ibatis.entity.User; import test.ibatis.entity.UserId; public class UserTest { @Test public void test() throws Exception { SqlSessionFactory factory = AppSqlSessionFactory.get(); SqlSession session = factory.openSession(); try { // 新規追加 User user = new User("ユーザ名"); user.setName("ユーザ名"); long count = session.insert("USERS.insert", user); assertEquals(1, count);// 追加件数が1件であることを確認 // 採番されたidを取得 UserId userId = (UserId) session.selectOne("USERS.getInsertedId"); long id = userId.get(); // 新規追加したUserを取得 user = (User) session.selectOne("USERS.findById", id); assertEquals(id, user.getId()); assertEquals("ユーザ名", user.getName()); assertEquals(1, user.getVersion()); // 更新 user.setName("ユーザ名改"); count = session.update("USERS.updateById", user); assertEquals(1, count);// 更新件数が1件であることを確認 // 更新結果確認 user = (User) session.selectOne("USERS.findById", id); assertEquals(id, user.getId()); assertEquals("ユーザ名改", user.getName()); assertEquals(2, user.getVersion());//バージョンがインクリメントされたことを確認 session.commit(); } catch (Exception e) { e.printStackTrace(); session.rollback(); fail(e.getMessage()); } finally { session.close(); } } }
結果
テストが全て通過しました。うまく動いているようです。
追加説明
SqlSessionFactoryBuilder
公式サイトのユーザーズガイド(後述)には、SqlSessionFactoryBuilderについて下記のように書かれています。
This class can be instantiated, used and thrown away. There is no need to keep it around once you've created your SqlSessionFactory. Therefore the best scope for instances of SqlSessionFactoryBuilder is method scope (i.e. a local method variable). You can reuse the SqlSessionFactoryBuilder to build multiple SqlSessionFactory instances, but it's still best not to keep it around to ensure that all of the XML parsing resources are freed up for more important things.
SqlSessionFactoryBuilderのベストスコープはメソッドスコープだとありますが、ビジネスロジック(=1トランザクション)毎にnewという解釈でいいのでしょうか?
今回のサンプルでは毎回newしていますが、SqlSessionFactoryをシングルトンで管理できるならそちらの方がよさそうです。
Eclipse3.5 + Maven(m2eclipse) + WTP
概要
EclipseでMavenプラグインのm2eclipseを使用した場合、WTPとの連携が悪いのか、mavenプロジェクトを作ってもサーバに追加できるプロジェクトとしては認識されていませんでした。今までは「動的Webプロジェクト」をMavenのディレクトリ構成にあわせて作成してから、「プロジェクトメニュー>Maven>依存関係管理を使用可能にする」 を選択という手順で対応していたのですが、今日調べてみたら状況が改善されています。
以下の記述は、Windows上でPleiades All In One Ultimate(3.5.2 Galileoベース)を使用して作業しています。
Eclipseへのインストール手順
1.m2eclipseのupdateサイトからプラグインをインストール
http://m2eclipse.sonatype.org/sites/m2e/
Eclipse 用 Maven 統合
2.WTP用のプラグインをインストール
http://m2eclipse.sonatype.org/sites/m2e-extras
WTP 用 Maven 統合
プロジェクトの作成
1.プロジェクト・エクスプローラーで右クリック > 新規 > プロジェクト
2.新規プロジェクトダイアログで、Maven > Maven Project
3.新規 Maven プロジェクトダイアログで、次へ > グループ Idで「maven-archetype-webapp」を選択
4.以下省略
結果
この手順で、プロジェクトがMavenのディレクトリ構成で作られて、サーバの「追加および除去」ダイアログでも選択可能になりました。
相変わらずsrc/main/javaディレクトリが作られてないのが惜しい。
m2eclipseのサイト
m2eclipse
http://m2eclipse.sonatype.org/index.html
その他参考にさせていただいたサイト
@//メモ
Maven Eclipseとの連携
http://hondou.homedns.org/pukiwiki/pukiwiki.php?Maven%20Eclipse%A4%C8%A4%CE%CF%A2%B7%C8
ぼそっと
EclipseのWTPでApache Ivy v.s m2eclipseその1 - 最近のm2eclipse
http://d.hatena.ne.jp/chiba_mk3/20080718/1216460791