日曜日, 3月 31, 2013

Commons Lang StringUtils

使うべし

Apache Commons Langの一部であるStringUtilsは極めて便利である.もしこれを存じ上げないのならば,すぐにでも実践投入することをお勧めする.

このユーティリティ,一言でいうならばString関連のニッチを上手く捉えた優れものだ.
例えばisEmptyメソッド.
今までは以下のようなコードがあったと考えられる.


if( str != null && !str.equals( "" )) {
...
}


このコードを見るたびにユニットテストのカバレージを維持する為の儀式的作業またはそれを放棄した場合でのカバレージの低下が頭に浮かんで,気分は萎えるばかりだ.

しかし,StringUtilsのisEmptyメソッドを使えば以下の通り,そのような悩みはもう無用!


if( !StringUtils.isEmpty( str ) ) {
...
}

別のニッチな例としてsplitメソッドがあげられるだろう.これを使えば今までStringTokenizerを使って書いていたコードがスッキリする.joinメソッドも合わせて使えばコードの読みやすさはgroovyのそれに近くなってくる.

サンプルコード

以下のコードではStringUtilsの便利なメソッドを色々と試してみている.
defaultIfEmpty() .. もし文字列が空ならデフォルトバリューとして指示した文字列を返す.プロパティファイルから値を取得した場合値がなかったらデフォルトバリューに変換,とかのケースで使えるだろう.
join() .. スクリプト言語でおなじみ.Iterableの値を区切り文字で区切って連結した文字列を返す
split() .. 指定した区切り文字で与えられた文字列をトークナイズする.返り値は配列.
abbreviate() .. 文字列を指定した文字列長の長さで省略記号(...)を使って省略する.
isEmpty() .. 文字列が null または 空文字 ( "" ) ならばtrue. さもなければfalse.



package test.java;

import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;

import java.util.ArrayList;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

public class StringUtilTest {

@Test
public void testStringUtils() {
// in real use case we may want to do the static import for StringUtils
// try defaultIfEmpty(). Remember first param of assertThat is actual and 2nd expected
assertThat( StringUtils.defaultIfEmpty( "Banana", "N/A" ), is( "Banana") );
assertThat( StringUtils.defaultIfEmpty( "", "N/A" ), is( "N/A" ) );

// try split. It can be an alternative of string tokenizer
for( String fruit: StringUtils.split( "Banana|Orange|Apple", '|' ) ) {
assertThat( fruit, anyOf( containsString( "Banana" ),containsString( "Orange" ), containsString( "Apple" ) ) );
}

// try join
assertThat( StringUtils.join( new ArrayList<String>(){{add("Banana");add("Orange");add("Apple");}}, "," ),  is( "Banana,Orange,Apple" ) );

// try abbreviate
// + 3 means the length of elipses ( "..." )
assertThat( StringUtils.abbreviate( "Tanu Tanuki", "Tanu Tanuki".indexOf( " " ) + 3 ), is( "Tanu..." ) );

// try isEmpty
assertThat( StringUtils.isEmpty( "Notempty."), is( false ) );
assertThat( StringUtils.isEmpty( "" ), is( true ) );
assertThat( StringUtils.isEmpty( null ), is( true ) );

}
}


他にも便利なメソッドが

以上で見てきたように,StringUtilsを一度知れば,これが非常に強力なツールの一つになることは想像できると思う.
上記以外でも大変便利なメソッドがたくさんあるので是非JavaDocを眺めてほしい.




金曜日, 3月 29, 2013

Java ロギングのこと .. log4j-over-slf4j.jar slf4j-log4j12.jar

ロガー移行

Javaには優れたロガーが多い.slf4j, logback 等々.
現時点ではおそらくlogbackならびにslf4jの評価が高く,実際私のところでもアプリケーションデベロッパ達は徐々にlog4jからそれらに移行しつつある.

問題発生!

そんな中私が早晩おこるであろうと思っていたことが案の定(笑)発生した.
log4j-over-slf4jとslf4j-log4j12のクラスパス上での衝突である.

上記2ライブラリはブリッジデザインでのslf4j-api具現パッケージであり,双方をクラスパスに載せると相互参照永久ループが発生しアプリケーションはクラッシュする.

もし以下の点がたくさん思い当たるならば,より問題がおこる確率は高くなる.

  1. プロジェクトやmulti-module mavenプロジェクトがある程度以上の規模である
  2. 相互独立した開発チームが同一のmulti-module mavenプロジェクトを開発している
  3. 未熟にデザインされたライブラリの依存性を持っている
  4. slf4jフレームワークに習熟した人が少ない
  5. 各プロジェクトはロガーを好きに選ぶことができる
  6. library dependenciesの管理が徹底されていない

手前のケースだとまさに上に述べたとおりで,あるプロジェクトがlog4jからslf4jへの移行過渡期にlog4j-over-slf4jへのdependencyを,また別のプロジェクトがlog4j-slf4jを採用した結果あるアプリケーションがクラッシュする羽目に陥った.

解決策。。


この問題を解決するのは決して難しくはない.slf4j-log4j12またはlog4j-over-slf4jのどちらかをdependencyから外せばよい.そして残したほうのスコープをruntimeにする.どちらを外すか?パフォーマンスの理由からlog4j-over-slf4jを外すのがいいだろう.slf4jの公式ホームページからパフォーマンス分析結果を見ることができる.

以下引用
NOTE ON PERFORMANCE Contrary to other bridging modules, namely jcl-over-slf4j and log4j-over-slf4j, which reimplement JCL and respectively log4j, the jul-to-slf4j module does not reimplement the java.util.logging because packages under the java.* namespace cannot be replaced. Instead, jul-to-slf4j translates LogRecord objects into their SLF4J equivalent. Please note this translation process incurs the cost of constructing a LogRecordinstance regardless of whether the SLF4J logger is disabled for the given level or nor. Consequently, j.u.l. to SLF4J translation can seriously increase the cost of disabled logging statements (60 fold or 6000%) and measurably impact the performance of enabled log statements (20% overall increase). As of logback version 0.9.25, it is possible to completely eliminate the 60 fold translation overhead for disabled log statements with the help of LevelChangePropagator.

これはいけない!さっさとけしちゃいましょう  エイヤッ 

以上問題解決.あとはロガー使用を中央管理する体制を強化するなりデベロッパをトレーニングするなりして適宜問題の芽をつむいでおきましょう.