<< < 1 2 3 4 5 6 7 8 9 10 > >>

WEBを支える技術読了

HTTP、GET、POST、URI、ステータスコード、ステートレス。
Webの仕事にに関わっていて、これらのキーワードと格闘した事のない人はいないでしょう。
ステータスコードをGoogleで検索したり、ブラウザのプラグインでHTTPヘッダーを見ながらRFCを読み解いてみたりした経験は多くの人があるでしょう。
その時々に分かった気になっても、これらの技術を体系的に学ぶ機会は少ないのではないでしょうか。

そんなWEBにまつわる技術を体系的に学べる本が、「WEBを支える技術」です。
WEBを支える技術の歴史から始まり、いかにHTTPがシンプルに設計され、それゆえに広まっているかをひも解いていきます。
その過程でHTTP周辺の技術を学んでいく事ができます。

かつてWicketに始めて触れて、ステートレス/ステートフルを意識したとき、これらの概念を理解し人に説明するのに苦労したものです。
その時にお世話になった解説も載っていました。ステートフル/ステートレス の説明をはじめ、わかりやすい解説がつまっています。

この本の主役はRESTでもあります。
HTTP周辺の技術を解説するとともに、RESTがHTTPにいかにマッチした技術であるかという話に続いていきます。
RESTとSOAPの話は、個人的にはApache AxisでSOAPと格闘した日々を思いだしたりもしました。

最後のRESTを用いた場合のWebAPIの設計手法、考え方は非常に参考になります。
基盤技術の設計意図を注意深く汲み取り、下位レイヤーの思想を壊さない形で上位レイヤーを設計していく過程は感動しました。

本書でWebにまつわる技術を体系的に学んだ後も、充実した索引やステータスコード、HTTPヘッダーの一覧がとても役に立ちます。
これからずっとそばに置いておきたい本です。


How Tomcat Works を購入した


私はServletと出会ったのは3年ほど前と、比較的日が浅いです。
生のServletと格闘した日々はほとんどなく、Struts等のWebフレームワークを通してしかServletを知りません。
むしろServlet APIは意識させないことがWebフレームワークの特性なので、Servletはあまり知らない部類の人間でしょう。

そんなわけで前から欲しかった How Tomcat Works を購入しました。
現在は eBookのみこちらから購入できるようです。
既に読了して解説されている方もいます。
How Tomcat Works その1 tokobayashiの日記:

この本は最小限のHTTPサーバーからTomcatにまで育てていく過程を解説している本です。
対応しているTomcatのバージョンはTomcat4、Tomcat5です。
Tomcat6が出ている今となってはやや古いのですが、私には十分かと思って購入しました。
ちなみに、本書のIntroにある想定読者は以下の通りです。

  • Servlet/JSPをTomcat上で使用する人、興味のある人
  • Tomcatの開発に参加したい人
  • Tomcatのような大きなシステム開発を学びたい人
  • Tomcatの設定やカスタマイズを行いたい人

一番目の理由はServlet/JSPの動きに興味があったからですが、Tomcatのような複雑で大きなシステムの成り立ちも勉強出来そうで、これから楽しみです。

転職 & 東京引越し

etc 

約7年間お世話になった会社を辞めて、転職する事になりました。
同時に東京へ引越しすることになります。

私はシステム開発未経験でこの会社に転職してきました。
大学も情報系ではないのでプログラミングも未経験でした。
どんな事でも最初のハードルを越えることは本当に大変だと思います。

最初に参加したプロジェクトは、ウォーターフォール型の開発だったのですが、各フェーズを丁寧に行うプロジェクトでした。
(そしてきっちり利益もあげていました!)
今思えば、システム開発での最初のハードルを越える上で、一番良いプロジェクトに参加させてもらったと思います。
おかげで色々教えてもらいながら、システム開発がどういうものかを学ぶことが出来ました。
そして転職に際しても、本当に暖かく送り出してもらい感謝しています。

さて、今月からは有休消化を経て新天地になります。
これまでは業務システムを手がけてきましたが、今度からはコンシューマ向けのWebサイト開発です。
Webと言う点はこれまでも出掛けてきたのですが、コンシューマ向けと言う点は未経験です。
これからも勉強することがいっぱいです。

しかも職場は3年前に転勤して以来の東京です。
3年前と比べて、勉強会も随分ブームになってますし積極的に参加して行きたいと思います。

今更ながらlibataを導入した

久々に我が家のデスクトップ Gentoo マシンをいじったので、メモしときます。
我が家のデスクトップは Raid1 構成の IDE ハードディスクをくっつけてます。
マザーボードは Albatron PX915G。
Raidコントローラーは IT8212 です。

で、新しいkernelを再構築して起動すると、ハードディスクへのアクセスが恐ろしく遅くなってしまいました。
dmesg の出力を見ると、"dma not selected" と出ています。
dmaが無効になってしまっているようです。
ちなみにmake menuconfigでkernelの再構築時にコンパイルしたドライバーは以下の通りです。
 Device Drivers  --->
   ATA/ATAPI/MFM/RLL support  --->
         IT821X IDE support

同じような事で悩んでいる人もいるようで、kernelのメーリングリストでも数年前に話題になっていたようです。
Re: IT821x: no DMA since 2.6.21

上記で組み込んだドライバーはメンテナンスされていないのでlibataを使うのが良いとか書いてあります。
libataはSerialなハードディスクだけの話かと思っていたのですが、parallelなハードディスクでも利用できるんですね。

make menuconfigで上記のドライバーをはずして、下記のドライバーを組み込みます。
 Device Drivers  --->  
  Serial ATA (prod) and Parallel ATA (experimental) drivers
    IT8211/2 PATA support   

我が家のPCでは IT8212経由のハードディスクは hde として認識されていました。
libataを使うことで、sda に変わったことが今回の変更の副作用です。
長らくハードディスクデバイスと言えばhd〜だったので、暫く違和感がありそう。
(OpenBSDのwd〜も暫く慣れなかったけど)

ハードディスクデバイスが /dev/sda に変わったので /boot/grub/grub.conf と /etc/fstab も忘れずに変更しましょう。

Wicket本が届きました!


先日発売された日本初のWicket本が届きました。
著者の矢野さん、お疲れさまでした。

レビューに参加したということで、名前も載せていただきました。
技術本のレビューという、貴重な体験をさせてもらった上、本を頂けるとは感謝しきりです。

今夜は紅茶で祝杯です。


Wicketでパネルのユニットテスト

WicketにはWicket Testerと呼ばれるテストツールが付属しています。
サーブレットコンテナを起動せずにテストが出来る優れものですが、
今回、Wicketのパネルをテストしたときに、ちょっと癖があったのでメモしておきます。

Wicketのパネルとはいろんなコンポーネントとマークアップを一まとめにした部品です。
標準だとページャーを表示する部品、PagingNavigator なんかがあります。
パネルを使いまわすことができれば、コンポーネントの部品化は非常に進むと思います。
「部品化」が机上の空論にならないためにも、パネルとテストはどんどん活用したいですね。

今回は単純に現在時刻を表示するパネルを作ってみました。


テスト対象のパネルにはラベルが2つあるだけの単純なものです。
リポジトリにも置いてあります。
 public class TimerPanel extends Panel {
     /** serialVersionUID */
     private static final long serialVersionUID = 1L;

     public TimerPanel(String id) {
         super(id);
         add(new Label("label", getString("label")));

         IModel<String> model = new AbstractReadOnlyModel<String> () {
             /** serialVersionUID */
             private static final long serialVersionUID = 1L;

             @Override
             public String getObject() {
                 DateFormat format = DateFormat.getTimeInstance();
                 return format.format(new Date());
             }
         };
         Label timerLabel = new Label("timer", model);
         timerLabel.add(new AjaxSelfUpdatingTimerBehavior(Duration.ONE_SECOND));
         add(timerLabel);
     }
 }

これをテストするコードは下記になります。
これもリポジトリにも置いてあります。
 public class TestTimerPanel {
     private WicketTester tester;
     @BeforeMethod public void setup() {
         tester = new WicketTester();
     }

     @Test public void initDisplay() {
         tester.startPanel(new TestPanelSource() {
             private static final long serialVersionUID = 1L;
             public Panel getTestPanel(String panelId) {
                 return new TimerPanel(panelId);
             }
         });
         tester.assertComponent(DummyPanelPage.TEST_PANEL_ID, TimerPanel.class);
         tester.assertComponent(DummyPanelPage.TEST_PANEL_ID + ":label", Label.class);
         tester.assertLabel(DummyPanelPage.TEST_PANEL_ID + ":label", "現在の時刻");
         tester.assertComponent(DummyPanelPage.TEST_PANEL_ID + ":timer", Label.class);
     }
 }
startPanelメソッドにTestPanelSourceのインスタンスを渡します。
このクラスのgetTestPanelメソッドの戻り値として、テスト対象クラスのインスタンスを返すようにします。
パネルのwicket:idはDummyPanelPage.TEST_PANEL_IDとなります。このidを前提としてテストコードを書きます。

パネルのwicket:idがDummyPanelPage.TEST_PANEL_IDで固定になっている事を記載しているドキュメントは見つけられませんでした。
この点はドキュメントに書くべき部分でしょうね。

OpenBSD を PxeBoot でインストール

久々の更新になっちゃいました。
ノートPCが手に入ったのでOpenBSDをインストールすることにしました。
もらってから発覚したのですが、CDROMが壊れているマシンでした。インストールCDからインストールできない訳です。
それならと言うことで、以前から興味のあったPxeBootでインストールしてみることにしました。
構成としてはGentooマシンでdhcpdとtftpをホストしました。

やってみた感想としては、インストールするのにISOを焼く作業がなくなるし、設定もそんなに難しくないのでおすすめです。
インストール作業なんかはそんなに頻繁に行う作業ではないでしょうが、OpenBSDは半年に一回Updateがありますし。

参考にさせてもらったのは以下のドキュメント。
The Gentoo Linux alternative installation method HOWTO
OpenBSD FAQ 6.10 How do I boot using PXE? (i386, amd64)

ハマった所は、dhcpd.confで next-server を指定しないといけなかった所と、
PxeBoot環境では Grub は OpenBSD をうまく起動できない所でした。
dhcpdについてですが、設定ファイルでnext-serverを指定しないとサーバ機のIPアドレスを認識してくれませんでした。
また、OpenBSDをpxegrubで起動しようとしたのですが、起動時に下記のようなエラーがでて先に進めませんでした。
 panic: /boot too old: upgrade!
Grubの設定を下記のようにすればよいとの情報があったのですが、現状には当てはまらないようです。
 title=Diskless OpenBSD
 root (nd)
 kernel --type=openbsd /bsd.rd

うまくいった手順についてですが、まずはGentoo機にdhcpdとtftpをインストールします。
 $ sudo emerge dhcp
 $ sudo emerge tftp-hpa

Bootファイル設置用のディレクトリを作成します。
 $ sudo mkdir -p /diskless
まず bsd.rd を取得して、 /diskless 配下に置きます。
次に pxeboot を取得して、 /diskless 配下に置きます。

最終的な /etc/dhcp/dhcpd.conf の内容は以下のようになりました。
 ddns-update-style none ;
 option routers 192.168.1.10;
 option subnet-mask 255.255.255.0;
 option broadcast-address 192.168.1.255;
 option domain-name-servers 218.176.253.97;
 next-server 192.168.1.12;
 
 default-lease-time 120;
 max-lease-time 120;
 
 subnet 192.168.1.0 netmask 255.255.255.0 {
     # クライアントにロードさせるブートファイルを指定
     filename "pxeboot";
     range 192.168.1.36 192.168.1.250;
 }
tftpの起動オプションは以下のようになります。
/etc/conf.d/in.tftp
 INTFTPD_PATH="/diskless"
 INTFTPD_USER="nobody"
 INTFTPD_OPTS="-u ${INTFTPD_USER} -l -vvvvvv -p -c -s ${INTFTPD_PATH}"

クライアントをネットワークブートし、起動後に boot> プロンプトが表示されるので、 bsd.rd と入力します。
後は通常のインストールと変わりません。

jMockitで依存クラスをモックオブジェクトに差し替える


これまでモックオブジェクトを作成する EasyMock や jMock を見てきました。
モックオブジェクトをパラメータとしてテスト対象に渡せる場合は、問題ないのですが、テスト対象のクラスの中で new されていると、一気にテストは困難になります。

jMockit は 実行時にクラスをモックオブジェクトへ差し替える機能を提供するツールです。
バイトコード操作の為のJVMへのコマンドライン引数、 -javaagent を用いてクラスの差し替えを行います。

Eclipseの場合はJREの設定を変える必要があります。JVMの引数に -javaagent:jmockit.jar を追加します。
設定自体は簡単なのですが、この点が壁になるかもしれません。

テスト対象は以下の通りです。MockSampleクラスのコンストラクタでサービスクラスを初期化しています。
この様に、テスト対象のクラス内で new されたクラスはモックオブジェクトを適用するのが困難です。
jMockitはこういったクラスをモックオブジェクトに差し替える機能を提供しています。
 public class MockSample {
     private ServiceImpl serviceImpl;
     public MockSample() {
         this.serviceImpl = new ServiceImpl();
     }

     public String execute() {
         return serviceImpl.echo();
     }

     public static class ServiceImpl {
         public String echo() {
             return "hoge";
         }
     }
 }
テストクラスは以下の通りです。差し替えられたMockServiceImplが実行されます。
 import mockit.Mockit;
 import org.testng.annotations.Test;
 import test.MockSample.ServiceImpl;
 import static org.testng.Assert.*;

 public class TestJMockitSample {
     @Test public void jmockTest() {
               // クラスの差し替え
         Mockit.redefineMethods(ServiceImpl.class, MockServiceImpl.class);
         MockSample sample = new MockSample();
         assertEquals(sample.execute(), "Mocked!!!");
     }
     public static class MockServiceImpl {
         public String echo() {
             return "Mocked!!!";
         }
     }
 }
コードはかなり簡単ですね。
JVMの引数をいじるため、Eclipseを設定したり、Mavenを使っている場合も一工夫がいる点が面倒ですが、
一度設定してしまえばかなり完結にテストをかける強力なツールです。

Mockソリューションを試す。 EasyMock と jMock の比較


ここまでで EasyMock と jMock の簡単な使い方を見てきました。
まだまだ使いこなしてはいないのですが、ここまでの知識をまとめておきたいと思います。

まず、Mock と Stub の違いについて。
EasyMock とか jMock に触れる前は、Mock と Stub が自分の中で明確に区別できていませんでした。
Wikipediaによると両者は異なるもので、Mock は Stub と比較して正しく利用されているかを検証する機能が付加されているとの事です。
確かに今まで Stub と認識していたものは、「呼ばれたときに値を返す」部分を代用する様なものでした。
しかし EasyMock や jMock はメソッドが想定されたパラメータ、順序で呼ばれるかを検証することができました。
この点で Stub とは 区別される物のようです。Mock が Stub の一種と言うことですね。

さて、 EasyMock、jMock ともに機能に大きな違いはなさそうです。
  • interface に対する Mockの作成
  • concrete class、abstract class に対する Mockの作成
  • メソッドが呼ばれているかを検査
  • メソッドが想定するパラメータで呼ばれているかを検査
  • メソッドが想定する順番で呼ばれているかを検査
  • メソッドの戻り値を偽造

また、基本的な使い方の比較をコードでしてみます。
まず、Mock を作る部分。
 //EasyMock
 mock = createMock(IService.class);

 // jMock
 context = new Mockery();
 mock = context.mock(IService.class);
次に想定される結果を検査する部分。
EasyMockの場合。
 // モックオブジェクトに対して想定される呼び出しを記述します。
 mock.echo();

 // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
 replay(mock);
 sample.execute();
 verify(mock);
jMockの場合
 // モックオブジェクトに対して想定される呼び出しを記述します。
 context.checking(new Expectations() {
    {oneOf(mock).echo();
     }});

 // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
 sample.execute();
 context.assertIsSatisfied();
EasyMockは想定される呼び出しを記述し、replay〜verifyメソッドで囲われた部分を検査します。
jMockは想定される呼び出しを context.checkingメソッド内のExpectationsクラス初期化子に記述し、context.checking〜context.assertIsSatisfiedメソッドで囲われた部分を検査します。
個人的には jMock の書き方の方が決められた場所が明確で好きなのですが、好みの問題かもしれません。

他にも "Mock" という文字列を含むソリューションがありましたが、試すまでには至りませんでした。
まず rMock なのですが、対応していないのかもしれないのですが TestNGにからの利用方法がわからなかったので、試しませんでした。
ただ、ドキュメントを読む限り BDD的な要素を絡めているようでおもしろそうではあります。
あと、ClassMock と呼ばれるものもありましたが、こちらは Mock と呼ばれるには必要な、正しく呼ばれているかを検証する機能がなく、簡単にクラスを作るための仕組みを提供している様です。
Mockとしての機能は他のソリューションに委ねている様で、ドキュメントにも jMock と連携した場合の例が載せられています。

Mock の使いどころとして浮かぶのは Servlet とかのテストなのではないでしょうか。
つまり擬似的な実装クラスを作るのが面倒な部分です。
Wicket だと WicketFilter クラス何かは Mock を使ったテストなんかは有効なのではと思います。

Mockソリューションを試す。 EasyMock + classextension


jMockではインターフェース以外のクラスについてモックオブジェクトを作れましたが、EasyMockについてもEasyMock Class Extension を導入することで実現できます。
まずEasyMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
 <dependency>
    <groupId>org.easymock</groupId>
    <artifactId>easymockclassextension</artifactId>
    <version>2.4</version>
 </dependency>

以前、インターフェースに対してモックオブジェクトを作成する部分はこんな感じでした。
 // IServiceクラスのモックを作成します。
 mock = createMock(IService.class);
EasyMock Class Extensionを導入する場合は、classextensionパッケージ下にある上記と同名のメソッドを使用するようにします。
具体的には、org.easymock.EasyMock.createMock から org.easymock.classextension.EasyMock.createMockメソッドを使用するようにimport文を変更します。
他にも replyメソッドやverifyメソッドもclassextensionパッケージ下のものを使うことになるので、インポート文に下記を加えておくとよいでしょう。
 import static org.easymock.classextension.EasyMock.*;

これでjMockと同様に、インターフェース以外のモックオブジェクトの作成が可能になります。