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を削除するとか、上記のアノテーションを追加するとかの対策を講じてみてください。
