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

curlでパラメータを指定してWebページ上のファイルを取得してみる

 とあるWebサイトからファイルをダウンロードする作業を自動化したいという話があり、Linux上で動作するものという要件でしたので
とりあえずcurlでコマンドを作ってみました。対象サイトの認証はログイン画面からIDとパスワードを入力するという一般的な内容ですが、画面以外の対象サイトにおける認証の仕様については、何も情報が無くどうやらWebアプリ独自仕様のようです。
 ログイン画面でどんなやり取りがされているかと、FireFoxの開発ツールで確認し、POSTするパラメータを指定するコマンドを作成していきます。クッキーも指定してやってみますが、まだダメ。。

 今度はhttpヘッダの情報も開発ツールで確認しながら、必要そうなヘッダもcrulコマンドに指定すると、うまく動作しました。下記のような感じです。

ログイン画面

curl -X POST -L -b my.cookie -c my.cookie --header "Content-Type: application/x-www-form-urlencoded" -d "log=****&pwd=****" "https://targethost/login"

ダウンロードするファイルのGET

curl -L -b my.cookie -c my.cookie -o getout --header "Referer:https://targethost/aaaaa" https://targethost/files/targetfile.zip

 出来る出来ないはWebサイト側の作り次第かもしれませんが、とりあえずcurlでアクセスする事が出来そうです。
 とは言え、対象サイトの認証パスワードは定期更新が必要だし、他にもいろいろ対処が必要なので別の手段も考えた方がいいかもしれません。。。

backlog4jで課題を登録してみる

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

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

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

カテゴリー: Java

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

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

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

カテゴリー: Java

redmine java api でチケットを登録してみる

 redmineへのチケット登録を自動化したいという話があり、内容的には常時トリガーを拾って登録という感じだったので、javaの常駐プロセスでトリガーを拾う事を前提として、こちらを使用して実装してみました。
https://github.com/taskadapter/redmine-java-api
なお、登録対象のredmineは2.5系でしたが、それほどバージョンを意識しなくても大丈夫そうです。

事前準備として、対象のredmineへapi登録用のユーザーを用意し、登録対象のプロジェクトへの権限を付与、個人設定画面からapiキーを取得します。

その他、プロジェクトidやトラッカーid等のredmine内部で持っているidの値はブラウザからapiで下記のようにidの値を確認してセットとなります。ただ、redmine java apiには、様々なマネージャーがあるので、id指定でなくても都度問い合わせしてidを取得できそうです。

ソース的には下記のようなラッパークラスを作って、登録や更新を呼び出し側で簡略化出来るようにしてます。

下記の呼び出し側では、親チケットを作成してジャーナルを追加、子チケットを2つ作成し関連付けてます。

いろいろやってみましたが、redmineに対して殆どの事は出来そうです。

カテゴリー: Java

JAX-RSでWeb画面にドラッグアンドドロップされたファイルを読み込む

テキストファイルをサーバへアップロードしたいという要件があり、Web画面からファイルをドラッグアンドドロップできるようにして、それをJAX-RSで処理する事にしてみました。

下記の参考にさせて頂き、それらを組合わせただけと言えばだけですが。。

Jersey(JAX-RS)でファイルアップロード
HTML5 の File API でドラッグ&ドロップする

まず、JAX-RSのルートパスを指定します

次にファイルを処理するJAX-RS部分を作ります

これでファイルアップローダーのJAX-RSパスは、{コンテンツルート}/api/uploadになりました。

続いてJavaScriptの部分(殆ど上記参考から持ってきただけです。。)

最後にHTML部分のドラッグアンドドロップ部分です。
ここにドロップされたファイルがJAX-RSの処理箇所のInputStream に繋がります。

これでとりあえずはJAX-RSでファイル処理が出来ることが確認出来ました。

最後にPOM。

bootstrap2 datetimepickerを1分単位にする

ちょっと前に作ったJavaEEアプリでbootstrap2のdatatimepickerを使ったのだが、
http://esoro.jp/?p=698
5分刻みは使いにくいという話が出てきて、ググッてみたところ下記を発見。
http://www.malot.fr/bootstrap-datetimepicker/
オプションのminuteStepで値を変えられるみたいなのでやってみました。
1分刻みにするとこうなります。

xhtml側は下記minuteStepのオプションを追加するだけ

デフォルトだと5分刻みなのでこんな感じ

ダイアログが長くなって画面ギリギリだけど、この方が便利ということでこちらでリリース
次作るものはbootstrap3にしないとなあ。。

OracleDBで文字化け発生

今構築中のPHP+apache+OracleDB構成のWeb業務アプリですが、
テスト環境を作ってもらったので動かしてみたところ、全角文字が??に化けて困ったことに。。

アプリケーションの要件上、2つのOracleDB(どちらも12c)に同時接続する必要があり、
一方の文字コードが
NLS_LANG=Japanese_Japan.JA16SJISTILDE で、
もう一方が、
NLS_LANG=Japanese_Japan.AL32UTF8 となっています。

とは言え、DBクライアントとなるphp側としては、Japanese_Japan.AL32UTF8でいいはず。

apacheのhttpd.confを見ると、
setEnv NLS_LANG Japanese_Japan.AL32UTF8
としているのですが、どうも有効にならない模様。

結果、apacheの起動シェルに
export NLS_LANG=Japanese_Japan.AL32UTF8
を入れてもらって解消。
念の為、はしご高とかを画面から入れてみましたが問題無し。

昔(Oracle8とか)は第2水準以上の漢字はトラブルの元だったとの記憶がありますが、今は便利になりましたね。

POIでエクセルファイルを読むにはStreamingReaderが必須っPOI

エクセルファイル内のシートをDBに取り込む要件があり、DBサーバであるlinux上で取り込む処理をする必要が出てきました。
とりあえずCSVにでも変換してしまえば後はどうにでもなるので、Apache POIを使って汎用で使えそうな簡易CSV変換ツールを作ってみました。簡単にテストした後、いざ対象となるエクセルファイルで実行してみたところいつまでたっても終わらないし、メモリを数G食うという異常な状態に。
 テストした時のエクセルは数千程度で、いざ実行したエクセルは数万規模の違いがあり、大量データが含まれるエクセルシートの場合にPOIでは問題があるみたいです。で、こりゃ何とかしないとと調べていくと、それを解消してくれる素晴らしいライブラリを発見。
https://github.com/monitorjbl/excel-streaming-reader

上記サイトのREADMEに従い、下記のようにWorkbookFactory.createの箇所を置き換えるのみでした。(inputはエクセルファイルのInputStream)

変えた後は何事もなかったかのようにちゃんと終わりました。
それにしてもPOIは非推奨メソッドをいつ置き換えてくれるのだろう?

カテゴリー: Java

EJBタイマーサービスを使ってみることにした

WebLogicを動かしているサーバ上で数分間隔にとある機能を動作させたいという話があり、常駐プロセス起動とかにしたくないので、EJBタイマーサービスを使ってみることにしました。
タイマーサービスの使用は初めてなので、WebLogic12Cの挙動を調べながらの作りこみです。

結果的に下記のようになりました。
5分間隔(0分2秒,5分2秒,10分2秒,15分2秒・・・)でタイマー処理を実行しています。
なお、トランザクション処理は必要無し、処理結果のエラーハンドリングも不要、指定の間隔で単純に起動されればよいような機能です。

WebLogicにおけるEJBタイマーの挙動として確認できたのは、

①タイムアウトを実装しておかないと30秒以上経つとエラーを吐いて、次のタイマーまでは動くのだが、その次以降はまったく動かなくなる

Caused By: weblogic.transaction.internal.TimedOutException: Transaction timed out after 30 seconds
中略・・
Truncated. see log file for complete stacktrace

なぜ30秒なのかというとトランザクションタイムアウト値がそうなっているっぽい。。
https://docs.oracle.com/cd/E28613_01/web.1211/b65951/ejb_jar_ref.htm#i1506703

何をするわけでもないが、とりあえず@Timeoutのアノテーションをつけたメソッドを追加

②処理時間が次回のタイマー処理開始を超えるとタイマーが止まる

例えば、5分間隔で動作させる場合、処理が5分以上掛かってしまうと、次のタイマー実行時にエラーが吐かれて動かない

<2016/11/15 16時55分07秒 JST> <Error> <EJB> <BEA-011088> <The following error occurred while invoking the ejbTimeout(javax.ejb.Timer) method of EJB ChkTimer(Application: appmgr, EJBComponent: /appmgr).
以下、略・・

なので、処理自体をExecutorServiceでスレッド処理にし、処理遅延が起きないようにしてみました

③永続化はしない方がよい

とりあえずサーバ上で空回りするようタイマー稼動テストを数日間したところ、ちゃんと指定間隔通りに動作していたので、機能を概ね実装していざデプロイしてみると、2重起動している状態に、、
なぜかといろいろ調べた結果、空回り開始時にpersistent=falseを指定していなかったので、ずっと残り続けていた模様。
一度、タイマー起動を無しにしたモジュールでデプロイした後、再度タイマーを有効にしてデプロイすることで、やっと2重起動が止まりました。

実行したいスケジュールや処理内容によっては、結構制御が難しいように思いますが、今回の要件ならOKという事で。