@blog.justoneplanet.info

日々勉強

CentOSにCouchDBをインストールしてみる

■インストール

epelを使ってyumする。

wget http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
rpm -Uvh epel-release-5*.rpm

インストール。

yum --enablerepo=epel install couchdb

■設定

vi /etc/couchdb/local.ini

ローカルホスト以外のアクセスを許可する。必要なければパス。

;port = 5984
;bind_address = 127.0.0.1
bind_address = 0.0.0.0

■起動

以下のコマンドで起動する。

/etc/init.d/couchdb start

■アクセス

http://192.168.11.3:5984/にアクセスしてみる。

{"couchdb":"Welcome","version":"0.10.2"}

できた!(*゚▽゚)ノ

■Futon

http://192.168.11.3:5984/_utils/にアクセスするとGUIでブラウザからDBを操作できる。

futon capture

MapReduceのfunctionをjsで記述できたりレプリケーションが簡単にできたりで楽しみなDBだ。

ThriftをインストールしてPHPからCassandraを使う

ThriftでPHPからCassandraが扱えるらしいぞ((o(´∀`)o))ワクワク

■ソースの入手と解凍

wget http://www.meisei-u.ac.jp/mirror/apache/dist//incubator/thrift/0.4.0-incubating/thrift-0.4.0.tar.gz
tar zxvf thrift-0.4.0.tar.gz

■前準備

cd thrift-0.4.0
./configure

エラー1

configureの途中で以下の様なエラーがでた。

checking for boostlib >= 1.33.1... configure: error: We could not detect the boost libraries (version 1.33 or higher). If you have a staged boost library (still not installed) please specify $BOOST_ROOT in your environment and do not give a PATH to --with-boost option.  If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.
対処1

boostlib1.33.1以上が必要らしいので以下のコマンドでインストールする。

yum install boost-devel

■インストール

make
make install

エラー2

makeの途中で以下の様なエラーがでた。

BUILD FAILED
/home/admin/thrift-0.4.0/lib/java/build.xml:96: Class org.apache.tools.ant.taskdefs.ConditionTask doesn't support the nested "typefound" element.
対処1

antのバージョンがイカンらしい。(;>_<;)

As you mention, there is no package for ant 1.7.1 on RHEL 5. Until the Hadoop build is fixed to work on ant 1.6 (which will likely not happen soon if ever), you will need to:

  1. Download the Apache Ant 1.7.1 tarball
  2. Extract it
  3. Setup the environment variable ANT_HOME to point to the location you extracted the tarball
  4. Add $ANT_HOME/bin to the front of your $PATH

以下のサイトを参考にソースからインストールする。

他にもインストールに必要なライブラリがあるらしいのでインストール。

yum install libtool
yum install flex
yum install bison
yum install ruby-devel
yum install libevent-devel

再チャレンジする!

make clean
./configure
make
make install

確認してみる。

thrift -version
Thrift version 0.4.0

キタ━━━━(゚∀゚)━━━━!!

■PHPから使う準備

Cassandraの場所を確認

cassandraの場所を忘れたので確認する。

whereis cassandra
cassandra: /usr/local/cassandra

モジュールを生成

以下のコマンドを実行するとPHP用のモジュールが生成される。

cp /usr/local/cassandra/interface/cassandra.thrift /home/admin/module/cassandra.thrift
thrift --gen php /home/admin/module/cassandra.thrift
ls /home/admin/module/gen-php/cassandra
Cassandra.php  cassandra_constants.php  cassandra_types.php

Thriftのphp用ライブラリをコピー

cp -r /home/admin/thrift-0.4.0/lib/php/src/* /home/admin/gen-php/cassandra/

■PHPから使う

いよいよだ。

ディレクトリ構成

  • ext
  • packages
  • protocol
  • server
  • transport
  • autoload.php
  • Thrift.php

packages/cassandraの中に以下の3ファイルが含まれる。

  • Cassandra.php
  • cassandra_constants.php
  • cassandra_types.php

データの登録

以下のようにしてCassandraにデータを登録することができる。

<?php
require_once 'cassandra/Thrift.php';
require_once 'cassandra/protocol/TBinaryProtocol.php';
require_once 'cassandra/transport/TSocket.php';
require_once 'cassandra/transport/TBufferedTransport.php';
require_once 'cassandra/packages/cassandra/Cassandra.php';
require_once 'cassandra/packages/cassandra/cassandra_types.php';

$socket    = new TSocket('192.168.11.3', 9160);
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol  = new TBinaryProtocolAccelerated($transport);
$client    = new CassandraClient($protocol);
$transport->open();

// set column path
$columnPath = new cassandra_ColumnPath();
$columnPath->column_family = 'Standard1';
$columnPath->column        = 'column';

// set value
$client->insert(
    'Keyspace1',
    'key',
    $columnPath,
    'value',
    time(),
    cassandra_ConsistencyLevel::ONE
);

// close
$transport->close();

データの取得

以下のようにして指定したキーを値を得ることができる。

<?php
require_once 'cassandra/Thrift.php';
require_once 'cassandra/protocol/TBinaryProtocol.php';
require_once 'cassandra/transport/TSocket.php';
require_once 'cassandra/transport/TBufferedTransport.php';
require_once 'cassandra/packages/cassandra/Cassandra.php';
require_once 'cassandra/packages/cassandra/cassandra_types.php';
$socket    = new TSocket('192.168.11.3', 9160);
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol  = new TBinaryProtocolAccelerated($transport);
$client    = new CassandraClient($protocol);
$transport->open();

// set column path
$columnPath = new Cassandra_ColumnPath();
$columnPath->column_family = 'Standard1';
$columnPath->column        = 'column';

// search
$result = $client->get(
    'Keyspace1',
    'key',
    $columnPath,
    null,
    Cassandra_ConsistencyLevel::ONE
);

// close
$transport->close();

// get the result
$data = $result->column->value;
var_dump($data);//value

■ベンチマーク

以下のコードで簡単にベンチマークしてみた。

<?php
require_once 'cassandra/Thrift.php';
require_once 'cassandra/protocol/TBinaryProtocol.php';
require_once 'cassandra/transport/TSocket.php';
require_once 'cassandra/transport/TBufferedTransport.php';
require_once 'cassandra/packages/cassandra/Cassandra.php';
require_once 'cassandra/packages/cassandra/cassandra_types.php';

$socket    = new TSocket('192.168.11.5', 9160);
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol  = new TBinaryProtocolAccelerated($transport);
$client    = new CassandraClient($protocol);
$transport->open();

// set column path
$columnPath = new cassandra_ColumnPath();
$columnPath->column_family = 'Standard1';
$columnPath->column        = 'column';

// set value
$start = microtime(true);
for($i = 0; $i < 1000; $i++){
    $client->insert(
        'Keyspace1',
        'key',
        $columnPath,
        'abcdefghijklmnopqrstuvwxyz',
        time(),
        cassandra_ConsistencyLevel::ONE
    );
}
$end = microtime(true);
print($end - $start);// 0.7892119884491

// close
$transport->close();

1回目の計測値だけ倍程度の時間がかかったが2回目以降は安定して上述の時間と変わらないような処理時間であった。また、他のKVSのベンチマークと単純に比較できない理由として以下のように考えた。

  • Cassandraはテーブルのような高次元のデータ構造を持っている点
  • 今回、CassandraにはPHPで記述されたライブラリを経由してアクセスしている点

ベンチマークをしておいて言うのもオカシイが、データストアにおいて全く同じ条件というものは再現できないので単純に比較してはならない。用途に応じた選択が必要である。

Pythonにおける型変換

Pythonにおける文字列の結合はPHPやJavaScriptとは違うので気を付けないといけない。

■文字列と数値の結合

Pythonでは以下のように文字列と数値を結合することはできない。

"hoge" + 2000
#TypeError: cannot concatenate 'str' and 'int' objects

正しい記述

"hoge" + str(2000)
#hoge2000

PHPやJavaScriptでは以下のように文字列と数字の結合ができる。

PHP

print('hoge' . 2000);// hoge2000

JavaScript

document.write("hoge" + 2000);// hoge2000

■比較時の型変換

Pythonは比較時に数字のような文字列が数字に変換されたりはしない。

if(2000 == "2000"):
    print('hoge');
elif(2000 != "2000"):
    print("fuga")
#fuga

従って、PHPやJavaScriptにおける===などの型まで比較する演算子が、Pythonには存在しない。

Linux(CentOS)でユーザを作る

■設定の確認

以下のコマンドで新規に作成されるユーザの初期設定を確認できる。

useradd -D

以下のように表示された。

GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes

ふむふむ。「/bin/bash」とかは何気に確認しといた方がいい。

■ユーザを作る

useradd justoneplanet

概念としてはsshを使用してこのユーザでログインすることができる。但し、以下のコマンドを使用してパスワードを設定しないと大抵の環境ではログインできないはずだ。

passwd justoneplanet

これでログインできる。ちなみにパスワードの有無とsshにおけるログインの可否に関しては、sshの設定ファイルに起因する。

/etc/ssh/sshd_config

以下の部分ですな。

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
PasswordAuthentication yes

Google Chromeの拡張機能で顔文字を簡単に使えるツール

わざわざ「顔文字」と検索して、ctrl + 「c」でコピーし、サイトに戻ってctrl + 「v」でペーストするなんて面倒でないですか?このツールを使えば簡単に顔文字が使えるようになります。

■インストール

こちらのサイトで行えます。

■iPhone版

こちらをご覧ください。

■android版

こちらをご覧ください。

■使い方

こちらのサイトで紹介していただきました。ありがとうございます!

■アップグレードについて

Google Chromeには拡張機能の自動アップグレード機能がついてますので、しばらくしますと最新バージョンが自動的にブラウザにインストールされます。いちはやく試したい方は、設定→tools→拡張機能→拡張機能を今すぐ更新、の手順で最新版に更新することができます。

ver. 1.1.3

バージョン1.1.3

Chrome Extension内でJavaScriptを使ってclipboardにテキストをコピーする

■対応ブラウザ

Chrome Extension内なので、当然Google Chrome(Chromium)だけ。

■ソース

以下のようにすると選択したテキストをコピーできる。

document.getElementById('original').select();
document.getElementById('original').focus();
document.execCommand("Copy");

現時点ではChrome Extension内でしか使用できない。

Internet Explorer 9(IE9)β版をインストールしてみる

■インストール

わくわくすっぞ!((o(^∇^)o))

Internet Explorer 9のインストール

更新プログラムのチェックはありがたいが結構長くかかる。

Internet Explorer 9をインストールしています

インストール完了。再起動を命ぜられる。これはUX的に良くない。( ゚ ρ ゚ )

Internet Explorer 9がインストールされました

■起動

早い!インストールしているプラグインの差があるので単純に比較はできないがChromeに迫るような勢いがある★

Internet Explorer 9を使ってみる

■遷移とか

JavaScriptを多用したサイトもストレスなく閲覧が可能だ。SVGとかもイケてまう!Firefox3.6よりも遥かに滑らかに動き、Chromeよりはわずかに重い程度だ。(*^◇^)/゚

■不具合

IE9では現在のところjQueryのドラッグ&ドロップが動作しない。゜(´□`。)°゜

File APIを使ってドロップでファイルをアップロードできるjQueryプラグインを作ってみる

■ダウンロード

クリックしてダウンロードして下さい。

■動作ブラウザ

Firefox 3.6+, Google Chrome 6+

■使い方

1.HTMLでプラグインを読み込む

jQuery本体を読み込んだ後にプラグインのJavaScriptを読み込む。

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> 
<script type="text/javascript" src="jquery.dropUploader.js"></script>

2.JavaScriptでプラグインを設置する

以下のようにして、p.uploaderにファイルをドロップすると、ファイルをアップロードすることができる。

$(function(){
    $('p.uploader').dropUploader({
        "action" : "./save.php",
        "allowedMimetypes" : [
            'image/png',
            'image/jpeg',
            'image/gif'
        ],
        /*
        "onDragstart" : function(elm){
            //alert('dragstart');
        },
        "onMouseover" : function(elm){
            //alert('mouseover');
        },
        */
        "onProgress" : function(value){
            //console.log(value);
        },
        "onPartialError" : function(){
            alert('onPartialError');
        },
        "onComplete" : function(){
            alert('finished');
        }
    });
});

3.サーバーサイド(./save.php)でファイルを保存する

以下のようにするとPHPでファイルを保存することができる。但し、以下のコードはチェックを行っていないので、実際に使用する際にはファイルサイズや拡張子などのチェックを必ずしなくてはならない。

file_put_contents(basename($_GET['filename']), fopen('php://input', 'r'));

プラグイン本体

ワクワクする部分★

(function($){
    $.fn.dropUploader = function(options){
        var self    = this;
        var options = options;
        
        /**
         * _onDragstart
         * @param {Object} elm
         */
        var _onDragstart = function(elm){
            if(typeof options['onDragstart'] === 'function'){
                options['onDragstart']();
            }
            else{
                $(elm).css({
                    "border" : "5px dotted #cccccc"
                });
            }
        }
        
        /**
         * _onMouseover
         * @param {Object} elm
         */
        var _onMouseover = function(elm){
            if(typeof options['onMouseover'] === 'function'){
                options['onMouseover']();
            }
            else{
                $(elm).css({
                    "border" : "5px dotted #cccccc"
                });
            }
        }
        
        /**
         * _onMouseout
         * @param {Object} elm
         */
        var _onMouseout = function(elm){
            if(typeof options['onMouseout'] === 'function'){
                options['onMouseout']();
            }
            else{
                $(elm).css({
                    "border" : "none"
                });
            }
        }
        
        /**
         * _getAction
         * @param {string} action
         * @param {string} filename
         */
        var _getAction = function(action, filename){
            var url = '';
            if(action.indexOf('?') > -1){
                url = action + '&filename=' + encodeURIComponent(filename);
            }
            else{
                url = action + '?filename=' + encodeURIComponent(filename);
            }
            if(options['params']){
                for(var i = 0, n = options['params'].length; i < n; i++){
                    for(param in options['params'][i]){
                        url += '&' + encodeURIComponent(param) + '=' + encodeURIComponent(options['params'][i][param]);
                    }
                }
            }
            return url;
        }
        
        var result = this.each(function(){
            var elm = this;
            if(window.addEventListener){
                elm.addEventListener(
                    'dragstart',
                    function(e){
                        _onDragstart(this);
                        e.preventDefault();
                    },
                    false
                );
                elm.addEventListener(
                    'dragenter',
                    function(e){
                        _onMouseover(this);
                        e.preventDefault();
                    },
                    false
                );
                elm.addEventListener(
                    'dragover',
                    function(e){
                        _onMouseover(this);
                        e.preventDefault();
                    },
                    false
                );
                elm.addEventListener(
                    'dragout',
                    function(e){
                        _onMouseout(this);
                        e.preventDefault();
                    },
                    false
                );
                elm.addEventListener(
                    'drop',
                    function(e){
                        e.preventDefault();
                        var files    = e.dataTransfer.files;
                        var total    = 0;
                        var loaded   = 0;
                        var progress = 0;
                        var counter  = 0;
                        var xcounter = 0;
                        var response = [];
                        $(this).addClass('wait');
                        for(var i = 0; i < files.length; i++){
                            if($.inArray(files[i].type, options['allowedMimetypes']) > -1){
                                var xhr = new XMLHttpRequest();
                                total = total + files[i].size;
                                xhr.upload.onprogress = function(e){
                                    loaded = loaded + e.loaded;
                                    if(typeof options['onProgress'] === 'function'){
                                        options['onProgress']((loaded / total) * 100 + "%");
                                    }
                                }
                                xhr.upload.onload = function(e){
                                    counter++;
                                    if(i === counter){// upload is finished
                                        if(typeof options['onComplete'] === 'function'){
                                            options['onComplete']();
                                        }
                                        $(this).removeClass('wait');
                                        if(typeof options['onProgress'] === 'function'){
                                            options['onProgress'](0);
                                        }
                                    }
                                    else{
                                    }
                                }
                                var filename = files[i].name;
                                var action = _getAction(options['action'], files[i].fileName);
                                xhr.open('post', action);
                                xhr.onreadystatechange = function(e){
                                    if(this instanceof XMLHttpRequest && this.readyState === 4){
                                        xcounter++;
                                        try{
                                            response.push(this.responseText);
                                            if(i === xcounter){// upload is finished
                                                $(this).removeClass('wait');
                                                if(typeof options['onProgress'] === 'function'){
                                                    options['onProgress'](0);
                                                }
                                                options['onComplete'](response);
                                            }
                                            else{
                                            }
                                        }
                                        catch(e){
                                            if(typeof options['onError'] === 'function'){
                                                options['onError']();
                                            }
                                        }
                                    }
                                }
                                xhr.send(files[i]);
                            }
                            else{
                                if(i === counter){// upload is finished
                                    if(typeof options['onComplete'] === 'function'){
                                        options['onComplete']();
                                    }
                                    $(this).removeClass('wait');
                                    if(typeof options['onProgress'] === 'function'){
                                        options['onProgress'](0);
                                    }
                                }
                                counter++;
                            }
                        }
                    },
                    false
                );
            }
        });
        return result;
    }
})(jQuery);

クロスブラウザにしないとなー♪((O(〃⌒∇⌒〃)O))♪

androidで画面を表示し続ける

アプリによっては画面を表示し続ける必要がある。以下のコードで実現できる。

getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);