ブログトップ 記事一覧 ログイン 無料ブログ開設

水まんじゅう

 

2017-06-01

Tomcat 8にantからリモートデプロイする

今更antかよ!というつっこみは置いておいて、
Webに残っているドキュメント類がことごとく古くて使用できなかったので更新メモとして。

1. antのフォルダにTomcatから以下のライブラリをコピーする。

2. Tomcatのconf/tomcat-users.xmlに以下の記載を追加(ユーザー名、パスワードは任意)

<user username="tomcat" password="tomcat" roles="manager-script" />

3. build.xmlに以下のtaskdefを追加

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project basedir="." default="build" name="ant_project">
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask"/>
・
・
・

4. あとはdeployタスクを実行

<deploy url="http://localhost:8080/manager/text"
        username="tomcat"
        password="tomcat"
        path="/${ant.project.name}"
        update="true"
        war="${basedir}/target/${ant.project.name}.war" />

username/passwordはtomcat-users.xmlで指定したもの。

antタスクの依存ライブラリが増えてた。

という感じで。

2017/06/01追記
最初はlocalWarというのが増えたとか書いてたけど、単純にTomcatバグを踏んでいただけだった・・・・・・
バージョンによっては上の設定だと動かないのでご注意を。
動かない場合は、以下のパッチで直ってますのでantの依存ライブラリだけ入れ替えてあげればおk。
https://github.com/apache/tomcat/commit/a64839abf2f4eafb48b738794588a4a99ece0320

2017-05-24

SpotBugs 3.1.0 RC2を使用してみた。

けっこうはまったのでメモとして。

SpotBugsはfindbugs本体の開発がアレな状態なのでフォークして生まれたツールです。
生まれてまだ日が浅いため、FindBugsとの互換性がけっこう高いです。
なので、FindBugsを使用していた人がSpotBugsに乗り換えられるようにということで、実行までのメモ。

バイナリとしての配布はされていない?ようなので、自前でビルドをする必要があります。
以下のサイトから落としてきてください。
https://github.com/spotbugs/spotbugs

ドキュメントには
gradlew buildを実行すれば普通に出来るよぐらいに書いてありますが、できません。
一部コマンドがgitに依存しているため、gitインストールしていない場合(Download ZIPダウンロードしてきた場合)はspotbugs/build.gradleの155行目〜159行目までをコメントアウトしてください。*1

task distSrcZip(type:Exec) {
  commandLine 'git', 'archive', '-o', "${buildDir}/distributions/spotbugs-${project.version}-source.zip",
    '--prefix', "spotbugs-${project.version}/", 'HEAD'
}
tasks['assembleDist'].finalizedBy distSrcZip

//task distSrcZip(type:Exec) {
//  commandLine 'git', 'archive', '-o', "${buildDir}/distributions/spotbugs-${project.version}-source.zip",
//    '--prefix', "spotbugs-${project.version}/", 'HEAD'
//}
//tasks['assembleDist'].finalizedBy distSrcZip

チケット切った。(修正済み)
https://github.com/spotbugs/spotbugs/issues/178

これでビルドが出来るはずです。

ビルド後にdestributionsに入ってるzipを使用するのが正しいです。
間違えてもlibsに入ってるjarを使用してみようとしてはいけない。(間違えた)

mavenの場合


以下のチケットに書いてあるとおり、maven-findbugs-pluginからfindbugsへの依存をSpotBugsに入れ替えてあげればよいらしい。
https://github.com/spotbugs/spotbugs/issues/8

なお、この場合は上記の自前でのコンパイルは不要。
ちゃんと最新版の3.1.0-RC2もmaven centralにリリースされています。


antの場合


antから使用する場合、たぶん、SpotBugsのantプラグインを利用すればいい(はずな)のですが、findbugsからのお試しということで、findbugsantプラグインを使用できるように以下のように修正します。

これだけで、SpotBugsが呼び出せるはずです。

使用した感想


判っていたことですが、本家findbugsパッチを取り込んでくれなくなってしまったため立ち上がったプロジェクトで本家findbugsが正常に稼動しなおしたらこのプロジェクトも終了するって公言されていることもあり、互換性が保たれたままfindbugsのように使用できる、ただしそれを超えたものを期待するものでもないのかなぁという印象です。
環境周りは本家ツールに対してやはりつらい。。。。。。
cccでのセッションでは20%以上の高速化が測られているとのことでしたので、現状としてはそれがどうしても欲しい人向けではないでしょうか。

---
2017/05/26 追記

自分の環境(Windows Server 2012 R2 + JDK8u102)ではspotbugsを使用すると逆に20%ほど動作が伸びてしまう結果となりました。
セッション内では高速化が図られているとの話でしたが、環境によるのかもしれません。ご注意ください。

*1:現在はすでに修正されています。

2017-05-14

セキュリティさくら分科会(仮称)第二回に参加してきた

なにやってたかの全容は以下の記事を参照。
http://calmery.hatenablog.com/entry/2017/05/14/221218

Docker上でStruts2アプリケーションを動かしたけど、そもそもTomcatってなによ?ってところの説明が抜けていたので、後ろのほうのメンバーは完全に?????という状態になっていた(ので軽く追加説明はした)
せっかくStruts2のサンプルアプリケーションのwarをデプロイしたのにアプリケーションの画面を見ることなく、Tomcatにアクセスだけして終わってたし・・・・・・
ちなみに、dockerアプリケーションを動かした時点で、以下のURLにアクセスするとデプロイしたStruts2アプリケーションに繋がります。
http://localhost:8080/struts2-rest-showcase

ただ、初回起動時に、勝手にセッションidがURLについてしまってエラーになるので、2回上のURLにアクセスする必要がある模様。*1

なお、この名前はDockerfileで読み込んでるwarファイルの名前と一緒になります。

私は裏側でTomcatの設定ファイルをDockerfileから追加してTomcatの挙動が変わるよたのしー。とかやってたけど、今回の感じだとStruts2アプリケーション脆弱性で壊してみてもDockerを起動しなおすと元に戻っちゃうよみたいなところをやったほうが判りやすかったんじゃないだろうか?とは思った。

ただ、Dockerやらないとなぁと思いつつやれてなかったのを一歩踏み出せたというのはありがたい。

*1:これはたぶんサンプルアプリケーションバグ。直さないけど

2017-05-07

Javaの文字列変換にどれくらい時間がかかってるかを測定してみる

前回の記事で、文字として扱わない状態で改行コードで分割することでパラレル実行向けに最適化されたようだと書きましたが、そういえば、Javaのバイトからの文字列への変換にどれくらいコストがかかるのかを調べたことがなかったので、調べました。

なお、環境は前回と同じ。
PCとしては5年ぐらい前のCore i7だけどHDDWindows 10のものを使用しています。
使用したJavaバイナリjdk-9-ea+167_windows-x64_bin.exeです。

ということで、以下の3通りで調べました。


//文字列として扱う
BufferedReader reader = Files.newBufferedReader(Paths.get(fileName));
while (reader.readLine() != null) {
}

//InputStreamで読む
InputStream inputStream = Files.newInputStream(Paths.get(fileName));
byte[] buff = new byte[8192];
while (inputStream.read(buff) == 8192) {
}

//InputStreamで読んだ後に文字列に自前で変換する。(参考用)
InputStream inputStream = Files.newInputStream(Paths.get(fileName));
byte[] buff = new byte[8192];
while (inputStream.read(buff) == 8192) {
    new String(buff);
}

ちなみに、InputStreamで使用されているバッファサイズ(8192)はBufferedReaderを作成する場合にデフォルトで使用されるバッファサイズです。
BufferedReaderとバッファサイズを併せました。
参考用は前回作成したテストデータだとBufferedReaderのほうがループ回数が極端に多くなってしまうため、ループ回数の差がどれくらいの影響を与えてるのかを見るための参考用のデータです。BufferedReaderの中ではバッファの中からさらに1行ごとに文字列を生成されているはず。

結果

  • BufferedReader->7.2秒ぐらい
  • InputStream->0.9秒ぐらい
  • InputStreamで自前で文字列化->5秒ぐらい

やはりそれなりに時間がかかるんだなぁという印象でした。

使ったコードはこちら
https://gist.github.com/megascus/04cab0e0c90d640c2e575e69c77546c4

2017-05-04

Java 9でjava.nio.Files#linesがパラレル実行向けに最適化されたと聞いたので試してみた

さくらばさんから以下のような話を聞いたので。



なお、対応するチケットは以下となっています。

(fs) Files.lines needs a better splitting implementation for stream source

JavaDocは以下の通り。
java.nio.Files#lines

ドキュメント上は文字コードUTF-8, US-ASCII and ISO-8859-1の場合のみサポートとなっていますが、実装を調べたところ、さらにファイルサイズが2Gを超えてもいけないようです。
中身のファイルのFileChannelをByteBufferクラスにダイレクトにリンクをし扱うようにしているためで、Java配列が2Gまでしか取り扱えないことによる制限のようです。

なお、高速化の方法ですが、従来はファイルの中身のバイト文字列を文字として変換してから1行を取り出していたのを、バイト文字列のまま1行取り出してから文字に変換するようにしたという感じのようです。
中身をbyteのまま\rとか\nと比べてるので、\rと\nがバイト単位で比べた時にほかの文字としても使われている文字コード(例えばUTF-16)の場合に不具合になってしまうため、確実に問題がない文字コードのみをサポートしている模様。
JavaDocを読む限りFileChannel自体は非同期からのアクセスに最適化されていますが、それを文字列の行として取り扱うBufferdReaderクラスは非同期からのアクセスに最適化されていませんでした。それを取り除くことで、非同期処理の恩恵を受けやすくしたみたいです。

ということで試してみた。


2Gbyteを基準に従来のコードと新しい処理が分かれるため、2Gを基準にシングススレッド、マルチスレッドでどれくらい処理時間に差が出るのかを雑に試してみました。

PCとしては5年ぐらい前のCore i7だけどHDDWindows 10のものを使用しています。
使用したJavaバイナリjdk-9-ea+167_windows-x64_bin.exeです。

使ったコードはこんな感じ。filterを入れてるのは、JITによって処理消えちゃわないよね?ってあたりが心配になっただけで、あまり意味はないです。

//マルチスレッド
Files.lines(Paths.get(fileName), UTF_8).parallel().filter(s -> true).forEach(s -> {return;});
//シングルスレッド
Files.lines(Paths.get(fileName), UTF_8).filter(s -> true).forEach(s -> {return;});

大体以下のような感じ。

  • マルチスレッドで最適化されている(2G以下) -> 2.6秒程度
  • マルチスレッドで最適化されていない(2G超) -> 9秒程度
  • シングルスレッドで最適化されている(2G以下) -> 6.6秒程度
  • シングルスレッドで最適化されていない(2G超) -> 7.1秒程度*1

ということで、ものすごく最適化されているという結果でした。

使ったコードはこちら。
https://gist.github.com/megascus/5bdf8d7d45b06cb639c40ce67d1533a3

コード解説(5/7追記)

もともとのlinesメソッドの中身はFiles#newBufferedReaderを呼び出してBufferedReaderを作成した後にBufferedReader#linesを呼び出しているだけでした。
そこからUTF-8かUS-ASCIIもしくはISO-8859-1であった場合に、新しい処理が呼び出されるようになりました。

(元のコード)
64行目と、110行目〜122行目まで
http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/4472aa4d4ae9#l2.64
http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/4472aa4d4ae9#l2.110

(新しい処理の条件、ファイルシステムがシステムのデフォルトである場合、かつ文字コードが上記の場合のみ)
http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/4472aa4d4ae9#l2.71
(使用できる文字コード自体は下記で定義)
http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/4472aa4d4ae9#l1.71
※つまりZipFileSystem等の場合はこの処理には入らないので、一度ローカルファイルに直す必要がある。

新しい処理の中で、FileChannelを開いた後にファイルサイズを確認し、2Gを超えていた場合は元の処理に戻しています。
http://hg.openjdk.java.net/jdk9/dev/jdk/rev/4472aa4d4ae9#l2.90

実際の処理はFileChannelLinesSpliteratorの中で行っていますが、
ByteBufferとして扱う場所は以下でやっています。
http://hg.openjdk.java.net/jdk9/dev/jdk/rev/4472aa4d4ae9#l1.203
http://hg.openjdk.java.net/jdk9/dev/jdk/rev/4472aa4d4ae9#l1.180

並列処理として扱うときに部分分割するメソッドとして、Spliterator#trySpritが用意されていますが、
そのなかでバイト文字列のまま分割するようになりました。(210行目〜)
http://hg.openjdk.java.net/jdk9/dev/jdk/rev/4472aa4d4ae9#l1.210

分割された後はByteBufferをソースにしたBufferedReaderを作成して処理をしているようです。
http://hg.openjdk.java.net/jdk9/dev/jdk/rev/4472aa4d4ae9#l1.121

BufferedReaderができてしまえばあとは変わりません。

trySpritの処理まではシングスルレッドで行うしかなかないので、そこでのバイトから文字列への変換をせずに、マルチスレッドで処理をする時に初めて文字列へ変換することで、処理を分散しているということでしょうか。

文字列変換にどれくらいのコストがかかっているかは次の記事で。
http://d.hatena.ne.jp/megascus/20170507/1494171105

*1:途中でなんかへんな処理が入ったかブレが大きかった