Hatena::ブログ(Diary)

何言語でも話したいPGの開発日記

※「2011年07月26日

EclipseでTomcatが起動しない。

| 14:55

EclipseTomcatが起動しない。


とりあえず誰でもささっと調べるだろう。

そして簡単に見つけられるだろう。

「プロジェクトパスに日本語が含まれているとアウト」

ということ。



しかし今回はプロジェクトのパスに日本語含まれていないけど、

Tomcatが立ち上がらない という現象におちいった。

とりあえずログを見てみることにした。

ログはworkspaceの.logを見てみよう。

XML 文書を構文解析できません。」とか「 Invalid byte 1 of 1-byte UTF-8 sequence.」が

Exceptionと共に出ているはずだ。

出ていなかったら手がかりが何もないので、

EclipseTomcatプラグインの再インストールをお勧めするw



これを読んでいる人と、俺の原因が同じかどうかはわからないが、

これで問題が解決したらうれしい。

以下、原因調査を長くうだうだ書くので、

結論だけ見たい人は下のほうを見てくれ。





Exceptionがでているとして、こんなのが出ていないだろうか。

XML 文書を構文解析できません。」とか「 Invalid byte 1 of 1-byte UTF-8 sequence.」

誰がどう見てもどっかのXMLファイルが悪いんだろう と想像がつく。

じゃぁどのXMLファイルが悪いのかということだ。

XMLといえばプラグインの設定ファイルのplugins.xmlとかしか思い浮かばないのだけれども、

Eclipse上にTomcatアイコンが出ている時点でその辺は問題ないと。

しょうがないので中を読むことにした。


テキトウなJAVAプロジェクトを作って、EclipseTomcatプラグインjarをクラスパスに入れる。

plugins\com.sysdeo.eclipse.tomcat_3.2.1\tomcat.jar とか。

これでいったい何を見るか…、いったんExceptionを振り返ってみよう。

!MESSAGE org.eclipse.core.runtime.CoreException: XML 文書を構文解析できません。
	at org.eclipse.debug.core.DebugPlugin.abort(DebugPlugin.java:1198)
	at org.eclipse.debug.core.DebugPlugin.parseDocument(DebugPlugin.java:1176)
	at org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector.doInitializeFromMemento(AbstractSourceLookupDirector.java:410)
	at org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector.initializeFromMemento(AbstractSourceLookupDirector.java:546)
	at org.eclipse.debug.internal.core.LaunchConfiguration.initializeSourceLocator(LaunchConfiguration.java:566)
	at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:754)
	at com.sysdeo.eclipse.tomcat.VMLauncherUtility.runVM(VMLauncherUtility.java:95)
	at com.sysdeo.eclipse.tomcat.TomcatBootstrap.runTomcatBootsrap(TomcatBootstrap.java:202)
	at com.sysdeo.eclipse.tomcat.TomcatBootstrap.start(TomcatBootstrap.java:95)
	at com.sysdeo.eclipse.tomcat.actions.StartActionDelegate.run(StartActionDelegate.java:38)
	at org.eclipse.ui.internal.PluginAction.runWithEvent(PluginAction.java:256)
	at org.eclipse.ui.internal.WWinPluginAction.runWithEvent(WWinPluginAction.java:229)
	at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:546)
	at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:490)
	at org.eclipse.jface.action.ActionContributionItem$6.handleEvent(ActionContributionItem.java:443)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:938)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3682)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3293)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2389)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2353)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2219)
	at org.eclipse.ui.internal.Workbench$4.run(Workbench.java:466)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:289)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:461)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:106)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:169)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:106)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:76)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:363)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:176)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:508)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:447)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1173)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1148)
org.eclipse.core.runtime.CoreException[120]: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 1 of 1-byte UTF-8 sequence.
	at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.invalidByte(UTF8Reader.java:674)
	at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader.java:547)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEntityScanner.java:1742)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.scanLiteral(XMLEntityScanner.java:1064)
	at com.sun.org.apache.xerces.internal.impl.XMLScanner.scanAttributeValue(XMLScanner.java:974)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanAttribute(XMLDocumentFragmentScannerImpl.java:1539)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1316)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2747)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
	at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:807)
	at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
	at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:107)
	at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:225)
	at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:283)
	at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:124)
	at org.eclipse.debug.core.DebugPlugin.parseDocument(DebugPlugin.java:1168)
	at org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector.doInitializeFromMemento(AbstractSourceLookupDirector.java:410)
	at org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector.initializeFromMemento(AbstractSourceLookupDirector.java:546)
	at org.eclipse.debug.internal.core.LaunchConfiguration.initializeSourceLocator(LaunchConfiguration.java:566)
	at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:754)
	at com.sysdeo.eclipse.tomcat.VMLauncherUtility.runVM(VMLauncherUtility.java:95)
	at com.sysdeo.eclipse.tomcat.TomcatBootstrap.runTomcatBootsrap(TomcatBootstrap.java:202)
	at com.sysdeo.eclipse.tomcat.TomcatBootstrap.start(TomcatBootstrap.java:95)
	at com.sysdeo.eclipse.tomcat.actions.StartActionDelegate.run(StartActionDelegate.java:38)
	at org.eclipse.ui.internal.PluginAction.runWithEvent(PluginAction.java:256)
	at org.eclipse.ui.internal.WWinPluginAction.runWithEvent(WWinPluginAction.java:229)
	at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:546)
	at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:490)
	at org.eclipse.jface.action.ActionContributionItem$6.handleEvent(ActionContributionItem.java:443)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:938)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3682)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3293)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2389)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2353)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2219)
	at org.eclipse.ui.internal.Workbench$4.run(Workbench.java:466)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:289)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:461)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:106)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:169)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:106)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:76)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:363)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:176)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:508)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:447)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1173)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1148)


いったんあたりをつけてみると、

	at com.sysdeo.eclipse.tomcat.VMLauncherUtility.runVM(VMLauncherUtility.java:95)
	at com.sysdeo.eclipse.tomcat.TomcatBootstrap.runTomcatBootsrap(TomcatBootstrap.java:202)
	at com.sysdeo.eclipse.tomcat.TomcatBootstrap.start(TomcatBootstrap.java:95)
	at com.sysdeo.eclipse.tomcat.actions.StartActionDelegate.run(StartActionDelegate.java:38)


この辺だろうか…。

なので、TomcatBootstrapクラスかVMLauncherUtillityクラスかを見てみる。

クラスパスに入れて、幸いソースもJadすれば見れるので、クラスを見る。

行数はずれているが、VMLauncherUtillity#runVMとかTomcatBootstrap#runTomcatBootsrapを見る。


ここで脱線なのだが、

2つ目のメソッドがrunTomcatBootsrap…。srap???これってタイプミスじゃないのw


public static void runVM(String label, String classToLaunch, String classpath[], String bootClasspath[], String vmArgs, String prgArgs, boolean debug, boolean showInDebugger, boolean saveConfig) throws CoreException {
	String mode = "";
	if(debug)
		mode = "debug";
	else
		mode = "run";
	ILaunchConfigurationWorkingCopy config = createConfig(label, classToLaunch, classpath, bootClasspath, vmArgs, prgArgs, debug, showInDebugger, saveConfig);
	String perspective = null;
	config.setAttribute(mode, perspective);
	org.eclipse.debug.core.ILaunch launch = config.launch(mode, null, false, showInDebugger);
}


runVMの中を見ながらExceptionに話を戻すと、

runVMの次のスタックが、

	at org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector.doInitializeFromMemento(AbstractSourceLookupDirector.java:410)
	at org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector.initializeFromMemento(AbstractSourceLookupDirector.java:546)
	at org.eclipse.debug.internal.core.LaunchConfiguration.initializeSourceLocator(LaunchConfiguration.java:566)
	at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:754)
	at com.sysdeo.eclipse.tomcat.VMLauncherUtility.runVM(VMLauncherUtility.java:95)


これらである。

なので、launchメソッドをコールしている部分。

configメソッドはILaunchConfigurationWorkingCopyのもの。

Exception的にはorg.eclipse.debug.internal.core.LaunchConfigurationである。

これはおそらくEclipeプラグインライブラリ?であろうと想像できる。

じゃぁEclipseプラグインを作成してみれば中身が見れるんじゃなかろうか。


なので、Eclipseプラグインプロジェクトを作る。

新規プロジェクト→プラグイン開発→プラグイン・プロジェクト

作ったら、plugin.xmlプラグインエディタ?みたいなのが自動で開くので、

これの右上にあるプラグイン・コンテンツの依存関係をクリック。

左側に「org.eclipse.debug.core」を追加しておく。

必要かわからないけど、っぽいやつをだーーーと追加しておいてもいいかも。


そうすると見たかった、

ILaunchConfigurationWorkingCopy とか LaunchConfiguration とかが

Javaプロジェクトでいうところのクラスパスに入るので、

Ctrl+Shift+Tでクラスを開いておく。

ILaunchConfigurationWorkingCopy は interfaceなので、実装クラスのLaunchConfigurationWorkingCopy

案の定、LaunchConfigurationWorkingCopyはLaunchConfigurationの子クラスだった。

その中からlaunchメソッドを探す。


LaunchConfigurationWorkingCopyにはなく、LaunchConfigurationにオーバーロードで3種ある。

runVMでコールしているメソッド引数4つなので、LaunchConfigurationにある引数4つのやつを見る。

中をばっとみるが、正直よくわからないw


Exceptionのスタックを見ると、次は#initializeSourceLocatorである。

中盤にコールしている部分がある。

initializeSourceLocatorは自クラスの定義なのでそっちを見る。


次に、Exceptionのスタックを見るとAbstractSourceLookupDirector#initializeFromMemento

initializeSourceLocatorには2箇所コールしている箇所がある。

行数がずれててどっちかよくわからないが、AbstractSourceLookupDirectorに飛んでみる。

どちらもAbstractSourceLookupDirector#doInitializeFromMementoのラッパーだったので、

doInitializeFromMementoをみる。


さらにスタックを見てみると、

	at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:283)
	at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:124)
	at org.eclipse.debug.core.DebugPlugin.parseDocument(DebugPlugin.java:1168)
	at org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector.doInitializeFromMemento(AbstractSourceLookupDirector.java:410)


doInitializeFromMementoの冒頭で、

Element rootElement = DebugPlugin.parseDocument(memento);

というのがある。Exceptionのスタックにもそう書いてある。

parseDocumentということは、mementoxmlにつながる何かなんだろう と。

なので、mementoとは何かを突き止めればいいんだろう。

「このときは、mementoとはXMLのパスだろうと想像していた。」


mementoはdoInitializeFromMementoの1つ目の引数

呼び出し元に戻ると、

initializeFromMementoの1つ目の引数

initializeSourceLocatorの中でmementoが生成されている。

String memento = getAttribute(ATTR_SOURCE_LOCATOR_MEMENTO, ((String) (null)));

であると。

ATTR_SOURCE_LOCATOR_MEMENTOとはいったいなんだろうか。

public static final String ATTR_SOURCE_LOCATOR_MEMENTO = DebugPlugin.getUniqueIdentifier() + ".source_locator_memento";


DebugPlugin.getUniqueIdentifier()は、

public static String getUniqueIdentifier() {
	return "org.eclipse.debug.core";
}

なので、

ATTR_SOURCE_LOCATOR_MEMENTO とは、org.eclipse.debug.core.source_locator_memento という文字列のようだ。

でも、これがいったい何なのか。


次に読むべきはLaunchConfiguration#getAttribute

public String getAttribute(String attributeName, String defaultValue) throws CoreException {
	return getInfo().getStringAttribute(attributeName, defaultValue);
}


LaunchConfiguration#getInfoは、

protected LaunchConfigurationInfo getInfo() throws CoreException {
	return getLaunchManager().getInfo(this);
}

LaunchConfiguration#getLaunchManagerは、  この辺で一回心が折れる…、やっぱゲッターが続くとつらい。

protected LaunchManager getLaunchManager() {
	return (LaunchManager)DebugPlugin.getDefault().getLaunchManager();
}

DebugPlugin.getDefault()の戻りクラスはDebugPluginなので、DebugPlugin#getLaunchManager()へ。

public ILaunchManager getLaunchManager() {
	if(fLaunchManager == null)
		fLaunchManager = new LaunchManager();
	return fLaunchManager;
}

LaunchConfiguration#getInfoのところのは

fLaunchManagerにたいして、getInfoしていることとなる。


ILaunchManager#getInfo は やたら長いので、

returnしているものを逆算して、追っていくと、

info = createInfoFromXML(stream);

こんなのが見つかる。

久々にでた、stream とはなんともわかりやすい変数名。

こいつがXMLストリームクラスであろうと。

これがいったい何かということだが、これまた難読なコード。なのでstream生成にかかわるところだけ見ると、

if(config.isLocal()) {
	IPath path = config.getLocation();
	File file = path.toFile();
	stream = new BufferedInputStream(new FileInputStream(file));
} else {
	IFile file = ((LaunchConfiguration)config).getFile();
	if(file == null)
		throw createDebugException(MessageFormat.format(DebugCoreMessages.LaunchManager_30, new String[] {
			config.getName()
		}), null);
	stream = file.getContents(true);
}
info = createInfoFromXML(stream);


じゃぁconfigとは何かというと、

getInfo の 引数で、

さかのぼると、LaunchConfiguration#getInfoでthisとして渡している。


じゃぁそのインスタンスを生成しているのはどこかというと、

runVMの

ILaunchConfigurationWorkingCopy config = createConfig(label, classToLaunch, classpath, bootClasspath, vmArgs, prgArgs, debug, showInDebugger, saveConfig);


createConfigが難解すぎて、心が折れる、二回目、しかもかなり強く折れる。

なのでいったん追うのをやめるw


ここはもうカンで、ILaunchManager#getInfoのif文の片一方だけとあたりをつける。

なので、今追うべくは

IPath path = config.getLocation();
File file = path.toFile();
stream = new BufferedInputStream(new FileInputStream(file));

config.getLocation は、いったん追うのをやめた ILaunchConfigurationWorkingCopy#getLocation である。

なかにifがあるが一旦無視して、

if(isLocal())
	path = LaunchManager.LOCAL_LAUNCH_CONFIGURATION_CONTAINER_PATH;
else
	path = getContainer().getLocation();
path = path.append(getName() + "." + "launch");

LaunchManager.LOCAL_LAUNCH_CONFIGURATION_CONTAINER_PATH は、

protected static final IPath LOCAL_LAUNCH_CONFIGURATION_CONTAINER_PATH = DebugPlugin.getDefault().getStateLocation().append(".launches");


なので、結局は、

getName() か DebugPlugin.getDefault().getStateLocation()に「.launch」をつけたものであると。


DebugPlugin.getDefault().getStateLocation()の中身をみたが、3度目の心が折れそうになったので、

getName()を見ることにする

public String getName() {
	return fName;
}

fNameは、LaunchconfigrationWorkingCopy(getName()と同じクラス)のコンストラクタでセットされている。

しかもコンストラクタ引数で渡したものをfNameにセットしているので、インスタンスが作られるところを探してみる。


結局、さっき一番強く心が折れた、createConfigを見ることになる。

でも、そのreturnしているもののインスタンスを作る瞬間だけ見ればいいので、今度は簡単だ。

ILaunchConfigurationType launchType = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType("org.eclipse.jdt.launching.localJavaApplication");
ILaunchConfigurationWorkingCopy config = launchType.newInstance(null, label);


これだ。

1行目はいったん無視して、ILaunchConfigurationType#newInstanceの中身を見る。

初めて出てきたクラスだが、Ctrl+Shift+Tすればいける。

うーん、interfaceだった。Ctrl+tで実装クラスを探せるので、探す。

LaunchConfigurationType

(そういや、よくIなんたらというinterfaceとなんたらっていう実装クラス出てくるけど、これってデザインパターンだったよなぁ)


public ILaunchConfigurationWorkingCopy newInstance(IContainer container, String name) {
	return new LaunchConfigurationWorkingCopy(container, name, this);
}

newInsetanceっていうくらいなので、リフレクションかと思ったら、単純だった。

引数は3つ。

LaunchConfigurationWorkingCopyの引数三つのコンストラクタを見てみると、

protected LaunchConfigurationWorkingCopy(IContainer container, String name, ILaunchConfigurationType type) {
	super(null);
	fParent = null;
	fDirty = false;
	fRenamed = false;
	fSuppressChange = true;
	setName(name);
	setInfo(new LaunchConfigurationInfo());
	getInfo().setType(type);
	setContainer(container);
	fSuppressChange = false;
}


setNameはというと、2つ目の引数のStringをそのまま格納している。

これをばーーーと上にさかのぼると、


newInstanceに戻ると、setNameの2つ目の引数は、newInstanceの2つ目の引数

createConfigのnewInstanceの2つ目の引数は、createConfigの1つ目の引数

createConfigの1つ目の引数は、runVMの1つ目の引数

TomcatBootstrap#runTomcatBootsrapのrunVMのコールするときの1つ目の引数はというと、TomcatBootstrap#getLabel()。


public abstract String getLabel();

である。近くのクラスを見渡してみると、

Tomcat3Bootstrap、Tomcat41Bootstrap、Tomcat4Bootstrap、Tomcat5Bootstrap、Tomcat6Bootstrap

とある。

この辺はどれを使っているとか考えるより、

EclipseTomcatの設定画面で選んだものがそのまま使用されると考えるほうが早いだろう。

今回は、Tomcat6なので、Tomcat6Bootstrap#getLabel()を見る。

public String getLabel() {
	return "Tomcat 6.x";
}

だった。


なので、キモとなるのは、「Tomcat 6.x.launch」という文字列

IPath path = config.getLocation();
File file = path.toFile();
stream = new BufferedInputStream(new FileInputStream(file));

このコードを合わせると、

Tomcat 6.x.launch」というファイルがあると想像できる。


どういった経緯でそのファイルを探したか忘れてしまったのだが、

大抵プラグインの設定ファイルはmetadataの下にあるのだ。


なので、Tomcat 6.x.launchファイルをmetadata配下で検索。

(metadataはworkspaceに.metadataという名前であるよ、多分)


発見!

そのファイルを見てみる前に、一旦コピーして、****.xmlをいう名前で保存して、IEとかで見てみる。

なるほどこれがXMLだったのか…。


LaunchConfiguration#getAttributeを思い出してほしい。

getStringAttributeしていることを、そのときにキーとなる文字列は、

「org.eclipse.debug.core.source_locator_memento」だ。

大分前だったので目的がわからなくなっていた、何回も心が折れてもんで忘れていたよ。


ざーと、getStringAttributeを書くと、

getAttributeTable().get(key);

getAttributeTable()は、

fAttributesのgetter。

getterをコールした後に、putしている部分はどこかというと、

void setAttribute(String key, Object value)だった。

それをコールするのは、

setStringAttribute、setIntegerAttribute、setMapAttribute とかのクラスによって分けられるメソッド

そいつらの呼び元は、

LaunchConfigurationInfo#initializeFromXMLである。

initializeFromXMLの呼び元は、さっきstreamという変数を作ってて分かりやすかったあいつ。

initializeFromXMLでは、

タグを見たり、なんだかんだあって、stringAttibuteタグをみて、key属性をキーに、value属性を値にし、

TreeMapの中に埋めていく

ということである。

これで、getStringAttributeの処理がわかった。


これで、Tomcat 6.x.launch と XMLパーサーがつながったと思ったのだが、違った。


mementoは、

AbsstractSourceLookupDirector#doInitializeFromMemento

の中で、

DebugPlugin.parseDocument(memento)

をやっていて、

DebugPlugin#parseDocumentの中で、mementoにたいして、

stream = new ByteArrayInputStream(document.getBytes());
root = parser.parse(stream).getDocumentElement();

する箇所がある。ByteArrayInputStreamとういことは、mementoはファイルではなく、

これ自体がXML文字列であるということだ。

さっきまで、mementoXMLのファイルパスだと思ったいたのだが、

この考えが間違っていた。


なので、本当のターゲットとなるXMLファイルはTomcat 6.x.launchではなかった。

追わなければいけなかったのは、Tomcat 6.x.launchの中に書いてある、XML文字列だった。



いったん戻って、

stringAttributeのキーに「org.eclipse.debug.core.source_locator_memento」があるものに対する値を見る。


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<sourceL


エスケープはされているけど、まさかの値自体がXMLとなw

これをエスケープを元に戻してみると、

XMLが出てきて、

<container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;javaProject name=&quot;sampleProject&quot;/&gt;&#13;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/>


となって、上で気づかされたXML文字列とはこいつのこと。


さらにこのmementoの値が、

<javaProject name="sampleProejct" />

となる。



ここで問題となるのが、jarの記述が入ること。

これが今回の一番ポイント。

そしてTomcatプラグイン関連で日本語のパスを含んではいけない という根本の理由である。


あるプロジェクトでjarを追加することがあるだろう。

そのjarのロードの仕方にもよるんだろうが、あるロードの形をとるとここに記載される場合がある。

こんな感じに。

<container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;packageFragmentRoot handle=&quot;=sampleProject/C:\/tomcat\/apache-tomcat-6.0.18\/lib\/annotations-api.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.packageFragmentRoot"/>


ちなみに、これはtomcatライブラリで、何も問題はない。



今回たまたま外部jarを手動で追加していた。

diff.jarというjarの実験でそのファイルをデスクトップにおいてあった。

その記述が、

<container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;packageFragmentRoot handle=&quot;=sampleProject/C:\/Documents and Settings\/username\/デスクトップ\/diff.jar&quot;/&gt;&#13;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.packageFragmentRoot"/>


となってしまった。


しかも残念なことに、XMLエンコード記述はUTF-8でも、パス文字列UTF-8エンコードされておらず、SJISで保存されてしまった。

これが、XMLパーサのエンコードミスによりUTF-8でロードできない というExceptionとなってしまったのだ。

(実際ここまで深くロードしたときじゃなくて、<stringAttributeのvalue属性をロードしているときに起こっているはず)



というこの長い話が、ソースを追ったチラ裏である。





◆結論

根本的な原因:

Tomcatプラグインの設定を、保存するファイルとなるTomcat xxx.launch ファイルに、

XML形式(のいれ子?)で文字列を保存するのだが、Eclipseの設定画面で入れた文字列が、

そのままのエンコーディングWinXPだからWindows-31J?)で保存される。

そのときにXMLのencodingとずれるために、Tomcat起動時、「Tomcat xxx.launch」がロードできないというエラーが出てしまう。



回避方法:

プロジェクトやプロジェクトパスに日本語を含めない

外部jarを追加する場合にそのパスに日本語を含めない

その他日本語が入りそうなものを全て避ける



想像:

VistaとかWin7ってデフォルト文字コードは(UTF-8?)だから、その場合には上記のようなエラーはでない?

・そもそもこのTomcatプラグインEclipseからおおもとの文字コードを受け取ってそれをUTF-8に変換してから保存する

 という処理が必要だったのではなかろうか。いやそれは違うか。エディタとかUTF-8で保存するようになっていても、

 設定ファイルとかはOSに依存した文字コードを使用するようになっているはずか。

 なのでその文字コードを取得して、UTF-8エンコードしてから保存するべきなのではと思う。

 思うものの、マルチバイト環境の人間が作ったわけじゃないから、

 そんなの必要ないといえばそれまでだ。

トラックバック - http://d.hatena.ne.jp/akubito/20110726/1311659717
Connection: close