jdbで実行したら”Invalid JNI signature character ‘;'”と怒られた

GUIのないサーバで、javaのプログラムをデバッグしていた時のおはなし。
Eclipseしか使ったことのない方には馴染みがないと思いますが、javaにはjdbというgdbみたいなデバッガがあります。
jdbは、画面のない環境でも(gdbを使い慣れた人なら)お手軽にデバッグできるツールなのですが、あるプログラムをデバッグしようとしたら

Exception in thread "JDI Internal Event Handler"
java.lang.IllegalArgumentException: Invalid JNI signature character ';'
        at com.sun.tools.jdi.JNITypeParser.nextTypeName(JNITypeParser.java:236)
        at com.sun.tools.jdi.JNITypeParser.typeNameList(JNITypeParser.java:140)

のようなExceptionが出てデバッグができませんでした。
jdbを使わなければ、普通に動きます。jdbでデバッグしようとするとこのようにExceptionが出ます。
ちなみに、Javaのバージョンは、1.6.0_18(OpenJDK)。OSは、Ubuntu 10.04です。

よくわからないので、いろいろ調べてみました。
Google先生で見つけたのが、このサイト。
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6547438

それらしいSolutionが載っているのですが、試してみても状況は変わりませんでした。
結局試行錯誤の上、プログラムを問題が起こる最小限のものにしてみて、やっと問題の回避策がわかりました。

問題が起こるソースコード

MarshalTest.java

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;


public class MarshalTest {

	@XmlRootElement
	static class Sample {
		private String[] messages;
		public String[] getMessages() {
			return messages;
		}
		public void setMessages(String[] messages) {
			this.messages = messages;
		}
	}

	public static void main(String[] args) {
		try {
			Marshaller marshaller = JAXBContext.newInstance(Sample.class).createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			Sample smpl = new Sample();
			smpl.setMessages(new String[]{"abc","def"});
			marshaller.marshal(smpl, new File("sample.xml"));
			System.out.println("marshal end");
		} catch (JAXBException e) {
			e.printStackTrace();
		}
	}
}

このサイトなどに書いてあるように、Java6では簡単にPOJOをxmlファイルにできます。
しかし、どうも配列の扱いがよろしくないようで、getterとsetterがある配列を含むJava Objectをmarshalすると、jdbでは上記のようなExceptionがでます。
(配列でなければ発生しません。)
JAXBがmarshallする際に、オブジェクトのメンバー名を使うか、getter/setterを使うかわからなくなっている気がしたので、”@XmlAccessorType(XmlAccessType.FIELD)”(メンバー名を使え)というアノテーションをつけたら回避できるようになりました。

対策済みのソースコード

※XmlAccessorTypeのアノテーション(13行目)を1行足しただけです。
MarshalTest.java

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;


public class MarshalTest {

	@XmlRootElement
	@XmlAccessorType(XmlAccessType.FIELD)
	static class Sample {
		private String[] messages;
		public String[] getMessages() {
			return messages;
		}
		public void setMessages(String[] messages) {
			this.messages = messages;
		}
	}

	public static void main(String[] args) {
		try {
			Marshaller marshaller = JAXBContext.newInstance(Sample.class).createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			Sample smpl = new Sample();
			smpl.setMessages(new String[]{"abc","def"});
			marshaller.marshal(smpl, new File("sample.xml"));
			System.out.println("marshal end");
		} catch (JAXBException e) {
			e.printStackTrace();
		}
	}
}

ちなみに、この現象はWindowsでも再現しました。
あと、Windows上のEclipseでも試してみたところ、createMarshallerを呼ぶあたりで、
「行番号属性が見つからないため、ブレークポイントをxxxxにインストールできません。」
みたいに言われたので、内部で同じ問題が発生しているかもしれません。

もし私と同じ問題で困っている方がいたら、
配列をやめるとか、getter/setterを削除するとか、上記のアノテーションを追加するとかの対策を講じてみてください。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*