kagamihogeの日記

kagamihogeの日記です。

Commons FileUpload 1.0 にやられた

今時 Struts 1.1 とか Commons FileUpload 1.0 とかビミョウな代物使ってる人は少なそうそうだけど…ちょっとはまったのでそれについてメモ。実際、知ってる人には有名な挙動(バグ?)なんだそうだけど。

どんな動作かというと、マルチパートアクセスのリクエストを投げるときに大量のフォームパラメータを伴っていると OutOfMemoryError が出る、というもの。一回に送るパラメータ数が結構多い画面があるし、同時に何ユーザかがアクセスすると確実に落ちるのでこりゃあマズイってことになった。

解消するのはカンタンで Commons FileUpload 1.1 以降を使えばいい。ついでに commons IO も必要なので一緒に入れればおk。

しかしまぁそれだけじゃなんとなくモヤモヤするのでちょっとソース追ってみた。

Struts 1.1 の、マルチパートのリクエストを受け取ってフォームの各フィールドに対応する ActionForm プロパティに関連付ける処理は CommonsMultipartRequestHandler#handleRequest あたりが核になっているらしい。

    public void handleRequest(HttpServletRequest request)
            throws ServletException {
        // Get the app config for the current request.
        ModuleConfig ac = (ModuleConfig) request.getAttribute(
                Globals.MODULE_KEY);

        // Create and configure a DIskFileUpload instance.
        DiskFileUpload upload = new DiskFileUpload();
        ...
        // Set the maximum size that will be stored in memory.
        upload.setSizeThreshold)((int) getSizeThreshold(ac))(;
        ...
        // Parse the request into file items.
        List items = null;
        try {
            items = upload.parseRequest(request);
        ...

DiskFileUpload というクラスに request を解析させることでアップロードファイルとフォームの各フィールドの詰まったリストが得られるらしい。んで、DiskFileUpload のインスタンスに対して setSizeThreshold でなんらかのバッファの大きさをしている。この値は Struts の設定情報が詰まっている ModuleConfig から取得しており、ControllerConfig の↓がデフォルト値のようだ。

    protected String memFileSize = "256K";

次は、DiskFileUpload#parseRequest(request) で、このメソッドは親クラスの FileUploadBase にある。

    public List /* FileItem */ parseRequest(HttpServletRequest req)
        throws FileUploadException
        ...
            while (nextPart)
            {
            ...
                            // A form field.
                            FileItem item = createItem(headers, true);
                            OutputStream os = item.getOutputStream();

少々長めのメソッドだが、フォームフィールドを処理するコードを示すらしきコメントがある。とりあえず先へ読み進んでみる。

次の、FileItem#getOutputStream() の実装は DefaultFileItem が使用される。

    public OutputStream getOutputStream()
        ....
        dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
        ....

更にもう一つ先のクラス DeferredFileOutputStream を見てみる。

    public DeferredFileOutputStream(int threshold, File outputFile)
        super(threshold);
        ...
        memoryOutputStream = new ByteArrayOutputStream(threshold);
        ...

ByteArrayOutputStream を作るときに threshold というバッファサイズを指定している。コードをちょっと端折ってるんだけど、ここには 262144 ≒ 256K が入る。

つまり、フォームフィールド一つを解析するために 256K のバッファを取るコードになってるみたい。そのため、フォームフィールドがたくさんあると物凄い勢いでメモリを消費していって・・・結果、メモリが溢れるというのが事の真相みたい。

じゃあ Commons FileUpload 1.1 以降や 2.x はどうなっているかっていうと…同じようにコードを追ってみる。順繰りに見ていくと Commons FileUpload が使用する Commons IO の DeferredFileOutputStream にいきついて…

    public DeferredFileOutputStream(int threshold, File outputFile)
        ...
        memoryOutputStream = new ByteArrayOutputStream();
        ...

バッファサイズの指定がなくなっている。この場合、ByteArrayOutputStream のバッファサイズはデフォルトの 32 バイトになる。なるほど。


いざ、って時に中のソースが追えるってのはやっぱり面白いなぁ・・・。

「旭山動物園」革命―夢を実現した復活プロジェクト

旭山動物園にものすごく行きたくなる一冊。このテの新書を好んで読む「いい大人」こそ、その傾向は一段と高いんじゃないだろうか。

前半は、絶え間ない試行錯誤が人気動物園を創り出し、そして支え続けている様子が書かれている。中間に、どん底から如何にして這い上がってきたか、動物園園長自身の組織論やビジネス観についてをはさみ、後半は動物園の果たす役割などについての分析や著者の非常に熱い想いが綴られている。

とりあえず第一章を読めば「旭山動物園はなんか色んな事していて面白そうだ。行ってみてぇ」と感じられると思う。

個人的に興味深いと感じたのは、充分な学術知識・実地経験があってこそ素晴らしい展示が出来るのだ、という話。「学術的知識は、よい展示をつくる」の章では、動物の視点を取り入れることで非常に斬新な展示方法が可能になったことについて詳述されている。そうした新しい発想を可能にしたのは、動物達の観察や、学術論文を漁ったりなど、地道な活動が下地になっているという。

とにかく試行錯誤の連続だったことが本書では何度も出てくるのが印象的。本書でもいくらかは失敗談に書かれているけど、他にも相当な失敗を繰り返してきたのかと思うと、もう、なんというか、狂気のレベルなんじゃないかと思えてくる。

そういった信念が滲み出てくるようなこの本を読んでいると・・・ホント、旭山動物園行きたくなってくるから困り者です。

誤字修正:2008/01/24 旭日山動物園 → 旭山動物園

「旭山動物園」革命―夢を実現した復活プロジェクト (角川oneテーマ21)

「旭山動物園」革命―夢を実現した復活プロジェクト (角川oneテーマ21)