サンプルのカンファレンス・アプリケーションを拡張するには、第 1 回のアプリケーションを再作成する必要があります。それには、前回の記事で説明した手順に従うか、Roo の script
コマンドを使用してください。script
コマンドは、リソース・ファイルに指定されたすべてのコマンドを実行します。第 1 回の手順に従っていれば、Roo によって log.roo という名前のファイルが作成されているはずです。このファイルに、Roo シェルで起動されるすべてのコマンドが含まれています。log.roo ファイルを実行して、アプリケーションを再作成します。
- このファイルは、サンプル・コードに含まれています。ファイルの名前を conference.roo に変更してください。
- conference というディレクトリーを新規に作成し、その中に conference.roo をコピーします。
- オペレーティング・システムのコマンドライン・シェルを開きます。
- 前の手順で作成した conference ディレクトリーに移動します。
script --file conference.roo
コマンドを実行します。
Maven リポジトリーに必要な JAR があれば、script
コマンドによって数秒間でアプリケーションが再作成されます。必要な JAR がない場合には、このコマンドがすべての JAR をダウンロードしなければならないため、もう少し時間がかかります。script
コマンドは、Spring が管理するプロジェクトを作成するためのテンプレートとして利用できるという点で、便利なコマンドです。
作業を進める前に、Maven プロジェクトを STS にインポートしてください。STS は Maven Eclipse プラグインにあらかじめバンドルされています。Maven プロジェクトをインポートするには、「File (ファイル)」 > 「Import (インポート)」 > 「Maven」 > 「Existing Maven Projects (既存の Maven プロジェクト)」の順に選択し、Maven プロジェクトのディレクトリーを選択します。このプロジェクトを STS にインポートする理由は、後でカスタム・コードを作成するからです。
作成した Web アプリケーションは今のところ機能するので、Speaker エンティティーと Talk エンティティーを手動で作成、読み取り、更新、削除してみることで、アプリケーションをテストすることができます。けれども、この手動のプロセスを自動化できたら素晴らしいと思いませんか?
そこで登場する Spring Roo の機能が、Selenium テスト・サポートです。Selenium は、Web ベースのアプリケーションの自動テストを迅速に作成できるようにする堅牢なツール・セットです。Selenium テスト・サポートをアプリケーションに追加するには、以下のコマンドを実行します。
selenium test --controller ~.web.SpeakerController selenium test --controller ~.web.TalkController |
上記の selenium test
コマンドによって、Speaker および Talk コントローラーを対象とした Selenium テストが作成されます。このコマンドには必須属性として、Selenium テストを作成するコントローラーの名前を指定する、controller という属性があります。また、このコマンドには、Selenium テストの名前を指定する name
、Web アプリケーションが使用可能なサーバーを指定する serverUrl
という 2 つのオプション属性もあります。さらに、selenium test
コマンドを実行すると、Spring Roo によって Selenium Maven プラグインも追加されます。
上記のコマンドでコントローラーの Selenium テスト・ケースは作成できましたが、これらのテスト・ケースを実行する前に、Spring Roo で作成される Selenium テスト・スイートの小さな欠陥を修正しなければなりません。サンプル・アプリケーションの Speaker エンティティーには、年齢が 25 歳から 60 歳の間でなければならないという制限を設定してありますが、このテスト・スイートは、この制限を考慮しないからです。年齢の値として 1 が使用されているため、テストは失敗することになります。そこで、test-speaker.xhtml という名前のファイルを修正して、リスト 1 に示すセクションを更新する必要があります。
リスト 1. test-speaker.xhtml の修正
<tr> <td>type</td> <td>_age_id</td> <td>1</td> </tr> |
上記のセクションを以下のように変更します。
<tr> <td>type</td> <td>_age_id</td> <td>26</td> </tr> |
この欠陥は、Spring Roo の今後のリリースで修正されるはずです。
Selenium テスト・ケースを実行するには、Tomcat サーバーを起動する必要があります。それには、Maven コマンド mvn tomcat:run
によってサーバーを起動します。Roo を使用して作成したすべての Web アプリケーションには、デフォルトで、Tomcat および Jetty Web サーバー対応の Maven プラグインが備わっています。selenium test
を実行するための Maven コマンドは、mvn selenium:selenese
です。
このコマンドを実行すると、Firefox ブラウザーが立ち上がり、Selenium テスト・ケースが実行されます。テストの実行中には、画面に図 1 に示すような内容が表示されます。
図 1. Selenium テスト
目下、このアプリケーションには誰もがアクセスすることができ、Speaker と Talk の作成、更新、削除をすることができます。リアルタイムのアプリケーションには、誰がどの操作を実行できるかに関するセキュリティーが適用されます。
Roo は、Spring Security を使用して、わずか 1 行でアプリケーションにセキュリティーを追加します。Spring Security は、極めて柔軟にカスタマイズできる、強力な認証およびアクセス制御フレームワークであり、Spring ベースのアプリケーションをセキュアにするためのフレームワークとしては、Spring Security がデファクト・スタンダードになっています。
Spring Security を追加するには、security setup
コマンドを実行します。
このコマンドは、必要なすべての Spring Security JAR を追加して、アプリケーションの基本セキュリティーをセットアップします。このコマンドは他にもファイルを作成しますが、その中で重要なファイルは、applicationContext-security.xml です。このファイルには、セキュリティーに関するすべての Bean 定義が含まれます。applicationContext-security.xml は、リスト 2 のような内容です。読みやすくするために、このリストではハッシュ化されたパスワードをドットに置き換えてあります。
リスト 2. applicationContext-security.xml の内容
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans \ http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security \ http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <!-- HTTP security configurations --> <http auto-config="true" use-expressions="true"> <form-login login-processing-url="/resources/j_spring_security_check" \ login-page="/login" authentication-failure-url="/login?login_error=t"/> <logout logout-url="/resources/j_spring_security_logout"/> <!-- Configure these elements to secure URIs in your application --> <intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')"/> <intercept-url pattern="/member/**" access="isAuthenticated()" /> <intercept-url pattern="/resources/**" access="permitAll" /> <intercept-url pattern="/**" access="permitAll" /> </http> <!-- Configure Authentication mechanism --> <authentication-manager alias="authenticationManager"> <!-- SHA-256 values can be produced using \ 'echo -n your_desired_password | sha256sum' \ (using normal *nix environments) --> <authentication-provider> <password-encoder hash="sha-256"/> <user-service> <user name="admin" password="..." authorities="ROLE_ADMIN"/> <user name="user" password="..." authorities="ROLE_USER"/> </user-service> </authentication-provider> </authentication-manager> </beans:beans> |
Roo が構成するセキュリティーは汎用セキュリティーであるため、サンプル・アプリケーションを参照しているところはありません。Roo はアプリケーションのセットアップまたは構成をすぐに始められるように支援しますが、最終的なアプリケーションをカスタマイズするのは開発者の責任であることを忘れないでください。この例の場合、Roo は単に Spring Security のテンプレートを提供したにすぎないので、私たちは必要に応じてこのテンプレートをカスタマイズすることになります。
サンプル・アプリケーションでは、誰でも Speaker を作成することができますが、Talk を作成できるのは Speaker のみです。したがって、applicationContext-security.xml は以下に示すように変更しなければなりません。リスト 3 には、修正が必要な XML の部分だけを抜粋しています。
リスト 3. applicationContext-security.xml の変更
<http auto-config="true" use-expressions="true"> <form-login login-processing-url="/resources/j_spring_security_check" \ login-page="/login" authentication-failure-url="/login?login_error=t"/> <logout logout-url="/resources/j_spring_security_logout"/> <!-- Configure these elements to secure URIs in your application --> <intercept-url pattern="/talks/**" access="hasRole('ROLE_USER')"/> <intercept-url pattern="/speakers/**" access="permitAll" /> <intercept-url pattern="/resources/**" access="permitAll" /> <intercept-url pattern="/**" access="permitAll" /> </http> |
上記では intercept-url
を更新して、RULE-USER をロールとして持つユーザーだけが Talk を作成できるようにし、すべてのユーザーが Speaker として自己登録できるようにしています。
Roo が生成した上記の Spring Security は、<user-service>
タグ内に構成されたメモリー内認証プロバイダーを使用します。サンプル・アプリケーションは Speaker エンティティーを管理することから、Speaker データを使用するカスタム認証プロバイダーを作成しなければなりません。認証には Speaker の E メールをユーザー名として使用し、Speaker エンティティーにパスワード・フィールドを追加することにします。このパスワードを、認証パスワードとして使用します。
ここでも Roo シェルを使用して、パスワード・フィールドを Speaker エンティティーに追加します。
field string --class ~.domain.Speaker --fieldName password --notNull --sizeMin 6 –sizeMax 10 |
上記では、パスワードがヌルでないこと、そしてパスワードの長さは 6 文字から 10 文字でなければならないことも制約として追加しました。
認証パラメーターには E メールとパスワードを使用することから、指定された E メールとパスワードの Speaker を検索できるようにしたいと思います。Spring Roo では、finder add
コマンドを使用して、アプリケーションのファインダーを作成することができます。
finder add --finderName findSpeakersByEmailAndPasswordEquals --class ~.domain.Speaker |
エンティティーのすべてのファインダーを調べるには、finder list
コマンドを使用します。finder add
コマンドは、ファインダーのコードを Speaker_Roo_Finder.aj ファイルに書き込み、ビュー関連のファイルを作成します。これにより、GUI から Speaker を検索することが可能になります。
カスタム AuthenticationProvider を作成する
AbstractUserDetailsAuthenticationProvider
クラスはユーザー名/パスワード方式の認証で機能するため、このクラスを継承してカスタム認証プロバイダーを作成します。AbstractUserDetailsAuthenticationProvider
を継承するクラスが実装しなければならないのは、additionalAuthenticationChecks
および retrieveUser
という 2 つの抽象メソッドです。プロバイダーは retrieveUser
メソッドを呼び出し、入力された E メールとパスワードを使って Speaker を認証します。データベースで Speaker を検索するには、前の手順で作成したファイダーが使用されます。Speaker が見つかると、その Speaker には GrantedAuthority ROLE_USER
が割り当てられます。このメソッドは最後に、ログインに成功した場合はデータを設定した UserDetails
オブジェクトを返し、ログインに失敗した場合には、適切なメッセージと一緒に BadCredentialsException
をスローします (リスト 4 を参照)。
リスト 4. カスタム認証
package com.dw.roo.conference.security; import java.util.ArrayList; import java.util.List; import javax.persistence.EntityNotFoundException; import javax.persistence.NonUniqueResultException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.\ dao.AbstractUserDetailsAuthenticationProvider; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.util.StringUtils; import com.dw.roo.conference.domain.Speaker; public class ConferenceAuthenticationProvider extends \ AbstractUserDetailsAuthenticationProvider { @Override protected void additionalAuthenticationChecks(UserDetails userDetails, \ UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { // TODO Auto-generated method stub } @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken \ authentication) throws AuthenticationException { String password = (String) authentication.getCredentials(); if (!StringUtils.hasText(password)) { throw new BadCredentialsException("Please enter password"); } List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); try { Speaker speaker = Speaker.findSpeakersByEmailAndPasswordEquals(username, \ password).getSingleResult(); authorities.add(new GrantedAuthorityImpl("ROLE_USER")); } catch (EmptyResultDataAccessException e) { throw new BadCredentialsException("Invalid username or password"); } catch (EntityNotFoundException e) { throw new BadCredentialsException("Invalid user"); } catch (NonUniqueResultException e) { throw new BadCredentialsException("Non-unique user, contact administrator"); } return new User(username, password, true, // enabled true, // account not expired true, // credentials not expired true, // account not locked authorities); } } |
applicationContext-security.xml で、conferenceAuthenticationProvider Bean を定義し、Roo によって生成されたメモリー内認証プロバイダーを conferenceAuthenticationProvider
に置き換える必要があります (リスト 5 を参照)。
リスト 5.
conferenceAuthenticationProvider
<beans:bean name="conferenceAuthenticationProvider" class="com.dw.roo.conference.security.ConferenceAuthenticationProvider"> </beans:bean> <!-- Configure Authentication mechanism --> <authentication-manager alias="authenticationManager"> <authentication-provider ref="conferenceAuthenticationProvider"/> </authentication-manager> |
ここでサーバーを起動して、Talk を作成してみてください。表示されたログイン画面には、作成済み Speaker の E メールとパスワードを入力してください。このカスタム認証プロバイダーを作成した目的は、理想的な認証プロバイダーを作成することではなく、Roo が行ってくれる作業と、開発者が自分で行わなければならない作業を説明するためです。
このサンプル・アプリケーションでは、Speaker が Talk を作成した場合に E メールが受信されるようにしたいので、今度は E メール・サポートをアプリケーションに追加します。作業の焦点を Roo によって E メールを送信する部分に絞るため、SMTP サーバーには Gmail を使用します。アプリケーションに E メール・サポートを追加するには、以下のコマンドを使用します。
email sender setup --hostServer smtp.gmail.com --username \ <Your email address> --password <Your email password> --port 587 --protocol SMTP |
email sender コマンドによって、プロジェクトに Spring JavaMailSender
がインストールされます。E メール関連のプロパティーは、email.properties ファイルで変更することができます。
Talk の作成後に E メールを送信するには、TalkController
に E メール・フィールドを追加しなければなりません。E メール・フィールドを追加するには以下のコマンドを実行します。
field email template --class ~.web.TalkController |
上記のコマンドによって、MailSender
テンプレートと sendMessage
メソッドが TalkController
に追加されます。今度は、Talk がデータベースに維持された後に sendMessage
メソッドがトリガーされるようにしなければなりません。TalkController
のコードはすべて TalkController_Roo_Controller.aj ファイルに含まれているため、このタスクを実現する最も簡単な方法は、この .aj ファイルのTalkController
クラスに encodeUrlPathSegment
メソッドを作成し、さらに create メソッドの talk.persist()
行の後に sendMessage
メソッドの呼び出しを追加することです (リスト 6 を参照)。
リスト 6. .aj ファイルの
TalkController
クラスに encodeUrlPathSegment
メソッドを作成するpublic class TalkController { @Autowired private transient MailSender mailTemplate; public void sendMessage(String mailFrom, String subject, String mailTo, String message) { org.springframework.mail.SimpleMailMessage \ simpleMailMessage = new org.springframework.mail.SimpleMailMessage(); simpleMailMessage.setFrom(mailFrom); simpleMailMessage.setSubject(subject); simpleMailMessage.setTo(mailTo); simpleMailMessage.setText(message); mailTemplate.send(simpleMailMessage); } @RequestMapping(method = RequestMethod.POST) public String create(@Valid Talk talk, BindingResult result, Model model, HttpServletRequest request) { if (result.hasErrors()) { model.addAttribute("talk", talk); return "talks/create"; } talk.persist(); sendMessage("spring.roo.playground@gmail.com", "Your talk is created", talk.getSpeaker().getEmail(), \ "Congrats your talk is created"); return "redirect:/talks/" + encodeUrlPathSegment(talk.getId().toString(), request); } private String encodeUrlPathSegment(String pathSegment, HttpServletRequest request) { String enc = request.getCharacterEncoding(); if (enc == null) { enc = WebUtils.DEFAULT_CHARACTER_ENCODING; } try { pathSegment = UriUtils.encodePathSegment(pathSegment, enc); } catch (UnsupportedEncodingException uee) { } return pathSegment; } } |
これにより、Talk が作成された後は、Speaker の指定アカウントに E メールが届くようになります。
私たちが作成しているのは、インターネット・ベースの Web アプリケーションです。したがって、各地のユーザーがこのアプリケーションを利用できるように、さまざまな言語をサポートすることが重要になります。Spring Roo では、web mvc install language
コマンドを使用してアプリケーションに新しい言語をインストールすることによって、国際化サポートを追加します。以下に例として、スペイン語とイタリア語をインストールする場合のコマンドを記載します。
web mvc install language --code es web mvc install language --code it |
Roo が現在サポートしているのは 6 カ国語です。その他の言語については、アドオン言語を作成できるようになっています。上記のコマンドを実行した後にアプリケーションを実行すると、英国国旗の他に 2 つの国旗 (イタリアとスペインの国旗) が表示されます。これらの国旗のいずれかをクリックすると、Web アプリケーションはその国旗に対応する言語で表示されます。
今や、ソーシャル・メディアの時代です。現在のアプリケーションには当たり前のように、ソーシャル機能が追加されています。このサンプル・アプリケーションのソーシャル機能としては、講演の様子の動画を追加するのが妥当でしょう。Roo では、YouTube、Vimeo、Viddler、Google Video などにアップロードされた動画を組み込めるようになっています。動画を組み込むには、以下のコマンドを使用します。
web mvc embed video --provider VIMEO --videoId 16069687 |
コマンドを実行した後、サーバーを起動してブラウザーでアプリケーションを立ち上げると、上記のコマンドで組み込まれた動画を見ることができるようなります。同じようにして、YouTube や Viddler の動画を追加することもできます。
Roo には、Twitter のメッセージ、文書、証券コード、地図、写真、ビデオ・ストリームをアプリケーションに組み込むオプションも用意されています。これらのコマンドをリスト 7 に記載します。
リスト 7. embed コマンド
web mvc embed document web mvc embed finances web mvc embed map web mvc embed photos web mvc embed stream video web mvc embed twitter web mvc embed video |
データベース・リバース・エンジニアリング (DBRE) によって、既存のデータベースをイントロスペクトし、アプリケーションとして公開することができます。DBRE の仕組みを説明するため、これから既存のフィードバック・スキーマからフィードバック・アプリケーションを作成します。データベースとして使用するのは MySQL です。
Roo を起動する前に、MySQL のインストール済み環境にスキーマを作成する必要があります。リスト 8 に記載する SQL スクリプトを実行すると、MySQL データベースにフィードバック・スキーマが作成されます。
リスト 8. フィードバック・スキーマを作成するための SQL スクリプト
create database feedback_schema; use feedback_schema; CREATE TABLE feedback ( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, TalkTitle VARCHAR(45) NOT NULL, SpeakerName VARCHAR(45) NOT NULL, Feedback VARCHAR(4000) NOT NULL, PRIMARY KEY (id) ) ENGINE = InnoDB; |
これは、テーブルが 1 つだけあり、リレーションは何も含まれていない単純なスキーマとなっていますが、複数のテーブルとリレーションを使用する複雑なスキーマでも、Roo では問題なくリバース・エンジニアリングを行うことができます。
上記の SQL スクリプトを実行してスキーマを生成した後は、以下の手順に従ってフィードバック・アプリケーションを作成します。
- feedback という名前のディレクトリーを作成します。
- オペレーティング・システムのコマンドラインから、feedback ディレクトリーに移動します。
roo
コマンドを実行して Roo シェルを開きます。project --topLevelPackage com.dw.roo.feedback
と入力して、新規 Maven プロジェクトを作成します。- このアプリケーションでは、MySQL をデータベースとして使用します。以下のコマンドを使用して、アプリケーションに対して永続化のセットアップを行います。
persistence setup --provider HIBERNATE --database MYSQL --databaseName feedback_schema --userName root --password password
ここでは root ユーザーとしてスキーマを作成したので、上記ではユーザー名として root を使用していますが、皆さんがこのコマンドを実行するときには、スキーマを作成するために使用したユーザー名とパスワードを入力してください。このコマンドは、パーシスタンスに必要なすべての JAR も追加します。
- データベース・スキーマをイントロスペクトするには、
database introspect --schema feedback_schema
を使用します。database introspect
コマンドは、データベース・スキーマに関連するメタデータを表示します。この例で使用するコマンドはスキーマのメタデータを Roo シェル・コンソールに表示しますが、--file 属性
を使用してメタデータ XML をファイルにエクスポートすることもできます。 - データベース・スキーマのイントロスペクションが完了したら、今度はスキーマのリバース・エンジニアリングを行うために、
database reverse engineer --schema feedback_schema --package ~.domain
を実行します。database reverse engineer
コマンドには、schema と package という 2 つの必須属性があります。前者はリバース・エンジニアリング対象のスキーマの名前を指定する属性、後者は Roo がソースを生成する先となるパッケージを指定する属性です。ここでは、すべてのエンティティーを com.dw.roo.feedback.domain パッケージ内に作成します。 - 次のステップでは、アプリケーションのコントローラーを生成します。それには、
controller all --package ~.web
を実行します。 - アプリケーションを実行する前に、persistence.xml に含まれるプロパティーを多少変更する必要があります。hibernate.ejb.naming_strategy プロパティーが使用している
ImprovedNamingStrategy
は、MySQL databasesデータベースには機能しないため、mvn clean install tomcat:run
を実行すると、例外を受け取ることになります。これを機能させるには、以下のように、hiberate.ejb.naming_strategy をDefaultNamingStrategy
に変更してください。<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.DefaultNamingStrategy"/>
- これで、Maven コマンド
mvn clean install tomcat:run
を使用して、フィードバック・アプリケーションを実行できるようになります。
カンファレンス・アプリケーションとフィードバック・アプリケーションのソース・コードはダウンロードすることができます (「ダウンロード」を参照)。
これまでの作業で、単純な CRUD ベースの Web アプリケーションは本格的なエンタープライズ・アプリケーションに拡張されました。この手順をとおして、Selenium テスト機能や Spring Security、そして国際化サポートなどの機能を、いかに簡単に追加できるかがわかったはずです。この記事ではまた、Spring Roo のデータベース・リバース・エンジニアリング機能を使って、既存のデータベースからアプリケーションを作成する方法も説明しました。Roo で簡単にアプリケーションに追加できる機能は、JMS、Solr、JSON サポートをはじめ、まだまだたくさんあります。
第 3 回では、このカンファレンス・アプリケーションを Google App Engine にポーティングする方法を説明します。
0 件のコメント:
コメントを投稿