Swagger + SpringBoot で APIサイトを作ってみる

既存システムのDBへアクセスしてデータを参照するようなAPIを、って事で、
SwaggerでAPIを定義し、Swaggerから自動生成したソースを使って実装してみました。
なお、今回対象としているのは参照系がメインでRESTって雰囲気ではありません。
対象が既存システムのDBなので。。

とりあえず、ざっくりとした要求と応答の仕様を整理し、Swagger Editorにyamlを書いてみます。

なお、Swagger Editorは、別件で使っていた端末内のxampp/htdocsにGitHubからダウンロードしたZIPを解凍して使ってます。
ある程度、定義を書いた後、Swagger EditorからGenerate ServerでSpringのサーバソースを出力してみます。

自動生成されたJavaソースはこんな構成です。

Controllerとレスポンスエンティティのモデルが揃ってます。全体的なソース構成は、コントローラ層、サービス層、データアクセス層という3構成になりますかね。
コントローラ層は概ねSwaggerで生成したので、データアクセス層も自動生成しようと、Doma2を使ってみます。
doma-gen-build.xmlに接続先DB情報と出力先ソースの設定等を記述し、こちらもGenerate!

結果、ソースを手書きする必要があるのは、

・サービス層全体

・Swaggerで自動生成されたSpringBootApplicationクラスのComponentScanにサービス層のパッケージを追記
  @ComponentScan(basePackages = { “io.swagger”, “io.swagger.api”,”jp.esoro.api” })

・Swaggerで自動生成されたControllerにServiceをコールする箇所を追記

・DAOインターフェースに下記アノテーションを追記
@ConfigAutowireable
@Repository

・自動生成不可能なSQL文の作成し、それに合わせてDAOにメソッド追加

といったところで、とりあえずの動作確認が出来ました。

ただ、実用するには認証やページング等も必要なので、こんな単純な話にはなりませんし、APIの仕様をちゃんと整理してからSwaggerでソースを生成して着手しないと後が面倒ですね。

カテゴリー: Java

NTLM認証のサイトからファイルをダウンロードする

SpringBootで作成中のアプリに、企業内のWebサイトよりエクセルファイルをダウンロードする要件があったので、何となくBasic認証のサイトだろうなと思い込んで実装を始めましたが全然繋がりません。

wget ‘http://username:password@filestore.bbb.co.jp/web/download/downloadfile.xlsx’
でファイルは取れます。

でも、
curl ‘http://username:password@filestore.bbb.co.jp/web/download/downloadfile.xlsx’
では動きません。

試しにwgetをデバッグモードでやってみて気が付きました。。

WWW-Authenticate: NTLM

このサイト、NTLM認証ですね。。

下記を参考に認証箇所を変更
http://code-addict.pl/reporting-services-rest-url-client/

また依存が増えてしまいましたが。。

ダウンロードファイルが更新されていた時だけダウンロードするという対応も必要なので、サーバ内にダウンロードしたローカルファイルとサイト上のファイルタイムスタンプをチェックし、一致しなければファイルをダウンロードしタイムスタンプをサイト上と同じ時間に書き換えるという対応になりました。

で、タイムスタンプはどこ?ここに書いてました。

確かにhttpヘッダに
Last-Modified:Tue, 14 Aug 2018 08:53:30 GMT
と書いてあります。

結局、こんな感じのコンポーネントを用意しました。

テストしてみます。

とりえあず、動いたので整理してから実装します。

カテゴリー: Java

SpringBootでWebアプリが動かない

 作りかけのSpringBootのRestアプリを別端末の開発環境(Windows+EclipseSTS+Maven)へ移行して動かしてみたところ、ブラウザからURLを打ち込んでも、RestControllerがうんともすんともいいません。元々の環境では普通に動作していたのですが。。
 困ったな・・と別環境への移行を諦めようかと思っていたところ、よく見るとSpring実行時のログ出力に下記を発見


[ERROR] C:\Users\admin\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.31\tomcat-embed-core-8.5.31.jarの読込みエラーです。invalid LOC header (bad signature)

 とりあえず該当のC:\Users\admin\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.31 内のファイルを全て消して再度、プロジェクト→実行→Maven install実施。
すると何事も無く動き始めました。。


2018-07-30 15:18:55.806 INFO 14844 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8090 (http) with context path ''

こういった問題はあまり深く考えても詮無き事。。
なお、デフォルトの8080ポートは他アプリと被ってたので、application.ymlに

server:
port: 8090

と書いています。

カテゴリー: Java

SpringBootのCacheにCSVを入れてみた

 別のところで開発してもらったSpringBootのソースを引き取って機能追加の対応をしているのですが、毎日定時に更新されるCSVファイルをマスターデータとして使用するという要件が含まれてました。
 CSVをそのまま処理の都度読み込むのもイマイチだし、今回はSpringBootのアプリケーションなんで、SpringBootCacheを使ってみる事にしました。

今回の要件的にCacheとしてはConcurrentMapCacheでこと足りそうなので、実装前に下記サイトの住所CSV関東版を使って試してみます。
住所データのダウンロードサイト【住所.jp】

まず適当にエンティティを・・

キャッシュ設定のevictスケジュールについては、ここではテストなので1分でクリアし、クリアした事がわかるように出力してます。実際には日次CSVファイル更新処理が終わった後くらいに動作するようにします。

で、サービスを作りますが、ここでCSVのデータを全部キャッシュに突っ込みます。CSV読み込みについてはこちらを参考にさせて頂きました。

事前にapplication.ymlに下記を書いておきます。

依存しているものです

最後にテストを書いて効果を確認します。

テスト結果です。

CSVを読んだ場合500ms程度ですが、キャッシュが効いてると殆ど1ms以内の世界です。
こりゃいい感じですね!

カテゴリー: Java

google-http-clientを使ってみた

 とあるWebAPIサービスを提供しているサイトへアクセスするクライアントをjavaで書こうかと、最初はjerseyを試してみたのですが、これがなかなか厄介で、次から次と依存が判明して動作するまで一苦労。。
最終的には、とあるサイト側の認証における制約により、jerseyの仕様ではどうやってもアクセスが出来ない事が判明。。

 失敗の原因は概ね当たりが付いていたので、その辺りに問題が無さそうなライブラリとしてgoogle-http-clientを使うことにしました。
 jerseyで途中まで実装していたものをそのままgoogle-http-clientを使う形に置き換えて疎通確認レベルまでやってみます。

 google-http-clientは依存が無く構成がとてもシンプルで置き換えた途端に、サックリとAPIアクセスに成功しました。proxy経由でもJVM引数で指定してあっさり通りました。

 細かいところでは、どうも指定したタイムアウトは効いてないような気がしますが、まあ、今回の実務上問題無さそうなので、利用しやすいように整理して実装しようと思います。

Apache CommonsのDateUtilsに機能を拡張する

 よくあるケースと思いますが、メジャーなライブラリにもうちょっと機能が欲しい時、そのライブラリを拡張して共通ライブラリとして使いまわしたりしますよね?
筆者がJavaを使う場合は、だいぶ前からApache CommonsのDateUtilsを拡張し、String・Dateの相互変換メソッドとかを追加していろんな開発案件で使いまわしてましたが、今回、文字列から日時型に変換するけど、どんな文字列パターンになるかが不明確、、という要件があったので、拡張していたクラスに機能を追加し、一般的に日時として使われる文字列からのDate変換機能を追加してみました。

で、テストです。

テスト結果
20170131 is null
2017-9-2 15:00:00 is 2017/09/02 15:00:00
2017/9/2 15:25:00 is 2017/09/02 15:25:00
2017年9月2日8時34分51秒 is 2017/09/02 08:34:51
2017年1月2日8時32分 is 2017/01/02 08:32:00
2017年12月31日午後11時集合 is 2017/12/31 23:00:00
2017年12月2日8時 is 2017/12/02 08:00:00
2017.12.2 PM8:00 is 2017/12/02 20:00:00
2017.12.3 AM8:25 is 2017/12/03 08:25:00
2017.01.31 is 2017/01/31 00:00:00
2017.1.32 is null

YYYYMMDDってのは、ここでは対象外です。だって、ただの数字の羅列は文字として日付とは言えないし。

カテゴリー: Java

ManagedBeanが非推奨になっていた

JavaEEでJSF2とJAX-RSを使った簡単な業務Webアプリを作っていたんですが、それまで作っていた開発環境のままWebLogicでローカルテストしてある程度動作するようになった後に、さすがにこの規模のAPにWebLogicを用意するのは無理!という事になったので、前にちょっと別件で使ってみたglashfish実装のpayaramicroで動くように変更してみました。
 まず、普通にweblogicで動かしていたwarをpayaraにデプロイすると下記エラーで失敗します・・

[[FATAL] No injection source found for a parameter of type public javax.ws.rs.core.Response

 正直あまり意識してなかったのですが、、やっぱりweblogicになると内包しているライブラリに依存しやすく、ちゃんと内部を理解していないと何が作用して動作しているかが解りにくいです。。
 エラーを見る限り、jax-rs関連でエラーになっている模様。
いろいろライブラリを置き換えていくと、既にManagedBeanが非推奨になっているようだ。。
結果pom.xmlは下記になり

@ManagedBeanを@Namedに置き換え、SessionScopedをjavax.enterprise.contextに変えたりして、ようやくpayaraで動くようになりました。

 で、とりあえず動かすことはできたとは言え、今やWebアプリを積極的にJavaEEで実装する理由が少なくなっている気がします。Wabアプリならphpとかruby on Railsでの大規模サイトの構築事例がたくさんあります。速度的なアドバンテージも今となってはキャッシュにより殆ど感じられないですし、その実装対象のシステムに何等かの制約やレガシーな理由が絡んでないと敢えてJavaEEを選択する理由は何だろう?JavaEEがいまいち普及しないのは結局の所、各実装はベンダー次第となっている事が結果的にアプリケーションサーバの実装次第となってしまい、Javaとして最大の魅力である(と筆者は思っている)OSを超えた一貫性というものが、各APサーバが内包しているライブラリがバラバラである事により、消え失せてしまっているような気がします。

JavaMailで送信日時が取得できないケースがある

 メールを受信する業務用ツールをJavaMailで実装して、かれこれ一年近く普通に使っていたのですが、新しい要件に沿った機能を追加してテストしていたところ、メールの送信日時を取得
javax.mail.Message.getSentDate()
でnullを想定しておらずエラーに。。
これまで何も問題がなかったのですが、どうもメールによっては取れないケースがある模様。
メールをemlファイルにして中身をエディタで見たところ、

問題無いケース Date: Fri, 23 Jun 2017 11:23:10 +0900
問題有るケース Date: Tue Jun 6 12:31:19 2017

 で、試しにemlファイルのDateヘッダを問題無いケースに書き換えてやってみたところ、普通にJavaMailで送信日時が取れた、、という事は?

 細かくは調べてませんが、問題となるメールはDate書式がMIMEの仕様に沿っていないようです。
仕様に沿っていないのか、仕様が明確でないのか、もしかして、仕様が明確になる前からあるSMTPサーバなのか・・?

 OutLook等の一般的なメーラーで見る限り、普通に送信日時として表示されます。
こういう場合、メールを受信する側からすれば、細かい箇所が仕様に沿っていなくても、問題無く処理できるものを用意しなくてはなりません。
とはいえ、なかなか事前に想定するのは難しく、メールみたいに昔からあるものは特に注意しなければいけませんね。。

カテゴリー: Java

backlog4jで課題を登録してみる

今度は、backlogへの課題登録を自動化したいという話があり、前回のredmineと似通った要件なので再利用箇所も多く、redmine java api部分をヌーラボ公認のbacklog4jに置き換えて実装してみました。
redmineとの違いはそれなりにありますが、backlog4jの方はKeyとIDを混同しやすい感じがします。例えば、課題のKeyはURLを見ればすぐ解りますが、IDの方は内部的なユニークな数値です。これに注意しながらbacklog用に前回のラッパークラスを置き換えてみました。

上記を呼び出すのはこんな感じ

後日談
GetIssieで3ヶ月経って問題発生・・・たまに課題の取得に失敗するようになりました・・・
下記のようにしましたが、最初からそうしろ!って事ですね。でも、3ヶ月は何も問題なかったのですが・・
public Issue getIssue(String issueKey)

redmine java api でチケットを取得してみる

 前回の続きで、今度はチケットを取得してみます。単純にチケットIDを指定して一つのチケットを取得するのでは無く、条件を指定し一括してチケットを取得して何かをするような要件への対応です。

 下記サンプルでは、クエリーを使用せずに、
ステータスID=2 かつ トラッカーID=10または11 かつ 題名に「テスト」を含む
という条件に一致するチケットを全て取得する内容です。
100件ずつチケットを取得して、100件以上のチケットがあればページ番号を変えて全チケットを取得します。