IntelliJ IDEA14のデフォルトの設定だと、左矢印キーを押していくと、行頭でとまり前の行の最後に移動しない。
これを前の行の最後に移動させる設定のメモ。
下記の項目のチェックを外す。
[Preference] > [Editor] > [General] > Virtual Spaceの中にあるAllow placement of caret after end of line
なお検索窓に"allow placement of caret end of line"のキーワードを入れると一発でたどり着ける。
2014年12月20日土曜日
2014年11月26日水曜日
麻雀の牌を表示するJavascriptライブラリ
以前に公開したJavaの麻雀AIサーバであるSmartBirdから麻雀の牌を描画する部分だけ取り出してライブラリ化しました。
https://github.com/kentan/MahjongTileDrawingJS/tree/master
麻雀の牌はフリー素材などがいっぱいあったりしますが、今回公開したのはHTML5のcanvasタグを使ってその場で描画するタイプです。
使い方は下記のようにシンプル。
萬子はCharacter,索子はBamboo,筒子はCircleで表現しているので例えば5索を書きたい場合はdrawBamboo5("canvasタグのID")といったふうに呼び出せば、引数に指定したcanvasタグに5索が描かれます。
サンプル用のhtmlファイル
<html> <head></head> <body onload="startDrawing()"> <canvas id = "canvas1" width="50px" height="60px"></canvas> </body>ライブラリを呼び出すサンプル用のjavascript
function startDrawing(){ tileDrawing.drawCharactor1("canvas1"); }
詳細はgithubにあるサンプルコードを見てください
Javaの文字コード変換サンプルコード2
java.nio.charset.Charsetにあるdecodeとencodeの挙動が気になったのでサンプルを書いてみた。
次に、サンプルコードを書いて、encode/decodeした時にどのようなバイト列が格納されているのか確認してみた。
見方を少し説明すると、Originalと書いてある行には、"日本語"を代入したStringのchar表現が出力されている。
次の行はそれぞれの文字コードでエンコードした結果のバイト列表現。
そして、3行目にはエンコードした結果を再度デコードし、一行目の値と同じか確認をしている。
出力結果を見ると、encodeした結果はそれぞれ違うバイト表現が入っているので、おそらくその文字コードでエンコードされた結果が入っていることが確認できる。そして、それらをdecodeした結果は再度String型の時と同じ表現に戻っているようだ。
ISO-2022-JPの時には、最後に0,0,0と他にはないバイトが入っているが。これはなぜだろうか。。
標準出力自体は"日本語"と正しく出力できているようだが。
まず、仕様の簡単な説明。
Charset.encodeは、入力としてStringを受け取り、出力として任意の文字コードのバイト列を出力する。
Charset.decodeは、入力として、UTF-8とかShift-JISでエンコードされた文字のバイト列を与えると、出力としてUnicode(UTF-16)で変換された文字のバイト列が出力される。
文字コードの指定は下記のようにCharsetのインスタンスを取得する過程で行う。
Charset cs = Charset.forName("Shift-JIS")
cs.encode(new String("エンコードしたい文字"));
次に、サンプルコードを書いて、encode/decodeした時にどのようなバイト列が格納されているのか確認してみた。
public static void sampleEncoder(String target,Charset cs){ /** まずは元のStringデータをCharに分解して確認してみる**/ System.out.print("Original: "); char[] chars = new char[target.length()]; target.getChars(0,target.length(),chars,0); for(int i = 0; i < chars.length; i++){ System.out.print((int)chars[i] + " "); } System.out.println(); /** 次にStringを指定されたEncodeに変換し、バイト表現を確認する **/ ByteBuffer bf = cs.encode(target); System.out.print(cs.toString() + ": "); for(int i = 0 ; i < bf.array().length; i++) { System.out.print((int)bf.array()[i] + " "); } System.out.println(); /** 最後にEncodeした文字を、同じ文字コードで再びDecodeし、Charとしての表現を確認する **/ CharBuffer cb = null; cb = cs.decode(bf); StringBuffer sb = new StringBuffer(); System.out.print("Unicode(UTF-16): "); for(int i = 0 ; i < cb.array().length; i++) { System.out.print((int)cb.array()[i] + " "); sb.append(cb.array()[i]); } System.out.println(" :" + sb.toString()); System.out.println(); } public static void main(String args[]){ sampleEncoder("日本語",Charset.forName("ISO-2022-JP")); sampleEncoder("日本語",Charset.forName("Shift-JIS")); sampleEncoder("日本語",Charset.forName("EUC-JP")); }これの実行結果は次のようになる。
Original: 26085 26412 35486 ISO-2022-JP: 27 36 66 70 124 75 92 56 108 27 40 66 Unicode(UTF-16): 26085 26412 35486 0 0 0 :日本語 Original: 26085 26412 35486 Shift_JIS: -109 -6 -106 123 -116 -22 Unicode(UTF-16): 26085 26412 35486 :日本語 Original: 26085 26412 35486 EUC-JP: -58 -4 -53 -36 -72 -20 0 0 0 Unicode(UTF-16): 26085 26412 35486 :日本語
見方を少し説明すると、Originalと書いてある行には、"日本語"を代入したStringのchar表現が出力されている。
次の行はそれぞれの文字コードでエンコードした結果のバイト列表現。
そして、3行目にはエンコードした結果を再度デコードし、一行目の値と同じか確認をしている。
出力結果を見ると、encodeした結果はそれぞれ違うバイト表現が入っているので、おそらくその文字コードでエンコードされた結果が入っていることが確認できる。そして、それらをdecodeした結果は再度String型の時と同じ表現に戻っているようだ。
ISO-2022-JPの時には、最後に0,0,0と他にはないバイトが入っているが。これはなぜだろうか。。
標準出力自体は"日本語"と正しく出力できているようだが。
2014年11月25日火曜日
CodingErrorAction.IGNOREとCodingErrorAction.REPLACE とCodingErrorAction.REPORT
CodingErrorAction.IGNOREとCodingErrorAction.REPLACE とCodingErrorAction.REPORTを使ったサンプルコードを書いてみました。
hex1はshift-jisで書かれた"日本語"という文字列の16進法表記です。
hex2は、hex1の間に不正なCharacterである00を挟んだ"不正な"文字列です。
これを実行すると出力結果は下記のようになります。
Aの場合正しいShift-JISコードを変換しているので、「日本語」と正しくでます。CodingErrorAction.IGNOREを指定していますが、不正な入力はないので、この値は使われることはなく、何を指定しても同じです。
B~Cの場合は、不正な値があるので、onMalformedInput、onUnmappableCharacterの指定の仕方によって出力結果が違います。
CodingErrorAction.IGNOREを指定した場合、不正な値は無視されますので、正しく解釈できた「日本」までが出力されています。なお、「日本」の直後は空白文字などが出力されているわけではなく、改行されています。
Cの場合は、不正な値をReplaceしているので、��というReplaceされた値が出力されています。
Dの場合は、不正な場合はエラーをReportするように指定しているので、Exceptionが発生しdecodeがされないままで終了しています。
public static void sampleCodingErroAction(String hex, CodingErrorAction actionOnMalformedInput, CodingErrorAction actionOnUnmappableCharacter){ ByteBuffer buff = ByteBuffer.allocate(hex.length()/2); for (int i = 0; i < hex.length(); i+=2) { buff.put((byte)Integer.parseInt(hex.substring(i, i+2), 16)); } buff.rewind(); Charset cs = Charset.forName("shift-jis"); CharBuffer cb = null; try { cb = cs.newDecoder() .onMalformedInput(actionOnMalformedInput) .onUnmappableCharacter(actionOnUnmappableCharacter) .decode(buff); System.out.println(cb.toString()); }catch(Exception e){ System.out.println("error"); } } public static void main(String args[]){ String hex1 = "93FA967B8CEA"; //日本語 String hex2 = "93FA967B8C00EA"; //不正な文字 System.out.print("正しいShift-JIS ");//A sampleCodingErroAction(hex1, CodingErrorAction.IGNORE, CodingErrorAction.IGNORE); System.out.print("不正なShift-JIS(Ignore) ");//B sampleCodingErroAction(hex2, CodingErrorAction.IGNORE, CodingErrorAction.IGNORE); System.out.print("不正なShift-JIS(Replace) ");//C sampleCodingErroAction(hex2, CodingErrorAction.REPLACE, CodingErrorAction.REPLACE); System.out.print("不正なShift-JIS(Report) ");//D sampleCodingErroAction(hex2, CodingErrorAction.REPORT, CodingErrorAction.REPORT); }
hex1はshift-jisで書かれた"日本語"という文字列の16進法表記です。
hex2は、hex1の間に不正なCharacterである00を挟んだ"不正な"文字列です。
これを実行すると出力結果は下記のようになります。
正しいShift-JIS 日本語
不正なShift-JIS(Ignore) 日本
不正なShift-JIS(Replace) 日本��
不正なShift-JIS(Report) error
Aの場合正しいShift-JISコードを変換しているので、「日本語」と正しくでます。CodingErrorAction.IGNOREを指定していますが、不正な入力はないので、この値は使われることはなく、何を指定しても同じです。
B~Cの場合は、不正な値があるので、onMalformedInput、onUnmappableCharacterの指定の仕方によって出力結果が違います。
CodingErrorAction.IGNOREを指定した場合、不正な値は無視されますので、正しく解釈できた「日本」までが出力されています。なお、「日本」の直後は空白文字などが出力されているわけではなく、改行されています。
Cの場合は、不正な値をReplaceしているので、��というReplaceされた値が出力されています。
Dの場合は、不正な場合はエラーをReportするように指定しているので、Exceptionが発生しdecodeがされないままで終了しています。
2014年11月22日土曜日
javaの文字コード変換サンプルコード
javaの文字コード変換はString.getBytes()を使う方法が主流な様子だけど、別の書き方を見つけたのでメモしておく。
参考文献
JavaでUTF-8の文字コードを判定する
[Java]いいから聞け! 俺が文字コードについて教えてやるよ その2(Javaの文字コード編)
public void testEncoding(){ String target = "日本語"; Charset cs = Charset.forName("UTF-8"); //Charset cs = Charset.forName("Shift-JIS"); // SHIFT-JISに変換したい場合はこれを使う。 //Charset cs = Charset.forName("EUC-JP");// EUC-JPに変換したい場合はこれを。 //Charset cs = Charset.forName("ISO-2022-JP");//ISO-2022-JPに変換したい場合はこれを。 ByteBuffer bf = cs.encode(target); try { File file = new File("output.txt"); FileOutputStream fos = new FileOutputStream(file); fos.write(bf.array()); fos.close(); }catch(Exception e){ e.printStackTrace(); } }なお、正しく出力されているのか確認したい場合はブラウザを使うと手っ取り早い。テキストファイルをブラウザで開き、文字コードを変更することによって希望のエンコーディングがされているか確認できる。
参考文献
JavaでUTF-8の文字コードを判定する
[Java]いいから聞け! 俺が文字コードについて教えてやるよ その2(Javaの文字コード編)
2014年11月14日金曜日
アルゴリズムが世界を支配する
タイトルにアルゴリズムとあるが、中身の多くは最近良く話題になる人工知能について割かれている。アルゴリズム自体の話よりも、いかに使われてきたか、それを開発した人たちにどのようなドラマがあったかを中心にまとめられている。
ウォール・ストリートの株式市場で人工知能が使われていることは有名だが、本書によると1987年ころには早くも使われていたとのことだ。正直、インターネットも一般的に普及していないような時代から利用されていたとは意外であった。
また、本書の別の箇所では、山をぶちぬいて二地点間を直線に高速回線を引いてしまうエピソードが載せられている。「最低でも2億ドル」と本書が述べる工事予算に出資していまう投資家がさらりと現れるところが、なんともアメリカらしい逸話だ。
金融以外では、音楽の自動作曲を試みた大学教授の話が興味深い。楽曲自体は優れたものでありながらも、自動生成されたということを知った途端、聴衆の興味が削がれている様など、古くから人工知能の限界に挑んでいた様子が詳細に描かれている。
ウォール・ストリートの株式市場で人工知能が使われていることは有名だが、本書によると1987年ころには早くも使われていたとのことだ。正直、インターネットも一般的に普及していないような時代から利用されていたとは意外であった。
また、本書の別の箇所では、山をぶちぬいて二地点間を直線に高速回線を引いてしまうエピソードが載せられている。「最低でも2億ドル」と本書が述べる工事予算に出資していまう投資家がさらりと現れるところが、なんともアメリカらしい逸話だ。
金融以外では、音楽の自動作曲を試みた大学教授の話が興味深い。楽曲自体は優れたものでありながらも、自動生成されたということを知った途端、聴衆の興味が削がれている様など、古くから人工知能の限界に挑んでいた様子が詳細に描かれている。
Hatching Twitter(Twitter創業物語)
Twitterの創業にまつわるエピソードを書いた本。
Twitterを立ち上げる前に取り組んでいたサービスなども詳細に書かれており、どのような経緯でTwitterが生まれていったかよく分かる。
また創業者間の人間関係の確執なども、せきららに書かれており、Twitterの創業の場に居合わせたかのような臨場感を味わうことが出来る。正直、よく許可を出したなと思えるくらい恥部が満載なのも読み手としては非常におもしろい。
読んでいて興味深いと思ったことをいくつか纏めてみる。
1. 創業者の一人(Evan)はすでに、スタートアップで実績があった
ずっと利益0の状態なのに、なぜか投資家による資金投入が行われているのは本を読むから知っていて、アメリカは違うなーと単純に思ってたんですが、EvanはすでにGoogleにブログサービス(私も使っているこのBlogger)を売却した実績があって、スタートアップ界隈ではすでに有名人だったようだ。
別に有象無象の人間に投資を続けていたわけではなく、投資されるだけの理由がちゃんとあったわけです。あと、はじめの方はEvan本人がBloggerの売却益を回したりしていたようだ。
2. テレビ業界などのメディアが積極的にTwitterを利用していた。
最近は若干ましになった気もするが、日本のメディアのIT嫌いは異常なほど。それに比べると積極的にテレビの番組などでTwitterを利用している様子は印象的だった。
2014年10月21日火曜日
Gitレポジトリの移行方法
gitのレポジトリを移行する方法のメモ。
bitbucketで管理してたレポジトリをgithubで管理したくて調べてみた。
export/importやるのかと思いきや、remoteサーバのurl設定を替えるだけでいいようだ。
まず、今の設定を確認
%>git remote -v
origin https://kentan@bitbucket.org/kentan/test.git (fetch)
origin https://kentan@bitbucket.org/kentan/test.git (push)
次に、remoteレポジトリの変更
%>git remote
set-url origin https://github.com/kentan/test2.git
%>git remote -v
origin https://github.com/kentan/test2.git (fetch)
origin https://github.com/kentan/test2.git (push)
そして、ソースのpush
%> git push origin mastergit logをすると、きちんと過去のcommit logまでpushされており、はじめからgithubにcommitしていたかのように移行できる。
2014年9月30日火曜日
[R言語] table関数
table関数の挙動をメモしておく。
vには"a"が2回,"b"が2回,"c"が1回出現したとういう意味。引数に与えたvectorの頻度が得られる。
列(row)のためのVentorを用意する。
そして、次に行(Colomn)のためのVectorを用意する。
これを、table関数の第一引数、第二引数にそれぞれ渡す。
すると下記の値が得られる。
これがどのような値か解説すると、 ("a","A")のペアが3組, ("b","B")のペアが3組, ("b","A")のペアが0組, ("a","B")のペアが0組、というデータ形式を表すことになる。
ペアは、ベクターrとcの同じ順序の値が選択される。例えばrの第一要素は"a", cの第一要素は"A"であるので、ペアとして("a","A")が選ばれる。
ペアを全部列挙すると、
("a","A")
("b","B")
("a","A")
("b","B")
("a","A")
("b","B")
という6組のペアが生成され、このペアの出現頻度を行列形式で表したのがtable(r,c)ということになる。
tableって名前から結果が想像しづらい。
参考文献
Table(v)
まず引数一つのパターン。
引数に渡すVectorを用意する。
v <- c("a","b","a","b","c")
これをtable関数に渡す。
t <- table(v)
結果は次のようになる。
a b c
2 2 1
Table(r,c)
次に引数2つのパターン。列(row)のためのVentorを用意する。
r <- c("a","b","a","b","a","b")
そして、次に行(Colomn)のためのVectorを用意する。
c <- c("A","B","A","B","A","B")
これを、table関数の第一引数、第二引数にそれぞれ渡す。
t <- table(r,c)
すると下記の値が得られる。
A B
a 3 0
b 0 3
これがどのような値か解説すると、 ("a","A")のペアが3組, ("b","B")のペアが3組, ("b","A")のペアが0組, ("a","B")のペアが0組、というデータ形式を表すことになる。
ペアは、ベクターrとcの同じ順序の値が選択される。例えばrの第一要素は"a", cの第一要素は"A"であるので、ペアとして("a","A")が選ばれる。
ペアを全部列挙すると、
("a","A")
("b","B")
("a","A")
("b","B")
("a","A")
("b","B")
という6組のペアが生成され、このペアの出現頻度を行列形式で表したのがtable(r,c)ということになる。
tableって名前から結果が想像しづらい。
参考文献
Frequency tables
2014年9月9日火曜日
機械学習入門 サンプルコードの間違い
Oreilly社発行の「機械学習入門」にて、サンプルコードの誤りを見つけたので、ここに残しておきます。
サンプルが管理されているgithubではissueとして報告されており、pull requestも出しているが、1年以上前のpull requestが放置されている状態をみると、公式に直される可能性は残念ながら低そうな様子。今回見つけたバグは3章のemail classifyのコード。
classify.email <- function(path, training.df, prior = 0.5, c = 1e-6) { # Here, we use many of the support functions to get the # email text data in a workable format msg <- get.msg(path) msg.tdm <- get.tdm(msg) msg.freq <- rowSums(as.matrix(msg.tdm)) # Find intersections of words msg.match <- intersect(names(msg.freq), training.df$term) # Now, we just perform the naive Bayes calculation if(length(msg.match) < 1) { return(prior * c ^ (length(msg.freq))) ★ } else { match.probs <- training.df$occurrence[match(msg.match, training.df$term)] return(prior * prod(match.probs) * c ^ (length(msg.freq) - length(msg.match))) ★ } }
パッと見た感じ問題がなさそうなんですが、実は★で示した行の処理は、ものすごい小さい浮動小数点になるため、値が0になってしまします。
c = 1e-6( = 0.000001)であり、それに対して(length(msg.freq) - length(msg.match)))という最大で数百になる数でべき乗を求めているので、数値上は1e-1000を越えることがあり、Rで扱える浮動小数点の最小値を下回ってしまっているようです。
そのため、数をRで扱える範囲に移動させるために、全体に対してlog10を与えます。
if(length(msg.match) > 1) { return (log10(prior) + length(msg.freq) * log10(c)) ★ } else { match.probs >- training.df$occurrence[match(msg.match, training.df$term)] return (log10(prior) + sum(log10(match.probs)) + (length(msg.freq) - length(msg.match)) * log10(c)) ★ } }
なお計算の途中で下記のlogに関する定理を利用しています。
log(A * B) = logA + logBまた prod(match.probs)は、match.probsというベクトルの全要素を掛け合わせるという計算式ですので、これにlog10を適用すると下記のようになります。
log(A ^ C) = C*logA
log(prod(match.probs) = log(a1 * a2 * ... * an)= log(a1) + log(a2) + ... + log(an)
= sum(log(match.probs))ちなみにissue#17にも報告がある通り、これを適用するとスパム判定結果が書籍の値と全く違う値になりますなんか、Amazonレビューによると書籍自体も作りが甘い箇所が多いようですし、加えてコードにもかなり致命的があります。本のコンセプト自体は他に類がない希少なものでありながら、結果としてこのような適当な作りに仕上がってしまったのはなんとも残念です。
2014年9月3日水曜日
tmパッケージを使ったサンプル [R言語]
R言語でtmパッケージを使ったサンプル
まずコーパスを作成して、TermDocumentMatrixを作る。
得られたTermDocumentMatrixをいろいろいじってみる。
例えば一行目は"This is a first line"だったので、Docs 1としてfirst, line, thisがそれぞれ1回カウントされている。
まずコーパスを作成して、TermDocumentMatrixを作る。
R> library('tm')読み込んでるsource.txtの内容は次のようなサンプル文
R> con <- file(file.path("source.txt"),open="rt")
R> text <- readLines(con)
R> close(con)
R> corpus <- Corpus(VectorSource(text))
R> tdm <- TermDocumentMatrix(corpus)
This is a first line.
This is a second line.
This is a third line.
The forth line is being written.
得られたTermDocumentMatrixをいろいろいじってみる。
inspect()
各Docs(今の例では1行がひとつのDocに対応)に出てくるTermを全部表示する。R> inspect(tdm)
A term-document matrix (10 terms, 4 documents)
Non-/sparse entries: 14/26
Sparsity : 65%
Maximal term length: 8
Weighting : term frequency (tf)
Docs
Terms 1 2 3 4
being 0 0 0 1
first 1 0 0 0
forth 0 0 0 1
line 0 0 0 1
line. 1 1 1 0
second 0 1 0 0
the 0 0 0 1
third 0 0 1 0
this 1 1 1 0
written. 0 0 0 1
例えば一行目は"This is a first line"だったので、Docs 1としてfirst, line, thisがそれぞれ1回カウントされている。
findFreqTerms
3回以上登場するtermを抽出する。R> findFreqTerms(tdm,3)
[1] "line." "this"
findAssocs
指定した単語に共起されるtermを抽出する。第3引数は共起率。R> findAssocs(tdm,"this",0.1)
line. first second third
1.00 0.33 0.33 0.33
2014年9月2日火曜日
R言語でのsapplyを使ったサンプルコード
プログラミング言語Rにあるsapplyを使ったサンプルコード。
sapplyはvector型の各要素に対して同じ処理を適用したいときに使用する関数。
例えば、vectorの全要素を2倍したりしたいときに使える。
例えば、渡された引数を2倍にして返す次の関数を考える。
sapplyはvector型の各要素に対して同じ処理を適用したいときに使用する関数。
例えば、vectorの全要素を2倍したりしたいときに使える。
例えば、渡された引数を2倍にして返す次の関数を考える。
%>times2 <- function(v){return (v*2)}試しにこれを実行するとちゃんと2倍した値が返される。
%> times2(20)これをsapplyと組み合わせると、vectorの値を2倍した値が得られる
%> [1] 40
%> l <- c(1,2,3,4,5)
%> sapply(l,times2)
%> [1] 2 4 6 8 10
2014年8月26日火曜日
@EJBと@INJECTの違い
どっちも依存性注入(Dependency Injection:DI)のために使うアノテーション。
違いは@EJBがejbしか注入できない一方、@INJECTはejbを含めてなんでも注入できること。
参考文献
Should I use @EJB or @Inject
違いは@EJBがejbしか注入できない一方、@INJECTはejbを含めてなんでも注入できること。
参考文献
Should I use @EJB or @Inject
量指定子(greedy,reluctant,possessive)による性能の違い
以前に正規表現における量指定子(greedy,reluctant,possessive)とはなにかというタイトルでブログを書きましたが、その時に調べた公式ドキュメントには量指定子によって性能差があると書いてありました。
具体的には、possessiveはgreedyに比べてバックトラックを行わないため実行速度が早い、とのことです。
試しにそれぞれの量指定子による正規表現マッチを1000万回行うコードを書いたところ、実行速度は次のようになりました。
確かにpossessiveに比べてgreedyのほうが早いです。実行速度にして1/3から1/2程度。
性能にシビアなコードを書くときは意識した方が良さそうですね。
具体的には、possessiveはgreedyに比べてバックトラックを行わないため実行速度が早い、とのことです。
試しにそれぞれの量指定子による正規表現マッチを1000万回行うコードを書いたところ、実行速度は次のようになりました。
greedy :7297 ms
reluctant :6125 ms
possessive :19624 ms
確かにpossessiveに比べてgreedyのほうが早いです。実行速度にして1/3から1/2程度。
性能にシビアなコードを書くときは意識した方が良さそうですね。
付録 測定に使ったコード(リンクしたgistと同じもの)
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegExpTest { private static String str = "xfooxxxxxxfoo"; private static int max = 10000000; public static void main(String args[]){ runGreedy(); runReluctant(); runPossessive(); } public static void runGreedy(){ long before = System.currentTimeMillis(); String regex = ".*foo"; for(int i = 0; i < max ; i++){ Pattern p = Pattern.compile(regex); Matcher m = p.matcher(str); m.find(); } long after = System.currentTimeMillis(); System.out.println("greedy :" + (after - before)); } public static void runReluctant(){ long before = System.currentTimeMillis(); String regex = ".*?foo"; for(int i = 0; i < max ; i++){ Pattern p = Pattern.compile(regex); Matcher m = p.matcher(str); m.find(); } long after = System.currentTimeMillis(); System.out.println("reluctant :" + (after - before)); } public static void runPossessive(){ long before = System.currentTimeMillis(); String regex = ".*+foo"; for(int i = 0; i < max ; i++){ Pattern p = Pattern.compile(regex); Matcher m = p.matcher(str); m.find(); } long after = System.currentTimeMillis(); System.out.println("possessive :" + (after - before)); } }
2014年7月24日木曜日
InitialContextを使ったディレクトリ操作のサンプル
EJBやRMIなどを利用しているとよく目にするInitialContext。公式ドキュメントを読んでいたら、どうやら、ファイルシステムもlookupしたりすることができるみたいです。
ファイルシステム以外にも、LDAPやDNSの検索ができたり、思ったよりも多機能な様子です。
そこで、指定したディレクトリ配下にあるファイルを出力するサンプルコードを書いてみました。
サブディレクトリがある場合は、その下を再帰的にほってファイル名を探しにいきます。
なお、filesystem操作用のサービスプロバイダーは標準で用意されていないようで、fscontext.jarを落としてくる必要があります。
公式のドキュメントでもjarのリンクが張ってあったんですが、com.sunの下にリンクがはられており、リンク切れでたどり着くことができませんでした。
そのため、私はこちらからjarを落として使いました。
参考文献
ファイルシステム以外にも、LDAPやDNSの検索ができたり、思ったよりも多機能な様子です。
そこで、指定したディレクトリ配下にあるファイルを出力するサンプルコードを書いてみました。
サブディレクトリがある場合は、その下を再帰的にほってファイル名を探しにいきます。
import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NameClassPair; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import java.util.Hashtable; import com.sun.jndi.fscontext.RefFSContext; class JNDIFileLookup { public static void main(String[] args) { String root = "file:/"; Hashtable env = new Hashtable(11); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); env.put(Context.PROVIDER_URL, root); try { Context ctx = new InitialContext(env); listFiles(ctx); } catch (Exception e) { } } private static void listFiles(Context context) { NamingEnumeration list; try { list = context.list(""); while (list.hasMore()) { NameClassPair nc = (NameClassPair) list.next(); Object sub = context.lookup(nc.getName()); if (sub instanceof RefFSContext) { listFiles((RefFSContext) sub); } else { System.out.println(sub); } } } catch (NamingException e1) { e1.printStackTrace(); } } }
なお、filesystem操作用のサービスプロバイダーは標準で用意されていないようで、fscontext.jarを落としてくる必要があります。
公式のドキュメントでもjarのリンクが張ってあったんですが、com.sunの下にリンクがはられており、リンク切れでたどり着くことができませんでした。
そのため、私はこちらからjarを落として使いました。
参考文献
2014年7月17日木曜日
正規表現における量指定子(greedy,reluctant,possessive)とはなにか
Javaの正規表現では、quantifier(量指定子)の種類としてGreedy、Reluctant、Possessiveというものがある。
webを検索すると説明はいろいろ出てくるが、日本語の付け方が違ったり、3つを並べて比較してなかったりしてわかりづらかったので、これの違いを整理してみる。
まずquantifier(量指定子)とはなにか。
これは"量"指定子という名称の通り、一致させたい文字の数を指定するための表現方法。
例えば、テキストファイルを検索するためによく使われる下記の表現。
これは任意の文字が0個以上続いたあとに、".txt"が続く文字列を表現したものであり、*がquantifier(量指定子)に当たる。
下記の例がReluctant Quantifier
webを検索すると説明はいろいろ出てくるが、日本語の付け方が違ったり、3つを並べて比較してなかったりしてわかりづらかったので、これの違いを整理してみる。
まずquantifier(量指定子)とはなにか。
これは"量"指定子という名称の通り、一致させたい文字の数を指定するための表現方法。
例えば、テキストファイルを検索するためによく使われる下記の表現。
.*\.txt
これは任意の文字が0個以上続いたあとに、".txt"が続く文字列を表現したものであり、*がquantifier(量指定子)に当たる。
Greedy Quantifier
greedy(欲張り)と名前が付いている通り、この量指定子を使うとなるべく多くの繰り返し数になるようにマッチする。
たとえば下記の例がGreedy Quantifier。
.*fooこのとき、xfooxxxxxxfooという文字列にマッチさせようとすると、次の文字列がマッチする。
xfooxxxxxxfoo
Reluctant Quantifier
reluctant(気乗りしない)と名前がついており、この量指定子を使うとなるべく少なくなる繰り返し数になるようににマッチする。下記の例がReluctant Quantifier
.*?fooこのとき、先ほどと同様にxfooxxxxxxfooという文字列にマッチさせようとすると、次の文字列がマッチする
xfoo
Possessive Quantifier
possesiveは「所有の」という意味がある。この量指定子を使うとGreedyと同様になるべく多くの繰り返し数になるようにマッチする。Greedyとの違いは、正規表現全体のマッチよりも、Possessive Quantifierを使った部分のマッチが優先される、ということ。
例えば下記の例がpossessive quantifier。
.*+foo
同様にxfooxxxxxxfooにマッチさせようとすると、結果は何にもマッチしない。
なぜなら、".*+"がなるべく多くの繰り返し数になるようにマッチするため、この部分だけでxfooxxxxxxfoo文字列全体がマッチする。そのため、fooの部分が一致する場所がなくなり、全体としてはマッチしない、という結果になる。
ここで、それぞれのQuantifierとfooの部分がマッチした箇所を整理すると次のようになる。
Greedy
Quantifierがヒットした場所 xfooxxxxxx
fooがヒットした場所 foo
Reluctant
Quantifierがヒットした場所 xfoo
fooがヒットした場所 xxxxxxfoo
Possessive
Quantifierがヒットした場所 xfooxxxxxxfoo
fooがヒットした場所 なし
参考文献
2014年7月2日水曜日
MIBとSMIとASN.1
SNMP(Simple Network Management Program)を調べているとよく目にする
について整理してみました。
SNMPというプロトコルを表現するための記法といえる。
- MIB(Management Information Base)
- SMI(Structure of Management Information)
- ASN.1 (Abstract Syntax Notation One)
について整理してみました。
MIBとはなにか
SNMPで通信を行う際に、やりとりされるデータのこと。SMIとはなにか
MIBがどのような値(整数値、文字列等)を取れるか定義したもの。SNMPというプロトコルを表現するための記法といえる。
ASN.1とはなにか
SMIを定義したもの。SMIはASN1.1の一部、と表現されることもある。
ASN.1自体は古くからプロトコルの定義のために使われており、SNMP以外にも応用の範囲は広い。
まとめると
SNMPでは、MIBというデータがやりとりされる。
で、そのMIBを定義するために、古くからあるASN.1というものを用いて、SMIという記法を作った、という感じだろうか。
2014年6月8日日曜日
Spotifyを使ってみて思った。音楽にお金を出すということ。
Spotifyを使ってみた
音楽ストリーミングサービスのSpotifyを使ってみました。Spotify日本では消費者を置き去りにした業界の事情によりまだ使えないみたいですが、すでに全世界で2000万人を越えるユーザがいる大人気サービスです。
僕はいまアメリカにいるので、何も後ろめたいこともせずに普通に楽しめているわけですが、正直な感想としてこれがタダで使えるのは非常に革新的に思えます。
Spotify自体は日本にいることから知ってましたが、実はYoutubeやTuneInで音楽は聞いていたので、まあ同じようなもんだろうと思って大して期待してませんでした。
しかしながら使ってみると、使い勝手の満足度として大きな違いがあります。
理由としては大きく以下の2つがあげられると思います。
1. 配信している楽曲数が多い
2. 楽曲の選び方の自由度が高い
Spotifyでは1500万以上の楽曲があるようです。
音楽というのは気分や日によって聞きたいものが全く違ったりするので、やはりバラエティは非常に重要です。
またアーティスト・楽曲を選択して再生できます。
ときどきピンポイントで聞きたくなる曲があるので、これは個人的に非常に満足度が高いです。
加えてロック、ポップなど「ジャンル」で指定できるもはもちろんのこと、パーティやロマンティックなど「気分」で指定することも可能です。指定すれば、適当に選択された音楽がエンドレスに流れるのでなんとなく音楽を流したいときとしては最適です。
TuneInとかYoutubeよりも多数の楽曲の選択肢の中から、聴きたい曲をきめ細やかに選べるので、これも非常に満足度が高いです。
楽曲購入はiPhoneの有料アプリを買うくらい抵抗がある行為になる
無料でここまで音楽が自由自在に聴けることを考えると、今後はますます音楽を購入するハードルが高くなる気がします。CD不況は久しく騒がれてますが、かと言って多少安いデジタルデータならば売れるという状況にもならなそうです。
楽曲を購入するという需要は決してなくならないとは思いますが、お金を出すという意味において、配信サービスと楽曲の購入という関係は、iPhone(Andoroid)の無料アプリと有料アプリのような関係になるのではないかな、と想像しています。
つまりリスナーにとって配信サービスを利用するのはiPhoneの無料アプリを使うようなもので、逆に音楽を購入するというのは有料アプリを買うようなもの。
現在、多数の高品質な無料アプリが手に入るようになったがために、100円のアプリですら購入のハードルが高く感じる方は多くいると思います。同様に、今後は音楽にお金を出すのに非常に抵抗を感じる方が多くなってくるのではないでしょうか。特にCDを購入した経験がほとんどない若い人たちを中心に。
2014年6月5日木曜日
html5のcanvasで多角形を書いてみた
html5のcanvasタグを使って3角形,4角形,5角形といった多角形を書いてみました。
一応999角形まで表示できますが、これくらいの大きさだと50角形を超える辺りで全部円にしか見えなくなりますね。
一応999角形まで表示できますが、これくらいの大きさだと50角形を超える辺りで全部円にしか見えなくなりますね。
三角形
六角形
十二角形
五十角形
<!DOCTYPE html> <html> <head> <script language="javascript"> var radius = 150; var center_x = 200; var center_y = 150; function draw_polygon(){ var vertex = document.forms["vertex"]["quantity"].value; var a = 2 * Math.PI / vertex; var arr_x = []; var arr_y = []; for(var i= 0; i < vertex; i++){ var rad =(i + 1) * 2 * Math.PI / vertex; arr_x[i] = center_x + radius * Math.cos(rad); arr_y[i] = center_y + radius * Math.sin(rad); } var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); ctx.clearRect(0,0,canvas.width,canvas.height); ctx.beginPath(); ctx.moveTo(arr_x[0],arr_y[0]); for(var i= 1; i < vertex; i++){ ctx.lineTo(arr_x[i],arr_y[i]); } ctx.lineTo(arr_x[0],arr_y[0]); ctx.stroke(); } </script> </head> <body> <form id = "vertex" onsubmit="draw_polygon();return false;"> Specify the Quantity (between 1 and 999): <input type="number" name="quantity" min="1" max="999" placeholder="4"> <input type="submit"> </form> <canvas id="myCanvas" width="600" height="300" style="border:1px solid #c3c3c3;"> Your browser does not support the HTML5 canvas tag. </canvas> </body> </html>
2014年5月13日火曜日
Puppet Enterprise と Puppet Open Sourceの違い
オープンソースなIT自動化ソフトウェアとして有名なpuppetですが、2つのエディションがあるようです。
こうして比較してみると、結構Open Source版は機能が劣る印象があります。
Supportがないのはまあ当然としても、GUIとかVMwareのプロビジョニングぐらいはOpenSource版でも欲しい気がします。
ちなみにEnterprise版のお値段ですが10ノードまでは無料。
それ以降は下記のようになってます。
Standard Supportでもノード一台あたり100ドル前後ってかなり高い気がしますが、Enterprise版は売れてるんですかね。
Puppet Enterprise
- Graphical User Interface
- Event Inspector - Visualize Infrastructure Changes
- Supported Modules
- Provisioning - Amazon EC2
- Provisioning - Google Compute Engine
- Provisioning - VMware VMs
- Configuration management - Discovery
- Configuration management - User accounts
- Configuration management - Operating systems & applications
- 2,000+ pre-built configurations on Puppet Forge
- Orchestration - Task automation
- Role-Based Access Control - Now with external authentication support
- Unified cross-platform installer of all components
- Support - Option for 24 x 7 x 365
- Support - Defined SLA
- Certified by Puppet Labs engineers
- Pre-packaged dependencies in one directory
- Smooth upgrade and maintenance path
Puppet Open Source
- Provisioning - Amazon EC2
- Provisioning - Google Compute Engine
- Configuration management - Operating systems & applications
- 2,000+ pre-built configurations on Puppet Forge
こうして比較してみると、結構Open Source版は機能が劣る印象があります。
Supportがないのはまあ当然としても、GUIとかVMwareのプロビジョニングぐらいはOpenSource版でも欲しい気がします。
ちなみにEnterprise版のお値段ですが10ノードまでは無料。
それ以降は下記のようになってます。
ノード数 | Standard Support(1ノードあたり) | Premium Support(1ノードあたり) |
---|---|---|
1-10 | Download FREE | N/A |
11-99 | $112 | 要問い合わせ |
100-249 | $105 | $199 |
250-499 | $99 | $135 |
500-999 | $95 | $119 |
1000-2499 | $93 | $112 |
2500+ | 要問い合わせ | 要問い合わせ |
Standard Supportでもノード一台あたり100ドル前後ってかなり高い気がしますが、Enterprise版は売れてるんですかね。
参考
2014年5月10日土曜日
TomcatとFirefoxのCache設定
Tomcatなどを使ってWeb開発をしていると、htmlとかwarの修正が、ブラウザ上に反映されなくて悩むことがたまにある。
以前からちょくちょく悩んでは適当に対応していたのだが、この度本格的に困ったので方法を探してみた。
Webを探しても、あまり情報が無かったり、なぜか私の環境では有効でなかったりしたので備忘録としてまとめる。
Webを探した限りではデフォルトの設定では特にcache等してないようだった。
ただし、webapp配下にデプロイするwarを置き換えて、tomcatを再起動しただけだと、なぜかwebapp配下に自動unpackされるはずのディレクトリの内容が更新されていなかった。
そのため、warを置き換える前に、自動unpackされるディレクトリを削除しておいた。
例を上げて説明すると、example.warを配置する場合下記の手順で行った。
今回は、terminalでtomcatを起動してごにょごにょやっているんですが、Eclipseからtomcatを起動した時はここまで更新されない現象に悩まされなかった気がします。
Eclipseさんはなにか特別なことをやってるんだろうか。
以前からちょくちょく悩んでは適当に対応していたのだが、この度本格的に困ったので方法を探してみた。
Webを探しても、あまり情報が無かったり、なぜか私の環境では有効でなかったりしたので備忘録としてまとめる。
環境
OS Macbook Air 10.8.5 Mountain Lion
ブラウザ Firefox 28
サーバ Tomcat 7.0.52
Firefoxの設定
- ブラウザのアドレスバーにabout:configと入れる。
- 「動作保証対象外になります」とメッセージが出るので、「最新の注意を払って使用する」をクリックする。
- 「検索」にbrowser.cache.memory.enableを入力する。
- browser.cache.memory.enableが表示されるので、ダブルクリックをして設定値をdisableにする。
- 同様にbrowser.cache.memory.enableの値もdisableにする。
- Firefoxを再起動する
Tomcatでデプロイ時にやること
Webを探した限りではデフォルトの設定では特にcache等してないようだった。
ただし、webapp配下にデプロイするwarを置き換えて、tomcatを再起動しただけだと、なぜかwebapp配下に自動unpackされるはずのディレクトリの内容が更新されていなかった。
そのため、warを置き換える前に、自動unpackされるディレクトリを削除しておいた。
例を上げて説明すると、example.warを配置する場合下記の手順で行った。
- example.warを作る
- <tomcatのホームディレクトリ>/webapp/exampleディレクトリを削除
- example.warを<tomcatのホームディレクトリ>/webapp/example.warに配置
- tomcatの再起動
今回は、terminalでtomcatを起動してごにょごにょやっているんですが、Eclipseからtomcatを起動した時はここまで更新されない現象に悩まされなかった気がします。
Eclipseさんはなにか特別なことをやってるんだろうか。
2014年4月4日金曜日
java.lang.IllegalStateExceptionの対処法
JavaEE7のRemoteEndpoint.Async.sendText()を連続して呼び出すと、java.lang.IllegalStateExceptionがスローされることがあります。
これは、sendText()の処理が終わる前に、再度sendText()が呼び出されたためです。
sendText()の処理は非同期で行われるために、メソッドが戻ってきたとしてもメッセージ送信が終了している保証はありません。
これを防ぐためには、sendText()の戻り値であるFutre型のオブジェクトのget()を呼び出して、処理が終わるのを待つ必要があります。
これは、sendText()の処理が終わる前に、再度sendText()が呼び出されたためです。
sendText()の処理は非同期で行われるために、メソッドが戻ってきたとしてもメッセージ送信が終了している保証はありません。
これを防ぐためには、sendText()の戻り値であるFutre型のオブジェクトのget()を呼び出して、処理が終わるのを待つ必要があります。
public void sendMessageToBrowser(String message) { for (Session s : sessions) { Future future = s.getAsyncRemote().sendText(message); try { future.get(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2014年4月1日火曜日
websocketのサンプルコードをJavaEEで書いた
JavaEE7にはwebsocketを実装したパッケージ(javax.websocket)が提供されています。
これを使ったサンプルコードを書いてみました。
ブラウザから入力された数字の数だけ、適当な文字列(数字列)を接続している全ブラウザに表示するコードです。
まずはJava側。Servletクラスとかを継承しなくていいみたいです。
注意が必要なのは、RemoteEndpoint.Async.sendText()はFuture型のオブジェクトを返すということ。つまり、sendText()メソッドの処理は非同期で行われます。
sendText()はその仕様として処理が終わる前に、再び呼び出されるとjava.lang.IllegalStateExceptionを送出します。そのため、for文などを用いて繰り返し、メッセージを送りたい場合はFuture.get()などを用いて、処理が終わるのを待ってから次のメッセージを送るようにします。
続いてhtml。
参考文献
WebSocketをネタにJava EE 7正式版を試してみる
これを使ったサンプルコードを書いてみました。
ブラウザから入力された数字の数だけ、適当な文字列(数字列)を接続している全ブラウザに表示するコードです。
まずはJava側。Servletクラスとかを継承しなくていいみたいです。
注意が必要なのは、RemoteEndpoint.Async.sendText()はFuture型のオブジェクトを返すということ。つまり、sendText()メソッドの処理は非同期で行われます。
sendText()はその仕様として処理が終わる前に、再び呼び出されるとjava.lang.IllegalStateExceptionを送出します。そのため、for文などを用いて繰り返し、メッセージを送りたい場合はFuture.get()などを用いて、処理が終わるのを待ってから次のメッセージを送るようにします。
import java.util.Collections; import java.util.HashSet; import java.util.Random; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/endpoint") public class Main { static Set<session> sessions = Collections.synchronizedSet(new HashSet<session>()); @OnMessage public void onMessage(String message) { int count = Integer.valueOf(message); run(count); } @OnOpen public void open(Session sess) { sessions.add(sess); } @OnClose public void close(Session sess) { sessions.remove(sess); } public void sendMessageToBrowser(String message) { for (Session s : sessions) { Future future = s.getAsyncRemote().sendText(message); try { future.get(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private void run(int count){ Random rnd = new Random(); for(int i = 0; i < count; i++){ String s = String.valueOf(rnd.nextInt()); sendMessageToBrowser(s); } } }
続いてhtml。
<!DOCTYPE html> <html> <head> <title>WebSocketテスト</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script src="http://code.jquery.com/jquery-1.9.0.js"></script> <script type="text/javascript"> var socket; $(document).ready(function(){ var server="ws://localhost:8080/temporary/endpoint"; socket = new WebSocket(server); socket.onmessage = function(message){ $('#messageFromServer').append(message.data + "<br/>"); }; $('#send').click(function(){ var text = $('#msg').val(); socket.send(text); }) }); </script> </head> <body> <div id="messageFromServer"> input the integer</div> <input id="msg" type="text"/> <button id="send">go</button> </body> </html>
参考文献
WebSocketをネタにJava EE 7正式版を試してみる
2014年3月14日金曜日
Mac X 10.8.5(Mountain Lion)のEclipseでJDK7を利用する方法
Mac OS X上のEclipseでJDK7を入れる方法をググると情報が錯綜しているようなので私がやった方法をまとめてみた。
1. まずOracleのサイトからJDKを取得(*)
2. dmgを叩いてインストール。
3. Eclipse->Preference->Java->Installed JREを開き、下記の場所をJRE Homeに指定。
(*)Apple公式から落とすとの情報もあったが、ファイル名にバージョンの表記がなくよくわからなかったので、Oracleから落とした。
JDKのインストール場所がわからなくって結構困った。
またjava preferences というアプリが、/Utility/Application配下にあり、ここにパス情報が書かれているとの情報が良く出てくるが、最近ではなくなっているようだ。
参考文献。
MacにJDK7をインストールしたからといってAppleのJDK6を消してはいけない
java preferencesがない
1. まずOracleのサイトからJDKを取得(*)
2. dmgを叩いてインストール。
3. Eclipse->Preference->Java->Installed JREを開き、下記の場所をJRE Homeに指定。
Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/
(*)Apple公式から落とすとの情報もあったが、ファイル名にバージョンの表記がなくよくわからなかったので、Oracleから落とした。
JDKのインストール場所がわからなくって結構困った。
またjava preferences というアプリが、/Utility/Application配下にあり、ここにパス情報が書かれているとの情報が良く出てくるが、最近ではなくなっているようだ。
参考文献。
MacにJDK7をインストールしたからといってAppleのJDK6を消してはいけない
java preferencesがない
2014年3月7日金曜日
Jerseyのサンプルコード
JavaでRestfulなサーバを実装するためのライブラリであるJerseyを触ってみました。
アノテーションを利用することによって、URLとJavaのメソッドの呼び出しを簡単に実装できるようになってます。
アノテーションの使い方の詳細は公式ドキュメントがわかりやすいです。
参考文献
Chapter 3. JAX-RS Application, Resources and Sub-Resources
アノテーションを利用することによって、URLとJavaのメソッドの呼び出しを簡単に実装できるようになってます。
import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; @Path("sample/{samplePathParam}") public class Main { @Path("get1") @GET @Produces(MediaType.TEXT_PLAIN) public String get1( @DefaultValue("2") @QueryParam("testInt") int testInt, @DefaultValue("true") @QueryParam("testBoolean") boolean testBoolean, @DefaultValue("value") @QueryParam("testString") String testString) { return String.valueOf(testInt) + ";" + String.valueOf(testBoolean) + ";" + String.valueOf(testString); } @Path("get2") @GET @Produces(MediaType.TEXT_PLAIN) public String get2( @PathParam("samplePathParam") String param) { return param; } @Path("get3/{samplePathParamWithConstraint: [a-z]*}") @GET @Produces(MediaType.TEXT_PLAIN) public String get3( @PathParam("samplePathParam") String param1, @PathParam("samplePathParamWithConstraint") String param2) { return param1 + ":" + param2; } }これに対して色々アクセスしてみました。
http://localhost:8080/SampleServer/webapi/sample/foo/get1?testInt=10&testBoolean=false&testString=hoge =>10;false;hogeget1以下のクエリストリングがjavaの引数に渡されています。
http://localhost:8080/SampleServer/webapi/sample/foo/get2 =>foo@Path("sample/{samplePathParam}")で指定したsamplePathParamがget2の引数に引き継がれてます。
http://localhost:8080/SampleServer/webapi/sample/foo/get3/bar =>foo:bar@Path("sample/{samplePathParam}")で指定したsamplePathParamと@Path("get3/{samplePathParamWithConstraint: [a-z]*}")で指定したsamplePathParamWithConstraintがget3の引数に引き継がれてます。
アノテーションの使い方の詳細は公式ドキュメントがわかりやすいです。
参考文献
Chapter 3. JAX-RS Application, Resources and Sub-Resources
登録:
投稿 (Atom)