まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2014/04

オープンソースのドキュメント指向データベースである mongoDB を CentOS にインストールします。

mongoDB はいわゆる NoSQL データベースの1つで、特長として JSON オブジェクトをそのまま格納することが可能です。RDB ではないためリレーションから関連データをたどって取り出す、といったことはできませんが、JSON に格納された非構造的なデータをそのまま(定義なしで)格納して取り出すことができる、といった特徴があります。


標準リポジトリには含まれていないので、まずは yum リポジトリを用意します:
# vi /etc/yum.repos.d/10gen.repo

[10gen]
name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64
(32bit版の場合は http://downloads-distro.mongodb.org/repo/redhat/os/i686 )
gpgcheck=0
enabled=1


インストールは yum コマンド一発です:
# yum install mongo-10gen mongo-10gen-server


インストールできたら起動&自動起動設定:
# /etc/init.d/mongod start
# chkconfig mongod on

 

「Java で使えるバーコード読み取りライブラリ」を探していました。

開発の関係で「Java から使える」ことが条件でした。また「QRコード」のライブラリは見かけるけど、いわゆる「バーコード」 のは意外と少ない。そしてバーコードを「生成する」のではなく、バーコード画像を「読み取り」たかった、という条件もあります。 加えてライセンスフリーだといいな、と。

これだけの条件で探すとほとんど選択肢はなく、唯一見つけたのが「ZXing(ゼブラクロッシング)」というライブラリでした。 ZXing は単に JavaSE で使えるというだけでなく、Android SDK からも使えます。また QR コードにも対応していたり、読み書き両方に使えたりと汎用性も高そうです。ZXing はソースファイルで提供されているので、そのビルド方法も含めた利用手順を紹介します。なお最新版(3.0)の ZXing をビルドするには JDK 1.7 以上、および Apache Maven が必要です。 

RedHat Enterprise Linux や CentOS であれば以下のコマンドで JDK1.7 をインストールできます:
# yum install java-1.7.0-openjdk-devel

Apache Maven はこんな感じ:
# cd /etc/yum.repos.d
# wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo # yum install apache-maven

JDK 1.7 と Maven が用意できたら ZXing をビルドすることができます。まず ZXing のダウンロードページから最新のソースファイルをダウンロードして、zip ファイルを展開しておきます。

と、言いたかったのですが、4月25日現在の最新版(3.0.2-SNAPSHOT)はビルドの途中でエラーになってしまい、なんかよく分からなかったので、僕はこちらの 2.3.0 というバージョンを使いました。

展開後、いくつかのフォルダが出来上がりますが、通常の Java(SE) 環境で利用するには core と javase のライブラリが必要です。そのためこの2つのフォルダで JAR ライブラリを Maven でビルドします:
# cd core
# mvn -DskipTests -Dgpg.skip=true install
→ ./target/ に JAR ファイルができている # cd ../javase # mvn -DskipTests -Dgpg.skip=true install
 → ./target/ に JAR ファイルができている

ビルドでできあがった core-X.X.X.jar と javase-X.X.X.jar(X.X.X 部分は ZXing のバージョン)が必要なライブラリになります。これらを Java のプロジェクト内で利用できるように(例えば WEB-INF/lib/ 以下に)コピーして、これで準備完了。

実際にバーコードをデコードするサンプルも記載しておきます。これはローカルシステムの c:/tmp/sample.jpg にバーコード画像がある場合の例ですが、こんな感じ: 
  String filename = "c:/tmp/sample.jpg";
    :
  try{
    //. 画像読み込み
    BufferedImage image = ImageIO.read( new File( filename ) );
    LuminanceSource source = new BufferedImageLuminanceSource( image );
    BinaryBitmap bitmap = new BinaryBitmap( new HybridBinarizer( source ) );

    //. デコード
    Reader reader = new MultiFormatReader();
    Result result = reader.decode( bitmap );

    //. バーコードフォーマット
    BarcodeFormat format = result.getBarcodeFormat();

    //. バーコードコンテンツ(読み取り結果)
    String text = result.getText();
      :
  }catch( Exception e ){
    e.printStackTrace();
  }
    :





さて問題の認識精度ですが、 、、まあ仕方ないのかな、って感じです。画像にバーコードだけが写っている状態であればまだしも、周囲に関係ない部分が含まれていたりすると認識してくれないケースが多いです。

とはいえフリーで使えるデコードライブラリは他に選択肢がないので、贅沢は言えませんよね。
 

突然始まった身に覚えのない Google Wallet への不正請求事件への対処を開始してから約半年、やっと、やっとこのエントリを書く時がやってきました。
過去の経緯はこちらを参照ください:     


先日、某クレジットサービス会社から1通の手紙が届きました:
gw99

「勝訴!」 d(ToT)

とまでは言わないけど、見に覚えないんだからそりゃそうだ。
当時よりは円安なんだから利子だけでなく為替差分も付けて返してほしい所だが・・・
2014041901
 ↑為替差分も利子もついてないし・・・



ではハッキリした以上言わせてもらおう。

まずこの件の直接の原因について、グーグルからは「原因はいろいろ考えられる」という返答しかもらっていないけど、他の多くの事例報告からもグーグルアカウントが流出したことしか考えられない。自分自身もグーグルアカウントの取り扱いを見直すきっかけになったし、お金の絡むサービスなんだからしっかり管理してほしい。 ただグーグルウォレットチームとのやりとりやこの件での素早くて真摯なサポートは本当に助かりました。敢えてお願いすることがあるとしたら、同様のサポートを日本でも(というか日本語でも)提供してほしいです。僕のブログのアクセスログを見る限りでも、同様の事件で困っている人は多そうなのです。今回の対応も僕が英語で直談判しなかったら何も解決しなかったと思ってます。

次に名前をぶちまけてやりたいくらいの某クレジットサービス社。何度も「子供のイタズラ」を疑ったり(独身子無しだと何度言えば・・)、「GMailアカウントを変えろ」とかムチャなことを言ってきたり、今回の対応も「やりたくない」感アリアリで、いちいち遅かった。こういうもんなの?

最後に某大手カード会社。上記サービス会社によると「ここが『カードを新規に作り直さない限りは対応しない』と言ってきた」そうだ。詳しくは過去のエントリに書いたけど、今回の件はグーグル側に原因がある(と思う)ので、カードを作り直しても、グーグルに登録し直したらすぐに再発する可能性が高い問題だ。当然番号も変わるのであちこち登録し直さないといけない。セキュアでも何でもなく単に不便なだけなんだけど、それでもカードを新規登録させる意味が分からない。

今回の件は極端な話、生活に支障がでるような請求額ではなかった。でも対応しているうちに額の問題ではなくなって、「何がなんでも自分の主張を通さないと気が済まない」本気モードになった。熱くさせてくれた上で最後にはこちらの主張を認めてくれた、という点で後者2社には感謝してます。 :P






 

こんなニュースがありました:
グーグルが「Gmail」の利用規約を更新 - メール内容の解析を明示

グーグルは Gmail に書かれた内容を読んでいる、ということ。
その是非が色々言われているようですが、その件についてコメントは控えます。

僕は知ってました。というか、そういうものだと思ってました。
普段から自分でいくつかのウェブサービスを遊びで開発して公開したりしてます。
ある時、まだ誰にも話してないし、リンクも作っていない作りたてのウェブサイトのURLを Gmail で知り合いに送った所、その数日後から Google のクローラーボットがやってくるようになりました(苦笑)。本当にこのメールがきっかけだったのかどうかは分からないのですが、原因がメール内容しか思い当たらなかったので、以来そういうものだと思っていました。

で、それを逆手にとったというわけではないですが、裏ワザを1つ紹介します。

これから公開しようとしているウェブサービスを開発したら、公開前にわざと GMail にその URL を書いて誰かに(例えば自分の別のアドレスに)送ります。それでグーグルはその新サービスの URL を知ることになるので、数日中にクローラーボットが送り込まれて、グーグルの検索エンジンにインデックスしてくれます。

これで、サービスがスタートする際には既にサービス内の大半のページがグーグル検索結果に表示される状態を作っておくことができます。厳密には、サービススタート前に(キーワード次第では)検索結果に表示されるのはともかく、実際に訪問されては困る、ということもあると思います。そういう場合は UserAgent を見て、グーグルボットの場合だけは訪問を許し、それ以外の場合はエラーにする、という処理を加えておいて、サービス開始直前に無効にする、といった対策で訪問を回避することもできます。


と、まあグーグルが勝手にメールを解析してくれるのであれば、それを勝手に有効活用させてもらいましょう、という方法でした。


 

ウェブ制作において、画像やサイトの一部を別モーダルウィンドウでプレビューのように表示する、通称「ライトボックス」と呼ばれる機能があります。 具体的にはこんな感じのものです:

マンホールマップ  ←クリックするとライトボックス内でマンホールマップを表示します

↑CSS が適当なので少し見にくいかも。あとスマホだとうまく表示されないかもしれません・・・


元のページに戻ってくることを前提に、違うページの情報を一瞬表示させる、という目的で便利に使える機能です。Facebook などでも写真を表示する時に使われてますね。こういった機能を実現するための JavaScript ライブラリも多く公開されています(ちなみに上記機能は Colorbox というライブラリを使って実現しています)。

Colorbox で iframe を使った簡単な実装例を紹介します。上記ページから js と css をダウンロードし、jQuery と合わせて <head> 内に以下のような記述をしておきます:
<link  href="css/colorbox.css" rel="stylesheet">
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery.colorbox-min.js"></script>
<script type="text/javascript">
$(function(){
	$('.box').colorbox( { iframe:true, width:"90%", height:"90%" } );
});
</script>

この例では box というクラスが付与されたリンクに対してライトボックスを実行するような記述になっています。 
その上で以下のような <a> リンクを用意、これだけです:
<a class="box" href="xxx">XXXX</a>


ところが、このライトボックスが使えないページ(ライトボックスの中身として表示できないページ)、というのがたまに存在しています。例えばグーグルのトップページ(http://www.google.co.jp/)です:

Google  ←クリックするとダイアログは表れますが、中に何も表示されません


実は同じようなページは他にもあります。僕が知る限りではアマゾン価格コムのページもライトボックスでは表示されません。

この違いは何でしょうか? 実はページを HTTP リクエストで取得する際のレスポンスヘッダ X-Frame-Optionsによって、そのコンテンツが iframe の内部に表示することを許可するかどうかを制御できます。上記の挙動の違いはこのレスポンスヘッダ X-Frame-Options の値によるものでした。
2014040600


デフォルト値は「なし」です。その場合(つまり特に何もしない場合)、そのページは iframe 内に表示可能です。一方この値が DENY に設定されていると、そのページを iframe 内に表示することはできません。また SAMEORIGIN(生成元と同じフレーム内に限り表示可)や、ALLOW-FROM xxx(生成元が xxx の場合に限り表示可)といった設定も可能です。詳しくはこちらを参照してください。
2014040601


こういう制限をかける側としてはそれなりの意図があって制限しているんでしょうけど、やはり表示できるものなら表示したい、という作成側の意図もあるわけです。また AJAX のクロスドメイン制限にも言えることですが、制限自体は JavaScript での挙動や iframe 内での挙動に制限をかけているだけであって、情報の取得そのものを制限しているわけではないので、回避することもできます。 で、今日はその(X-Frame-Options 制限の)回避に挑戦してみます。

考え方としては iframe での表示に制限をかけられているサイトに対して、Java や PHP などのサーバーサイドプログラミングによって情報を取得(これらには制限がないので可能)し、その結果を iframe 内に入れてしまおう、というものです。まあクロスドメイン対策と同じ考え方です。
2014040602


ということで、上記の紫色の部分を別途作成する(加えて、それをどこかのサーバーに設置しておく)必要があるわけです。この部分は例えば Java のサーブレットであれば、おおまかにはこんな感じになるでしょうかね:
public class SpoofXFrameOptionsServlet extends HttpServlet {
  public void doGet( HttpServletRequest req, HttpServletResponse res ) throws IOException{
    req.setCharacterEncoding( "UTF-8" );

    String html = "";
    String url = "", _url = req.getParameter( "url" );
    if( _url != null && _url.length() > 0 ) url = _url;
    if( url != null && url.length() > 0 ){
      try{
        GetMethod method = new GetMethod( url );
        HttpClient client = new HttpClient();
        int sc = client.executeMethod( method );
        html = method.getResponseBodyAsString();
      }catch( Exception e ){
        e.printStackTrace();
      }
    }

    res.setContentType( "text/html; charset=UTF-8" );
    res.setCharacterEncoding( "UTF-8" );
    res.getWriter().println( html );
}

このサーブレットが http://xxxx/spoofxframeoptions という名前でアクセスできるようになっていたとすると、
http://xxxx/spoofxframeoptions?url=(iframe 内に入れたいページのURL)

のように指定することで、目的のページのコンテンツを取得できることになります。 なので、この上記の URL を iframe 内で指定すればいい、ということになります。
<iframe class="box" href="http://xxxx/spoofxframeoptions?url=(iframe 内に入れたいページのURL)"></iframe>

で、これを実際にやってみたのがこちらのリンクです:
Google  ←ライトボックス内でGoogleトップページが表示できるようになりました

厳密には上記のサーブレットのサンプルはまだ不完全で、実際には文字コードを考慮したり、HTML 内の <base href="..."> 要素を追加する必要があったり、ページによっては Referer を指定しないと取得できなかったり、、、といった細かな修正が必要になるのですが、とりあえず X-Frame-Options の制約を回避する手段としては使えそうですね。


 

このページのトップヘ