LombokでJavaの冗長なコードを排除する
Javaで開発しているとリクエストやレスポンスをマッピングするクラス(Value Object)とか
レイヤを跨ぐ場合に使うクラス(Data Transfer Object)など多用することがあるので、
アクセサー(getter/setter)を書くのが大変で、辛い。
Lombokを使うとかなり楽になると思うので、それについて、書きます。
Lombokのインストール
利用する為には、Eclipseにインストールする必要があります。
http://projectlombok.org/からlombok.jarをダウンロードしてきます。
ダウンロードしたJARファイルを起動します。
java -jar lombok.jar
GUIが立ち上がってくると思います。
通常は、IDEsに利用しているEclipseのパスが表示されていると思うので、
そのままInstall/Updateで、インストールは完了します。
Apache Mavenの設定
POMファイルの依存関係を定義する部分にlombokを追加します。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency>
普通にValue Objectを書いた場合
まず、普通にValue Objectを書いた例です。
EclipseのIDEのコード補完とか駆使すれば、ある程度、楽に書けるのですが、
冗長なコードになってしまうのと、Value ObjectとかDTOをいっぱい作るようなある程度の規模のアプリの場合、
大変だったりします(辛い)。
public class Message implements java.io.Serializable { private Integer id; private String message; public Message() { super(); } public Message(Integer id, String message) { super(); this.id = id; this.message = message; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((message == null) ? 0 : message.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Message other = (Message) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (message == null) { if (other.message != null) return false; } else if (!message.equals(other.message)) return false; return true; } @Override public String toString() { return "Message [id=" + id + ", message=" + message + "]"; } }
Lombokを利用して書いた場合
Lombokは、その冗長なコードの部分をAOPで書くことができます。
これは、コンパイル時に、そのその冗長なコード部分を付加して、コンパイルされる仕組みになっています。
上のクラスと同じようなクラスを作る場合は、3つのAOPを使います。
@AllArgsConstructor
メンバーを引数にとるコンストラクタをコンパイル時に補完してくれます。|
上の3つのAOPを使って書いたクラスは、下の様なクラス定義になります。
@Data @AllArgsConstructor @NoArgsConstructor public class Message implements java.io.Serializable { private Integer id; private String message; }
また、setterを付けたくない場合は、@Dataでなく、個別にAOPを指定する必要があります。
その場合は、@EqualsAndHashCode、@ToStringをクラスに付け、メンバーに対して、@Getterを付与します。
@EqualsAndHashCode @ToString @AllArgsConstructor @NoArgsConstructor public class Message implements java.io.Serializable { @Getter private Integer id; @Getter private String message; }
Eclipseの補完機能より、かなり楽になるかも(個人的には、メソッドにコメントとか書かなくてもいいからwww)。
それ以外のAOPもまだあるので、いろいろ楽になるかもしれない。
SpringFrameworkのHandlerInterceptorで共通処理
Spring MVCでのリクエストに対してのアクセスコントロールや認証などの共通処理を
実装する方法は、HandlerInterceptorを利用すると実現可能です。
Servlet Filterでも可能なんですが...。
それで、このHandlerInterceptorは、インターフェイスで、それを実装する必要があります。
この方法を使うと、Controllerの前後で、処理を呼び出すことが可能になります。
HandlerInterceptorには、下記の3つのメソッドが定義されています。
postHandle
ビューのレンダリング完了前に呼ばれるメソッドです。
サーバーサイドMVCで利用可能ですがSPAでは、
ビューのレンダリング等がないので、RestAPIだと呼ばれないようです。
ビューのレンダリング前に何らかの処理を入れたい場合は、 このメソッド内に処理を書きます。
afterCompletion
ビューのレンダリング完了後に呼ばれるメソッドです。
後処理を記述する場合は、このメソッド内に処理を書きます。
サーバーサイドMVC及び、SPAともに呼ばれます。
サーバーサイドMVCは、postHandleに後処理を書きますが、
SPAの場合は、このメソッド内に後処理を書きます。
実際のインターフェイスを実装したクラスを作成して、
必要なメソッド内に共通処理を記述すれば完了です。
public class SampleHandlerInterceptor implements HandlerInterceptor { /** * {@inheritDoc} */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 共通処理を記述する。 return true; } /** * {@inheritDoc} */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception { // 共通処理を記述する。 } /** * {@inheritDoc} */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView view) throws Exception { // 共通処理を記述する。 } }
HandlerInterceptorを実装したクラスを作成しただけでは、利用できません。
Webアプリケーションの設定クラスで、作成したHandlerInterceptorを登録します。
Webアプリケーションの設定クラスは、WebMvcConfigurationSupportを継承して、
@ConfigurationのAOPを付加する必要があります。
@Configuration public class SampleWebApplicationConfiguration extends WebMvcConfigurationSupport { @Bean public SampleHandlerInterceptor sampleHandlerInterceptor() { return new SampleHandlerInterceptor(); } /** * {@inheritDoc} */ @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(sampleHandlerInterceptor()).addPathPatterns("/**") }
最後に、ApplicationContext.xmlにWebアプリケーション設定クラスを設定すれば完了です。
<bean class="com.chronos.configuration.SampleWebApplicationConfiguration"/>
システムテスト自動化カファレンスへ参戦してきた
1時間で分かるSTA
Software Test Automationとは?
テスト自動化において考慮すべき事項が網羅的に扱っている。
テスト自動化の実例など。
ソフトウェアテストの自動化について一般的な知識を網羅的に扱う書籍がない?
翻訳された書籍がシステムテスト自動化標準ガイド
テスト自動化のコンテキスト
自動化以前にテストはまともなのか?
- 効果的
- 経済的
- 発展的
- 典型的
キャプチャーリプレイ
- 準備がほとんどいらない。
- 操作ログを残せる。
- しかし、テスト自動化ではない。
自動比較
- 何を?どれだけ?比較するか?
- センシティブな比較。
- できるだけ多くの情報を比較。
- ロバストな比較。
- 費用最低限の情報に絞り比較。
- 動的比較。
- 実行後比較。
テストウェアアーキテクチャ
- テスト自動化によって増殖するファイル(どんどんファイルが増殖)。
- 共有スクリプトなど、効率的に再利用できる必要がある。
- 管理なしだと破綻する。
- 前処理と後処理の自動化。
- 保守性の高いテストを構築する。
- テストウェアのメンテナンス。
- 最初は、短めで、徐々に長くする。
メトリックス
- DDP
- DFP
- ROI
テスト自動化のパターンと実践
テスト自動化パターンについて
そもそも始めるのに勇気と力が必要。
最初は、テンションが高いが、徐々に低下してしまう。
テンションを維持するのが大変。
パタンランゲージ
- コンテキスト(問題の背景)
- 問題(実際の問題)
- フォース(問題を発生させる要因となる外部からの力)
- 解決(解決策)
- 結果(レポートなど)
テスト結果を適切に分析し、活用することができていない。
多くの情報を解釈するには担当者のリソースが必要。
目的をもった結果レポートを出力するようにする。
メトリックスを測定する。
全員が理解できる共通言語(ユビキタス言語)で話す。
自分の経験を皆に伝えるツールとして。
パターン解説①
GUI自動テストの保守性を高めるには
Seleniumとその保守性
Excelから自動生成
失敗した原因を突き止める
- テスト結果に誰でも簡単にアクセスできる。
- メールやブラウザ上で見れると結果確認が簡単。
- チーム全体で結果を共有し、テスト失敗への関心を高める。
- 画面キャップチャ・動画・サーバーログなど、エラー調査に必要なデータを残す。
- エラーになったテストだけを手元で流して確認できる
- テストスクリプトはバージョン管理し、誰でも手元に取得できるように
- 原因が分かったら修正(簡単に修正できる)。
状態遷移テストにおけるテスト設計と実行の自動化
Value Stream Map
テストを自動化することが正しいのではなく、ソフトフェア開発自体をよいサイクルにすること。
リリースにかかる時間を短くするための有用な選択肢。
Deployment pipeline
テスト自動化をする上で、ある程度は俯瞰しなれば、局所化してしまう。
ソフトウェアをバージョン管理から取り出してユーザーに届けるまで。
ビルドパイプラインを実装するというのは、テストの大まかな分割方針、フィードバック順序、
実行頻度を決め、そして実装後には多様なテストを早期から設計、実装、実行することを意味する。
ビルドプロセスとCI
なにが問題なのか?
- 規模(ファイルの数が多い)
- 再利用性(建て増し旅館)
- 複数のバージョン(製品のバージョンとの同期)
- プラットフォームと環境からの独立(動作環境ごとにテストウェアのコピーを作るのか)
ツールで解決できるもの
- CIツールで解決(CIの結果、レポート機能など)⇒ 規模
- テスト資料は、バージョン管理システムで管理してしまう ⇒ 複数のバージョン
- テストコード、テストデータ(SQL)、テーブル定義、比較用の出力データ(期待結果)
- Git-flowなど、ブランチやタグなどの規制しておく。
前処理と後処理の自動化
- 前処理(生成(DBへのデータ投入)、チェック、再配置、変換)
- 後処理(削除、チェック、再配置、変換)
- 後処理の削除、再配置は、前処理でやった方がいい。
- ビルドツール(Make Rake Ant Gradleなど)
Gradleをはじめてみた
Apache Mavenに不満があるかと言うとないのだけど、
Gradleを触ってみることにしました。
それで、Gradleで、Springを使ったRestを試しに作ってみました。
まずは、build.gradleの作成から、
GradleもMavneリポジトリから依存関係を定義したものをダウンロードして来てくれます。
ただし、Mavenとは、依存関係の解釈が違うぽいです。
作ったbuild.gradleは、こんな感じです。
apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'war' sourceCompatibility = 1.8 version = '1.0' repositories { mavenCentral() } dependencies { // Unit Test Library testCompile 'org.mockito:mockito-all:1.9.5' testCompile 'org.powermock:powermock-module-junit4:1.5.5' testCompile 'org.powermock:powermock-api-mockito:1.5.5' testCompile 'com.jayway.jsonpath:json-path:0.9.1' testCompile 'junit:junit:4.11' testCompile 'org.springframework:spring-test:4.1.2.RELEASE' // Web Application Library compile 'org.aspectj:aspectjweaver:1.7.3' compile 'org.springframework:spring-core:4.1.2.RELEASE' compile 'org.springframework:spring-context:4.1.2.RELEASE' compile 'org.springframework:spring-context-support:4.1.2.RELEASE' compile 'org.springframework:spring-jdbc:4.1.2.RELEASE' compile 'org.springframework:spring-tx:4.1.2.RELEASE' compile 'org.springframework:spring-web:4.1.2.RELEASE' compile 'org.springframework:spring-webmvc:4.1.2.RELEASE' compile 'commons-logging:commons-logging:1.1.3' compile 'commons-lang:commons-lang:20030203.000129' compile 'commons-collections:commons-collections:3.2' compile 'org.slf4j:slf4j-api:1.7.5' compile 'ch.qos.logback:logback-classic:1.1.2' compile 'com.fasterxml.jackson.core:jackson-core:2.4.0' compile 'com.fasterxml.jackson.core:jackson-databind:2.4.0' compile 'com.fasterxml.jackson.core:jackson-annotations:2.4.0' // Web Application Provider Library providedCompile 'org.eclipse.jetty:jetty-webapp:9.2.5.v20141112' }
XMLよりこちらの方が抵抗がなく書けるかもしれません。
Groovyで書けるので、楽かもしれませんが....Groovy自体、そんなに詳しくないので、
これを機にまじめに勉強するかもしれません。
次にRestを作ります。
今回は、単純にメッセージを戻すだけのものにします。
作成したものは、下の3つです。
JSONマッピングクラス
public class HelloMessage implements java.io.Serializable { private String id; private String message; public HelloMessage() { super(); } public HelloMessage(String id, String message) { super(); this.id = id; this.message = message; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
Restサービスインターフェイス
public interface HelloMessageService { HelloMessage hello(String messageId) throws Exception; }
Restサービスインターフェイス実装クラス
@RequestMapping(value="/message") @RestController @Service("helloMessageService") @Scope("request") public class HelloMessageServiceImpl implements HelloMessageService { @RequestMapping(value = "/hello/{messageId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @Override public HelloMessage hello(@PathVariable String messageId) throws Exception { return new HelloMessage("hello", "Hello World"); } }
とりあえず、Eclipseで見る限り、ビルドは、通ってるようです。
Gradleでもビルドしたら正常にビルド出来ました。
gradle clean build :clean :compileJava :processResources UP-TO-DATE :classes :war :assemble :compileTestJava :processTestResources :testClasses :test :check :build BUILD SUCCESSFUL Total time: 9.581 secs
それで、ここから問題で、Jettyで動かしたいのだが、デフォルトのJetty plugin使ったら
見事に動きませんでした。
なんかJetty関連のライブラリがないとかのエラー。
本家のドキュメントには、パラメーターの設定が少し載っているぐらいで、役にたたない。
これは、時間が掛かりそうなんで、また、今度にすることに...
gradle clean build JettyRun :clean :compileJava :processResources UP-TO-DATE :classes :war :assemble :compileTestJava :processTestResources :testClasses :test :check :build :jettyRun Failed startup of context org.gradle.api.plugins.jetty.internal.JettyPluginWebAppContext java.lang.IllegalArgumentException: Object is not of type class org.eclipse.jetty.webapp.WebAppContext at org.mortbay.xml.XmlConfiguration.configure(XmlConfiguration.java:189) at org.mortbay.jetty.webapp.JettyWebXmlConfiguration.configureWebApp(JettyWebXmlConfiguration.java:103) at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1269)
次にSpring MVCのテストを書いて、テストが正常に動くかやってみました。
作成したテストクラスは、下のクラスです。
public class TestHelloMessageServiceImpl { private static HelloMessageService helloMessageService; private MockMvc mockMvc; @Before public void setUp() throws Exception { GenericApplicationContext genericApplicationContext = new GenericApplicationContext(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(genericApplicationContext); xmlBeanDefinitionReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml")); ConfigurableListableBeanFactory beanFactory = genericApplicationContext.getBeanFactory(); Scope request = new SimpleThreadScope(); beanFactory.registerScope("request", request); genericApplicationContext.refresh(); helloMessageService = (HelloMessageService)genericApplicationContext.getBean("helloMessageService"); mockMvc = MockMvcBuilders.standaloneSetup(helloMessageService).build(); } @Test public void テスト() throws Exception { mockMvc.perform( get("/message/hello/100") .characterEncoding("UTF-8") .accept("application/json;charset=UTF-8")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json;charset=UTF-8")) .andExpect(jsonPath("$.id").value("hello")) .andExpect(jsonPath("$.message").value("Hello World")); } }
それでは、テストを実行!!
これは、すんなりうまく行ったwww。
テストが動作しているか?不明だったので、一度、エラーになるテストを書いて確認したので、
恐らくこれで、大丈夫だと思います。
gradle clean test :clean :compileJava :processResources UP-TO-DATE :classes :compileTestJava :processTestResources :testClasses :test BUILD SUCCESSFUL Total time: 6.32 secs
とりあえず、今日は、これで終わり。
簡単に触った感じは、楽そうですが、なれるまで、少し時間がかかるかなぁ。
それと、実際で、システムで導入するには、環境依存とか自動生成系のものとか、
テンプレート化など、いろいろあると思うので、
かなり扱えるレベルにならないと使えない気がするので、
お休みの日にひっそりがんばりたいと思います。
【第48回】すくすくスクラムへ潜入
すくすくスクラムへ潜入してきました。
若干、遅刻してしまいました。
東京駅で、迷子になったwww。
それで、今回の勉強の内容
Scurmな人材育成〜プロダクト開発に必要な人材像や育成方法〜
・組織としてスクラムを導入するメリットについて?
・人材育成の観点で、プロダクト開発の中で、伸ばせるスキル、伸ばせないスキルは?
ディスカッションのやり方は、ワールードカフェスタイル?に近い形式です。
最初の議論の内容は、人に足りないもの?
議論して、出た項目
・集中力
・表現能力
・伝達能力
・感情のコントロール
・体力
・視力
・知識力
・信頼
次の議論の内容は、人が成長に必要なもの?
議論して、出た項目
・モチベーション
⇒ 〜なりたい!
⇒ あこがれ
⇒ お金
⇒ 評価
・プレッシャー
・ライバル
・いい仕事
⇒ キャリアパス
⇒ 仕事を楽しむ
⇒ 自分のレベルに合った仕事
⇒ 少し高いレベルの仕事
・勉強・学習
・好奇心
・時間
・チャレンジ
・自発的
次の議論の内容は、では?議論した中でスクラムが役立つことは?
人になりないもの?について、役立つもの
知識力、信頼力ぐらい?
人が成長に必要なもの?
・時間
⇒ スクラムのタイムイベント
・評価
⇒ 価値とか、ユーザーに必要なものとか議論して作っていくから?
・勉強・学習
⇒ スクラムの定義としては、ないかも?
⇒ プロセス改善的な取組みとしては、あるかも?
⇒ それは、スクラムではなく、XPとか、チーム独自の取組みなんじゃ?
最後に人材育成に関しての紹介
・アダプティヴィ・ラーニング
⇒ 学習者の学習履歴データを蓄積・解析することで、各自に適した学習コンテンツを提供する。
・マスタリー・ラーニング
⇒ ほぼ全ての学習者に一定水準以上の学力を保証することにオモキをおく
・インストラクショナル・デザイン
⇒ 主に心理学、学習学、コミニケーション学、情報学、行動分析学などを基礎として、
各分野で確率された科学的な知見を網羅しながら教育方法として、あるべき姿を提示する。
・教育方法の設計
⇒ 5フェーズ(分析、設計、開発、実践、評価)
⇒ 4フェーズ(準備、反復設計、反復開発、本格運用)
以上、これで、終了でした。
人材教育の側面でのスクラムとして、なにが出来るかの議論は、
いろいろな人の意見を聞いたり、議論出来たのは良かった。
人材育成の手法を何パターンか紹介してもらえたので、ちょっと調べようかと思います。
スクラムマスタのみなさんお疲れ様でした。
【第4回】スクラムナイトセミナーに潜入
スクラムナイトセミナーに潜入しました。
このセミナーも徐々に参加する人数が多くなってきました。
業界も幅広く、いろいろな人と話せるので、楽しい。
今回も最初に聞きたいことを付箋に書いて、
みんなで、出し合いホワートボードに張り出し、共有します。
・見積もりに関して
・ストーリーに関して
・スクラムの導入に関して
などなど、いろいろなことが張り出されてました。
その後は、いつもと同じく、
どれについて、議論するかを投票で上位4つを決定して、
チームに分かれる感じでした。
今回は、プロダクトバックロググルーミングに関して、
議論するチームに入らせてもらいました。
他のスクラムマスターは、どうやってのかなぁと思ったので、
スクラムの中で、プロダクトバックロググルーミングをやると、
結構時間が掛かってしまうものなので、
みんなは、どうやっているのか興味があった。
・やってない...エッセンシャルを見て知った。
・スプリント中、小分けして実施する。
・スプリントの最後で。
などなど(結構あいまいで、覚えてないwwww)。
最後は、各チーム発表です!!
Play FrameworkとSpring Frameworkの連携
最近、Play Frameworkにも手を出して、家で、いろいろやっているので、
今回は、Play Frameworkについてのエントリーです。
それで、Play Frameworkで、DIコンテナのSpring Frameworkを使いたい場合の
連携方法に関して書きたいと思います。
やっぱり、DIしたいなぁと思ったので!!
まずは、最初にbuild.sbtにSpringに必要なものを設定します。
libraryDependencies ++= Seq( "org.aspectj" % "aspectjweaver" % "1.7.3", "org.springframework" % "spring-context" % "4.0.5.RELEASE", "org.springframework" % "spring-orm" % "4.0.5.RELEASE", "org.springframework" % "spring-jdbc" % "4.0.5.RELEASE", "org.springframework" % "spring-tx" % "4.0.5.RELEASE", "org.springframework" % "spring-expression" % "4.0.5.RELEASE", "org.springframework" % "spring-aop" % "4.0.5.RELEASE", "org.springframework" % "spring-test" % "4.0.5.RELEASE" % "test" )
設定が完了したら、playのコマンドで、Springをダウンロードさせます。
play dependencies
Springのダウンロードがうまく行ったら、
Playのグローバル設定を行う為、クラスを作成します。
ここで、Springの設定ファイルのapplicationContext.xmlを読ませて、
ApplicationContextを生成します。
PlayのGlobalSettingsクラスを継承した、ApplicationConfigurationクラスを作成します。
public class ApplicationConfiguration extends GlobalSettings { private static ApplicationContext context = null; private String lock = "lock"; @Override public void onStart(Application application) { if (context == null) { synchronized(lock) { context = new FileSystemXmlApplicationContext( new String[] {"conf/applicationContext.xml"}); } } } @Override public <A> A getControllerInstance(Class<A> clazz) { return context.getBean(clazz); } }
configディレクトリにSpringのapplicationContext.xmlを作成します。
設定内容は、下記の内容です。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!-- Autowire --> <context:component-scan base-package="logic,controllers" scoped-proxy="targetClass"/> <!-- AOP --> <aop:aspectj-autoproxy/> </beans>
次にルーティングの設定をするので、これもconfigにあるroutesファイルを
下記の様に編集します。
GET / @controllers.Application.index()
これで、連携の準備は、完了です。
あとは、コントローラーにインジェクションできるようになるので、
何らかのクラスを@Componentのアノーテションを付けて、
下の様に、コントローラークラスにインジェクションするように書くだけです。
@Service public class Application extends Controller { @Autowired @Qualifier("logic") private Logic logic; public Result index() { System.out.println(logic.getMessage()); return ok(index.render("Your new application is ready.")); } }