apt でミラーサーバーを自動的に選択する

deb mirror://mirrors.ubuntu.com/mirrors.txt vivid main restricted universe multiverse
deb mirror://mirrors.ubuntu.com/mirrors.txt vivid-updates main restricted universe multiverse
deb mirror://mirrors.ubuntu.com/mirrors.txt vivid-backports main restricted universe multiverse
deb mirror://mirrors.ubuntu.com/mirrors.txt vivid-security main restricted universe multiverse

このとき、

E: Method gave invalid 103 Redirect message

と表示される場合がある。そのような場合は

sudo rm -rf /var/lib/apt/lists/*

すればとりあえず動く。

リンク

MySQLで破損したテーブルを修復する方法

自宅サーバーのMySQLのテーブルが壊れ、SELECT文で書き込まれていないはずの値が返ってきたり、mysqldumpが途中で停止しまったりするようになってしまいました。修復した時のメモを残しておきます。

データベースのバックアップを取る

$ sudo service mysql stop
$ mkdir mysql_backup
$ su -
$ cp -r /var/lib/mysql/* mysql_backup
$ exit

データベースのダンプをする

$ sudoedit /etc/mysql/my.cnf

# 以下の内容をmy.cnfに加える
[mysqld]
innodb_force_recovery = 4

$ sudo service mysql start
$ mysqldump -uroot -p[password] -A -v --flush-privileges --routines --result-file mysql_backup.sql
$ sudo service mysql stop

# 上記で加えた内容を削除する
$ sudoedit /etc/mysql/my.cnf

データベースを再作成する

sudo rm -Rf /var/lib/mysql/*
sudo mkdir -p /var/lib/mysql
sudo mysql_install_db
sudo chown -Rf mysql:mysql mysql
sudo service mysql start
# 一番初めに聞かれるパスワードは初期接続用のパスワード。
# 何も入力せずにそのままエンターキーを押すと通る。
sudo mysql_secure_installation

ダンプしたデータベースをリストアする

mysql -uroot -p[password] < mysql_backup.sql

リンク先ではmysql_install_dbとmysql_secure_installationについて触れられていなかったので、参考にしつつ記事にしました。

リンク

心地良すぎるDependency Injectionライブラリ Guice

etc9さんの"心地良すぎるモックライブラリ Mockito"がとても勉強になったので、似たような形式でGuiceの紹介をしてみます。

Dependency Injection

きちんと勉強したわけではないので間違っていたらごめんなさい。Dependency Injection (DI)はユニットテストを書きやすくするためのクラスの書き方の一つです。クラスAが内部でクラスBを使うとき、Aの中でBをnewする代わりに、AのコンストラクタやsetterでBのインスタンスを外部から渡せるしておきます。こうしておくとAのユニットテストを書くとき、Bの動作を真似るモックオブジェクトを渡すことで、Bの中身を考えずにAをのテストを書けるようになります。これはAとBの動作を同時に考えてテストを書くよりずっと楽だと思います。依存性注入とかレポジトリパターンとも呼ばれているっぽいです。
GuiceはDIの補助をしてくれるライブラリです。Guice Moduleと呼ばれるクラスにインスタンス生成ルールを記述しておくと、依存関係を解析した上でインスタンスを作成してくれます。Guiceの内部ではリフレクションで依存クラスの解析とインスタンスの作成をしているんだと思います。多分。単にインスタンスを作成するだけでなく、Singletonにしてくれたり、Factoryクラスを自動実装してくれたり、色々やってくれます。

簡単な例

以下の例ではInjectorを作成したあと、Injector経由でクラスAのインスタンスを作成しています。デフォルトのルールでインスタンス生成ができるので、Moduleは使っていません。

import com.google.inject.Guice;
import com.google.inject.Injector;

public class Sample00 {
  public static class A {
    public void run() {
      System.out.println("A#run() is called.");
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector();
    A a = injector.getInstance(A.class);
    a.run();
  }
}

出力

A#run() is called.

Aのインスタンスを作るよう指示しました。するとinjectorが内部でインスタンスを作って返してくれます。

引数なしコンストラク

引数なしのコンストラクタは特別な指定なしで呼び出してくれます。

import com.google.inject.Guice;
import com.google.inject.Injector;

public class Sample01 {
  public static class A {
    public A() {
      System.out.println("A() is called.");
    }

    public void run() {
      System.out.println("A#run() is called.");
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector();
    A a = injector.getInstance(A.class);
    a.run();
  }
}

出力

A() is called.
A#run() is called.

Aのインスタンスを作るよう指示しました。するとinjectorはAの引数なしコンストラクタを呼び出してインスタンスを作ってくれます。

Constructor Bindings

コンストラクタが引数を持つ場合、引数の型に応じたインスタンスを生成して渡した上で、対象のインスタンスを生成してくれます。ただし、引数付きのコンストラクタには@Injectアノテーションを付けておかないとエラーになってしまいます。クラス間の依存関係がありますが、まだModuleは使わなくて大丈夫です。
バグ防止のためguavaライブラリのPreconditions.checkNotNull()でnullチェックをしています。

import com.google.common.base.Preconditions;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class Sample02 {
  public static class A {
    public A() {
      System.out.println("A() is called.");
    }

    public void run() {
      System.out.println("A#run() is called.");
    }
  }

  public static class B {
    private final A a;

    @Inject
    public B(A a) {
      this.a = Preconditions.checkNotNull(a);
      System.out.println("B() is called.");
    }

    public void run() {
      a.run();
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector();
    B b = injector.getInstance(B.class);
    b.run();
  }
}

出力

A() is called.
B() is called.
A#run() is called.

Bのインスタンスを作るよう指示しました。するとinjectorはBのコンストラクタの引数にAが入っているのを見つけ、先にAのインスタンスを作ります。そしてそれをBのコンストラクタに渡して、Bのインスタンスを作ってくれます。出力からBにAのインスタンスが渡されているのが分かります。
ちなみに@Injectを付けないとこんなエラーが出ます。

Exception in thread "main" com.google.inject.ConfigurationException: Guice configuration errors:

1) Could not find a suitable constructor in Sample02$B. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
  at Sample02$B.class(Sample02.java:20)
  while locating Sample02$B

1 error
  at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1004)
  at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:961)
  at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1013)
  at Sample02.main(Sample02.java:32)

こういったエラーがコンパイル時に分からないのがGuiceの難点の一つです。どうにかならないのでしょうか・・・。
Constructor Bindingsの他にもsetterによるbindingやfield bindingといった方法もありますが、Constructor Bindings推奨だったように思います。

Linked Bindings

Module初登場です。Moduleを使って、Aをinjectする代わりにAを継承したBをinjectするよう指定します。これにはbind()メソッドを使います。"bind(A.class).to(B.class);"は、"型Aの引数にはBのインスタンスを結び付けろ"という意味です。ModuleはcreateInjector()の引数に渡します。
Moduleのconfigure()メソッドの中ではinstall()というメソッドも使えます。これは別のModuleの定義を取り込むというものです。別のパッケージで定義されているModuleの内容を取り込む特に使うと良いでしょう。

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class Sample03 {
  public interface A {
    void run();
  }

  public static class B implements A {
    public B() {
      System.out.println("B() is called.");
    }

    public void run() {
      System.out.println("B#run() is called.");
    }
  }

  public static class SampleModule extends AbstractModule {
    @Override
    protected void configure() {
      bind(A.class).to(B.class);
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SampleModule());
    A a = injector.getInstance(A.class);
    a.run();
  }
}

出力

B() is called.
B#run() is called.

Aのインスタンスを作るよう指示しました。injectorはModuleの中に"型Aの引数にはBのインスタンスを結び付けろ"と指示があるので、Bのインスタンスを作って返してくれます。
Aがクラスだったり抽象クラスでも動きます。Aが別のクラスの引数に指定されていても動きます。

Scopes

即別な指定をしない場合、Guiceインスタンスをinjectのたびに新しいインスタンスを生成します。ModuleでScopes.SINGLETONを指定すると、インスタンスをinjectするときに最初の一回だけインスタンスを作ってinjector内で使いまわすようになります。いわゆるSingletonパターン的な使い方ができます。

import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Scopes;

public class Sample04 {
  public static class A {
    public A() {
      System.out.println("A() is called.");
    }

    public void run() {
      System.out.println("A#run() is called.");
    }
  }

  public static class B {
    private final A a;

    @Inject
    public B(A a) {
      this.a = Preconditions.checkNotNull(a);
      System.out.println("B() is called.");
    }

    public void run() {
      System.out.println("B#run() is called.");
      a.run();
    }
  }

  public static class C {
    private final A a;

    @Inject
    public C(A a) {
      this.a = Preconditions.checkNotNull(a);
      System.out.println("C() is called.");
    }

    public void run() {
      System.out.println("C#run() is called.");
      a.run();
    }
  }

  public static class SampleModule extends AbstractModule {
    @Override
    protected void configure() {
      bind(A.class).in(Scopes.SINGLETON);
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SampleModule());
    B b = injector.getInstance(B.class);
    b.run();
    C c = injector.getInstance(C.class);
    c.run();
  }
}

出力

A() is called.
B() is called.
B#run() is called.
A#run() is called.
C() is called.
C#run() is called.
A#run() is called.

BとCのインスタンスを作るよう指示しました。それぞれのコストラクタの引数にAが入っているので、injectorはAのインスタンスを生成して渡そうとします。ここでModuleの中でScopes.SINGLETONが指定されているので、injectorはAを1回だけ生成し、それを2回目以降はそのインスタンスを返します。出力からAのコンストラクタが1回しか呼ばれてないのが分かります。
@Singletonアノテーションを使っても同様のことができますが、Scopeをどこで指定したのかわからなくなってしまうためやめたほうが良いと思います。Linked Bindingsと組み合わせることもできます。

Binding Annotations

同じ型の引数に@Namedアノテーションで別の名前をつけることで、別のインスタンスをinjectするよう指定できます。

import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.name.Named;
import com.google.inject.name.Names;

public class Sample05 {
  public interface A {
    void run();
  }

  public static class B implements A {
    public B() {
      System.out.println("B() is called.");
    }

    @Override
    public void run() {
      System.out.println("B#run() is called.");
    }
  }

  public static class C implements A {
    public C() {
      System.out.println("C() is called.");
    }

    @Override
    public void run() {
      System.out.println("C#run() is called.");
    }
  }

  public static class D {
    private final A a0;
    private final A a1;

    @Inject
    public D(@Named("BBB") A a0, @Named("CCC") A a1) {
      this.a0 = Preconditions.checkNotNull(a0);
      this.a1 = Preconditions.checkNotNull(a1);
      System.out.println("D() is called.");
    }

    public void run() {
      System.out.println("D#run() is called.");
      a0.run();
      a1.run();
    }
  }

  public static class SampleModule extends AbstractModule {
    @Override
    protected void configure() {
      bind(A.class).annotatedWith(Names.named("BBB")).to(B.class);
      bind(A.class).annotatedWith(Names.named("CCC")).to(C.class);
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SampleModule());
    D d = injector.getInstance(D.class);
    d.run();
  }
}

出力

B() is called.
C() is called.
D() is called.
D#run() is called.
B#run() is called.
C#run() is called.

Dのインスタンスを作るよう指示しました。injectorはDの引数に"BBB"と名付けられたAと"CCC"と名付けられたAが入っているのを見つけます。ModuleにそれぞれBとCを作るような指示が入っているため、BとCを生成してDのコンストラクタに渡し、Dのインスタンスを生成して返してくれます。
ただし、@Namedを使うとインスタンスの実際の型が分かりにくくなってしまうため、あまり使わないほうが良いかもしれません。

Instance Bindings

injectするインスタンスを手動で生成して直接結びつけます。必然的にSingletonになります。

import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class Sample06 {
  public interface A {
    void run();
  }

  public static class B implements A {
    public B() {
      System.out.println("B() is called.");
    }

    @Override
    public void run() {
      System.out.println("B#run() is called.");
    }
  }

  public static class C {
    private final A a;

    @Inject
    public C(A a) {
      this.a = Preconditions.checkNotNull(a);
      System.out.println("C() is called.");
    }

    public void run() {
      System.out.println("C#run() is called.");
      a.run();
    }
  }

  public static class SampleModule extends AbstractModule {
    @Override
    protected void configure() {
      bind(A.class).toInstance(new B());
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SampleModule());
    C c = injector.getInstance(C.class);
    c.run();
  }
}

Cのインスタンスを生成するよう指示しました。injectorはCのコンストラクタの引数にAが入っているのを見つけます。Moduleに型Aの引数にはconfigure()内で作られたインスタンスを渡すように支持があるため、そのインスタンスを渡してCのインスタンスを生成します。
@Named等と併用できます。

@Provides Methods

injectするクラスやインスタンスを指定する代わりに、インスタンスを作るメソッドをModule内に定義すると、そのメソッドを使ってインスタンスを作ってinjectしてくれます。戻り値の型をinjectしたい型にして@Providesをつけるだけです。メソッドに引数を加えると、そこにもinjectしてくれます。@Namedや@Singletonと併用できます。

import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provides;

public class Sample07 {
  public static class A {
    public A() {
      System.out.println("A() is called.");
    }

    public void run() {
      System.out.println("A#run() is called.");
    }
  }

  public interface B {
    void run();
  }

  public static class C implements B {
    private final A a;

    @Inject
    public C(A a) {
      this.a = Preconditions.checkNotNull(a);
      System.out.println("C() is called.");
    }

    @Override
    public void run() {
      System.out.println("C#run() is called.");
      a.run();
    }
  }

  public static class D {
    private final B b;

    @Inject
    public D(B b) {
      this.b = Preconditions.checkNotNull(b);
      System.out.println("D() is called.");
    }

    public void run() {
      System.out.println("D#run() is called.");
      b.run();
    }
  }

  public static class SampleModule extends AbstractModule {
    @Override
    protected void configure() {
    }

    @Provides
    private B provideA(A a) {
      return new C(a);
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SampleModule());
    D d = injector.getInstance(D.class);
    d.run();
  }
}

出力

A() is called.
C() is called.
D() is called.
D#run() is called.
C#run() is called.
A#run() is called.

Dのインスタンスを生成するよう指示しました。Dのコンストラクタの引数はBですので、Bのインスタンスを生成して渡そうとします。Moduleに戻り値の型がBで@Providesアノテーションが付いているメソッドがあるので、これを使ってインスタンスを生成しようとします。providerメソッドの引数にAがあるので、先にAのインスタンスを生成します。Aのインスタンスを生成しproviderメソッドに渡し、Bのインスタンスを生成します。ここで実際にはCのインスタンスが生成されています。そのインスタンスをDのコンストラクタに渡し、Dのインスタンスを生成して返します。

TypeLiterals

bind()ではジェネリクスを伴ったクラスを直接bindすることができません。これはジェネリクスの型情報がコンパイル時に消えてしまうためだと思います。ジェネリクスを伴ったクラスをbind()する場合は、TypeLiteralで包んで指定します。

import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;

public class Sample08 {
  public interface A<T> {
    void run(T t);
  }

  public static class B<T> implements A<T> {
    public B() {
      System.out.println("B() is called.");
    }

    public void run(T t) {
      System.out.println("B#run() is called with \"" + t + "\".");
    }
  }

  public static class C {
    private final B<Integer> b;

    @Inject
    public C(B<Integer> b) {
      this.b = Preconditions.checkNotNull(b);
      System.out.println("C() is called.");
    }

    public void run() {
      System.out.println("C#run() is called.");
      b.run(42);
    }
  }

  public static class SampleModule extends AbstractModule {
    @Override
    protected void configure() {
      bind(new TypeLiteral<A<Integer>>() {
      }).to(new TypeLiteral<B<Integer>>() {
      });
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SampleModule());
    C c = injector.getInstance(C.class);
    c.run();
  }
}

出力

B() is called.
C() is called.
C#run() is called.
B#run() is called with "42".

Cのインスタンスを生成するように指示しました。Cのコンストラクタの引数にはAが含まれているので、Aインスタンスを生成しようとします。Module内で"型Aの引数にはBのインスタンスを結び付けろ"と指示があるので、Bのインスタンスが生成され、Cのコンストラクタの引数に渡されます。最後にCのインスタンスが生成されます。
Javaはあまり詳しくないため、なぜTypeLiteralでくるむとジェネリクス情報が残るのかわかっていませんorz

AssistedInject

プログラム実行中にクラスのインスタンスの生成をするとき、コンストラクタの引数の一部を手動で渡したい場合があると思います。このような場合はFactoryクラスを経由するのが定石だと思います。FactoryModuleBuilderを使用するとFactoryクラスを自動的に実装してくれます。このとき、引数の一部は手動で、残りはGuiceが自動的にインスタンスを生成して渡す形になります。
FactoryModuleBuilderを使うときは、Factoryインタフェースと@Assistedアノテーションを使います。初めにFactoryインタフェースを定義し、内部にFactoryメソッドを定義しておきます。このとき引数にはコンストラクタにプログラム実行時に手動で渡したいものを書いておきます。また戻り値は生成したいインスタンスの型か、その親クラス/インタフェースを書いておきます。
次にFactoryクラスに生成させたいクラスのコンストラクタの引数のうち、手動で渡したいものに@Assistedアノテーションを付けます。Factoryクラスのメソッドの引数と@Assistedのついた引数が一致すると、FactoryModuleBuilderが内部でFactoryクラスを自動的に実装してくれます。
Factoryクラスのメソッドの戻り値が、欲しいクラスの親クラス/インタフェースの場合は、FactoryModuleBuilder#implement()を使ってどのクラスを生成するか指定します。

import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryModuleBuilder;

public class Sample09 {

  public static class A {
    public A() {
      System.out.println("A() is called.");
    }

    public void run() {
      System.out.println("A#run() is called.");
    }
  }

  public interface B {
    void run();
  }

  public static class C implements B {
    private final A a;

    @Inject
    public C(A a, @Assisted int number) {
      this.a = Preconditions.checkNotNull(a);
      System.out.println("C() is called with " + number + ".");
    }

    @Override
    public void run() {
      System.out.println("C#run() is called.");
      a.run();
    }
  }

  public interface BFactory {
    B create(int number);
  }

  public static class D {
    private final BFactory bFactory;

    @Inject
    public D(BFactory bFactory) {
      this.bFactory = Preconditions.checkNotNull(bFactory);
      System.out.println("D() is called.");
    }

    public void run() {
      System.out.println("D#run() is called.");
      B b = bFactory.create(42);
      b.run();
    }
  }

  public static class SampleModule extends AbstractModule {
    @Override
    protected void configure() {
      install(new FactoryModuleBuilder().implement(B.class, C.class).build(BFactory.class));
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SampleModule());
    D d = injector.getInstance(D.class);
    d.run();
  }
}

出力

D() is called.
D#run() is called.
A() is called.
C() is called with 42.
C#run() is called.
A#run() is called.

Dのインスタンスを生成するように指示しました。Dのコンストラクタの引数にはBFactoryがありますので、BFactoryのインスタンスを生成しようとします。Moduleの中で「BFactoryの実装をしろ。型Bを戻り値に持つメソッドがあったらCのインスタンスを生成して返せ。」と指定されたModuleがinstallされているので、この通りにBFactoryのインスタンスを自動実装してDのコンストラクタに渡します。このとき、Bのコンストラクタが呼ばれていない点がポイントです。Bのコンストラクタは初期化が終わったあと、BFactory#create()を呼んだときに初めて呼ばれます。
続いてBFactory#create()が呼ばれBのインスタンスが生成されようとしています。BFactoryはBの代わりにCのインスタンスを作るように指定されていましたので、Cのコンストラクタが呼ばれます。CのコンストラクタにはAが指定されていますので、Aのインスタンスが生成されて渡されます。またCの引数numberには、BFactory#create()に渡されたものが渡されます。Cのインスタンスが生成され、BFactory#create()から返ってきます。
implement()にはTypeLiteralも渡せますので、ジェネリクスを含むクラスも指定することができます。またimplement()にはAnnotationが渡せるようになっており、一つのFactoryクラスの中で、@Namedで違う名前を付けた、同じ型の戻り値を持つFactoryメソッドを作ることもできます。ただし、これは複雑すぎるため避けたほうが良いと思います。

Multibindings

Multibindingを使うと、複数のインスタンスをSetに入れた状態でinjectすることができます。やりかたはMultibinderのインスタンスにaddBinding()でインスタンス生成ルールを追加していくだけです。あまり使用機会はないのではないかもしれません。

import java.util.Set;

import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.multibindings.Multibinder;

public class Sample10 {
  public interface A {
    void run();
  }

  public static class B implements A {
    public B() {
      System.out.println("B() is called.");
    }

    public void run() {
      System.out.println("B#run() is called.");
    }
  }

  public static class C implements A {
    public C() {
      System.out.println("C() is called.");
    }

    public void run() {
      System.out.println("C#run() is called.");
    }
  }

  public static class D {
    private final Set<A> as;

    @Inject
    public D(Set<A> as) {
      this.as = Preconditions.checkNotNull(as);
      System.out.println("D() is called.");
    }

    private void run() {
      System.out.println("D#run() is called.");
      for (A a : as) {
        a.run();
      }
    }
  }

  public static class SampleModule extends AbstractModule {
    @Override
    protected void configure() {
      Multibinder<A> multibinder = Multibinder.newSetBinder(binder(), A.class);
      multibinder.addBinding().to(B.class);
      multibinder.addBinding().to(C.class);
    }
  }

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SampleModule());
    D d = injector.getInstance(D.class);
    d.run();
  }
}

出力

B() is called.
C() is called.
D() is called.
D#run() is called.
B#run() is called.
C#run() is called.

Dのインスタンスを生成するように指示しました。Dのコンストラクタの引数にはSetが含まれていますので、injectorはこの生成ルールを探します。Module内でMultibinderでSetBinderが作らていますので、これに従ってBとCのインスタンスを生成してSetに入れてDに渡し、Dのインスタンスを生成します。

次回予告?

  • Web and Servlets Integration
  • Test with Mockito
  • GuiceBerry

あたりをやるかもしれません。

結合テスト向けGuice補助ライブラリGuiceBerry

GuiceBerryの目的と使い方が分かった気になったので備忘録として書いてみる。GuiceBerryはGuiceを使用しているコードツリー内で、JUnitTestNGを使用して結合テストを書いている場合、テスト内でのインスタンスの生成の補助をしてくれるライブラリっぽい。ニッチだなぁ・・・。

まず、GuiceBerryを使わない場合の結合テストの例。自分の作っているQMACloneから抜粋。

@RunWith(JUnit4.class)
public class ChatManagerTest {
  private ChatManager manager;

  @Before
  public void setUp() throws Exception {
    manager = Guice.createInjector(new QMACloneModule()).getInstance(ChatManager.class);
  }

  @Test
  public void testWriteRead() {
    PacketChatData expected = new PacketChatData();
    expected.body = "body";

    manager.write(expected, "1.2.3.4");

    PacketChatDataList list = manager.read(0);
    PacketChatData actual = list.list.get(list.list.size() - 1);

    assertEquals(expected.body, actual.body);
  }

  @Test
  public void testGetChatDataListWebSocket() {
    assertNotNull(manager.getChatDataListWebSocket());
  }
}

ここではChatManagerクラスを手えすとしようとしている。setUp()で毎回GuiceのInjectorを作成して、ChatManagerクラスを生成している。ちょっとかっこ悪い気がする。これをGuiceBerryを使って書き直すとこうなる。

public class QMACloneTestEnv extends AbstractModule {
  @Override
  protected void configure() {
    install(new GuiceBerryModule());
    install(new QMACloneModule());
  }
}

@RunWith(JUnit4.class)
public class ChatManagerTest {
  @Rule
  public final GuiceBerryRule rule = new GuiceBerryRule(QMACloneTestEnv.class);
  @Inject
  private ChatManager manager;

  @Test
  public void testWriteRead() {
    PacketChatData expected = new PacketChatData();
    expected.body = "body";

    manager.write(expected, "1.2.3.4");

    PacketChatDataList list = manager.read(0);
    PacketChatData actual = list.list.get(list.list.size() - 1);

    assertEquals(expected.body, actual.body);
  }

  @Test
  public void testGetChatDataListWebSocket() {
    assertNotNull(manager.getChatDataListWebSocket());
  }
}

GuiceBerry用のGuiceモジュール(Env)を定義し、GuiceBerryModuleとテストに使いたいモジュールをインストールする。次にテスト本体に@Rule付きでGuiceBerryRuleのフィールドを追加して、先ほど作ったEnvを指定する。最後にテストしたいクラスを@Inject付きでフィールドに追加する。テストが少しだけスッキリした。

サーバー監視ツール munin

C83で入手したThe Database Times Vol. 2で@zembutsu氏が紹介されていたサーバー監視ツール"munin"が気になったので試してみた。

munin-nodeがサーバーの情報を集めて、muninがvisualizeをするらしい。

Ubuntuには標準でmuninのパッケージが用意されているようなので、apt経由でインストール。mysqlの監視にはperlのCache::Cacheが必要になるらしいので同時にインストールしておく。

sudo aptitude install munin munin-node munin-plugins munin-plugins-java munin-plugins-openstack libcache-cache-perl ruby libxml-simple-perl

muninはプラグインを追加することで様々なプログラムの監視ができるようになるらしい。有効になっているプラグインと推奨プラグインのリストを見てみる。

sudo munin-node-configure -suggest

推奨プラグインを有効にするには以下のコマンドを叩く。手動でシンボリックリンクを貼って設定する方法もあるらしいが、こちらのほうが楽。

sudo munin-node-configure -shell | sudo sh

muninが正しく設定されているかどうかチェックスクリプトを走らせてみる。

munin-check

以下のコマンドでmunin-nodeが正しく走るか試すことができるらしい。

sudo -u munin munin-cron

WordPressのURL書き換えのためにmod_rewriteを使用しており、muninのcgiが正しくは知らない場合がある。設定ファイルに以下を適宜追加して、mod_rewriteを無効化しておく。

  <IfModule mod_rewrite.c>
    RewriteEngine Off
  </IfModule>

リンク

Apache2 + MPM Worker + mod_fastcgi + php5-fpm でWordPressを動かす

今までQMACloneはTomcat6に付属のHTTP Connectorを使用してhttpをサーブしていた。つい先日、httpのサーブはApacheを使用し、AJP Connector等でTomcatと繋げたほうが通信が安定するという都市伝説をネット上で見かけた。なんでもSocket通信のエラーハンドリングがTomcat6のJVMのものよりApacheのモノの方がしっかり作られているらしい。ということで早速やってみた。

ところが・・・、Apache2のMPMをPreforkのままにしたため、一つのhttpコネクションに対して一つのプロセスが作られ、最終的には約800のApacheプロセスが立ち上がり、あわやスワップアウトを起こしかけるという状態になってしまった。

これを防ぐためにMPMをWorkerに切り替えることにした。ところが・・・、MPM Workerではmod_phpが使用できないらしく、トップページのWordPressが動かなくなってしまった。色々調べたところmod_fastcgi + php5-fpmならMPM Workerでも動かすことができ、かつキャッシュが効くのでそこそこの速度が出るという情報を得た。

以下に備忘録として作業内容を残しておく。

作業内容

php5-fpmとmod_fastcgiをインストールして、php5-fpmを起動しておく。

% sudo aptitude install php5-fpm libapache2-mod-fastcgi
% sudo update-rc.d php5-fpm enable
% sudo service php5-fpm restart

必要となるモジュールを有効にする

% cd /etc/apache2/mods-enabled
% sudo ln -s ../mods-available/actions.conf
% sudo ln -s ../mods-available/actions.load
% sudo ln -s ../mods-available/rewrite.load

cgiを有効にする

% sudoedit /etc/apache2/sites-enabled/000-default
OptionsにExecCGIを追加
AllowOverrideをAllに変更

Ubuntuに含まれるphp5-fpmパッケージに合わせてfastcgiの設定をする。標準設定ではバイナリの場所は"/usr/sbin/php5-fpm"、Socketのパスは"/var/run/php5-fpm.sock"とのこと。

% sudoedit fastcgi.conf
<IfModule mod_fastcgi.c>
  FastCgiExternalServer /usr/sbin/php5-fpm -socket /var/run/php5-fpm.sock
  AddHandler php-fastcgi .php
  ScriptAlias /fcgi-bin/ /usr/sbin/
  Action php-fastcgi /fcgi-bin/php5-fpm
</IfModule>

この時点でApacheを再起動してWordPressが正しく動くことを確認する。

% sudo service apache2 restart

最後にMPM PreforkからMPM Workerに切り替える。

% sudo aptitude install apache2-mpm-worker

以上でApache2 + MPM Worker + mod_fastcgi + php5-fpmが動くようになった。
レスポンスは以下の通り。

% ab -n 10 http://kishibe.dyndns.tv/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking kishibe.dyndns.tv (be patient).....done


Server Software:        Apache/2.2.22
Server Hostname:        kishibe.dyndns.tv
Server Port:            80

Document Path:          /
Document Length:        11589 bytes

Concurrency Level:      1
Time taken for tests:   0.684 seconds
Complete requests:      10
Failed requests:        0
Write errors:           0
Total transferred:      119100 bytes
HTML transferred:       115890 bytes
Requests per second:    14.62 [#/sec] (mean)
Time per request:       68.418 [ms] (mean)
Time per request:       68.418 [ms] (mean, across all concurrent requests)
Transfer rate:          170.00 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        7    8   1.3      8      11
Processing:    48   60   8.5     61      72
Waiting:       47   59   8.4     59      70
Total:         56   68   8.3     69      80

Percentage of the requests served within a certain time (ms)
  50%     69
  66%     72
  75%     77
  80%     79
  90%     80
  95%     80
  98%     80
  99%     80
 100%     80 (longest request)

同時実行性や負荷耐久などは調べていないが、トップページにアクセスする人は少ないのでこれで良しとする。

botプログラムからFacebookページにAPI経由で投稿するためのアクセストークンを取得する

自分の作っているQMACloneにはFacebookのページに問題を投稿する機能があるのですが、トークンの期限が切れたために動かなくなっていました。
トークンの取得方法を調べ直しましたので備忘録として書いておきたいと思います。

短期間用(1〜2時間)アクセストークンの取得

以下のURLにアクセスして短期間用のアクセストークンを取得します。アクセストークンはリダイレクト先のURLのフラグメントの部分に書かれます。

https://www.facebook.com/dialog/oauth?
	client_id=[App ID/API Key]&
	redirect_uri=https://www.facebook.com/connect/login_success.html&
	response_type=token&
	scope=publish_stream,manage_pages

長期間用(60日)アクセストークンの取得

以下のURLにアクセスして短期間用のアクセストークンを長期間用のアクセストークンに交換します。

https://graph.facebook.com/oauth/access_token?
	client_id=[App ID/API Key]&
	client_secret=[アプリのシークレットキー]&
	grant_type=fb_exchange_token&
	fb_exchange_token=[短期間用アクセストークン]

ページ投稿用アクセストークンの取得

以下のURLにアクセスしてページ投稿用のアクセストークンを取得します。

https://graph.facebook.com/me/accounts?access_token=[長期間用アクセストークン]

以上で60日間くらい使えるアクセストークンが手に入ります。このアクセストークンを使用してGraph API経由でページに投稿することができます。

リンク