システム開発で思うところ

Javaで主にシステム開発をしながら思うところをツラツラを綴る。主に自分向けのメモ。EE関連の情報が少なく自分自身がそういう情報があったら良いなぁということで他の人の参考になれば幸い

Enumのメモ

Enumに関するメモ。随時更新。

できること

Effective Java 読書会 8 日目 「それ enum で出来るよ」 - IT戦記

メソッド拡張

Java: ステータスをあらわす Enum に isXxx() メソッドを持つのが好き - Mitsuyuki.Shiiba

Enumの型比較にはinstanceofを使う

enumをもうちょっと使う - しげるメモ

再帰的ジェネリクスを活用したJava8時代のコード定義Enum用インタフェースを作成してみた | キャスレーコンサルティング株式会社

メモ

列挙定数にメソッドを追加するとisEnumでは型比較で失敗するので注意すること。

その他

Enumのテストでカバレッジが100%にならない謎 - Qiita


InterfaceとEnumを組み合わせた実装例として

github.com


あなたのエラーコードは何ですか? - Qiita


スクラム冬の陣2017 みんなで学ぶスクラム に行ってきました

postudy.doorkeeper.jp

これまでJava界隈のセミナー(主にJJUG関係)に行っていましたが、今回は開発手法に関する勉強会に行ってきました。

一人開発の私にアジャイルとかスクラムとか関係は無いのかもしれませんが、以前、開発プロセスの仕事をしていたこともあり、こういう情報は聞くだけでも十分面白いですね。

改めて感想の分量とお話の面白さは比例しないことを先に断った上でツラツラと書いていきたいと思います。

20分でスクラム入門 ステップ・バイ・ステップ

スクラムのイロハのお話。資料をパブリックドメインと明言していただけると社内で勉強会をしようと思う人たちにとって使いやすいので良いですね。

Scrumを使うと本当に成長できるの?

外的動機付けがクリエイティブな仕事の生産性を下げるというのは興味深かったです。クリエイティブな仕事については、芸術家に1億出すから感動する作品を作れって言っても「金額の問題じゃない、才能とインスピレーションの問題だ!」という話だと思っていたので。必ずしもそうではないんですね。まぁ、芸術家の制作物に対して「生産性」というのも変な話かもしれませんが。。
研究者にとって一番の報酬は、生活に苦労することなく一番好きな研究を死ぬまで続けられることだ、という話をどこかで聞いた記憶がありますが、つまりはそういうことなんでしょうね。それは理解できる気がします。

多様な働き方をするチームでスクラムを実践してみた

開発業務以外でのスクラム適用のお話。
業務改善手法として活用した、ということで良いのかな?以前、WEB-DBPressで知った業務フロー改善ツールのマジカの記事を思い出しつつ、話を聞きました*1
間接部門といわれる人たちの生産性や成果については定量化が難しいと言われますが、こうやって見えるようにするだけでも随分と変わるだろうなぁ、と思います。
スクラムの導入ハードルを考えたら、営利活動に直接影響のない間接部門から適用をして、会社として有効性を立証してから開発部門に展開をする、というのも良いアプローチなのかもしれないと思いました。開発部門展開前に自社にスクラムマスターになる人がいるというのもメリットの1つなように思いますね。

お悩み解決!持ってて損しない道具箱

モデリングのお話。
事前に資料を見てしまっていたこともあり、ちょっと休憩モードになってしまった。。 モデリングトレーニングは、能力ではなく技術なので、やればやるほどスキルアップするというのは実感があるので納得するところ。言いたい事は理解できるので駄目というわけではないですが、鉛筆のモデリングは製造系システムの人だったらピンとくるかもしれないけど、ちょっと私にはモデリングのためのモデリング、みたいな気がしてしっくりこなかったかな。個人的には、こちらの書籍にあるような結果に対して、その背景となる構造をモデリングする、というのが、しっくりくるかな。

楽々ERDレッスン (CodeZine BOOKS)

楽々ERDレッスン (CodeZine BOOKS)

導入に困っているあなたに贈る スクラム導入コミュニケーション術

【Scrumを使うと本当に成長できるの?】と同じく、スクラム導入にむけた障害解消ノウハウのお話(もっと良い表現が思いつかない。。)
どちらも「こんなにスクラムは良いのだから採用しましょう!」というアプローチではないことが良いと思った。ちょっとずれた表現になるとは思うけど「良い商品が売れるのではなくて、売れる商品が良い商品」という感じ(いや違うなぁ)。何かを推進したい場合は、自分の正しさのみを正義にしてもダメだよ(これもちょっと違うなぁ)。
いずれにしても、この知見は、実際にスクラムを運用する際にも活用できるメンタリティだと思います。

組織”規模毎”のアジャイル開発アプローチ事例~成功・失敗・ピボット・トライ~

スクラムマネージャーの話。 手の届く範囲のスクラムマネージャーから、全社統轄のスクラムマネージャーになった際に同じ手法のままでは駄目だったという話。全社で約50%の適用(アーリーマジョリティ)な状態になってからのサポート指針の見直しについては、数値的考察であったことも含めて、とても興味深かったです。事例としては、部門別に見てスクラムの占拠率が低いところに重点的にサポートをして定着を図るという話でした。キャズム理論の話も少しされていましたが、スクラムの適用そのものを戦略とした場合、ランチェスター戦略的アプローチも検討してみてはどうかな?と、知ったかで思ったりもしました。いずれにしても導入がゴールではなく、花と同じようにきちんと水や肥料を与えないと枯れてしまうというのは世の常だな、と思いました。企業規模も鑑みると、たぶん、スクラムマネージャーマネージメントが必要な気がします。

産業機器B2Bビジネスへのスクラム手法の有効活用

機器製造でのスクラムというのは初めて聞いたので面白かったというか意外でした。恣意的なマネージメント力とか開発部門による営業力強化みたいな抽象的な指針ではなく「スクラム」というツールの介在が、物を作る情熱と専門性へのプライドのバランスを取ったのかな?と思いました。制約があるから発想が生まれるという話を聞いたことがありますが、スクラムというツールが程よい制約・ルールになったのかもしれませんね。

エンタープライズアジャイルの可能性と実現への提言

約2年間の知見の集約と提言のお話。
ちょっと文章で表現するのは難しいので、上述のリンクから資料をみていただくのが一番よろしいかと。。 という前提を置いたうえで、スライドでポイントしておきたいところのメモです。
「うちでもアジャイル開発やってみましたアンチパターン」と、アジャイルの適用ターゲットになる「ポストERPとしての次世代手組み」の【手組み2.0の領域】の要素群のところ。
あと、経営層向けには「ROIを考えるための要素」のところ。定量的にアジャイル開発でリリースした方が収益化を考えても良いよ、という論理的な説明になっています。
アジャイル開発導入の課題」も体制など具体的な数値の話があるので目を通しておきたいところです。

客先常駐案件でスクラムを導入したスクラムマスターの話

運用保守フェーズにスクラムを適用したお話。
大規模な段階的イテレーションで無ければ、確かに運用保守フェーズにスクラムは適用しやすいように思います*2。むしろ、開発の基本基盤や実現性の担保がある程度とれている運用保守フェーズこそ、お試しアジャイル導入には有効な気がします。

ベトナムでのアジャイルオフショア開発への挑戦

現地駐在でのオフショア開発でのアジャイル適用のお話。

  • どんな人も達成感を味わうことは喜びである
  • 理解が出来ても実践できないことはある、ということを謙虚に受け止めよう
  • スクラムの効果はメンバーとの物理的な距離の近さに比例する

なんたって”DevQA” アジャイル開発とQAの合体が改善を生む

開発プロセスにかかわっていた時に、BDD駆動開発に興味を持ちました。そのプロジェクトに参画した工程がシステムテストだったということもあり、TDDというよりもBDDの方がしっくりとくる感じでした。その時「基本設計工程が終わったら、そこからテスト計画を並行して行い、開発と並行もしくは先んじてあるべき状態を検証すれば品質は良くなるのではないだろうか?」と考えていました。その解として私にはBDDがしっくり来たわけです。
ですが、私ごときが思うくらいですから、多くの知見において、それはすでに実践されていると思いましたし、そうだとして何故上手くいっていないのだろうか?とも思っていました。また設計要件が満たされることを保証すること=機能が充足している・品質が良い、というのにも疑問がありました。なぜなら、ITやST工程になって発生する「仕様漏れ」「検討漏れ」といった上流工程*3起因のバグは取り残されてしまうからです。むしろ、そういうバグは質が悪く致命的なものになるケースになりやすいです。つまり本来解消しておきたい致命的な仕様漏れは上流工程関係者だけでは解消できないという開発プロセスとして構造的な問題があると感じていました。
アジャイルがその解なのかなぁ、と思うところもありましたが、顧客の事業計画上、そういう形式での予算構成では無く、理想と現実の差を感じつつ、ウォーターフォールの範疇で出来うることを考える日々でした。
おそらく、その解に近いアプローチが、この"DevQA"なように思います。アジャイルの方がより参加者にとって適用しやすいというところはあるかもしれませんが、このアプローチは開発手法の大小に関係ないように思います。他の方のお話も十分に面白いものでしたが、このお話を聞けただけでも参加させていただいた価値があるくらい面白かったです。

全体を通じて

皆さんの話に共通して感じたのは、スクラムアジャイルが素晴らしいということではなく、チーム全員が参加して作り出す仕事は面白い、ということでしょうか。スクラムは、その手法としてプロセスそのものに、その考慮を生き渡らせているので有効ですよ、というところでしょうか。 PMOが流行ったときに、現場側で感じた「高みの見物の寸評者」感よりも、スクラムの方が運用プロセスにマネージャーの参加を促す仕組みが組み込まれている感じを受けました*4

さいごに

初めて参加したアジャイル勉強会でしたが、色々と得るものが多かったように思います。一人開発で何を生かすのか?というのはありますが、明日への活力的なものを共有できたというだけでも十分な効用だと思います。

*1:マジカはフローそのものの話なので、プロセスであるアジャイルとはレイヤーが違いますけどね

*2:大規模な段階的イテレーションをしている時点を運用保守フェースといって良いのか?というのは置いておいて

*3:この言い方はあまり好きではないですが

*4:誤解無く言うと正しいPMOも血の通った価値あるものです。ただ何故か知識優先というか、あたまでっかちというか、そういう印象が強かったというだけです

Gitのメモ

GitHubの使用するにあたって必要と思われる自分向けのサイトリンクおよびメモです。
本記事を都度更新します。

作法

Git がわからなくても Github を利用しよう | そんなこと覚えてない

注意事項

チーム開発においてGit初心者が踏みがちな地雷まとめ|TechRacho by BPS株式会社

● 他のメンバがcheckoutしている可能性のあるリモートブランチに対してpush -fしてはいけません
● featureブランチは一人で開発している場合、かつdevelopにmergeする前においてのみpush -fしても大丈夫

VSCodeでGit操作

(Lubuntu環境での開発時はVSCodeで完結させたいため)

VScodeだけでGit操作を完結させるのだ~~ッ!!

push -fを使う事例

Githubでアカウントを晒した事故に対する対処 #Android - Qiita

【git】git pushを取り消す - tweeeetyのぶろぐ的めも

GitHubgit gcは定期処理なので即時削除ではない
● 【私見】履歴の掃除のやり方は知っておくべきだが、本当にそれをしなければならないか考える事。汚い履歴であっても履歴そのものが悪で無ければ諦める事も大切だと思う。*1

リポジトリ管理

実験やサンプルのリポジトリを分離、自分の GitHub アカウントのリポジトリを分類・整理する : @jsakamoto

gitリポジトリを軽くしよう!|TechRacho by BPS株式会社

Gitリポジトリをメンテナンスして軽量化する #Git - Qiita

アホみたいにでかいgit repositoryを上手く扱う方法 #Git - Qiita

git repository size を削減する | DriftwoodJP

Gitでやらかした時に使える19個の奥義 #Git - Qiita

コマンドおよび作業フロー

最近のgitを使った開発フローについて - その手の平は尻もつかめるさ

2011-05-28

Gitをある程度使えるようになったら、更に覚えておくと良いかもしれないコマンドとそのオプション #Git - Qiita

Gitでやらかさないための事前予防策 #Git - Qiita

gitで差分ファイルを抽出する #Git - Qiita

GitHub初心者はForkしない方のPull Requestから入門しよう | qnyp blog

http://moznion.hatenadiary.com/

コミット履歴を綺麗にするときの`git commit --fixup`と`git rebase --autosquash` - 理系学生日記

NetBeansGitHub

https://netbeans.org/kb/docs/ide/git_ja.html

NetBeans Git操作 - ソフトウェアエンジニアリング - Torutk

開発はローカルブランチで行う*2

NetBeansMarkdown

NetBeans Markdown plugin | junichi11.com

記載内容をすぐ確認したい場合は
[オプション]→[一般]→[Webブラウザ]:埋め込みWebkitブラウザ
[オプション]→[その他]→[Markdown]:[View Html On Save]をオン
保存する都度、埋め込みブラウザの内容が更新されます。
ディスプレイが小さい場合は、埋め込みブラウザのタブを[フロート]して別ウィンドウにすると作業がやり易いです。

bitbucket

https://toruuetani.bitbucket.io/bitbucket.html

鍵の作成

ssh 複数アカウント設定 #GitHub - Qiita

BitbucketにSSH認証鍵を設定する #Git - Qiita

http://tec-shi.com/tips/648/

その他

How to Publish Maven Site Docs to BitBucket or GitHub Pages - DZone

MarkdownでSlide作成して自分のGitHub Pagesで公開する - 山pの楽しいお勉強生活

書籍

*1:アカウント情報などセキュリティに関するものは悪

*2:この発想が無いので戸惑ってしまった

Pluggable Annotation Processing API Sample

Pluggable Annotation Processing APIについて調べたり試したりしたまとめです。
今回やりたかったことは、Annotation Processorで生成したソースが確認できるまでの環境準備です。「とりあえずAnnotation Processorで簡易かつ完結したプロジェクトが欲しい」という事を満たすことが目的です。

参考サンプル

参考リンクとは別に、今回動くもののサンプルとして参考にさせていただいたサイトを先に挙げておきます。サンプルとさせていただいたAnnotation Processorそのものの意図などはこちらを確認いただければと思います。

qiita.com

GitHub

今回のソースおよび実行環境の全量です*1

GitHub - vermeer-1977-blog/annotation-processor-sample: Pluggable Annotation Processing API Sample

ポイント

今回の目的である「完結したプロジェクトを作成する」というところに絞ったポイントです。

テストについて多くのサイトではAptina Unitを話題に出していました。実際、DomaJsonPullParserでも使われています。私自身も試してみたのですがJavaのバージョン@SupportedSourceVersion(SourceVersion.RELEASE_8)で警告が出る事と今回のサンプルで上手くソース生成結果を取得できなかったので(getGeneratedSourceSourceNotGeneratedExceptionが出る)、私はCompile Testingを使うことにしました。
ただCompile TestingにはgetGeneratedSourceのようなメソッドが見当たりませんでしたので、今回のプロジェクトでは直接コンソールに結果を出力するようにしています。*2


テストとは直接関係ないのですが、生成後想定クラスSampleFactory.javaについては、NetBeansで編集して保存をすると自動でフォーマットされてしまい、こちらの意図した形式と不一致になってしまいました。拡張子をtxtにして編集をして、最後に拡張子だけをjavaに変えるという力技で対処しました。

参考リンク

Annotation Processorについて教科書的知識として必ず目を通した方が良いもの*3


実行サンプル(上述と同じ)
Annotation Processingを使ったソースコード生成プログラムを作ってみる。(Java編) - Qiita


Compile Testing

アノテーションプロセッサで生成したコードをCompile Testingを使ってテストする - 量産型エンジニアの憂鬱


その他

アノテーションプロセッサで AST 変換 - Lombok を参考にして変数の型をコンパイル時に変更 - なんとなくな Developer のメモ

さいごに

Annotation Processorを調べている中でDomaJsonPullParserのソースを少しだけ読んだりしました。正直、私には難しかったです。ですが分からないなりにAnnotation Processorの良さも分かったようにも思います。うらがみさんの「白魔術」という表現も言い得て妙だと思いました。

https://twitter.com/backpaper0/status/578776701989052416

ツール

(2017/11/17追記)

ツールを作ってみました。

ついでに、その実装説明も書いてみました。

vermeer.hatenablog.jp

vermeer.hatenablog.jp

*1:GitHubを初めて使いました。思った構成にならず、Readmeの書き方に戸惑い、何度もRepositoryを削除するという繰り返し。まだ良く分かっていませんが、とりあえずプロジェクト公開まで至れたので良しとしたいと思います。

*2:その他のメッセージも同様ですが実際の稼動時には出力しないようにしてください。

*3:といいつつ、私は途中までしか読んでいません。。なので自分向けのリンクでもあります

Netbeans で Pluggable Annotation Processing API

こちらの記事のNetBeans版のようなものです。

d.hatena.ne.jp

AnnotationProcessorが動くまでのところのチュートリアル的にやってみようと思ってやってみたところ、少し躓いたところもあったので誰かの参考になればと思い、まとめてみました。事例のベースもリンク元と同じなので詳細は上述のリンクを確認ください。本記事では動くまでの流れを中心にします。

はじめに

NetBeansのビルドではなくmavenプロジェクトで実施しています。試してはいませんがNetBeansに強依存はしていないとは思います。
実行環境はJava8です。

新規プロジェクト(Processor提供側)を作る

mavenプロジェクトを作成する

f:id:vermeer-1977:20170102233832p:plain f:id:vermeer-1977:20170102233833p:plain

提供側ソース

package com.mycompany.annotationprocessor;

import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("java.lang.SuppressWarnings")
public class SuppressWarningsProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
                           RoundEnvironment roundEnv) {
        Messager messager = processingEnv.getMessager();
        for (TypeElement annotation : annotations) {
            for (Element element : roundEnv
                    .getElementsAnnotatedWith(annotation)) {
                messager.printMessage(Kind.WARNING, "@SuppressWarningsしちゃだめ",
                                      element);
            }
        }
        return true;
    }
}

サービスプロバイダーコンフィギュレーションファイルの保存先作成

このフォルダの作成自体がポイントです。*1

f:id:vermeer-1977:20170102233835p:plain f:id:vermeer-1977:20170102233836p:plain f:id:vermeer-1977:20170102233837p:plain f:id:vermeer-1977:20170102233838p:plain

サービスプロバイダーコンフィギュレーションファイルを作成

f:id:vermeer-1977:20170102233839p:plain f:id:vermeer-1977:20170102233840p:plain ファイル名:javax.annotation.processing.Processorを作成し、AnnotationProcessorを登録する

com.mycompany.annotationprocessor.SuppressWarningsProcessor

今回は1つですが複数のAnnotationProcessorがある場合 改行区切りで登録できます。

最終的なフォルダ構成

├─src
│  └─main
│      ├─java
│      │  └─com
│      │      └─mycompany
│      │          └─annotationprocessor
│      │                  SuppressWarningsProcessor.java
│      │                  
│      └─resources
│          └─META-INF
│              └─services
│                      javax.annotation.processing.Processor

pom.xmlの編集

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>AnnotationProcessor</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <!-- Disable annotation processing for ourselves. -->
                    <compilerArgument>-proc:none</compilerArgument>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

ポイントはコンパイルオプションのところ-proc:noneです。またpom.xmlgroupIdは次の利用側で参照します。ただし、今回は「同一プロジェクトグループ」という形で参照します。

新規プロジェクト(Processor利用側)を作る

mavenプロジェクトを作成する

プロジェクト名samplesで作成。作成手順は上述の通りなので省略。
既存のmavenプロジェクトがあれば新規作成は不要です。

利用側プログラム

package com.mycompany.samples;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unchecked")
public class Sample {

    void hoge() {
        List list = new ArrayList<>();
        list.add("");
        System.out.println(list.size());
    }
}

pom.xmlの編集

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>samples</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- for my group annotation processor start -->
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>AnnotationProcessor</artifactId>
            <version>${project.version}</version>
            <type>jar</type>
        </dependency>
        <!-- for my group annotation processor end -->
    </dependencies>

</project>

今回はgroupIdは、提供側と同じなので${project.groupId}と指定しています。

適用された結果

f:id:vermeer-1977:20170102233841p:plain

警告を見ると適用されていることが分かります。

さいごに

他に参考にさせていただいた情報だともっと面倒な印象でしたが、結果としてmavenの設定だけで大きな手間も無く動くところまで確認できて良かったです。

今回の記事とは関係ないですがmavenプロジェクトを個別に作成をして依存を定義するというのは、自分のシステムでレイヤー間の依存関係を明確にするために使っています。
プロジェクトで分割しておくことで、うっかりController層のクラスを誤ってService層で参照しないように強制させることが出来ます。*2

*1:私が躓いたところです。当初、場所が正しくなく参照されませんでした。

*2:Java9のJigsawがリリースされるまでの工夫といったところでしょうか

EnumのInterfaceに共通処理を実装する

以前の記事で、Interfaceとユーティリティのペアを説明しました。
Enumに状態を保持して、ユーティリティで振舞いを実装する、というやり方です。

vermeer.hatenablog.jp

ロジックの冗長さを避けようと思ったわけですがオブジェクト指向ってそうじゃないんだよね、とも思います。オブジェクト指向至上主義ではありませんが、ユーティリティを使うやり方は、気を抜くとユーティリティまみれになり兼ねないです*1

というわけで、前回の実装をベースに、Interfaceで出来そうな範囲の実装をしてユーティリティクラスを使わない実装を考えてみたいと思います。

実装:staticメソッドを追加する

Interface

package com.mycompany.samples.enumclass.test;

import lombok.NonNull;

/**
 * propertiesの仕様を前提として拡張をしたEnumクラスを共通操作するためのInterface
 *
 * @author Yamashita
 */
public interface EnumCodePropertyInterface {

    public Object getCode();

    public String getProperty();

    /**
     * properties用に拡張したEnumのcode値から取得した拡張Enumを生成する
     *
     * @param <E> EnumCodePropertyInterfaceで拡張したEnumClass
     * @param enumType 生成対象となるEnumClassの型となるClass
     * @param code テーブルや定数として指定しているcode値
     * @return codeに一致するEnumクラス
     */
    public static <E extends Enum<E> & EnumCodePropertyInterface> E codeOf(Class<E> enumType, @NonNull Object code) {
        for (E type : enumType.getEnumConstants()) {
            if (type.getCode().equals(code)) {
                return type;
            }
        }
        throw new IllegalArgumentException("enum class has not code : " + code.toString());
    }

    /**
     * properties用に拡張したEnumのpropertyKey値から取得した拡張Enumを生成する
     *
     * @param <E> EnumCodePropertyInterfaceで拡張したEnumClass
     * @param enumType 生成対象となるEnumClassの型となるClass
     * @param property propertiesの検索に使用するpropertyKey
     * @return propertyKeyに一致するEnumクラス
     */
    public static <E extends Enum<E> & EnumCodePropertyInterface> E propertyOf(Class<E> enumType, @NonNull String property) {
        for (E type : enumType.getEnumConstants()) {
            if (type.getProperty().equals(property)) {
                return type;
            }
        }
        throw new IllegalArgumentException("enum class has not propertyKey : " + property);
    }

}

EnumClass

package com.mycompany.samples.enumclass.test;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;

@AllArgsConstructor @Getter
public enum Gender implements EnumCodePropertyInterface {
    MALE(1, "男"),
    FEMALE(2, "女");

    private final Integer code;
    private final String property;

    public static Gender codeOf(@NonNull Object code) {
        return EnumCodePropertyInterface.codeOf(Gender.class, code);
    }

    public static Gender propertyOf(@NonNull String property) {
        return EnumCodePropertyInterface.propertyOf(Gender.class, property);
    }
}

使用例

    public void exec() {
        Gender gender = Gender.codeOf(Gender.FEMALE.getCode());
        System.out.println("gender.getCode() = " + gender.getCode());
        System.out.println("gender.getProperty() = " + gender.getProperty());
    }

さいごに

staticによる実装なので、結局のところユーティリティクラスを内包しただけとも言えるかもしれません*2。メリットは使用するEnumClassに仕様が凝集していて使用時の実装もすっきりと読みやすくはなっているように思います。なによりもEnumvalueOfと並列な感じで個人的には分かりやすいように思います。デメリットは、Enumを定義する際に儀式的にメソッドを実装しないといけないというところです。*3

後日追記

自分なりにAnnotationProcessorによる実験的な実装も検討してみましたが素直にユーティリティを使うやり方がシンプルで良さそうだ、という結論に至りました*4

*1:必然性があれば、ユーティリティは悪ではないですが、やり易いからという理由でユーティリティを多用するのは、基本的に良くないと思っています

*2:おそらく実際そうです

*3:冗長さを解消するためにはPluggable Annotation Processing APIが解な気がします

*4:lombokの拡張が自分のイメージに一番近いのですが、そこまででも無いかな、と

JSFでResourceBundleを適用する

前回は、全部入りのResourceBundleを作ってみました。

vermeer.hatenablog.jp

今回は、そのResourceBundleをJSFで使えるようにするためのパーツとサンプル実装を作りました。ほとんどは、参考リンクのままです。

ResourceBundleのファクトリークラス

/*
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *  Copyright © 2016 Yamashita
 */
package com.mycompany.samplejsf.infrastructure.jsf.uicomponet;

import com.mycompany.samplejsf.infrastructure.resourse.CustomControl;
import java.util.Locale;
import java.util.ResourceBundle;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * JSFで参照するResourceBundleを生成するFactoryクラス.<br>
 * 本クラスで生成したクラスを{@literal faces-config.xml}に設定してJSFから参照する.<br>
 *
 * @author Yamashita
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsfResourceBundle {

    /**
     * ResourceBundleを生成する.<br>
     * 取得方法の制御については、CustomControlで指定をする.<br>
     *
     * @see com.mycompany.samplejsf.infrastructure.resourse.CustomControl
     *
     * @param bundleBaseName 取得対象のResourceBundleのBaseName(AAAA.propertiesのAAAAの部分)
     * @param customControl 必要情報を設定したCustomControl
     * @return 生成したResourceBundle
     */
    public static ResourceBundle getBundle(String bundleBaseName, CustomControl customControl) {
        Locale locale = JsfContextUtil.getLocale();
        return ResourceBundle.getBundle(bundleBaseName, locale, customControl);
    }

    /**
     * ResourceBundleを生成する.<br>
     * Controlを未指定の場合は、文字コードはデフォルト(ascii)、取得対象はpropertiesファイルのみとしてResourceBundleを生成する.<br>
     * 取得対象をpropertiesのみとするのは、検索対象を絞る事で性能改善を図る.
     *
     * @param bundleBaseName 取得対象のResourceBundleのBaseName(AAAA.propertiesのAAAAの部分)
     * @return 生成したResourceBundle
     */
    public static ResourceBundle getBundle(String bundleBaseName) {
        CustomControl customControl = CustomControl.builder()
                .formats(CustomControl.FORMAT_PROPERTIES)
                .build();
        return JsfResourceBundle.getBundle(bundleBaseName, customControl);
    }
}

ResoureceBundleを生成する

/*
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *  Copyright © 2016 Yamashita
 */
package com.mycompany.samplejsf.domain.resource;

import com.mycompany.samplejsf.infrastructure.jsf.uicomponet.JsfResourceBundle;
import java.util.Enumeration;
import java.util.ResourceBundle;

/**
 * 表示ラベルResourceBundle
 *
 * @author Yamashita
 */
public class LabelResourceBundle extends ResourceBundle {

    public LabelResourceBundle() {
        ResourceBundle bundle = JsfResourceBundle.getBundle("label");
        super.setParent(bundle);
    }

    @Override
    protected Object handleGetObject(String key) {
        return super.parent.getObject(key);
    }

    @Override
    public Enumeration<String> getKeys() {
        return super.parent.getKeys();
    }
}

propertiesファイルのbaseNameであるlabelを指定します。今回は、デフォルト指定のためCustomControlを使用していませんが、先日作成したCustomControlを引数として使用することで文字コードや参照リソースの絞り込みなどができます。

properties

label.properties

hello=こんにちは
hello.hi=こんにちは、こんちは

message.properties

noselect=選択無し
gender_male=男
gender_female=女

faces-config.xmlを設定する

<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
              xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">

    <application>
        <resource-bundle>
            <base-name>
                message
            </base-name>
            <var>msg</var>
        </resource-bundle>
        <resource-bundle>
            <base-name>
                com.mycompany.samplejsf.domain.resource.LabelResourceBundle
            </base-name>
            <var>label</var>
        </resource-bundle>
    </application>
</faces-config>

xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <head>
        <title>properties label</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    </head>
    <body>
        <h:form>
            <div>
                <p>#{label.hello}</p>
                <p>#{label['hello.hi']}</p>
                <p>#{msg['gender_male']}</p>
            </div>
        </h:form>
    </body>
</html>

出来るは出来たのですが、msgNetBeans*1で入力補完がされるのですが、独自に拡張したResourceBundleで参照しているlabelについては入力補完がされませんでした。タイプセーフになることを期待したのですが、ちょっと残念です。

実行結果

f:id:vermeer-1977:20161231163714p:plain

参考リンク

http://noknow.info/it/2015/javaee7_jsf2_2_use_resourcebundle_of_utf8file_ja.html

Code

2018/4/17 追加

Bitbucket

*1:Product Version: NetBeans IDE 8.1(Build 201510222201)