JAISTで開催されたマルチメディア情報ハイディング・エンリッチメント(EMM)にて、ポスター発表をしました。これまでの研究成果を実際にアプリケーションとして実装し、デモンストレーションを交えながら説明させて頂きました。多くのご意見を頂いたので今後の検討ポイントとしたいと思います。
論文名: センシティブデータの漏洩検知による適応的な公開範囲設定システムのプロトタイプ実装
JAISTで開催されたマルチメディア情報ハイディング・エンリッチメント(EMM)にて、ポスター発表をしました。これまでの研究成果を実際にアプリケーションとして実装し、デモンストレーションを交えながら説明させて頂きました。多くのご意見を頂いたので今後の検討ポイントとしたいと思います。
論文名: センシティブデータの漏洩検知による適応的な公開範囲設定システムのプロトタイプ実装
国立情報学研究所で開催された国際ワークショップ:社会のイノベーションを誘発する情報システムで口頭発表をさせて頂きました。
SNS版のプライバシー侵害情報分類表をベースとしたプライバシー侵害検知アプリケーションを開発し、その初お披露目となり緊張しましたが、たくさんのフィードバックをいただき、今後の展開に役立てられそうです。
発表名:Setting of Access Control by Detecting Privacy Leaks in SNS
京都で開催された国際会議 SIGNAL IMAGE TECHNOLOGY & INTERNET BASED SYSTEMS 2013で口頭発表をしました。
英語による口頭発表は初めての経験でしたが、レアジョブを約4年間続けてきた甲斐があり、問題なくこなせたと思います。
今後は、論文を正しい英語で速く書けるように、ライティングレベルの向上させていきます。
論文名:Settings of Access Control by Detecting Privacy Leaks in SNS
コンピュータセキュリティシンポジウム2013(CSS2013)にて、優秀論文賞とコンセプト論文賞の2賞を頂きました。
論文名:SNS上のプライバシーセンシティブ情報の漏洩検知に基づく公開範囲の設定方式
今後もSNSにおけるプライバシー侵害にフォーカスし、研究を深めていきたいと思います。
熊本で開催されたマルチメディア情報ハイディング・エンリッチメント(EMM)にて、口頭発表をしました
論文名: デジタル私文書におけるプライバシーセンシティブ情報の漏洩検知に基づく公開範囲の設定方式の提案
Couchbaseへの理解と英語強化の為に、Couchbase Server 2.0のドキュメント翻訳に参加しました。
途中仕事が忙しくなったこともあり、当初自分が予定していたほど翻訳活動に参加できませんでしたが、他メンバの方々のおかげで無事公開されました。
翻訳チームに参加したのは初めてでしたが、自社ソフトウェアのプルーフリード作業にも活かしていきたいと思います。
先週末で大学院修士課程を修了しました。科目聴講生時代を含めると2年半通ったことになります。
会社勤めをしながらの大学院であった為、途中半年間の出張もあったりと非常に苦しい時もありましたが、無事乗り切ることができました。何よりもこのような状況を支えてくれた妻に感謝します。
2年間の修士課程から何を得たのかを考えてみると、技術的なことは当然ながら、研究の進め方や考え方・プレゼンテーションといったソフトスキルまで多くのことを得たと実感しています。
自分のスキルを縦軸と横軸で考えると、2年前は縦横どちらも経験相応に伸びていたと思います。但し、複数に伸びている縦軸同士が上手くリンクしていなかったり、横軸の伸びが甘いことから曖昧な知識のもとで何となく進めていた部分がありました。このような状況を打破する為に、もう一度学び直そうと考えました。
結果的に、当初の目的はクリアでき、業務を進めていく中でも自分の様々なスキルが向上していることが日々実感できています。
また、研究として「SNSにおけるプライバシー侵害」をテーマとして、現代社会のプライバシー侵害への考え方、機械による侵害要因分析と検知手法を研究しました。入学前は自分がプライバシー侵害の研究をするとは考えても見なかったですが、今後更なる技術革新が進んでいくなかで本研究テーマは非常に良いテーマであったと思います。当然、1年間の研究からは大きな成果が出せておらず、改善点も多くありますので博士後期課程で同テーマの研究を継続することにしました。
博士課程ではこれまで以上に困難があると推測しますが、3年後に学位が取れるよう研究に邁進していきます。
最後に2年前と同様に博士課程へ進むことに対して、快諾してくれた妻に感謝します。
CouchConf Tokyoに行ってきました。
仕事の都合で午前の部のみとなりましたが、Couchbaseが良く出来ていることを実感したカンファレンスでした。
自分の会社製品とよく似たコンセプトである為、今後の製品としての伸びや自分のスキルの振り方判断になればと思い、少し触っていました。Couchbaseユーザグループでver2.0のマニュアル翻訳に参加したので、これからもっと機能を確認していこうと思います。
それにしても、コンソールの機能・デザインが秀逸です。馬鹿高い自社製品も見習って欲しいところです。
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"
CEATEC JAPAN 2012の会場内で開催されたマルチメディア情報ハイディング・エンリッチメント(EMM)にて、口頭発表をしました
論文名: SNS写真投稿に起因するプライバシー侵害の類型化とその保護策
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からFQLを使用して友人の投稿写真を取得するPYthonスクリプトを作りました。取得した写真の取り扱いには注意してください。
https://github.com/shmachid/facebook_mining/blob/master/getPic_from_fb.py
スクリプト実行手順は以下となります。
http://miningthesocialweb.appspot.com/
全ての権限を与える必要はないが、スクリプトが動かなくなる可能性もあるので注意してください。
求められた権限はそのまま許可し、不要になったら、Facebookアプリを削除すれば良いです。
http://miningthesocialweb.appspot.com/query
自分のコネクション数にもよるが、画像1万枚で4時間ほどかかります。
一枚ずつ見るのが面倒な場合は、 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による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: 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 & Java developer challenge2011でRICOH賞をいただきました。
アプリケーション機能は非常にシンプルでしたが、顔認識技術とビジネスモデルとの組み合わせ、アプリケーションとしての完成度が評価されたのではと思います。また、水着の三愛がリコーの傘下といこともあり、画像解析 × ファッションレポート のアプリケーションが受け入れられたのもあるようです。
グランプリの北陸先端技術大学院さんも顔画像技術を使用されており、エンタープライズアプリケーションにおいてもこれらの技術は注目されているのだと感じました。
また、他チームのアイデアや実装は素晴らしく、若い方々の発想力に驚きました。もう少しブラッシュアップすればRICOHからアプリケーションとして出ても良いのではと思われるアプリケーションもあり良い刺激をもらえました。
約8ヶ月ほど、チームで活動をしてきましたが、最後に良い結果が出て良かったです。チームの皆さんありがとうございました。
大学院の有志メンバと参加しているRICOH & Java Developer Challenge 2011の最終選考に提出できました。
9月末に1次選考(Emulator編)を無事通過し、最終選考へ向けて実機上でMFPアプリケーションを開発してきました。
5月にチームを発足してから半年以上と長期に渡って活動してきましたが、ようやくシンプルながらも面白いモノが完成しました。
このプログラミングコンテストからは技術的な部分のみでなく、チーム活動の進め方という点に関しても多くの得る事あった為、参加して良かったと思います。
最終選考は2012/1/12(木)です。あと、Ustreamでも放送があるそうです。
自身の開発プロセス改善を目的として7回に渡りシナリオ仕様毎に、
計画立案 → 設計 → 設計レビュー → コーディング → コードレビュー → コンパイル → テスト → 事後分析
を繰り返し行いましたので、最後に結果を分析しました。
・ プログラムごとの見積規模と実績規模を比較し、見積の正確性を検証する。
【プログラム毎の見積規模と実績規模】
Program No. | 見積規模(LOC) | 実績規模/KLOC |
1 | – | 60 |
2 | 150 | 79 |
3 | 164 | 124 |
4 | 124 | 104 |
5 | 144 | 96 |
6 | 137 | 152 |
7 | 248 | 189 |
全体的に見積規模と実績規模はある程度の近似値で見積が出来ていた。
通常最も避ける必要がある点は、見積規模に対して実績規模が大きく上まるケース(受注金額に対して、予定工数が上まってしまう為)であるが、そのような例は見受けられなかった。
仕様が複雑化していった後半のプログラムにおいても、大きな差異は無かった理由としては設計フェーズで仕様を完全に把握出来たことが功を奏したと推測される。
・ プログラムごとの見積時間と実績時間を比較し、見積の正確性を検証する。
【プログラム毎の見積時間と実績時間】
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 | 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程度がテスト時間であると定義出来そうである。
・ 設計、コーディングで作り込まれた欠陥を型ごとに分類し、グラフにより型による欠陥数の傾向を図示する。
※ 型番号に関しては、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件であったが、自分の欠陥傾向を掴む為には統合開発環境を使用せずに計測したほうが良かったかもしれない。
・各プロセスで発見した欠陥数/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は前回のプログラムを再利用・追加していく仕様である為、前プログラムで欠陥を押さえることが出来ていれば、必然的に当該プログラムでも欠陥が減少することが確認できた。これにより、プログラムやクラスを再利用する時は、当然ではあるが、欠陥が無い状態で利用可能とすべきことを再認識できた。
・各プロセスで除去した欠陥数/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:機能 の欠陥が大半を占めたようにコードレビューでより欠陥を除去出来た可能性がある。よって、今後はレビューにより時間を割くように意識していく。
・設計レビュー、コードレビュー時の時間当たりのレビューLOC数に対する欠陥除去率を一覧化し、傾向を分析する。
時間 | LOC数 | 欠陥除去数 | 欠陥除去率 | |
設計レビュー | 45 | 437 | 0 | 0.0% |
コードレビュー | 55 | 0 | 0.0% |
コードレビュー、設計レビューにおいて、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以降は設計フェーズで仕様を正確に捉えるようにシフトすることができた。また、設計レビューやコーディングレビューで事前に欠陥を発見・修正することで、テストフェーズにおける欠陥数、大きな欠陥による修正時間の増加を押さえることが出来ると考えた。
【最も優先すべき点】
統一した計測方法で作業ログを残し、改善点を見つける為の振り返りを行う。
【理由】
現在の個人データは不十分 且つ、データ量が少ない為、正確な見積値として使用する事が出来ない。これを改善する為に、日々の業務や個人開発おいて、統一した計測方法で作業ログを残し、後日、改善点や傾向を把握する為の振り返りを行い、パーソナルプロセス改善を行う。
【現在の作業能力】
規模見積に関して、今までは仕様に対する規模推測を大きく見積過ぎていた。演習を行うことで似たような仕様(過去に経験した仕様)であれば、ある程度正確な規模が見積もれるようになっている。過去の実績時間を使用することで、全体を見通した時間が見積もれるようになったが、まだ若干のブレがある。
また、計画や設計フェーズの重要性を自分のデータを通して、実感出来ている。例えば、仕様は確実に理解することで、コーディング時間や欠陥数、欠陥に対する対応時間の短縮等の効果が大きい事を理解できている。
【将来あるべき作業能力】
まず最初に持たなければならない能力としては、時間見積の正確性である。規模見積が行えたとしても、時間見積が全く異なっていれば、見積が出来ているとは言えないからである。その為に必要なアクションとしては、今後の作業ログを確実に残していき、自分の見積時の基データを収集することである。次に必要な能力としては、レビュー能力と考えた。演習では欠陥検出は出来なかったが、欠陥傾向としてコーディングレビューが有効と判断した為、本能力を向上させることにより欠陥をある程度減少させることが可能である。
最終的なゴールとしては、自分が作業を行う為の見積ではなく、一般的な技術者が作業を行う場合の適性値を見積ることである。この為には、自分の能力と一般的な技術者の能力差を把握する必要がある。
PSP演習を実施するにつれて、設計の重要性が再認識出来たのは非常に良い結果であった。また、いつも見積を行う際にドンブリ勘定を行なっていたが、より精度の高い見積を行うことの重要性と手法を学べた事が良かった。今後は自分の基データを収集すると共に継続的にプロセス改善を行なっていき、機会があればチームに対してTSPを適用していきたい。これにより、トラブルプロジェクト(欠陥や期間、金額など)を減少させる1要素になると感じた。
ということで、PSPの演習自体の仕様としては業務にあまり活かせない可能性はありますが、
個々のプロセスを振り返る事で自身の開発プロセスを改善する第1歩となるという点では間違いありません。
以上です。
よく複数のプログラミングパラダイムを理解することは非常に良いことだと言われますが、
今回は関数型言語である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).
● 実行方法・結果
OS: CentOS5.6
Ver: Erlang R12B-5.12
Emu: Erlang emulator Ver5.6.5
$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>
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
前回に続いて、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 とは、ソフトウェア開発効率の向上の為に、個人毎のソフトウェア開発プロセスを見直しする枠組みです。
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 wifi versionを買ってきました。
結果的に価格は日本から通販で買うのとほぼ変わらなかったようですが、
これから購入する方の参考になるかもしれません。
台北にはいくつか電気街があり、今回廻ったのは忠孝新生駅の北側にある八徳路電気街となります。
その中でもビル一棟に電気器具や部品、パソコン、周辺機器などを取り扱う店がひしめき合う光華商場に何度か足を運んできました。
台湾におけるFlyerの価格ですが、上記の光華商場内のお店では大体16500NTD ≒ 43395円でした。(但し、中古です)
ここから免税申請を行うと5%が返ってきます。ですので、約41225円程度となりますが、免税申請に関しては自分から切り出して聞かないとスルーされる可能性があります。
また、どのお店も価格交渉があまり効かず、下げてくれても500NTDでした。
#1NTD ≒ 2.63円 (2011/9/17時点)
結局、外の正規HTCショップで購入したのですが、どこのお店も17900NTD ≒ 47077円の設定で、免税申請して44723円といったところです。
あと、現金かカード利用により本体の値段が変わりますので、何かを購入予定の方は予め現金を持って行くと良いです。(現金の方が当然安い)
なお、420グラムと言えども電車内で持ったままの状態では若干重く感じましたが、
iPad程重くもなく、視線も集めにくいので良い機種だと思います。
プログラミングコンテストにチームで参加してみて、技術面以外にも勉強になった事が多々ありました。
3/11の地震以降、在宅勤務を導入しようと模索する企業が増えていますが、今回のコンテストから在宅勤務を成功させる為にはルールが必要だと感じたので、在宅勤務を成功させる為の必要条件を考察しました。
なお、私達のチームでは基本的に各自の家で空いている時間を使ってタスクを進め、
コミュニケーションはSkype上でテキスト形式(chat)で行いました。
ある程度チーム単位で動く在宅勤務を成功させる為の最低限必要なルールは以下と纏めました。
在宅勤務の場合、個々が時間を上手く利用出来るようになる一方、
通常であればすぐにコミュニケーションを取って確認出来ていたような事項が
すぐに確認出来ないといった弊害が生まれます。
よって、在宅勤務と言えども、活動時間帯は同じにするのが良いと感じました。
ここではテキストベースでコミュニケーションを取るのを前提としていますが、
相手の顔が見えない分、コミュニケーションロスが頻発します。
例えば、仕様が全メンバへ伝わりきれていなかったり、ある事項に対してのOK/NG、了承/拒否の返事が無かったり等です。
コミュニケーションポータルサイトを利用し、全情報はこのサイトを参照するのも良いと思いますが、
個々が積極的にコミュニケーションを取っていこうとする姿勢がまず重要です。
タスクを分けて作業している場合、必ずグレーゾーンにあるタスクが発生します。
これをどちらが取るかは不毛の戦いであり、対面式のプロジェクトでもこれらのタスクは浮いたままになることが多いと思います。
このようなグレーゾーンタスクをどちらともなく積極的に解消していく事、またそのような気持ちを持つ事が必要です。
個々がタスクを進める上で、足りない知識(情報)や技術が必ず出てきます。
そのような時はまず自分で調べようとする気持ちが大事です。結果的に分からなくても良いと思います。
その時に改めて他メンバに聞けばよいです。
最も大事なのが、全メンバが同じゴールを共有することです。
対面式のプロジェクトでも、同じベクトルを向く事が重要ですが、在宅形式のプロジェクトでも同様です。
ゴールが共有出来ていれば、これまで挙げてきた事項を自主的に進めてくれる可能性があります。
対面式プロジェクトであったり、同じ在宅形式のオープンソースソフトウェアプロジェクトであれば、
これらの事項は発生しない、もしくは解消しやすい事だと思います。
(OSSの場合は、技術スキルや向上心が高い・ゴールが共有出来ている等から)
在宅において、誰かが手を抜くということは誰かがフォローするということであり、
誰かに負荷がかかるということでもあります。
今後、在宅勤務を導入していくなかで、様々なルールが定義されていくと思われますが、
最低限上記のルールを個々が認識することで良いスタートがきれると言えます。
大学院の有志メンバと参加しているRICOH & Java Developer Challenge 2011でアプリケーション、ドキュメントを無事提出出来ました。
企画段階から入れると約4ヶ月掛けて、進めてきました。大学院や仕事をしながらの作業であった為、
非常に疲れましたが、チームとしてはシンプルながらも当初の案はアプリケーションに落とし込めたと思います。
また、個人としては、Javaに慣れ親しむという目標が達成出来たと思います。
1次予選の結果は今月末です。本選に進めるか分かりませんが、参加して良かったなと感じています。
PyConJP 2011に行ってきました。
会場が現在通っている産業技術大学院であった為、会場学生枠で参加させて頂きました。
200人以上の方が参加していたようです。
以下のセッションに参加しました。
今までPythonは「Perl, Python, PHPのベンチマークを計測しました」をする際に初歩的な部分しか扱った事がありませんでした。
本セッションに参加するにあたり、Pythonチュートリアル 第2版は一通り目を通してから参加したのですが、テンポよく教えて頂き非常に良かったです。
個人的にDBが絡んだ話がやはり楽しいなと感じました。
PyQtを使用してデスクトップアプリケーションを作ってみましょうというお話でした。
自社のDeveloper Network BlogでPyQtを使用して、基幹システムの情報を一覧表示するアプリケーションが
紹介されていたので、サンプルを作ってみようと思います。
最近、スマートフォンやタブレットが浸透し、業務で使用する端末を会社が用意するのではなく、
個人端末を使用するケースが増えています。
その際にポイントとなるのが、セキュリティであり、本セッションでは自社システムに接続可能な端末を
サーバ側から効率的に管理するということでした。同様の製品が多く出ているところから、
MDM(Mobile Device Management)分野は伸びているのだなと感じました。
なお、Androidに対するPush通知は時々こけるそうで、その点iPhoneの方が機器管理をする上では
機能的には進んでいるそうです。
入門 自然言語処理
の12章:Pythonによる日本語自然言語処理に沿った解説でした。
10月頭に少しまとまった時間が取れそうなので、何かアプリケーションを作ってみようと思います。
家のマシンから自分が作った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インストールに引き続き、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パソコンベストムック)
を参考にさせていただきました。
以上です。
ドキュメント指向型の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がイマイチ腹落ちしていなかったので、調べました。
1. ACID
まず、RDBMSのトランザクション処理におけるACID特性は以下である。
トランザクションが実行されるか、全くされないかのどちらかである。
データベースの関係制約を保証する。
トランザクション中に経過が他トランザクションから参照出来ない。隔離性があるということ。
トランザクション完了後、データは失われない。耐障害性があるということ。
2. CAP定理
続いて、クラウド化(クラウドは分散システムと考える)した際はACID特性が保つのは非常に大変である。(無理ではない)
CAP定理では、C.A.Pの3つの内、同時に満たせるのは2つまでという主張。
Eric Brewer氏のCAP定理
データ更新後の次アクセス時には、更新後データが読取可能である事。
ユーザがアクセスした時はサービス利用可能である事。稼働率。
分散システムを構成する物理/仮想サーバのうち、1つが壊れてもサービスは継続可能である。
3つの内、2つを選択すると以下となる。
クラウド環境ではデータ量やScalabilityの観点から分散処理が必要となる。
よって、P: Partition Torerance を選択することとなる。
では、残り1枠は C(一貫性), P(可用性)のどちらを選択するかであるが、
Webではユーザがアクセスした時はサービスが利用可能であることが前提である為、
基本的には可用性を選択する事になる。(個々の要件によって、選択する事)
その場合、一貫性の取り扱いが問題となる。そこで出てくるのばBASEである。
3. BASE
ACID特性が個別のデータベースのトランザクションの特性であるのに対して、
BASEはデータベース機能を含んだシステム全体の特性である。
可用性が高く、常に利用可能である。
ステータスは厳密ではない。外部から送られる情報により変化する。
最終的には一貫性が保たれる。ある時点では更新されていないケースがある。
4. まとめ
クラウドではCAP定理のうち、A(可用性)とP(ネットワーク分断耐性)を選択する。
C(一貫性)については、若干妥協して BASEのEventually Consistent を採用する。
ある時点では、一貫性がない可能性があるが、最終的には一貫性が保つことが出来る。
気になった点としては、企業システムにおいてはこの一貫性の妥協がどのように許容されるのかです。
ここはもう少し調べることとします。
なお、以下を参考に勉強させていただきました。
Cloudの技術的特徴
CAPのCとACIDのC
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 ―ギークであり続けるためのキャリア戦略の中で、『デモ』についての章があったのでメモ書きします。
私はかなり緊張しやすく、伝えたい事が半分程度にしか伝わらない事が多い為、備忘の為に残します。
デモは参加者により参加してもらうもの。
だから自信を持てば良い。
デモをする場合、相手が誰であろうと目的はみんな一緒。
みんな目的は情報を得る事である。デモをする際は下記の基本ルールを守る。
デモを理解する上で知っておくべき事を伝える
発見、アイデアの具体的内容を詳しく説明する。
発見、アイデアのどこに最も価値があるかをようやくする。
そして、聞き手に意見を求める。
この手のタイプが最も厄介である。
打破するには、背景情報や発見に至るまでの物語を長く話すと良い。
毎回このタイプを想定しておけば、怖いものはない。
一見、厄介に感じるが、最もありがたい存在。この手のタイプから建設的な意見が出だすと
デモは成功していると言える。だが、積極的な人がよく理解出来ていない状況の場合は
かなりまずい状況と言える。まずは深呼吸をして立て直そう。
デモ対象について最も知っているのは自分。
仕切り屋は途中(もしくは最初から)で自分のペースに持って行こうとする。
デモを行う人ではないのに。そんな時は”完全な沈黙”、”デモのテンポを変更する”等を行い、
自分のペースに引き戻す。また、仕切り屋によって、あらぬ方向へデモが進んでしまう事により
新たな発見があるかもしれない。(自分が見えていなかった事など)
そんな事は絶対に不可能。理想のデモをどのようにやれば良いとは言えないが、
結局試行錯誤を重ねていくしかない。
結局、プレゼンテーションの本(ZENなど)にも同じ事が書いてありますが、
本番前の練習がモノを言うということでしょうか。
WebSocketを使用したmashupを作るにあたり、以前から知られているCometについては、
サーバからpushする技術ぐらいにしか理解していなかった為、少し調べてみました。
従来のWebページはクライアントからリクエストがあった時のみ、サーバからレスポンスが返ってくる。
その際、クライアントからはHTTPコネクションを生成し、サーバからレスポンスが返るとコネクションはクローズする。
上記の手法でも通常は問題は無いが、リアルタイム性を求められるアプリケーション等の場合は不都合である。
(例えば、チャットや株価情報など)
その場合、AJAX等で非同期にサーバへHTTP通信をする手法が考えられるが、
サーバ/クライアントに負荷が掛かったり、他ユーザからの更新情報をリアルタイムに受け取る事は難しい。
こうした経緯からCometと呼ばれる概念(Cometは複数の技術の総称)が生まれた。
なお、Comet, AJAXの名称は共に洗剤から由来している。
サイトあるいはアプリケーションから、更新有無を問い合わせるリクエストを定期的に送信するという非常に単純な仕組み。
デメリットとして等間隔(例えば、5秒など)でリクエストを送信するが、サーバ側で高負荷になっていた場合も常にリクエストを出し続けてしまう事が起こり得る。この場合、サーバ側に未処理リクエストが溜まり続けてしまう。
クライアントからリクエストをサーバに送信し、すぐに返却可能なデータがある場合はすぐにレスポンスを返すが、まだ返却可能なレスポンスデータが存在しない場合は、レスポンスを返さずに未応答リクエストとしてサーバ側で保持する。
データの用意が出来た段階で、保持していた接続を見つけてレスポンスを返す。
Meeboで使用されて、有名となった。
ロングポーリングの発展系として、スマートポーリングと呼ばれる手法がある。
スマートポーリングはレスポンスデータが返って来なかった場合にリクエスト頻度を下げるポーリング手法である。
例えば、空レスポンスであったら、次回のリクエスト間隔は1.5倍にするなど。
ロングポーリングは永久フレームから生まれた手法である。
永久フレームとは、非表示のインラインフレーム(iframe)を開き、
チャンク形式エンコーディングを利用して送信する。
この方式は非常に大きなドキュメントを段階的にロードする為に考案された。
但し、IEでは永久フレームを使用した場合、チャンクを受け取る度に「カチッ」という効果音がなってしまう為、
使用に耐えないものであった。
そこで登場したのが、GoogleがGoogle Talk考案したhtmlfileというActiveXオブジェクトを用いる手法である。
この手法により、IE問題はクリアされ永久フレームは有効な手法として広まった。
サーバと通信をする為の最も良い方法はXMLHttpRequestを使用する事である。
通常、ポーリングやロングポーリングのデータ転送で使用されている。
XHRストリーミングでは、XHR通信を行った後にreadystateをチェックし続け、
接続が切れる前に同一コネクションを使用して会話をする。
現状、最善の手法であるが、いくつかデメリットが存在する。
通常Webサーバはリクエストを受けるとすぐにレスポンスを返すように最適化されているが、
ストリーミングでは接続を保持し続ける必要がある為、注意が必要である。
また、クライアント側においてははXHRストリーミングがパフォーマンス上の問題を引き起こすケースがある。
ストリーミングのレスポンスがあまりに長く続くと、ブラウザのメモリ消費が過大となり、クラッシュしてしまう。
解決方法としては、一定数のレスポンス後に一旦レスポンスを終了し、新たにリクエストを生成すれば良い。
なお、IEではXHRストリーミングを正式サポートしていないので注意が必要である。
前述までのCometを使用したとしても、大量コネクション保持やサポート問題等の為、完全なソリューションではない。
そこで、HTML5からforkしたWebSocketに期待が集まっている。
現在策定中のWebSocketはCometのデメリットを補いつつ、リアルタイムアプリケーションを容易に構築出来る可能性を秘めている。
調査にあたり、続・ハイパフォーマンスWebサイト ―ウェブ高速化のベストプラクティスを参照させてもらいました。
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は次バージョンからサポートされるようです。
現在対応出来ていない仕様やバグは以下です。
いづれも軽微な対応で済みそうですので、暇をみて対応したいと思います。
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 -> 東京都品川区東大井
以上です。
=====
1. 環境 及び シナリオ
実行環境は以下である。
Platform: Linux(CentOS 5.6) @sakura VPS
Memory: 512 MB
CPU: 仮想2Core
実行・考察を行うプログラミング言語はLAMP構成として通常扱われるスクリプト言語の以下を使用する。
・Perl
・Python2.4
・PHP5.2
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文の計測が行いづらい為。
本レポートで実行対象となるプログラミング言語はそれぞれ実績のある言語であり、Webサービス開発においてよく使用されている。私はPerl, PHPの経験はあるがPythonは今回が初めての使用となる。
Perlは全体的に処理が速く、ファイル操作に長けているイメージがある為、全般的にPerlのパフォーマンスが良いと考えている。
PHPは全体的に処理が遅いイメージがある。特にrandom()やファイル操作が遅いイメージがある。
Pythonを使用するのは今回が初めてである為、あまりイメージが出来ないが、処理速度的にはPerlと同様の計測値となると予測する。
よって、 ほぼ全シナリオでPerl < Python < PHP の順番で処理コストが高くなると考える。
以下の要領で実施を行う。
言語毎の実行時間計測は以下のように行う。
以下では、各言語のシナリオ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. 実行
以下が実行結果である。シナリオ毎に最も良いタイムに橙色にしている。また、特徴的な値となった結果は赤色とし、次項以降で考察を行う。なお、単位は全て”秒”である。
演算 | 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ならではの優位性が示されているシナリオもある為、現状のまま考察を進める事とする。
Pythonのrandint()が非常に遅い結果となった。Perlと比較すると約27倍の差である。調査したところ、Perl, PHPのrand()は組み込み関数であるのに対し、Pythonのrandint()は標準モジュールでの提供であった。
また、randint()よりもchoice()を使用した方が速いとの情報があった為、変更し実行計測したところ、
27.65 → 12.36 と半分以下の実行結果となった。但し、Perl, PHPと比較するとまだ遅い結果であった。
この事からC等で記述される組み込み関数はやはり速いということが分かった。
Perlのswitch文が異常な程、遅い結果となった。これはPerlには組み込みでswitch文がない為、標準モジュールのSwitchを使用した事が影響していると考えられる。最速であったPHPと比較するとその差は100倍である。
3.1のrand()と同様に以下に組み込み機能が速いかを再認識させられた。
実行前の考察よりPHPはファイル操作が不得意であると考えていたが、その通りの結果であった。特にfopen(), fclose(), fwrite()のパフォーマンスが悪かった。
また、ファイル操作はPerlが非常に有利だと考えていたが、Pythonが健闘している。
シナリオ毎の実行結果の色付けを確認すると、PHP列に橙色が多く付いている。PHPは全般的に処理が遅いと考えていたが、結果から見ると異なっていた。以前の経験ではPHPのrandom()は遅いと感じていたが、やはりPerlと比較すると倍以上の差が出ていた。(Pythonとの比較に関しては、3.1を参照の事)
それぞれのプログラミング言語には高速化技術が存在する。例えば、Perl: mod_perl, Python: mod_wsgi, PHP: APC, eAcceralator等である。これらの技術を使用すれば、より良いパフォーマンスが出やすいが、その前段階として、言語毎の特徴を押さえた上でプログラミング言語の選択や処理ロジック, アルゴリズムを組んでいく必要がある。
=====
おわりです。
WindowsのWMIサービスからクエリ実行が出来ます。
例えば、稼働中のデバイスドライバ一覧を出力する場合、コマンドプロンプトより下記を実行します。
$ wmic sysdriver where “(state = “Running”)” get name
上記の結果は、” C:WindowsSystem32Msinfo32.exe”を実行した際と同じ結果となります。
State=Runningのドライバを確認するとwmicのクエリ実行結果と同一となることが確認出来ます。
imageをアップロードすると、フルサイズしか選べないのは何故でしょう、、、
RICOH & Java Developer Challenge 2011の講習会に行ってきました。
地方から参加されている学校もおり、みんな真剣に講習を受けていました。
仕事、大学院、家族と忙しい毎日ですが、みんなで協力しあって1次審査を突破したいものです。
締め切りまであと2ヶ月です。
最近、初めてpostgreSQLをさわったので、アーキテクチャなど少し調べました。
postgreSQLとその他のDBMSとの大きな違いは追記型である点です。
updateを行う際に、オリジナル行に対して更新をするのではなく、
新規行をinsertとオリジナル行に無効化フラグを付加します。
よって、データベース内には複数versionのデータを保持することになります。
この機能をMVCC(Multi Version Concurrency Control)と呼ばれています。
更新トランザクションが発生する毎にレコードのversionが増える為、大量トランザクション発生するシステムの場合、
データ量が膨大になります。
そこで、vacuum機能で、無効化フラグのついたレコードをお掃除します。
vacuumには以下の2種類があります。
無効化フラグが付加されたレコードを再利用可能とする。排他ロックは行われない。
従来からある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の変化には、エバンジェリストが必要
2→3の変化には、市場創造者が必要
3→4の変化には、マネジメントが必要
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. 不安定期 (市場創造者)
市場創造者はいくつかありそうである。以下にまとめた。
MySQLを使用しているたくさんのユーザを狙った買収を行い、
MySQLユーザを自社のサーバへ誘導しようとした。
Oracleに勝つ為に、自前のDBを開発しようとしていた。
また、開発を取りやめ、Sybaseを買収し、最近の流行りの垂直統合化で自社のエコシステムを構築しようとしている。クラウドに関しては、流れに乗り遅れているように感じる。
SUNの買収、MySQLへ対抗する為にInnoBaseの買収と、無駄がないように感じる。
周囲の競合ベンダを威嚇しながら、買収を繰り返し、垂直統合システムを完成させていっている。だが、これはベンダーロックインとなる為、懸念される可能性もある。(中立的な立場であるPublicSectorがMicrosoftを嫌がり、OpenOfficeへ入れ替えていっているように)
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つにまとめめられる。
読み取り負荷の軽減には非常に役立つ。
但し、アプリケーションは複雑になり、軽減可能な負荷に限界がある。
クエリの最適化も同様に限界がある
非常に効果的。問題は2つ。値段が高い。最も良いハードウェアを購入するとその後の手立てがなくなる。
最後はシャーディング。シャーディングに適しているのは、ユーザID等から取得先のデータベースを
振り分けるような場合(西海岸と東海岸とか)。だけど、ちょっと面倒。
どうすれば簡単にスケール出来るのか? どうすればデータを近くに保存出来るのか? どうすればアプリケーションを再構築しなくて済むか? といった悩みから生まれたのがNoSQLである。
NoSQLデータベースの概要
NoSQLは以下の5つに分類出来る
キーバリュー型は昔からある。memcachedで再注目された。
非常にシンプルでキーを渡せばバリューが返る。
キーバリュー型とほとんど同じ。バリューをリスト・セット・ハッシュ等のデータ構造で格納する。
リストだとpush, popが出来たりとアプリケーションのデータ構造と同様に操作出来る。
Redis
データ構造型のデータ構造がグラフになっただけ。データはキーバリューではなく、
ノードとエッジで格納される。
キーバリュー型に似ているが、バリューがドキュメントとなる。
ドキュメントはJSONオブジェクトで格納されている。
このタイプは、データ構造が事前に分からない場合に有用である。
CouchDB
MongoDB
複数の形式があるが、共通しているのは複数ノードへのデプロイに最適化されている事。
分散システムでキャパシティを増やすには、新しいノードをクラスタに追加すれば良い。
1つのノードに障害があっても データが失われることなく、
キャパシティが損なわれるだけである。一貫性を犠牲にして、可用性と分割耐性を保証するものが多い。
なぜ、高分散型を使用するか?他に選択肢がないから。
SQLベースのDBでは、大量のデータを扱えないが、高分散型では可能。
但し、面倒。
Cassandra
HBase
Riak
例で挙げた各データベースについては、後日調べる事とする。
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のリスニングは非常に聞き取りづらかったです。
また、文法やらボキャブラリのセクションももう少し勉強が必要だと認識しました。
ですので、スコア的にはあまり上がっていないかもしれません。
ひとまず、現状把握出来たので、次回までに修正していきたいと思います。(次回は11月予定です)
入門 HTML5を読み進めています。今まで漠然と理解していた事が、詳細に説明されています。
まだ読み途中ですが、メモ書き程度に残します。
HTML5のサポート
HTML5はひとつの大きなものではなく、個別の機能が集まったものである為、「HTML5のサポート」という言葉は意味をなさない。例えば、Canvasやビデオといった新機能がサポートされているかどうかは、機能毎に異なるということ。
新機能の内、注目する機能は以下である。
解像度に依存するビットマップ描画機能で、グラフ・ゲームのグラフィックスやビジュアル画像を表示する
QuickTimeやFlashは不要となる。HTML5ビデオでは、全てのブラウザでサポートされているコーデックは存在しない。最終的には2種類に絞り込める。MAC系のh264, その他のOggである。
GoogleによるChrome次期バージョンからのH264の非サポートは、コーデックを巡る主導権争いの1つである。
従来のクッキーは容量制限があるが、HTML5ストレージはより大きな情報量を扱えるようになる。ローカルストレージのセキュリティだが、コンピュータに物理的にアクセス出来てしまった場合は読み取られる。しかし、ブラウザの中では、自身が保存した値を読んだり変更は可能だが、別サイトが保存した値へのアクセスは出来ない。同一生成元制限(SOP)という。
ブラウザがJavaScriptをバックグラウンドで動作させるための標準的な方法を提供する。同時に動作する複数のスレッドを生成可能となる。
文字通り、オフラインで動くアプリケーションで、オンライン状態に戻った際は、オフライン時に行われた処理(例えば、変更など)をサーバ側へアップする。
該当者(例えば、自分)が、世界のどこにいるのかを検出する機能で、その情報を他人と共有出来る。
従来のフォームのinputタイプに定義が多く追加された。
入力フィールドのプレースホルダを設定出来る。プレースホルダとは、フィールドにヘルプ文のように表示される文字の事。
Webページに意味付けを追加する為の標準的な方法であるが、独自フォーマット定義も可能。
個々の機能については、また読み進めてから検証していくこととします。
4月にMySQL Conference & Expoが開催されたので、その時のスライドをチェックしてみてると、
4年前にYahoo! incでパフォーマンスチューニングで相談させてもらったJayさんを発見した。
Websiteもあった。
あの相談をさせてもらった頃から、自分自身が少しは成長していると良いのですが。。
しかし、スライドがkeynoteで見れない・・・。学校の人に変換してもらおう。
当時、現場で使える MySQL (DB Magazine SELECTION)
がバイブルでした。
Mavenに続いて、Antも必要とのことだった為、セットアップしました。こちらはあっさり完了しました。
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を探しにいきます。(そして、無かった)
<?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が必要になった為、Laptopに設定しました。
セットアップが完了したところで、以下のエラーメッセージがEclipseのコンソールに出ていて苦労したのですが、MAVEN→M2Eclipseの順番で入れ直すことで解消しました。 → が、また出た。。
Unable to update index for central|http://repo1.maven.org/maven2
以下の手順でセットアップします。
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"
設定ファイル”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
と、書いていた矢先に、またエラー[Unable to update index for central]が・・・
もしかして、、作業していたネットワークの設定だけだったかもしれない。。
DHDのAndroid OSを2.2 → 2.3 へUpdateしました。
UIパフォーマンスの改善が体感出来ますので、満足してます。
アンドロイドはOSアップデートで動作が軽くなる?で書かれている方によると、Dalvik(だるびっく)vmのチューニング結果ではないかとのことです。
DalvikでググるとAndroid開発の概要がありました。
少しずつ調べてみようと思います。だが、、時間がない・・・、、
大学院のレポートでオブジェクト指向分析設計をする必要があったので、家のマシンにUMLモデリングツールを
入れようと思い、チェンジビジョンのサイトにいったら、JUDEは提供終了となっていました。
後継製品として、astah*(アスター)という製品が出ていたようだ。
community版があったので、使用してみましたが、違和感なく普通に使えました。
astah*とJUDEの機能比較、データ互換性のまとめが以下にありました。
機能比較
データ互換性
今後はこちらを使用させてもらおうと思います。
ちなみに、チェンジビジョンは少し前に親会社が永和システムマネジメントさんに変わっていたのですね。
チェンジビジョンの親会社の異動について
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のコンソール上で日本語などの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も多いですし、今後のAndroidアプリ開発時にも使えそうですので、ちょこちょこやってみようと思います。
また、新しい言語を覚える事は新たな発見がたくさんあり、他言語で不明瞭な点がくっきりするかもしれません。
久しぶりに使うEclipseは高機能なこともあり使用方法に戸惑っています。
一番面倒なのはデフォルトではエディタがvi風に使用出来なく、特に移動などが面倒でしたので、ViLikeを投入してみました。
まだ少ししか触っていないですが、これは良いです。
当初、viPluginを入れようとしたのですが、有料版になってしまったようです。
Eclipse 3.6 完全攻略を買ったので、まずは色々さわってみよう。
去年の前半に英文法を基礎から再学習したのですが、最近は忘れてきているのでもう一度やっています。
教材はNext Stage英文法・語法問題―入試英語頻出ポイント215の征服
を使っているのですが、単に解くだけではまた忘れるので、回答の日本語訳→英語へ瞬間英作文が出来るように繰り返し唱えています。
ですので、1ページ終わるのにかなりの時間がかかってしまっています。これだとページは進まないので、他に良い方法がないか思案中です、、
あと、はてな界隈でGuruのみなさんから紹介される英語本はほとんど家にあることが分かりました。
記事を読んだ時に買って、積ん読していたようです。結局いつかやるのであれば、今やってみようと思います。
なお、電車でこの本を読んでいたら、前にいた高校生が同じ本を読んでいました。
普通に恥ずかしかったですが、彼の本はかなり使い込まれていました。。
プログラマが知るべき97のことを読み終えたので、備忘の為に1言ずつ残します。
01 分別のある行動
02 関数型プログラミングを学ぶことの重要性
03 ユーザが何をするかを観察する(あなたはユーザではない)
04 コーディング規約を自動化する
05 美はシンプルさに宿る
06 リファクタリングの際に注意すべきこと
07 共有は慎重に
08 ボーイスカウト・ルール
09 他人よりまず自分を疑う
10 ツールの選択は慎重に
11 ドメインの言葉を使ったコード
12 コードは設計である
13 コードレイアウトの重要性
14 コードレビュー
15 コードの論理的検証
16 コメントについてのコメント
17 コードに書けないことのみをコメントにする
18 学び続ける姿勢
19 誰にとっての「利便性」か
20 すばやくデプロイ、こまめにデプロイ
21 技術的例外とビジネス例外を明確に区別する
22 1万時間の訓練
23 ドメイン特化言語
24 変更を恐れない
25 見られて恥ずかしいデータは使わないこと
26 言語だけでなく文化も学ぶ
27 死ぬはずのプログラムを無理に生かしておいてはいけない
28 「魔法」に頼りすぎてはいけない
29 DRY原則
30 そのコードに触れてはならない!
31 状態だけでなく「ふるまい」もカプセル化する
32 浮動小数点数は実数ではない
33 オープンソースプロジェクトで夢を実現する
34 API設計の黄金律
35 超人の神話
36 ハードワークは報われない
37 バグレポートの使い方
38 余分なコードは決して書かない
39 最初が肝心
40 プロセス間通信とアプリケーションの応答時間の関係
41 無駄な警告を排除する
42 コマンドラインツールを使う
43 プログラミング言語は複数習得すべき
44 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 シングルトンパターンの誘惑に負けない
71 パフォーマンスへの道は地雷コードで敷き詰められている
72 シンプルさは捨てることによって得られる
73 単一責任原則
74 「イエス」から始める
75 面倒でも自動化できることは自動化する
76 コード分析ツールを利用する
77 偶然の仕様ではなく本物の仕様のためのテストを書く
78 テストは夜間と週末に
79 テストのないソフトウェア開発はあり得ない
80 1人より2人
81 エラーがエラーを相殺してしまう
82 他者への思いやりを意識したコーディング
83 UNIXツールを友にする
84 正しいアルゴリズムとデータ構造を選ぶ
85 冗長なログは眠りを妨げる
86 WETなシステムはボトルネックが見つかりにくい
87 プログラマとテスターが協力してできること
88 コードは生涯サポートするつもりで書く
89 関数の「サイズ」を小さくする
90 コードを見る人のためにテストを書く
91 良いプログラマになるには
92 顧客の言葉はそのまま受け取らない
93 エラーを無視するな
94 リンカは魔法のプログラムではない
95 ペアプログラミングと「フロー」
96 テストは正確に、具体的に
97 ステートに注目する
以前読んだソフトウェアアーキテクトが知るべき97のことについても、そのうちまとめます。既に内容は忘れました・・
今日は大学院の入学式に行ってきました。
昨年から科目履修生として通っていましたが、今日から1年生です。
私はオライリー本の目次を見るとワクワクする事が多いのですが、この大学院の科目群を初めて見た時、同様の感覚を感じました。
単純ですが、これが複数候補からこの大学院から選んだ理由です。きっと、自分が今欲しい知識・情報・技術と合致していたのでしょう。
何でもそうですが、今したいと思った時が最善の時です。読みたいと思った本は今読むのが最も効果があるのです。
私は行動をして上手くいかなかったら次に行くようにしています。
この2年間は以下をより意識しながら過ごしてみます。
■既に得ている知識を更に深く・体系的に学ぶ
■不足している知識・技術を幅広く補完する
■全ての情報を得る時間は無い為、取捨選択を大事にする
快く了承してくれた妻に感謝しつつ、安くはないお金と大切な時間を大事にしながら、
少しでも大きく成長して、2年後を迎えたいです。
なお、入学式はスーツで行った方が良いようです・・・(私は私服で行って、失敗しました。。)
初めての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”が表示されます。
O’REILLYで被災者救済キャンペーンとして、eBookが半額で購入可能です。
期間中(3/23-3/26)に購入された売上は、著作権者への印税や決済手数料を除いた全額が
日本赤十字社への義援金にするそうです。
Deal of the Week – 被災者支援キャンペーン開催
もともとeBookは本に比べて若干安くなっていますが、今回は更に半額となるキャンペーンですので、
ぜひ購入をしてはいかがでしょうか。
さて何を買おうかな。
2011/03/29追記
結局これらを買いました。(先ほどようやくO’REILLYからDownloadリンクが送られてきました)
PHP5.2へバージョンアップしたついでに、PHPアクセラレータを入れました。
こちらを参考にさせていただきました。
PHPはアクセス都度、パース→コンパイル→(中間コード)→実行を行いますが、
PHPアクセラレータを導入する事で、ある条件下でパース→コンパイルが不要となります。
代表的なPHPアクセラレータは以下です。
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します。
[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 ]
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年間の内、85%くらいはレッスン受講が出来たのではと思います。
年間85%受講ですと、365日×0.85 = 310.25日 × 25分 = 7756.25分 ≒ 129時間 英会話をしたことになります。
英語初級者(TOEIC:600程度)ということもあり、
当初、フィリピン人の先生の言う事が全く理解出来なかったのを覚えています。
英会話自体を全くしたことがなかったのでしょうがないですが、そもそも文法があやふやであった為、
Hatena界隈で度々紹介されていた以下の本を6ヶ月かけ、2,3週繰り返して熟読しました。
これである程度の文法を理解出来たと思います。
業務上、英語の技術ドキュメントやメール等を読む機会が度々あるのですが、読む際のスピードや理解力が上がったと実感出来ました。
ですが、レッスンの中ではあまり効果を感じられませんでした。
また、文法と並行して、本の付属CDをipodにいれて、通勤時にリスニングをやりました。
当初は全く聞き取れなかったのが、次第に理解出来るようになってきました。
但し、自分の知らない単語はやっぱり何回聴いても理解出来ないので、注意が必要です。
ちなみに、オライリーの本が好きで良く読むのですが、英語を聞き流しながら技術系の本を読むと理解度がかなり落ちます。
と、こんなことをしている内に、自然とフィリピン先生の言っている事が理解出来るようになってきました。
そこで相手のレベルに合わせて喋ってくれるベテラン先生から、レベルに関係なく喋ってくる若い先生に切り替えてみました。
当然、最初は理解出来ないことが多いですが、1, 2ヶ月程すると少しずつ理解出来るようになります。
また、先生を固定せずに色々な先生を指名してみました。先生毎に喋り方も違ってくるのでかなり聞き取りにくいです。
現在は1人の先生に絞り、Breaking News Englishを教材にして教えてもらっています。
この1年間の大きな気づきとして、10ヶ月ほど経過したころからあまり成長を感じなくなった事が挙げられます。
原因は語彙力(単語力)です。
ここまで単語に特化した勉強はしていなかったので、使用する言い回しや言葉がある程度限定されていたのではと分析しています。
やはりある程度の単語を知らないと会話を色々な方面へ、または様々な言い回しが出来ないと実感しました。
ですので、今後1年間で単語力をつけるように計画を練っていきます。
以上のことから、初級者がレアジョブのみやっても英語向上の現状の限界点はすぐ見える為、
文法・リスニング・単語を並行して進めていくのが良いです。
(当然といえば当然ですが、自分が実践して体感出来たので、、)
最後に。発音とか文法間違えたら等は途中から気にならなくなるので大丈夫です。
そのうち矯正しなければならないのでしょうが、気にしていると前に進めなかったので私は無視することにしました。
特にTOEIC用の準備をしているわけではないですが、5月のTOEICを受けてみようと思います。
1年間やった成果として多少上がることを願いつつ。