愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

Vimを使って複数の文字列を一度で置換する

Vimについて書くのとか久しぶりすぎる。

例えば、テキスト中の

  1. FFFF00をFFFFFFに
  2. FFFFFFをFFFF00に

一発で置き換えたい。これを1 => 2の順でやると1の後で全部FFFFFFになってしまうので、アカン。

perlとかでコマンドラインからやってもいいけど、まあVimからやりたいとしよう。

おそらく色々方法はあるのだろうけど、一番楽なのは tpope/vim-abolish · GitHub を入れて

:%Subvert/{FFFF00,FFFFFF}/{FFFFFF,FFFF00}

かな、と思った。

jOOQ (Java Object Oriented Querying) の使い方の紹介

jOOQ (http://www.jooq.org/) についてのメモ。 jOOQは、type safeかつdatabase orientedなクエリビルダーである。 database orientedなので、「アプリケーションからDBのことなんて全然意識したくないねん」的な思想に基いて作られた類のプロダクトとは異なり、思い通りのSQLを生成できるようになっている。

以下gradle前提で。version等はよしなに。

gradle

dependenciesはこんな感じ

compile 'org.jooq:jooq:3.7.1'
compile 'org.jooq:jooq-meta:3.7.1'

テーブル等に相当するクラスをgradle taskから生成するためにbuildscriptのdependenciesにも以下をかいておく

classpath 'org.jooq:jooq-codegen:3.7.1'

んで、以下のようなコードをbuild.gradleに書く。MySQL前提です。細かい部分は自分の環境に合わせてよしなに。

def genXml = { db, writer ->
    new groovy.xml.MarkupBuilder(writer).configuration('xmlns': 'http://www.jooq.org/xsd/jooq-codegen-3.6.0.xsd') {
        jdbc() {
            driver("com.mysql.jdbc.Driver")
            url("jdbc:mysql://localhost:3306/")
            user("root")
            password("")
        }
        generator() {
            database() {
                inputSchema(db)
            }
            generate() {
            }
            target() {
                packageName("com.foo.bar.jooq." + db)
                directory("src/main/java")
            }
        }
    }
}

def generateJooqSources = { db -> 
    def writer = new StringWriter()
    def xml = genXml(db, writer)
    org.jooq.util.GenerationTool.generate(
        javax.xml.bind.JAXB.unmarshal(new StringReader(writer.toString()), org.jooq.util.jaxb.Configuration.class)
    )
}

で、以下のようにデータベース毎にtaskを定義する、と。

task("jooq-generate-example") << {
    generateJooqSources(name)
}

若干DRYではないがここでは気にしない。

初期化

単にクエリを発行するだけなら、ConnectionやDataSourceからDSLContextクラスのオブジェクトを作ればよい。

DSLContext ctx = DSL.using(dataSource, SQLDialect.MYSQL);

クエリの書き方

SQLでやりたいことは大体出来る。たまにめんどくさい書き方をする必要があるが...

以下、userというtableからコード生成して、適宜static importしている前提。

単品SELECT

UserRecord record = ctx.selectFrom(USER)
    .where(USER.ID.equal(userId)
    .fetchOneInto(UserRecord.class); 

カラムを指定して単品SELECT

返り値を制御する関係で、結果として二回カラム名を指定する必要がある

String name = ctx.select(USER.NAME)
    .from(USER)
    .where(USER.ID.equal(userId)
    .fetchOne(USER.NAME); 

複数SELECT

List<UserRecord> userRecords = ctx.selectFrom(USER)
    .where(USER.NAME.equal(name)
    .fetchInto(UserRecord.class);

特定のカラムの値をkeyにしたMapを取得

PerlのDBIでいうところのselectrow_hashref的なことも出来る。1対1のマップ。

Map<Integer, UserRecord> userRecords = ctx.selectFrom(USER)
    .where(USER.NAME.equal(name)
    .fetchMap(USER.ID, UserRecord.class);

特定のカラムの値をkey、keyの値でグルーピングされたレコードのListをvalueにしたMapを取得

PerlのDBIでいうところの…なんだっけ。あったっけか。1対nのマップ。

Map<Integer, List<UserRecord>> userRecords = ctx.selectFrom(USER)
    .where(USER.ID.in(userIds))
    .fetchGroups(USER.STATUS, UserRecord.class);

INSERT

ctx.insertInto(USER, USER.NAME, USER.STATUS)
    .values(name, 0)
    .execute();

batch INSERT

List<UserRecord> userRecords = new ArrayList<UserRecord>();
...
ctx.batchInsert(userRecords)
    .execute();

UPDATE

ctx.update(USER)
    .set(USER.NAME, name)
    .where(USER.ID.equal(userId))
    .execute();

DELETE

ctx.deleteFrom(USER)
    .where(USER.ID.equal(userId))
    .execute();

トランザクション

ctx.transaction(new TransactionalRunnable() {
    @Override
    public void run(Configuration configuration) throws Exception {
        // ここの中
    }
})

where条件の動的な組み立て

SelectQuery<UserRecord> query = ctx.selectFrom(USER).getQuery();

for (User u : users) {
    Condition condition = USER.NAME.equal(userName).and(USER.STATUS.equal(1));
    query.addConditions(Operator.OR, condition);
}

List<UserRecord> userRecords = query.fetchInto(UserRecord.class);

DAO

使ったことはないが生成できるらしい

http://www.jooq.org/doc/2.6/manual/sql-execution/daos/

で、実際どうなん?

たまにかゆい所もあるけど、一通りのことは出来てまあまあ使いやすいです。

ドキュメントが結構充実しているのと、開発者がstackoverflowでこまめにレス付けてる所を見ると、気合の入っていることが分かります。

JDBCによるJavaデータベースプログラミング 第2版

JDBCによるJavaデータベースプログラミング 第2版

「神聖喜劇」に寄す

大西巨人「神聖喜劇」を読んだ。

神聖喜劇〈第1巻〉 (光文社文庫)

神聖喜劇〈第1巻〉 (光文社文庫)

「神聖喜劇」とは何のことやらと思うかもしれないが、これはダンテの「神曲」(La Divina Commedia) の直訳である。

全五巻に渡り、非常にきびきびとした文章で主人公・東堂太郎の対馬における軍隊生活が綴られる。その文章の生硬さもさることながら、主人公の設定(=超人的な記憶力の持ち主)から来る膨大な引用、出来事および引用それ自体を契機とした数十ページに渡る逸脱、言葉遣いや表現の綾に対する主人公の偏執的とも言えるこだわり等々、ことばそのものへの尋常ならざる執着を楽しむことがほぼ主眼となっているような小説でもある。

「戦争の出てくる小説」といえばついつい反戦的なテーマだとか、あるいは軍隊生活の厳しさ、そこにおける上官の理不尽さなどのありきたりな主題を誰しも想定してしまうだろう。この小説においてもそれらの要素自体は含まれているものも、典型的なそれを期待して読むといずれの場面でも肩透かしを食うことになるだろう。批判的に戦争や軍隊を眺める眼の中にもいわゆる反戦はなく、理不尽な軍隊生活の中にも捩れたロジックがあり、粗暴・質朴な人間の中にも決して単純には断じきれない深淵がある。これはそういう小説なのである。

こういう風に書くとひたすらお堅い文学青年御用達の小説のように思えるかもしれないが、神聖喜劇には読みながら声を出して笑ってしまうような場面が頻発する。「笑ってはいけない◯◯」というシリーズがダウンタウンのガキの使いにあるが、当人たちが大真面目であり、また真面目に振る舞わなくてはいけないとされている場面の僅かな歪みで我々の笑いは噴出する。軍隊こそは、大真面目に滑稽なことが行われ、また真面目の極みを求められるが故にまさに神聖な「喜劇」の場になり得るのだ。

現代のグローバル資本主義のありようを「戦争」、会社を「軍隊」と例えるほど僕はアナクロではないし鈍感でもない。だが神聖喜劇の中で時折爆発する笑いの震源について一度考えてみるならば、我々がその日常において大真面目であることを強いられ、それが故に滑稽さを免れない状況をもたらすところの会社もまた「喜劇」の場の一つであることはどうやら間違いがなさそうである。

そう、神聖喜劇は、軍隊でのみ演じられるのではない。人間の作るあらゆる組織と、その組織が大真面目に行うゲームからそれは生まれるのである。

今調べたら漫画版も出ているようだが、おすすめはしない。先ほどから書いているようにこれは文章そのものをとことん楽しむための小説であり、また一般に優れた文学作品というのはテーマや話の筋を追っただけではその半分も味わえない。文章を読む時間そのものが体験であり、快楽であり、小説である。そうしたものなのだ。

神聖喜劇 第一巻

神聖喜劇 第一巻

万人向けのやさしい小説ではないが、歯ごたえのあるものを食べたい向きにはお勧めする。

TLS Session Ticketの動作をtsharkでなんとなく覗きみる

こういう仕組みですよ、というWeb上の記事を読んだだけでは納得できない!論より証拠だ!ということで論より証拠ツールその2であるtsharkを使ってTLS Session Ticketの動作を「なんとなく」覗いてみる。

ちなみに、クライアントは Google Chrome (49.0.2576.0 canary (64-bit)) 、サーバはh2o (1.6.0-beta1, LibreSSL 2.2.4) である。

まずはざっくり

sudo tshark -i eth0 -f "tcp port 443" -Y ssl

状態をクリアするためにh2oを再起動してからhttpsなリクエストを送ると以下の様なシーケンスになる。

  4   0.013439 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy SSL 583 Client Hello
  6   0.018197 yyy.yyy.yyy.yyy -> xxx.xxx.xxx.xxx TLSv1.2 2842 Server Hello
  7   0.018254 yyy.yyy.yyy.yyy -> xxx.xxx.xxx.xxx TLSv1.2 2842 Certificate
  8   0.018283 yyy.yyy.yyy.yyy -> xxx.xxx.xxx.xxx TLSv1.2 848 Certificate Status
 11   0.032880 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 192 Client Key Exchange, Change Cipher Spec, Hello Request, Hello Request
 12   0.033993 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 119 Application Data
 13   0.034004 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 116 Application Data
 14   0.034008 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 108 Application Data
 15   0.034130 yyy.yyy.yyy.yyy -> xxx.xxx.xxx.xxx TLSv1.2 308 New Session Ticket, Change Cipher Spec, Hello Request, Hello Request
 16   0.034270 yyy.yyy.yyy.yyy -> xxx.xxx.xxx.xxx TLSv1.2 125 Application Data
 17   0.034589 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 425 Application Data
(以下略)

そこから一度タブ閉じて、もう一度リクエストを送ると以下の様なシーケンス。

  3   0.014875 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy SSL 583 Client Hello
  5   0.015423 yyy.yyy.yyy.yyy -> xxx.xxx.xxx.xxx TLSv1.2 216 Server Hello, Change Cipher Spec, Hello Request, Hello Request
  7   0.042222 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 117 Change Cipher Spec, Hello Request, Hello Request
  8   0.042255 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 119 Application Data
  9   0.042262 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 116 Application Data
 10   0.042266 xxx.xxx.xxx.xxx -> yyy.yyy.yyy.yyy TLSv1.2 108 Application Data
(以下略)

Certificate, Certificate Status, Server Key Exchange, Client Key Exchangeなどが省略され、Server Helloから一気にChange Cipher Specまで行っていることが分かる。 (注:tsharkの結果には出力されていないが、Server Key ExchangeはCertificate Statusと同じパケットに入っている)

もうちょっとだけ詳しく

以下のコマンドで見れば、

sudo tshark -i eth0 -f "tcp port 443" -Y ssl -O ssl

再起動後のシーケンスの最後、サーバからのChange Cipher Specと同じパケットにSession Ticketの値が含まれていることが分かる。

Secure Sockets Layer
    TLSv1.2 Record Layer: Handshake Protocol: New Session Ticket
        Content Type: Handshake (22)
        Version: TLS 1.2 (0x0303)
        Length: 186
        Handshake Protocol: New Session Ticket
            Handshake Type: New Session Ticket (4)
            Length: 182
            TLS Session Ticket
                Session Ticket Lifetime Hint: 3600
                Session Ticket Length: 176
                Session Ticket: fcf1faec48a3153aa98072c75e1aae922ebd76da303ea06e...
    TLSv1.2 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec
        Content Type: Change Cipher Spec (20)
        Version: TLS 1.2 (0x0303)
        Length: 1
        Change Cipher Spec Message
    TLSv1.2 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.2 (0x0303)
        Length: 40
        Handshake Protocol: Hello Request
            Handshake Type: Hello Request (0)
            Length: 0
        Handshake Protocol: Hello Request
            Handshake Type: Hello Request (0)
            Length: 0

以降、Client HelloにはSession Ticketが含まれ、それがvalidであった場合はもろもろが省略されてハンドシェイクがすぐ終わる。

            Extension: SessionTicket TLS
                Type: SessionTicket TLS (0x0023)
                Length: 176
                Data (176 bytes)

FT142A (priori2 3G) は安かろう悪かろう

iPhone (もう、いくつだったか忘れた)からNexus5に持ち替えて、Nexus5がぶっ壊れたので、freetelのpriori2に変えていた。

Nexus5を使っていた時は「この端末いいぜ!」みたいなエントリーを書いたり人に勧めたりしていたけど、すまんあれはウソじゃったウソとまでは言わないが、買った当初はよくてもAndroid5.0にしてから極端にバッテリーの持ちが悪くなり、なんだか微妙な端末であった。

で、「もうスマホに金を掛けることが既にダサい。お、10000円で買えるのがあるじゃないか」と思ってpriori2の3Gを買ったのだが、これはこれでケチりすぎた。

スマホとしての機能にそこまで問題があるわけではないが、やはりタッチ感度が悪いのと、LTEが使えないのは流石にやりすぎだ。今は反省している。まあ、スマホが欲しい!と泣く子に買い与える用途なんかにはいいんじゃないでしょうか。え、何?iPhoneがいいって?iPhoneは子供が持つと乳首が伸びるのよ!

しばらくはこのままいくけど、どこかのタイミングで端末を安く買える格安スマホ業者(例えば、楽天とか)にSIMごと引っ越そうかと思っている。