« 2008年2月 | メイン | 2008年4月 »

2008年3月27日

JavaでPowerPointファイルを読み込む
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。中村です。

JavaからMicrosoft Officeのファイルを読み書きするのには、100%Javaで実装されているApache POIを利用することができます。

昔はPowerPointファイルを読み書きするAPI群であるHSLFはなかったので、POIで検索するとEXCELファイルを読み書きするHSSFが中心に紹介されているようです。

今回はHSLFを使ってPowerPointファイルの中にある文字列を出力する簡単なサンプルを書いてみました。

package net.unoh.ppt2text;

import java.io.FileNotFoundException;
import java.io.IOException;

import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.model.Shape;
import org.apache.poi.hslf.model.Slide;
import org.apache.poi.hslf.model.TextBox;
import org.apache.poi.hslf.usermodel.SlideShow;

public class PPT2Text {

    public static void main(String[] args) {

        if (args.length != 1) {
            printUsage();
            System.exit(1);
        }

        try {

            SlideShow slideShow = new SlideShow(new HSLFSlideShow(args[0]));
            Slide[] slides = slideShow.getSlides();

            for (int i = 0; i < slides.length; i++) {

                Shape[] shapes = slides[i].getShapes();

                for (int j = 0; j < shapes.length; j++) {

                    if (shapes[j] instanceof TextBox) {
                        TextBox shape = (TextBox)shapes[j];
                        String text = shape.getText();
                        if (text != null) {
                            System.out.println(text);
                        }
                    }

                }
            
            }

        } catch (FileNotFoundException e) {
            System.err.println("File not found");
        } catch (IOException e) {
            System.err.println("IO error");
        }

    }
    
    public static void printUsage() {
        System.out.println("Usage: ppt2text filename");
    }

}

POIのクラスファイルをダウンロードページから取得してビルドしています。

これで例えばLinuxのコンソール上からPowerPointファイルを読むというニッチなことも、以下のようにjarファイルを実行するだけで可能です。

ppt2text
ppt2text posted by (C)フォト蔵

Busy Developers' Guide to HSLF drawing layerにサンプルコードがまとまっていて参考になります。

Microsoft Officeのファイルを扱う実装はJava以外にもありますが、POIも良い選択肢の一つになるのではないでしょうか。

2008年3月25日

Amazonの商品個別ページのURLからASINを取得する
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

isogawaです。

題名のままのコードを書く用事があったので、できたものを晒します。とはいえ、ググれば多くの先達が既にさまざまなコードを書いておられ、目新しいものではないんですが。ついでなんで、大昔の「ISBN=~」を含めて、自分の知ってるURLのパターンを洗いなおしてみました(それでも、まだ他のパターンがありそうな気がしますが…)。

例えば、「The Elements of Style」という書籍の個別商品URLとして思いつくものには、以下のようなパターンがあります(細かいバリエーションは適宜省略)。

スキームがhttpの場合とhttpsの場合、ホストの「www.」の有無、及び日本のサイトについては「.co.jp」と「.jp」の両ドメイン名で、表示内容に違いは見られないようです。

なお、ASIN以降スラッシュに続けて、アソシエイトIDを初めとした各種パラメータが付与されますが、必ずASIN以降であるようなので、ここでは無視します。

というわけでコードですが。以下はJavaScriptですが、やってることはパスをバラして正規表現で評価してるだけなので、他の言語にもサクっと置き換えられるかなと。

/**
 * Retrieves ASIN value from the given Amazon product page URL.
 *
 * @param  string   url   Amazon product page URL
 * @return string         ASIN value contained in the given URL
 *                        Returns empty string if no match
 */
function getAsinFromUrl(url) {
  if (!/^https?:\/\/(?:www\.)?amazon\.(?:com|ca|co\.uk|de|co\.jp|jp|fr|cn)\/(.+)$/.test(url)) {
    return '';
  }
  var path = RegExp.$1;
  var paths = path.split('/');
  if (paths[0].toLowerCase() == 'exec') {
    paths = paths.slice(1);
  }
  if (paths.length <= 1 || paths[1] == '') {
    return '';
  }
  switch (paths[0]) {
  case 'OBIDOS':
  case 'obidos':
  case 'o':
    /**
     * May match the following examples:
     * - http://www.amazon.co.jp/exec/obidos/ASIN/020530902X
     * - http://www.amazon.co.jp/o/ASIN/020530902X
     */
    if (paths[1].toLowerCase() == 'asin' && paths.length >= 3 && paths[2] != '') {
      return paths[2];
    /**
     * May match the following examples:
     * - http://www.amazon.co.jp/exec/obidos/ISBN=020530902X
     * - http://www.amazon.co.jp/exec/obidos/ISBN%3D020530902X
     * - http://www.amazon.co.jp/o/ISBN=020530902X
     */
    } else if (/^isbn(?:=|%3d)(.+)$/i.test(paths[1])) {
      return RegExp.$1;
    /**
     * May match the following examples:
     * - http://www.amazon.co.jp/exec/obidos/tg/detail/-/020530902X
     * - http://www.amazon.co.jp/exec/obidos/tg/detail/-/Elements-Style/020530902X
     * - http://www.amazon.co.jp/o/tg/detail/-/020530902X
     * - http://www.amazon.co.jp/o/tg/detail/-/Elements-Style/020530902X
     */
    } else if (paths[1].toLowerCase() == 'tg'
      && paths.length >= 5
      && paths[2].toLowerCase() == 'detail'
      && paths[3] == '-') {
      if (/^[0-9a-zA-Z]{10}$/.test(paths[4])) {
        return paths[4];
      } else if (paths.length >= 6 && paths[5] != '') {
        return paths[5];
      }
    }
    break;
  /**
   * May match the following examples:
   * - http://www.amazon.co.jp/gp/product/020530902X
   * - http://www.amazon.co.jp/gp/product/product-description/020530902X
   */
  case 'gp':
    if (/^gp\/product(?:\/product-description)?\/([0-9a-zA-Z]{10})/.test(path)) {
      return RegExp.$1;
    }
    break;
  /**
   * May match the following examples:
   * - http://www.amazon.co.jp/dp/020530902X
   * - http://www.amazon.co.jp/Elements-Style/dp/020530902X
   * - http://www.amazon.co.jp/Elements-Style/dp/product-description/020530902X
   */
  default:
    if (/\/?dp(?:\/product-description)?\/([0-9a-zA-Z]{10})/.test(path)) {
      return RegExp.$1;
    }
  }
  return '';
}

なお、個別商品ページを表示した状態でブックマークレットとかでASINを拾うとか、ページのDOMが利用できる状況なら、URLをいぢくるよりdocument.getElementById('ASIN')のほうが確実なのは、これもググればさまざまなに述べられている通りなので、念のため。

2008年3月21日

Apache MPM の基礎をしっかりと理解しよう!
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

naoya です。最近、とうとう花粉症の季節に入ったので、生まれて初めて空気清浄機を購入しました。

さて、今日は Apache HTTP サーバの MPM (Multi Processing Module) について解説したいと思います。普段、ウェブサーバを構築するとき、Apache HTTP サーバを使うことが多いと思いますが、Apache HTTP サーバを使う上で MPM について理解しておくことは大事です。

この MPM ですが、Apache HTTP サーバではリクエストを処理する部分のことをさします。MPM は、Apache HTTP サーバ 2.0 系から採用されています。Apache HTTP サーバの MPM には、次の種類があります。

  • prefork
  • worker
  • perchild
  • winnt

それぞれの MPM について解説します。まずは、一番代表的な prefork MPM について解説します。prefork MPM の特徴は、次のとおりです。

  • Apache HTTP サーバ 1.3 系以前のモデル
  • 1 つのリクエストに対して、1 プロセスを割り当てる
  • 負荷に応じて、子プロセスを増減させる

prefork MPM は、次に解説する worker MPM と同じくらいよく使われる MPM です。PHP で使うときは、PHPで書かれたwebサービスを高速化する にもあるとおり、prefork MPM を使う必要があります。PHP を使うとき、worker MPM を使うことは推奨されていないので注意してください。

prefork MPM のデフォルト設定は、次のようになっています。prefork MPM では、ServerLimit と MaxClients は同じ値を設定します。



StartServers 8
MinSpareServers 5
MaxSpareServers 20
ServerLimit 256
MaxClients 256
MaxRequestsPerChild 4000

関連するディレクティブの説明は、次のとおりです。

  • StartServers: 最初に起動する子プロセスの数
  • MinSpareServers: 待機する子プロセスの最小数
  • MaxSpareServers: 待機する子プロセスの最大数
  • MaxClients: 生成する子プロセスの最大数
  • MaxRequestsPerChild: それぞれの子プロセスが扱うリクエスト数の制限数

フォト蔵 のウェブサーバも prefork MPM を使っています。フォト蔵のウェブサーバの設定は、次のとおりです。



StartServers 8
MinSpareServers 5
MaxSpareServers 30
ServerLimit 300
MaxClients 300
MaxRequestsPerChild 50

MaxRequestsPerChild の値が小さいのが特徴だと思います。MaxRequestsPerChild を小さくしている理由は、PHP を使っているとかなりメモリを消費するため、頻繁に子プロセスを生成しなおしたいためです。

prefork MPM を使う場合、大量のアクセスを捌きたい場合には MaxClients の値を増やせばいいのですが、あまり増やしすぎてしまうとメモリ不足などになることが多いので、MaxClients を増やすときは ab(Apache Bench) コマンドなどを使って負荷テストを粉って、どのくらいまで設定できるのか見極める必要があります。


次に workder MPM について解説します。worker MPM の特徴は、次のとおりです。

  • スレッド対応型
  • リクエスト処理は、スレッドが対応する
  • 負荷に応じて、子プロセス数を増減させる
  • 1 子プロセスあたりのスレッド数は固定になっている

worker MPM は、子プロセス数を増減させるというところは prefork MPM と似ていますが、リクエスト処理をスレッドが対応するというところが大きく違っています。worker MPM は子プロセスを生成したとき、リクエスト処理をするスレッドを最初にすべて生成するので、新しいリクエストがきたとき、prefork MPM のように子プロセスの生成を待つ必要がないので prefork MPM よりパフォーマンスがいいです。(*1)

worker MPM のデフォルト設定は、次のようになっています。



StartServers 2
MaxClients 150
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0

関連するディレクティブの説明は、次のとおりです。

  • StartServers: 最初に起動する子プロセスの数
  • MinSpareThreads: アイドル状態のスレッド最小数
  • MaxSpareThreads: アイドル状態のスレッド最大数
  • MaxClients: 生成する子プロセスの最大数
  • ThreadsPerChild: 子プロセスそれぞれに生成されるスレッド数
  • MaxRequestsPerChild: それぞれの子プロセスが扱うリクエスト数の制限数


次に perchild MPM について解説します。perchild MPM の特徴は、次のとおりです。



  • スレッド対応型

  • リクエスト処理は、スレッドが対応する

  • 負荷に応じて、スレッド数を増減させる

  • 子プロセスの数は固定になっている

perchild MPM は、worker MPM の反対のような特徴をもっており、スレッド数だけが増減する仕組みとなっています。そのため、子プロセスの生成を待つ必要がなく、スレッドも必要なだけになるため、もっともリソースを有効に使うことができる MPM といえます。ただし、prechild MPM は不安定な面もあるという情報があるので、本番環境で使う前には入念なテストが必要です。


最後に winnt MPM について解説します。winnt MPM の特徴は、次のとおりです。



  • スレッド対応型

  • リクエスト処理は、スレッドが対応する

  • Windows NT に特化したスレッドモデル

winnt MPM は、その名の通り Windows NT 向けに特化した MPM です。Windows 上の Apache HTTP サーバでは、winnt MPM がデフォルトで使われます。


というわけで、今回は Apache HTTP サーバの MPM について解説しました。普段から使っているオープンソースでも内部の仕組みを理解しておくと、何かのトラブルに遭遇したときに助けのヒントになることがあるので、仕組みを理解するということを大事にしてきたいと思います。

(*1) 追記:prefork MPM より worker MPM の方がパフォーマンスが高い理由は、リクエスト処理をするために子プロセスを生成するではなくスレッドを使うところにありますが、プロセスとスレッドの生成コストの違いについては別の機会にエントリしたいと思います。

2008年3月18日

プロセスの監視を行う デーモン monit
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは satoです。 monitは プロセスの監視を行うデーモンです。 条件とそれに伴うアクションを指定することができます。

条件とは例えば以下のようなものがあります

  • プロセスが起動していなかったら
  • 特定のプロセスのメモリの使用量が あるサイズを超えたら
  • 特定のプロセスのCPUの使用率が 50%を超えている状態が 10分続いたら
  • 特定のポートに接続できなくなったら など

アクションには以下のような物があります

  • 起動、再起動する
  • アラートメールを送信する
  • ユーザスクリプトを実行する など

これらを組み合わせて、プロセスの監視を行います。とくにユーザが作成したプログラムの監視などに効果を発揮します。インストールは RedHat系なら

yum install monit

で入ります。(CentOSや商用のRedHatはrpmforgeをリポジトリとして追加する必要があります) 主な設定ファイルは

  • /etc/monit/monit.conf logの場所やメールの送信先、監視の間隔などを設定
  • /etc/monit/monit.d アプリケーション別の設定ファイルを置きます

になります。簡易的な postfixの監視ファイル postfix.conf は以下のような感じになります。

 check process postfix with pidfile /var/run/postmaster.pid
   start program = "/etc/init.d/postfix start"
   stop  program = "/etc/init.d/postfix stop"
   if failed port 25 protocol smtp then restart

pidの場所を指定して、 startと stopを定義します。monitの起動は

/etc/init.d/monit start 

で行います。

試しにpostfixを止めてみてください。どうでしょうか。ちゃんとリスタートしたでしょうか。
もちろん Railsで作成した mongrelのプロセスなども監視できるので、様々な場面で活躍できそうですよね。
公式サイトはかなり充実したマニュアルとサンプルがあるので、ぜひ参照してみてください。


2008年3月 2日

ドキュメントを楽しく楽に書くために
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

yukiです。
今回は主にアジャイル開発におけるコード以外のドキュメントについてお話しようと思います。

「包括的なドキュメントよりも、動作するソフトウェア」という言葉がありますが、最近これを「ドキュメントはいらない」もしくは「極力書くべきではない」と誤解され、ドキュメントが軽視されがちだと感じます。
確かに、ドキュメントは面倒です。コードを書いている方が何万倍も楽しいですし。私も出来れば面倒なドキュメントは書きたくはありません。

「コード見ればわかるでしょ」とか「コードがドキュメントだ」と言い切るのは簡単ですし、事実そういった事もあるでしょう。というより、むしろその方が多いかもしれません。ですがコードをドキュメントとして扱うには、必要十分となるようなものでなくてはなりません。

そうではない(見てもわからんような)コードが多いのも事実ですし、それにドキュメントはまったく必要ないかといえばそうではない場合も多いですし、であればいっそ「(詳細な)ドキュメントを書かずとも済ませる」こと、「ドキュメントを書くことを楽しくする」ことを目指してみませんか?

問題点を把握する

なぜドキュメント軽視の状況になるのか、少し考えてみました。

なぜドキュメントを書かないのか
ドキュメントを書く時間がない
なぜ時間がないのか
書く時間が工数に計算されていないから
なぜ工数計算されないのか
成果として評価対象ではないから
なぜ評価対象ではないのか
直接パフォーマンスに影響を及ぼすものでないから
なぜパフォーマンスに影響を及ぼさないと認識されるのか
保守するコストを考慮しないから
なぜ保守コストを考慮しないのか?
問題なく運用されて然るべきという危機意識(リスク)の欠如
なぜ危機意識が欠如しているのか?
もし○○したらという認識が不足している

まだ突き詰められるとは思いますし、まったく違った結果になったり、特殊な事情でこの限りではない場合もあると思いますが、このように認識の誤差から来るものが多いように思います。保守コストは無視できるものではないですし、運用後のリスクを考えると十分考慮して然るべきもののはずです。

評価体制を作る

ご存知のように、コスト削減はダイレクトに売上の向上に直結するものですから、もっと評価されてもよい箇所だと思うわけです。上記のような保守コストと運用リスクを天秤にかけた場合の意識の欠如や、属人的な開発を行った場合のリスクを軽視しているということが問題のようです。
リスクについてはあらゆる場面を想定し、実際に「そうなった」場合のコストを数字にして認識あわせを行い、ドキュメントを書く事そのものを評価し奨励する体制作りが重要です。

人的リソースを削減する

では、どうしたらコストは削減できるでしょうか。まず最もコストがかかることといえば人件費であり、つまり我々エンジニアの時間です。

なのでまず最初に「人が書く」ことそのものが削減できないかを検討してみましょう。ドキュメント作成においてもっともコストがかかる部分はどこか、なぜコストがかかるのかを徹底して突き詰めると、また違った形の問題が見えてくる事もあると思います。

この場合では、どうやったら最も人的リソースを消費せず自動化できるのかを考えるます。これはドキュメント生成の自動化ツールを作ってもいいでしょうし、そもそも生成せずともすむような形を考えるのもよいでしょう。

命名にもっと時間をかける

ドキュメントが古くなっていることもありますし、なんだかんだいっても、やはり最も信頼のおけるものは実際のコードです。
コードを読み込んで理解しようとする場合には、「3日たったら別人のコード」というようになかなか理解しづらいものですし、複雑になればなるほどそのコストは膨大なものになっていきます。このとき、よりこのコストを削減するにはやはり命名です。ざっと見ても理解できるような見通しのよい設計ももちろんですが、後々のクラスやメソッドの変更や追加が起こっても通用するような命名を心がけましょう。私自身が気をつけている点は、

  • 重複しない
  • 誤解しない
  • 見ただけで内容がわかる

です。
よい命名は、ドキュメント生成ツールで作ったドキュメントであっても、とてもわかりやすいものになるはずです。

コミットログで説明

コミットログでは、誰が(Who)、何を(What)、いつ(When)、何処で(Where)、どのように(How)は付属情報でわかるのですが、なぜそうしたのか(Why)は人が書かない限りわかりませんので、何故こうしたのか、を必ず書くようにしましょう。

かいつまんでお話してきましたが、いかがでしょうか。
少しでもドキュメントを書く習慣が楽しく、楽になれば幸いです。


About 2008年3月

2008年3月にブログ「ウノウラボ by Zynga Japan」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2008年2月です。

次のアーカイブは2008年4月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Zynga Japan