            Linux 向け Java(tm) バイナリのカーネルサポート v1.03
            ----------------------------------------------------

Linux はすべてにおいて先んじています！他のすべての OS が、OS 内で Java
バイナリの直接サポートについて議論をしている中、Linux はサポートをして
います！

下記のことを行った後、他のプログラムと同じように Java アプリケーション
とJava アプレットを実行できます -

1) まず、Linux 向け Java Developers Kit (JDK) をインストールしなければ
   なりません。Java-HOWTO は JDK の入手とインストールを説明しています。
   この HOWTO は次のところから入手できます -
   [訳注：2001 年 12 月現在ではリンク切れになっていました。]

        ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/Java-HOWTO

   また、標準ではないクラス (アプリケーション自体と同じディレクトリに
   含まれていないクラスのために) を利用している Java アプリケーションを使
   用するために、合理的な CLASSPATH 環境変数を設定しなければなりません。

2) モジュールとして、あるいはカーネルに組込む (CONFIG_BINFMT_MISC) た
   めに、BINFMT_MISC をコンパイルし、適切に設定しなければなりません。
   モジュールとしてコンパイルすることを選んだなら、kmod で binfmt_misc
   をサポートすることが簡単にできないので、modprobe/insmod により手動
   でロードしなければなりません。設定についてもっと知りたいのなら、
   Documentation ディレクトリにあるファイル 'binfmt_misc.txt' を読んで
   ください。
   [訳注：日本語訳が
   http://www.linux.or.jp/JF/JFdocs/kernel-docs-2.6/binfmt_misc.txt.html
   にあります。]

3) binfmt_misc に次の設定項目を追加してください (いますぐ
   binfmt_misc.txt を読むことをお勧めします) -
   Java アプリケーションのサポート -
     ':Java:M::\xca\xfe\xba\xbe::/usr/local/bin/javawrapper:'
   実行可能 jar ファイルのサポート -
     ':ExecutableJAR:E::jar::/usr/local/bin/jarwrapper:'
   Java アプレットのサポート -
     ':Applet:E::html::/usr/bin/appletviewer:'
   もしくは、より選択的にしたい場合 -
     ':Applet:M::<!--applet::/usr/bin/appletviewer:'

   もちろんパス名を修正しなければなりません。この文書で使用しているパ
   スやファイル名は Debian 2.1 システムに合わせています (つまり jdk は
   /usr 内に、この文書のカスタムラッパーは /usr/local 内にインストール
   されています)。

   注記、アプレットサポートをより選択的にする場合、動作させるには、既
   存の html ファイルの最初の行に <!--applet--> を含むよう修正しなけれ
   ばなりません ('<' の文字から始まらなければなりません！)。

   コンパイルされた Java プログラムは、下記のようなラッパースクリプト
   が必要です (これは Java にファイル名の取り扱いの不具合があるためで
   す)。ここでも、スクリプトと上記で与えた設定文字列の両方のパス名を見
   直してください。

   このスクリプトの他にちょっとしたプログラムも必要です。
   gcc -O2 -o javaclassname javaclassname.c のようにコンパイルし、
   /usr/local/bin に置いてください。

   javawrapper シェルスクリプトと javaclassname プログラムは二つとも
   Colin J. Watson <cjw44@cam.ac.uk> により提供されました。

====================== Cut here ===================
#!/bin/bash
# /usr/local/bin/javawrapper - binfmt_misc/java 用ラッパー

if [ -z "$1" ]; then
	exec 1>&2
	echo Usage: $0 class-file
	exit 1
fi

CLASS=$1
FQCLASS=`/usr/local/bin/javaclassname $1`
FQCLASSN=`echo $FQCLASS | sed -e 's/^.*\.\([^.]*\)$/\1/'`
FQCLASSP=`echo $FQCLASS | sed -e 's-\.-/-g' -e 's-^[^/]*$--' -e 's-/[^/]*$--'`

# 例えば：
# CLASS=Test.class
# FQCLASS=foo.bar.Test
# FQCLASSN=Test
# FQCLASSP=foo/bar

unset CLASSBASE

declare -i LINKLEVEL=0

while :; do
	if [ "`basename $CLASS .class`" == "$FQCLASSN" ]; then
		# このディレクトリで素直に動作するか確かめます
		cd -L `dirname $CLASS`
		CLASSDIR=$PWD
		cd $OLDPWD
		if echo $CLASSDIR | grep -q "$FQCLASSP$"; then
			CLASSBASE=`echo $CLASSDIR | sed -e "s.$FQCLASSP$.."`
			break;
		fi
		# ディレクトリ名を相対参照してみます
		cd -P `dirname $CLASS`
		CLASSDIR=$PWD
		cd $OLDPWD
		if echo $CLASSDIR | grep -q "$FQCLASSP$"; then
			CLASSBASE=`echo $CLASSDIR | sed -e "s.$FQCLASSP$.."`
			break;
		fi
		# 他に可能なファイル名が無い場合
		if [ ! -L $CLASS ]; then
			exec 1>&2
			echo $0:
			echo "  $CLASS should be in a" \
			     "directory tree called $FQCLASSP"
			exit 1
		fi
	fi
	if [ ! -L $CLASS ]; then break; fi
	# シンボリックリンクのレベルを一つ以上たどります
	let LINKLEVEL+=1
	if [ $LINKLEVEL -gt 5 ]; then
		exec 1>&2
		echo $0:
		echo "  Too many symbolic links encountered"
		exit 1
	fi
	CLASS=`ls --color=no -l $CLASS | sed -e 's/^.* \([^ ]*\)$/\1/'`
done

if [ -z "$CLASSBASE" ]; then
	if [ -z "$FQCLASSP" ]; then
		GOODNAME=$FQCLASSN.class
	else
		GOODNAME=$FQCLASSP/$FQCLASSN.class
	fi
	exec 1>&2
	echo $0:
	echo "  $FQCLASS should be in a file called $GOODNAME"
	exit 1
fi
if ! echo $CLASSPATH | grep -q "^\(.*:\)*$CLASSBASE\(:.*\)*"; then
	# クラスは CLASSPATH 内にないので、CLASSPATH にクラスのディレ
	# クトリを追加します
	if [ -z "${CLASSPATH}" ] ; then
		export CLASSPATH=$CLASSBASE
	else
		export CLASSPATH=$CLASSBASE:$CLASSPATH
	fi
fi

shift
/usr/bin/java $FQCLASS "$@"
====================== Cut here ===================


====================== Cut here ===================
/* javaclassname.c
 *
 * Extracts the class name from a Java class file; intended for use in a Java
 * wrapper of the type supported by the binfmt_misc option in the Linux kernel.
 *
 * Copyright (C) 1999 Colin J. Watson <cjw44@cam.ac.uk>.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>

/* From Sun's Java VM Specification, as tag entries in the constant pool. */

#define CP_UTF8 1
#define CP_INTEGER 3
#define CP_FLOAT 4
#define CP_LONG 5
#define CP_DOUBLE 6
#define CP_CLASS 7
#define CP_STRING 8
#define CP_FIELDREF 9
#define CP_METHODREF 10
#define CP_INTERFACEMETHODREF 11
#define CP_NAMEANDTYPE 12

/* Define some commonly used error messages */

#define seek_error() error("%s: Cannot seek\n", program)
#define corrupt_error() error("%s: Class file corrupt\n", program)
#define eof_error() error("%s: Unexpected end of file\n", program)
#define utf8_error() error("%s: Only ASCII 1-255 supported\n", program);

char *program;

long *pool;

u_int8_t read_8(FILE *classfile);
u_int16_t read_16(FILE *classfile);
void skip_constant(FILE *classfile, u_int16_t *cur);
void error(const char *format, ...);
int main(int argc, char **argv);

/* Reads in an unsigned 8-bit integer. */
u_int8_t read_8(FILE *classfile)
{
	int b = fgetc(classfile);
	if(b == EOF)
		eof_error();
	return (u_int8_t)b;
}

/* Reads in an unsigned 16-bit integer. */
u_int16_t read_16(FILE *classfile)
{
	int b1, b2;
	b1 = fgetc(classfile);
	if(b1 == EOF)
		eof_error();
	b2 = fgetc(classfile);
	if(b2 == EOF)
		eof_error();
	return (u_int16_t)((b1 << 8) | b2);
}

/* Reads in a value from the constant pool. */
void skip_constant(FILE *classfile, u_int16_t *cur)
{
	u_int16_t len;
	int seekerr = 1;
	pool[*cur] = ftell(classfile);
	switch(read_8(classfile))
	{
	case CP_UTF8:
		len = read_16(classfile);
		seekerr = fseek(classfile, len, SEEK_CUR);
		break;
	case CP_CLASS:
	case CP_STRING:
		seekerr = fseek(classfile, 2, SEEK_CUR);
		break;
	case CP_INTEGER:
	case CP_FLOAT:
	case CP_FIELDREF:
	case CP_METHODREF:
	case CP_INTERFACEMETHODREF:
	case CP_NAMEANDTYPE:
		seekerr = fseek(classfile, 4, SEEK_CUR);
		break;
	case CP_LONG:
	case CP_DOUBLE:
		seekerr = fseek(classfile, 8, SEEK_CUR);
		++(*cur);
		break;
	default:
		corrupt_error();
	}
	if(seekerr)
		seek_error();
}

void error(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	vfprintf(stderr, format, ap);
	va_end(ap);
	exit(1);
}

int main(int argc, char **argv)
{
	FILE *classfile;
	u_int16_t cp_count, i, this_class, classinfo_ptr;
	u_int8_t length;

	program = argv[0];

	if(!argv[1])
		error("%s: Missing input file\n", program);
	classfile = fopen(argv[1], "rb");
	if(!classfile)
		error("%s: Error opening %s\n", program, argv[1]);

	if(fseek(classfile, 8, SEEK_SET))  /* skip magic and version numbers */
		seek_error();
	cp_count = read_16(classfile);
	pool = calloc(cp_count, sizeof(long));
	if(!pool)
		error("%s: Out of memory for constant pool\n", program);

	for(i = 1; i < cp_count; ++i)
		skip_constant(classfile, &i);
	if(fseek(classfile, 2, SEEK_CUR))	/* skip access flags */
		seek_error();

	this_class = read_16(classfile);
	if(this_class < 1 || this_class >= cp_count)
		corrupt_error();
	if(!pool[this_class] || pool[this_class] == -1)
		corrupt_error();
	if(fseek(classfile, pool[this_class] + 1, SEEK_SET))
		seek_error();

	classinfo_ptr = read_16(classfile);
	if(classinfo_ptr < 1 || classinfo_ptr >= cp_count)
		corrupt_error();
	if(!pool[classinfo_ptr] || pool[classinfo_ptr] == -1)
		corrupt_error();
	if(fseek(classfile, pool[classinfo_ptr] + 1, SEEK_SET))
		seek_error();

	length = read_16(classfile);
	for(i = 0; i < length; ++i)
	{
		u_int8_t x = read_8(classfile);
		if((x & 0x80) || !x)
		{
			if((x & 0xE0) == 0xC0)
			{
				u_int8_t y = read_8(classfile);
				if((y & 0xC0) == 0x80)
				{
					int c = ((x & 0x1f) << 6) + (y & 0x3f);
					if(c) putchar(c);
					else utf8_error();
				}
				else utf8_error();
			}
			else utf8_error();
		}
		else if(x == '/') putchar('.');
		else putchar(x);
	}
	putchar('\n');
	free(pool);
	fclose(classfile);
	return 0;
}
====================== Cut here ===================


====================== Cut here ===================
#!/bin/bash
# /usr/local/java/bin/jarwrapper - the wrapper for binfmt_misc/jar

java -jar $1
====================== Cut here ===================


さて、実行するために .class, .jar や .html ファイルを chmod -x してく
ださい。パスに Java プログラムを追加するには、.class 拡張子を省略する
ために /usr/bin (もしくは好みの別の場所) 内に、メインの .class ファイ
ルのシンボリックファイルを置くのが最良です。オリジナルの .class ファイ
ルを含むディレクトリは 実行の間、あなたの CLASSPATH に追加されることに
なります。


新しい設定をテストするために、次の単純な Java アプリケーションを入力
し、その名前を "HelloWorld.java" としてください -

        class HelloWorld {
                public static void main(String args[]) {
                       System.out.println("Hello World!");
                }
        }

さて、次のようにアプリケーションをコンパイルしてください -
        javac HelloWorld.java

次のコマンドでバイナリファイルに実行許可を設定してください -
        chmod 755 HelloWorld.class

そして実行してください -
	./HelloWorld.class


Java Jar ファイルを実行するには、chmod で *.jar ファイルに実行ビットを
立て、次のコマンドを実行してください -
       ./Application.jar


Java アプレットを実行するには、chmod で *.html ファイルに実行ビットを
立て、次のコマンドを実行してください -
	./Applet.html


                    著者：Brian A. Lantz, brian@lantz.com
binfmt_misc に関する更新：Richard Gther
                日本語訳：野本浩一 <hng@ps.ksky.ne.jp>
      日本語訳最終更新日：2004/05/11
                    校正：中谷千絵さん <jeanne@mbox.kyoto-inet.or.jp>
                    更新：JF プロジェクト < http://www.linux.or.jp/JF/ >
