flywayで環境(開発・テスト・本番)ごとにデータベースの接続先を切り替える

このブログは個人の調査および見解によって書いたものです。実際に以下の内容を使われる場合は自己責任でお願いします。

flywayとは

flywayはJavaでデータベースのマイグレーションを行うためのツールです。

http://flywaydb.org/

JavaでWEBアプリケーションを作っていて、Ruby On Railsのようにスキーママイグレーションを実行したいと思って、Googleで探したところヒットしたので使ってみました。

前提条件

ソフトウェア バージョン
Gradle 1.9
flyway 3.0
MySQL 5.1.73

実行方法(基本編)

ちなみに、flywayをGradleで使う方法はドキュメント(http://flywaydb.org/documentation/gradle/)に書いてあります。

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'org.flywaydb:flyway-gradle-plugin:3.0'
    }
}
flyway {
  url      = 'jdbc:mysql://localhost:3306/example'
  user     = 'app_user'
  password = 's3cret'
}

src/main/resources/db/migrateにSQLスクリプトを配置します。 SQLファイルには命名規則があり、先頭大文字Vで始まり、バージョン、(アンダースコア)2つ、説明と続きます。 バージョンと説明は(アンダースコア)または.(ドット)で区切ることが可能です。

V0.0.1__create_todo.sql

create table todo(
   id           INTEGER      NOT NULL AUTO_INCREMENT
  ,title        VARCHAR(30)
  ,description  TEXT
  ,PRIMARY KEY (id)
)

Gradleの実行

./gradlew flywayInit

上記タスクを実行すると、スキーマにschema_versionテーブルが作成されます。 このテーブルがマイグレーションをどこまで実行しているかを保持します。

./gradlew flywayMigrate

上記タスクでマイグレーションが実行されます。

環境ごとにデータベースの接続先を分ける場合

ちなみに、正しいアプローチなのかは不明です。こういうツールの場合は、すでにこのような機能が実装されていることを期待していたのですが、今の時点では機能が見つかっていないため、以下の実装にしました。

build.gradle

flyway {

    def enviroment = 'development'

    switch(enviroment) {
        case "development" :
            url      = 'jdbc:mysql://localhost:3306/example_development'
            user     = 'app_user_development'
            password = 'development_s3cret'

        case "test" :
            url      = 'jdbc:mysql://test-database:3306/example_test'
            user     = 'app_user_test'
            password = 'test_s3cret'

        case "production" :
            url      = 'jdbc:mysql://database:3306/example_production'
            user     = 'app_user'
            password = 'producition_s3cret'

        default :
            //Same development
            url      = 'jdbc:mysql://localhost:3306/example_development'
            user     = 'app_user_development'
            password = 's3cret'
    }
}

以下の方法で実行します。

./gradlew flywayMigrate -Pflyway.enviroment=production

flywayは Gradleタスクに-Pを付与することで、実行時にプロパティを指定できます。

以上です。

SpringのDIコンテナについて

いまどきのJavaを使ったWEBアプリケーション開発を使いたいと思い調べたSpringの基本のメモです。

DIコンテナ

SpringのDIコンテナを使うとオブジェクトの依存関係を簡単に解決できるとのこと。アスペクト指向プログラミングの詳しいことは分からないので、これから勉強していきたいと思います。  

コードの具体例

ちなみに、Springのバージョンは4.0.4です。

src/main/hello/person.java

public class Person {

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"services.xml"});
Person john1 = (Person)ctx.getBean("person");

src/main/resources/services.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="hello.Person">
        <constructor-arg index="0" value="john" />
        <constructor-arg index="1" value="11" />
    </bean>

</beans>

Personクラスのコンストラクタに"john"と"11"を設定してインスタンスを生成しています。 DIコンテナを使わないとnewでコンストラクタに引数を指定して生成しますが、DIコンテナの場合は その記載が不要です。

こんな書き方も出来ます。

Person john2 = ctx.getBean("person", Person.class);

DIコンテナのスコープ

ちなみに、SpringのDIコンテナにはスコープがあり、デフォルトはシングルトンです。なので、scopeを指定せずに複数のオブジェクトを生成してもインスタンスは1つです。都度新しいインスタンスを生成する場合は、beanに以下のように設定します。

 <bean id="person" class="hello.Person" scope="prototype">

 

DIコンテナのメリット

何が便利かというとユニットテストが書きやすい。あと、クラスがプラッガブルになるのでクラスの差し替えが容易です。 今後はWEBアプリケーションの構築に向けて理解を深めていきたいと思います。

 

ScalaのUnitについて

Scalaでよく見かけるUnitがわからなかったので、調べてみました。
以下の様なコードで戻り値を示す際に使われています。

def hoge(): Unit = {
  println("hoge")
}
hoge()

http://www.scala-lang.org/api/current/index.html#scala.Unit

Scalaのリファレンスを見ると、Javaのvoidのようなものと書いてあります。
Scalaの設計思想として関数型プログラミングの考え方があります。ここでいう
関数とはC言語のような言語で処理を定義するという意味の関数ではなく、数学
で使う必ず値を戻す関数のことです。
Scalaは戻り値を返さなければならないけれども、voidのように使える値がない
と不便なので定義されていると理解しました。

RSpec + Seleniumを試す。

個人的な備忘録です。この記事は個人の理解によって書いたものであり、内容を保証するものではありません。

1.Seleniumとは

Java, Ruby, Pythonなどの言語からWEBブラウザを操作するためのAPIです。Seleniumを使うと実際にブラウザが起動して処理が実行されます。
詳細は本家のページ(http://seleniumhq.org/)に書いてあります。

2.今回のテーマ

インテグレーションテストを自動化するために、RSpecからSeleniumを使いたいと思います。なぜ、RSpec + Seleniumなのかというところですが、RSpecRubyのテスト用ライブラリでRubyならではの生産性の高さが魅力です。Rubyを使うという意味ではTest::Unitも選択肢にありますが、個人的にRSpecを使ったほうが読みやすいコードを書きやすいと思います。
Seleniumを使う理由ですが、テストをWEBアプリケーションを作っている言語で書かなくてもよいということがあります。例えば、Javaでアプリケーションを作ったので、インテグレーションテストもJavaで書くというのは個人的に避けたいです。Javaコンパイルによる静的チェックや継承の制限、JVMの解析によるパフォーマンス分析のやりやすさが魅力で、チームでの開発では魅力的な言語ですが、Rubyと比較すると記述が冗長な部分があり、言語レベルでの生産性の高さではRubyの方が優れていると思います。WEBアプリケーションはJavaで作ったけど、テストを効率的に書きたいというときに選択肢の一つとして、Ruby+Seleniumはありだと思っています。
ちなみに、状況によりますが、Railsなどのテストがサポートされているフレームワークでは、インテグレーションテストもそのフレームワークのルールに従って作るほうが効率がいい場合もあると思います。

3.試してみる

開発環境はMac OS X 10.8.2 でrvmを使っています。APIのリファレンスはhttp://selenium.googlecode.com/svn/trunk/docs/api/rb/index.htmlを参考にしました。テストを実行するプログラムのディレクトリ構成は以下のとおりです。

selenium_rspec
  |- spec
  |- vendor
  |- .rspec
  |- .gitignore
  `- Gemfile

ライブラリの管理はbundleを使います。Gemfileは以下のとおりです。

source "http://rubygems.org"

gem 'rspec'
gem 'selenium'
gem 'selenium-webdriver'

bundleでgemをインストールします。

bundle install --path vendor/bundle

specディレクトリに以下のプログラムを配置します。ちなみに、使うブラウザによってWebDriverを生成するコードが異なります。今回は最もシンプルなFireFoxで試しました。RSpecではspec/**/*_spec.rbにマッチするファイルを自動で認識して実行してくれますので、その規約にそってファイルを作ります。

firefox_example_spec.rb

require 'selenium-webdriver'

describe "Google", "FireFox Example" do

  before do
    @webdriver = Selenium::WebDriver.for :firefox
  end

  it "Http get should be successfull." do
    @webdriver.navigate.to "http://www.google.co.jp"
    element = @webdriver.find_element(:name, 'q')
    element.send_keys "Hello World"
    element.submit
    @webdriver.title.should == "Google"
  end

  after do
    @webdriver.quit
  end
end

Googleにアクセスして、HelloWorldと入力するテストです。Matcherではページのタイトルが"Google"であることを検証しています。
プロジェクトのルートで以下のコマンドを実行すると、ブラウザが起動してテストが自動実行されます。

bundle exec rspec

ちなみに、プロジェクトルートに.rspecファイルを配置すると、カラー表示や結果の表示を変更できます。
.rspec

--format documentation
--color

今後はJenkinsとの連動にチャレンジしたいと思います。

Rails 勉強会 東京 第68回まとめ

11/19(土)にRails勉強会@東京 第68回に参加してきました。そのまとめです。


1.概要
 参加者は20人位でした。初参加の方が多かったという印象です。今回はファシリテーションを任せて
 いただけたので、私がセッションを仕切らせてもらいました。

 (1) 全体の流れ 
 セッション1:カッコイイUIを簡単につくる。/ 初心者向けセッション
 セッション2:テストの話
 セッション3:Rails3.1の機能


2.セッション1 :カッコイイUIを簡単につくる。
 出来る限りCSSを書かずに、カッコイイUIを作りたいというテーマで話がありました。

 (1) twitter bootstrap の紹介
  http://twitter.github.com/bootstrap/
  git からソースを取得してコードも簡単に読みました。
  https://github.com/dannymcc/Support-Systemからbootstrapを使ったオープンソースプロダクト
  をgit cloneして、実際に動かして紹介しました。

 (2) ExtJS(javascriptライブラリ)の紹介
  http://extjs.co.jp/

  デモを簡単に見ました。

 (3) backbone.jsの紹介
  http://documentcloud.github.com/backbone/

  概要だけ紹介しました。次回はこれを使ったアプリの紹介とかが出来るといいかもしれないという
  話になりました。

 (4) まとめ
  UIを簡単につくるための方法として、様々な選択肢があって、それはお客さんの望む内容や、アプリ
  ケーションの仕様によって決めるべきという話でした。

 初心者向けセッションには参加しなかったので、ここでは書きません。


3.セッション2:テストの話
 (1) テスト駆動開発の話
  テストコードを作ることにこだわり過ぎないことも大切。
  まずは動くものを作って、APIを良く考えなければいけないときにテストコードから作るという考え方もある。

 (2) rspecで書かれたテストコードのソースコードリーディング
  https://github.com/resolve/refinerycms/blob/master/dashboard/spec/requests/refinery/admin/dashboard_spec.rb
  TDDのライブコーディングにトライしようという流れでしたが、ライブコーディングは準備していないと
  失敗する可能性が高いので、ソースコードリーディングをしました。

4.セッション3:Rails 3.1の機能
 (1) Asset Pipelineの紹介
  (a) SCSSの紹介
  (b) Coffee Scriptの紹介

 (2) Http Streamingの紹介
 (3) マウント可能なエンジンの紹介
  http://ja.asciicasts.com/episodes/277-mountable-engines

 (4) Rails 3.0からRails 3.1へのバージョンアップ
  標準の機能でアップグレードできるところはする。routes.rbとか標準のツールで出来ないところ
  は、gemで便利なツールをインストールして使う。
 
 あと、時間が余ってしまったので、参加者の方に開発環境を聞きました。
 ちなみに、emacsを使ってる人はおらず、vimが大多数でした。IDEも聞いてみたところ、
 NetBeansを使っている人が数名いらっしゃって、Aptanaを使っている人はいませんでした。
 Aptanaは重いようです。


5.最後に
 勉強会を開催してくださった永和システムマネジメントの方、参加者のみなさんありがとうございました。
 

CentOSにZabbix 1.8.8をソースコードからビルドする。

CentOSにZabbixをインストールします。この記事ではhttpdphpソースコードからビルドします。

1.ソフトウェアのバージョン
 CentOS 5.5 x86_64
 Zabbix : 1.8.8
 MySQL : 5.5.8
 Apache : 2.2.17
 php : 5.3.4
 libpng : 1.5.5
 libjpeg : 8c
 net-snmp:5.7.1

2.作業の前提条件
 (1) rootユーザで作業します。
 (2) ソースはダウンロードして、/usr/local/srcに配置されているものとします。
 (3) MySQLはすでにインストールしてあるものとします。
   MySQLのインストール手順はhttp://d.hatena.ne.jp/pgmgonta/20110308/1299593213を参照してください。

3.httpdのインストール
(1)apacheユーザの追加

groupadd apache
useradd -g apache apache

(2) ディレクトリの作成

mkdir /usr/local/httpd

(3) httpdのビルド

cd /usr/local/src
tar -zxvf httpd-2.2.17.tar.gz
cd httpd-2.2.17
./configure --enable-so --prefix=/usr/local/httpd/
make && make install

(4) ディレクトリのアクセス権変更

chown -R apache:apache /usr/local/httpd/

(5) 起動
以下のコマンドを実行してから、「http://サーバのIPアドレス」へ接続してください。HTTPレスポンスが帰ってくれば成功です。

/usr/local/httpd/bin/apachectl start

4.phpのインストール
(1) 依存するライブラリをmakeします。
libjpegをmakeします。

cd /usr/local/src
tar -zxvf jpegsrc.v8c.tar.gz
cd jpeg-8c/
./configure
make && make install

libpngをmakeします。

cd /usr/local/src
tar -zxvf libpng-1.5.5.tar.gz
cd libpng-1.5.5
./configure
make && make install

(2) phpのビルド
phpをビルドします。Zabbixを動かすために必要なconfigureオプションを指定します。

cd /usr/local/src
tar -zxvf php-5.3.4.tar.gz
cd php-5.3.4
./configure --with-mysql=/usr/local/mysql/ --enable-zip --enable-mbstring --enable-bcmath --with-gd --with-apxs2=/usr/local/httpd/bin/apxs --enable-sockets
make && make install

5.Zabbixのインストール
(1)zabbixユーザの追加

groupadd zabbix
useradd -g zabbix zabbix

(2) net-snmpのビルド

cd /usr/local/src
tar -zxvf net-snmp-5.7.1.tar.gz
cd net-snmp-5.7.1
./configure 
ここではコンソールに値を入力しながらインストールします。デフォルト値で特に問題はありません。
make && make install

(3) Zabbixのビルド

tar -zxvf zabbix-1.8.8.tar.gz
cd zabbix-1.8.8
./configure --enable-server --with-mysql=/usr/local/mysql/bin/mysql_config --with-net-snmp --with-libcurl
make && make install

(4) Zabbixの設定ファイルの配置

mkdir /etc/zabbix
cp misc/conf/zabbix_server.conf /etc/zabbix/

(5) Zabbixの自動起動の設定

cd /usr/local/src/zabbix-1.8.8
cp misc/init.d/redhat/8.0/zabbix_server /etc/init.d/
chmod +x /etc/init.d/zabbix_server
chkconfig --add zabbix_server
chkconfig zabbix_server on

/etc/init.d/zabbix_serverの内容を書き換えます。

#progdir="/usr/local/zabbix/bin/"
progdir="/usr/local/sbin/"

(6) php.iniの変更
php.iniは/usr/local/libに配置します。

cd /usr/local/src/php-5.3.4
cp php.ini-development /usr/local/lib/php.ini

php.iniを編集します。

diff php.ini-development /usr/local/lib/php.ini
440c440
< max_execution_time = 30
---
> max_execution_time = 600
450c450
< max_input_time = 60
---
> max_input_time = 600
458c458
< memory_limit = 128M
---
> memory_limit = 256M
728c728
< post_max_size = 8M
---
> post_max_size = 32M
879c879
< upload_max_filesize = 2M
---
> upload_max_filesize = 16M
993c993
< ;date.timezone =
---
> date.timezone = Asia/Tokyo

6.Zabbixの起動
Zabbixを起動させるために、net-snmpmysql-clientのライブラリを参照できるようにします。

vi /etc/ld.so.conf.d/net-snmp.conf

/etc/ld.so.conf.d/net-snmp.confの内容

/usr/local/lib
vi /etc/ld.so.conf.d/net-snmp.conf

/etc/ld.so.conf.d/mysql.confの内容

/usr/local/mysql/lib

Zabbixを起動します。

service zabbix_server start

7.WEBインタフェースの設定
(1)プログラムの配置
phpのプログラムを配置します。

mkdir /usr/local/httpd/htdocs/zabbix
cd /usr/local/src/zabbix-1.8.8
cp -Rp frontends/php/* /usr/local/httpd/htdocs/zabbix/

(2)MySQLの設定
MySQLにテーブルを作成して、データを登録します。

/usr/local/mysql/bin/mysql -u root
> create database zabbix;
> grant all on zabbix.* to zabbix@localhost identified by "PASSWD";
> flush privileges;
> exit

cd /usr/local/src/zabbix-1.8.8
/usr/local/mysql/bin/mysql -u zabbix -D zabbix -p < create/schema/mysql.sql 
/usr/local/mysql/bin/mysql -u zabbix -D zabbix -p < create/data/data.sql 
/usr/local/mysql/bin/mysql -u zabbix -D zabbix -p < create/data/images_mysql.sql

(3)Apacheの起動

/usr/local/httpd/bin/apachectl start

以下のページへアクセスしてセットアップしてください。
http://ZABBIXサーバのIPアドレス/zabbix/index.php

作業は以上です。

CentOSにSyslog-ng 3.1.1をソースコードからビルドする。

1.環境
 OS : CentOS 5.5(64bit)
 Syslog-ng : Syslog-ng 3.3.1 オープンソースエディション

2.インストール作業
ここでは作業を開始する前に、/usr/local/srcに以下(1)と(2)のファイルが配置されているものとします。

(1) eventlog_0.2.12.tar.gz
(2) syslog-ng_3.3.1.tar.gz

eventlogからインストールします。作業はrootユーザで行います。

インストールするために、環境変数を設定します。

vi ~/.bash_profile

以下の内容を追記します。

〜省略〜
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
export PKG_CONFIG_PATH
〜省略〜

~/.bash_profileを反映します。

. ~/.bash_profile

eventlogをビルドします。

 cd /usr/local/src
 tar -zxvf eventlog_0.2.12.tar.gz
 cd eventlog-0.2.12/ 
 ./configure
 make && make install

syslog-ngをビルドします。

 cd /usr/local/src
 tar -zxvf syslog-ng_3.3.1.tar.gz
 cd syslog-ng-3.3.1/
 ./configure
 make && make install 

起動スクリプトを配置して自動起動の設定をします。

 cd /usr/local/src/syslog-ng-3.3.1
 cp contrib/init.d.RedHat-7.3 /etc/init.d/syslog-ng
 chmod +x /etc/init.d/syslog-ng
 chkconfig --add syslog-ng

以下のコマンドで設定を確認します。稼動させたいランレベルでonになっていることを確認します。

 chkconfig --list syslog-ng 

標準のsyslogを停止します。合わせて、syslogの自動起動をOFFにします。

 service syslog stop
 chkconfig syslog off

/usr/local/varディレクトリが存在しない場合は、以下のコマンドで作成してください。

 mkdir /usr/local/var

設定ファイルはデフォルトで以下のパスに配置されます。必要に応じて変更してください。

/usr/local/etc/syslog-ng.conf

syslog-ngを起動します。

 service syslog-ng start

3.動作確認
loggerコマンドで確認します。

 tail -f /var/log/messages
 logger "test"

作業は以上です。