月曜日, 4月 29, 2013

Commons IO DateUtils

Yoda Timeの影に隠れて

Commons IOの中に,探してみればあった, DateUtils.
決して悪くはないが,JodaTimeがある以上無理して使うまでもないだろう..とは思うが,とりあえず私たちのツールボックスの内に加えておいて損はない,ということでご紹介.

とりあえず便利

DateUtilsのJavaDocはこちらのとおり.
とりあえず加減算可能なset シリーズ ( setDays(), setYears() etc. ), 面倒なCalendarのインスタンスを省略できるtoCalendar(), 指定したフィールド以下を0にセットするtruncate()あたりが一番役立つと考えられる.iterator()もカレンダーアプリ関連で役立つかもしれない.

サンプルコード

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;

import org.apache.commons.lang3.time.DateUtils;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.*;

public class DateUtilTest {

 private Date date;
 
 @Before
 public void init() {

  Calendar cal = Calendar.getInstance();
  cal.set( Calendar.YEAR, 1980 );
  cal.set( Calendar.MONTH, 0 );
  cal.set( Calendar.DAY_OF_MONTH, 2 );
  cal.set( Calendar.HOUR_OF_DAY,  5 );
  cal.set( Calendar.MINUTE,  20 );
  cal.set( Calendar.SECOND,  30 );
  cal.set( Calendar.MILLISECOND,  790 );
  date = cal.getTime();
  
 }
 
 @Test
 public void testAddAndSubtract() {

  Calendar cal = Calendar.getInstance();
  cal.setTime( DateUtils.addDays( date, 4 ) );
  assertThat( cal.get( Calendar.DAY_OF_MONTH ), is( 6 ) );
  cal = Calendar.getInstance();
  cal.setTime(  DateUtils.addHours( date, 5 ) );
  assertThat( cal.get( Calendar.HOUR_OF_DAY ), is( 10 ) );
  cal = DateUtils.toCalendar( DateUtils.addMilliseconds( date, 25 ) );
  assertThat( cal.get( Calendar.MILLISECOND ), is( 815 ) );
  // subtract
  cal = DateUtils.toCalendar( DateUtils.addYears( date, -10 ) );
  assertThat( cal.get( Calendar.YEAR ), is( 1970 ) );
  
  // truncate
  assertThat( DateUtils.truncate( date, Calendar.DAY_OF_MONTH ).toString(), is( "Wed Jan 02 00:00:00 EST 1980" ) ); 
 }
 
 @Test
 public void testIterator() {
  
  Iterator<Calendar> iter = DateUtils.iterator( date, DateUtils.RANGE_MONTH_SUNDAY );
  int i = 0;
  SimpleDateFormat sdf = new SimpleDateFormat( "yyyy/MM/dd" );
  while( iter.hasNext() ) {
   if( ++i == 10 ) break;
   System.out.print( sdf.format(iter.next().getTime()) + " ");
  }
 }

}




日曜日, 4月 28, 2013

C# - testboxを末尾まで自動スクロール

AppendText関数を使うべし

TextBoxで += を使った場合は単純にTextBoxのテキストプロパティに文字列を追加するだけで,テキストボックス自体へのアクションは何も起こらない.
一方AppendText関数を使った場合は文字列末へ自動スクロールしてくれる.


火曜日, 4月 16, 2013

Algorithm 問題を解く4 - 長さ不明の配列のサーチ

問題

Suppose you do not know the length of A in advance; accessing A[i] for i beyond the end of the array throws an exception.
Find the index of the first occurrence in A of a specified key k; return -1 if k does not appear in A.
(出典: Algorithms for interviews )

ヒント

終点がわからなければ探すしかない.終点がわかってからサーチ.ひと手間増えただけのこと.
問題はソート済みかどうかは言っていないがとりあえずソート済みということにしておこう.



まずは終点,つまり配列の終点を探す.ここで以下に効率よく絞り込みを行えるかが鍵となる.
安全に,手軽に行くのならば線形検索でよいが,O(N)のオーダであるので配列が小さい時にのみにしか適用すべきではない.
コードは以下の通り.


#include <cstdio>
#include <cstdlib>

int arrVal[] = {1,6,21,25,28,28,31,45,51,53,62,64,85};

int search_linear(int val) {

 int st = 0;
 int mid;
 int end = 0;
 int t;
 while(true) {

  // simulate out of index exception
  try {
   if( end >= (sizeof(arrVal) / sizeof(int)) )
    throw "out of index access";
   t = arrVal[end];
   printf("t=%d,end=%d,val=%d,idx=%d\n",t,end,val,end);
   if( t == val )
    return end;
   else if( t > val )
    break;

  } catch( char* str ) {
   printf("exception:%s.\n", str );
   break;
  }
  end++;
 }

 // now we know the end index of the "unbounded array.". Do the bsearch!
 while( st <= end ) {
  mid = (st + end) / 2;
  printf("st=%d,end=%d,mid=%d,val=%d,midval=%d\n",st,end,mid,val,arrVal[mid]);
  if( arrVal[mid] == val)
   return mid;
  else if( arrVal[mid] > val )
   end = mid - 1;
  else
   st = mid + 1;
 }
 return -1;

}


int main() {

 printf("%d ? %d\n",64,search(64));
 printf("%d ? %d\n",25,search(25));
 printf("%d ? %d\n",6,search(6));
 printf("%d ? %d\n",19,search(19));
 printf("%d ? %d\n",53,search(53));
 printf("%d ? %d\n",85,search(85));
 return EXIT_SUCCESS;
}

データが大きくなった場合,どうするか?上のコードでは計算量が多くて話にならないので,
なんとかlog2Nのオーダで絞り込むことを考える.
例外が投げられるか,又はA[2^k-1]の値が検索対象の値より大きくなるかのどちらかを満たすまで配列の終点と推定できるインデックスを探す.そしてその配列の終点と仮定した点2^k-1と2^kの間でバイナリサーチをすれば,log2Nのオーダの計算量で収まる.


日曜日, 4月 14, 2013

Algorithm 問題を解く3 - バイナリサーチ(2分探索) その弐

問題

SEARCH A SORTED ARRAY FOR A[i] = i

Suppose that in addition to being sorted, the entries of A are distinct integers. Design an efficient algorithm for finding an index i such that A[i] = i or indicating that no such index exists.

( 出典: algorithms for interviews )

ヒント

sorted arrayときたらまずとりあえず考えるのは? 
distinct integers, つまり重複無しのソート済み配列である.

答え

重複無しのソート済み配列とのこと.昇順であると仮定すれば以下が成り立つ

S[i] > S[i-1]

私の解答は以下の通り.
ロジックとはしては調べたいインデックスを2分木で絞り込む.調べたい値targetと2分木の中央要素v.at(mid)が等価ならばそのインデックスを返し,2分木探索終了時まで見つからなければ問題の指示通り-1を返す.


#include <iostream>
#include <vector>
#include <utility>
using namespace std;

#define INTARRAY_MAX 10

int search(vector<pair<int,int>> v, int target) {
 
 int st = 0;
 int end = v.size() - 1;
 int mid;
 while( st < end ) {
  
  mid = (st + end) / 2;
  cout << st << ',' << end << ',' << mid << ',' << v.at(mid).second << endl;
  if( v.at(mid).second == target )
   return mid;
  else if( v.at(mid).second > target )
   end = mid - 1;
  else
   st = mid + 1;
 }
   
 return -1;
}

int main() {
 vector<pair<int,int>> v;
 int arrVal[] = { 0,1,3,15,18,26,38,45,50,62};
 for( int i = 0;i < INTARRAY_MAX;i++ )
  v.push_back(make_pair(i,  arrVal[i] ));

 cout << "find? " << search(v, 1) << endl;
 cout << "find? " << search(v, 5) << endl;

 return EXIT_SUCCESS;
}


プログラムを走らせると以下のようなアウトプットを得る.
0,9,4,18
0,3,1,1
find? 1
0,9,4,18
0,3,1,1
2,3,2,3
find? -1
O(log2N)の平均又は最悪計算量で終えることできるのでデータ量が多くても大丈夫.(最もここではソートのコストは考慮に入れていない.)
ちなみにutilityヘッダはSTLのpairを使用する為に必要.

アルゴリズム基本編 1 - ランダムな整数列の生成

ランダムで要素の重複があってもよい整数列

ランダムな整数列をつくりなさい,要素の重複はあってもいいですよ,といわれた場合,あなたはどうするか.
当然ながら最も簡単な答えは整数列に疑似乱数を生成して割り当てればよい.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define INTARRAY_MAX 10

int* gen_rand_intarray(int* arr, int size) {
 
 for(int i = 0;i < size;i++){
  *arr = rand() % size;
  printf("%d\n",*arr);
  arr++;
 }
 return arr;

}

int main() {

 srand(time(NULL));
 int* arr = (int*)malloc(INTARRAY_MAX*sizeof(int));
 gen_rand_intarray(arr,INTARRAY_MAX);
 for( int i = 0;i < INTARRAY_MAX;i++ ) {
  printf("%i - %d[@%d]\n",i,*arr,arr);
  arr++;
 }
 return EXIT_SUCCESS;

}

では要素の重複はゆるさないよ,となったら?リストからセットにデータを移し替えればおのずと重複要素は消えるが,整数列長は縮む可能性があるので解として不適な可能性がある.
やはりここは定石の配列のインデックスを乱数生成しシャッフル,だろう.乱数値の生成とシャッフル合わせてO(2N).もっと効率的な方法はありそうだが..


#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define INTARRAY_MAX 10

int main() {

 srand(time(NULL));
 int r,t;

 int arr2[INTARRAY_MAX];
 for(int i = 0;i < sizeof(arr2)/sizeof(int);i++)
  arr2[i] = i;

 for(int i = 0;i < INTARRAY_MAX;i++) {
  r = rand() % INTARRAY_MAX;
  t = arr2[r];
  arr2[r] = arr2[i];
  arr2[i] = t;
 }

 for(int i = 0;i < INTARRAY_MAX;i++)
  printf("%d ", arr2[i]);

 char ch;
 scanf(&ch);
 return EXIT_SUCCESS;

}

ちなみにSTLのrandom_shuffleを使った場合の解は以下の通り.


#include <iostream>
#include <vector>
#include <algorithm>

#define INTARRAY_MAX 10

int main() {
 std::vector<int> v;
 for( int i = 0;i < INTARRAY_MAX;i++ )
  v.push_back(i);
 std::random_shuffle(v.begin(),v.end());
 for(std::vector<int>::iterator it = v.begin();it != v.end();++it)
  std::cout << *it << ' ';
 std::cout << std::endl;
 return EXIT_SUCCESS;
}

金曜日, 4月 12, 2013

blogにソースコードを埋め込む

最初は知らず..

このブログを始めた時,ブログにソースコードを綺麗に貼り付ける方法を知らなかった..ので,ベタベタと能も無く貼り付けていたのだが,色々なページを見ているうちに私もエディター風の綺麗なコードを埋め込みたい!と思って調べてみたら,実にわかりやすい解説をしてくださっているサイトを発見.このブログの紹介もこのSyntax Highlighterもvery good jobだね.

http://www.craftyfella.com/2010/01/syntax-highlighting-with-blogger-engine.html


水曜日, 4月 10, 2013

Commons Lang - EqualsBuilder & HashCodeBuilder

equals() と hashCode()

Javaでドメインモデル設計の際重要になってくるequals()とhashCode()のオーバーライド.
これを自分で実装するのは結構めんどうくさいし,コードの可読性も決して高くはないのが通常だろうと思う.そんな時にお勧めなのはやはりApache CommonsのEqualsBuilderとHashCodeBuilderだろう.

サンプル

以下のようなトレードクラスがあったとする.
このクラスの主キーフィールドはtradeIdとeventIdとしよう.
equals()とhashCode()ではそれぞれEqualsBuilderとHashCodeBuilderを使って複合主キーを比較している.この例では非主キーのフィールドは比較をしていない.

import java.util.Date;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Trade {
 
 private String tradeId;
 private String eventId;
 private Date settleDate;
 private Date tradeDate;
 private int counterPartyId;
 
 public Trade( String tradeId, String eventId, Date settleDate, Date tradeDate, int counterPartyId ) {
  this.tradeId = tradeId;
  this.eventId = eventId;
  this.settleDate = settleDate;
  this.tradeDate = tradeDate;
  this.counterPartyId = counterPartyId;
 }
 
 public boolean equals( Object obj ) {
  boolean result = false;
  if( obj instanceof Trade ) {
   Trade other = (Trade)obj;
   result = new EqualsBuilder().append( tradeId, other.getTradeId() )
     .append( eventId, other.eventId )
     .isEquals();
  }
  return result;
 }
 
 public int hashCode() {
  return new HashCodeBuilder().append( tradeId )
    .append( eventId ).toHashCode();
 }
 
 public String getTradeId() { return tradeId; }
 public String getEventId() { return eventId; }
 public Date getSettleDate() { return settleDate; }
 public Date getTradeDate() { return tradeDate; }
 public int getCounterPartyId() { return counterPartyId; }
}



それでもってこのTradeのequals()とhashCode()のテストクラスが以下の通り.
以下のテストで分かる通り,trd6は主キーが同じなのに非主キーフィールドは異なっているという異常ケースである.上記の実装ではこのような例外ケースには対応できていない.理想的には対応したほうがよい,と思うがとりあえずそれをするかどうかはプロジェクトの様々なvariablesを考慮した結果次第,だろうか.



import java.text.ParseException;
import java.text.SimpleDateFormat;

import org.junit.Test;
import static org.junit.Assert.*;

public class BuilderSampleTest {

 @Test
 public void testEqualsAndHashCode() throws ParseException {
  SimpleDateFormat sdf = new SimpleDateFormat( "yyyy/MM/dd" );
  Trade trd1 = new Trade( "TRD123", "21-123221", sdf.parse( "2012/03/04" ), sdf.parse( "2012/04/04" ), 565000 );
  Trade trd2 = new Trade( "TRD238", "21-123250", sdf.parse( "2012/03/05" ), sdf.parse( "2012/04/04" ), 565000 );
  Trade trd3 = new Trade( "TRD123", "21-123250", sdf.parse( "2012/03/05" ), sdf.parse( "2012/04/09" ), 565300 );
  Trade trd4 = new Trade( "TRD126", "21-123221", sdf.parse( "2012/03/04" ), sdf.parse( "2012/04/04" ), 565000 );
  Trade trd5 = new Trade( "TRD123", "21-123221", sdf.parse( "2012/03/04" ), sdf.parse( "2012/04/04" ), 565000 );
  Trade trd6 = new Trade( "TRD123", "21-123221", sdf.parse( "2012/05/04" ), sdf.parse( "2012/06/04" ), 620000 );
  
  // trd1 and trd2 - composite primary key different
  assertTrue( !trd1.equals( trd2 ) );
  // trd1 and trd3 - composite primary key different
  assertTrue( !trd1.equals( trd3 ) );
  // trd1 and trd4 - composite primary key different
  assertTrue( !trd1.equals( trd4 ) );
  // trd1 and trd5 - both composite primary key and non-key fields are same. Of course.
  assertTrue( trd1.equals( trd5 ) );
  // trd1 and trd6 - composite primary key is same but non-key fields are different - WHAT?!
  assertTrue( trd1.equals( trd6 ) );
  // trd1 and non-Trade object
  assertTrue( !trd1.equals( new String() ) );
 }
}

火曜日, 4月 09, 2013

Algorithm 問題を解く2 - バイナリサーチ(2分探索)

問題.


Write a method that takes a sorted array A of integers and a key k and
returns the index of first occurrence of k in A. Return -1 if k does not
appear in A. 
(出典: Algorithm for interviews )

これは,ただ単に2分検索.あまりに捻りがないので逆に疑って問題を読んでしまった.

解.


#include <stdio.h>
#include <stdlib.h>

static int intArray[] = {2,16,18,18,21,35,42,59,60,68,74,92};

int bsearch(int k) {

int st,end,mid;
st = 0;end = sizeof(intArray)/sizeof(int)-1;

while(st <= end) {
mid = (st+end)/2;
printf("st-%d,end-%d,mid-%d,k-%d,midVal=%d\n",st,end,mid,k,intArray[mid]);
if(intArray[mid] == k)
return mid;
else if(intArray[mid] < k)
st = mid + 1;
else
end = mid - 1;
}
return -1;
}

void show_result(int num, int result){
printf("%d:%d\n", num, result);
}

int main(int argc, char **argv) {
// not found
show_result(68,bsearch(68));
// found
show_result(74,bsearch(74));
char ch;
scanf(&ch);
return EXIT_SUCCESS;
}


特に捻りもなく,実に普通のbsearchである.
アウトプットは以下の通り.bsearchを書くといつも義経の八艘飛びを思い浮かべるのは私だけだろうか.

st-0,end-11,mid-5,k-68,midVal=35
st-6,end-11,mid-8,k-68,midVal=60
st-9,end-11,mid-10,k-68,midVal=74
st-9,end-9,mid-9,k-68,midVal=68
68:9
st-0,end-11,mid-5,k-74,midVal=35
st-6,end-11,mid-8,k-74,midVal=60
st-9,end-11,mid-10,k-74,midVal=74
74:10

平均オーダはO(log2 n).最悪でもO(log2 n).線形サーチはO(n)なのでソートするコスト(例えば n log n ) を合わせても十分見合うことがわかる.

Algorithm 問題を解く 1 - 平方根計算

問題

Implement a fast integer square root function 1 that takes
in a 32-bit unsigned integer and returns another 32-bit unsigned integer
that is the floor of the square root of the input.
There are many variants of searching a sorted array that require a little more thinking and create opportunities for missing corner cases. For the following problems, A is a sorted array of integers.
( 出典: Algorithms for interviews )

一言でいうと,一つの32ビットの符号なし整数を引数に取り,その平方根の切り下げた32ビット整数型の帰り値を返す,関数を実装せよ,所与としてソートされた整数の配列がある,ということだ.基本的な問題だが,解いていると楽しいもの.



高校の数学を思い返せば,以下を証明で使ったことをきっと思い出すはずだ.

mとnの2数があってm < nなら
m^2<= x < n^2

問題文の "A is a sorted array of integers"と上記のm^2<= x < n^2 を見ると..うーん,あのデータ構造がドヤァって顔をしてるね.


#include <stdio.h>
#include <stdlib.h>

unsigned my_sqrt(unsigned val) {

unsigned st = 0;
unsigned end = val + 1;
unsigned mid = 0;
while(end - st > 1) {
mid = (st + end)/2;
printf("st-%d,end-%d,mid-%d\n",st,end,mid);
if(mid * mid <= val)
st = mid;
else
end = mid;
}
return st;
}

int main(int argc, char **argv) {
printf("sqrt of %d is %d\n", atoi(argv[1]),my_sqrt(atoi(argv[1])));
return EXIT_SUCCESS;
}

アウトプットは

st-0,end-66,mid-33,mid*mid-1089
st-0,end-33,mid-16,mid*mid-256
st-0,end-16,mid-8,mid*mid-64
st-8,end-16,mid-12,mid*mid-144
st-8,end-12,mid-10,mid*mid-100
st-8,end-10,mid-9,mid*mid-81
sqrt of 65 is 8

ちなみに出典に書いてある解は正しくない(笑.

これは32bitの平方根は必ず16ビットの間に収まる,という事実を上手く捉えた面白い回答なんだけども.

( 実際,Powershellで以下のステートメントを実行してみると
[Math]::floor([Math]::sqrt([UInt32]::MaxValue));
答えは65535,つまり符号なし16ビット整数の最大値.)

以下が本にのってた答えをCで書いたもの.65とかのインプットだと動かないよ.
st = 5, end = 16でmid = 5になって詰み.一つでも動かすとオーバーフローでボンッ!だ.


#define MINIMUM_UINT16BIT 0
#define MAXNUM_UINT16BIT 65535

unsigned my_sqrt_wrong(unsigned val) {

int st = MINIMUM_UINT16BIT;
int end = MAXNUM_UINT16BIT + 1;
unsigned mid = 0;
unsigned midVal = 0;
while(st + 1 < end) {
mid = (end - st) / 2;
printf("st-%d,end-%d,mid-%d,mid*mid-%d\n",st,end,mid,mid*mid);
midVal = mid * mid;
if( midVal == val )
return mid;
else if(midVal > val)
end = mid;
else
st = mid;
}
return st;
}





日曜日, 4月 07, 2013

Spring 基本のキホン2 - コレクションをインジェクト

Spring CollectionsのDI

Springの基本第二弾としてCollectionsのインジェクトを選んでみた.とはいえ,Collectionsのインジェクションの機能は正直言って個人的に微妙である.何故微妙なのか?理由は2つある.まず1つめには,Collectionsをコンテクストファイルに記述しればコンテクストファイルが冗長になって可読性が低くなる.2つ目に一般的にプロパティファイルのプロパティの数は決して少なくないしプロダクトがサポートしなければいけない環境又はインスタンスの数が多い場合はこういったプロパティファイルはビルド時に自動生成する.勿論,コンテクストファイルを自動生成する手もあるにはあるが,今のところは私のチームではやっていない.

とはいえ,非常に便利な機能には違いがないので,是非私たちのツールセットの一つに加えておきたい.

サンプル

ここでは一つのJavaクラス,1つのコンテクストファイルそして実行確認用に1つのテストクラスを用意している.
そんな特別なことはなく,単にSet,Map,List,Propertyのフィールドとセッターを注入対象クラスに定義し,コンテクストファイルにはそれぞれに対応するpropertyタグを定義する.Listならlistタグ,MapならMapタグ,Setならセットタグ,Propertiesならpropsタグを定義.ListならびにSetのエレメント定義にはvalueタグ,Mapならentryタグ,Propertiesならばpropタグを使うこと.

CollectionsHolder.java


package org.tanuneko.spring;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class CollectionsHolder {

private List<String> strList;
private Set<String> strSet;
private Map<Integer,String> strMap;
private Properties props;

public List<String> getStrList() { return strList; }
public Set<String> getStrSet() { return strSet; }
public Map<Integer,String> getStrMap() { return strMap; }
public Properties getProps() { return props; }

public void setStrList(List<String> strList ) {
this.strList = strList;
}

public void setStrSet(Set<String> strSet) {
this.strSet = strSet;
}

public void setStrMap(Map<Integer,String> strMap) {
this.strMap = strMap;
}

public void setProps(Properties props ) {
this.props = props;
}

}

context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">
   
    <bean id="colHolder" class="org.tanuneko.spring.CollectionsHolder">
        <!-- injecting list -->
    <property name="strList">
    <list>
    <value>Cat</value>
    <value>Dog</value>
    <value>Dog</value>
    </list>
    </property>
    <!-- injecting set -->
    <property name="strSet">
    <set>
    <value>Okachimachi</value>
    <value>Brooklyn</value>
    <value>Brooklyn</value>
    </set>
    </property>
    <!-- injecting map -->
    <property name="strMap">
    <map>
    <entry key="1" value="Vanderbilt St"/>
    <entry key="2" value="Willoghby St"/>
    </map>
    </property>
    <!-- injecting map -->
    <property name="props">
    <props>
    <prop key="prop.shape.name">Triangle</prop>
    <prop key="prop.shape.height">12.5</prop>
    <prop key="prop.shape.base">7</prop>
    </props>
    </property>
    </bean>

</beans>

SpringSampleTest.java

package org.tanuneko.spring;

import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.hamcrest.CoreMatchers.*;

/**
 * Unit test for simple App.
 */
public class SpringSampleTest {
static ApplicationContext ctx = null;
@BeforeClass
public static void prep() {
ctx = new ClassPathXmlApplicationContext( "spring/context.xml" );
}
@Test
public void testListHolder() {
CollectionsHolder holder = (CollectionsHolder)ctx.getBean( "colHolder" );
// list.
assertThat( holder.getStrList().get( 0 ), is( "Cat" ) );
assertThat( holder.getStrList().get( 1 ), is( "Dog" ) );
assertThat( holder.getStrList().get( 2 ), is( "Dog" ) );
// Set. In different with list set won't allow to have any dupe entries
for( String str: holder.getStrSet() ) {
assertThat( str, anyOf( is("Okachimachi"), is("Brooklyn" ) ) );
}
assertThat( holder.getStrSet().size(), is( 2 ) );
// Map. Similarly to set no dupe entries are allowed. O(1) reachable unless there are conflicts
assertThat( holder.getStrMap().get( 1 ), is( "Vanderbilt St" ) );
assertThat( holder.getStrMap().get( 2 ), is( "Willoghby St" ) );
// Properties. O(1) reachable for arbitrary keys
assertThat( (String)holder.getProps().get( "prop.shape.name"), is( "Triangle" ) );
assertThat( ( Double.parseDouble( (String)holder.getProps().get( "prop.shape.height" ) ) *
     Double.parseDouble( (String)holder.getProps().get( "prop.shape.base" ) ) ) / 2,
     is( 43.75 ) );
}
}


土曜日, 4月 06, 2013

JavaでREST クライアント

JavaでREST

RESTクライアントとしてJavaを選ぶことは正直言って正しい Tech Choiceとは思えないが,それでも現実的にそれをやらざる得ないことも多い.
とはいえ,それでもCommons等を使ってなんとかそれなりに書くことはできる(最もPythonやgroovyの非ではないが).
JerseyやRestEasy等といったRESTライブラリもあるが,ここではApache HttpComponentsを使う.

サンプル

ここではMongoDBのRESTインターフェースに接続し,テストDBのテスト用コレクションに対してクエリをかける例を示す.
MongoDBはデフォルトではRESTインターフェースを有効化していないので,Mongodを起動する際 --restパラメータを渡す必要がある.


package org.tanuneko.restclient;

import java.io.IOException;
import java.io.StringWriter;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MongoRestClient {

private static Logger logger = LoggerFactory.getLogger( MongoRestClient.class );

public static void queryFind( String dbName, String collection, String userName ) {

DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet req = new HttpGet( "http://127.0.0.1:28017/" + dbName + "/" + collection + "/?filter_user_id=" + userName );

try {
HttpResponse res = httpClient.execute( req );
StringWriter sw = new StringWriter();
IOUtils.copy( res.getEntity().getContent(), sw );
logger.info( sw.toString() );
} catch( IOException ioE ) {
logger.error( ioE.getMessage(), ioE );
System.exit( 1 );
}

}

public static void main(String args[]) {
        queryFind( "DevTest", "DevTest", "tanu" );
}

}


MongoDBのRESTインターフェースについてはここを参照されたし.



金曜日, 4月 05, 2013

Spring 基本のキホン

Spring

Springを上手く使いこなすことができれば,DI/IOCをエレガントにこなすことが出来る.オブジェクトの初期化やインスタンス生成でぐちゃぐちゃしたコードを書きたくない,テスト用DIをスマートに行いたい,JDBCやHibernate等をもう少し綺麗に書きたい,と思っておれば是非Springを試してみてほしい.

Spring サンプル

Springの基礎を丁寧に解説しているページはネットにゴマンとあるので,ここでは基本のキホンのサンプルと簡略な説明にとどめておく.

このサンプルでは4つのクラス,1つのインターフェース,1つのコンテクストxmlを使用する.

まずは土台となるBeanIFインタフェースとSpringSampleクラス.
DIに適用するクラスはテスト用・実働用に別のクラスを注入できるようにするため,インターフェース又は抽象クラスであるべき.

BeanIF.java 

public interface BeanIF {

int getInstanciationCounter();
String getMessage();

}

SpringSample.java


import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component("sample")
@Scope("prototype")
public class SpringSample implements BeanIF {

private static int instanciationCounter = 0;

@Autowired
private Injectee inject;

@Resource
private Injectee2 inject2;

public SpringSample() {
instanciationCounter++;
}

public int getInstanciationCounter() {
return instanciationCounter;
}

public void getMessage() {
return inject.showMessage() + ":" + inject2.showMessage();
}

}


上記クラスでは4つのアノテーションを使っている.
Component .. Spring Bean Management Frameworkで管理されるPojoに汎用的に使われる.
                   Controller,Repository,ServiceもSpring Mojoとしてクラスをマークするが用途は違う. 
Scope .. Singletonは最初のPojo登録の時にのみクラスは初期化される.prototypeはgetBean()されるたびに別のインスタンスが帰ってくる.
Autowired .. 型が合致又は継承又はインターフェース実現クラスであれば自動的に注入.requiredパラメータを使えばautowiringが失敗した場合の挙動も制御できる.Spring由来.
Resource .. Autowiredの条件+PojoのIdが合致する場合に注入.JSR250由来.Autowired + QualifierはResourceとほぼ同様の振る舞いになる.

次にInjecteeとinjectee2 - それぞれautowired又はResourceでDIされる - を見てみる.


@Component("inject")
public class Injectee {

public void showMessage() {
                return "Injected.";
}

}


@Component("inject2")
public class Injectee2 {

public String showMessage() {
        return "injectee2";
}

}



Componentアノテーションによりこれら2つのクラスもまたSpringにより管理化に置かれる.
ではどのようにこのアノテーションドリブンによるPojoの検索・登録がおこなわれるのか.
キーはcontext.xmlにある.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="myapp.package" />

</beans>


context:annotation-configでautowired等のアノテーションを有効にし,context:component-scanにて指定されたパッケージ以下をSpringがスキャンし,Componentアノテーション等でマークアップされたクラスを登録し,autowiring等も行う.contextネームスペースを使う際,xmlns.contextをbeansタグに追加することを忘れないこと.

これでPojoの用意及び依存性注入も出来たはずなので,実際以下のテストクラスを使って試してみる.


import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.hamcrest.CoreMatchers.*;

/**
 * Unit test for simple App.
 */
public class SpringSampleTest {

static ApplicationContext ctx = null;

@BeforeClass
public static void prep() {
ctx = new ClassPathXmlApplicationContext( "spring/context.xml" );
}

@Test
public void testBase() {
SpringSample sample = (SpringSample)ctx.getBean( "sample" );
sample = (SpringSample)ctx.getBean( "sample" );
assertThat( sample.getMessage(), is( "injectee:injectee2" ) );
assertThat( sample.getInstanciationCounter(), is( 2 ) );
}

}

テストが成功すれば,依存性注入も成功し,prototypeの挙動も確認できていることとなる.
Springは結構丁寧なロギングをしているので,もしその情報を見たければlog4j又はslf4j api + slf4j-log4j12を使う必要がある.

木曜日, 4月 04, 2013

英語の方言に囲まれて

英語は方言だらけ

私の職場は実に多彩な人種に富んでいる.自分の所属している部門全体を見てもまさにサラダボウルの様相を呈している.こんな環境の中,当然コミュニケーションは今や世界の標準共通語たる英語で行われるのだが,アクセントは其々の国によって随分違う.

国別の特徴

私が感じたそれぞれの国の人の英語の特徴は,以下の通り.
(あくまで私個人の感想である.)

アメリカ・カナダ人(白人,ユダヤ人) .. クセがなく一番聞き易い.
アメリカ人(黒人) .. 独特の強弱のクセがあるが,慣れれば耳馴染みが良い.
アメリカンチャイニーズ .. 上手い人もいると思うが私の周りでは訛りのある人が多い.広東語圏由来の人は特にアクセントの独特な訛りがきつくなるのですぐ判別がつく.
イギリス人 .. 語尾がスパっと切れるような,シャープな感じ.個人的には聞きにくい.しかし私の会社のNYオフィスには結構いるのだ.英語の発音ですぐ分かる.サンキューがサンキー,と聞こえれば100%確実.鼻にかかったような感じの英語ともいえるかもしれない.
中国人(マンダリン) .. 上手下手の個人差が激しい.上手な人は実に上手に喋るがそうでない人は中国語風のリズムと訛りのせいか,きつい印象を受けてしまう.でも日本人よりは総じて上手だ.
中国人(広東語) .. 一層訛りはきつくなる.広東語のリズムとアクセントが巧みに英語にアダプトされた感じだ.
韓国人 .. ハングルが日本語と同じ母音数のせいか訛りはあったにしても聞き易い.文節毎に上あがりのアクセントが目立つ気がする.
シンガポール人 .. 早口で訛りはきつめ.新入社員当時シンガポールの人の英語は実に厳しかった.
インド人 .. アクセント付英語のチャンピオンである.母語により特徴が変わってくるのでインド人というひとくくりにはできないが,インド人独特の英語表現等もあり,慣れれば実に面白い.
プエルトリコ人 .. アメリカ人の英語とは違うかな,というのは分かるが十二分に聞き易い.
ナイジェリア人 .. クセは相当強い.アメリカ黒人の英語をもっと強烈にした感じかも.しかしサンプル数は少ないので信頼性低し
タンザニア人 .. ナイジェリアの人ほどきつい訛りはないかもしれない.サンプル数低いので信頼性低し
コロンビア人 .. 自分の経験上アメリカ英語を上手に喋る人が多い.元気な人多し.
トリニダード・トバコ人 .. イギリス英語のようでそうでない,黒人英語のようでそうでない,不思議な感じ.
日本人 .. 抑揚がない.残念ながら外人には一番聞き取りにくい英語を喋るのかもしれない.

このように方言だけで大変バラエティに富んでいて,面白いものだ.







非同期ロガー

非同期ロギング

Javaはlog4jから始まってslf4j, logbackとロギングAPIの歴史の立役者が揃っている.
そのようなロギングAPIはサイズまたは日付にによる自動ローリング,仔細なフォーマッティングの指定等といった必要不可欠でかつ極めて有用な機能をすべて提供してくれている.

そういった機能の中で私が注目したいのが非同期ロギングである.マルチスレッディングのアプリケーションにおいてロギングは避けることの出来ないクリティカルセクションである.しかしながら,非同期ロガーを使えばロギングはもはやクリティカルセクションではなくなるlog.

ここでは実装はlogback,インターフェースはslf4j-apiの組み合わせによる非同期ロガーの例を紹介してみる.

logback Async Logger

使い方は単純.logback.xml (ユニットテストにはlogback-test.xml )にAsyncAppenderの定義をして,そのappenderをrootの入れ子要素として宣言すればよい.
以下は実際のlogback.xmlの一例.
ここではASYNCアペンダーにFILEアペンダーを宣言して,ファイルへのロギングを非同期化している.


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

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>
    </encoder>
  </appender>
  
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <!-- <file>${LOGBACK_LOGDIR}/sample.log</file> -->
    <file>${LOGBACK_HOMEDIR}/samplelog.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${LOGBACK_HOMEDIR}/samplelog-%d{yyyy-MM-dd}.log</fileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <maxFileSize>20MB</maxFileSize>
        <maxHistory>2</maxHistory>
      </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>    
    </encoder>
  </appender>
  
  <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
      <appender-ref ref="FILE"/>
      <!-- discardingThreashold == 0 means it won't drop TRACE, DEBUG and INFO level log even when the remaining capacity of blocking queue is less than 20% -->
      <discardingThreshold>0</discardingThreshold>
  </appender>

  <!-- Strictly speaking, the level attribute is not necessary since -->
  <!-- the level of the root level is set to DEBUG by default.       -->
  <root level="INFO">          
    <appender-ref ref="STDOUT" />
    <appender-ref ref="ASYNC" />
  </root>  
  
</configuration>

考慮すべきこと

ASYNCロガーは色々便利そうではあるが,非同期の常,扱いは同期のものよりやや手数がかかる.一番気をつけなければいけないのはやはりブロッキングキューのサイズだろう.
もし大量のスレッドが(大量ではないにせよ)沢山のログを非同期ロガーに送り込んだ場合,言い換えるならば沢山のログをロガーのブロッキングキューに追加した場合,最悪の場合ブロッキングキューがフルになってしまう.そのような場合,非同期ロガーはブロッキングキューに空きが出るまでアプリケーションスレッドをブロックさせる.この振る舞いをlogbackはpseudo synchronous (疑似同期)と呼んでいる.logbackでは以下のポイントがpseudo synchronousを引き起こしやすいとしている.

 logback 公式ページより引用
  • Large numbers of application threads (沢山のスレッド)
  • Large numbers of logging events per application call (沢山のロギング)
  • Large amounts of data per logging event (各ロギングイベントでの沢山のデータ)
  • High latency of child appenders (レイテイシの大きい子アペンダー)
上記いずれかに当てはまると思ったならば,是非改善したいところである.

月曜日, 4月 01, 2013

Commons IO FileUtils

また Commons IO

前回のCommons Lang StringUtilsに引き続き,Commons IO FileUtilsの紹介..
StringUtilsに負けず劣らずニッチを上手く扱っている上,使い勝手が良い.

Version 2.4を使うとして,Mavenのdependencyは

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>

Gradleならば

'commons-io:commons-io:2.4'

Grapesであれば

@Grapes(
@Grab(group='commons-io', module='commons-io', version='2.4')
)

便利なメソッド群

Javadocを一瞥してみると,便利そうなメソッドがすぐ見つかるだろう.実際使い勝手は良い.

いくつかをピックアップして試してみたコードは以下の通り.
FileUtils無しに同じコードを書いたとすれば沢山のStream系オブジェクトの扱い及び例外処理だらけのコードになるであろうことは想像に難くない.

import java.io.File;

import static org.hamcrest.CoreMatchers.*;
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.apache.commons.io.FileUtils.*;


public class FileUtilTest {

@Test
public void testReadWrite() throws Exception {
File tmpFile = null;
File tmpFile2 = null;
File tmpFile3 = null;
try {
tmpFile = File.createTempFile( "tmp", "test" );
           // write() .. write string to file
                        // sizeOf() .. checks file size
write( tmpFile, "1\n2\n" );
assertThat( sizeOf( tmpFile ), is( 4L ) );

                        // readLine() .. read lines from files and persist them to List<String>
int lineNum = 1;
for( String line : readLines( tmpFile ) ) {
assertThat( Integer.parseInt( line ), is( lineNum++ ) );
}
                        // readFileToString() .. read file and returns its content as single string
assertThat( "1\n2\n", is( readFileToString( tmpFile ) ) );
tmpFile2 = File.createTempFile( "tmp", "test" );

                        // write() .. write string to file
write( tmpFile2, "1\n2\n" );
                        // contentEquals() .. compare two files each other
                        // contentEqualsIgnoreEOL .. compare two files each other and ignore EOL
contentEquals( tmpFile, tmpFile2 );
tmpFile3 = File.createTempFile( "tmp", "test" );
write( tmpFile3, "1\r\n2\r\n" );
contentEqualsIgnoreEOL( tmpFile, tmpFile3, null );
} finally {
                        // forceDelete() .. delete file or directory in force manner ( it means delete all recursively )
forceDelete( tmpFile );
forceDelete( tmpFile2 );
forceDelete( tmpFile3 );
}
}
}