[Java] Bean validation 1.1(JSR303, JSR349)の拡張

Javaでオブジェクトをバリデーションする仕様にBean validationがあります。
デフォルトのBean validationでは、”nullでないこと””桁数が9桁以上であること”など、最小単位のバリデーションしか用意されていませんので、ある程度複雑なアプリケーションを開発する場合、独自のバリデーションを追加する必要に迫られます。
この記事ではBean vaidationを使った独自のバリデーションを作る方法を紹介します。

Bean validation 1.1(JSR303, JSR349) とは

以下に仕様がまとめられています。

リンク先より抜粋
  • アノテーションでオブジェクトの制約を表現できる
  • 独自の制約を追加することができる
  • オブジェクト(ネストしたオブジェクトを含む)をバリデーションするAPIを提供している
  • メソッド、コンストラクターの引数、戻り値を検証するAPIを提供している
  • (ローカライズされた)バリデーションエラーを取得できる
  • Java EEに統合されている(Java EE以外でも使える)

例: コード1


作ったバリデーションはどのF/W(Java EE, Spring, Hibernate, ...etc)からでも共通的に使うことができます。

まずは、サンプルとしてSpringからBean validationを使った例でBean validationの用法を確認した上で、独自のバリデーションを定義していきます。


Bean validationの用法紹介: Spring Controllerの引数を検証するために使う

例としてDocumentオブジェクトを保存するAPIを用意します。
(前提: SpringとBean validationは事前に使える状態になっていること)

コード2: DocumentController.java

コード3: Document.java


上のコードの説明
Validアノテーション(Bean Validationが提供)
このアノテーションを付与すると、引数 doc に対してバリデーションが実行されます。

BindingResultクラス(Springが提供)
BindingResultを引数に指定しておくと、Springがバリデーション結果(エラーのあったフィールド名、満たせなかった制約、エラーメッセージ)をこのオブジェクトに設定して渡してくれる。もし、BindingResultを引数に指定しなかった場合、例外(Springの場合: MethodArgumentNotValidException.class)がThrowされるので、Springの@ExceptionHandlerとかを使ってハンドルすれば共通的にバリデーションエラー時の挙動を記述できます。

CustomConstraintアノテーション(独自に定義)
このプログラムに合わせてカスタマイズしたバリデーションを実施します。
(* 後ほど説明します)

NotNullアノテーション(Bean Validationが提供)
textに設定された値がnullの場合にバリデーションエラーになります。

⑤ ネストしたオブジェクト(配列、Listに対しても付与可能)に付与したValidアノテーション(Bean Vaildationが提供)
Validアノテーションを付けるとネストしたオブジェクトに対してもバリデーションが実施されます。

利用側からするとアノテーションを付与するだけになるため、コード簡潔に保つことができています。この例でいうと、CustomConstraintという独自のアノテーションを使っていますがこのようなアノテーションを共通的に用意しておけばバリデーションロジックを汎用的に用意しておくことができるため開発の効率化に繋がります。

それでは次にDocumentオブジェクトのidフィールドに対して、独自の制約(CustomConstraintアノテーション)を導入してみましょう

独自の制約を作成する

上のコード3にある@CustomConstraintアノテーションを作ります。
これから見ていくサンプルを簡単に説明すると、CustomConstraintアノテーションのpayloadフィールドにバリデーション内容(Validatorインターフェースの実装クラス)を指定しておくとそのバリデーション内容が実行されるというものです

コード4: CustomConstraint.java

コード5: CustomValidator.java

コード6: Validators.java
コード7: Validator.java
コード8: Message.java

コード9: WebConfig.java
上のコードの説明
独自の制約を作るときにはConstraintアノテーションを付与したアノテーション(コード4)を用意します。バリデーションロジックは、ConstraintアノテーションのvalidateByに指定したクラス(コード5)に記述します。

またこのアノテーションには、以下3フィールドを定義しておく必要があります。

  • message 制約に違反したときにこのメッセージが返されます。このメッセージにはローカライズ等の変換処理が施され返されます。変換に関する仕様はこちらに書かれています。 仕様ではResourceBundleにあるValidationMessages(ValidationMessages.properties)がメッセージの変換に使われますが、独自のメッセージ定義を使いたいときには、Springの場合、コード9に記載のあるようにWebConfigurerAdaptor#getValidatorをoverrideし独自の設定をすることでこれが実現できます。
  • groups groupsを指定した場合、バリデーションする/しないを任意の単位で分割することができます。 例えば、「あるAPI Aではバリデーションしたいけど、別のAPI Bではバリデーションしたくない」といったときにAPI Aグループと、API Bグループをつくり、バリデーションするときにこのグループを指定すれば、指定したグループ単位のバリデーションが実施されます。(* Springでグループ単位のバリデーションをするには、javax.validation.Validアノテーションの代わりに、org.springframework.validation.annotation.Validatedアノテーションを使います)
  • payload 制約アノテーション(e.g. NotNull)を付与するときに、制約に対して更に情報を付与したいときに使用します。 例えば、フィールドA、BにNotNullアノテーションを付けていて、フィールドAがnullのときはエラーとして扱い、フィールドBがnullのときは警告として扱いたいといったときに、フィールドAには@NotNull(payload=sample.Error.class)、フィールドBには@NotNull(payload=sample.Warning.class)を指定することで、バリデーションエラー受取側では、このpayloadに設定された値によって挙動を変えることができます。


実際のバリデーションはコード5の①で実施されています。
この例では、payloadに指定されたバリデーター(e.g. Validators.Id)に定義されたバリデーションロジックを実施し、エラーがあればメソッドの引数のConstraintValidatorContextにメッセージを追加します。
以上でBean validationを拡張した独自のバリデーションが完成しました。


Author
Yuki Sanada

[Scala] Unfilteredで簡単ストリーミングダウンロード

本記事では、Scala製のUnfilteredと呼ばれるフレームワーク(後述)を使用し、簡単にファイルをストリーミングダウンロードする方法を紹介します。


Unfilteredとは?

ここでの詳しい説明はしませんが、簡単に説明するとUnfilteredは、Scalaのパターンマッチの機能を使用し、HTTPリクエストを処理するフレームワークです。

詳しい説明は、公式ドキュメントにサンプルとともに記載されてあります。

ResponseBytes

まずUnfilteredには、ResponseBytesと呼ばれるクラスが用意されており、そちらを利用することによりByteデータを簡単に返却することが可能となっています。

しかしながら、ResponseBytesを使った実装では、以下の様な問題が出てきます。

  • ダウンロード対象が大きいファイルの場合、データをArray[Byte]に溜め込んでしまうため、ダウンロード可能なファイルサイズがヒープ容量に依存。
  • ダウンロード時も全てファイルデータを読み込んだ後にファイルダウンロード開始されるため、開始されるまで止まっているように見える。


Channel(Netty)

そこで、NettyのChannelを使い、ストリーミングすることでResponseBytesで起きた問題を解決することが可能となります。

ここで注意すべき点としては、非同期処理を行うため、Planをasync.Planにする必要があります。

以上でストリーミングダウンロードが可能となりますが、Unfilteredフレームワークに限らず、ファイルダウンロードAPIを作成する際は、Channelなどを用いた非同期な実装を検討してみてはいかがでしょうか。

Author
Satoru Takaishi

BEMに基づいたCSS設計手法のメリットについて

BEMの採用でCSSとHTMLの開発プロセスの改善をする


私はCSS/HTMLの開発の際、構成管理をどのようにすれば良いか考える機会がありました。どんなコードベースでも以下の点の改善ができれば、開発のプロセスがもっと早く、もっと簡易に、もっと確かになるためです。
  • 再利用性
  • 拡張性
  • 開発生産性とメンテナンス性

上記の点の改善を可能にするCSSとHTML設計手法のうちの一つにBEMがあります。どのように改善ができるか、詳しく説明したいと思います。

再利用性


BEMにはブロックエレメントいう2つの概念があります。
ブロックとは独立した存在で、デザインの「構成要素」であります。ブロックはコンテキスト(親HTMLエレメント等)に依存せずに、ページのどこの位置に配置されても自分の機能を保つべきものです。
ブロックを組み合わせて、アプリケーションページを組み立てます。
エレメントは、ブロックの一部分であり、特定の役割を果たしています。 エレメントはコンテキストに依存していて、そのエレメントが属するブロックだけで利用されています。
ブロックとブロックの中にネストされたエレメントの例:
ブロックの独立した性質のおかげで、他のブロックと自由な組み合わせでアプリケーションのどこのページにでも簡単に再利用ができます。

拡張性


BEMには更にモディファイヤーという概念があります。
モディファイヤーはブロックとエレメントのスタイルにバリエーションを持たせるためのものです。CSSとHTMLの開発フェーズが終わっていても、CSSのベーシックな知識しか持っていないチームメンバーでも新しいモディファイヤーを追加することで、既存のブロックやエレメントのデザインの変更が簡単にできます。
既存のブロックの背景色を変更するモディファイヤーを追加する例:
また、既存のモディファイヤーを組み合わせたりすることで、新しいデザインが作れます。
既存の二つのモディファイヤーの組み合わせで新しいデザインのボタンを作成する例(ボタンの色とサイズの組み合わせ):

開発生産性とメンテナンス性


ブロックはコンテキストに依存しない存在であるため、ブロックを組み合わせることでページの作成が可能になります。
CSSとHTMLの作成を外部デザイナーに依頼する場合、アプリケーションページの全体の作成ではなく、必要なブロックだけの作成を依頼すれば、あとは基本レベルのCSSとHTMLの知識を持つチームメンバーでもブロック利用して、アプリケーションページの組み立てができます。
2つのブロックの簡単な組み立ての例:

簡略化されたBEMへのアプローチ


BEM自体は結構大きなフレームワークです。CSS設計ルールだけではなくて、専用のテンプレートエンジンやビルドツールやJavaScriptを取り扱う方法などが含まれています。このフレームワークの全ての要素を導入するには、相当な努力が必要でしょう。
幸いなことに、ブロック、エレメント、モディファイヤーというコアコンセプトを有効に使うのに、全体のフレームワークを取り入れる必要はありません。BEMに基づいたCSS設計ルールだけを採用しても、BEMをはじめることができます。
弊社では、こういうCSS設計ルールセットを用意しております。この設計ルールセットのメインの部分を紹介したいと思います。

BEMに基づいたCSS設計ルール


  1. クラスのネーミング

    概念的に、クラスのネーミングルールはBEMのものと同じですが、ブロック、エレメント、モディファイヤーの区切り用のエレメントを自分の好みにあわせています。
    各ブロックの定義は個別のファイルに納めることで、ブロック名の衝突を避けています。
  2. カスケードセレクターを利用しない

    コンテキストに依存してしまい、ブロックの再利用が困難になるため、カスケードのセレクターを利用しません。
    エレメントの場合も同じです。理由は2つあります。
    • セレクターのspecificity(詳細度)が上がってしまうので、CSSルールが特殊になり、モディファイヤーでのスタイルの上書きが難しくなります。
      Specificityというのは、CSSルールの優先順位のことです。複数のCSSルールが同じHTMLエレメントの同じ要素をコントロールしているとき、specificityが高い方が優先されます。基本的に、カスケードのあるセレクターのspecificityがカスケードのないセレクターより高いので、以下の例でテキストの色はredのままになり、モディファイヤーが効かなくなります。
      だめな定義の例:
      セレクターのspecificityが同じ場合、後から定義された方が優先になります。
    • クラスのネーミングルールによって、エレメントのクラス名にすでにブロック名が入っているため、他のブロックのエレメントのクラス名との衝突はおきません。
  3. タイプセレクターを利用しない(セレクタにタグ名が使われているセレクターのこと)

    • カスケードセレクターと同じように、specificityが上がるので、モディファイヤーでのスタイルの上書きが難しくなります。
    • 定義された以外のエレメントでの利用が不可になります。
  4. セレクターにIDを利用しない

    IDを使ってしまうと、そのブロックをページ内で一回しか使えなくなるために、再利用が不可になります。
以上が設計ルールセットのメインの部分の紹介になります。

簡単なブロックのCSSとHTMLのサンプル


以下に簡単なブロックとモディファイヤーの定義と利用方法のサンプルを紹介します。

結果


まとめ


最近手がけたエンタープライズ向けプロジェクトでも上で説明したCSS設計手法を採用しています。その感想を簡単にシェアしたいと思います。
  • 今回のプロジェクトではブロックのCSSとHTMLの作成を外部デザイナーに依頼していました。作成していただいたブロックを組み立て、合計で50以上の画面を作りましたた。ブロックの再利用が本当に簡単で、画面作成にかかるコストを大幅に減らせた印象があります。
  • デザイナーからブロックのCSSとHTMLが納品された後でもデザインの多少の変更がありましたが、CSSの再設計を依頼せずに、モディファイヤーを利用することで既存のブロックの拡張が簡単にできました。
著者について
グリシン キリルは、11年前にロシアから来日し、2013年からエンラプトでウェブアプリケーションの開発に従事する。Functional Reactive Programmingに夢中で、クライアントサイドの開発を簡単に、より確かにするための研究を行う。

Popular Posts