2011/03/06


public class foo {
    final static String bar = "a" + "b";

    public foo() { }

    public String bar(String baz) {
        return "a" + "b" + baz + "c" + "d";
    }
}
コンパイルしてデコンパイル # javac -version
javac 1.6.0_24
# javac foo.java
# jad foo.class
// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   foo.java


public class foo
{

    public foo()
    {
    }

    public String bar(String s)
    {
        return (new StringBuilder()).append("ab").append(s).append("c").append("d").toString();
    }

    static final String bar = "ab";
}
+ をStringBuilder に置き換えてくれたまでは良かった。a と b は最適化されて一つの ab になったけど c と d はそのままだ。
この最適化が有効なのは、業務のソースなんかでたまに見かける String sql = "SELECT "
           + "  * "
           + " FROM "
           + "  USER_MST"
           + " WHERE "
           + "  BAR = ?";

PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1"baz");
こんなソースの場合だと思うんだけど、テーブル名なんかを変数に持ってて String sql = "SELECT "
    + " * "
    + " FROM "
    + " " + tableName
    + " WHERE "
    + "     BAR = ?";
こんな事しちゃうと、変数以降の最適化が StringBuilder での最適化に止まってしまう。まぁよく見るよね。こういうコード。
String s1 = (new StringBuilder()).append("SELECT *  FROM ").append(s).append(" WHERE ").append(" BAR = ?").toString();
PreparedStatement preparedstatement = connection.prepareStatement(s1);
preparedstatement.setString(1, "baz");
しばらく「最近のJavaはconstなStringを結合して最適化する」っていうのはやめておこうと思った。
Posted at by




この時をずっとまってた。
go-gtk runs on win32!
Google製の新しいコンパイル型言語Go上で動くGTK GUIライブラリ、go-gtkが今日、windowsで動いた。
Xming上で動いている様にも見えますが、ちゃんとwin32 nativeなGTKで動いてます。
まずは一般的なGUIのサンプル。
go-gtk-win32-demo
そしてgoroutineで非同期にtwitter public timeline(非streaming API)を読み込むサンプル。
go-gtk-win32-twitter

だいたい動きました。ここ数日、cgoのwin32対応に関するsubmitが多くなってたので、そろそろ動くかなと思って着手しました。win32向けに対応した部分としては実はそんなに多くないです。go-gtkはイベントポーリングを自前でやっていて、その排他制御にpthreadを使っているんですが、win32だとpthread_mutex_lock/pthread_mutex_unlockでうまく排他制御出来なかった(gdkのスレッドと相性が良くないのか落ちる)ので、win32ではクリティカルセクションを使う様にお得意の#ifdefで修正しました。
gtksourceviewの一部で落ちるのを1件確認してますが、その他はほぼ動きました。

GUIが扱えるスクリプト言語は数ありますが、nativeにコンパイル出来てオブジェクト思考で、非同期(goroutine)が使えるGoでGUIライブラリがwindows上で動いたのは、正直大きいと個人的には思っています。

Goの高速なコンパイルと、Duck Typingによる自由度の高い型変換により脳内で設計した物が思考停止する事なくアウトプット出来るのです。
同じコードで比べないと比較になりませんが、C言語でcurlとgtkを使ってtwitterのpublic timelineをGUI表示(アイコン表示あり)というコードがあったとして、僕のノートPC(Intel Core i5)ではコンパイル/リンクがだいたい5秒程度くらいかかりますが、go-gtkの同等サンプルexample/twitterだと2秒くらいでビルド出来ます。

これからGoでGUIを使ったアプリケーションを作りたいという方にはぜひ試して欲しいと思います。

Goがwindowsで動き出してまだホヤホヤですが、これから普及していく事を望んでます。

Posted at by



2011/03/03


こんばんわ。node.jsやってないとjavascriptのもぐりだと言われている昨今、いかがお過ごしでしょうか。
個人的には node.js は2回くらい飽きてしまって、3周目くらいです。

よく考えたらこのブログでは一度も node.js に触れてなかったなーと思ったのと、最近触ってないから忘れてそうだな...という思いからエントリにしてみました。
node.js が面白いと言われている理由の一つに非同期処理があります。そして非同期を面白くする題材として websocket があります。今日はその websocket を使って、サンプルを作ります。
物としては、twitter の filter stream から、instagr.ampicplz.com の画像URLを収集し、それをクライアントにブロードキャストします。クライアントはそれを受けて Growl 風にポップアップ表示するという物です。
まずサイトに仕上げるには、静的ファイルサーバが必要になります。
どのサイトでも紹介してそうですが、適当ながら動くレベルの物を作ります。
var sys = require('sys'),
    fs = require('fs'),
    url = require('url'),
    http = require('http');

// static file server
var extmap = {'.htm''text/html''.css''text/css'};
var server = http.createServer(function(req, res){
  var uri = url.parse(req.url).pathname;  
  if (uri.match('/$')) uri = '/index.html'
  try {
    var filename = __dirname + '/static' + uri;
    var contenttype = extmap[filename.substr(filename.lastIndexOf("."), 4)] || 'application/octet-stream';
    var rs = fs.createReadStream(filename);
    res.writeHead(200, {'Content-Type': contenttype});
    sys.pump(rs, res);
  } catch(e) {
    console.log(e);
    res.sendHeader(404, {"Content-Type""text/plain"});  
    res.write("Not Found\n");  
    res.close();  
  }
});
server.listen(8080);
これを server.js というファイル名で保存して # node server.js と実行すると、static フォルダ配下のファイルが http://localhost:8080/ でサーブされる仕組みです。
次に、websocket を扱える様にします。既存のサーバ上に websocket を乗せるには以下の様にサーバを指定すればokです。(おそらくこれは sugyan さんへのヒントになるのかもしれませんが)
var ws = websocket.createServer({server: server});
クライアント側の実装だとこんな感じですね。
var connection = new WebSocket('ws://' + location.host);
connection.onopen = function(event) {
}
connection.onmessage = function(event) {
}
さて、下準備は出来たので twitter stream を扱いましょう。twitter stream API はまだ basic 認証で扱えます。http-basic-auth モジュールを使いましょう。なお、Ubuntu 向けに launchpad から提供されている nodejs には node-waf が入っていませんので、依存で入る base64 モジュールのビルドに失敗します。どうしても試したい人は nodejs をソースからビルドしましょう。
twitter filter stream を扱うコードは以下の様になります。
var basicauth = require('http-basic-auth');

var basicauthclient = basicauth.createClient(80'stream.twitter.com'falsefalse, account)
var req = basicauthclient.request('GET''/1/statuses/filter.json?track=picplz,instagr', {'host''stream.twitter.com'})
req.end();
req.on('response'function (res) {
  res.on('data'function(chunk) {
  })
})
account は username と password をキーに持つオブジェクトです。ご自分のアカウントを設定しましょう。
twitter stream を受信したら、tweet の entities から URL 一覧を取得し、instagr.am と picplz.com の画像URLを取得しましょう。picplz.com はURLの後ろに /thumb/400 を足すだけですが、instagr.am は API にアクセスしなければなりません。
tweet 受信時の処理は以下の様になりました。
req.on('response'function (res) {
  res.on('data'function(chunk) {
    try {
      var tweet = JSON.parse(chunk);
      // parse entities urls
      [].forEach.call(tweet.entities.urls, function(item) {
         // pick images of instagr.am or picplz.com
         if (item.url.match('^http://instagr.am/p/')) {
           var client = http.createClient(80'instagr.am');
           var req = client.request('GET''/api/v1/oembed/?format=json&maxheight=330&url=' + item.url, {'host''instagr.am'});
           req.on('response'function(res){
             res.on('data'function(chunk){
               var url = JSON.parse(chunk).url
               console.log(url)
               ws.broadcast(url)
             });
           });
           req.end();
         }
         if (item.url.match('^http://picplz.com/')) {
           var url = item.url + '/thumb/400'
           console.log(url)
           ws.broadcast(url)
         }
      });
    } catch(e) { console.log(e) }
  });
});
サーバ部は出来上がりですね。最後にクライアント部を作りましょう。
$(function() {
  var connection = new WebSocket('ws://' + location.host);
  connection.onopen = function(event) {
    $('#fotoflo').empty();
  }
  connection.onmessage = function(event) {
    var x = (Math.random() * ($(document).width() - 330)).toFixed();
    var y = (Math.random() * ($(document).height() - 330)).toFixed();
    var item = $('<div/>')
      .addClass('popup-image')
      .css({'left': x + 'px''top': y + 'px''position''absolute''display''none'})
      .appendTo('body')
    $('<img/>')
      .attr('src'event.data)
      .bind('load'function() {
        $(item).fadeIn(500).delay(10000).fadeOut(500function() {
          $(this).remove()
        })
      }).appendTo(item)
  }
})
こんな感じでしょうか。受信する度にランダムな位置へ画像をフェイドイン表示し、数秒経過したら消すというオーソドックスな物です。
以下、リポジトリの場所は示しますが、サーバの全体ソースを載せておきます。
var sys = require('sys'),
    fs = require('fs'),
    url = require('url'),
    http = require('http'),
    websocket = require('websocket-server'),
    basicauth = require('http-basic-auth');

// load twitter account
var account = JSON.parse(fs.readFileSync(__dirname + '/config.json''utf8'))

// static file server
var extmap = {'.htm''text/html''.css''text/css'};
var server = http.createServer(function(req, res){
  var uri = url.parse(req.url).pathname;  
  if (uri.match('/$')) uri = '/index.html'
  try {
    var filename = __dirname + '/static' + uri;
    var contenttype = extmap[filename.substr(filename.lastIndexOf("."), 4)] || 'application/octet-stream';
    var rs = fs.createReadStream(filename);
    res.writeHead(200, {'Content-Type': contenttype});
    sys.pump(rs, res);
  } catch(e) {
    console.log(e);
    res.sendHeader(404, {"Content-Type""text/plain"});  
    res.write("Not Found\n");  
    res.close();  
  }
});
server.listen(80);

// listen websocket server on the server
var ws = websocket.createServer({server: server});

// twitter filter stream
var basicauthclient = basicauth.createClient(80'stream.twitter.com'falsefalse, account)
var req = basicauthclient.request('GET''/1/statuses/filter.json?track=picplz,instagr', {'host''stream.twitter.com'})
req.end();
req.on('response'function (res) {
  res.on('data'function(chunk) {
    try {
      var tweet = JSON.parse(chunk);
      // parse entities urls
      [].forEach.call(tweet.entities.urls, function(item) {
         // pick images of instagr.am or picplz.com
         if (item.url.match('^http://instagr.am/p/')) {
           var client = http.createClient(80'instagr.am');
           var req = client.request('GET''/api/v1/oembed/?format=json&maxheight=330&url=' + item.url, {'host''instagr.am'});
           req.on('response'function(res){
             res.on('data'function(chunk){
               var url = JSON.parse(chunk).url
               console.log(url)
               ws.broadcast(url)
             });
           });
           req.end();
         }
         if (item.url.match('^http://picplz.com/')) {
           var url = item.url + '/thumb/400'
           console.log(url)
           ws.broadcast(url)
         }
      });
    } catch(e) { console.log(e) }
  });
});
簡単ですね!
imagestream
server.js と同じフォルダに config.json というファイルがあるので、そのファイル内を twitter アカウントで修正して頂ければ動きます。
mattn/node-image-stream - GitHub

display images from twitter stream

https://github.com/mattn/node-image-stream
みなさんもぜひ面白い物作ってみて下さい。
Posted at by