土曜日, 12月 31, 2016
2016年最初で最期の投稿
この一年も人に道をよく聞かれたけど、一時期ほどの頻度ではなくなった気がする。
twitterを見返すとアウトプットを増やしたいのと、電子工作なことをやりたいと書いていて、見事にその通りに活動してきたように思う。
正月あたりはRaspberry Piに接続された白黒液晶をGoで動かすために、i2cの簡単な使い方や、Goでのクロスコンパイルの仕方について調べていた。このあたりの成果は6月に開催されたPayPal Tech MeetupのDemo Nightの際に存分に生かされている。
また直接的ではないものの、Raspberry PiのGPIO制御に慣れたことは、11月のArt Hack Dayにおいて非常に役に立った。
Demo NightにしろArt Hack Dayにしろ、会社の外で、特に評価がつくイベントに参加することは、緊張感を強いられるけど、地力をブーストしていくにはうってつけと思う。適度な危険に身を晒さないと成長実感を得るのは難しい。
夏には小規模ながらGoの勉強会を主催したりして、今更感はあるもののGoの布教活動もした。とはいうものの、Goを教えるという点では社外より社内の方が頻繁にあり、先生役が求められている気がしないでもない。
一方で、今年は応用というか実装面での学習、成長にパワーが割かれすぎて、もう少し基礎的な知識や、新しい言語を覚える時間が削られてしまった気もする。
夏ぐらいにCommon Lispに挑戦しようとしていたのだけど、Common Lisp自体はともかく、Emacsの半強制(一応elvisは入れたのだけど)に閉口してしまい、結局起動自体が億劫になってしまった。
他にもOCamlやRustを時々触ってはいたのだけど、イベント発表用の制作に追われて、結局まともには出来なかった。
プログラミング言語に限らず、自然言語についても、英語というか英会話を要求されるケースが仕事だけでなくプライベートでも何故か増えてきていて、そろそろ本格的に開始しないと日常生活でストレスがたまるようになってきている(弊社は英語公用語とかではない)。
一応、英会話学習サービスをやってはいたのだけど、やはり制作が佳境に入るとそれどころではなくなってしまった。
ということで、来年はもう少し基礎的な学習と英会話に力を入れていきたいなと思っている。
このブログももう少し活用するか。もしくはmediumあたりに引っ越すか。
bloggerはいつになったら公式でMarkdownをサポートするのだろう。
日曜日, 12月 27, 2015
LLDBでGoのデバッグをする その2 test編
その1ではgo buildしたLLDBでデバッグしてみましたが、その2ではgo testでのdebugの仕方です。
今回のデバッグ対象として以下のようなプログラム(main.go)とそのテスト(main_test.go)を書きました。
package main
import "fmt"
func RectArea(w, h int) int {
return w * h
}
package main
import "testing"
func TestRectArea(t *testing.T) {
s := RectArea(6, 8)
if s != 48 {
t.Error("error")
}
}
testをビルドする
go test には -c というオプションがあり、これを使うとtestのバイナリをビルドできます。これと -gcflags を組み合わせてデバッグ可能なtestバイナリを作ります。
# -o は出力結果のファイル名を指定するオプション
$ go test -c -gcflags '-N -l' -o test
# 実行するとgo testと同じ結果が得られる
$ ./test
PASS
デバッグする
後は、前回同様lldbに、このtestバイナリを渡すだけです。
$ lldb test
(lldb) target create "test"
Current executable set to 'test' (x86_64).
(lldb)
せっかくなのでプログラム本体とテスト両方にブレイクポイントを置いてみます。
(lldb) target create "test"
Current executable set to 'test' (x86_64).
(lldb) b main.go:4
Breakpoint 1: where = test`_/Users/kmtr/misc/c.RectArea + 9 at main.go:6, address = 0x000000000007dc09
(lldb) b main_test.go:7
Breakpoint 2: where = test`_/Users/kmtr/misc/c.TestRectArea + 58 at main_test.go:7, address = 0x000000000007dc5a
(lldb)
後は run するだけです。
main.go側で止まったところで、変数を確認。
3 func RectArea(w, h int) int {
4 return w * h
5 }
(lldb) fr v
(long) w = 6
(long) h = 8
(long) ~r2 = 0
(lldb)
c で継続して、次はmain_test.goで止まるので、こちらでも変数を確認。
そして c で終了させます。
4
5 func TestRectArea(t *testing.T) {
6 s := RectArea(6, 8)
-> 7 if s != 48 {
8 t.Error("error")
9 }
10 }
(lldb) fr v
(long) s = 48
(testing.T *) t = 0x000000c82009c000
(lldb) c
Process 25022 resuming
PASS
Process 25022 exited with status = 0 (0x00000000)
(lldb)
まとめ
-c オプションでtestバイナリを作るのがポイントで、後は前回と同じですね。
また続くかもしれない。
土曜日, 12月 26, 2015
LLDBでGoのデバッグをする その1
環境
環境は以下の通りです。
- OS X El Capitan 10.11.2
- lldb-340.4.119
- go version go1.5 darwin/amd64
LLDBはxcodeについてきたものです。
やってみる
デバッグ対象のコードは、やっつけですがこんな感じです。ファイル名はmain.goとしました。
package main
import "fmt"
func main() {
a := 10
fmt.Printf("Add1(10) == %d\n", Add1(a))
}
// Add1 ...
func Add1(i int) int {
return i + 1
}
LLDBでデバッグするために、-gcflags '-N -l' というオプションをつけてビルドします。
$ go build -gcflags '-N -l' main.go
$ ls
main main.go
lldbコマンドに作成されたファイルを渡してみます。
$ lldb main
(lldb) target create "main"
Current executable set to 'main' (x86_64).
(lldb)
ここから先はlldbの操作になります。とりあえず q と打つとlldbから抜けられることだけは覚えてください。
デバッグを開始するには run または r と打ちます。
(lldb) run
Process 22044 launched: '/Users/kmtr/misc/c/main' (x86_64)
Add1(10) == 11
Process 22044 exited with status = 0 (0x00000000)
(lldb)
無事実行されました。
これではデバッグにならないので、ブレイクポイントを打ちます。
試しに6行目と12行目に打ってみます。ブレイクポイントを打つ方法はいくつかありますが、行番号指定の場合 b ソースファイル名:行番号 が楽です。
(lldb) b main.go:6
Breakpoint 1: where = main`main.main + 31 at main.go:6, address = 0x000000000000205f
(lldb) b main.go:12
Breakpoint 2: where = main`main.Add1 + 9 at main.go:12, address = 0x0000000000002199
(lldb)
br list もしくは b と打つと、現在のブレイクポイントの一覧が表示されます。
br delete ブレイクポイント番号 で対象のブレイクポイントを削除できます。
2番のブレイクポイントを消した後、再度ブレイクポイントを一覧表示して削除されることを確認してみてください。
ブレイクポイントを打った状態で再び run すると、こんな感じに止まります。
3 import "fmt"
4
5 func main() {
-> 6 a := 10
7 fmt.Printf("Add1(10) == %d\n", Add1(a))
8 }
9
(lldb)
ステップオーバーは n です。打ってみるとカーソルが7行目に移ると思います。fr v 変数名 で変数の中身を表示できるので打ってみます。
(lldb) fr v a
(long) a = 10
(lldb)
なぜかlongですが、とりあえずの確認には十分です。
現在のブレイクポイント周辺のソースを再表示する場合は f と打ちます。
ソースレベルでのステップインは s です。7行目で実行すると次のようにAdd1関数の中に入ると思います。
9
10 // Add1 ...
11 func Add1(i int) int {
-> 12 return i + 1
13 }
(lldb)
fr v と変数名を指定せずに打つと、現在のフレームの変数が全て表示されます。
(lldb) fr v
(long) i = 10
(long) ~r1 = 0
(lldb)
(~r1 てなんだろう、、、)
c と打つと、次のブレイクポイント、もしくは終了するまで停止せずに実行されます。
(lldb) c
Process 22103 resuming
Add1(10) == 11
Process 22103 exited with status = 0 (0x00000000)
(lldb)
LLDBの使い方は、The LLDB Debugger のgdbとの比較がわかりやすいです。
watchが効かないという話も見かけましたが、少なくとも私の環境では問題ありませんでした。
感想
少ししか使っていないので、どこまでちゃんと動くのか、GDBとの差がどれだけあるかわからないけど、LLDBでもデバッグはできそうです。
続くかもしれない。
水曜日, 5月 13, 2015
Goで書かれたHTTP2対応サーバーCADDYを試す
CADDYというHTTP2対応サーバーを見つけたので簡単に使ってみる。
Goで書かれていて、インストールは各プラットホーム毎のバイナリを適当に配置するだけなので、インストールが楽。
起動する
とりあえずPATHが通っているところに置いて叩いてみる。
$ caddy
0.0.0.0:2015
デフォルトポートが2015なのだけど、来年になったら2016になるのだろうか。
curlで叩いてヘッダを見てみる。
$ curl -I localhost:2015
HTTP/1.1 404 Not Found
Server: Caddy
Date: Tue, 12 May 2015 13:52:04 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8
404だけど、とりあえず動いている。HTTP2はHTTPSじゃないと動かないので、HTTP/1.1なのが残念。
(CADDYのサイトはCADDYでホストされていて、HTTP2が使われていることをブラウザで確認しています)
設定を変える
CADDYの設定ファイルは-confオプションで任意のファイルを指定出来るけど、デフォルトではコマンドを叩いたディレクトリにあるCaddyfileを見るらしい。
1行目には必ずlistenするアドレスを書けということなので、ポートをデフォルトから変更したものを記述してみる。
$ echo "localhost:2016" > Caddyfile
$ caddy
localhost:2016
無事ポート2016で起動した。
ちなみに1行目と言うのは、設定値としての1行目で、コメントは普通にかける。コメントはapacheと同様、#から始まる文字列。
静的ファイル配信
このままでは404を返すだけのサーバーなので、とりあえず静的ファイルを返すようにしてみる。
まずはドキュメントルートとなるディレクトリを作り、適当なHTMLファイルを置く。
$ mkdir public_html
$ echo "Hello, world" > public_html/index.html
次にCaddyfileをこんな感じに編集する。
rootはドキュメントルートを指定するディレクティブ。カッコでくくらずに、そのまま次の行に書いてもよいのだけど、書いた方が分かりやすいと思う。
# example
localhost:2016 {
root ./public_html/
}
再起動して、curlしてみる。
$ curl localhost:2016
Hello, world
無事、静的ファイルを配信出来た。
markdown配信
リダイレクトやプロキシといった機能の他に、CADDY特有の機能がいくつかある。その一つのmarkdown配信機能を使ってみる。
設定はmarkdownディレクティブの後ろに、適用するPATHを記述する。このPATHはリクエストのパスであると同時にmarkdownファイルを置くディレクトリの事も示すらしい。
つまりmarkdownファイルは、public_html/blogではなくblogというディレクトリに置く必要がある。
# example
localhost:2016 {
root ./public_html/
markdown /blog
}
次に記事を作る。
$ mkdir blog
$ echo "# Hello, markdown" > blog/hello.md
curlで確認。
$ curl localhost:2016/blog/hello.md
<!DOCTYPE html>
<html>
<head>
<title># Hello, markdown</title>
<meta charset="utf-8">
</head>
<body>
<h1>Hello, markdown</h1>
</body>
</html>
ソースを確認したところ、markdownの処理には https://github.com/russross/blackfriday を使っていた。
性能
性能について、FAQには正確に答えるのは難しいとしながら、概ね速いと書いてある。
Apacheよりは速くて、nginxよりちょっと遅いぐらいという事なので、実用上十分な速さと思っておけば良さそう。
CADDYのコンセプトは設定が簡単なWebサーバなので、それでよいのだと思う。
感想
全部の機能を試したわけではないけど、1バイナリで完結して、設定が簡単に出来て、それなりに速いという使い勝手の良いサーバーという印象。
ちょっとしたサーバーが欲しいのだけど、Apacheやnginxだと大げさ過ぎるとか、設定が面倒という時に重宝しそう。
あとはfcgiを使ったプロジェクトのルートディレクトリにCaddyfileを置いといて、git cloneしてcaddyと叩いたら開発環境として立ち上がるみたいな使い方とか。
小回りが効くので、応用範囲が広そう。
水曜日, 4月 29, 2015
Goのtesting/quickを簡単に触ってみる
Goのpackageを眺めていたら、いまさらtesting/quickというものを見つけたので試してみる。
quickの説明をみると、
help with black box testing
と書いてあるし、Checkという関数があったりするので、QuickCheckのためのパッケージのはず。
普通のtest
まずはquickを使わないテスト。
2つのintで割り算をする関数のテスト。
package main
import "testing"
func div(n, m int) int {
return n / m
}
func TestDiv(t *testing.T) {
if div(4, 2) != 2 {
t.Error("error!")
}
if div(3, 2) != 1 {
t.Error("error!")
}
}
明らかにテストケースが不足してる。
とりあえず第二引数に0を入れたら、確実にエラーになるはずだけど、このケースでは考慮されていない。
quick導入
というわけでquickを導入。
package main
import (
"fmt"
"testing"
"testing/quick"
)
// テスト回数計測のためのカウンター
var count int = 0
func div(n, m int) int {
count++
return n / m
}
func TestDiv(t *testing.T) {
f := func(x, y int) bool {
return div(x, y) == (x / y)
}
if err := quick.Check(f, nil); err != nil {
t.Error(err.Error())
}
fmt.Printf("テスト回数:%d\n", count)
}
これでgo testしてみる。
$ go test
テスト回数:100
PASS
ok quick_example 0.005s
通ってしまった。
quickというかQuickTestは要はとにかくいろいろ試してみるテストなので、100回程度では通ってしまうのも仕方ない。
今回の場合、第二引数に0が入るのは 「1/intの取りうる値」 なので、試行回数が全然足りない。
試行回数を変える
ということで試行回数を増やしてみる。
試行回数の設定をするには、quick.Configのインスタンスを作って、
MaxCountに試行回数を設定する。
package main
import (
"fmt"
"testing"
"testing/quick"
)
var count int = 0
func div(n, m int) int {
count++
return n / m
}
func TestDiv(t *testing.T) {
f := func(x, y int) bool {
return div(x, y) == (x / y)
}
config := &quick.Config{
MaxCount: 10000,
}
if err := quick.Check(f, config); err != nil {
t.Error(err.Error())
}
fmt.Printf("テスト回数:%d\n", count)
}
そしてgo testする。
$ go test
テスト回数:10000
PASS
ok quick_example 0.015s
通ってしまった。
math.MaxInt64を突っ込めば確実に失敗するのだろうけど、
それは最早ブラックボックステストではない。
(ちなみにMaxInt64でやってみたら、手元のマシンでは3分経っても終わらなかった)
テスト値を制御する
とはいえ、0ぐらい入れて欲しい。
ブラックボックステストと言っても、本当に無作為に値を突っ込むのではなく、とりあえず0から100と、0から-100で動いてもらえば最低限の仕様は満たすという事はままある。
そのように、自分でランダム性を制御するにはconfig.Valuesに値を生成する関数を設定する。
値を生成する関数はfunc(args []reflect.Value, rand *rand.Rand)というシグネチャを持つ。
argsには、quick.Check関数の第一引数に渡す関数の引数の数のサイズを持つスライスが渡されてくる。
今回のケースで、0から100の範囲の値を無作為に選ばせるのであれば、こんな感じの関数をconfig.Valuesに渡せば良い。
func(args []reflect.Value, rand *rand.Rand) {
args[0] = reflect.ValueOf(rand.Intn(100))
args[1] = reflect.ValueOf(rand.Intn(100))
}
これで10000回くらい回せば、流石にargs[1]に0が入るはず。
第二引数のrandは見ての通りRandインスタンスが渡ってくる。このRandインスタンスはconfig.Randで設定しないとデフォルトのものが使われるのだけど、go 1.4.2ではデフォルトのrandはseedが0固定されているので、変えないと毎回同じ結果になる(デフォルトのseed=0は意図したものなのか?)。
上記を踏まえるとこんな感じのソースになる。
package main
import (
"math/rand"
"reflect"
"testing"
"testing/quick"
"time"
)
var count int = 0
func div(n, m int) int {
count++
return n / m
}
func TestDiv(t *testing.T) {
f := func(x, y int) bool {
return div(x, y) == (x / y)
}
config := &quick.Config{
MaxCount: 10000,
Rand: rand.New(rand.NewSource(time.Now().UTC().UnixNano())),
Values: func(args []reflect.Value, rand *rand.Rand) {
args[0] = reflect.ValueOf(rand.Intn(100))
args[1] = reflect.ValueOf(rand.Intn(100))
},
}
if err := quick.Check(f, config); err != nil {
t.Error(err.Error())
}
fmt.Printf("テスト回数:%d\n", count)
}
これでgo testすると、まず確実にこんなかんじで0除算エラーが発生する。めでたしめでたし。
— FAIL: TestDiv (0.00s)
panic: runtime error: integer divide by zero [recovered]
panic: runtime error: integer divide by zero
まとめ
今回はquickを使うというのが目的なので、こんな感じのテストコードになったけど、実際には普通のテスト(=ホワイトボックステスト)と組み合わせて使うはず。
ブラックボックステストはホワイトボックスを置き換えるものではないし。
といっても、特に難しく考えず、とりあえず値を大量に作ってくれる装置として使うだけでも恩恵があるかと。今回のように完全な無作為ではなく、そこそこの無作為にしてグレーボックスとして使ったりとか。
quick固有の問題として、デフォルトのRandがseed=0で初期化されているという点だけは要注意ですね。
火曜日, 2月 17, 2015
Firefox OS WoTハッカソンに参加してきた
2/14-15に行われたFirefox OS WoTハッカソンに参加してきました。
申し込んだ時点で70名の募集のところ既にいっぱいで、補欠でしたが数日でキャンセルが何人か出て、繰り上がりで参加出来ることになりました。
終わった今、改めて見てみたら50位ぐらいまで繰り上がっていたので、こういうのは諦めたら駄目ですね。
アイディア出し
初日は企画コンセプトやWoTの概要説明から始まり、アイディア出し&チーム決め。
最終的には、以下にまとめられているものを作ったわけですが、
http://fabble.cc/dobogo/pitagoraxsugoroku
いきなり「要件定義」に書いているような立派な案が出たわけではなく、お菓子を食べつつ、光らせたいとか、NFCを使いたいという、使いたいものベースで、だらだらとホワイトボードに書き付けていました。
途中でゲーム的なものや動きのあるものが作りたいという流れになり、すごろくというアイディアと、ピラゴラ装置というアイディアが発生。それらを組み合わせた「いろんな仕掛けのあるすごろく」になっていきました。
当初、そのすごろくはFx0を持った人間が歩き回るという話になっていましたが、どこかのタイミングでロボット(この時点ではふぉくすけの存在は無く、車に乗せたFx0)に移動させるというアイディアが出てきて「すごろく」と「ロボット」の二本立てとなりました。
最終的に、今回はインパクト重視で「ロボット」に注力し、すごろくは余力があったらという方針になりました。
ふぉくすけが採用された経緯が記憶からすっぽり抜けていますが、他のチームの方から、ふぉくすけのヌイグルミをお借りして、ふぉくすけロボという具体的なゴールが昼食前には完成しました。
設計と実装
ロボットの方針が出たあたりで、ざっくりとどういうモジュールが必要かと、それぞれの間のやり取りの図を書き、それを元に役割分担をしました。私はhttpd.jsを使って、Fx0内で動くサーバーの実装を担当しました。
ふぉくすけロボは、足と尻尾はmbedで制御されているのですが、その指示はFx0からHTTPで、mbed側のサーバに飛ばしています。私が作ったのは、リモコン側からのリクエストをそのままmbedに飛ばす、一種のリバースプロキシというわけです。
とりあえずリモコン担当の方、mbed担当の方と、ざっくり必要そうなAPIを相談し実装。
mbedが制御しない、表情制御については、並行で作られている表情プログラムを呼び出す事になるので、一旦呼び出し口だけを用意しておきました。
実装そのものは大したことが無いのですが、テストが大変でした。ひとまずFx0にインストールしてみて動かしてみたのですが、デバッガにソースが出てこない。console.logも出てこない(確か)。
仕方がないので、FirefoxのWebIDEからシミュレータを起動して、そこでテストしようとするも、バージョン2.1以降(unstable)でないとやはりソースが出てこない。
やっとデバッガにソースが出たと喜んだものの、コールバック内のブレイクポイントは機能しないという状況でした。Flame(fxos ver3.0?)をお借りして試してみたところ、シミュレータと同様の状態だったため、console.logでプリントデバッグで進めました。
また、初日はネットワーク環境が不安定で、テスト用の無線LANになかなか繋げられず、mbed側サーバーとの疎通が安定して出来るようになったのは2日目になってからでした。
今回のハッカソンはWoTがテーマになっていますが、実際のWoTの現場でも、安定したネットワークの確保は課題になると思います。
もう一つ大変だったのは、現状のhttpd.jsのパスHandler機能、要するに静的ファイル配信の機能がFx0でしか動作しないことでした。リモコンは静的なHTML+JSで書かれているので、これをロボット側のFx0から配信しようとしたのですが、シミュレータやFlameでは動作せず、しばらくたってFx0で試して、初めて機能する事を確認しました。
そんな新しい端末やOSらしいトラブルに見舞われながらも、何とかかんとか、ふぉくすけロボを完成させることができました。
デモ
完成したふぉくすけロボですが、mbed側は、mbed + 無線LANモジュール + モーター*3という構成のため、非常に電気を使うようです。さらに、それを乾電池だけで動かしているため、下手に動かすとデモが出来ない恐れがありました。
それでも、デモの数分間、ふぉくすけロボは頑張ってくれて、おかげさまでARM賞もいただけました。
Firefox OSに対する感想
Firefox OSは使う分には、特に問題無いレベルにあると思います。少なくともFx0やFlameは、スマートフォンとして普段使うのに必要十分な動作をしていました。
開発環境も、若干使いづらいところはあるものの、今後改善していくだろうし、何よりWeb技術でアプリが作れるという特性は、潜在的な開発者数が、AndroidやiOS以上に存在しているという事を示しています。httpd.jsは普通のアプリにはまず使われないと思われるので、今回ほどの苦労はしないはずです。
また今回はFirefoxOSの端末としてFx0を使うことが、ある種の縛りとしてあったのですが、他のチームの発表を見ると、Fx0ではなくOpen Web Boardを主にしたものもいくつかあり、スマートフォン以外の応用可能性が、Firefoxには十分あるように思います。
ふぉくすけロボの発展
ふぉくすけロボはその動きをリモコンに頼っていたわけですが、リモコンからは単にHTTPでリクエストを飛ばしているだけです。つまりロボのFx0にHTTPに投げられる存在であれば、何であれふぉくすけロボを操作出来るわけです。
今回作れなかったすごろく側ですが、センサーとHTTPクライアントを埋めた壁、床を作ることで、すごろくがふぉくすけを動かす仕組みなんかが作れると、わけがわからなくて良いと思います。
それと懇親会の最後で、ふぉくすけが出た状態のFx0をお面のようにしたのですが、カメラAPIと組み合わせて、任意の顔をお面にするアプリが作れるなあと考えていました。
問題はFx0もFlameも持っていないことなわけですが。
今後機会があれば作りたいです。
金曜日, 1月 23, 2015
天丼屋さんを教える
木曜日, 1月 22, 2015
CarthageでGPUImageをSwiftから使う
CarthageでGPUImageをSwiftから使う
XCodeで何かを作ろうとした場合、標準のframework以外のフレームワークを導入したくなります。
Objective-Cな時代にはCocoaPodsを使うのが定石だったようですが、Swift時代にはどうしたらよいのか、調べていたところCarthage(カルタゴ)というものが存在している事を知りました。
https://github.com/Carthage/Carthage
備忘録代わりに、Carthageを使ってGPUImageを使ったアプリケーションを作成してみます。Carthageの使い方の説明を主とするため、アプリケーション自体は、GPUImageのSimpleSwiftVideoFilterExampleをそのまま用います。
https://github.com/BradLarson/GPUImage/tree/master/examples/iOS/SimpleSwiftVideoFilterExample
0. 前提
この記事はYosemite 10.10.1、XCode 6.1.1の環境で確認しています。
Carthageは0.4.0を使いました。
実機確認する場合、実機とiOS Developer Programへの登録が必要となります。
1. Carthageのインストール
CarthageのREADME.mdを読めば分かりますが、pkgファイル、Homebrew、makeの3通りの方法でインストール出来ます。
私はHomebrewでbrew install carthageしました。
2015-01-19時点で、Homebrew経由でインストールされるのは0.4.0でした。
pkg版は0.5.2まで上がっており、いろいろ機能が追加されているようですが、今回の場合は0.4.0の機能でも問題ありません。
2. XCodeプロジェクトを作る
ベースとなるXCodeプロジェクトを作ります。
XCodeを開き、”File -> New -> Project…”を選択し、”iOS -> SingleView Application”を選んで、Nextを押します。
次の画面でLanguageにSwiftを、DevicesにiPhoneを選びます。
他の項目は適当に入力してください。
Nextを押すと、プロジェクトの保存場所を選択すると、新しいプロジェクトが作られ、XCodeに表示されます。
3. Cartfileの作成
Cartfileを作成します。Cartfileは利用したいframeworkを記述したファイルです。npmのpackage.json、mvnのpomです。
作成したプロジェクトのディレクトリの*.xcodeprojと同じ階層に、Cartfileという名前のテキストファイルを作成し、以下のように記述します。
git "https://github.com/BradLarson/GPUImage.git" == 0.1.6
==の部分には他にも~>と>=が使えます。バージョンはsemverで解決されるそうです。
対象となるリポジトリがgithubの場合、以下のような指定も出来ます。
github "BradLarson/GPUImage" == 0.1.6
4. 外部フレームワークのビルド
ターミナルで、プロジェクトのディレクトリに移動します。
その位置でcarthage updateを実行すると、対象のgitリポジトリからソースコードがプロジェクトディレクトリ下の”Carthage/Checkouts/”以下にコピーされます。
その後、ビルドが実行され、生成物がプロジェクトディレクトリ下の”Carthage/Build”以下に出力されます。
5. 外部フレームワークをプロジェクトに追加
XCodeのプロジェクトのTARGETSのGeneralタブにある、”Linked frameworks and Libraries”に、”Carthage/Build/iOS/GPUImage.framework”をドラッグアンドドロップします。
次にBuild Phasesタブの左上にある”+”を押し、”New Run Script Phase”を選びます。
Shell欄の下の”Type a Script or …”と書いてある欄に、/usr/local/bin/carthage copy-frameworksと入力し、”Input Files”に$(SRCROOT)/Carthage/Build/iOS/GPUImage.frameworkと入力します。
ここまででCarthageを使って、frameworkを導入するという作業は終わりました。後はコードを書いていくだけです。
6. コードを書く
ここからはCarthageとは関係なく、GPUImageViewの使い方になります。
最初にMain.storyboardを開き、ViewのCustom ClassをGPUImageViewに変更します。
次にViewController.swiftの内容を、SimpleSwiftVideoFilterExampleのそれと同じにします。
7. 実行する
iPhoneに転送して実行してみましょう。
8. 感想
他言語における依存性解決ツールに比べると、やっていることは単純ですが、XCodeというGUIが大前提のツールと共存させようとすると、これが正解な気がします。
CocoaPodsとは違い、ソースのダウンロードとビルド以上の事は一切行わないところも、個人的には好感が持てます。
Bridging Headerもいらないので、これからは積極的に使っていき、かつ自分でライブラリを書くときも、*.frameworkを吐くように作ろうと思います。
水曜日, 12月 24, 2014
bingから画像をダウンロードしてMacの壁紙に設定するツールをGoで書いた
bingから画像をダウンロードしてMacの壁紙に設定するbinggoというツールをGoで書きました。
https://github.com/kmtr/binggo
go get github.com/kmtr/binggoでインストール出来ます。
なぜ書いたのか
私はbingのトップページの画像が大好きで、Windowsの壁紙はBingデスクトップを使って、毎日bingの画像が設定されるようにしています。 ですが、残念ながらBingデスクトップのMac版は存在していません。 無いのであれば作るしかないということで、書いてみました。 ちなみに同じことを考える人はそれなりにいるようで、Pythonやbash、Automatorによる同様のツールがすでに存在しています。 ただインストールが面倒であったり、私の環境での動作が不安定だったりと不満があったので、自分で書いてみました。
なぜGoなのか
Goが好きだからというのが最大の理由です。
前々からGoには少し触っていたのですが、Go Conference 2014 autumnに参加して自分の中のGo熱が高まり、何か書きたいけど丁度良いネタが無いかと考えていたところ、bingに「今日の画像をダウンロード」という機能が追加され、Goで書いてみようと思いつきました。 ただ機能が単純なのでchannelどころかgoroutineすら使わなかった(使うまでもなかった)のが少し残念でした。
おまけ
bingの過去画像が欲しい場合は、Bing Homepage Galleryが便利です。
土曜日, 7月 12, 2014
gulp-coffeekupを作りました
どういう意味かは、公式サイトのデモを見ればなんとなく分かるかとおもいます。
coffeekup.org
テンプレート言語としてはなんとなくjadeっぽいので、coffee-scriptを知らない人でも使うのは難しくないはずです。テキストをいちいちクォートしているあたりにcoffee-scriptそのものを使っている感が見て取れるかと思います。
さて、これを静的HTMLの生成に使おうとしたのですが、CLIなコマンドは提供されているもののうまく動作せず、gulp経由でやってみようかと思ったら、今時にしては珍しくgulpプラグインがありませんでした(検索力不足かも)。
想像ですが、サーバサイドでレンダリングするために利用する事がメインで、静的HTML出力はあまり力を入れていないのかな?
ということでgulpプラグインを書いてみました。
https://github.com/kmtr/gulp-coffeekup
手元ではとりあえず、動作を確認しています。
(テンプレート言語がcoffee-scriptそのものである利点って何か実はよく分かっていない)
火曜日, 6月 10, 2014
丁目番地を聞かれたのでiPhoneで道案内
水曜日, 3月 05, 2014
都庁への入り方
火曜日, 2月 25, 2014
2014年初道案内
木曜日, 10月 17, 2013
小規模企業によるFOSS開発のモデルケースになりうるかもしれない事例
そんな中で、今注目している一連の流れ。
そもそもの発端。20人/月のほとんどはサーバではなくアプリ側に使われているのだろうか。
世界初 メッセンジャーアプリ「Spika」を完全オープンソースで公開、フロントからバックエンドまで提供
問題点の指摘。最大の問題点である問題が指摘されているDBの部分は2013/10/16時点のコミットで完全に消去されているみたい。
レガシーズ公式ブログ PHPerはSpikaのどこを見たのか?
そして有志?によって直す会が開かれるらしい。
spika hackathon Spika-Server のコードを15年ぐらい若返らせます
行数は少なめでやる事は決まっているので、設計上の問題が無ければ割と簡単なはずだけど、モダンになった後のコードで、元の企業の人たちがカスタマイズやコンサルティングができるようになるのかが気になるところ。
一番最初の記事中で、WordPressのような存在になりたいと言っているけど、WordPressもこんな問題を抱えている。せめてPSRの議論と投票には参加して欲しい。
レガシーズ公式ブログ 活動報告 「レガシーコード in WordPress」
日曜日, 8月 04, 2013
夏の日に道を教える
以前、外国人の方に聞かれたのと同じホテルの場所を聞かれる。有名なホテルなのかな?
今回は歩道沿いにある地図でご案内。
道を聞かれたのは日本人だけど、最近外国人観光客が多い印象。それもどうみてもお金の無さそうな大学生っぽい人たちとか、割と普通な家族。
円安の効果?
水曜日, 6月 05, 2013
exportsされていないjavascriptやCoffeeScriptのメソッドをテストする。
exportsされていないjavascriptのメソッドをテストするにはどうするか。
理屈としては、以下の記事2つを読めばOK。要はjsを別コンテクストにロードして、その時にプライベートなメソッドを引き上げるわけですね。
Testing Private State and Mocking Dependencies
Nodeでプライベートな(exportsされてない)メソッドのテスト
不思議なのは、どちらの記事でも、node-mocksに触れられていない事(一番目の記事のコメントで触れられているだけ)。
これは一番目の記事のvojtajinaさんが作ったライブラリで、ざっくり言えば、解説されている内容が実装されているテスト用のライブラリ。
これを使えば、いちいち上記解説記事のコードを書かなくて済むわけですね。めでたしめでたし。
と思ったのですが、このライブラリ、JSは読み込めるのだけど、CoffeeScriptを直接読み込むことが出来ません(README.mdのサンプルテストコードはCoffeeScriptで書かれているのに)。
もちろん、JSにコンパイルすればよいのだけど、面倒なわけです。
というわけで、CoffeeScriptをロード出来るようにしてみました。
https://github.com/kmtr/node-mocks/tree/coffeescript
npmでインストールするなら、以下を実行。
npm install https://github.com/kmtr/node-mocks/tarball/coffeescript --save-dev
可能な限りCoffeeScriptから出ずに、開発したい私みたいな人におすすめです。
一応、pull requestを送ってみたけどどうだろうか。
オリジナルと比べてcoffee-scriptへの依存が増えてしまったし。
土曜日, 5月 04, 2013
木曜日, 12月 27, 2012
DBFluteをCDIで使ってみる(適当レベル)
でもJavaEEの一部という事は、JavaEE対応のアプリケーションサーバなら追加のjar無しで使えるわけで、依存するjarを減らせるわけです。
一方、これまた当たり前になったORMはhibernateの方向性が主流になりつつあるも、やはりSQLで書きたいケースも多く、というかRDBに問い合わせるならSQLを使うのが結局楽だったりするわけです。とはいえSQLを生で書くというのもあり得ないですよね。結果はオブジェクトとして受け取りたいし。という要求の元、DBFluteやDomaやMyBatisと言った、SQLと親和性のあるORMも根強い人気があります。
私も基本的にはSQL大好きなので、これらのORMを使いたいわけです。
DIは標準規格のCDIを、ORMは手のひら返してJPA系ではないものを使うわがままをなんとか実現したい。で、幸いにもDBFlute、Doma、MyBatisのどれもCDIで使えるらしい。という事でDBFluteでCDIを試してみました。DBFluteを選んだ理由は最近1.0になったからというだけ。
ちなみにCDIも初体験。
とりあえず深く考えずに、GlassFish 3.1.2 + DBFlute 1.0 + Derbyでやってみる事に。
DBFluteのドキュメントを見つつ、以下のようにセッティング。
mvn dbflute:download
mvn dbflute:create-client
#DDLを書いたり、dfpropを編集したり
mvn dbflute:replace-schema
mvn dbflute:jdbc
mvn dbflute:generate
これでDBFlute側の準備は完了。WEB-INFの下にbeans.xmlという名前のファイルを置いて、CDIを有効にする。これで設定はすべて完了したはず。という事でGlassFIshに乗せてみると、こんなエラーが発生。
デプロイメント中にエラーが発生しました: Exception while loading the app : Exception List with 5 exceptions:
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [EmployeeBhv] with qualifiers [@Default] at injection point [[field] @Inject com.example.servlet.EmployeeServlet.employeeBhv]. Possible dependencies [[Managed Bean [class com.example.dbflutecdi.db.exbhv.EmployeeBhv] with qualifiers [@Any @Default], com.example.dbflutecdi.db.allcommon.DBFluteModule$DBFluteBean@422f8df7]]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:314)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:163)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:382)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:380)
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:199)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:313)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:461)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:389)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:348)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:363)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1085)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:95)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1291)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1259)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:461)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:212)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:179)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
at com.sun.enterprise.v3.services.impl.ContainerMapper$Hk2DispatcherCallable.call(ContainerMapper.java:354)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [DataSource] with qualifiers [@Default] at injection point [[parameter 1] of [method] @Inject public com.example.dbflutecdi.db.allcommon.ImplementedInvokerAssistant.setDataSource(DataSource)]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:311)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:163)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:382)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:380)
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:199)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:313)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:461)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:389)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:348)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:363)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1085)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:95)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1291)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1259)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:461)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:212)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:179)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
at com.sun.enterprise.v3.services.impl.ContainerMapper$Hk2DispatcherCallable.call(ContainerMapper.java:354)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [CommonColumnAutoSetupper] with qualifiers [@Default] at injection point [[parameter 1] of [method] @Inject public com.example.dbflutecdi.db.bsbhv.BsEmployeeBhv.setCommonColumnAutoSetupper(CommonColumnAutoSetupper)]. Possible dependencies [[Managed Bean [class com.example.dbflutecdi.db.allcommon.ImplementedCommonColumnAutoSetupper] with qualifiers [@Any @Default], com.example.dbflutecdi.db.allcommon.DBFluteModule$DBFluteBean@14423bc8]]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:314)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:163)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:382)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:380)
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:199)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:313)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:461)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:389)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:348)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:363)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1085)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:95)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1291)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1259)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:461)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:212)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:179)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
at com.sun.enterprise.v3.services.impl.ContainerMapper$Hk2DispatcherCallable.call(ContainerMapper.java:354)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [DataSource] with qualifiers [@Default] at injection point [[parameter 1] of [method] @Inject public com.example.dbflutecdi.db.allcommon.ImplementedInvokerAssistant.setDataSource(DataSource)]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:311)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:384)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:380)
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:199)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:313)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:461)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:389)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:348)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:363)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1085)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:95)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1291)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1259)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:461)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:212)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:179)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
at com.sun.enterprise.v3.services.impl.ContainerMapper$Hk2DispatcherCallable.call(ContainerMapper.java:354)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [CommonColumnAutoSetupper] with qualifiers [@Default] at injection point [[parameter 1] of [method] @Inject public com.example.dbflutecdi.db.bsbhv.BsEmployeeBhv.setCommonColumnAutoSetupper(CommonColumnAutoSetupper)]. Possible dependencies [[Managed Bean [class com.example.dbflutecdi.db.allcommon.ImplementedCommonColumnAutoSetupper] with qualifiers [@Any @Default], com.example.dbflutecdi.db.allcommon.DBFluteModule$DBFluteBean@14423bc8]]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:314)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:384)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:380)
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:199)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:313)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:461)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:389)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:348)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:363)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1085)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:95)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1291)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1259)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:461)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:212)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:179)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
at com.sun.enterprise.v3.services.impl.ContainerMapper$Hk2DispatcherCallable.call(ContainerMapper.java:354)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)
weldというのはGlassFishが使っているCDIの実装。CDIのリファレンス実装でもある。どうやらweldが何をInjectionしたら良いのか分からなくてエラーになっている様子。
残念ながら、DBFlute 1.0.0でCDIをそのまま使う事は出来ないようです。
ここで諦めても良かったのですが、悔しいので解決策を探る事にしました。
まずAmbiguous dependenciesという例外に対応する事に。
エラーログを読む限り、それぞれのクラスのインスタンスとDBFluteModuleクラスのsetupDfComponentsメソッドで生成されているインスタンスが競合している様子。
おそらく解決策は2つあり、DBFluteModuleのsetupDfComponentsの一部をコメントアウトするか、QualifierアノテーションというInjection対象を指定するためのアノテーションを作って、Injectionして欲しい方に付与する必要がある。
とりあえずDBFluteModuleがなにしているのか調べるのが面倒なので、DBFluteGenerateというQualifierアノテーションを作ってInjection対象になってほしいクラスにつけてみたところ成功。
DBFluteGenerateのソースはこんな感じ。
//import 省略
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
public @interface DBFluteGenerate {
}
エラーログにはもう一つUnsatisfied dependenciesというものがあり、これはdatasourceのインジェクションのところで起きています。たぶんインジェクション対象が見つからないという事なのだと思うのだけど、どうすればいいのかさっぱり分からない。仕方ないのでsetDatasourceについていた@Injectを外して、_datasourceフィールドに@ResourceでDatasouceの名前を直接指定。
よくないやり方とは思うのだけど、これでデプロイし直すと動作するようになりました。
ここまでの対応+実際に動く例としてのソースコードをbitbucketに上げてみました。もっとエレガントな解決策があれば誰か教えてください。
なおDBFluteのgenerateタスクはソースコードの上書きをするので、根本的に対応するにはDBFlute側のテンプレートなどを変える必要があります。
ただJavaEE7の足音が聞こえてきているので、これ以上追求する元気が失われつつもあり。
水曜日, 8月 22, 2012
初めて道案内にiPhoneを使う
日曜日, 4月 22, 2012
たのまれごと2012 春と日記的な
いろいろ忙しくて全然更新していない。TwitterとGoogle+はやたら使っているので、長い文章を書く元気と時間が無いとも言える。
去年の12月から3ヶ月ほどプログラミングHaskellの練習問題を解いたりして楽しんでいたので、それを書けば良いのだけど、パラダイムチェンジしたいという曖昧なリクエストを持っていた人に本をあげてしまったので、それも書けない。買えばいいだけではあるけど。
とはいえ、関数型言語熱は引いていなくて、Erlangとかを経由しつつ、一応お仕事ではJavaが多いので、それをScalaで置き換えられないか、少なくともHaskellよりは導入が楽だろうと、Scalaを調べつつ、引越しもしなきゃだったりでバタバタしている今日この頃。
Scalaスケーラブルプログラミング第2版てのを読めば良いのだろうけど電子書籍版が無いのよね。収納の都合上、これ以上物理本は増やしたくないのだけど。
章毎に分解して、読んだ端から捨てるという読み方を実践してみるか。勿体無いけど、その方がちゃんと覚える気もする。
読書記録はここに書けば良いし。
さて、最近の頼まれ事は、新宿で親子連れにあるスタジオの場所を聞かれたので応えたのと、外国人の女性(おそらく旅行者)に新宿中央公園の場所を聞かれた。
iPhoneを持っているので、それを出せばきちんと場所を伝えられるのだけど、そういう場面になるとそれを忘れてしまう。
頼まれ頻度は最近落ちてきたかな?