Xvfb - 仮想フレームバッファ

仮想フレームバッファ

Java で使用できるほとんどの画像操作用ライブラリは Java 標準の AWT を使用しています。しかし、Unix 上で AWT を使用するためには X が起動されていなければいけません。X が起動されていないのに強引に実行しようとすれば以下のようなエラーが出るでしょう。

torao@cobalt$ java Awt
Exception in thread "main" java.lang.InternalError: Can't connect to X11 window server using ':0.0' as the value of the DI
SPLAY variable.
        at sun.awt.X11GraphicsEnvironment.initDisplay(Native Method)
        at sun.awt.X11GraphicsEnvironment.<clinit>(X11GraphicsEnvironment.java:63)
        at java.lang.Class.forName1(Native Method)
        at java.lang.Class.forName(Class.java:134)
        at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:64)
        at java.awt.Window.<init>(Window.java:191)
        at java.awt.Window.<init>(Window.java:233)
        at java.awt.Frame.<init>(Frame.java:318)
        at java.awt.Frame.<init>(Frame.java:297)
        at Awt.<init>(Awt.java:5)
        at Awt.main(Awt.java:9)
torao@cobalt$

もちろんこれは Java 標準の AWT が起動中の X にアクセスしようとして失敗しているわけです。しかしサーバ用途のマシン上で常に X を立ち上げた状態にしておくというのは非常に無駄な話です。そこで今回は Xvbf (X Virtual Frame Buffer; 仮想フレームバッファ) と呼ばれているライブラリを導入してみようと思います。

Xvfb は仮想的な X を立ち上げることで、実際に X を起動しておかなくても X を使用した処理を行うことが出来るようにするためのライブラリです。もちろんこれを起動しておけばコンソールだけのサーバマシンで AWT ベースの画像処理を行うことが出来ます。

1. ダウンロード

Xvfb は XFree86 のサイトからダウンロードすることが出来ます。実行環境によっていくつかありますが、私のマシンでは glibc-2.1 を使用しているため ftp://ftp.xfree86.org/pub/XFree86/4.0.1/binaries/Linux-ix86-glibc21/ から Xvfb.tgz をダウンロードしました。このファイルを解凍すると /bin/Xvfb という実行ファイルが作成されますので、/usr/X11R6/bin に放り込んでおきます。

2. Xvfb の起動

ここで早速 Xvfb を起動してみようと思います。コマンドラインから以下のように入力してみます。

root@sapphire$ export DISPLAY="localhost:1.0"
root@sapphire$ /usr/X11R6/bin/Xvfb :1 -screen 0 1024x768x8 &
[1] 23058
root@sapphire$ PEXExtensionInit: Couldn't open default PEX font file  Roman_M
Could not init font path element /usr/X11R6/lib/X11/fonts/CID/, removing from list!
root@sapphire$

デフォルトのフォントが見つからないというようなエラーが出ていますが、どうやら起動したようです。引き続き本当に AWT が動作するかどうかテストしてみましょう。

テスト用に以下のような画像縮小用 Java アプリケーションを作成してみました。コマンドライン引数 1 で指定された画像を 100x100 の大きさに縮小して引数 2 のファイルに保存する Java アプリケーションです。もちろんこれは X が起動していなければエラーになってしまいます。

import java.io.*;
import java.awt.*;
import java.awt.image.*;
import com.sun.image.codec.jpeg.*;

public class Jpeg{
    public static void main(String[] args) throws IOException{
        InputStream in = new BufferedInputStream(new FileInputStream(args[0]));
        JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(in);
        BufferedImage img = decoder.decodeAsBufferedImage();
        BufferedImage icon = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
        Graphics g = icon.getGraphics();
        g.drawImage(img.getScaledInstance(100, 100, Image.SCALE_SMOOTH), 0, 0, null);
        FileOutputStream out = new FileOutputStream(args[1]);
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        encoder.encode(icon);
        out.flush();
        out.close();
        in.close();
        return;
    }
}

このアプリケーションを実行したところ、まんまと縮小画像を保存することに成功しました。まだフォントの警告が出ているようですが、どうやら X を起動しなくても Xvfb で AWT を利用することが出来るようになりました。

root@sapphire$ java Jpeg img.1.jpg img.2.jpg
Warning: Cannot convert string "-watanabe-mincho-medium-r-normal--*-140-*-*-c-*-jisx0208.1983-0" to type FontStruct
PEXExtensionInit: Couldn't open default PEX font file  Roman_M
Could not init font path element /usr/X11R6/lib/X11/fonts/CID/, removing from list!
root@sapphire$ ps aux
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
…
root     23058  0.0  0.6  7612 3528 pts/0    S    17:50   0:00 /usr/X11R6/bin/Xvfb :1 -screen 0 1024x768x8
…
root@sapphire$

プロセスの状態を見るとだいたい 3.5MB 程度で常駐しているようです。

2. XVfb の自動起動設定

Java アプリケーションや Tomcat で画像を扱うためにはあらかじめ XVfb を起動しておく必要があります。Tomcat だけでなく通常の Java アプリケーションでも AWT の機能を利用できるように Linux の起動と同時に XVfb も起動するように設定してみます。

Linux マシン上で root ユーザになり /etc/rc.d/init.d/ ディレクトリの直下に xvfb というファイルを作成します。

#!/bin/bash
#
# chkconfig: - 91 35
# description: Starts and stops XVfb. \
#              used to provide virtual frame buffer.

# Source function library.
. /etc/init.d/functions

prog=$"Xvfb"

# Xvfb program
XVFB=/usr/X11R6/bin/Xvfb

start() {
    echo -n $"Starting $prog: "
    daemon /usr/X11R6/bin/Xvfb :1 -screen 0 1024x768x24 &
    echo
}

stop() {
    echo -n $"Shutting down $prog: "
    killproc Xvfb
    echo
}

# See how we were called.
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart|reload)
        stop
        start
        ;;
  *)
        echo $"Usage: $0 {start|stop|restart}"
        exit 1
esac

exit 0

ファイルを作成したら実行権限を与えて実際に実行してみましょう。

root@iolite$ chmod 755 xvfb
root@iolite$ ./xvfb start
Xvfbを起動中:                                              [  OK  ]
root@iolite$ ps -ef | grep Xvfb
root      3100  3099  0 05:53 pts/0    00:00:00 /usr/X11R6/bin/Xvfb :1 -screen 0
root@iolite$ ./xvfb stop
Xvfbを停止中:                                              [  OK  ]
root@iolite$ 

実行できることが確認できたら chkconfig コマンドを用いてシステム起動時に xvfb が起動するよう設定を行います。

root@iolite$ chkconfig --add xvfb
root@iolite$ chkconfig xvfb on
root@iolite$ chkconfig --list
…
xvfb            0:オフ  1:オフ  2:オフ  3:オン  4:オン  5:オン  6:オフ
…
root@iolite$

以上でシステム起動時と同時に XVfb を起動するための操作は終了です。ここで実際にマシンをリブートしてちゃんと起動するかどうか確認を行っておくと良いでしょう。

3. Tomcat シェルの設定

Tomcat から AWT の処理を利用するためには環境変数 DISPLAY を定義しておかなければいけません。Tomcat 4 は /usr/bin/dtomcat4 スクリプトから起動するため、このファイルの先頭に設定を加えておきます。

…
#
#   JSSE_HOME       (Optional) May point at your Java Secure Sockets Extension
#                   (JSSE) installation, whose JAR files will be added to the
#                   system class path used to start Tomcat.
#
# $Id: index.html,v 1.1 2004/11/04 16:44:05 torao Exp $
# -----------------------------------------------------------------------------

DISPLAY="localhost:1.0"
export DISPLAY

# OS specific support.  $var _must_ be set to either true or false.
cygwin=false
case "`uname`" in
CYGWIN*) cygwin=true;;
esac
…


[HOME] [TOP]