EMM研究会でポスター発表しました

JAISTで開催されたマルチメディア情報ハイディング・エンリッチメント(EMM)にて、ポスター発表をしました。これまでの研究成果を実際にアプリケーションとして実装し、デモンストレーションを交えながら説明させて頂きました。多くのご意見を頂いたので今後の検討ポイントとしたいと思います。

論文名: センシティブデータの漏洩検知による適応的な公開範囲設定システムのプロトタイプ実装

ADCS

ISSI2013で発表しました

国立情報学研究所で開催された国際ワークショップ:社会のイノベーションを誘発する情報システムで口頭発表をさせて頂きました。
SNS版のプライバシー侵害情報分類表をベースとしたプライバシー侵害検知アプリケーションを開発し、その初お披露目となり緊張しましたが、たくさんのフィードバックをいただき、今後の展開に役立てられそうです。

発表名:Setting of Access Control by Detecting Privacy Leaks in SNS

国際会議 SITIS2013で発表しました

京都で開催された国際会議 SIGNAL IMAGE TECHNOLOGY & INTERNET BASED SYSTEMS 2013で口頭発表をしました。
英語による口頭発表は初めての経験でしたが、レアジョブを約4年間続けてきた甲斐があり、問題なくこなせたと思います。
今後は、論文を正しい英語で速く書けるように、ライティングレベルの向上させていきます。

論文名:Settings of Access Control by Detecting Privacy Leaks in SNS

また、帰りがけに清水寺の夜間開放に行ってきました。
20131205_184617

コンピュータセキュリティシンポジウム2013にて、優秀論文賞・コンセプト論文賞を頂きました

コンピュータセキュリティシンポジウム2013(CSS2013)にて、優秀論文賞とコンセプト論文賞の2賞を頂きました。

論文名:SNS上のプライバシーセンシティブ情報の漏洩検知に基づく公開範囲の設定方式

今後もSNSにおけるプライバシー侵害にフォーカスし、研究を深めていきたいと思います。

Couchbase Server 2.0 ドキュメント翻訳に参加しました

Couchbaseへの理解と英語強化の為に、Couchbase Server 2.0のドキュメント翻訳に参加しました。
途中仕事が忙しくなったこともあり、当初自分が予定していたほど翻訳活動に参加できませんでしたが、他メンバの方々のおかげで無事公開されました。

Couchbase Server 2.0 ドキュメント

翻訳チームに参加したのは初めてでしたが、自社ソフトウェアのプルーフリード作業にも活かしていきたいと思います。

卒業と入学

先週末で大学院修士課程を修了しました。科目聴講生時代を含めると2年半通ったことになります。
会社勤めをしながらの大学院であった為、途中半年間の出張もあったりと非常に苦しい時もありましたが、無事乗り切ることができました。何よりもこのような状況を支えてくれた妻に感謝します。

2年間の修士課程から何を得たのかを考えてみると、技術的なことは当然ながら、研究の進め方や考え方・プレゼンテーションといったソフトスキルまで多くのことを得たと実感しています。
自分のスキルを縦軸と横軸で考えると、2年前は縦横どちらも経験相応に伸びていたと思います。但し、複数に伸びている縦軸同士が上手くリンクしていなかったり、横軸の伸びが甘いことから曖昧な知識のもとで何となく進めていた部分がありました。このような状況を打破する為に、もう一度学び直そうと考えました。
結果的に、当初の目的はクリアでき、業務を進めていく中でも自分の様々なスキルが向上していることが日々実感できています。
また、研究として「SNSにおけるプライバシー侵害」をテーマとして、現代社会のプライバシー侵害への考え方、機械による侵害要因分析と検知手法を研究しました。入学前は自分がプライバシー侵害の研究をするとは考えても見なかったですが、今後更なる技術革新が進んでいくなかで本研究テーマは非常に良いテーマであったと思います。当然、1年間の研究からは大きな成果が出せておらず、改善点も多くありますので博士後期課程で同テーマの研究を継続することにしました。
博士課程ではこれまで以上に困難があると推測しますが、3年後に学位が取れるよう研究に邁進していきます。
最後に2年前と同様に博士課程へ進むことに対して、快諾してくれた妻に感謝します。

CouchConf に行ってきました

CouchConf Tokyoに行ってきました。
仕事の都合で午前の部のみとなりましたが、Couchbaseが良く出来ていることを実感したカンファレンスでした。
自分の会社製品とよく似たコンセプトである為、今後の製品としての伸びや自分のスキルの振り方判断になればと思い、少し触っていました。Couchbaseユーザグループでver2.0のマニュアル翻訳に参加したので、これからもっと機能を確認していこうと思います。
それにしても、コンソールの機能・デザインが秀逸です。馬鹿高い自社製品も見習って欲しいところです。
couchbase_top

CouchDB on AWS 最速インストール

CouchDBをインストールをする際に何度かハマってしまったので、AWS上で動かすまでの最速インストール手順を残します。
以下の手順を実行する前に、EC2インスタンスをLaunchしておきます。なお、今回はAmazon Imageを対象としています。

ssh -i [your pem-file] ec2-user@[your ec2-instance]
sudo yum -y update
sudo yum -y install git expect
mkdir git
git clone https://gist.github.com/1171217.git
cd git

vi couchdb-ec2-install.sh
##  you need to change 2 lines for version of couchdb and erlang.
##  For checking CouchDB ver: https://github.com/apache/couchdb/tags
##  For checking Erlang ver: http://www.erlang.org/doc/apps/erts/notes.html

sudo sh couchdb-ec2-install.sh
sudo couchdb start -b
Apache CouchDB 1.2.1 (LogLevel=error) is starting.
Apache CouchDB has started. Time to relax.

Gitからcloneしたら、インストール用のshellスクリプト内のCouchDB, Erlangのバージョンだけ最新に変更して実行します。
(この実行は少し時間がかかる)
あとFutonにアクセスする時に使うAdminパスワードはスクリプト実行結果に出力されますが、
変更する場合は /usr/local/etc/couchdb/local.ini を変更します。事前にshellを変更しておいても良いです。
最後にFutonにアクセスして起動を確認して完了です。

編集後のcouchdb-ec2-install.sh はこのような感じです。(今回はCouchDB:1.2.1, Erlang: 5.9.3.1とした)

#!/bin/bash

#
# This script installs and configures couchdb on a fresh Amazon Linux AMI instance.
#
# Must be run with root privileges
# Tested with Amazon Linux AMI release 2011.02.1.1 (ami-8c1fece5)
#

export BUILD_DIR="$PWD"

# install gem dependencies
yum install gcc gcc-c++ libtool curl-devel ruby-rdoc zlib-devel openssl-devel make automake rubygems perl git-core
gem install rake --no-ri --no-rdoc

if [ ! -e "/usr/local/bin/couchdb" ]
then

  if [ ! -d "$BUILD_DIR/build-couchdb" ]
  then
    # get build-couch code
    git clone git://github.com/iriscouch/build-couchdb
    cd $BUILD_DIR/build-couchdb/
    git submodule init
    git submodule update
  fi

  # run build-couch
  cd $BUILD_DIR/build-couchdb/
  rake git="git://git.apache.org/couchdb.git tags/1.2.1" install=/usr/local
fi

# install our .ini
cat << 'EOF' > /usr/local/etc/couchdb/local.ini
[couchdb]
delayed_commits = false

[httpd]
port = 80
bind_address = 0.0.0.0
socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}]
WWW-Authenticate = Basic realm="administrator"
;WWW-Authenticate = bummer

[couch_httpd_auth]
require_valid_user = true

[log]
level = error

[admins]
EOF

# generate & set the initial password
export ADMIN_PASSWORD=`mkpasswd`
echo "admin = ${ADMIN_PASSWORD}" >> /usr/local/etc/couchdb/local.ini

# allow beam to bind to port 80 (not necessary if you make httpd.port >=1024)
setcap 'cap_net_bind_service=+ep' /usr/local/lib/erlang/erts-5.9.3.1/bin/beam

if [ ! -e "/etc/logrotate.d/couchdb" ]
then
  # add couch.log to logrotate
  ln -s /usr/local/etc/logrotate.d/couchdb /etc/logrotate.d/
  # change to daily rotation
  sed -e s/weekly/daily/g -i /usr/local/etc/logrotate.d/couchdb
  #logrotate -v -f /etc/logrotate.d/couchdb 
fi

# add couchdb user
adduser --system --home /usr/local/var/lib/couchdb -M --shell /bin/bash --comment "CouchDB" couchdb

# change file ownership
chown -R couchdb:couchdb /usr/local/etc/couchdb /usr/local/var/lib/couchdb /usr/local/var/log/couchdb /usr/local/var/run/couchdb

# run couchdb on startup
ln -s /usr/local/etc/rc.d/couchdb /etc/init.d/couchdb
chkconfig --add couchdb
chkconfig --level 345 couchdb on

# done!
echo
echo
echo "Installation complete!"
echo "Couchdb admin password was set to: ${ADMIN_PASSWORD}"
echo
echo "Couchdb is ready to start. Run:"
echo "    sudo service couchdb start"

InfoTalk Summer Workshop 2012(Amazon DynamoDB Hackathon)に行ってきました

InfoTalk Summer Workshop 2012(Amazon DynamoDB Hackathon)に行ってきました。
DynamoDBにさわるのは初めてでしたが、非常に有意義な時間でした。今回はチームに分かれてDynamoDBを使用したアプリを作ったりしました。
このDBの一番の特徴は読み書きのThroughputをコントロール出来る点です。これにより運用コストを低減することが出来ます。
但し、現状はThroughputのダウンが1日1回のみの制限がありますので、注意が必要です。

ハッカソンで作ったものではないですが、DynamoDBがどのように動くのかを確認する為に、
CouchDBに流し込んでいるPythonスクリプトを変更し、DynamoDBに入れ込むようにしてみました。
PythonからDynamoDBを扱う場合は、botoを使用します。なお、本スクリプトを実行前にテーブルは作成済みであることとします。

# -*- coding: utf-8 -*-

import sys
import tweepy
import json
import time
import jsonpickle
from datetime import datetime
import re
import boto

CONSUMER_KEY = 'YOUR CONSUMERKEY'
CONSUMER_SECRET = 'YOUR CONSUMER_SECRET'
ACCESS_TOKEN = 'YOUR ACCESS_TOKEN'
ACCESS_TOKEN_SECRET = 'YOUR ACCESS_TOKEN_SECRET'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

conn = boto.connect_dynamodb()
table = conn.get_table('twitter')
count = 0

class CustomStreamListener(tweepy.StreamListener):

    def on_status(self, status):
        results = {}
        try:
            if re.match(u"[ァ-ヶーぁ-ん]" , status.text): 

                pickled = jsonpickle.encode(status)
                results = json.loads(pickled)
                del results['_api']

                item = table.new_item(
                    hash_key=int(results['id_str']),
                    range_key=20120729,
                    attrs={'user': results['author']['screen_name'],
                           'text': results['text']})

                item.put()
                #print item

        except Exception, e:
            print >> sys.stderr, "Encountered Exception:", e
            pass

    def on_error(self, status_code):
        print >> sys.stderr, "Encountered error with status code:", status_code
        return True

    def on_timeout(self):
        print >> sys.stderr, "Timeout..."
        return True 


streaming_api = tweepy.streaming.Stream(auth, CustomStreamListener(), timeout=60)

try:  # sample(): streaming_api.sample()
    #streaming_api.filter(follow=None, locations=QUERY)
    #streaming_api.filter(follow=None, track=QUERY)
    streaming_api.sample()

except Exception, e:
    print >> sys.stderr, "Error: '%s' '%s'" % (str(datetime.now()), str(e))

finally:
    print >> sys.stderr, "disconnecting..."
    streaming_api.disconnect()

Facebookから友人の投稿写真を取得するPythonスクリプト

プライバシー侵害の研究の一環として、FacebookからFQLを使用して友人の投稿写真を取得するPYthonスクリプトを作りました。取得した写真の取り扱いには注意してください。

https://github.com/shmachid/facebook_mining/blob/master/getPic_from_fb.py

スクリプト実行手順は以下となります。

  1. 下記のサイトにアクセスし、Facebookアプリの登録をする
  2. http://miningthesocialweb.appspot.com/
    全ての権限を与える必要はないが、スクリプトが動かなくなる可能性もあるので注意してください。
    求められた権限はそのまま許可し、不要になったら、Facebookアプリを削除すれば良いです。

  3. 権限を与えたら、query実行ページに遷移する
  4. http://miningthesocialweb.appspot.com/query

  5. ページ遷移時に表示される ACCESS_TOKEN をコピーする
  6. ディレクトリ配下の out/facebook.access_token に3のTOKENを貼り付ける
  7. プログラムを実行する
  8. 自分のコネクション数にもよるが、画像1万枚で4時間ほどかかります。

  9. 処理終了後、ディレクトリ配下に picture/ が出来るので写真を確認する
  10. 一枚ずつ見るのが面倒な場合は、 out/get_picture.html を開いても良です。

ディレクトリ構成は以下のようにします。out/以下にトークン情報を格納します。

[shmachid@hoge:~/fb] $ ls
getPic_from_fb.py  out
[shmachid@hoge:~/fb] $ cd out
[shmachid@hoge:~/fb/out] $ ls
facebook.access_token
# -*- coding: utf-8 -*-

import os
import sys
import json
import urllib
import urllib2
from urllib import urlencode
import webbrowser

class FQL(object):

    ## This class was made by ptwobrussell.
    ## please check original code.
    ## https://github.com/ptwobrussell/Mining-the-Social-Web.git

    ENDPOINT = 'https://api.facebook.com/method/'

    def __init__(self, access_token=None):
        self.access_token = access_token

    def _fetch(cls, url, params=None):
        conn = urllib2.urlopen(url, data=urlencode(params))
        try:
            return json.loads(conn.read())
        finally:
            conn.close()

    def query(self, q):
        if q.strip().startswith('{'):
            return self.multiquery(q)
        else:
            params = dict(query=q, access_token=self.access_token, format='json')
            url = self.ENDPOINT + 'fql.query'
            return self._fetch(url, params=params)

    def multiquery(self, q):
        params = dict(queries=q, access_token=self.access_token, format='json')
        url = self.ENDPOINT + 'fql.multiquery'
        return self._fetch(url, params=params)


if __name__ == '__main__':
    try:
	    ## First, you need to assign facebook application and get access-token.
        ## please get the access token from following web-site and copy&paste to file.
        ## http://miningthesocialweb.appspot.com/
        ACCESS_TOKEN = open('out/facebook.access_token').read()
        if ACCESS_TOKEN == '':
            print >> sys.stderr, "Check your token"
            exit()

        if not os.path.isdir('picture'):
            os.mkdir('picture')

        q = "select target_id from connection where source_id = me() and target_type = 'user'"

    except IOError, e:
        try:
            print >> sys.stderr, "IO Error"
            exit()
        except IndexError, e:
            print >> sys.stderr, "Index Error"
            exit()

    fql = FQL(access_token=ACCESS_TOKEN)

    ## 1. get persons id
    print "get person id start"
    persons = [str(t['target_id']) for t in fql.query(q)]
    print json.dumps(persons, indent=4)


    ## 2. get album id
    print "get album id start"
    cnt = 1
    work = []
    for i in persons:
        u = 'select aid from album where owner = %s' % (i)
        work += fql.query(u)
        print cnt
        cnt += 1

    albums = [str(t['aid']) for t in work]
    print json.dumps(albums, indent=4)


    ## 3. get photo id
    print "get photo id start"
    cnt = 1
    work2 = []
    for al in albums:
        e = 'select object_id from photo where aid = "%s"' % (al)
        work2 += fql.query(e)
        print cnt
        cnt += 1

    photos = [str(t['object_id']) for t in work2]
    while 'error_code' in photos: photos.remove('error_code')
    while 'request_args' in photos: photos.remove('request_args')
    print json.dumps(photos, indent=4)
    print "Abount photo count: %s" % (len(photos))


    ## 4. get photo url
    print "get photo url start"
    cnt = 1
    photo_srcs = []
    N = 30
    for k in range(len(photos) / N + 1):
        r = 'select src, width, height from photo_src where photo_id in (%s)' \
               % (','.join(photos[k * N:(k + 1) * N]))
        photo_srcs += fql.query(r)
        print cnt
        cnt += 1


	## 5. select picture : width=320 only
    print "get photo via urllib start"
    cnt = 1
    photo_src = []
    for photo in photo_srcs:
        if photo['width'] == 320:
            photo_src.append('<img src="' + photo['src'] + '">')
            filename = photo['src'].split('/')

            ## get picture
            try:
                urllib.urlretrieve(photo['src'], os.path.join(os.getcwd(), 'picture', filename[-1]))
            except:
                print >> sys.stderr, "Can't get this  picture '%s'" % (filename[-1])

            print cnt
            cnt += 1 


    ## 6. create HTML
    print "create HTML start"
    OUT = os.path.basename('get_picture.html')
    html = "<html><body>%s</body></html>" % (' '.join(photo_src[0:]))
    f = open(os.path.join(os.getcwd(), 'out', OUT), 'w')
    f.write(html)
    f.close()

    ## if you want to show these pictures in a browser, please execute following  command at your terminal.
    ## `open out/get_picture.html`
    #webbrowser.open('file://' + f.name)

    print "Done!"

MapReduce Views in CouchDB

前回に続いて、MapReduceによるView作成、View実行を確認してみました。
Javascript以外の言語でMapReduceを行う場合は、couchDBのiniファイルに以下のようなイメージで追記します。
正常反映されれば、FutonのView Codeセグメントでlanguageとして使用可能となります。

[query_servers]
python = /usr/bin/couchpy

下記はサンプルとして、tweetのユーザ情報でlocation = ‘Tokyo’であるtweetを出力しています。
また、本サンプルではReduce処理を使用していません。
https://github.com/shmachid/twitter_mining/blob/master/define_view_in_couch.py

# -*- coding: utf-8 -*-

import couchdb
from couchdb.design import ViewDefinition


SERVER_URL = 'YOUR COUCHDB URL'    #ex: http://localhost:5984
DB_USER = 'YOUR USER'
DB_PASSWD = 'YOUR PASSWORD'
DB = 'YOUR DB NAME'

server = couchdb.Server(SERVER_URL)
server.resource.credentials = (DB_USER, DB_PASSWD)


try:
    db = server.create(DB)

except couchdb.http.PreconditionFailed, e:
    db = server[DB]

    def mapper(doc):
        if doc['author']['location'] == "Tokyo":
            yield (doc['id'], doc['text'])

    ## if you need to use reduce function, please remove bellow the comment-tag.
    #def reducer(keys, values, rereduce):
    #    return max(values)

    view = ViewDefinition('index', 'map_location', mapper, language='python')
    #view = ViewDefinition('index', 'map_location', mapper, reducer, language='python')
    view.sync(db)


    records = [(row.key, row.value) for row in db.view('index/map_location')]

    for record in records:
        print record[1]

Viewを作成・更新後、初回実行時にViewの更新処理が走る為、View実行完了まで時間が掛かりました。(対象件数は25万件であった)
データ更新が絶えず入っている状況でViewへアクセスがあった場合は、どのよう挙動となるのかが気になります。
ログは以下のように出力されます。何か良い方法あるのかな、探したけど分からなかった。

[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244494 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244520 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244548 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244579 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244606 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244634 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244664 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244693 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244721 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244747 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244773 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244797 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244821 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244848 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244877 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244903 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244934 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244961 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244991 for tweets _design/index

Twitter Streaming API to CouchDB with tweepy and jsonpickle

Twitter Streaming API: filter()からCouchDBへ流しこむサンプルスクリプトを作りました。
返ってきたStatus情報等を全て格納しておりテーブルサイズが大きくなりやすいので、適度にフィルタを掛けると良いです。
連休前と連休中で画像共有サービスの利用頻度が変わるのかを確認する為にFilter条件をtwippleで行なったところ、ゴールデンウィーク中は1日で19万件程度tweetされていました。
連休前として2週間程前に収集した際は10万件程でしたので、行楽中は画像共有サービスの利用が増えるようです。
https://github.com/shmachid/twitter_mining/blob/master/stream2couch.py

# -*- coding: utf-8 -*-

import sys
import time
from datetime import datetime
import tweepy
import json
import jsonpickle
import couchdb


if (len(sys.argv) < 2):
    print "Usage: please check your parameter"
    sys.exit()


QUERY = sys.argv[1:]

SERVER_URL = 'YOUR COUCHDB URL'    #ex: http://localhost:5984
DB_USER = 'YOUR USER'
DB_PASSWD = 'YOUR PASSWORD'
DB = 'YOUR DB NAME'

CONSUMER_KEY = 'YOUR CONSUMERKEY'
CONSUMER_SECRET = 'YOUR CONSUMER_SECRET'
ACCESS_TOKEN = 'YOUR ACCESS_TOKEN'
ACCESS_TOKEN_SECRET = 'YOUR ACCESS_TOKEN_SECRET'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

server = couchdb.Server(SERVER_URL)
server.resource.credentials = (DB_USER, DB_PASSWD)


try:
    db = server.create(DB)

except couchdb.http.PreconditionFailed, e:
    db = server[DB]

class CustomStreamListener(tweepy.StreamListener):

    def on_status(self, status):
        results = {}
        try:
            if status.id_str in db:
                return True

            pickled = jsonpickle.encode(status)
            results = json.loads(pickled)
            del results['_api']

            db[results['id_str']] = results

        except Exception, e:
            print >> sys.stderr, "Encountered Exception:", e
            pass

    def on_error(self, status_code):
        print >> sys.stderr, "Encountered error with status code:", status_code
        return True

    def on_timeout(self):
        print >> sys.stderr, "Timeout..."
        return True 


streaming_api = tweepy.streaming.Stream(auth, CustomStreamListener(), timeout=60)
print >> sys.stderr, 'Filtering parameters: "%s"' % (' '.join(sys.argv[1:]),)

try:  # sample(): streaming_api.sample()
    streaming_api.filter(follow=None, track=QUERY)

except Exception, e:
    print >> sys.stderr, "Error: '%s' '%S'" % (str(datetime.now()), str(e))

finally:
    print >> sys.stderr, "disconnecting..."
    streaming_api.disconnect()

RICOH賞を受賞しました

大学院の有志チームで参加していたRICOH & Java developer challenge2011でRICOH賞をいただきました。
アプリケーション機能は非常にシンプルでしたが、顔認識技術とビジネスモデルとの組み合わせ、アプリケーションとしての完成度が評価されたのではと思います。また、水着の三愛がリコーの傘下といこともあり、画像解析 × ファッションレポート のアプリケーションが受け入れられたのもあるようです。

グランプリの北陸先端技術大学院さんも顔画像技術を使用されており、エンタープライズアプリケーションにおいてもこれらの技術は注目されているのだと感じました。
また、他チームのアイデアや実装は素晴らしく、若い方々の発想力に驚きました。もう少しブラッシュアップすればRICOHからアプリケーションとして出ても良いのではと思われるアプリケーションもあり良い刺激をもらえました。

約8ヶ月ほど、チームで活動をしてきましたが、最後に良い結果が出て良かったです。チームの皆さんありがとうございました。

RICOH & Java Developer Challenge 2011 最終選考 提出しました

大学院の有志メンバと参加しているRICOH & Java Developer Challenge 2011の最終選考に提出できました。
9月末に1次選考(Emulator編)を無事通過し、最終選考へ向けて実機上でMFPアプリケーションを開発してきました。
5月にチームを発足してから半年以上と長期に渡って活動してきましたが、ようやくシンプルながらも面白いモノが完成しました。
このプログラミングコンテストからは技術的な部分のみでなく、チーム活動の進め方という点に関しても多くの得る事あった為、参加して良かったと思います。

最終選考は2012/1/12(木)です。あと、Ustreamでも放送があるそうです。

PSP:Personal Software Process – 結果分析

自身の開発プロセス改善を目的として7回に渡りシナリオ仕様毎に、
計画立案 → 設計 → 設計レビュー → コーディング → コードレビュー → コンパイル → テスト → 事後分析
を繰り返し行いましたので、最後に結果を分析しました。

    1. 見積規模の正確さの分析

・  プログラムごとの見積規模と実績規模を比較し、見積の正確性を検証する。

【プログラム毎の見積規模と実績規模】

Program No. 見積規模(LOC) 実績規模/KLOC
1 60
2 150 79
3 164 124
4 124 104
5 144 96
6 137 152
7 248 189

全体的に見積規模と実績規模はある程度の近似値で見積が出来ていた。
通常最も避ける必要がある点は、見積規模に対して実績規模が大きく上まるケース(受注金額に対して、予定工数が上まってしまう為)であるが、そのような例は見受けられなかった。
仕様が複雑化していった後半のプログラムにおいても、大きな差異は無かった理由としては設計フェーズで仕様を完全に把握出来たことが功を奏したと推測される。

    1. 見積時間の正確さの分析

・  プログラムごとの見積時間と実績時間を比較し、見積の正確性を検証する。

【プログラム毎の見積時間と実績時間】

Program No. 見積時間(min) 実績時間(min)
1 300
2 120 470
3 250 297
4 315 369
5 385 565
6 224 451
7 168 381

時間に関しては、規模と対照的な結果であった。実績時間が見積時間を大きく上まっているケースが多く、終盤においてもこの現象が見られる。まだ見積時に使用する自分のデータが安定していない事と仕様の複雑さや難易度が見積時間に反映出来ていないことが影響していると推測出来る。対応方法としては、見積時間に対してある程度の時間を上乗せすると実績時間と見積時間が合致する。

    1. フェーズ毎の実績時間

・  フェーズ毎の実績時間を相関し、フェーズ間の関連を確認する。

【プログラム毎の実績時間】

プログラム 計画立案 設計 設計レビュー コーディング コードレビュー コンパイル テスト 事後分析
1 10 45 120 0 65 60
2 20 30 183 5 192 40
3 20 60 94 3 90 30
4 60 105 80 1 63 60
5 60 65 20 190 15 5 140 70
6 40 65 15 137 20 1 113 60
7 70 50 10 100 20 1 70 60
合計 280 420 45 904 55 16 733 380

コーディング(水色)とテスト(緑色)の関係をみると、コーディング時間が長くなると連動してテスト時間も大きくなっている事が確認出来る。また、ほぼ一定の距離感で連動する事が確認出来る。これにより、直感的な単純な見積を行う場合に、開発時間の3/4程度がテスト時間であると定義出来そうである。

    1. 設計、コーディングで作り込まれた欠陥の型

・  設計、コーディングで作り込まれた欠陥を型ごとに分類し、グラフにより型による欠陥数の傾向を図示する。
※ 型番号に関しては、PSPの手順書を参照のこと。

作り込み欠陥
10 20 30 40 50 60 70 80 90 100 合計
設計 0 0 0 0 0 0 0 0 0 0 0
コード 0 1 0 0 0 0 0 23 0 0 24

欠陥の殆どが80:機能に集中している。
この結果から考察すると、単純なミスというよりもまだプログラムの機能として仕様が満たされていないケースがあると思われる。プログラム3以降は設計フェーズで仕様を確実に把握してから、コーディングに移っていた事を考慮すると、テストやコンパイル前に行うコーディングレビューが不十分である可能性が高い。よって、コーディングレビューの精度を高める事で欠陥数は押さえることが出来そうである。
また、今回はEclipseを使用した為、コンパイル時の欠陥は1件であったが、自分の欠陥傾向を掴む為には統合開発環境を使用せずに計測したほうが良かったかもしれない。

    1. 設計レビュー、コードレビュー、コンパイル、テストで発見した欠陥数/KLOCの傾向

・各プロセスで発見した欠陥数/KLOCを一覧化し、傾向を分析する。

発見欠陥数 発見欠陥数/KLOC
設計レビュー 0 0
コードレビュー 0 0
コンパイル 1 1.2
テスト 23 28.6
合計 24 29.9

 

Program No. 1 2 3 4 5 6 7
欠陥数 5 4 3 3 4 3 2
欠陥数/KLOC 83.3 50.6 24.2 28.4 41.7 19.7 10.6

若干ムラはあるものの演習を行う事で、欠陥数は減少傾向である。特にプログラム3以降で欠陥が大きく下がっているのは、このプログラム以降は計画・設計フェーズを重視し、仕様を完全に把握してからコーディングを行うようにしたからと考えられる。また、プログラム5, 6,7は前回のプログラムを再利用・追加していく仕様である為、前プログラムで欠陥を押さえることが出来ていれば、必然的に当該プログラムでも欠陥が減少することが確認できた。これにより、プログラムやクラスを再利用する時は、当然ではあるが、欠陥が無い状態で利用可能とすべきことを再認識できた。

    1. 設計レビュー、コードレビュー、コンパイル、テストでの欠陥除去数/KLOC

・各プロセスで除去した欠陥数/KLOCを一覧化し、プロセス別の除去欠陥能力を示す。

除去欠陥数 除去欠陥数/KLOC 除去欠陥率
設計レビュー 0 0 0.0%
コードレビュー 0 0 0.0%
コンパイル 0 0 0.0%
テスト 24 29.9 100.0%
合計 24 29.9 100.0%

欠陥は全てテストフェーズにて確認し、除去を行った。テストフェーズは最終段階であり、後フェーズで発見するほど対応コストが高くなる為、より前フェーズである設計レビューやコードレビューで欠陥を発見する必要がある。前段で確認した欠陥タイプの分析では、80:機能 の欠陥が大半を占めたようにコードレビューでより欠陥を除去出来た可能性がある。よって、今後はレビューにより時間を割くように意識していく。

    1. レビューしたLOCに対して欠陥除去率を分析

・設計レビュー、コードレビュー時の時間当たりのレビューLOC数に対する欠陥除去率を一覧化し、傾向を分析する。

時間 LOC数 欠陥除去数 欠陥除去率
設計レビュー 45 437 0 0.0%
コードレビュー 55 0 0.0%

コードレビュー、設計レビューにおいて、1件も欠陥を発見・除去出来ていないのは問題である。まだ実施時間が1時間以下である為、今後のレビュー活動に注視する。設計に関しては、仕様を完全に把握してから進めるようにしていた為、発見出来なかったのも納得出来ているが、コードレビューでは数件は発見・除去できたはずである。

    1. 欠陥の修正時間を分析

・  欠陥の修正時間を確認する。

【テストフェーズにおける欠陥修正時間と平均修正時間】

プログラム 欠陥数 修正時間 平均修正時間
1 5 50 10
2 4 197 49.25
3 3 73 24.3
4 3 45 15
5 4 116 29
6 3 80 26.7
7 2 50 25
合計 24 611 25.5

突出して欠陥修正時間が伸びているプログラム2の際は、設計が非常に甘かったと言える。この演習で非常に時間が掛かってしまった為、プログラム3以降は設計フェーズで仕様を正確に捉えるようにシフトすることができた。また、設計レビューやコーディングレビューで事前に欠陥を発見・修正することで、テストフェーズにおける欠陥数、大きな欠陥による修正時間の増加を押さえることが出来ると考えた。

    1. パーソナルプロセスを改善するための最も優先すべき点とその理由

【最も優先すべき点】

統一した計測方法で作業ログを残し、改善点を見つける為の振り返りを行う。

【理由】

現在の個人データは不十分 且つ、データ量が少ない為、正確な見積値として使用する事が出来ない。これを改善する為に、日々の業務や個人開発おいて、統一した計測方法で作業ログを残し、後日、改善点や傾向を把握する為の振り返りを行い、パーソナルプロセス改善を行う。

    1. 現在の作業能力と将来あるべき作業能力および改善のゴール

【現在の作業能力】
規模見積に関して、今までは仕様に対する規模推測を大きく見積過ぎていた。演習を行うことで似たような仕様(過去に経験した仕様)であれば、ある程度正確な規模が見積もれるようになっている。過去の実績時間を使用することで、全体を見通した時間が見積もれるようになったが、まだ若干のブレがある。
また、計画や設計フェーズの重要性を自分のデータを通して、実感出来ている。例えば、仕様は確実に理解することで、コーディング時間や欠陥数、欠陥に対する対応時間の短縮等の効果が大きい事を理解できている。

【将来あるべき作業能力】
まず最初に持たなければならない能力としては、時間見積の正確性である。規模見積が行えたとしても、時間見積が全く異なっていれば、見積が出来ているとは言えないからである。その為に必要なアクションとしては、今後の作業ログを確実に残していき、自分の見積時の基データを収集することである。次に必要な能力としては、レビュー能力と考えた。演習では欠陥検出は出来なかったが、欠陥傾向としてコーディングレビューが有効と判断した為、本能力を向上させることにより欠陥をある程度減少させることが可能である。
最終的なゴールとしては、自分が作業を行う為の見積ではなく、一般的な技術者が作業を行う場合の適性値を見積ることである。この為には、自分の能力と一般的な技術者の能力差を把握する必要がある。

    1. PSP演習後の感想

PSP演習を実施するにつれて、設計の重要性が再認識出来たのは非常に良い結果であった。また、いつも見積を行う際にドンブリ勘定を行なっていたが、より精度の高い見積を行うことの重要性と手法を学べた事が良かった。今後は自分の基データを収集すると共に継続的にプロセス改善を行なっていき、機会があればチームに対してTSPを適用していきたい。これにより、トラブルプロジェクト(欠陥や期間、金額など)を減少させる1要素になると感じた。

ということで、PSPの演習自体の仕様としては業務にあまり活かせない可能性はありますが、
個々のプロセスを振り返る事で自身の開発プロセスを改善する第1歩となるという点では間違いありません。

以上です。

Collatz Problem with Erlang

よく複数のプログラミングパラダイムを理解することは非常に良いことだと言われますが、
今回は関数型言語であるErlangでCollatz問題を実装してみました。
Erlangをさわるのは今回が初めてになります。実装に際しては、評判の良かった戦闘機本を読みました。

普段使用している言語であれば、あっさり実装出来そうな(完全解決出来ていない問題なので、プログラム上の意味)仕様でさえも、かなりの時間が掛かってしまいました。
新しいパラダイムを理解するのは非常に大変だと実感できました。また、並行処理を実装する・理解するまではもっと訓練が必要そうです。

● 仕様
Nが1以上256未満の整数のとき、Nに対して関数fを有限回繰り返し適用すると1になることを示す。

【Collatz問題とは】
問題の概要は、「任意の0でない自然数 n をとり、
n が偶数の場合、n を 2 で割る
n が奇数の場合、n に 3 をかけて 1 を足す
という操作を繰り返すと、有限回で 1 に到達する」という主張である(1 に到達すると、1→4→2→1 を繰り返す)。

-module(hoge).
-export([f/1]).

f(X) when is_integer(X), (X >= 1), (X < 256) -> collatz(X);
f(_X) -> io:format("This function can use Integer and under 256.n").

collatz(1) -> 1;
collatz(X) when (X rem 2 == 0) -> collatzEn(X);
collatz(X) when (X rem 2 == 1) -> collatzOn(X).

collatzEn(X) ->
    io:format("~p~n", [X]),
    collatz(X div 2).

collatzOn(X) ->
    io:format("~p~n", [X]),
    collatz(X * 3 + 1).

● 実行方法・結果

  1. 実行環境
  2. OS: CentOS5.6
    Ver: Erlang R12B-5.12
    Emu: Erlang emulator Ver5.6.5

  3. 実行方法・結果
  4. $erlcによってコンパイルすると、hoge.beamが生成されます。
    その後、erlコマンドでEmulatorを起動し、実装したCollatz関数を呼び出すと、最終的に1となります。

    [shmachid@hoge work]$ erlc hoge.erl
    [shmachid@hoge work]$ erl
    Erlang (BEAM) emulator version 5.6.5  [64-bit] [smp:2] [async-threads:0] [hipe] [kernel-poll:false]
    
    Eshell V5.6.5  (abort with ^G)
    1> hoge:f(13).
    13
    40
    20
    10
    5
    16
    8
    4
    2
    1
    2>
    


PSP:Personal Software Process – Lesson7

Lesson3, Lesson5, Lesson6で開発したプログラムをもとに、Lesson7用の仕様を追加していきます。
これにより、自身の計測値さえあれば、開発に対する多少のブレを含めた上での見積時間と見積総行数を見積もることが出来ます。
※ なお、Lesson5, 6に関しては、クラスとして利用出来るよう変更してください。

Lesson7: 「線形回帰パラメータと予測区間を計算する」
● プログラム仕様スクリプト
ASGKIT PROG7を参照のこと

● 実行結果
線形回帰パラメータと予測区間を計算する

rxy: 0.954497
r2: 0.911064
tail area: 0.000018
β0: -22.552533
β1: 1.727932
yk: 644.429384
range: 230.001974
UPI: 874.431357
LPI: 414.42741

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson7: 
 * 線形回帰パラメータと予測区間を計算する
 * 
 */

public class Program7 {

	private int size;
	private double[] data1;
	private double[] data2;
	private double sumData1;
	private double sumData2;
	private double avgData1;
	private double avgData2;
	private double powData1;
	private double powData2;
	private double sumMulti;
	private double beta0;
	private double beta1;
	private double r;
	private double r2;
	private double y;
	private double tailArea;
	private double p;
	private double x;
	private double deviation;
	private double ran;
	private double range;
	private double upi;
	private double lpi;

	public static void main(String[] args) {

		Program7 pg7 = new Program7();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			// 分析対象となるデータ行を指定する
			int data1 = Integer.parseInt(args[1]);
			int data2 = Integer.parseInt(args[2]);

			// 分析対象のLOC
			long num = Long.parseLong(args[3]);

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[data1 - 1]));
				linkData2.add(Double.parseDouble(strrec[data2 - 1]));
			}
			pg7.size = linkData1.size();

			pg7.data1 = new double[pg7.size];
			pg7.data2 = new double[pg7.size];

			// 合計値を計算する
			pg7.sumData1 = pg7.calcSum(linkData1);
			pg7.sumData2 = pg7.calcSum(linkData2);

			// 合計値から平均値を計算する
			pg7.avgData1 = pg7.calcAvg(pg7.sumData1);
			pg7.avgData2 = pg7.calcAvg(pg7.sumData2);

			// X, Yの二乗の合計値、X*Yの合計値を計算する
			pg7.powData1 = pg7.calcSumPow(linkData1);
			pg7.powData2 = pg7.calcSumPow(linkData2);
			pg7.sumMulti = pg7.calcSumMulti(linkData1, linkData2);

			// β1 を計算する
			pg7.beta1 = pg7.calcBeta1();

			// β0 を計算する
			pg7.beta0 = pg7.calcBeta0();

			// R, R2 を計算する
			pg7.r = pg7.calcR();
			pg7.r2 = pg7.calcR2();

			// tail areaを計算する
			pg7.tailArea = pg7.calcTail();

			// yを計算する
			pg7.y = pg7.calcY(num);

			// xを計算する
			pg7.x = pg7.calcX();

			// 標準偏差を計算する
			pg7.deviation = pg7.calcDeviation();

			// Rangeを前計算する
			pg7.ran = pg7.calcRan(num);

			// Rangeを計算する
			pg7.range = pg7.calcRange();

			// UPIを計算する
			pg7.upi = pg7.calcUPI();

			// LPIを計算する
			pg7.lpi = pg7.calcLPI();

			System.out.println("rxy: " + pg7.changeScale(pg7.r, 6));
			System.out.println("r2: " + pg7.changeScale(pg7.r2, 6));
			System.out.println("tail area: "
					+ new DecimalFormat("0.#######").format(pg7.changeScale(
							pg7.tailArea, 8)));
			System.out.println("β0: " + pg7.changeScale(pg7.beta0, 6));
			System.out.println("β1: " + pg7.changeScale(pg7.beta1, 6));
			System.out.println("yk: " + pg7.changeScale(pg7.y, 6));
			System.out.println("range: " + pg7.changeScale(pg7.range, 6));
			System.out.println("UPI: " + pg7.changeScale(pg7.upi, 6));
			System.out.println("LPI: " + pg7.changeScale(pg7.lpi, 6));

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 合計値を計算する
	 * 
	 */
	private Double calcSum(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Double.parseDouble(i.next().toString());
		}

		return sum;

	}

	/**
	 * 平均値を計算する
	 * 
	 */
	private Double calcAvg(Double sum) {

		return sum / size;

	}

	/**
	 * X, Yの2乗合計値を計算する
	 * 
	 */
	private Double calcSumPow(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Math.pow(Double.parseDouble(i.next().toString()), 2);
		}

		return sum;

	}

	/**
	 * X*Yの合計値を計算する
	 * 
	 */
	private Double calcSumMulti(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double sum = 0;
		int i = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			data1[i] = linkData1.pop();
			data2[i] = linkData2.pop();

			sum = sum + (data1[i] * data2[i]);
			i++;
		}

		return sum;

	}

	/**
	 * β1 を計算する
	 * 
	 */
	private Double calcBeta1() {

		double num1 = (sumMulti - (size * avgData1 * avgData2));
		double num2 = (powData1 - (size * avgData1 * avgData1));

		return num1 / num2;

	}

	/**
	 * β0 を計算する
	 * 
	 */
	private Double calcBeta0() {

		return avgData2 - (beta1 * avgData1);

	}

	/**
	 * R を計算する
	 * 
	 */
	private Double calcR() {

		double num1 = (size * sumMulti) - (sumData1 * sumData2);

		double num21 = (size * powData1) - (sumData1 * sumData1);
		double num22 = (size * powData2) - (sumData2 * sumData2);

		return num1 / (Math.sqrt(num21 * num22));

	}

	/**
	 * R2 を計算する
	 * 
	 */
	private Double calcR2() {

		return r * r;

	}

	/**
	 * Tail Area を計算する
	 * 
	 */
	private Double calcTail() {

		double num1 = r * Math.sqrt(size - 2);
		double num2 = Math.sqrt(1 - r * r);

		Tdist tDist = new Tdist();
		tDist.start(num1 / num2, size - 2);
		p = tDist.getP();

		return (double) (1 - 2 * p);

	}

	/**
	 * Y を計算する
	 * 
	 */
	private Double calcY(long num) {

		return beta0 + beta1 * num;

	}

	/**
	 * PからXを計算する
	 * 
	 */
	private Double calcX() {

		RevTdist RevTdist = new RevTdist();
		RevTdist.start(0.0, size - 2, 0.35);
		double x = RevTdist.getX();

		return x;

	}

	/**
	 * 標準偏差を計算する
	 * 
	 */
	private Double calcDeviation() {

		double sum = 0;
		for (int i = 0; i < data1.length; i++) {
			sum += Math.pow((data2[i] - beta0 - beta1 * data1[i]), 2);
		}

		double dist = sum / (size - 2);
		double dev = Math.sqrt(dist);

		return dev;

	}

	/**
	 * Rangeを前計算する
	 * 
	 */
	private Double calcRan(long num) {

		double num1 = Math.pow(num - avgData1, 2);

		double num2 = 0;
		for (int i = 0; i < data1.length; i++) {
			num2 += Math.pow(data1[i] - avgData1, 2);
		}

		double sq = 1 + (double) 1 / size + (double) (num1 / num2);

		return Math.sqrt(sq);

	}

	/**
	 * Rangeを計算する
	 * 
	 */
	private Double calcRange() {

		return x * deviation * ran;

	}

	/**
	 * UPIを計算する
	 * 
	 */
	private Double calcUPI() {

		return y + range;

	}

	/**
	 * LPIを計算する
	 * 
	 */
	private Double calcLPI() {

		return y - range;

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson6

Lesson5で開発したプログラムをもとに、Lesson6用の要件を追加します。

Lesson6: 「t分布関数を0.0からxの範囲で積分した結果がpとなるようなxを求める」
● プログラム仕様スクリプト
ASGKIT PROG6を参照のこと

● 実行結果
正しい積分結果値よりxをを絞り込みながら調整しつつ、シンプソン法の最適幅、積分結果、t分布関数値を得る

OK! [W=2]
Result P: 0.0
NG! [W=2] 0.062143
NG! [W=4] 0.000017
OK! [W=8]
Result P: 0.178335
NG! [W=2] 0.116564
NG! [W=4] 0.000061
OK! [W=8]
Result P: 0.31305
NG! [W=2] 0.145412
NG! [W=4] 0.00164
OK! [W=8]
Result P: 0.396
NG! [W=2] 0.139119
NG! [W=4] 0.008551
NG! [W=8] 0.000075
OK! [W=16]
Result P: 0.441942
NG! [W=2] 0.10295
NG! [W=4] 0.021035
NG! [W=8] 0.000398
OK! [W=16]
Result P: 0.466616
NG! [W=2] 0.048413
NG! [W=4] 0.035425
NG! [W=8] 0.001493
OK! [W=16]
Result P: 0.480029
NG! [W=2] 0.014094
NG! [W=4] 0.047057
NG! [W=8] 0.003926
NG! [W=16] 0.00001
OK! [W=32]
Result P: 0.487552
NG! [W=2] 0.077696
NG! [W=4] 0.052655
NG! [W=8] 0.007991
NG! [W=16] 0.00004
OK! [W=32]
Result P: 0.491935
NG! [W=2] 0.13875
NG! [W=4] 0.050897
NG! [W=8] 0.013594
NG! [W=16] 0.000132
OK! [W=32]
Result P: 0.494589
NG! [W=2] 0.195789
NG! [W=4] 0.041945
NG! [W=8] 0.020339
NG! [W=16] 0.000344
OK! [W=32]
Result P: 0.496255
NG! [W=2] 0.13875
NG! [W=4] 0.050897
NG! [W=8] 0.013594
NG! [W=16] 0.000132
OK! [W=32]
Result P: 0.494589
NG! [W=2] 0.167808
NG! [W=4] 0.04727
NG! [W=8] 0.016859
NG! [W=16] 0.000218
OK! [W=32]
Result P: 0.495514
NG! [W=2] 0.153413
NG! [W=4] 0.049303
NG! [W=8] 0.015197
NG! [W=16] 0.000171
OK! [W=32]
Result P: 0.495078
NG! [W=2] 0.146114
NG! [W=4] 0.050156
NG! [W=8] 0.014388
NG! [W=16] 0.000151
OK! [W=32]
Result P: 0.49484
NG! [W=2] 0.149772
NG! [W=4] 0.049743
NG! [W=8] 0.01479
NG! [W=16] 0.000161
OK! [W=32]
Result P: 0.494961
NG! [W=2] 0.151592
NG! [W=4] 0.049525
NG! [W=8] 0.014993
NG! [W=16] 0.000165
OK! [W=32]
Result P: 0.49502
NG! [W=2] 0.150681
NG! [W=4] 0.049634
NG! [W=8] 0.014892
NG! [W=16] 0.000162
OK! [W=32]
Result P: 0.49499
NG! [W=2] 0.151136
NG! [W=4] 0.049578
NG! [W=8] 0.014943
NG! [W=16] 0.000164
OK! [W=32]
Result P: 0.495005
NG! [W=2] 0.150912
NG! [W=4] 0.049609
NG! [W=8] 0.014916
NG! [W=16] 0.000164
OK! [W=32]
Result P: 0.494998
NG! [W=2] 0.151025
NG! [W=4] 0.049595
NG! [W=8] 0.014929
NG! [W=16] 0.000163
OK! [W=32]
Result P: 0.495001
NG! [W=2] 0.150967
NG! [W=4] 0.049602
NG! [W=8] 0.014923
NG! [W=16] 0.000163
OK! [W=32]
Result P: 0.495
Result X: 4.60400390625

● ソースコード

import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * PSP Lesson6:
 * t分布関数を0.0からxの範囲で積分した結果がpとなるようなxを求める
 */

public class Program6 {

    private double x;
    private int dof;
    private double p;
    private double d;
    private int W;
    private int initW;
    private double E;
    private int checkF1;
    private int checkF2;
    private long adjustCount;

    public Program6() {
        initW = 1;
        W = initW;
        E = 0.00001;
        d = 0.5;
        checkF1 = 0;
        checkF2 = 0;
        adjustCount = 0;
    }

    public static void main(String[] args) {

        Program6 pg6 = new Program6();

        Double doubleArray[] = new Double[2];

        // 実行時引数からx(trial value), dof, pを取得する
        pg6.x = Double.parseDouble(args[0]);
        pg6.dof = Integer.parseInt(args[1]);
        pg6.p = Double.parseDouble(args[2]);
        boolean loopF = true;

        while (loopF) {

            for (int i = 0; i < 2; i++) {

                doubleArray[i] = (double) 0;

                // wを計算する
                double w = pg6.x / pg6.W;

                for (int j = 0; j <= pg6.W; j++) {

                    // p2を計算する
                    double p2 = 1 + (Math.pow((double) w * j, 2) / pg6.dof);
                    p2 = pg6.changeScale(p2, 6);

                    // p3を計算する
                    double p3 = Math.pow(p2, -(double) (pg6.dof + 1) / 2);
                    p3 = pg6.changeScale(p3, 6);

                    // p4を計算する
                    double p4 = pg6.calcP4(p3, pg6.dof);
                    p4 = pg6.changeScale(p4, 6);

                    // p5を計算する
                    double p5 = p3 * p4;
                    p5 = pg6.changeScale(p5, 6);

                    // p6を計算する
                    double p6 = pg6.calcP6(w, p5, j);

                    doubleArray[i] += p6;

                }

                doubleArray[i] = pg6.changeScale(doubleArray[i], 6);

                if (i == 0) {
                    pg6.W = pg6.W * 2;
                } else {
                    if ((doubleArray[i - 1] - doubleArray[i]) < pg6.E
                            && (doubleArray[i] - doubleArray[i - 1]) < pg6.E) {
                        System.out.println("OK! [W=" + pg6.W + "]");
                        System.out.println("Result P: " + doubleArray[i]);

                        if (doubleArray[i] != pg6.p) {
                            pg6.adjustD(doubleArray[i]);
                            pg6.x += pg6.d;
                            pg6.W = pg6.initW;
                            i = -1;
                        } else {
                            System.out.println("Result X: " + pg6.x);
                            loopF = false;
                        }
                    } else {
                        if (doubleArray[i - 1] > doubleArray[i]) {
                            System.out.println("NG! [W="
                                    + pg6.W
                                    + "] "
                                    + new DecimalFormat("0.######")
                                            .format(doubleArray[i - 1]
                                                    - doubleArray[i]));
                        } else {
                            System.out.println("NG! [W="
                                    + pg6.W
                                    + "] "
                                    + new DecimalFormat("0.######")
                                            .format(doubleArray[i]
                                                    - doubleArray[i - 1]));
                        }
                        // 結果保持
                        doubleArray[i - 1] = doubleArray[i];

                        // シンプソン法適用時の幅拡張
                        pg6.W = pg6.W * 2;
                        i = 0;
                    }
                }

            }
        }
    }

    /**
     * dを調整する
     * 
     */
    private void adjustD(Double result) {

        adjustCount += 1;
        double w = (double) (result - p);

        if (d < 0) {
            d = -d;
        }
        if (adjustCount == 1) {
            if (w > 0) {
                checkF1 = -1;
                checkF2 = -1;
            } else {
                checkF1 = 1;
                checkF2 = 1;
            }
        }

        // 正しい積分結果値(P)を通り越えるまでは、0.5ポイント単位で増減させる
        // 通過後は、より詳細に増減させて積分結果値(P)に近づくように調整する
        if (checkF1 == checkF2) {
            if (w > 0) {
                d = -0.5;
                checkF2 = -1;
            } else {
                d = 0.5;
                checkF2 = 1;
            }
        } else {
            if (w > 0) {
                d /= 2;
                d = -d;
            } else {
                d /= 2;
            }
        }

    }

    /**
     * p4を計算する
     * 
     */
    private Double calcP4(Double p3, int dof) {

        double base = fact(((double) (this.dof + 1) / 2) - 1);
        double frac = fact((double) this.dof / 2 - 1.0);
        return base / (Math.pow(this.dof * Math.PI, 0.5) * frac);

    }

    /**
     * p6を計算する
     * 
     */
    private Double calcP6(Double w, Double p5, int i) {

        int multi = 0;

        if (i == 0 || i == this.W) {
            multi = 1;
        // 偶数の場合はmulti = 2
        } else if (i % 2 == 0) {
            multi = 2;
        // 奇数の場合はmulti = 4
        } else {
            multi = 4;
        }

        return (double) (w / 3) * multi * p5;

    }

    /**
     * 階乗計算
     * 
     */
    private double fact(double i) {

        if (i == 1) {
            return 1;
        } else {
            if (i == 0) {
                return i;
            } else if (i < 1) {
                return i * Math.sqrt(Math.PI);
            } else {
                // 再帰呼び出しする
                return i * fact(i - 1);
            }
        }

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

PSP:Personal Software Process – Lesson5

Lesson5: 「シンプソン法を使用して、t分布関数を数値的に分析する」
● プログラム仕様スクリプト
ASGKIT PROG5を参照のこと

● 実行結果
シンプソン法の幅をより詳細に設定していき、差分が0.00001以下であれば適切な幅とみなし終了する。

NG! [W=2] 0.094385
NG! [W=4] 0.02666
NG! [W=8] 0.000162
OK! [W=16]
Result: 0.494999

● ソースコード

import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * PSP Lesson5:
 * シンプソン法を使用して、t分布関数を数値的に分析する
 */

public class Program5 {

    private double x;
    private int dof;
    private int W;
    private double E;

    public static void main(String[] args) {

        Program5 pg5 = new Program5();

        Double doubleArray[] = new Double[2];

        // 実行時引数からx, dof, Wを取得する
        pg5.x = Double.parseDouble(args[0]);
        pg5.dof = Integer.parseInt(args[1]);
        pg5.W = Integer.parseInt(args[2]);
        pg5.E = 0.00001;

        for (int i = 0; i < 2; i++) {

            doubleArray[i] = (double) 0;

            // wを計算する
            double w = pg5.x / pg5.W;

            for (int j = 0; j <= pg5.W; j++) {

                // p2を計算する
                double p2 = 1 + (Math.pow((double) w * j, 2) / pg5.dof);
                p2 = pg5.changeScale(p2, 6);

                // p3を計算する
                double p3 = Math.pow(p2, -(double) (pg5.dof + 1) / 2);
                p3 = pg5.changeScale(p3, 6);

                // p4を計算する
                double p4 = pg5.calcP4(p3, pg5.dof);
                p4 = pg5.changeScale(p4, 6);

                // p5を計算する
                double p5 = p3 * p4;
                p5 = pg5.changeScale(p5, 6);

                // p6を計算する
                double p6 = pg5.calcP6(w, p5, j);

                doubleArray[i] += p6;

            }

            doubleArray[i] = pg5.changeScale(doubleArray[i], 6);

            if (i == 0) {
                pg5.W = pg5.W * 2;
            } else {
                if ((doubleArray[i-1] - doubleArray[i]) < pg5.E
                        && (doubleArray[i] - doubleArray[i-1]) < pg5.E) {
                    System.out.println("OK! [W=" + pg5.W + "]");
                    System.out.println("Result: " + doubleArray[i]);
                } else {
                    if (doubleArray[i-1] > doubleArray[i]) {
                        System.out
                                .println("NG! [W="
                                        + pg5.W
                                        + "] "
                                        + new DecimalFormat("0.######")
                                                .format(doubleArray[i-1]
                                                        - doubleArray[i]));
                    } else {
                        System.out
                                .println("NG! [W="
                                        + pg5.W
                                        + "] "
                                        + new DecimalFormat("0.######")
                                                .format(doubleArray[i]
                                                        - doubleArray[i-1]));
                    }
                    // 結果保持
                    doubleArray[i - 1] = doubleArray[i];

                    // シンプソン法適用時の幅拡張
                    pg5.W = pg5.W * 2;
                    i = 0;
                }
            }
        }
    }

    /**
     * p4を計算する
     * 
     */
    private Double calcP4(Double p3, int dof) {

        double base = fact(((double) (this.dof + 1) / 2) - 1);
        double frac = fact((double) this.dof / 2 - 1.0);
        return base / (Math.pow(this.dof * Math.PI, 0.5) * frac);

    }

    /**
     * p6を計算する
     * 
     */
    private Double calcP6(Double w, Double p5, int i) {

        int multi = 0;

        if (i == 0 || i == this.W) {
            multi = 1;
            // 偶数の場合はmulti = 2
        } else if (i % 2 == 0) {
            multi = 2;
            // 奇数の場合はmulti = 4
        } else {
            multi = 4;
        }

        return (double) (w / 3) * multi * p5;

    }

    /**
     * 階乗計算
     * 
     */
    private double fact(double i) {

        if (i == 1) {
            return 1;
        } else {
            if (i == 0) {
                return i;
            } else if (i < 1) {
                return i * Math.sqrt(Math.PI);
            } else {
                // 再帰呼び出しする
                return i * fact(i - 1);
            }
        }

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

PSP:Personal Software Process – Lesson4

Lesson4: 「ファイルから数値データのリストを読み込み、相対規模表を作成する」
● プログラム仕様スクリプト
ASGKIT PROG4を参照のこと

● 実行結果
シンプソン法の幅をより詳細に設定していき、差分が0.00001以下であれば適切な幅とみなし終了する。

Very Small: 6.3375
Small: 8.4393
Medium: 11.2381
Large: 14.965
Very Large 19.928

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson4: 
 * ファイルから数値データのリストを読み込み、相対規模表を作成する
 */

public class Program4 {

	private int size;
	private LinkedList<Double> linkArray;
	private LinkedList<Double> logArray;

	private double vs;
	private double s;
	private double m;
	private double l;
	private double vl;

	public static void main(String[] args) {

		Program4 pg4 = new Program4();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[1]));
				linkData2.add(Double.parseDouble(strrec[2]));
			}
			pg4.size = linkData1.size();

			// アイテム当たりの規模を計算する
			pg4.linkArray = new LinkedList<Double>();
			pg4.calcVol(linkData1, linkData2);

			// アイテム毎のln(x)を計算する
			pg4.logArray = new LinkedList<Double>();
			Double lnSum = pg4.calcLn();

			// ln(x)の平均値を計算する
			Double lnAvg = pg4.calcLnAvg(lnSum);

			// 平均からの対数値の分散を計算する
			double lnPowSum = pg4.calcSumPow(lnAvg);

			// 標準偏差を計算する
			double lnDeviation = pg4.calcDeviation(lnPowSum);

			// 規模範囲を計算する
			pg4.calcScale(lnAvg, lnDeviation);

			System.out.println("Very Small: " + pg4.vs);
			System.out.println("Small: " + pg4.s);
			System.out.println("Medium: " + pg4.m);
			System.out.println("Large: " + pg4.l);
			System.out.println("Very Large " + pg4.vl);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * アイテム当たりの規模を計算する
	 * 
	 */
	private void calcVol(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double i = 0;
		double j = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			i = linkData1.pop();
			j = linkData2.pop();

			this.linkArray.add(this.changeScale(i / j, 4));

		}

	}

	/**
	 * アイテム毎のln(x)を計算する
	 * 
	 */
	private Double calcLn() {

		double j = 0;
		double logSum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {

			j = this.changeScale(
					Math.log(Double.parseDouble(i.next().toString())), 6);
			this.logArray.add(j);

			// 合計値も計算する
			logSum += j;
		}

		return this.changeScale(logSum, 5);

	}

	/**
	 * ln(x)の平均値を計算する
	 * 
	 */
	private Double calcLnAvg(Double sum) {

		return this.changeScale(sum / size, 6);

	}

	/**
	 * 平均からの対数値の分散を計算する
	 * 
	 */
	private Double calcSumPow(double calclnAvg) {

		double sum = 0;
		for (Iterator i = logArray.iterator(); i.hasNext();) {
			sum = sum
					+ Math.pow(Double.parseDouble(i.next().toString())
							- calclnAvg, 2);
		}

		return this.changeScale(sum, 6);

	}

	/**
	 * lnの標準偏差を計算する
	 * 
	 */
	private Double calcDeviation(Double sum) {

		return this.changeScale(Math.sqrt(sum / (size - 1)), 6);

	}

	/**
	 * 規模範囲を計算する
	 * 
	 */
	private void calcScale(double lnAvg, double deviation) {

		vs = this.changeScale(Math.exp(lnAvg - (2 * deviation)), 4);
		s = this.changeScale(Math.exp(lnAvg - deviation), 4);
		m = this.changeScale(Math.exp(lnAvg), 4);
		l = this.changeScale(Math.exp(lnAvg + deviation), 4);
		vl = this.changeScale(Math.exp(lnAvg + (2 * deviation)), 4);

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson3

Lesson1, Lesson3に続いて、進めていきます。
各回のスクリプトはSoftware Engineering Instituteよりダウンロード可能です。

Lesson3: 「ファイルから数値データのリストを読み込み、相関係数と線形回帰パラメータを計算し、時間等の見積を行う」
● プログラム仕様スクリプト
ASGKIT PROG3を参照のこと

● 実行結果

β0: -22.54
β1: 1.7279
r: 0.9545
r2: 0.9111
y: 644.4294

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson3:
 * ファイルから数値データのリストを読み込み、相関係数と線形回帰パラメータを計算し、時間等の見積を行う
 */

public class Program3 {

	private int size;
	private double sumData1;
	private double sumData2;
	private double avgData1;
	private double avgData2;
	private double powData1;
	private double powData2;
	private double sumMulti;
	private double beta0;
	private double beta1;
	private double r;
	private double r2;
	private double y;

	public static void main(String[] args) {

		Program3 pg3 = new Program3();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			// 分析対象となるデータ行を指定する
			int data1 = Integer.parseInt(args[1]);
			int data2 = Integer.parseInt(args[2]);

			// 分析対象のLOC
			long num = Long.parseLong(args[3]);

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[data1 - 1]));
				linkData2.add(Double.parseDouble(strrec[data2 - 1]));
			}
			pg3.size = linkData1.size();

			// 合計値を計算する
			pg3.sumData1 = pg3.calcSum(linkData1);
			pg3.sumData2 = pg3.calcSum(linkData2);

			// 合計値から平均値を計算する
			pg3.avgData1 = pg3.calcAvg(pg3.sumData1);
			pg3.avgData2 = pg3.calcAvg(pg3.sumData2);

			// X, Yの二乗の合計値、X*Yの合計値を計算する
			pg3.powData1 = pg3.calcSumPow(linkData1);
			pg3.powData2 = pg3.calcSumPow(linkData2);
			pg3.sumMulti = pg3.calcSumMulti(linkData1, linkData2);

			// β1 を計算する
			pg3.beta1 = pg3.calcBeta1();

			// β0 を計算する
			pg3.beta0 = pg3.calcBeta0();

			// R, R2 を計算する
			pg3.r = pg3.calcR();
			pg3.r2 = pg3.calcR2();

			// 最終的な結果を得る
			pg3.y = pg3.calcY(num);

			System.out.println("β0: " + pg3.beta0);
			System.out.println("β1: " + pg3.beta1);
			System.out.println("r: " + pg3.r);
			System.out.println("r2: " + pg3.r2);
			System.out.println("y: " + pg3.y);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 合計値を計算する
	 * 
	 */
	private Double calcSum(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Double.parseDouble(i.next().toString());
		}

		return sum;

	}

	/**
	 * 平均値を計算する
	 * 
	 */
	private Double calcAvg(Double sum) {

		return this.changeScale(sum / size, 2);

	}

	/**
	 * X, Yの2乗合計値を計算する
	 * 
	 */
	private Double calcSumPow(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Math.pow(Double.parseDouble(i.next().toString()), 2);
		}

		return sum;

	}

	/**
	 * X*Yの合計値を計算する
	 * 
	 */
	private Double calcSumMulti(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double sum = 0;
		double i = 0;
		double j = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			i = linkData1.pop();
			j = linkData2.pop();

			sum = sum + (i * j);
		}

		return sum;

	}

	/**
	 * β1 を計算する
	 * 
	 */
	private Double calcBeta1() {

		double num1 = (sumMulti - (size * avgData1 * avgData2));
		double num2 = (powData1 - (size * avgData1 * avgData1));

		return this.changeScale(num1 / num2, 4);

	}

	/**
	 * β0 を計算する
	 * 
	 */
	private Double calcBeta0() {

		return this.changeScale(avgData2 - (beta1 * avgData1), 2);

	}

	/**
	 * R を計算する
	 * 
	 */
	private Double calcR() {

		double num1 = (size * sumMulti) - (sumData1 * sumData2);

		double num21 = (size * powData1) - (sumData1 * sumData1);
		double num22 = (size * powData2) - (sumData2 * sumData2);

		return this.changeScale(num1 / (Math.sqrt(num21 * num22)), 4);

	}

	/**
	 * R2 を計算する
	 * 
	 */
	private Double calcR2() {

		return this.changeScale(r * r, 4);

	}

	/**
	 * Y を計算する
	 * 
	 */
	private Double calcY(long num) {

		return this.changeScale(beta0 + beta1 * num, 4);

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson2

前回に続いて、Lesson2を実施しました。
分析結果は特に載せていませんが、簡単なプログラムでもバグがあったり、結構時間を食ったりしました。
なお、PSPではコーディングに凝り過ぎる必要は無いと明記していますので、大まかな感じでOKです。
但し、コーディングフェーズを終了する際は要件が確実に含まれているか、コードエラーがないかなどのチェックを設けてる必要があります。

Lesson2: 「ソースファイルの行数、クラ数数、メソッド数をカウントする」
● プログラム仕様スクリプト
ASGKIT PROG2を参照のこと

● 実行結果

Part Name: Program3
Number of Items: 11
Part Size: 112
Total Size: 122

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.ArrayList;

/**
 * PSP Lesson2: 
 * ソフトウェア開発プロセス特論 演習1 Date 2011/10/13 ソースファイルの行数、クラス数、メソッド数をカウントする
 */
public class Program2 extends StreamTokenizer {

	private Boolean wordBool = false;
	private String beforeWord = null;
	private String className = null;
	private int lineSize = 0;
	private int totalSize = 0;
	private int methodCnt = 0;
	ArrayList<String> classArray = new ArrayList<String>();
	ArrayList<String> methodArray = new ArrayList<String>();

	public Program2(Reader r) {
		super(r);

		this.eolIsSignificant(true); // 改行コードを検出する
		this.slashStarComments(true); // /* */ 型のコメント処理を有効にする
		this.slashSlashComments(true); // //型コメントの処理を有効にする
		this.wordChars('_', '_'); // 英数字の他に識別子に使える文字の定義
		this.parseNumbers(); // 数値リテラルを解析する

		classArray.add("class");
		classArray.add("interface");
		methodArray.add("void");
	}

	public static void main(String[] args) {

		Reader reader = null;

		try {
			reader = new BufferedReader(new FileReader(new File(args[0])));
			Program2 pg2 = new Program2(reader);
			pg2.checkCnt();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	void checkCnt() throws IOException {
		while (this.nextToken() != TT_EOF) {
			switch (this.ttype) {
			case TT_EOL: // 改行コードの検出
				if (this.wordBool == true) {
					this.lineSize++;
					this.wordBool = false;
				}
				this.beforeWord = "r";
				break;
			case TT_WORD: // 語の検出(キーワードを含む)
				this.wordBool = true;

				if (this.beforeWord != null) {
					// method件数をカウントする (コンストラクタは対応出来ていない)
					if (this.methodArray.contains(this.sval)
							|| this.beforeWord.equals("return")) {
						this.methodCnt++;
					}

					// class名を取得する, 複数class対応
					if (this.classArray.contains(this.beforeWord)) {
						this.className = this.sval;
						if (this.totalSize > 0) {
							this.print();
						}
						this.initialize();
					}
				}
				this.beforeWord = this.sval;

				break;
			default: // その他のトークン {}()[]+-=*/<>,;:
				this.wordBool = true;
				this.beforeWord = "symbol";
				break;
			}
		}

		// 最終行に改行が無いケースの対応
		if (this.beforeWord.equals("symbol")) {
			this.lineSize++;
		}

		this.totalSize += this.lineSize;

		this.print();
		System.out.println("Total Size: " + this.totalSize);

	}

	private void print() {

		System.out.println("Part Name: " + this.className);
		System.out.println("Number of Items: " + this.methodCnt);
		System.out.println("Part Size: " + this.lineSize);

	}

	private void initialize() {

		this.totalSize += this.lineSize;
		this.lineSize = 0;
		this.methodCnt = 0;

	}
}

PSP:Personal Software Process – Lesson1

PSP: Personal Software Process とは、ソフトウェア開発効率の向上の為に、個人毎のソフトウェア開発プロセスを見直しする枠組みです。
PSPはグローバルに多くの大学やソフトウェア開発会社で適用しており、マイクロソフトやボーイングでも使用しているそうです。
個々がPSPによって、自分の開発時の癖やパフォーマンスを把握・改善を行い、開発効率向上を行います。
最終的には全チームメンバがPSPを実施し、TSP:Team Software Process へと発展させます。

今回はPSPガイドブック ソフトウェアエンジニア自己改善 (IT Architects’ Archive)に沿いながら、8本ほどのプログラムを開発し、開発プロセスの見直しをしました。
各プログラムは小さいものばかりで、コーディング自体は2時間程あればコーディング出来ると思います。
今回使用する言語はJAVAを選択しましたが、PHPやPascal、Cでも何でも大丈夫です。
但し、狙いはコーディングではなく、実装前の全体計画や設計、テスト、バグの傾向等を含めた事後分析と多岐に俯瞰する必要があります。
なお、各回のスクリプトはSoftware Engineering Instituteよりダウンロード可能です。

Lesson1: 「ファイルから数値データのリストを読み込み、データの平均値と標準偏差を計算する」
● プログラム仕様スクリプト
ASGKIT PROG1を参照のこと

● 実行結果

Average: 60.32
Standard Deviation: 62.26

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson1:
 * ファイルから数値データのリストを読み込み、データの平均値と標準偏差を計算する
 */

public class Program1 {

    private LinkedList<Double> linkArray;
    private double resultAvg;
    private double resultStgDeviation;

    public static void main(String[] args) {

        Program1 pg1 = new Program1();

        try {
            File f = new File(args[0]);
            FileInputStream fi = new FileInputStream(f);
            BufferedReader br = new BufferedReader(new InputStreamReader(fi,
                    "UTF-8"));
            pg1.linkArray = new LinkedList<Double>();
            String line;
            while ((line = br.readLine()) != null) {
                pg1.linkArray.add(Double.parseDouble(line));
            }

            pg1.calcAvg();
            pg1.calcStgDeviation();

            System.out.println("Average: " + pg1.resultAvg);
            System.out.println("Standard Deviation: " + pg1.resultStgDeviation);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 平均値を計算する
     * 
     */
    private void calcAvg() {

        double avg = 0;
        for (Iterator i = linkArray.iterator(); i.hasNext();) {
            avg = avg + Double.parseDouble(i.next().toString());
        }

        resultAvg = this.changeScale(avg / linkArray.size(), 2);

    }

    /**
     * 標準偏差を計算する
     * 
     */
    private void calcStgDeviation() {

        // 1. データ毎に平均値との差分を2乗し合計値を取る
        // 2. 合計値をデータ個数-1で除算し、平方根を計算する
        double wkStg = 0;
        for (Iterator i = linkArray.iterator(); i.hasNext();) {
            wkStg = wkStg
                    + Math.pow(Double.parseDouble(i.next().toString())
                            - resultAvg, 2);
        }

        resultStgDeviation = this.changeScale(
                Math.sqrt(wkStg / (linkArray.size() - 1)), 2);

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

HTC Flyer買ってきました

夏休みで台湾旅行に行ったので、HTC Flyer wifi versionを買ってきました。
結果的に価格は日本から通販で買うのとほぼ変わらなかったようですが、
これから購入する方の参考になるかもしれません。

台北にはいくつか電気街があり、今回廻ったのは忠孝新生駅の北側にある八徳路電気街となります。
その中でもビル一棟に電気器具や部品、パソコン、周辺機器などを取り扱う店がひしめき合う光華商場に何度か足を運んできました。
台湾におけるFlyerの価格ですが、上記の光華商場内のお店では大体16500NTD ≒ 43395円でした。(但し、中古です)
ここから免税申請を行うと5%が返ってきます。ですので、約41225円程度となりますが、免税申請に関しては自分から切り出して聞かないとスルーされる可能性があります。
また、どのお店も価格交渉があまり効かず、下げてくれても500NTDでした。
#1NTD ≒ 2.63円 (2011/9/17時点)

結局、外の正規HTCショップで購入したのですが、どこのお店も17900NTD ≒ 47077円の設定で、免税申請して44723円といったところです。
あと、現金かカード利用により本体の値段が変わりますので、何かを購入予定の方は予め現金を持って行くと良いです。(現金の方が当然安い)

なお、420グラムと言えども電車内で持ったままの状態では若干重く感じましたが、
iPad程重くもなく、視線も集めにくいので良い機種だと思います。

プログラミングコンテストから在宅勤務を考える

プログラミングコンテストにチームで参加してみて、技術面以外にも勉強になった事が多々ありました。
3/11の地震以降、在宅勤務を導入しようと模索する企業が増えていますが、今回のコンテストから在宅勤務を成功させる為にはルールが必要だと感じたので、在宅勤務を成功させる為の必要条件を考察しました。

なお、私達のチームでは基本的に各自の家で空いている時間を使ってタスクを進め、
コミュニケーションはSkype上でテキスト形式(chat)で行いました。

ある程度チーム単位で動く在宅勤務を成功させる為の最低限必要なルールは以下と纏めました。

  1. 活動時間帯
  2. 在宅勤務の場合、個々が時間を上手く利用出来るようになる一方、
    通常であればすぐにコミュニケーションを取って確認出来ていたような事項が
    すぐに確認出来ないといった弊害が生まれます。
    よって、在宅勤務と言えども、活動時間帯は同じにするのが良いと感じました。

  3. コミュニケーションは密に
  4. ここではテキストベースでコミュニケーションを取るのを前提としていますが、
    相手の顔が見えない分、コミュニケーションロスが頻発します。
    例えば、仕様が全メンバへ伝わりきれていなかったり、ある事項に対してのOK/NG、了承/拒否の返事が無かったり等です。
    コミュニケーションポータルサイトを利用し、全情報はこのサイトを参照するのも良いと思いますが、
    個々が積極的にコミュニケーションを取っていこうとする姿勢がまず重要です。

  5. グレーゾーンに対する対応
  6. タスクを分けて作業している場合、必ずグレーゾーンにあるタスクが発生します。
    これをどちらが取るかは不毛の戦いであり、対面式のプロジェクトでもこれらのタスクは浮いたままになることが多いと思います。
    このようなグレーゾーンタスクをどちらともなく積極的に解消していく事、またそのような気持ちを持つ事が必要です。

  7. 足りない知識や技術
  8. 個々がタスクを進める上で、足りない知識(情報)や技術が必ず出てきます。
    そのような時はまず自分で調べようとする気持ちが大事です。結果的に分からなくても良いと思います。
    その時に改めて他メンバに聞けばよいです。

  9. 同じゴールを描く
  10. 最も大事なのが、全メンバが同じゴールを共有することです。
    対面式のプロジェクトでも、同じベクトルを向く事が重要ですが、在宅形式のプロジェクトでも同様です。
    ゴールが共有出来ていれば、これまで挙げてきた事項を自主的に進めてくれる可能性があります。

対面式プロジェクトであったり、同じ在宅形式のオープンソースソフトウェアプロジェクトであれば、
これらの事項は発生しない、もしくは解消しやすい事だと思います。
(OSSの場合は、技術スキルや向上心が高い・ゴールが共有出来ている等から)
在宅において、誰かが手を抜くということは誰かがフォローするということであり、
誰かに負荷がかかるということでもあります。

今後、在宅勤務を導入していくなかで、様々なルールが定義されていくと思われますが、
最低限上記のルールを個々が認識することで良いスタートがきれると言えます。

RICOH & Java Developer Challenge 2011 提出しました

大学院の有志メンバと参加しているRICOH & Java Developer Challenge 2011でアプリケーション、ドキュメントを無事提出出来ました。
企画段階から入れると約4ヶ月掛けて、進めてきました。大学院や仕事をしながらの作業であった為、
非常に疲れましたが、チームとしてはシンプルながらも当初の案はアプリケーションに落とし込めたと思います。
また、個人としては、Javaに慣れ親しむという目標が達成出来たと思います。

1次予選の結果は今月末です。本選に進めるか分かりませんが、参加して良かったなと感じています。

PyConJP 2011に行ってきました

PyConJP 2011に行ってきました。
会場が現在通っている産業技術大学院であった為、会場学生枠で参加させて頂きました。
200人以上の方が参加していたようです。

以下のセッションに参加しました。

  1. Pythonチュートリアル
  2. 今までPythonは「Perl, Python, PHPのベンチマークを計測しました」をする際に初歩的な部分しか扱った事がありませんでした。
    本セッションに参加するにあたり、Pythonチュートリアル 第2版は一通り目を通してから参加したのですが、テンポよく教えて頂き非常に良かったです。

    Python Tutorial

  3. PythonとMongoDBでWeb開発
  4. 個人的にDBが絡んだ話がやはり楽しいなと感じました。

  5. PyQtではじめるGUIプログラミング
  6. PyQtを使用してデスクトップアプリケーションを作ってみましょうというお話でした。
    自社のDeveloper Network BlogでPyQtを使用して、基幹システムの情報を一覧表示するアプリケーションが
    紹介されていたので、サンプルを作ってみようと思います。

  7. Pythonで1万台のiPhoneを管理する
  8. 最近、スマートフォンやタブレットが浸透し、業務で使用する端末を会社が用意するのではなく、
    個人端末を使用するケースが増えています。
    その際にポイントとなるのが、セキュリティであり、本セッションでは自社システムに接続可能な端末を
    サーバ側から効率的に管理するということでした。同様の製品が多く出ているところから、
    MDM(Mobile Device Management)分野は伸びているのだなと感じました。
    なお、Androidに対するPush通知は時々こけるそうで、その点iPhoneの方が機器管理をする上では
    機能的には進んでいるそうです。

  9. Pythonによる日本語自然言語処理
  10. 入門 自然言語処理
    の12章:Pythonによる日本語自然言語処理に沿った解説でした。

10月頭に少しまとまった時間が取れそうなので、何かアプリケーションを作ってみようと思います。


WebSocketプロトコルのバージョン変更

家のマシンから自分が作ったWebSocketサービスに接続出来なくなったので、少し調べてみたら、
Chrome14からはWebSocketプロトコルのバージョンがdraft10になっていた為でした。
Chrome13まではdraft76で、draft10とは互換性が無いとの事。

使用しているPHPWebSocketはまだ対応していないようですが、下記に関して対応する必要があります。

  $status = '101 Web Socket Protocol Handshake';
  if (array_key_exists('Sec-WebSocket-Key1', $headers)) {
    // draft-76
    $def_header = array(
    'Sec-WebSocket-Origin' => $origin,
    'Sec-WebSocket-Location' => "ws://{$host}{$path}"
    );
    $digest = $this->securityDigest($headers['Sec-WebSocket-Key1'], $headers['S    ec-WebSocket-Key2'], $key3);
  } else {
    // draft-75
    $def_header = array(
    'WebSocket-Origin' => $origin,
    'WebSocket-Location' => "ws://{$host}{$path}"
    );
    $digest = '';
  }

もう少し調べたいところですが、ちょっと時間がないので、、少し落ち着いてから調べます。

こちらに詳しく書かれていました。

CouchDB, CouchAppをさわってみる

前回のCouchDBインストールに引き続き、CouchDBをさわりました。
また、CouchDBでアプリケーション開発するツールであるCouchAppをインストールし、少しさわってみます。

1. adminユーザ設定
データベース作成の前に、必要に応じてadminユーザ設定を行います。
/etc/couchdb/local.ini内にadminユーザ設定箇所あるので、任意のユーザを設定します。
設定後、管理コンソール「Futon」に行き、ログインして確認します。
場所が分かりづらかったのですが、右下部にログインリンクがあります。
また、コマンドライン経由で何らかのアクションを実行する場合、URLにユーザ名とパスワードを含めます。
例えば、http://[user]:[passwd]@[serverIP]:[port]/[db] のようなイメージとなります。

2. データベース作成
データベース作成は、ブラウザ経由でやっても良いですし、コマンドラインからも行えます。
今回はコマンドラインから行います。
下記では最初にadminユーザの指定をしなかったので、エラーとなっています。
また、”PUT”を指定しているところからも、Restfulインタフェースを通じてCouchDBを操作しているのが
確認出来ます。

[shmachid@hoge couchdb]$ curl -X PUT http://49.212.1.17:xxxx/test
{"error":"unauthorized","reason":"You are not a server admin."}
[shmachid@hoge couchdb]$ curl -X PUT http://[user]:[passwd]@49.212.1.17:xxxx/test
{"ok":true}

上記を実行後、Futon(http://49.212.1.17:xxxx/_utils/index.html)からデータベースを確認してください。

3. データ投入
作成したデータベースにデータ投入を行います。
最初はコマンドラインから直に指定してみます。

[shmachid@hoge couchdb6]$ curl -X POST -d '{"data":"hoge test"}' -H 'Content-Type:appl
ication/json; charset=UTF-8' http://[user]:[passwd]@49.212.1.17:xxxx/test
{"ok":true,"id":"ee326263173915e2e424eb8d62002388","rev":"1-2328b45ab4c4437012c5b7defd9ef59d"}

コマンドライン、ブラウザの双方でデータがJSON形式になっているのが確認出来ます。

次に作成済みのHTMLファイルを投入します。
まず、投入対象のHTMLファイルを作ります。

<html>
  <body>test hogehoge</body>
</html>

下記のようなJSONファイルを作ります。コマンドラインで指定をしていたデータ情報を記述します。

{
  "id" : "test_data",
  "_attachments" : {
      "index.html" : {
         "data" : "PGh0bWw+CiA8Ym9keT50ZXN0IGhvZ2Vob2dlPC9ib2R5Pgo8L2h0bWw+Cg==",
         "content_type" : "text/html"
      }
  }
}

上記JSONファイル中のdataには下記のbase64でエンコードした文字列をセットします。

[shmachid@hoge couchdb_test]$ openssl base64 -in index.html
PGh0bWw+CiA8Ym9keT50ZXN0IGhvZ2Vob2dlPC9ib2R5Pgo8L2h0bWw+Cg==

準備は完了したので、投入してみます。

[shmachid@hoge couchdb_test]$ curl -X POST -H 'Content-Type:application/json; charset=
UTF-8' -d @test_data.json http://[user]:[passwd]@49.212.1.17:xxxx/test
{"ok":true,"id":"ee326263173915e2e424eb8d62002945","rev":"1-d7387e19cfef70dc557a98a8d4d65816"}

再び、Futonからデータベース内を確認すると、データが増えているのが分かります。
index.htmlもJSON形式で格納されており、開く事も出来ます。
今度はFutonからExcelファイルをアップロードしてみます。
New Documentボタンを押下すると、Key値が自動提案されてきます。

4. CouchAppインストール
CouchDBでアプリケーション開発を行うツールであるCouchAppをインストールします。
ここが結構ハマったのですが、CouchAppを使用する場合、Pythonはversion2.6以上である必要があります。
sakuraVPSの場合、デフォルトではversion2.4となる為、最後のインストール部分でSyntax errorとなります。
Python2.6へのアップグレードはこちらを参考にさせてもらいました。
では、CouchAppのセットアップをしていきます。
途中、Python2.6が認識出来ずに2.4が適用されてしまう部分では、明示的に指定しています。

[shmachid@hoge couchdb6]$ curl -O http://python-distribute.org/distribute_setup.py
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 15757  100 15757    0     0  11195      0  0:00:01  0:00:01 --:--:-- 25853
[shmachid@hoge couchdb6]$ sudo /usr/local/python-2.6.2/bin/python distribute_setup.py
[sudo] password for shmachid:
Downloading http://pypi.python.org/packages/source/d/distribute/distribute-0.6.19.tar.gz
Extracting in /tmp/tmpGa3LNw
Now working in /tmp/tmpGa3LNw/distribute-0.6.19
Installing Distribute
Before install bootstrap.
Scanning installed packages
Setuptools installation detected at /usr/local/python-2.6.2/lib/python2.6/site-packages/setuptools-0.6c12dev_r88846-py2.6.egg
Egg installation
Patching...
Renaming /usr/local/python-2.6.2/lib/python2.6/site-packages/setuptools-0.6c12dev_r88846-py2.6.egg into /usr/local/python-2.6.2/lib/python2.6/site-packages/setuptools-0.6c12dev_r88846-py2.6.egg.OLD.1312983070.12
Patched done.
Relaunching...
Before install bootstrap.
Scanning installed packages
Setuptools installation detected at /usr/local/python-2.6.2/lib/python2.6/site-packages/setuptools-0.6c12dev_r88846-py2.6.egg
Egg installation
Already patched.
running install
running bdist_egg
running egg_info
writing distribute.egg-info/PKG-INFO
writing top-level names to distribute.egg-info/top_level.txt
writing dependency_links to distribute.egg-info/dependency_links.txt
writing entry points to distribute.egg-info/entry_points.txt
reading manifest file 'distribute.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'distribute.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
creating build
creating build/lib
copying pkg_resources.py -> build/lib
copying easy_install.py -> build/lib
copying site.py -> build/lib
creating build/lib/setuptools
copying setuptools/extension.py -> build/lib/setuptools
copying setuptools/__init__.py -> build/lib/setuptools
copying setuptools/depends.py -> build/lib/setuptools
copying setuptools/package_index.py -> build/lib/setuptools
copying setuptools/sandbox.py -> build/lib/setuptools
copying setuptools/archive_util.py -> build/lib/setuptools
copying setuptools/dist.py -> build/lib/setuptools
creating build/lib/setuptools/command
copying setuptools/command/bdist_rpm.py -> build/lib/setuptools/command
copying setuptools/command/test.py -> build/lib/setuptools/command
copying setuptools/command/build_py.py -> build/lib/setuptools/command
copying setuptools/command/easy_install.py -> build/lib/setuptools/command
copying setuptools/command/sdist.py -> build/lib/setuptools/command
copying setuptools/command/__init__.py -> build/lib/setuptools/command
copying setuptools/command/upload_docs.py -> build/lib/setuptools/command
copying setuptools/command/rotate.py -> build/lib/setuptools/command
copying setuptools/command/saveopts.py -> build/lib/setuptools/command
copying setuptools/command/install_lib.py -> build/lib/setuptools/command
copying setuptools/command/install_scripts.py -> build/lib/setuptools/command
copying setuptools/command/alias.py -> build/lib/setuptools/command
copying setuptools/command/build_ext.py -> build/lib/setuptools/command
copying setuptools/command/upload.py -> build/lib/setuptools/command
copying setuptools/command/register.py -> build/lib/setuptools/command
copying setuptools/command/egg_info.py -> build/lib/setuptools/command
copying setuptools/command/setopt.py -> build/lib/setuptools/command
copying setuptools/command/bdist_wininst.py -> build/lib/setuptools/command
copying setuptools/command/install.py -> build/lib/setuptools/command
copying setuptools/command/install_egg_info.py -> build/lib/setuptools/command
copying setuptools/command/develop.py -> build/lib/setuptools/command
copying setuptools/command/bdist_egg.py -> build/lib/setuptools/command
creating build/lib/setuptools/tests
copying setuptools/tests/test_build_ext.py -> build/lib/setuptools/tests
copying setuptools/tests/doctest.py -> build/lib/setuptools/tests
copying setuptools/tests/test_develop.py -> build/lib/setuptools/tests
copying setuptools/tests/server.py -> build/lib/setuptools/tests
copying setuptools/tests/test_upload_docs.py -> build/lib/setuptools/tests
copying setuptools/tests/__init__.py -> build/lib/setuptools/tests
copying setuptools/tests/test_resources.py -> build/lib/setuptools/tests
copying setuptools/tests/test_sandbox.py -> build/lib/setuptools/tests
copying setuptools/tests/test_packageindex.py -> build/lib/setuptools/tests
copying setuptools/tests/test_easy_install.py -> build/lib/setuptools/tests
copying setuptools/gui.exe -> build/lib/setuptools
copying setuptools/cli.exe -> build/lib/setuptools
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/egg
copying build/lib/easy_install.py -> build/bdist.linux-x86_64/egg
copying build/lib/pkg_resources.py -> build/bdist.linux-x86_64/egg
copying build/lib/site.py -> build/bdist.linux-x86_64/egg
creating build/bdist.linux-x86_64/egg/setuptools
copying build/lib/setuptools/extension.py -> build/bdist.linux-x86_64/egg/setuptools
copying build/lib/setuptools/__init__.py -> build/bdist.linux-x86_64/egg/setuptools
creating build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/bdist_rpm.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/test.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/build_py.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/easy_install.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/sdist.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/__init__.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/upload_docs.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/rotate.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/saveopts.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/install_lib.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/install_scripts.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/alias.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/build_ext.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/upload.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/register.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/egg_info.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/setopt.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/bdist_wininst.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/install.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/install_egg_info.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/develop.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/command/bdist_egg.py -> build/bdist.linux-x86_64/egg/setuptools/command
copying build/lib/setuptools/depends.py -> build/bdist.linux-x86_64/egg/setuptools
copying build/lib/setuptools/gui.exe -> build/bdist.linux-x86_64/egg/setuptools
copying build/lib/setuptools/package_index.py -> build/bdist.linux-x86_64/egg/setuptools
copying build/lib/setuptools/sandbox.py -> build/bdist.linux-x86_64/egg/setuptools
creating build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/test_build_ext.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/doctest.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/test_develop.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/server.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/test_upload_docs.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/__init__.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/test_resources.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/test_sandbox.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/test_packageindex.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/tests/test_easy_install.py -> build/bdist.linux-x86_64/egg/setuptools/tests
copying build/lib/setuptools/cli.exe -> build/bdist.linux-x86_64/egg/setuptools
copying build/lib/setuptools/archive_util.py -> build/bdist.linux-x86_64/egg/setuptools
copying build/lib/setuptools/dist.py -> build/bdist.linux-x86_64/egg/setuptools
byte-compiling build/bdist.linux-x86_64/egg/easy_install.py to easy_install.pyc
byte-compiling build/bdist.linux-x86_64/egg/pkg_resources.py to pkg_resources.pyc
byte-compiling build/bdist.linux-x86_64/egg/site.py to site.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/extension.py to extension.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/bdist_rpm.py to bdist_rpm.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/test.py to test.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/build_py.py to build_py.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/easy_install.py to easy_install.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/sdist.py to sdist.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/upload_docs.py to upload_docs.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/rotate.py to rotate.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/saveopts.py to saveopts.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/install_lib.py to install_lib.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/install_scripts.py to install_scripts.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/alias.py to alias.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/build_ext.py to build_ext.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/upload.py to upload.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/register.py to register.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/egg_info.py to egg_info.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/setopt.py to setopt.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/bdist_wininst.py to bdist_wininst.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/install.py to install.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/install_egg_info.py to install_egg_info.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/develop.py to develop.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/command/bdist_egg.py to bdist_egg.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/depends.py to depends.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/package_index.py to package_index.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/sandbox.py to sandbox.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/test_build_ext.py to test_build_ext.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/doctest.py to doctest.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/test_develop.py to test_develop.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/server.py to server.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/test_upload_docs.py to test_upload_docs.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/test_resources.py to test_resources.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/test_sandbox.py to test_sandbox.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/test_packageindex.py to test_packageindex.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/tests/test_easy_install.py to test_easy_install.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/archive_util.py to archive_util.pyc
byte-compiling build/bdist.linux-x86_64/egg/setuptools/dist.py to dist.pyc
creating build/bdist.linux-x86_64/egg/EGG-INFO
copying distribute.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO
copying distribute.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying distribute.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying distribute.egg-info/entry_points.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying distribute.egg-info/entry_points.txt.orig -> build/bdist.linux-x86_64/egg/EGG-INFO
copying distribute.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying distribute.egg-info/zip-safe -> build/bdist.linux-x86_64/egg/EGG-INFO
creating dist
creating 'dist/distribute-0.6.19-py2.6.egg' and adding 'build/bdist.linux-x86_64/egg' to it
removing 'build/bdist.linux-x86_64/egg' (and everything under it)
Processing distribute-0.6.19-py2.6.egg
creating /usr/local/python-2.6.2/lib/python2.6/site-packages/distribute-0.6.19-py2.6.egg
Extracting distribute-0.6.19-py2.6.egg to /usr/local/python-2.6.2/lib/python2.6/site-packages
Adding distribute 0.6.19 to easy-install.pth file
Installing easy_install script to /usr/local/python-2.6.2/bin
Installing easy_install-2.6 script to /usr/local/python-2.6.2/bin

Installed /usr/local/python-2.6.2/lib/python2.6/site-packages/distribute-0.6.19-py2.6.egg
Processing dependencies for distribute==0.6.19
Finished processing dependencies for distribute==0.6.19
After install bootstrap.
Creating /usr/local/python-2.6.2/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg-info
Creating /usr/local/python-2.6.2/lib/python2.6/site-packages/setuptools.pth
[shmachid@hoge couchdb6]$ sudo easy_install pip
Searching for pip
Reading http://pypi.python.org/simple/pip/
Reading http://www.pip-installer.org
Reading http://pip.openplans.org
Best match: pip 1.0.2
Downloading http://pypi.python.org/packages/source/p/pip/pip-1.0.2.tar.gz#md5=47ec6ff3f6d962696fe08d4c8264ad49
Processing pip-1.0.2.tar.gz
Running pip-1.0.2/setup.py -q bdist_egg --dist-dir /tmp/easy_install-g89SGU/pip-1.0.2/egg-dist-tmp-Ci0LUo
warning: no files found matching '*.html' under directory 'docs'
warning: no previously-included files matching '*.txt' found under directory 'docs/_build'
no previously-included directories found matching 'docs/_build/_sources'
Adding pip 1.0.2 to easy-install.pth file
Installing pip script to /usr/local/python-2.6.2/bin
Installing pip-2.6 script to /usr/local/python-2.6.2/bin

Installed /usr/local/python-2.6.2/lib/python2.6/site-packages/pip-1.0.2-py2.6.egg
Processing dependencies for pip
Finished processing dependencies for pip
[shmachid@hoge couchdb6]$ sudo pip install couchapp
Downloading/unpacking couchapp
  Downloading Couchapp-0.8.1.tar.gz (422Kb): 422Kb downloaded
  Running setup.py egg_info for package couchapp
    warning: manifest_maker: MANIFEST.in, line 5: 'recursive-include' expects <dir> <pattern1> <pattern2> ...
Installing collected packages: couchapp
  Running setup.py install for couchapp
    building 'couchapp.autopush._select' extension
    gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -DHAVE_EPOLL=1 -DHAVE_SYS_EPOLL_H=1 -I/usr/local/python-2.6.2/include/python2.6 -c couchapp/autopush/selectmodule.c -o build/temp.linux-x86_64-2.6/couchapp/autopush/selectmodule.o
    gcc -pthread -shared build/temp.linux-x86_64-2.6/couchapp/autopush/selectmodule.o -o build/lib.linux-x86_64-2.6/couchapp/autopush/_select.so
    Linking /home/shmachid/work/couchdb6/build/couchapp/build/lib.linux-x86_64-2.6/couchapp/autopush/_select.so to /home/shmachid/work/couchdb6/build/couchapp/couchapp/autopush/_select.so
    changing mode of build/scripts-2.6/couchapp from 644 to 755
    warning: manifest_maker: MANIFEST.in, line 5: 'recursive-include' expects <dir> <pattern1> <pattern2> ...
    changing mode of /usr/local/python-2.6.2/bin/couchapp to 755
Successfully installed couchapp
Cleaning up...

5. CouchApp 確認
CouchAppのインストールが完了したので、確認します。
アプリケーションを作ってみます。

[shmachid@hoge couchdb6]$ couchapp generate fuga
2011-08-11 02:02:00 [INFO] /home/shmachid/work/couchdb6/fuga generated.

作成したベーステンプレートをCouchDBへpushします。

[shmachid@hoge couchdb6]$ cd fuga
[shmachid@hoge fuga]$ couchapp push http://[user]:[passwd]@49.212.1.17:xxxx/test
2011-08-11 02:04:28 [INFO] Visit your CouchApp here:
http://49.212.1.17:xxxx/test/_design/fuga/index.html


Futonにて、一緒にpushしたその他のファイルが格納されたことを確認できます。

なお、これらはみてわかるクラウドマガジンvol.3 (日経BPパソコンベストムック)
を参考にさせていただきました。

以上です。


CentOS5.6にCouchDBをインストール

ドキュメント指向型のNoSQLであるCouchDBをインストールしてみました。
OSはCentOS5.6となります。

1. CouchDBインストール
yumからCouchDBをパッケージインストールします。

[shmachid@hoge ~]$ yum list | grep couchdb
couchdb.x86_64                             0.11.2-2.el5                epel
[shmachid@hoge ~]$ su -
[root@hoge ~]# yum install couchdb enablerepo=remi
Loaded plugins: downloadonly, fastestmirror, priorities
Loading mirror speeds from cached hostfile
 * addons: ftp.nara.wide.ad.jp
 * base: ftp.nara.wide.ad.jp
 * epel: ftp.kddilabs.jp
 * extras: ftp.nara.wide.ad.jp
 * updates: ftp.nara.wide.ad.jp
addons                                                              |  951 B     00:00
base                                                                | 2.1 kB     00:00
epel                                                                | 3.7 kB     00:00
extras                                                              | 2.1 kB     00:00
updates                                                             | 1.9 kB     00:00
Excluding Packages in global exclude list
Finished
208 packages excluded due to repository priority protections
Setting up Install Process
No package enablerepo=remi available.
Resolving Dependencies
--> Running transaction check
---> Package couchdb.x86_64 0:0.11.2-2.el5 set to be updated
--> Processing Dependency: erlang-tools for package: couchdb
--> Processing Dependency: erlang-sasl for package: couchdb
--> Processing Dependency: erlang-crypto for package: couchdb
--> Processing Dependency: erlang-mochiweb for package: couchdb
--> Processing Dependency: erlang-stdlib for package: couchdb
--> Processing Dependency: erlang-kernel for package: couchdb
--> Processing Dependency: erlang-erts for package: couchdb
--> Processing Dependency: erlang-inets for package: couchdb
--> Processing Dependency: erlang-ibrowse for package: couchdb
--> Processing Dependency: erlang-oauth for package: couchdb
--> Processing Dependency: libjs.so.1()(64bit) for package: couchdb
--> Running transaction check
---> Package erlang.x86_64 0:R12B-5.10.el5 set to be updated
---> Package erlang-ibrowse.x86_64 0:2.1.0-1.el5 set to be updated
---> Package erlang-mochiweb.x86_64 0:1.4.1-5.el5 set to be updated
---> Package erlang-oauth.x86_64 0:1.0.1-1.el5 set to be updated
---> Package js.x86_64 0:1.70-8.el5 set to be updated
--> Finished Dependency Resolution

Dependencies Resolved

===========================================================================================
 Package                   Arch             Version                   Repository      Size
===========================================================================================
Installing:
 couchdb                   x86_64           0.11.2-2.el5              epel           558 k
Installing for dependencies:
 erlang                    x86_64           R12B-5.10.el5             epel            39 M
 erlang-ibrowse            x86_64           2.1.0-1.el5               epel            48 k
 erlang-mochiweb           x86_64           1.4.1-5.el5               epel           368 k
 erlang-oauth              x86_64           1.0.1-1.el5               epel            27 k
 js                        x86_64           1.70-8.el5                epel           392 k

Transaction Summary
===========================================================================================
Install       6 Package(s)
Upgrade       0 Package(s)

Total download size: 41 M
Is this ok [y/N]: y
Downloading Packages:
(1/6): erlang-oauth-1.0.1-1.el5.x86_64.rpm                          |  27 kB     00:00
(2/6): erlang-ibrowse-2.1.0-1.el5.x86_64.rpm                        |  48 kB     00:00
(3/6): erlang-mochiweb-1.4.1-5.el5.x86_64.rpm                       | 368 kB     00:00
(4/6): js-1.70-8.el5.x86_64.rpm                                     | 392 kB     00:00
(5/6): couchdb-0.11.2-2.el5.x86_64.rpm                              | 558 kB     00:00
(6/6): erlang-R12B-5.10.el5.x86_64.rpm                              |  39 MB     00:02
-------------------------------------------------------------------------------------------
Total                                                       12 MB/s |  41 MB     00:03
Running rpm_check_debug
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing     : erlang                                                              1/6
  Installing     : js                                                                  2/6
  Installing     : erlang-oauth                                                        3/6
  Installing     : erlang-mochiweb                                                     4/6
  Installing     : erlang-ibrowse                                                      5/6
  Installing     : couchdb                                                             6/6

Installed:
  couchdb.x86_64 0:0.11.2-2.el5

Dependency Installed:
  erlang.x86_64 0:R12B-5.10.el5                erlang-ibrowse.x86_64 0:2.1.0-1.el5
  erlang-mochiweb.x86_64 0:1.4.1-5.el5         erlang-oauth.x86_64 0:1.0.1-1.el5
  js.x86_64 0:1.70-8.el5

Complete!

あっさりインストールは完了します。CouchDBのdefault portは5984となります。
また、CouchDBにはWebサーバ機能も付加されている為、bindアドレスを変更します。
/etc/couchdb以下にCouchDBのiniファイルがありますので、必要に応じてbindアドレスとportを変更します。

[root@hoge couchdb]# pwd
/etc/couchdb

2. CouchDB起動
以下を実行し、CouchDBを起動します。option “-b”を付与することで、バックグラウンド実行となります。

[root@hoge ~]# couchdb
Apache CouchDB 0.11.2 (LogLevel=info) is starting.

=INFO REPORT==== 10-Aug-2011::01:20:31 ===
    warning: "TODO: max is currently unsupported"
    max: "2048"
Apache CouchDB has started. Time to relax.
[info] [<0.29.0>] Apache CouchDB has started on http://49.212.1.17:xxxx/

上記の”xxxx”には先ほど変更したportが入ります。
なお、CouchDBを止めたい時は$ couchdb -d で終了させます。
続いて、上記のURLにアクセスします。まず、curlで実行してみます。
JSON形式でレスポンスが返ってきます。また、今回のバージョンは0.11.2とわかります。

[shmachid@hoge ~]$ curl  http://49.212.1.17:xxxx/
{"couchdb":"Welcome","version":"0.11.2"}

ブラウザからアクセスしてみます。

3. 管理コンソール「Futon」
CouchDBには、管理コンソール「Futon」が用意されています。
先ほどのURLに”_utils”を付加し、ブラウザからアクセスします。

これでベースセットアップは完了です。

以上です。

CAP定理, BASEのまとめ

以前からCAP定理やBASEがイマイチ腹落ちしていなかったので、調べました。

1. ACID
まず、RDBMSのトランザクション処理におけるACID特性は以下である。

  1. Atomicity: 原子性
  2. トランザクションが実行されるか、全くされないかのどちらかである。

  3. Consistency: 一貫性
  4. データベースの関係制約を保証する。

  5. Isolation: 独立性
  6. トランザクション中に経過が他トランザクションから参照出来ない。隔離性があるということ。

  7. Durability: 永続性
  8. トランザクション完了後、データは失われない。耐障害性があるということ。

2. CAP定理
続いて、クラウド化(クラウドは分散システムと考える)した際はACID特性が保つのは非常に大変である。(無理ではない)
CAP定理では、C.A.Pの3つの内、同時に満たせるのは2つまでという主張。
Eric Brewer氏のCAP定理

  1. Consistency: 一貫性
  2. データ更新後の次アクセス時には、更新後データが読取可能である事。

  3. Availability: 可用性
  4. ユーザがアクセスした時はサービス利用可能である事。稼働率。

  5. Partition Tolerance: ネットワーク分断耐性
  6. 分散システムを構成する物理/仮想サーバのうち、1つが壊れてもサービスは継続可能である。

3つの内、2つを選択すると以下となる。

  • C + A: 一貫性と可用性を選択すると、分散処理が出来ない。  例: 単一サーバ
  • C + P: 一貫性とネットワーク分断耐性を選択すると、可用性が失われる。  例: 分散データベース
  • A + P: 可用性とネットワーク分断耐性を選択すると、一貫性が失われる。  例: DNS
  • クラウド環境ではデータ量やScalabilityの観点から分散処理が必要となる。
    よって、P: Partition Torerance を選択することとなる。
    では、残り1枠は C(一貫性), P(可用性)のどちらを選択するかであるが、
    Webではユーザがアクセスした時はサービスが利用可能であることが前提である為、
    基本的には可用性を選択する事になる。(個々の要件によって、選択する事)
    その場合、一貫性の取り扱いが問題となる。そこで出てくるのばBASEである。

    3. BASE
    ACID特性が個別のデータベースのトランザクションの特性であるのに対して、
    BASEはデータベース機能を含んだシステム全体の特性である。

    1. Basically Available
    2. 可用性が高く、常に利用可能である。

    3. Soft-State
    4. ステータスは厳密ではない。外部から送られる情報により変化する。

    5. Eventually Consistent
    6. 最終的には一貫性が保たれる。ある時点では更新されていないケースがある。

    4. まとめ
    クラウドではCAP定理のうち、A(可用性)とP(ネットワーク分断耐性)を選択する。
    C(一貫性)については、若干妥協して BASEのEventually Consistent を採用する。
    ある時点では、一貫性がない可能性があるが、最終的には一貫性が保つことが出来る。

    気になった点としては、企業システムにおいてはこの一貫性の妥協がどのように許容されるのかです。
    ここはもう少し調べることとします。

    なお、以下を参考に勉強させていただきました。
    Cloudの技術的特徴
    CAPのCとACIDのC

    memcached 設定メモ

    memcachedをPHPから使えるようにしたので、設定方法をメモします。

    1. memcached install
    最初にmemcachedをインストールします。repositoryはremiにしました。

    [root@hoge ~]# yum install memcached --enablerepo=remi
    Loaded plugins: downloadonly, fastestmirror, priorities
    Loading mirror speeds from cached hostfile
     * addons: ftp.nara.wide.ad.jp
     * base: ftp.nara.wide.ad.jp
     * epel: ftp.iij.ad.jp
     * extras: ftp.nara.wide.ad.jp
     * remi: remi-mirror.dedipower.com
     * updates: ftp.nara.wide.ad.jp
    Excluding Packages in global exclude list
    Finished
    303 packages excluded due to repository priority protections
    Setting up Install Process
    Resolving Dependencies
    --> Running transaction check
    ---> Package memcached.x86_64 0:1.4.5-2.el5.remi set to be updated
    --> Finished Dependency Resolution
    
    Dependencies Resolved
    
    ==========================================================================================================
     Package                  Arch                  Version                         Repository           Size
    ==========================================================================================================
    Installing:
     memcached                x86_64                1.4.5-2.el5.remi                remi                 73 k
    
    Transaction Summary
    ==========================================================================================================
    Install       1 Package(s)
    Upgrade       0 Package(s)
    
    Total download size: 73 k
    Is this ok [y/N]: y
    Downloading Packages:
    memcached-1.4.5-2.el5.remi.x86_64.rpm                                              |  73 kB     00:02
    Running rpm_check_debug
    Running Transaction Test
    Finished Transaction Test
    Transaction Test Succeeded
    Running Transaction
      Installing     : memcached
    
    Installed:
      memcached.x86_64 0:1.4.5-2.el5.remi
    
    Complete!
    

    完了したら、memcachedを立ち上げたら、telnetでlocalhostのport:11211に接続します。
    memcachedのデフォルトportは11211で、接続出来たら stats を実行すると、下記となります。
    telnetからquit出来なかった場合hあ、ctrl + ] を実行後にquitします。

    [shmachid@hoge ~]$ memcached -u memcached -d
    [shmachid@hoge ~]$ telnet localhost 11211
    Trying 127.0.0.1...
    Connected to localhost.localdomain (127.0.0.1).
    Escape character is '^]'.
    stats
    STAT pid 32743
    STAT uptime 79
    STAT time 1312625362
    STAT version 1.4.5
    STAT pointer_size 64
    STAT rusage_user 0.000000
    STAT rusage_system 0.000999
    STAT curr_connections 10
    STAT total_connections 11
    STAT connection_structures 11
    STAT cmd_get 0
    STAT cmd_set 0
    STAT cmd_flush 0
    STAT get_hits 0
    STAT get_misses 0
    STAT delete_misses 0
    STAT delete_hits 0
    STAT incr_misses 0
    STAT incr_hits 0
    STAT decr_misses 0
    STAT decr_hits 0
    STAT cas_misses 0
    STAT cas_hits 0
    STAT cas_badval 0
    STAT auth_cmds 0
    STAT auth_errors 0
    STAT bytes_read 7
    STAT bytes_written 0
    STAT limit_maxbytes 67108864
    STAT accepting_conns 1
    STAT listen_disabled_num 0
    STAT threads 4
    STAT conn_yields 0
    STAT bytes 0
    STAT curr_items 0
    STAT total_items 0
    STAT evictions 0
    STAT reclaimed 0
    END
    

    続いて、memcachedへデータを入れてみます。
    下記では、key:testに対して、value:12345をセットしています。

    [shmachid@hoge ~]$ telnet localhost 11211
    Trying 127.0.0.1...
    Connected to localhost.localdomain (127.0.0.1).
    Escape character is '^]'.
    set test 0 0 5
    12345
    STORED
    get test
    VALUE test 0 5
    12345
    END
    

    2. PHP拡張モジュール install
    memcachedは使えるようになったので、次にPHPからmemcachedを使用する為に拡張モジュールをインストールします。
    peclには”memcached”もあるようですが、こちらはlibmemcachedの事前インストールが必要となる為、
    今回は”memcache”をインストールしました。

    [root@hoge ~]# pecl install memcache
    

    PHPからmemcachedに追加出来るか確認します。下記では、key:hogeに対して、value:fugaをセットしています。

    <?php
    
     $memcache_obj = memcache_connect("localhost", 11211);
    
     $memcache_obj->add('hoge', 'fuga', false, 0);
    
    ?>
    

    上記のPHPを実行後、再度telnetで接続してプログラムでセットした値が格納されたかを確認します。

    [shmachid@hoge ~]$ telnet localhost 11211
    Trying 127.0.0.1...
    Connected to localhost.localdomain (127.0.0.1).
    Escape character is '^]'.
    get hoge
    VALUE hoge 0 4
    fuga
    END
    

    key:hoge に対して、value:fugaが取れましたので、確認OKです。

    以上です。

    デモの進め方

    Being Geek ―ギークであり続けるためのキャリア戦略の中で、『デモ』についての章があったのでメモ書きします。
    私はかなり緊張しやすく、伝えたい事が半分程度にしか伝わらない事が多い為、備忘の為に残します。

  • デモとプレゼンテーションは異なる
  • デモは参加者により参加してもらうもの。

  • デモ対象について最も知っているのは自分
  • だから自信を持てば良い。

  • 3つのフェーズ
  • デモをする場合、相手が誰であろうと目的はみんな一緒。
    みんな目的は情報を得る事である。デモをする際は下記の基本ルールを守る。

    1. 背景説明
    2. デモを理解する上で知っておくべき事を伝える

    3. 内容説明
    4. 発見、アイデアの具体的内容を詳しく説明する。

    5. ポイント説明
    6. 発見、アイデアのどこに最も価値があるかをようやくする。
      そして、聞き手に意見を求める。

  • 聞き手のタイプ
    1. 何も言わない人
    2. この手のタイプが最も厄介である。
      打破するには、背景情報や発見に至るまでの物語を長く話すと良い。
      毎回このタイプを想定しておけば、怖いものはない。

    3. 積極的な人
    4. 一見、厄介に感じるが、最もありがたい存在。この手のタイプから建設的な意見が出だすと
      デモは成功していると言える。だが、積極的な人がよく理解出来ていない状況の場合は
      かなりまずい状況と言える。まずは深呼吸をして立て直そう。
      デモ対象について最も知っているのは自分。

    5. 仕切り屋
    6. 仕切り屋は途中(もしくは最初から)で自分のペースに持って行こうとする。
      デモを行う人ではないのに。そんな時は”完全な沈黙”、”デモのテンポを変更する”等を行い、
      自分のペースに引き戻す。また、仕切り屋によって、あらぬ方向へデモが進んでしまう事により
      新たな発見があるかもしれない。(自分が見えていなかった事など)

  • 理想のデモとは、発表者のいらないデモ
  • そんな事は絶対に不可能。理想のデモをどのようにやれば良いとは言えないが、
    結局試行錯誤を重ねていくしかない。

    結局、プレゼンテーションの本(ZENなど)にも同じ事が書いてありますが、
    本番前の練習がモノを言うということでしょうか。


    Comet メモ

    WebSocketを使用したmashupを作るにあたり、以前から知られているCometについては、
    サーバからpushする技術ぐらいにしか理解していなかった為、少し調べてみました。

  • ■ 目的
  • 従来のWebページはクライアントからリクエストがあった時のみ、サーバからレスポンスが返ってくる。
    その際、クライアントからはHTTPコネクションを生成し、サーバからレスポンスが返るとコネクションはクローズする。
    上記の手法でも通常は問題は無いが、リアルタイム性を求められるアプリケーション等の場合は不都合である。
    (例えば、チャットや株価情報など)
    その場合、AJAX等で非同期にサーバへHTTP通信をする手法が考えられるが、
    サーバ/クライアントに負荷が掛かったり、他ユーザからの更新情報をリアルタイムに受け取る事は難しい。
    こうした経緯からCometと呼ばれる概念(Cometは複数の技術の総称)が生まれた。
    なお、Comet, AJAXの名称は共に洗剤から由来している。

  • ■ トランスポート処理手法
    1. ポーリング
    2. サイトあるいはアプリケーションから、更新有無を問い合わせるリクエストを定期的に送信するという非常に単純な仕組み。
      デメリットとして等間隔(例えば、5秒など)でリクエストを送信するが、サーバ側で高負荷になっていた場合も常にリクエストを出し続けてしまう事が起こり得る。この場合、サーバ側に未処理リクエストが溜まり続けてしまう。

    3. ロングポーリング
    4. クライアントからリクエストをサーバに送信し、すぐに返却可能なデータがある場合はすぐにレスポンスを返すが、まだ返却可能なレスポンスデータが存在しない場合は、レスポンスを返さずに未応答リクエストとしてサーバ側で保持する。
      データの用意が出来た段階で、保持していた接続を見つけてレスポンスを返す。
      Meeboで使用されて、有名となった。
      ロングポーリングの発展系として、スマートポーリングと呼ばれる手法がある。
      スマートポーリングはレスポンスデータが返って来なかった場合にリクエスト頻度を下げるポーリング手法である。
      例えば、空レスポンスであったら、次回のリクエスト間隔は1.5倍にするなど。

    5. 永久フレーム
    6. ロングポーリングは永久フレームから生まれた手法である。
      永久フレームとは、非表示のインラインフレーム(iframe)を開き、
      チャンク形式エンコーディングを利用して送信する。
      この方式は非常に大きなドキュメントを段階的にロードする為に考案された。
      但し、IEでは永久フレームを使用した場合、チャンクを受け取る度に「カチッ」という効果音がなってしまう為、
      使用に耐えないものであった。
      そこで登場したのが、GoogleがGoogle Talk考案したhtmlfileというActiveXオブジェクトを用いる手法である。
      この手法により、IE問題はクリアされ永久フレームは有効な手法として広まった。

    7. XHRストリーミング
    8. サーバと通信をする為の最も良い方法はXMLHttpRequestを使用する事である。
      通常、ポーリングやロングポーリングのデータ転送で使用されている。
      XHRストリーミングでは、XHR通信を行った後にreadystateをチェックし続け、
      接続が切れる前に同一コネクションを使用して会話をする。
      現状、最善の手法であるが、いくつかデメリットが存在する。
      通常Webサーバはリクエストを受けるとすぐにレスポンスを返すように最適化されているが、
      ストリーミングでは接続を保持し続ける必要がある為、注意が必要である。
      また、クライアント側においてははXHRストリーミングがパフォーマンス上の問題を引き起こすケースがある。
      ストリーミングのレスポンスがあまりに長く続くと、ブラウザのメモリ消費が過大となり、クラッシュしてしまう。
      解決方法としては、一定数のレスポンス後に一旦レスポンスを終了し、新たにリクエストを生成すれば良い。
      なお、IEではXHRストリーミングを正式サポートしていないので注意が必要である。

  • ■ 今後の動向
  • 前述までのCometを使用したとしても、大量コネクション保持やサポート問題等の為、完全なソリューションではない。
    そこで、HTML5からforkしたWebSocketに期待が集まっている。
    現在策定中のWebSocketはCometのデメリットを補いつつ、リアルタイムアプリケーションを容易に構築出来る可能性を秘めている。

    調査にあたり、続・ハイパフォーマンスWebサイト ―ウェブ高速化のベストプラクティスを参照させてもらいました。


    twitter × Google maps with WebSocket

    twitter API と Google Maps API のmashupサービス twigeo を作りました。
    これだけだとありきたりの為、最近話題に上がるWebSocketを使用し、
    “ほぼ”リアルタイムにServer => Clientへtweetをpushするようにしています。
    誰得なサービスか分からないのですが、、皆さん様々な場所でtweetされているのですね。

    WebSocketを実現する為にPHP WebSocketを使用させてもらいました。
    PHP WebSocketはチャットのような複数クライアント間でメッセージのやり取りを行いやすいように
    設計がされていますが、本サービスではクライアント間のやり取りはなく、
    一方的にサーバ側からpushするのみとなりますので、少し手を入れる必要がありました。
    また、twitter StreamingAPIを使用する際にOAuthが必要ですので、tmhOAuthを使用しています。
    なお、現状WebSocketをサポートしているブラウザは、Chrome, Safariとなります。
    Firefoxは次バージョンからサポートされるようです。

    現在対応出来ていない仕様やバグは以下です。

  • tagエラー
  • 初期画面で自身の位置情報が固定
  • InのTweet量に対して、Outは視認性を良くする為にSleepを入れている関係上、叙々に配信が遅れてしまう
  • いづれも軽微な対応で済みそうですので、暇をみて対応したいと思います。

    簡易Javaネットワークプログラミング

    Javaでsocket機能を使用した非常に簡易なネットワークプログラムを作りました。
    ありきたりですが、Clientから郵便番号をリクエストすると、Serverから対応する住所情報をレスポンスします。
    郵便データは、日本郵便 郵便データダウンロードから事前取得し、Server起動時に読み込むような非常に簡易なプログラムです。

    1. Server Code

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.SocketException;
    import java.util.HashMap;
    
    public class OriginalServer {
    
    	public HashMap<String, String> map;
    	private String file;
    
    	public OriginalServer(String file) {
    		this.file = file;
    	}
    
    	public void read() {
    
    		try {
    			File f = new File(file);
    			byte[] b = new byte[(int) f.length()];
    			FileInputStream fi = null;
    			fi = new FileInputStream(f);
    			fi.read(b);
    			String str = new String(b);
    			String[] strrec = str.split("n");
    
    			System.out.println("To be prepared.....");
    
    			String[] workArray;
    			map = new HashMap<String, String>();
    			for (int i = 0; i < strrec.length; i++) {
    				workArray = strrec[i].split(",");
    				map.put(workArray[2].replace(""", ""),
    						workArray[6].replace(""", "")
    								+ workArray[7].replace(""", "")
    								+ workArray[8].replace(""", ""));
    			}
    
    			System.out.println("Wait for request.....");
    
    		} catch (FileNotFoundException e) {
    			System.out.println("File Not Found Error");
    			System.exit(-1);
    		} catch (IOException e) {
    			System.out.println("File Read Error");
    			System.exit(-1);
    		}
    	}
    
    	public static void main(String args[]) {
    		try {
    			ServerSocket serverSocket = new ServerSocket(
    					Integer.parseInt(args[0]));
    
    			OriginalServer os = new OriginalServer(args[1]);
    
    			// read address-File
    			os.read();
    
    			while (true) {
    				Socket socket = serverSocket.accept();
    				BufferedReader reader = new BufferedReader(
    						new InputStreamReader(socket.getInputStream()));
    
    				String post;
    				while ((post = reader.readLine()) != null) {
    					// readline() + Empty() = Start HTTP-BODY
    					if (post.isEmpty()) {
    						post = reader.readLine().replace("-", "");
    						break;
    					}
    				}
    				String addr;
    				if (os.map.containsKey(post)) {
    					addr = "〒" + post.substring(0, 3) + "-"
    							+ post.substring(3, post.length()) + " -> "
    							+ os.map.get(post) + "n";
    				} else {
    					addr = "There is no postal code.n";
    				}
    
    				PrintStream writer = new PrintStream(socket.getOutputStream());
    
    				writer.print("HTTP/1.1 200 OKn");
    				writer.print("Connection: closen");
    				writer.print("Content-Length: " + addr.length() + "n");
    				writer.print("Content-Type:text/plainnn");
    				writer.print(addr);
    				writer.flush();
    				writer.close();
    				reader.close();
    				socket.close();
    			}
    		} catch (SocketException e) {
    			System.out.println("Socket Error");
    			System.exit(-1);
    		} catch (IOException e) {
    			System.out.println("IO Error");
    			System.exit(-1);
    		}
    	}
    }
    

    2. Client Code

    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.net.SocketException;
    import java.net.UnknownHostException;
    
    public class OriginalClient {
    
    	public static void main(String args[]) {
    		try {
    
    			Socket socket = new Socket(args[0], Integer.parseInt(args[1]));
    			BufferedReader reader = new BufferedReader(new InputStreamReader(
    					socket.getInputStream()));
    			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
    					socket.getOutputStream()));
    
    			writer.write("GET / HTTP/1.1rn");
    			writer.write("Host: " + args[0] + ":" + args[1] + "rn");
    			writer.write("Connection: closern");
    			writer.write("rn");
    			writer.write(args[2] + "rn");
    			writer.flush();
    
    			String line;
    			while ((line = reader.readLine()) != null) {
    				if (line.isEmpty()) {
    					System.out.println(reader.readLine());
    				}
    			}
    
    			writer.close();
    			reader.close();
    			socket.close();
    
    		} catch (UnknownHostException e) {
    			System.out.println("Host not found");
    			System.exit(-1);
    		} catch (SocketException e) {
    		} catch (IOException e) {
    			System.out.println("IO error");
    			System.exit(-1);
    		}
    	}
    }
    

    3. 実行結果: Server

    C:hogenetworkPog > java OriginalServer 12345 KEN_ALL.CSV
    To be Prepared....
    Wait for request....
    

    4. 実行結果: Client

    C:hogenetworkPog > java OriginalClient localhost 12345 1400011
    〒140-0011 -> 東京都品川区東大井
    

    以上です。

    Perl, Python, PHPのベンチマークを計測しました

    Perl, Python, PHPのベンチマークを計測しました。

    =====
    1. 環境 及び シナリオ

    1. 実行環境
    2. 実行環境は以下である。
      Platform: Linux(CentOS 5.6)  @sakura VPS
      Memory: 512 MB
      CPU: 仮想2Core

    3. プログラミング言語
    4. 実行・考察を行うプログラミング言語はLAMP構成として通常扱われるスクリプト言語の以下を使用する。
      ・Perl
      ・Python2.4
      ・PHP5.2

    5. 実施シナリオ
    6. No. 演算 概要
      1 i = i + 1 加算
      2 i = i – 1 減算
      3 i = i * i 乗算
      4 i = i / 3 除算
      5 i = i % 3 剰余
      6 s = “Hello,World!” 文字列代入
      7 i = len(string) 文字列数
      8 s = random() ランダム(指定なし)
      9 s = random(1, 100) ランダム(指定あり)
      10 IF (s = “Hello,World!) 文字列判断
      11 IF (i = 5) 数値判断
      12 IF (i = TRUE) Bool値判断
      13 IF文ネスト 条件分岐ネスト
      14 Switch文 ループ(Switch)
      15 While文 ループ(While)
      16 For文 ループ(For)
      17 Foreach文 ループ(Foreach)
      18 print “Hello, World!” 文字列出力
      19 関数呼び出し 関数
      20 File Open, Close オープン, クローズ
      21 File read 1行毎の読み込み
      22 File Write 1行毎の書き込み

      実行時のループ回数は、10000000回ループさせる。但し、実行に時間の掛かり過ぎるシナリオ等に関しては回数を減らす事とする。例外シナリオは以下である。
      No.17: 100000回 PHPにおいて例外が発生する為。
      No.18: 10000回 標準出力に文字列出力する為、処理
      対象のprint文の計測が行いづらい為。

    7. 実施前考察
    8. 本レポートで実行対象となるプログラミング言語はそれぞれ実績のある言語であり、Webサービス開発においてよく使用されている。私はPerl, PHPの経験はあるがPythonは今回が初めての使用となる。
      Perlは全体的に処理が速く、ファイル操作に長けているイメージがある為、全般的にPerlのパフォーマンスが良いと考えている。
      PHPは全体的に処理が遅いイメージがある。特にrandom()やファイル操作が遅いイメージがある。
      Pythonを使用するのは今回が初めてである為、あまりイメージが出来ないが、処理速度的にはPerlと同様の計測値となると予測する。

      よって、 ほぼ全シナリオでPerl < Python < PHP の順番で処理コストが高くなると考える。

    9. 実施方法
    10. 以下の要領で実施を行う。

      1. プログラミング言語毎に22個のプログラムを作成する。
      2. 実施シナリオ毎のアルゴリズムは同一とする。実行時間計測アルゴリズムに関しては後述する。
      3. 言語によっては命令が存在しないケースがある為、その場合は代替(同等)機能を使用する。No.16のPythonにおけるFor文等。
      4. Perlに関しては、ミリ秒単位を取得する組み込み関数がない為、Time::Hiresモジュールを使用する。
      5. シナリオ毎に10回実行し、実行結果として平均値を記録する。その際のコマンドは以下ように実行する。(以下はPythonでの例)
        # loop=1; while [ $loop -le 10 ]; do loop=`expr $loop + 1`; ./check1.py >> result/check1; done;
      6. eの実行結果から平均値を出力するスクリプトを実行し、平均値を得る。平均値出力スクリプトは以下である。
        ====
        #! /usr/bin/perl
        open(IN, $ARGV[0]);
        while (<IN>) {
        $total += $_;
        $count++;
        }
        close(IN);
        print $total / $count . “n”;
        ====
    11. 実行時間計測方法とサンプルコード
    12. 言語毎の実行時間計測は以下のように行う。

      以下では、各言語のシナリオ1のプログラムである。

      Perl
      ====
      #!/usr/bin/perl
      use Time::HiRes;

      my $starttime = Time::HiRes::time;
      for($i=0; $i<10000000; $i++) {
      }
      my $looptime = Time::HiRes::time – $starttime;
      my $starttime = Time::HiRes::time;

      my $j=0;
      for(my $i=0; $i<10000000; $i++) {
      $j = $j + 1;
      }
      print Time::HiRes::time – $starttime – $looptime . “n”;
      ====

      Python
      ====
      #!/usr/bin/python
      import time

      starttime = time.clock()
      for i in range(10000000):
      pass

      looptime = time.clock() – starttime
      starttime = time.clock()

      j = 0
      for i in range(10000000):
      j = j + 1

      print time.clock() – starttime – looptime
      ====

      PHP
      ====
      <?php
      $starttime = microtime(true);
      for($i = 0; $i<10000000; $i++) {
      }
      $looptime = microtime(true) – $starttime;
      $starttime = microtime(true);

      $j=0;
      for($i=0; $i<10000000; $i++) {
      $j = $j + 1;
      }
      echo microtime(true) – $starttime – $looptime . “n”;
      ?>
      ====

    2. 実行

    1. 実行結果
    2. 以下が実行結果である。シナリオ毎に最も良いタイムに橙色にしている。また、特徴的な値となった結果は赤色とし、次項以降で考察を行う。なお、単位は全て”秒”である。

      演算 Perl Python PHP
      i = i + 1 0.8 1.04 0.44
      i = i – 1 0.81 1.13 0.45
      i = i * i 0.82 1.3 0.46
      i = i / 3 0.74 1.56 0.73
      i = i % 3 0.84 1.47 0.5
      s = “Hello,World!” 0.87 0.65 0.93
      i = len(string) 1.12 1.64 1.8
      s = random() 0.22 2.05 1.73
      s = random(1, 100) 0.95 27.65 2.86
      IF (s = “Hello,World!) 0.89 0.51 0.78
      IF (i = 5) 0.94 0.49 0.44
      IF (i = TRUE) 0.97 1.08 0.37
      IF文ネスト 0.60 2.05 1.67
      Switch文 251.03 2.81 2.01
      While文 1.13 1.64 0.77
      For文 0.67 1.18 0.91
      Foreach文 0.02 0.01 0.06
      print “Hello, World!” 1.2 0.47 0.96
      関数呼び出し 1.74 1.52 1.56
      File Open, Close 69.01 61.46 80.04
      File read 2 1.46 3.54
      File Write 2.02 4.94 23.18

    3. 考察

    実行前の考察では、全てのシナリオでPerlが優位だと考えていたが、結果は異なっていた。
    この考察としてはPerlの時間計測用の関数において、ミリ秒単位で計測が出来なかった為、
    Time::Hiresモジュールを使用した事が影響している可能性がある。
    但し、Perlならではの優位性が示されているシナリオもある為、現状のまま考察を進める事とする。

    1. randint()
    2. Pythonのrandint()が非常に遅い結果となった。Perlと比較すると約27倍の差である。調査したところ、Perl, PHPのrand()は組み込み関数であるのに対し、Pythonのrandint()は標準モジュールでの提供であった。
      また、randint()よりもchoice()を使用した方が速いとの情報があった為、変更し実行計測したところ、
      27.65 → 12.36 と半分以下の実行結果となった。但し、Perl, PHPと比較するとまだ遅い結果であった。
      この事からC等で記述される組み込み関数はやはり速いということが分かった。

    3. switch文
    4. Perlのswitch文が異常な程、遅い結果となった。これはPerlには組み込みでswitch文がない為、標準モジュールのSwitchを使用した事が影響していると考えられる。最速であったPHPと比較するとその差は100倍である。
      3.1のrand()と同様に以下に組み込み機能が速いかを再認識させられた。

    5. File操作
    6. 実行前の考察よりPHPはファイル操作が不得意であると考えていたが、その通りの結果であった。特にfopen(), fclose(), fwrite()のパフォーマンスが悪かった。
      また、ファイル操作はPerlが非常に有利だと考えていたが、Pythonが健闘している。

    7. その他
    8. シナリオ毎の実行結果の色付けを確認すると、PHP列に橙色が多く付いている。PHPは全般的に処理が遅いと考えていたが、結果から見ると異なっていた。以前の経験ではPHPのrandom()は遅いと感じていたが、やはりPerlと比較すると倍以上の差が出ていた。(Pythonとの比較に関しては、3.1を参照の事)

    9. 最後に
    10. それぞれのプログラミング言語には高速化技術が存在する。例えば、Perl: mod_perl, Python: mod_wsgi, PHP: APC, eAcceralator等である。これらの技術を使用すれば、より良いパフォーマンスが出やすいが、その前段階として、言語毎の特徴を押さえた上でプログラミング言語の選択や処理ロジック, アルゴリズムを組んでいく必要がある。

    =====

    おわりです。

    WMIサービスからクエリ実行

    WindowsのWMIサービスからクエリ実行が出来ます。

    例えば、稼働中のデバイスドライバ一覧を出力する場合、コマンドプロンプトより下記を実行します。
    $ wmic sysdriver where “(state = “Running”)” get name

    上記の結果は、” C:WindowsSystem32Msinfo32.exe”を実行した際と同じ結果となります。
    State=Runningのドライバを確認するとwmicのクエリ実行結果と同一となることが確認出来ます。

    imageをアップロードすると、フルサイズしか選べないのは何故でしょう、、、

    postgreSQLアーキテクチャ メモ

    最近、初めてpostgreSQLをさわったので、アーキテクチャなど少し調べました。

    postgreSQLとその他のDBMSとの大きな違いは追記型である点です。
    updateを行う際に、オリジナル行に対して更新をするのではなく、
    新規行をinsertとオリジナル行に無効化フラグを付加します。
    よって、データベース内には複数versionのデータを保持することになります。
    この機能をMVCC(Multi Version Concurrency Control)と呼ばれています。
    更新トランザクションが発生する毎にレコードのversionが増える為、大量トランザクション発生するシステムの場合、
    データ量が膨大になります。
    そこで、vacuum機能で、無効化フラグのついたレコードをお掃除します。

    vacuumには以下の2種類があります。

    1. concurrent vacuum
    2. 無効化フラグが付加されたレコードを再利用可能とする。排他ロックは行われない。

    3. vacuum full
    4. 従来からあるvacuumで排他ロックが発生する。
       → 定期的にサービス停止が必要

    以下はメモです。
    ■ postgreSQL起動
    起動は$pg_ctl start で行います。 (停止、再起動はそれぞれstop, restart)

    [postgres@hoge data]$ pg_ctl -w start
    waiting for postmaster to start....LOG:  database system was shut down at 2011-06-22 13:36:38 JST
    LOG:  checkpoint record is at 0/ACB3E0
    LOG:  redo record is at 0/ACB3E0; undo record is at 0/0; shutdown TRUE
    LOG:  next transaction ID: 576; next OID: 17231
    LOG:  database system is ready
     done
    postmaster started
    

    起動後、プロセスを確認します。

    [postgres@hoge data]$ ps aux | grep postgres
    postgres 26561  0.0  0.0  92552   360 ?        S    12:26   0:00 /usr/local/pgsql/bin/postmaster
    postgres 26563  0.0  0.0  92552   340 ?        S    12:26   0:00 postgres: writer process
    postgres 26564  0.0  0.0  82820   180 ?        S    12:26   0:00 postgres: stats buffer process
    postgres 26565  0.0  0.0  81948   188 ?        S    12:26   0:00 postgres: stats collector process
    

    ■ 設定ファイル
    MySQLで言うmy.confに相当する設定ファイルとして、postgres.confがあります。
    私の場合は /usr/local/data/postgres.conf にあります。(以下、$PGDATA)

    [postgres@hoge data]$ ls -al
    合計 64
    drwx------ 8 postgres postgres  4096  6月 22 12:26 .
    drwxr-xr-x 9 postgres root      4096  6月 21 13:19 ..
    -rw------- 1 postgres postgres     4  6月 21 13:19 PG_VERSION
    drwx------ 5 postgres postgres  4096  6月 21 13:21 base
    drwx------ 2 postgres postgres  4096  6月 22 12:26 global
    drwx------ 2 postgres postgres  4096  6月 21 13:19 pg_clog
    -rw------- 1 postgres postgres  3406  6月 21 13:19 pg_hba.conf
    -rw------- 1 postgres postgres  1460  6月 21 13:19 pg_ident.conf
    drwx------ 2 postgres postgres  4096  6月 21 13:19 pg_subtrans
    drwx------ 2 postgres postgres  4096  6月 21 13:19 pg_tblspc
    drwx------ 3 postgres postgres  4096  6月 21 13:19 pg_xlog
    -rw------- 1 postgres postgres 11030  6月 22 12:23 postgresql.conf
    -rw------- 1 postgres postgres    30  6月 22 12:26 postmaster.opts
    -rw------- 1 postgres postgres    48  6月 22 12:26 postmaster.pid
    

    ■ プロセスファイル
    postgreSQLを起動すると、$PGDATA/postmaster.pidが生成されます。
    pidファイルの中身は以下のようになっています。

    26561
    /usr/local/pgsql/data
      5432001   2326529
    

    1行目:”26561″はプロセスID
    2行目:postgreSQLのDATA格納ディレクトリ
    3行目:port番号 (後ろのほうは分からなかった)

    ■ リスナー
    postgreSQLを起動すると、フロントエンドとバックエンド間の通信を行う為のリスナーが生成されます。
    リスナーは /tmp/.s.PGSQL.5432 になります。

    [postgres@hoge data]$ ls -al /tmp/.s.PGSQL.5432*
    srwxrwxrwx 1 postgres postgres  0  6月 22 13:36 /tmp/.s.PGSQL.5432
    -rw------- 1 postgres postgres 28  6月 22 13:36 /tmp/.s.PGSQL.5432.lock
    

    “5432”はport番号で、postgreSQLのデフォルトportは5432となります。
    変更したい場合はpostgres.confで変更すればOKです。(標準ではコメントアウトされている)

    ■ template database
    コンソールから$psql -l を実行すると、作成されているデータベースが確認出来ます。

    [postgres@hoge data]$ psql -l
            List of databases
        Name    |  Owner   | Encoding
    ------------+----------+----------
     shmachidDB | postgres | UNICODE
     template0  | postgres | UNICODE
     template1  | postgres | UNICODE
    (3 rows)
    

    template0, template1というデータベースは、標準機能として提供しているデータベースで、
    ユーザが新規データベースを作成する際に内部でコピーするテンプレートデータベースとなります。
    データベースの実体は、$PGDATA/base以下にあり、ファイル名は数値となります。

    [postgres@hoge base]$ ls -al
    合計 20
    drwx------ 5 postgres postgres 4096  6月 21 13:21 .
    drwx------ 8 postgres postgres 4096  6月 22 13:36 ..
    drwx------ 2 postgres postgres 4096  6月 21 13:19 1
    drwx------ 2 postgres postgres 4096  6月 21 13:19 17229
    drwx------ 2 postgres postgres 4096  6月 21 13:21 17230
    

    ■ 複数起動
    既にpostgreSQLが起動している状況で、もう1つpostgreSQLを立ち上げてみます。

    [postgres@hoge base]$ pg_ctl start
    pg_ctl: another postmaster may be running; trying to start postmaster anyway
    FATAL:  lock file "/usr/local/pgsql/data/postmaster.pid" already exists
    HINT:  Is another postmaster (PID 26808) running in data directory "/usr/local/pgsql/data"?
    pg_ctl: could not start postmaster
    Examine the log output.
    

    既に$PGDATA/postmaster.pidが存在する為、怒られます。
    そこで、postmaster.pidを別名に変更して、再度起動します。

    [postgres@hoge data]$ pg_ctl start
    postmaster starting
    [postgres@hoge data]$ LOG:  could not bind IPv4 socket: Address already in use
    HINT:  Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
    WARNING:  could not create listen socket for "localhost"
    FATAL:  lock file "/tmp/.s.PGSQL.5432.lock" already exists
    HINT:  Is another postmaster (PID 26808) using socket file "/tmp/.s.PGSQL.5432"?
    

    今度はリスナーがあると怒られます。
    では、次にpostgresql.confでport番号を別指定(今回は11111にします)し、再度起動します。(portは1…65535)

    [postgres@hoge data]$ pg_ctl start
    postmaster starting
    [postgres@hoge data]$ LOG:  database system was interrupted at 2011-06-22 13:36:47 JST
    LOG:  checkpoint record is at 0/ACB3E0
    LOG:  redo record is at 0/ACB3E0; undo record is at 0/0; shutdown TRUE
    LOG:  next transaction ID: 576; next OID: 17231
    LOG:  database system was not properly shut down; automatic recovery in progress
    LOG:  record with zero length at 0/ACB428
    LOG:  redo is not required
    LOG:  database system is ready
    

    立ち上がりましたので、リスナーとプロセスを確認します。

    [postgres@hoge data]$ ls -al /tmp/.s.PGSQL.*
    srwxrwxrwx 1 postgres postgres  0  6月 22 13:51 /tmp/.s.PGSQL.11111
    -rw------- 1 postgres postgres 28  6月 22 13:51 /tmp/.s.PGSQL.11111.lock
    srwxrwxrwx 1 postgres postgres  0  6月 22 13:47 /tmp/.s.PGSQL.5432
    -rw------- 1 postgres postgres 28  6月 22 13:47 /tmp/.s.PGSQL.5432.lock
    [postgres@hoge data]$ ps aux | grep postgres
    postgres 26808  0.0  0.2  92552  1320 pts/0    S    13:36   0:00 /usr/local/pgsql/bin/postmaster
    postgres 26810  0.0  0.1  92552   632 pts/0    S    13:36   0:00 postgres: writer process
    postgres 26811  0.0  0.0  82820   396 pts/0    S    13:36   0:00 postgres: stats buffer process
    postgres 26812  0.0  0.0  81948   452 pts/0    S    13:36   0:00 postgres: stats collector process
    postgres 26863  0.0  0.6  92552  3132 pts/0    S    13:51   0:00 /usr/local/pgsql/bin/postmaster
    postgres 26865  0.0  0.1  92552   992 pts/0    S    13:51   0:00 postgres: writer process
    postgres 26866  0.0  0.1  82820   704 pts/0    S    13:51   0:00 postgres: stats buffer process
    postgres 26867  0.0  0.1  81948   832 pts/0    S    13:51   0:00 postgres: stats collector process
    

    2つのport分(5432, 11111)のリスナーが生成されています。
    プロセスも2つ分起動しています。

    よって、今回は最初に起動していたpostmaster.pidを別名にしていますので、2つ立ち上がった事になりませんが、
    $PGDATAを複数用意して、別portで起動すれば複数起動することが出来ると思われます。

    あと今回はゴミプロセスが残っていますので、killしてください。

    技術サイクルと情報アーキテクトの位置づけ

    大学院で技術サイクルと情報アーキテクトの位置づけを事例と共に考察しました。
    合っているかは分かりません。適当に流してください。。

    1. 技術サイクルとは
    技術または企業が進化・発展する為には技術サイクルが必要不可欠である。
    具体的には以下の流れを繰り返すことで、成長していく。

    1. 技術の不連続性
    2. 1→2の変化には、エバンジェリストが必要

    3. 不安定期
    4. 2→3の変化には、市場創造者が必要

    5. ドミナントデザイン
    6. 3→4の変化には、マネジメントが必要

    7. 安定期
    8. 4→1の変化には、発明・発見者必要

    本考察では、1: ”技術の不連続性”→2: ”不安定期”→3: ”ドミナントデザイン”までの2期で活躍をする、
    エバンジェリストと市場創造者の動きに着目し、今後の技術者の在り方について考察する。

    2. MySQL
    考察時のモデル事例として、MySQLを取り上げる。
    私が初めてMySQLを知ったのは2005年春でバージョンは4.0の頃であった。
    それまでMS SQLServer, DB2といった商用データベース(以下、DB)を使用して、
    アプリケーションを組んでいたが、この頃初めてオープンソースDBに触れた。
    Webサービス構築を通して、すぐにMySQLの虜になってしまった。
    その当時を振り返りながら、MySQLの進化・発展、取り巻く環境の変化を確認していく。

    2.1. MySQLの収益源
    MySQLはオープンソースDBである為、無償で使用可能である。(※厳密にはGPL Version2)
    使用者側でMySQLを使いこなす事が出来れば、追加のコンサルティング費用等は不要である。
    MySQL社では、各種サポート(問題発生時のサポートなど)やサービス(チューニングなど)を提供しており、
    年間売上は$60million – 70million程である。(認知度に対して決して大きくない)

    2.2. ある日本人MySQLコンサルタント
    現在、日本語によるMySQLの本が多数出版されており、中には内部構造を詳細に解説した本や、
    パフォーマンスチューニングのTIPSをふんだんに散りばめられた本もある。
    2005年当時、英語の本は多数出ていたが、日本語によるMySQL本は3冊のみであった。
    但し、内容的に古かったり薄かったりとコンテンツとしては充分とは言えない状況であった。
    自分が担当していたWebサービスのパフォーマンス改善をしていたが、MySQL4.0→4.1→5.0→・・・と、
    バージョンアップする毎にパフォーマンス改善や新機能が盛り込まれていった為、これらの情報収集には苦労していた。
    そんな時「現場で使える MySQL」(著:松信 嘉範さん)という本が出版された。
    今でこそ、この本よりも多くのTIPSが書かれた本は多くあるが、当時としてはこれ以上の本はなく、
    何度も繰り返し読み、また実機で実践をした。
    この頃は既にMySQLはWeb系サービスでかなり使用されており、
    メジャーな存在であったが、まだまだ未完成なデータベースで
    文字コードの問題やストレージエンジンのパフォーマンス問題などの問題があった。
    この本はこういった問題を丁寧に解説+対応方針を示し、他エンジニアも含めて参考にしていた。

    2.3. WebサービスとMySQL
    今日の有名なWeb系サービスでMySQLを使用していないサービスはほぼ無いと思われる。
    前述の2005,6年頃では、YahooやGoogleは大規模に使用をしていたし、
    新鋭サービスとしてはYoutubeや日本ではmixiが大量のアクセス・データを
    どのようなアーキテクチャで捌くかに注目が集まっていた。
    これらのサービスの成功により、MySQLの認知度は一気に上がり、様々なサービスの中核となった。
    LAMPがWebサービスのデファクトスタンダードとして広まったのもこの頃であった。
    一方、エンタープライズ業界ではまだ浸透しきれておらず、現場で使用されるまでに至っていなかった。
    現在はWeb・エンタープライズ共に、多くのMySQLが稼働しており、商用データベースを脅かす存在となっている。

    2.4. SUNによる買収
    2008年2月、SUNによりMySQLは買収された。
    収益はサービス・サポートからで$60million – 70million程であったMySQLを何故買収したのか。
    理由はMySQLを使用する多数のユーザの存在である。
    これらユーザの大多数はSUN以外のサーバを使用しており、
    買収をする事で自社のサーバへ誘導する事が狙いであったと考えられる。

    2.5. SAPの画策
    SAPとMySQLは2003年頃から提携して、次世代DBを開発してきた。
    MySQLをベースとして、SAP用に機能を削ぎ落したDBである。
    このDBではSAP DBと言われ、後にMAX DB, live cacheと言われるデータベースとなった。
    SAPがMySQLと組んだ理由としては、Oracleを打ち負かす為だと考えられる。
    OracleはSAPと競合するERPを保持している為、SAPとしてはOracle DBを使用する事は推奨しない。
    また、TomorrowNowに関連した賠償問題もある。
    現在、SAP ERPのバックエンドで稼働可能なDBとしてSQL Server, DB2, Oracle, MAXDBが選択肢としてあるのだが、
    OracleはERPで競合となるソフトウェアを保持しており競合にあたる為、メリットが無い。
    そこで、メリット/デメリットあるもののライセンス費用がかかる3社ではなく、
    ライセンス費用をかけずにSAPに特化したDBを共同開発可能なMySQLを選択したと考えられる。
    しかし、SAPは2007年9月頃にMySQL社との提携を解除し、次なる道を模索し始めた。
    結果、2010年5月にSybase買収へと進んだと思われる。この買収の狙いはOracleへの対抗である事は明白である。
    また、Sybaseはインメモリ技術を保持していた為、現状のアーキテクチャの流れに足していたとも言える。
    その後、このインメモリを搭載したリアルタイム分析ツール HANAをリリースした。

    2.6. Oracleによる買収
    2009年4月、OracleはSUNを買収した。この買収は最近の流行りである垂直統合システム化への道である。
    自社でハードウェア・ソフトウェア・データベース等を一貫して提供し、
    自社製品によるエコシステムを作り上げて親和性を武器に顧客を囲い込む作戦である。
    前段のSAPによるSybase買収もこの垂直統合システム化を狙ったエコシステム構築の1つの動きである。
    またOracleにはSUN買収にもう1つ狙いがあった。それはMySQLの存在である。
    Webアプリケーションを席巻するMySQLを手にすることで、エンタープライズ側のOracle DB、
    Web側のMySQLを使い分けることが出来る。
    Oracleには更にメリットがあった。ストレージエンジンのInnoDBである。
    MySQLはDBの核となるストレージエンジンを入れ替える事が出来る。
    デフォルトのストレージエンジンはMyISAMであったが、このエンジンはトランザクションをサポートしていない為、
    トランザクションを伴う場合はInnoDBを使用する。
    以前からMySQLの存在が邪魔であったOracleは、2005年10月にInnoDBを保有するInnobase社を買収した。
    これにより、MySQLは自身のソフトウェアの中核ストレージエンジンを奪われる事となる。
    但し、Oracle側で制限をしなかった為、今までと変わらずにInnoDBは使用可能であった。
    但し、SUN買収後のバージョン5.5からはデフォルトストレージエンジンがMyISAM→InnoDBへと変更された。
    Oracle社内では元々あったInnoDBチームと買収したMySQLチームを統合し、開発にスピード間が出てきたとの事である。

    2.7. RDBMSへの刺客
    2009年、RDBMSへの刺客としてkey-value型のデータベースが注目され始めた。
    これはNoSQL(Not Only SQL)と言われる。
    Key-value型の考え方は以前からあり、memcached等によりWebのアプリケーションサーバではよく使用されてきた。
    情報が爆発的に増えていくなかで、Big Dataを効率的に処理する為にkey-value型データベース等のNoSQLに注目が集まっている。当初は、RDBMSが無くなる・・と言った意見もあったが、現状は適材適所でRDBMSとNoSQLを使い分けていくというのが流れである。
    NoSQLの流れに対抗する為に、MySQLではバージョン5.6からmemcachedプロトコルを利用したNoSQLアクセスを提供する予定である。
    NoSQLなDBが今後どのように発展していくかは分からないが、RDBMSと適材適所で使い分ける必要がある。

    2.8. MySQLコアメンバの動き
    冒頭で、日本人のMySQLコンサルタントの話をしたが、今度はMySQLのコアメンバの動きを確認する。
    まず、MySQLの創設者であるMichael “Monty” Wideniusは、MySQLからforkしたMariaDBを開発し始めた。(Mariaは娘の名前)
    このDBは、ストレージエンジン”Maria”をデフォルトエンジンとするDBで、MySQL6.0(リリースされず)で出荷される予定だった機能が盛り込まれている。データベース本体として、MySQLを打ち負かす可能性は低いが、ストレージエンジンの1つの選択肢として使用されていく可能性はある。
     次にMySQLの開発責任者であったBrian Akerは、MySQLからforkしたDrizzleを開発し始めた。
    このDBはクラウド上での使用を想定しており、Webサービスのバックエンドで使用する時に不要と思われる機能を極限まで削ったDBである。
    今後、Cassandra等のNoSQLのライバルとなってくる可能性がある。

    3. 考察
    以上が、ここ数年でMySQLに起こった事象である。
    この個人や企業の様々なアクションを技術サイクルに当て込み考察する。

    3.1. 技術の不連続性 (エバンジェリスト)
    まず、日本人MySQLコンサルタントの松信嘉範さんである。
    彼はMySQLがまだ日本に浸透したとは言えなかった時代にDBマガジン(少し前に廃刊)や
    書籍でMySQLの良さを布教していった。
    彼がいなければ、日本におけるMySQLの成功はなかったかもしれない。

    3.2. 不安定期 (市場創造者)
    市場創造者はいくつかありそうである。以下にまとめた。

    1. SUN
    2.  MySQLを使用しているたくさんのユーザを狙った買収を行い、
       MySQLユーザを自社のサーバへ誘導しようとした。

    3. SAP
    4.  Oracleに勝つ為に、自前のDBを開発しようとしていた。
       また、開発を取りやめ、Sybaseを買収し、最近の流行りの垂直統合化で自社のエコシステムを構築しようとしている。クラウドに関しては、流れに乗り遅れているように感じる。

    5. Oracle
    6.  SUNの買収、MySQLへ対抗する為にInnoBaseの買収と、無駄がないように感じる。
       周囲の競合ベンダを威嚇しながら、買収を繰り返し、垂直統合システムを完成させていっている。だが、これはベンダーロックインとなる為、懸念される可能性もある。(中立的な立場であるPublicSectorがMicrosoftを嫌がり、OpenOfficeへ入れ替えていっているように)

    7. MySQL
    8.  MySQLはWebで圧倒的な強さを見せた。
       これは、Web業界を対象として製品をプッシュしていったからではないだろうか。
       また、Webがオープンソースを利用していくという流れにも乗っていた。
       だが、その他のオープンソースDB御三家(MySQL, PostgreSQL, Firebird)と何が違ったのだろうか。
       特に機能としては、優っているとの見解もあるPostgreSQLは何が足りなかったのか興味がある。

    3.3. ドミナントデザイン (マネジメント)
    ここでの登場人物はいなかったので、割愛する。

    3.4. 安定期 (発明・発見者)
    2人のMySQLの開発者は、新たな道を歩み初めている。
    MontyはMariaDBを始め、BrianAkerはDrizzleを進めている。
    先日、どちらのDBも新バージョンをリリースした為、次のTermである技術の不連続性へ進んでいく。
    彼らはMySQL時代にエバンジェリストを経験し、既に流れを作った経験を持っている。
    これはまさに技術のサイクルでもある。

    3.5. 1技術者として自分が出来る事、取るべき行動
    ここ数年は今後を左右する技術の変遷期にあたると考えている。
    まず、技術者が避けなければならない事として、自分のスキルが陳腐化することである。
    バズワードのように、たくさんの技術が出てきて、1部はメジャーとなり、一部はすぐ消える。
    また、既存の技術であっても消えていく事も充分ありえる。
    よって、業界動向、技術サイクルを見極め、今どこのTermにいるのか、
    エバンジェリストや市場創造者はどこに進もう・進ませようとしているかを冷静に判断しなければならない。
    その上で自分がアーリーアダプターとなり、他者をリードしていけるように心がける。(そうなりたい)
    以前はエンタープライズ業界がIT業界を牽引していた。
    例えば、IBM、Microsoftといったベンダーが新しい技術を産み、市場を創造していった。
    しかし、今はWeb業界がIT業界を牽引している。
    例えば、GoogleがAndroidを開発し、携帯メーカーはそのOSを利用して製品を作る
    (もはや、auのCMはAndroidの機能紹介となっており、au独自の機能はないように感じる)。
    Twitterがブームとなったことにより、Salesforce.のchatterやSAPのマイクロブログツールへと連鎖した。
    このことから、まずはWebに認められるか否かが、ポイントとなると考えられる。
    そのWebを支えているのは技術者である。よって、Webに認められたければ、技術者へ訴え続ける必要がある。
    エグゼクティブを対象としたカンファレンスはかりを開くのではなく、
    技術者を対象とした技術カンファレンスを定期的に開き、技術者を味方につけることである。
    技術者は次の技術サイクルを創出していく可能性を常に秘めている。

    以上。

    プレゼンテーションは難しい

    会社で「Webアーキテクチャ」に関してプレゼンテーションをしてきたのですが、やはりプレゼンは難しいです。
    私はあまり人前で話すのが得意ではない(上手くないの意味)ので、プレゼン前には練習が欠かせないのですが、
    いざ始まると練習の意味がなく、完全にトビます。
    また、今回は興味がない題材だったのか、反応もいまひとつだったなど、反省点がありました。
    今月はあと1回、長い時間のプレゼン機会がありますので、少しでも修正していきたいと思います。
    パブリックスピーカーの告白 ―効果的な講演、プレゼンテーション、講義への心構えと話し方を読んだのですが、
    結論としてはプレゼン前に練習あるのみとのことでした。

    やはり、事前の練習あるのみですかねー。次回までにもう一度読み直します。


    ウェブオペレーション(非リレーショナルデータベース)

    その時の気分で本を変えてしまってますが、ウェブオペレーション ―サイト運用管理の実践テクニック
    15章 非リレーショナルデータベースを読んだメモを残します。
    イマイチNoSQLが自分の中に落ち切っていないので、この章から確認しました。

    SQLデータベースのスケーラビリティ
    SQLデータベースのスケーラビリティは、4つにまとめめられる。

    1. キャッシュの最適化
    2.  読み取り負荷の軽減には非常に役立つ。
       但し、アプリケーションは複雑になり、軽減可能な負荷に限界がある。

    3. クエリの最適化
    4.  クエリの最適化も同様に限界がある

    5. 新しいハードウェアの購入
    6.  非常に効果的。問題は2つ。値段が高い。最も良いハードウェアを購入するとその後の手立てがなくなる。

    7. シャーディング
    8.  最後はシャーディング。シャーディングに適しているのは、ユーザID等から取得先のデータベースを
       振り分けるような場合(西海岸と東海岸とか)。だけど、ちょっと面倒。

    どうすれば簡単にスケール出来るのか? どうすればデータを近くに保存出来るのか? どうすればアプリケーションを再構築しなくて済むか? といった悩みから生まれたのがNoSQLである。

    NoSQLデータベースの概要
    NoSQLは以下の5つに分類出来る

    1. キーバリュー型
    2. キーバリュー型は昔からある。memcachedで再注目された。
      非常にシンプルでキーを渡せばバリューが返る。

    3. データ構造型
    4. キーバリュー型とほとんど同じ。バリューをリスト・セット・ハッシュ等のデータ構造で格納する。
      リストだとpush, popが出来たりとアプリケーションのデータ構造と同様に操作出来る。
      Redis

    5. グラフ型
    6. データ構造型のデータ構造がグラフになっただけ。データはキーバリューではなく、
      ノードとエッジで格納される。

    7. ドキュメント指向型
    8. キーバリュー型に似ているが、バリューがドキュメントとなる。
      ドキュメントはJSONオブジェクトで格納されている。
      このタイプは、データ構造が事前に分からない場合に有用である。
      CouchDB
      MongoDB

    9. 高分散型
    10. 複数の形式があるが、共通しているのは複数ノードへのデプロイに最適化されている事。
      分散システムでキャパシティを増やすには、新しいノードをクラスタに追加すれば良い。
      1つのノードに障害があっても データが失われることなく、
      キャパシティが損なわれるだけである。一貫性を犠牲にして、可用性と分割耐性を保証するものが多い。
      なぜ、高分散型を使用するか?他に選択肢がないから。
      SQLベースのDBでは、大量のデータを扱えないが、高分散型では可能。
      但し、面倒。
      Cassandra
      HBase
      Riak

    例で挙げた各データベースについては、後日調べる事とする。


    QRコード with ZXing

    ZXing(”Zebra Crossing”)を使用したQRコード生成ツールを作成しました。
    ZXingはGoogleがオープンソースで公開しているQRコード生成ライブラリです。
    Androidでも使用されていますが、今回はローカル起動までとなります。
    以下では、Antを使用してビルドしていますので、Antのセットアップをしておいてください。

    1. ZXing セットアップ
    まず、Google CodeからZXingをダウンロードします。
    任意のディレクトリへ展開します。私は “C:Eclipse_programzxing-1.6” としました。
    次に展開済みのフォルダ内にある READ MEファイルを開き、ビルド用のコマンドを確認します。

    Please refer to the project page for more information:
    http://code.google.com/p/zxing/
    in particular:
    http://code.google.com/p/zxing/wiki/GettingStarted
    
    To get started, you can try building and running the command-line client;
    you will need to have Apache's Ant tool installed to run this:
    
    ant -f core/build.xml
    ant -f javase/build.xml
    java -cp javase/javase.jar:core/core.jar com.google.zxing.client.j2se.CommandLineRunner [URL]
    

    コマンドプロンプトを開き、展開をしたディレクトリへ移動し、12,13行目のAntコマンドを実行します。

    C:Eclipse_programzxing-1.6>ant -f core/build.xml
    Buildfile: C:Eclipse_programzxing-1.6corebuild.xml
    
    clean:
       [delete] Deleting directory C:Eclipse_programzxing-1.6corebuild
       [delete] Deleting: C:Eclipse_programzxing-1.6corecore.jar
    
    build:
    
    init:
    
    compile:
        [mkdir] Created dir: C:Eclipse_programzxing-1.6corebuild
        [javac] C:Eclipse_programzxing-1.6corebuild.xml:36: warning: 'includeant
    runtime' was not set, defaulting to build.sysclasspath=last; set to false for re
    peatable builds
        [javac] Compiling 171 source files to C:Eclipse_programzxing-1.6corebuild
          [jar] Building jar: C:Eclipse_programzxing-1.6corecore.jar
    
    BUILD SUCCESSFUL
    Total time: 10 seconds
    
    C:Eclipse_programzxing-1.6>ant -f javase/build.xml
    Buildfile: C:Eclipse_programzxing-1.6javasebuild.xml
    
    init:
    
    build:
        [javac] C:Eclipse_programzxing-1.6javasebuild.xml:40: warning: 'includea
    ntruntime' was not set, defaulting to build.sysclasspath=last; set to false for
    repeatable builds
    
    BUILD SUCCESSFUL
    Total time: 0 seconds
    

    最後にEclipseで使用する為に “C:Eclipse_programzxing-1.6corecore.jar” をライブラリ追加すればOKです。

    2. 最終的なソースコード
    何らかの文字列を入力すると、都度QRコードを生成するようになっています。
    また、保存ボタン押下で生成済みのQRコードを保存します。
    ちょっと怪しい部分もありますが、、ひとまず動くと思います。

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Container;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.Hashtable;
    
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFileChooser;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    import com.google.zxing.EncodeHintType;
    import com.google.zxing.WriterException;
    import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
    import com.google.zxing.qrcode.encoder.ByteMatrix;
    import com.google.zxing.qrcode.encoder.Encoder;
    import com.google.zxing.qrcode.encoder.QRCode;
    
    public class Url2QR extends JFrame implements ActionListener {
    
    	private static Url2QR frame;
    	private final JTextField textField;
    	private final JButton dispButton;
    	private final JButton saveButton;
    	private final JLabel label;
    	private final JPanel panelTop;
    	private final JPanel panelBottom;
    	private final ImageIcon icon;
    	private final BufferedImage bufImg;
    	private static int SIZE = 4;
    
    	public Url2QR() throws WriterException {
    		super("QRコード生成");
    
    		panelTop = new JPanel();
    		panelBottom = new JPanel();
    
    		textField = new JTextField(20);
    		dispButton = new JButton("表示");
    		saveButton = new JButton("保存");
    
    		bufImg = barcodeWrite();
    		icon = new ImageIcon(bufImg);
    		label = new JLabel(icon);
    
    		Container container = getContentPane();
    		container.setLayout(new BorderLayout());
    
    		container.add(textField, BorderLayout.CENTER);
    		container.add(panelTop, BorderLayout.EAST);
    		container.add(panelBottom, BorderLayout.SOUTH);
    		panelTop.setLayout(new BorderLayout());
    		panelTop.add(dispButton, BorderLayout.WEST);
    		panelTop.add(saveButton, BorderLayout.EAST);
    		panelBottom.setLayout(new BorderLayout());
    		panelBottom.add(label, BorderLayout.WEST);
    
    		dispButton.setActionCommand("display");
    		dispButton.addActionListener(this);
    		saveButton.setActionCommand("save");
    		saveButton.addActionListener(this);
    		saveButton.setEnabled(false);
    
    		KeyListener keyListener = new KeyListener() {
    			public void keyPressed(KeyEvent keyEvent) {
    			}
    
    			public void keyReleased(KeyEvent keyEvent) {
    				repaintIcon();
    			}
    
    			public void keyTyped(KeyEvent keyEvent) {
    			}
    		};
    		textField.addKeyListener(keyListener);
    	}
    
    	public void actionPerformed(ActionEvent e) {
    
    		if (e.getActionCommand().equals("display")) {
    			repaintIcon();
    		} else if (e.getActionCommand().equals("save")) {
    			saveQR();
    		}
    
    	}
    
    	public void repaintIcon() {
    
    		try {
    			if (textField.getText().isEmpty()) {
    				saveButton.setEnabled(false);
    			} else {
    				saveButton.setEnabled(true);
    			}
    			label.setIcon(new ImageIcon(barcodeWrite()));
    		} catch (WriterException e) {
    			e.printStackTrace();
    		}
    		label.setToolTipText(textField.getText());
    		frame.pack();
    
    	}
    
    	public void saveQR() {
    
    		JFileChooser filechooser = new JFileChooser();
    
    		int selected = filechooser.showSaveDialog(this);
    		if (selected == JFileChooser.APPROVE_OPTION) {
    			File file = filechooser.getSelectedFile();
    			try {
    				ImageIO.write(barcodeWrite(), "png", file);
    			} catch (IOException e) {
    				System.out.println(e);
    			} catch (WriterException e) {
    				e.printStackTrace();
    			}
    		} else if (selected == JFileChooser.CANCEL_OPTION) {
    		} else if (selected == JFileChooser.ERROR_OPTION) {
    			label.setText("Error");
    		}
    	}
    
    	public BufferedImage barcodeWrite() throws WriterException {
    
    		Hashtable hints = new Hashtable();
    		hints.put(EncodeHintType.CHARACTER_SET, "SHIFT_JIS");
    		QRCode qrCode = new QRCode();
    		Encoder.encode(textField.getText(), ErrorCorrectionLevel.L, hints,
    				qrCode);
    		ByteMatrix byteMatrix = qrCode.getMatrix();
    
    		BufferedImage image = new BufferedImage(byteMatrix.getWidth() * SIZE,
    				byteMatrix.getHeight() * SIZE, BufferedImage.TYPE_4BYTE_ABGR);
    
    		if (!textField.getText().isEmpty()) {
    			Graphics2D g2D = image.createGraphics();
    			for (int y = 0; y < byteMatrix.getHeight(); y++) {
    				for (int x = 0; x < byteMatrix.getWidth(); x++) {
    					if (byteMatrix.get(x, y) == 1) {
    						g2D.setColor(Color.black);
    					} else {
    						g2D.setColor(Color.white);
    					}
    					g2D.fillRect(x * SIZE, y * SIZE, SIZE, SIZE);
    				}
    			}
    		}
    
    		return image;
    
    	}
    
    	public static void startGUI() throws WriterException {
    
    		frame = new Url2QR();
    		frame.pack();
    		frame.setLocationRelativeTo(null);
    		frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
    		frame.setVisible(true);
    
    	}
    
    	public static void main(String[] args) {
    
    		SwingUtilities.invokeLater(new Runnable() {
    			public void run() {
    				try {
    					startGUI();
    				} catch (WriterException e) {
    					e.printStackTrace();
    				}
    			}
    		});
    	}
    
    }
    

    ソフトウェア技術者としての責務に関する考察

    大学院のソフトウェア工学で、「ソフトウェア開発の専門家としてのソフトウェア技術者は、雇用主によって明示的に要求されていない場合であっても、保守と変更が容易なプログラムを作成する責務がある。」に対する考察を行ったので、今後の備忘のために残します。

    考察があっているかは分かりませんが、心に留めながら仕事していきたいと思います。

     

    1. 「ソフトウェア開発の専門家としてのソフトウェア技術者は、雇用主によって明示的に要求されていない場合であっても、保守と変更が容易なプログラムを作成する責務がある。」に対する考察をせよ。

    結論から述べると、ソフトウェア技術者は成果物リリース後の運用フェーズを常に意識しながらソフトウェア開発を行う必要がある。また、常にそのソフトウェアを使用するユーザの立場に立ち、ソフトウェア開発を行う事も重要である。理由として、一度リリースしたシステムやソフトウェアは7~10年間程使用される為、この長いソフトウェアライフサイクルの中で、どのような事が起きる・行われるかを意識するかしないかにより、ソフトウェア本体や各種ドキュメント等の成果物の優劣が大きく変わってくるからである。この結論に至るまでの過程を次項以降で詳細に分析を行う。

    2. 「ソフトウェア技術者の職業倫理」による分析

    ソフトウェア技術者の職業倫理として常に心掛ける事として、「ユーザ志向であること」を挙げる。ユーザとは、ソフトウェアの発注元(顧客)やそのソフトウェアを実際に使用する使用者を指す。私達ソフトウェア技術者は誰の為に何の為にこのソフトウェアを開発しているかを考えると、最終的にユーザ志向に行き着くと考えている。また、再発注を受ける為には、顧客満足度を上げる必要もある。ユーザ志向を持つと、ユーザインタフェースが格段に良くなる。開発者としては工数の掛かる機能であっても、その機能を10年間使用するユーザにとっては、その一瞬の不都合な機能が積み重なる事により、大きな対応工数になる事が多い。例えば、1機能毎に開発担当者が別であったとする。それぞれの開発担当者が実現し切れない部分を安易に”ユーザによるマニュアル対応”と位置付けて処理した場合、何が起きるか。対象機能が100あれば、100のマニュアル対応が発生する事になる。マニュアル対応によるヒューマンエラーを無くし、極力システム化する事が私達の使命である事が念頭にあれば、安易な仕様決定とはならないはずである。次の職業倫理として、「保守性を意識すること」がある。ソフトウェアはリリース後に必ず機能追加やバグが発生する。その際に、保守性が高いソフトウェアは機能追加やバグ修正が容易である。保守性が低い場合、対応時の工数が大きく異なる。1機能毎の対応工数に差がある場合、ソフトウェアが使用される10年間ではどの程度差が開くかを考えると、保守性を意識することの重要性は理解出来る。また、対応工数差の比率は常に同じではない。保守性が低いながらも、機能追加を行い続けたソフトウェアは、後続の機能追加が非常に難しくなる。難しくなると、設計、実装、テスト等の各工程で工数を大きく取られる為、対応工数差の比率は更に広がる。よって、保守性を意識することは、対価を支払う顧客の為でもあり、運用保守を行う仲間の為にもなる。

    3. 「ソフトウェアの品質」による分析

    ソフトウェアの品質とは人により捉え方が異なるものである。例えば、ソフトウェア技術者にとっては、ソースコードの品質であり、そのレベル感も個々に異なる。ユーザにとっては、システム(ソフトウェア)の品質であり、同様に個々に使用感や、ビジネスプロセスの効率化に品質を感じるかもしれない。よって、ソフトウェア技術者として大事な事は、誰の為のソフトウェアであるかを意識することである。個人的にはソースコードの品質が大事なのは当然の事として、よりユーザの立場に立ち、分析・設計・実装等を行って行く事が、ソフトウェアの価値向上に繋がり、ソフトウェア品質が向上する。ユーザの立場に立ったのと同様に運用保守担当者の立場に立った場合はどのように振舞うべきか。意識をしていなければ、成果物には反映されない事は明白である。

    4. 「ソフトウェアプロセス」による分析

    ウォーターフォールモデルのような規範的なプロセスモデルとアジャイル開発のようなアジャイルプロセスモデルを比較すると、どちらも一長一短があり、どちらが優れているということは言えない。よって、必ずどちらかのプロセスモデルで実施するのではなく、状況により選択していく柔軟性が大事である。例えば、運用保守フェーズであれば、アジャイルプロセスモデルが適している。顧客もチームメンバとして迎え、設計→プロト実装→本実装→テスト→リリースのイテレーションを行う。これにより保守運用フェーズのスピード感を出し易い。一方、規範的なプロセスモデルでは、開発フェーズの前にプロトタイプフェーズを行うのが良い。プロトタイプを行う事で余分な工数が掛かるが、顧客やユーザとの仕様のズレが解消され、顧客満足度を上げやすい。また、プロトタイプを実装するにあたり、調査等も行う為、設計・実装工程以降のインプットともなり易く、結果として工数短縮に繋がりやすい。設計・実装の前にある程度詳細に把握出来ていると、後工程の成果物精度が上がり、結果より良いソフトウェアライフサイクルに繋がるのではないだろうか。

    5. 「ソフトウェアの分析、設計、実装、テスト」による分析

    本項ではソフトウェアの分析、設計、実装、テストの各工程における分析を行う。

     
    5.1. 分析

    分析工程ではオブジェクト指向分析、構造化分析等の分析方法があるが、最終的な成果物は、顧客のようなソフトウェア技術者以外の人が確認し易い記法で書かなければならない。例えば、オブジェクト指向分析であれば、UMLを使用し、ユースケース図やユースケース記述、アクティビティ図等であり、構造化分析であれば、DFD等である。これらの成果物は設計工程以降のインプット情報となり、運用保守にも読まれる成果物となる。また、要求仕様化をするにあたり、As-Isモデル、To-Beモデルを定義すると良い。これらは顧客側や開発側の漏れを無くし、ビジネスプロセスの再構築に役立つ。同様に運用保守時の理解促進に役立つ。

     
    5.2. 設計

    設計書は何の為にあるか。私は運用保守フェーズ以降の為にあると考える。運用保守フェーズでは、新しいベンダーや担当者が入ってくる事が多いが、分析工程の成果物と一緒に参照されるのは設計書類となる。よって、設計書は実装の為に書くのではなく、保守対応者へのメッセージとして書く必要がある。但し、注意点として、設計書が必ず正しいわけではない。結局真実はソースコードそのものである。また、設計と言っても、基本設計、概要設計、詳細設計と複数あるが、実装をする為の詳細設計は必要ないと考えている。必要があれば、後追いで作成が可能である。実装中に詳細設計に記述している内容とズレが出てくる事は普通であるし、運用保守において詳細設計書を確認する事は少ないはずだからである。前述しているが、詳細設計書を参照するのであれば、ソースコードを確認したほうが良い。詳細設計が無い事により、設計書の作成や修正といった無駄な工数削減が可能である。注意点として、その前工程にあたる概要設計の記述レベルとして詳細に記述する事は極力避けるべきである。特に大規模開発で開発工程を開発パートナーへ依頼する場合は、ある程度の仕様に揺れ(不明点や考慮が必要だと感じてもらう)を持たせ、開発工程で検討を行ってもらう事が大事である。理由として、請け負った時点である程度詳細に決まっていると、更なる詳細部分(例えば、頻度の低いエラーケース等)を考慮しなくなる恐れがあるからである。よって、各設計書の記述レベルを事前に定義し、周知徹底する事が結果的に品質のよいソフトウェアが完成する。

     
    5.3. 実装

    シンプルなソースコードは、可読性・保守性・開発効率、そして美がある。言い換えると美しいソースコードは結局シンプルである。シンプルなコードを実装する事は、ソフトウェアライフサイクルが上手く循環する為の根幹となる。シンプルなコード→可読性が良い→保守性が良い→開発効率が上がる→機能毎の工数が減る→他機能に工数を掛ける→→→……(循環する)また、最低限の処理効率化は必要あるが、過度な効率化やテクニカルな記述は避けるべきである。個々の要件にもよるが、以前と比較し、CPUやメモリ等のハードウェア性能も上がっている為、過度な効率化を進めるよりも、多少の緩みを持たせ可読性を上げるべきである。必要であれば、スケールアウト・スケールアップや他技術の併用によりパフォーマンス改善は可能である。

     
    5.4. テスト

    テストの無いソフトウェア開発は有り得ない。これは誰もが共通の認識であると思うが、テストにたいする意識レベルの差により出来は全く違うものになる。まず、ソフトウェアのテストは何の為にあるのかを考えると、テストケースやテストデータ作成に対して、気を抜けなくなるはずである。私にとってテストケース・データの作成タスクは楽しい作業ではないが、ケースやデータがソフトウェアの仕様を網羅しているかを常に意識している。テスト方法としては、ホワイトボックステストよりもブラックボックステストの方が大事である。ソフトウェアが持つ本来の仕様を満たしているかどうかは、ブラックボックスでないと分からないからである。また、後工程で発覚したテスト不足は前工程に逆戻りするだけでなく、コストに大きなインパクトを与える事が多い。テスト工程は時間が掛かるが、やはり大事な工程である。以上のような意識を持ながらテストを行う事で、運用保守フェーズにおけるバグ発覚は非常に少なくなる。

    6. まとめ

    以上の事から、保守と変更が容易なプログラムを作成する為には、ソフトウェア技術者がどの立ち位置で振舞うかによって、結果は全く異なる。ソフトウェア構築サービスを請け負う以上、当然これらの責務を負い、遂行しなければならないと考える。

    以上

     

    情報処理試験(データベース)

    震災の影響で情報処理試験は延期となっていましたが、そろそろ試験日が近づいてきてしまったので、
    少しずつ進めています。今回もデータベーススペシャリスト試験を受けます。
    去年は午前Ⅱで敢え無く沈んだので、今年は何とかしたいものです。
    各試験の応募者数は以下になります。

     
    応募者数
    総数
    294,657
    ITパスポート試験
    61,984
    基本情報技術者試験
    88,001
    応用情報技術者試験
    62,116
    高度試験
    82,556
     プロジェクトマネージャ試験
    20,459
     データベーススペシャリスト試験
    20,207
     エンベデッドシステムスペシャリスト試験
    6,196
     情報セキュリティスペシャリスト試験
    30,704
     システム監査技術者試験
    4,990

    情報セキュリティスペシャリスト試験って意外に応募者多いのですね。


    久しぶりにTOEIC受けてきました

    今日、久しぶりにTOEICを受けてきました。感触的には芳しくないです。。
    レアジョブで多少の会話が出来ていても、TOEICのリスニングは非常に聞き取りづらかったです。
    また、文法やらボキャブラリのセクションももう少し勉強が必要だと認識しました。
    ですので、スコア的にはあまり上がっていないかもしれません。

    ひとまず、現状把握出来たので、次回までに修正していきたいと思います。(次回は11月予定です)

    HTML5入門

    入門 HTML5を読み進めています。今まで漠然と理解していた事が、詳細に説明されています。
    まだ読み途中ですが、メモ書き程度に残します。

    HTML5のサポート
    HTML5はひとつの大きなものではなく、個別の機能が集まったものである為、「HTML5のサポート」という言葉は意味をなさない。例えば、Canvasやビデオといった新機能がサポートされているかどうかは、機能毎に異なるということ。

    新機能の内、注目する機能は以下である。

       

    1. Canvas
    2. 解像度に依存するビットマップ描画機能で、グラフ・ゲームのグラフィックスやビジュアル画像を表示する
       

    3. video
    4. QuickTimeやFlashは不要となる。HTML5ビデオでは、全てのブラウザでサポートされているコーデックは存在しない。最終的には2種類に絞り込める。MAC系のh264, その他のOggである。
      GoogleによるChrome次期バージョンからのH264の非サポートは、コーデックを巡る主導権争いの1つである。
       

    5. ローカルストレージ
    6. 従来のクッキーは容量制限があるが、HTML5ストレージはより大きな情報量を扱えるようになる。ローカルストレージのセキュリティだが、コンピュータに物理的にアクセス出来てしまった場合は読み取られる。しかし、ブラウザの中では、自身が保存した値を読んだり変更は可能だが、別サイトが保存した値へのアクセスは出来ない。同一生成元制限(SOP)という。
       

    7. Web Workers
    8. ブラウザがJavaScriptをバックグラウンドで動作させるための標準的な方法を提供する。同時に動作する複数のスレッドを生成可能となる。
       

    9. オフラインWebアプリケーション
    10. 文字通り、オフラインで動くアプリケーションで、オンライン状態に戻った際は、オフライン時に行われた処理(例えば、変更など)をサーバ側へアップする。
       

    11. Geolocation
    12. 該当者(例えば、自分)が、世界のどこにいるのかを検出する機能で、その情報を他人と共有出来る。
       

    13. inputタイプ
    14. 従来のフォームのinputタイプに定義が多く追加された。
       

    15. プレースフォルダ
    16. 入力フィールドのプレースホルダを設定出来る。プレースホルダとは、フィールドにヘルプ文のように表示される文字の事。
       

    17. マイクロデータ
    18. Webページに意味付けを追加する為の標準的な方法であるが、独自フォーマット定義も可能。

    個々の機能については、また読み進めてから検証していくこととします。


    MySQL Expert at Yahoo!

    4月にMySQL Conference & Expoが開催されたので、その時のスライドをチェックしてみてると、
    4年前にYahoo! incでパフォーマンスチューニングで相談させてもらったJayさんを発見した。
    Websiteもあった。

    あの相談をさせてもらった頃から、自分自身が少しは成長していると良いのですが。。
    しかし、スライドがkeynoteで見れない・・・。学校の人に変換してもらおう。

    当時、現場で使える MySQL (DB Magazine SELECTION)
    がバイブルでした。


    Antセットアップ

    Mavenに続いて、Antも必要とのことだった為、セットアップしました。こちらはあっさり完了しました。

    1. Ant ダウンロード
      Apache Ant ProjectからAntをダウンロードします。
      Mavenと同様に解凍後、任意のディレクトリへ格納します。私は “C:Eclipse_programant-1.8.2” にしました。
    2. 環境変数設定
      次に環境変数を設定し、AntへPathを通します。(JAVAへもPathを通すこと)
      環境変数の設定が出来たら、コマンドプロンプトで確認します。

      C:Usershoge_user>ant -v
      Apache Ant(TM) version 1.8.2 compiled on December 20 2010
      Trying the default build file: build.xml
      Buildfile: build.xml does not exist!
      Build failed
      

      エラーが出ています。Antはビルドファイルの情報を元に、ビルドしますが、ファイル指定がなかった為にカレントディレクトリのbuild.xmlを探しにいきます。(そして、無かった)

    3. ビルドファイル作成
      テストの為にサンプルのbuild.xmlを任意のディレクトリに作成します。

      <?xml version="1.0" encoding="UTF-8" ?>
      
      <project name="anttest" default="test">
      
        <target name="test">
          <echo message="Hello, World"/>
        </target>
      
      </project>
      

      ビルドファイルを作成したディレクトリに移動し、antを実行します。

      C:Eclipse_programant-1.8.2>ant
      Buildfile: C:Eclipse_programant-1.8.2build.xml
      
      test:
           [echo] Hello, World
      
      BUILD SUCCESSFUL
      Total time: 0 seconds
      

    以上です。


    Mavenセットアップ

    大学院でMavenが必要になった為、Laptopに設定しました。
    セットアップが完了したところで、以下のエラーメッセージがEclipseのコンソールに出ていて苦労したのですが、MAVEN→M2Eclipseの順番で入れ直すことで解消しました。  → が、また出た。。

    Unable to update index for central|http://repo1.maven.org/maven2
    

    以下の手順でセットアップします。

    1. Mavenダウンロード
      Apache Maven ProjectからMavenのzipファイルをダウンロードします。今回はMaven3.0.3にしました。
      zipファイルは解凍し、適当なディレクトリへ格納します。
      私は “C:Eclipse_programmaven-3.0.3” としました。
    2. 環境変数設定
      次に環境変数を設定し、MavenへPathを通します。当然、JavaへのPathも通しておいてください。
      環境変数の設定が出来たら、コマンドプロンプトで確認します。

      C:Usershoge_user>mvn -v
      Apache Maven 3.0.3 (r1075438; 2011-03-01 02:31:09+0900)
      Maven home: C:Eclipse_programmaven-3.0.3bin..
      Java version: 1.6.0_24, vendor: Sun Microsystems Inc.
      Java home: C:Program FilesJavajdk1.6.0_24jre
      Default locale: ja_JP, platform encoding: MS932
      OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows"
      
    3. その他設定
    4. 設定ファイル”C:Eclipse_programmaven-3.0.3confsetting.xml” を編集します。
      ■Repositoryを適当に変更します。デフォルトだと下記のコメント内に出来ます。(初回起動時に勝手に作られる)

        <!-- localRepository
         | The path to the local repository maven will use to store artifacts.
         |
         | Default: ~/.m2/repository
        -->
        <localRepository>C:Eclipse_programmaven2Repository</localRepository>
      

      ■proxyを使っている場合はここをいじります。冒頭で記載したエラー文言でググると先頭の”proxies”からコメントアウトしろと書いてありましたが、特に変わらなかったです。一応、エラーが出たらココを確認してみてください。

      <proxies>
          <!-- proxy
           | Specification for one proxy, to be used in connecting to the network.
           |
          <proxy>
            <id>optional</id>
            <active>true</active>
            <protocol>http</protocol>
            <username>proxyuser</username>
            <password>proxypass</password>
            <host>proxy.host.net</host>
            <port>80</port>
            <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
          </proxy>
          -->
        </proxies>
      

      では、Eclipseにプラグインを追加する前に単体での動きを確認してみます。
      なお、こちらのサイトを参考にさせてもらいました。

      mvn archetype:create -DgroupId=jp.co.hogehoge -DartifactId=maventest
      

      初回実行時は複数のコンポーネントをダウンロードして、先に定義変更したLocal Repositoryに格納されます。
      また、C:UsersIhoge_usermaventest が出来ているので、change directryしてください。

      続いてビルドします。実行後、maventest配下にtargetディレクトリが出来て、classファイルも確認できます。

      mvn compile
    5. Eclipseにプラグインをインストール
      Eclipse上でM2Eclipseのプラグインをインストールします。
      [ヘルプ]-[新規ソフトウェアのインストール]で “http://m2eclipse.sonatype.org/sites/m2e” を登録し、プラグインをインストールします。インストールが完了したら、新規プロジェクトでMavenを選択し、確認してみてください。

    と、書いていた矢先に、またエラー[Unable to update index for central]が・・・
    もしかして、、作業していたネットワークの設定だけだったかもしれない。。



    DHD Android 2.2 → 2.3

    DHDのAndroid OSを2.2 → 2.3 へUpdateしました。
    UIパフォーマンスの改善が体感出来ますので、満足してます。

    アンドロイドはOSアップデートで動作が軽くなる?で書かれている方によると、Dalvik(だるびっく)vmのチューニング結果ではないかとのことです。

    DalvikでググるとAndroid開発の概要がありました。
    少しずつ調べてみようと思います。だが、、時間がない・・・、、

    JUDE→astah*

    大学院のレポートでオブジェクト指向分析設計をする必要があったので、家のマシンにUMLモデリングツールを
    入れようと思い、チェンジビジョンのサイトにいったら、JUDEは提供終了となっていました。
    後継製品として、astah*(アスター)という製品が出ていたようだ。
    community版があったので、使用してみましたが、違和感なく普通に使えました。

    astah*とJUDEの機能比較、データ互換性のまとめが以下にありました。
    機能比較
    データ互換性

    今後はこちらを使用させてもらおうと思います。

    ちなみに、チェンジビジョンは少し前に親会社が永和システムマネジメントさんに変わっていたのですね。
    チェンジビジョンの親会社の異動について

    EclipseのJavadoc日本語化

    EclipseのJavadocを日本語化しました。
    EclipseのデフォルトJavadocは、Web上の公式ドキュメント(英語版)を参照するようになっています。
    そのままでも良いのですが、ネットワークに繋がっていなくてもドキュメント参照出来るようにローカル側に配置しなおし、せっかくなので日本語版にしました。

    オラクルのダウンロードサイトからJava SE 6ドキュメントをダウンロードします。
    Zipファイルがダウンロード出来たら、任意の場所へ配置します。(解凍してもしなくてもどちらでも良いです)
    私はEclipseのアプリケーションディレクトリ直下にいれてしまいました。

    次に、Eclipseでパッケージエクスプローラを開きます。表示されていない場合は、
    「ウィンドウ」-「ビューの表示」から選択します。
    適当なプロジェクトを選択して、「JREシステム・ライブラリ」内のrt.jarのプロパティを開きます。
    開いたらJavadocロケーションを選択し、アーカイブ内のJavadocオプションを選択→外部ファイルで
    先ほどダウンロードしたファイル(ZIPのままでもOK)を指定します。
    アーカイブ内のファイルパス項目はdocs/ja/api 等のapiまでのパスを指定します。
    これで作業は完了なので、適当なクラス名等を打ち込んで、Javadocを呼び出します。(SHIFT+F2)

    作業効率が少しは上がるかもしれません。。

    Eclipseコンソール文字化け

    Eclipseのコンソール上で日本語などのDBCSが文字化けしていたので、文字コードをUTF-8に統一しました。

    Eclipseのデフォルトエンコードは”MS932″になっています。
    確認は、Eclipseのメニューから「実行構成」を開き、「共通タブ」を開きます。
    エンコードのセグメントにデフォルトエンコード”MS932″が指定されていることが確認出来ます。
    ここで、他のエンコードを指定しなおして見たのですが、文字化けは変わりませんでした。

    この指定はクラス毎に行っているようですので、そもそもEclipseのデフォルトエンコードを変更するようにしました。
    Eclipseのアプリケーションディレクトリ内のeclipse.iniがあるので、”-Dfile.encoding=utf-8″を追加してEclipseを再起動すれば完了です。

    -Xms40m
    -Xmx384m
    -javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar
    -Dfile.encoding=utf-8
    

    なお、-javaagent:plugins…..の部分は日本語化した際の追記行となります。



    JAVAを始めました(今更)

    猛烈に今更感がありますが、、JAVAを勉強し始めました。
    新卒の時に勉強していて結局使用することなく、他の言語に移ってしまっていました。
    今思うと、何故もっとやっておかなかったの・・・と考えてしまいますが、、人に言うのも恥ずかしさがあります。
    自社製品はJAVAも多いですし、今後のAndroidアプリ開発時にも使えそうですので、ちょこちょこやってみようと思います。
    また、新しい言語を覚える事は新たな発見がたくさんあり、他言語で不明瞭な点がくっきりするかもしれません。

    久しぶりに使うEclipseは高機能なこともあり使用方法に戸惑っています。
    一番面倒なのはデフォルトではエディタがvi風に使用出来なく、特に移動などが面倒でしたので、ViLikeを投入してみました。
    まだ少ししか触っていないですが、これは良いです。
    当初、viPluginを入れようとしたのですが、有料版になってしまったようです。

    Eclipse 3.6 完全攻略を買ったので、まずは色々さわってみよう。


    英文法を再学習

    去年の前半に英文法を基礎から再学習したのですが、最近は忘れてきているのでもう一度やっています。
    教材はNext Stage英文法・語法問題―入試英語頻出ポイント215の征服
    を使っているのですが、単に解くだけではまた忘れるので、回答の日本語訳→英語へ瞬間英作文が出来るように繰り返し唱えています。
    ですので、1ページ終わるのにかなりの時間がかかってしまっています。これだとページは進まないので、他に良い方法がないか思案中です、、

    あと、はてな界隈でGuruのみなさんから紹介される英語本はほとんど家にあることが分かりました。
    記事を読んだ時に買って、積ん読していたようです。結局いつかやるのであれば、今やってみようと思います。

    なお、電車でこの本を読んでいたら、前にいた高校生が同じ本を読んでいました。
    普通に恥ずかしかったですが、彼の本はかなり使い込まれていました。。


    「プログラマが知るべき97のこと」のまとめ

    プログラマが知るべき97のことを読み終えたので、備忘の為に1言ずつ残します。

    01 分別のある行動

      その場しのぎの対応は結局先送りとなって残る事が多い。これを技術的負債と言う。分別のある行動で早く負債は返済しよう。

    02 関数型プログラミングを学ぶことの重要性

      自分の書くコード品質が大きく高まるよ。参照透過性を意識してプログラミングする

    03 ユーザが何をするかを観察する(あなたはユーザではない)

      プログラマとユーザは思考が違うよ。立ち位置はユーザ側へ

    04 コーディング規約を自動化する

      コーディング規約は結局守られない。自動的、強制的に入れれば良い。品質も上がるよ。

    05 美はシンプルさに宿る

      シンプルなコードは可読性、保守性、開発効率、美があるよ。美しいコードは結局シンプルである。

    06 リファクタリングの際に注意すべきこと

      リファクタリングだからって、全部書き直しはNG。変更を少しずつ公開。人間はミスするよ。

    07 共有は慎重に

      コードの再利用化をする際は、コンテキストを読み取らないとダメだよ

    08 ボーイスカウト・ルール

      チェックイン時は必ずチェックアウト時よりも美しくすること

    09 他人よりまず自分を疑う

      まず自分のミスを疑ったほうが早く解決するよ

    10 ツールの選択は慎重に

      安易にツールをたくさん使用しない。最初は最低限のツールのみでスタート。

    11 ドメインの言葉を使ったコード

      ユーザ定義型を使え

    12 コードは設計である

      コードを書くことは機械的な作業ではない。創造的な仕事だよ。

    13 コードレイアウトの重要性

      コードレイアウトが良ければ、保守性があがる。素晴らしいプログラムは詩のようにみえるよ。

    14 コードレビュー

      コードレビューは楽しいものに。チーム全員に同じ知識を共有させる。その前にガイドラインは示すこと。

    15 コードの論理的検証

      バグを出さない為に制約を設けて、チーム内で互いに共有しあう。

    16 コメントについてのコメント

      コメントは悪ではない。でもコメント内容は気をつけて

    17 コードに書けないことのみをコメントにする

      コードに書いていないことを書くのではなく、書けないことをコメントする

    18 学び続ける姿勢

      とにかく学び続けないと置いていかれちゃうよ。

    19 誰にとっての「利便性」か

      APIは多様性を持たせずに分けた方が利用者側では使いやすい

    20 すばやくデプロイ、こまめにデプロイ

      デプロイタスクはプロジェクト初期から取り組んだほうが良い

    21 技術的例外とビジネス例外を明確に区別する

      技術的例外とビジネス例外を混在させると、本来の意味が分かりづらくなる。分けること。

    22 1万時間の訓練

      1万時間練習すれば、誰でもエキスパート。

    23 ドメイン特化言語

      DSLを導入すれば、作業スピードが上がるよ

    24 変更を恐れない

      コードの変更を恐れずに積極的にシステムを改良しよう

    25 見られて恥ずかしいデータは使わないこと

      テストデータも気を抜くな。誰がいつ見るか分からないよ

    26 言語だけでなく文化も学ぶ

      複数言語から新しい発想を得れば、違った解決方法が出るかもしれない

    27 死ぬはずのプログラムを無理に生かしておいてはいけない

      例外をユーザに絶対に見せない事が正しいわけではないよ

    28 「魔法」に頼りすぎてはいけない

      他人の仕事は簡単に見えるもの。簡単に見えると魔法のように自動的に出来るように感じる。それを解くと大変なことが起きる

    29 DRY原則

      繰り返しはダメだよ

    30 そのコードに触れてはならない!

      開発者はステージング、本番環境のコードに触っちゃダメ

    31 状態だけでなく「ふるまい」もカプセル化する

      バグが発生しにくくなるし、保守性もあがるよ

    32 浮動小数点数は実数ではない

      浮動小数点を使用する時は丸めが発生するかが重要

    33 オープンソースプロジェクトで夢を実現する

      オープンソースプロジェクトに参加すれば力がつくよ

    34 API設計の黄金律

      APIを提供するときはAPI自身のテストのみでなく、APIを利用するコードのテストも行うこと

    35 超人の神話

      超人はいないよ。みんな努力してきたからだよ

    36 ハードワークは報われない

      プロジェクトへの貢献は短い時間で高いパフォーマンスを出せばよい。その為には自分を磨け。

    37 バグレポートの使い方

      バグレポートには、再現方法・本来の仕様・実際の動作を記述する

    38 余分なコードは決して書かない

      今使用しないコードは書かない。その時書けば良い。

    39 最初が肝心

      取っ掛かり易くしてあげること

    40 プロセス間通信とアプリケーションの応答時間の関係

      リモートプロセス間通信を少なくすることが大事。結構コストがかかるよ

    41 無駄な警告を排除する

      警告を出し過ぎると、本来検知して欲しい警告が埋もれるよ

    42 コマンドラインツールを使う

      IDEは便利だけど、裏で何をしているかを知ることが大事

    43 プログラミング言語は複数習得すべき

      少なくとも2つのパラダイムの言語を習得すること

    44 IDEを知る

      IDEを深く知れば、作業効率が上がる

    45 限界を知る

      何事も限界を知れば、次の一手が出てくる

    46 すべきことは常に明確に

      やっている事を明確にしないと遠回りになるよ

    47 大量のデータはデータベースで

      あたりまえだけど、データはデータベースへ

    48 いろいろな言葉を学ぶ

      複数プログラミング言語だけでなく、その他の職業毎の用語を知ればコミュニケーションは円滑になるよ

    49 見積りとは何か

      見積を大事に。見積の上に、ターゲット、コミットメントがあるよ

    50 Hello, Worldから始めよう

      何でも自分で試してみるのが大事

    51 プロジェクト自身にしゃべらせる

      自動検知する仕組みがあれば楽

    52 「その場しのぎ」が長生きしてしまう

      暫定ソリューションは結局残ることが多い。勇気をだして、暫定ソリューションは捨てて新しくつくろう。

    53 正しい使い方を簡単に、誤った使い方を困難に

      インタフェースはユーザのもの。開発側の利便の為ではないよ

    54 見えないものを見えるように

      何でも見える化することが大事

    55 並行処理に有効なメッセージパッシング

      共有メモリを使わずにメッセージパッシングを使ってプログラミングすると良い

    56 未来へのメッセージ

      自分の書くコードは未来の誰かへのメッセージ。自分に対してかもしれない

    57 ポリモーフィズムの利用機会を見逃さない

      ポリモーフィズムを有効に使えば、コード量が減り読み易くなる

    58 テスト担当者はプログラマの友人

      テスト担当者と仲良くしよう。顧客に見られる事を防いでくれたのは誰ですか

    59 バイナリは常に1つ

      どの環境でも同一バイナリを使用しよう

    60 真実を語るはコードのみ

      設計書は必ず正しいわけではない。最後はやっぱりコードが真実

    61 ビルドをおろそかにしない

      ビルドスクリプトを知らない人が多い。ビルドプロセスも勉強しよう

    62 プリミティブ型よりドメイン固有の型を

      コードの質が大きく向上するよ

    63 ユーザの操作ミスを防止する

      ユーザが操作ミスをしない仕組みをつくってあげよう

    64 プロのプログラマとは?

      「自分が責任を取る」という態度、責任感を持て。

    65 バージョン管理システムを有効に使う

      とにかく何でもバージョン管理していこう

    66 いったんコンピュータから離れてみる

      一旦離れると、新しい答えが見つかって解決することがあるよ。

    67 コードを読む

      本を読むより、他人のコードを読むほうが力がつくよ

    68 「人間」を知る

      相手を知れば、円滑にいくよ

    69 車輪の再発明の効用

      新しい発見を目指すと失敗が多いけど、大きな経験が出来る。

    70 シングルトンパターンの誘惑に負けない

      何でもシングルトンにすれば良いのではない。必要なインスタンスが絶対に1つという確信がある時以外は使わない。

    71 パフォーマンスへの道は地雷コードで敷き詰められている

      パフォーマンス改善には、コードの依存性や複雑さが問題になる。これらを計測することが大事。

    72 シンプルさは捨てることによって得られる

      コードはシンプルであるべき。良い部分を残して悪い部分は捨てる。または最初から書きなおすという気持ちを持っていれば、無駄なコードは書かなくなる

    73 単一責任原則

      変更する理由が同じなら集める。異なるなら分ける。

    74 「イエス」から始める

      否定から入らず、肯定から入れば、対立せずに協力関係が生まれるよ

    75 面倒でも自動化できることは自動化する

      自動化すれば対応工数が少なくなる

    76 コード分析ツールを利用する

      コード品質を高める為にコード分析ツールを利用すると良い

    77 偶然の仕様ではなく本物の仕様のためのテストを書く

      ホワイトボックスよりブラックボックステストの方が大事

    78 テストは夜間と週末に

      人がいない時のほうがマシンパフォーマンスが出やすいよ

    79 テストのないソフトウェア開発はあり得ない

      テストは時間がかかるけど、やっぱり大事

    80 1人より2人

      ペアプログラミングは色んな効果があるよ。成長。不意な退職など

    81 エラーがエラーを相殺してしまう

      ある箇所と別の箇所で矛盾が生じているとエラーが相殺されて、正常の動きに見えることがある。思い込みを捨てて、冷静さを保つことが大事。

    82 他者への思いやりを意識したコーディング

      プログラマは1人ではない。他社への思いやりが大事。

    83 UNIXツールを友にする

      UNIXツールを使いこなすと作業効率があがるよ

    84 正しいアルゴリズムとデータ構造を選ぶ

      アルゴリズム毎の効果と適用ケースを学べ

    85 冗長なログは眠りを妨げる

      重要なログは必要最低限にする

    86 WETなシステムはボトルネックが見つかりにくい

      DRY原則に反したシステムをWETなシステムと言う。

    87 プログラマとテスターが協力してできること

      開発当初から両者が協力すれば奇跡が起きる。テスターはソフトウェアを破壊するのが役目ではない。プログラマはテスターを邪魔をする敵と考えてはダメ。

    88 コードは生涯サポートするつもりで書く

      良くないコードを書いているとキャリアアップのチャンスも減るよ

    89 関数の「サイズ」を小さくする

      関数のサイズ(コード量だけでなく、取りうる値)を小さくするとバグが少なくなる

    90 コードを見る人のためにテストを書く

      良いテストはドキュメントの代わりになるよ

    91 良いプログラマになるには

      積極的に新しい言語に取り組む。分かりやすい、正しい、保守しやすいコードを書くよう心がける。一人ではなくチームでやっていることを意識する

    92 顧客の言葉はそのまま受け取らない

      顧客独自の言葉があるので確認すること

    93 エラーを無視するな

      小さなエラーもスルーしない。こまめに対応すれば後から楽になる

    94 リンカは魔法のプログラムではない

      リンカは複雑なことはしていない。エラーをちゃんと見ればヒントがあるよ

    95 ペアプログラミングと「フロー」

      完全に没頭している時をフロー状態という。この状態を維持するにはペアプログラミングあ有用だ

    96 テストは正確に、具体的に

      テスト仕様と予想結果はちゃんと書こう。

    97 ステートに注目する

      実行時に常にステートを意識しよう

    以前読んだソフトウェアアーキテクトが知るべき97のことについても、そのうちまとめます。既に内容は忘れました・・


    入学式に行ってきました

    今日は大学院の入学式に行ってきました。
    昨年から科目履修生として通っていましたが、今日から1年生です。

    私はオライリー本の目次を見るとワクワクする事が多いのですが、この大学院の科目群を初めて見た時、同様の感覚を感じました。
    単純ですが、これが複数候補からこの大学院から選んだ理由です。きっと、自分が今欲しい知識・情報・技術と合致していたのでしょう。
    何でもそうですが、今したいと思った時が最善の時です。読みたいと思った本は今読むのが最も効果があるのです。
    私は行動をして上手くいかなかったら次に行くようにしています。

    この2年間は以下をより意識しながら過ごしてみます。
     ■既に得ている知識を更に深く・体系的に学ぶ
     ■不足している知識・技術を幅広く補完する
     ■全ての情報を得る時間は無い為、取捨選択を大事にする

    快く了承してくれた妻に感謝しつつ、安くはないお金と大切な時間を大事にしながら、
    少しでも大きく成長して、2年後を迎えたいです。

    なお、入学式はスーツで行った方が良いようです・・・(私は私服で行って、失敗しました。。)

    Androidの開発環境を整えました

    初めてのAndroid 第3版
    を参考にして、Androidの開発環境を整えました。

    必要なモノは以下となります。
    1. Java SE Development Kit(JDK)
     Android, Eclipseの実行には、Javaが必要です。念のため、バージョンを確認します。

    C:>java -version
    java version "1.6.0_24"
    java(TM) SE Runtime Environment (build 1.6.0_24-b07)
    Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02, mixed mode)
    

    上記のように出力されていない場合は、JDKを入れます。
    その後、環境変数”JAVA_HOME”にJavaのパスを設定します。

    2. Android SDK
    Android Developersから Android SDK をダウンロードします。
    展開先は任意ですので、適当に C:GoogleAndroidandroid-sdk 等とします。
    “SDK Manager.exe”を実行し、SDKマネージャを起動します。
     ・Installed packages でパッケージをインストールします。(多少時間がかかります)
     ・Virtual devices でデバイスを定義します。platformは自分が使用したいバージョンが良いです。

    最後に環境変数”Path”に、toolsとplatform-toolsを追加します。(Android2.3以降のplatformから両方必要です)

    3. Eclipse
    開発用IDEとしてEclipseをダウンロードします。
    いくつかパッケージがありますが、”Eclipse IDE for Java Developers”等で良いです。
    ダウンロード後、Eclipseを起動し、以下を設定します。
     ・[Help-Install New Software]からAndroid用のプラグインをインストールします。
       ⇒ Locationは、https://dl-ssl.google.com/android/eclipse/ となります。
     ・[Window-preferences-Android]で、Android SDKのパスを設定します。
       ⇒ Tagetに各platformが表示されていればOKです。

    以上で環境構築完了です。

    続いて、”HelloWorld”アプリを作成して、実行可能か確認します。
    [File-New-Project-Android-Android Project]で、Androidプロジェクトを作成します。
    作成後、実行すると、Build Targetで指定したplatformからEmulatorが起動されます。
    OS起動後、Home画面が表示⇒Menuボタンを押下すると、ロックが解除され”HelloWorld”が表示されます。


    なかなかEmulatorもかっこいい。。



    O’REILLYのeBook半額

    O’REILLYで被災者救済キャンペーンとして、eBookが半額で購入可能です。
    期間中(3/23-3/26)に購入された売上は、著作権者への印税や決済手数料を除いた全額が
    日本赤十字社への義援金にするそうです。

    Deal of the Week – 被災者支援キャンペーン開催

    もともとeBookは本に比べて若干安くなっていますが、今回は更に半額となるキャンペーンですので、
    ぜひ購入をしてはいかがでしょうか。

    さて何を買おうかな。

    2011/03/29追記
    結局これらを買いました。(先ほどようやくO’REILLYからDownloadリンクが送られてきました)


    PHPアクセラレータを入れてみる

    PHP5.2へバージョンアップしたついでに、PHPアクセラレータを入れました。
    こちらを参考にさせていただきました。

    PHPはアクセス都度、パース→コンパイル→(中間コード)→実行を行いますが、
    PHPアクセラレータを導入する事で、ある条件下でパース→コンパイルが不要となります。

    代表的なPHPアクセラレータは以下です。

  • APC(Alternative PHP Cache)
  • eAccelerator
  • XCache
  • PHPA(PHP Accelerator)

    PHPAはPHP5系をサポートしていない為、選択肢としては残りの3つからとなりますが、
    特に性能を求めている訳でもないので、他の方々もよくいれているAPCにします。

    まず、yumのリポジトリを設定します。

    [root@centos]# vi /etc/yum.repos.d/sea-mew.repo
    [sea-mew-noarch]
    name=SeaMew Fedora AddIn Packages
    baseurl=ftp://sea-mew.jp/yum/el5/noarch/
    enabled=1
    gpgcheck=0
    
    [sea-mew-common]
    name=SeaMew Fedora AddIn Packages
    baseurl=ftp://sea-mew.jp/yum/el5/x86_64
    enabled=1
    gpgcheck=0
    

    パッケージがあるか検索します。

    [root@centos]# yum search php-apc
    Loaded plugins: downloadonly, fastestmirror, priorities
    Loading mirror speeds from cached hostfile
     * addons: ftp.nara.wide.ad.jp
     * base: ftp.nara.wide.ad.jp
     * epel: ftp.jaist.ac.jp
     * extras: ftp.nara.wide.ad.jp
     * remi: rpms.famillecollet.com
     * rpmforge: fr2.rpmfind.net
     * updates: ftp.nara.wide.ad.jp
    addons                                                           |  951 B     00:00
    base                                                             | 2.1 kB     00:00
    c5-testing                                                       | 1.9 kB     00:00
    epel                                                             | 3.7 kB     00:00
    epel/primary_db                                                  | 3.6 MB     00:00
    extras                                                           | 2.1 kB     00:00
    remi                                                             | 2.6 kB     00:00
    remi/primary_db                                                  | 251 kB     00:01
    rpmforge                                                         | 1.1 kB     00:00
    sea-mew-common                                                   |  951 B     00:00
    sea-mew-common/primary                                           | 2.0 MB     00:01
    sea-mew-common                                                                6548/6548
    sea-mew-noarch                                                   |  951 B     00:00
    sea-mew-noarch/primary                                           | 1.0 MB     00:01
    sea-mew-noarch                                                                3898/3898
    updates                                                          | 1.9 kB     00:00
    Reducing CentOS-5 Testing to included packages only
    Finished
    2227 packages excluded due to repository priority protections
    =================================== Matched: php-apc ===================================
    php-apc.x86_64 : The apc (Alternative PHP Cache) module for PHP
    php-apc-admin.x86_64 : Web admin GUI for apc (Alternative PHP Cache)
    

    sea-mew-common にありましたので、yumでインストールします。

    [root@centos]# yum install php-apc
    Loaded plugins: downloadonly, fastestmirror, priorities
    Loading mirror speeds from cached hostfile
     * addons: ftp.nara.wide.ad.jp
     * base: ftp.nara.wide.ad.jp
     * epel: ftp.jaist.ac.jp
     * extras: ftp.nara.wide.ad.jp
     * remi: rpms.famillecollet.com
     * rpmforge: fr2.rpmfind.net
     * updates: ftp.nara.wide.ad.jp
    Reducing CentOS-5 Testing to included packages only
    Finished
    2227 packages excluded due to repository priority protections
    Setting up Install Process
    Resolving Dependencies
    --> Running transaction check
    ---> Package php-apc.x86_64 1:3.1.6-0m set to be updated
    --> Finished Dependency Resolution
    
    Dependencies Resolved
    
    ========================================================================================
     Package           Arch             Version              Repository                Size
    ========================================================================================
    Installing:
     php-apc           x86_64           1:3.1.6-0m           sea-mew-common           304 k
    
    Transaction Summary
    ========================================================================================
    Install       1 Package(s)
    Upgrade       0 Package(s)
    
    Total download size: 304 k
    Is this ok [y/N]: y
    Downloading Packages:
    php-apc-3.1.6-0m.x86_64.rpm                                      | 304 kB     00:00
    Running rpm_check_debug
    Running Transaction Test
    Finished Transaction Test
    Transaction Test Succeeded
    Running Transaction
      Installing     : php-apc                                                          1/1
    
    Installed:
      php-apc.x86_64 1:3.1.6-0m
    
    Complete!
    

    GUIが用意されているようですので、インストールしておきます。

    [root@centos]# yum install php-apc-admin
    Loaded plugins: downloadonly, fastestmirror, priorities
    Loading mirror speeds from cached hostfile
     * addons: ftp.nara.wide.ad.jp
     * base: ftp.nara.wide.ad.jp
     * epel: ftp.jaist.ac.jp
     * extras: ftp.nara.wide.ad.jp
     * remi: rpms.famillecollet.com
     * rpmforge: fr2.rpmfind.net
     * updates: ftp.nara.wide.ad.jp
    Reducing CentOS-5 Testing to included packages only
    Finished
    2227 packages excluded due to repository priority protections
    Setting up Install Process
    Resolving Dependencies
    --> Running transaction check
    ---> Package php-apc-admin.x86_64 1:3.1.6-0m set to be updated
    --> Finished Dependency Resolution
    
    Dependencies Resolved
    
    ========================================================================================
     Package               Arch           Version              Repository              Size
    ========================================================================================
    Installing:
     php-apc-admin         x86_64         1:3.1.6-0m           sea-mew-common          22 k
    
    Transaction Summary
    ========================================================================================
    Install       1 Package(s)
    Upgrade       0 Package(s)
    
    Total download size: 22 k
    Is this ok [y/N]: y
    Downloading Packages:
    php-apc-admin-3.1.6-0m.x86_64.rpm                                |  22 kB     00:00
    Running rpm_check_debug
    Running Transaction Test
    Finished Transaction Test
    Transaction Test Succeeded
    Running Transaction
      Installing     : php-apc-admin                                                    1/1
    /var/tmp/rpm-tmp.74136: line 1: fg: no job control
    エラー: %post(php-apc-admin-3.1.6-0m.x86_64) スクリプトの実行に失敗しました。終了ステー タス 1
    
    Installed:
      php-apc-admin.x86_64 1:3.1.6-0m
    
    Complete!
    

    エラーが出ましたが、一旦このまま進めます。
    以下をコピーし、apacheをrestartします。

  • apc-mmap.so
  • apc-sem.so
    [root@centos]# cp -a /usr/lib64/php/extensions/apc-*.so /usr/lib64/php/modules/
    [root@centos]# ls -al /usr/lib64/php/modules/apc*
    -rwxr-xr-x 1 root root 166067  1月 20 06:09 /usr/lib64/php/modules/apc-mmap.so
    -rwxr-xr-x 1 root root 163217  1月 20 06:09 /usr/lib64/php/modules/apc-pthread.so
    -rwxr-xr-x 1 root root 163395  1月 20 06:09 /usr/lib64/php/modules/apc-sem.so
    -rwxr-xr-x 1 root root 162476  1月 20 06:09 /usr/lib64/php/modules/apc-spinlocks.so
    [root@centos]# service httpd restart
    httpd を停止中:                                            [  OK  ]
    httpd を起動中:                                            [  OK  ]
    
  • PHP5.2xインストール

    WordPressのplugin quick-cacheをインストールしたところ、PHP5.2へバージョンアップしろと言い出した為、
    PHP5.1 → 5.2へバージョンアップをする事にしました。
    現在のyumでは5.1までしか入らないようですので、新たにリポジトリを追加して5.2をインストールします。
    なお、こちらを参考にさせて頂きました。

    最初にPHPのバージョンを確認します。

    [root@centos]# php -v
    PHP 5.1.6 (cli) (built: Nov 29 2010 16:47:46)
    Copyright (c) 1997-2006 The PHP Group
    Zend Engine v2.1.0, Copyright (c) 1998-2006 Zend Technologies
    
    [root@centos]# yum list installed | grep php*
    php.x86_64                                5.1.6-27.el5_5.3             installed
    php-cli.x86_64                            5.1.6-27.el5_5.3             installed
    php-common.x86_64                         5.1.6-27.el5_5.3             installed
    php-mbstring.x86_64                       5.1.6-27.el5_5.3             installed
    php-mcrypt.x86_64                         5.1.6-15.el5.centos.1        installed
    php-mysql.x86_64                          5.1.6-27.el5_5.3             installed
    php-pdo.x86_64                            5.1.6-27.el5_5.3             installed
    

    続いて、リポジトリを追加します。

    [root@centos]# vi /etc/yum.repos.d/utterramblings.repo
    [utterramblings]
    name=Jason's Utter Ramblings Repo
    baseurl=http://www.jasonlitka.com/media/EL$releasever/$basearch/
    enabled=1
    gpgcheck=1
    gpgkey=http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka
    priority=0
    

    updateします。

    [root@centos]# yum -y update php
    Loaded plugins: downloadonly, fastestmirror, priorities
    Loading mirror speeds from cached hostfile
     * addons: ftp.nara.wide.ad.jp
     * base: ftp.nara.wide.ad.jp
     * epel: ftp.jaist.ac.jp
     * extras: ftp.nara.wide.ad.jp
     * remi: rpms.famillecollet.com
     * rpmforge: fr2.rpmfind.net
     * updates: ftp.nara.wide.ad.jp
    Reducing CentOS-5 Testing to included packages only
    Finished
    548 packages excluded due to repository priority protections
    Setting up Update Process
    Resolving Dependencies
    --> Running transaction check
    ---> Package php.x86_64 0:5.2.16-jason.1 set to be updated
    --> Processing Dependency: php-common = 5.2.16-jason.1 for package: php
    --> Processing Dependency: php-cli = 5.2.16-jason.1 for package: php
    --> Running transaction check
    ---> Package php-cli.x86_64 0:5.2.16-jason.1 set to be updated
    --> Processing Dependency: php-common = 5.1.6-27.el5_5.3 for package: php-pdo
    --> Processing Dependency: php-common = 5.1.6-27.el5_5.3 for package: php-mbstring
    --> Processing Dependency: php-common = 5.1.6-27.el5_5.3 for package: php-mysql
    ---> Package php-common.x86_64 0:5.2.16-jason.1 set to be updated
    --> Running transaction check
    ---> Package php-mbstring.x86_64 0:5.2.16-jason.1 set to be updated
    ---> Package php-mysql.x86_64 0:5.2.16-jason.1 set to be updated
    --> Processing Dependency: libmysqlclient.so.16(libmysqlclient_16)(64bit) for package: php-mysql
    --> Processing Dependency: libmysqlclient.so.16()(64bit) for package: php-mysql
    ---> Package php-pdo.x86_64 0:5.2.16-jason.1 set to be updated
    --> Running transaction check
    --> Processing Dependency: libmysqlclient.so.15()(64bit) for package: mysql-server
    --> Processing Dependency: libmysqlclient.so.15()(64bit) for package: perl-DBD-MySQL
    --> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: mysql-server
    --> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: perl-DBD-MySQL
    --> Processing Dependency: libmysqlclient_r.so.15()(64bit) for package: mysql-server
    --> Processing Dependency: libmysqlclient_r.so.15(libmysqlclient_15)(64bit) for package: mysql-server
    --> Processing Dependency: mysql = 5.0.77-4.el5_5.4 for package: mysql-server
    ---> Package mysql.x86_64 0:5.1.52-jason.1 set to be updated
    --> Running transaction check
    ---> Package mysql-server.x86_64 0:5.1.52-jason.1 set to be updated
    ---> Package mysqlclient15.x86_64 0:5.0.91-1.jason.1 set to be updated
    --> Finished Dependency Resolution
    
    Dependencies Resolved
    
    ========================================================================================
     Package              Arch          Version                 Repository             Size
    ========================================================================================
    Updating:
     php                  x86_64        5.2.16-jason.1          utterramblings        3.8 M
    Installing for dependencies:
     mysqlclient15        x86_64        5.0.91-1.jason.1        utterramblings        2.0 M
    Updating for dependencies:
     mysql                x86_64        5.1.52-jason.1          utterramblings        3.5 M
     mysql-server         x86_64        5.1.52-jason.1          utterramblings         13 M
     php-cli              x86_64        5.2.16-jason.1          utterramblings        2.6 M
     php-common           x86_64        5.2.16-jason.1          utterramblings        522 k
     php-mbstring         x86_64        5.2.16-jason.1          utterramblings        1.4 M
     php-mysql            x86_64        5.2.16-jason.1          utterramblings        280 k
     php-pdo              x86_64        5.2.16-jason.1          utterramblings        169 k
    
    Transaction Summary
    ========================================================================================
    Install       1 Package(s)
    Upgrade       8 Package(s)
    
    Total size: 27 M
    Downloading Packages:
    Running rpm_check_debug
    Running Transaction Test
    Finished Transaction Test
    Transaction Test Succeeded
    Running Transaction
      Updating       : php-common                                                      1/17
      Installing     : mysqlclient15                                                   2/17
      Updating       : php-pdo                                                         3/17
      Updating       : mysql                                                           4/17
      Updating       : php-cli                                                         5/17
      Updating       : php-mysql                                                       6/17
      Updating       : php                                                             7/17
    
     ******************************************************************************
     * If this is a new installation you should run 'mysql_secure_installation'.  *
     *                                                                            *
     * If you are upgrading from a previous version of MySQL you should run the   *
     * 'mysql_upgrade' script to check your tables and update them to the newest  *
     * format. This command may take a long time to run if you have large tables. *
     ******************************************************************************
    
      Updating       : mysql-server                                                    8/17
      Updating       : php-mbstring                                                    9/17
      Cleanup        : php-common                                                     10/17
      Cleanup        : php-mysql                                                      11/17
      Cleanup        : php                                                            12/17
      Cleanup        : php-pdo                                                        13/17
      Cleanup        : php-cli                                                        14/17
      Cleanup        : mysql                                                          15/17
      Cleanup        : mysql-server                                                   16/17
      Cleanup        : php-mbstring                                                   17/17
    
    Dependency Installed:
      mysqlclient15.x86_64 0:5.0.91-1.jason.1
    
    Updated:
      php.x86_64 0:5.2.16-jason.1
    
    Dependency Updated:
      mysql.x86_64 0:5.1.52-jason.1              mysql-server.x86_64 0:5.1.52-jason.1
      php-cli.x86_64 0:5.2.16-jason.1            php-common.x86_64 0:5.2.16-jason.1
      php-mbstring.x86_64 0:5.2.16-jason.1       php-mysql.x86_64 0:5.2.16-jason.1
      php-pdo.x86_64 0:5.2.16-jason.1
    
    Complete!
    

    インストール出来たので、確認します。

    [root@centos]# php -v
    PHP Warning:  PHP Startup: mcrypt: Unable to initialize module
    Module compiled with module API=20050922, debug=0, thread-safety=0
    PHP    compiled with module API=20060613, debug=0, thread-safety=0
    These options need to match
     in Unknown on line 0
    PHP 5.2.16 (cli) (built: Dec 17 2010 14:11:16)
    Copyright (c) 1997-2010 The PHP Group
    Zend Engine v2.2.0, Copyright (c) 1998-2010 Zend Technologies
    

    PHP自体は5.2になっていますが、Warningが出ていました。
    php-mcryptが5.1のままでしたので、明示してupdateします。

    [root@centos]# yum list installed | grep php*
    php.x86_64                                5.2.16-jason.1               installed
    php-cli.x86_64                            5.2.16-jason.1               installed
    php-common.x86_64                         5.2.16-jason.1               installed
    php-mbstring.x86_64                       5.2.16-jason.1               installed
    php-mcrypt.x86_64                         5.1.6-15.el5.centos.1        installed
    php-mysql.x86_64                          5.2.16-jason.1               installed
    php-pdo.x86_64                            5.2.16-jason.1               installed
    
    [root@centos]# yum update php-mcrypt
    Loaded plugins: downloadonly, fastestmirror, priorities
    Loading mirror speeds from cached hostfile
     * addons: ftp.nara.wide.ad.jp
     * base: ftp.nara.wide.ad.jp
     * epel: ftp.jaist.ac.jp
     * extras: ftp.nara.wide.ad.jp
     * remi: rpms.famillecollet.com
     * rpmforge: fr2.rpmfind.net
     * updates: ftp.nara.wide.ad.jp
    Reducing CentOS-5 Testing to included packages only
    Finished
    548 packages excluded due to repository priority protections
    Setting up Update Process
    Resolving Dependencies
    --> Running transaction check
    ---> Package php-mcrypt.x86_64 0:5.2.16-jason.1 set to be updated
    --> Finished Dependency Resolution
    
    Dependencies Resolved
    
    ========================================================================================
     Package            Arch           Version                 Repository              Size
    ========================================================================================
    Updating:
     php-mcrypt         x86_64         5.2.16-jason.1          utterramblings          50 k
    
    Transaction Summary
    ========================================================================================
    Install       0 Package(s)
    Upgrade       1 Package(s)
    
    Total download size: 50 k
    Is this ok [y/N]: y
    Downloading Packages:
    php-mcrypt-5.2.16-jason.1.x86_64.rpm                             |  50 kB     00:00
    Running rpm_check_debug
    Running Transaction Test
    Finished Transaction Test
    Transaction Test Succeeded
    Running Transaction
      Updating       : php-mcrypt                                                       1/2
      Cleanup        : php-mcrypt                                                       2/2
    
    Updated:
      php-mcrypt.x86_64 0:5.2.16-jason.1
    
    Complete!
    

    忘れずに最初追加したリポジトリを無効化(enabled=0)しておきます。

    [root@centos]# vi /etc/yum.repos.d/utterramblings.repo
    [utterramblings]
    name=Jason’s Utter Ramblings Repo
    baseurl=http://www.jasonlitka.com/media/EL$releasever/$basearch/
    enabled=0
    gpgcheck=1
    gpgkey=http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka
    priority=0
    

    apache restart

    [root@centos]# service httpd restart
    httpd を停止中:                                            [  OK  ]
    httpd を起動中:                                            [  OK  ]
    

    ブラウザから確認してみると表示されなかった為、apacheのエラーログを確認すると
    メモリサイズがオーバーしているようなので、今回は手っ取り早く割り当てを増やしました。

    [root@centos]# tail -f /etc/httpd/logs/error_log
    PHP Fatal error:  Allowed memory size of 33554432 bytes exhausted.........
    [root@centos]# vi /etc/php.ini
    ...
    memory_limit = 48M
    ...
    

    最後にもう一度apacheをrestartすれば完了です。

    初級者がレアジョブを1年続けてみました

    レアジョブを始めて1年ちょっと経ちました。
    1年間の内、85%くらいはレッスン受講が出来たのではと思います。
    年間85%受講ですと、365日×0.85 = 310.25日 × 25分 = 7756.25分 ≒ 129時間 英会話をしたことになります。

    英語初級者(TOEIC:600程度)ということもあり、
    当初、フィリピン人の先生の言う事が全く理解出来なかったのを覚えています。
    英会話自体を全くしたことがなかったのでしょうがないですが、そもそも文法があやふやであった為、
    Hatena界隈で度々紹介されていた以下の本を6ヶ月かけ、2,3週繰り返して熟読しました。

  • 英語ベーシック教本―ゼロからわかる
  • 英語リーディング教本―基本からわかる
  • 総合英語Forest 6th edition
  • これである程度の文法を理解出来たと思います。
    業務上、英語の技術ドキュメントやメール等を読む機会が度々あるのですが、読む際のスピードや理解力が上がったと実感出来ました。
    ですが、レッスンの中ではあまり効果を感じられませんでした。

    また、文法と並行して、本の付属CDをipodにいれて、通勤時にリスニングをやりました。
    当初は全く聞き取れなかったのが、次第に理解出来るようになってきました。
    但し、自分の知らない単語はやっぱり何回聴いても理解出来ないので、注意が必要です。
    ちなみに、オライリーの本が好きで良く読むのですが、英語を聞き流しながら技術系の本を読むと理解度がかなり落ちます。

    と、こんなことをしている内に、自然とフィリピン先生の言っている事が理解出来るようになってきました。
    そこで相手のレベルに合わせて喋ってくれるベテラン先生から、レベルに関係なく喋ってくる若い先生に切り替えてみました。
    当然、最初は理解出来ないことが多いですが、1, 2ヶ月程すると少しずつ理解出来るようになります。
    また、先生を固定せずに色々な先生を指名してみました。先生毎に喋り方も違ってくるのでかなり聞き取りにくいです。
    現在は1人の先生に絞り、Breaking News Englishを教材にして教えてもらっています。

    この1年間の大きな気づきとして、10ヶ月ほど経過したころからあまり成長を感じなくなった事が挙げられます。
    原因は語彙力(単語力)です。
    ここまで単語に特化した勉強はしていなかったので、使用する言い回しや言葉がある程度限定されていたのではと分析しています。
    やはりある程度の単語を知らないと会話を色々な方面へ、または様々な言い回しが出来ないと実感しました。
    ですので、今後1年間で単語力をつけるように計画を練っていきます。

    以上のことから、初級者がレアジョブのみやっても英語向上の現状の限界点はすぐ見える為、
    文法・リスニング・単語を並行して進めていくのが良いです。
    (当然といえば当然ですが、自分が実践して体感出来たので、、)

    最後に。発音とか文法間違えたら等は途中から気にならなくなるので大丈夫です。
    そのうち矯正しなければならないのでしょうが、気にしていると前に進めなかったので私は無視することにしました。

    特にTOEIC用の準備をしているわけではないですが、5月のTOEICを受けてみようと思います。
    1年間やった成果として多少上がることを願いつつ。


    さくらVPS借りてみました

    2ヶ月程前からさくらVPSのホスティング環境を借りていたのですが、
    しばらく放置していたので、少しイジリ始めました。

    環境構築時は色々なサイトを参考にさせてもらいつつも、ちょっとしたこと事で止まってしまい調べたり、、
    そもそも自分でドメインさえも取得した事がなかったので、何事も経験ですね。
    現状はひと通りのモノは入れ終わり、ある程度は動くようになっています。

    普段、ERP等のパッケージソフトに携わっているとこのあたりがうとくなりがちなので、
    これからこの環境で少しずつ検証していきます。