本セッションでシーケンス"****_seq"のcurrvalはまだ定義されていません

DBの話


今回はPostgreSQLだけど

本セッションでシーケンス"****_seq"のcurrvalはまだ定義されていません


ってなるときがある。


『何も悪くないのにぃ』
って思うのだけれども、やっぱ悪いには悪い。なので怒られる。


原因は、

トランザクションがオートになっていて
SQL発行と同時にコミットされてしまうために
currvalしたときにはnextvalの値が残っていない

というわけ。


今回たまたまSa-StrutsのActionに書いてあったソースをそのまま
バッチプロジェクトにあてがったために、
UserTransactionでのbeginがされていなかったためである。

GAEでJSPを使わない

GAEでJSPを使わない
というただの宣言w


GAEでJSPコンパイルできず、一番ひどいときはアプリが立ち上がらないので開発もできない。
デプロイするときにエラーでデプロイできない
ということがおきるので、確かに便利だけど、使わなくてもいいんじゃないかと思う。


でも、MVCは分けておきたいのでHTMLレンダリングエンジンが必要だ。
なので、じゃぁお得意の自分で作ってしまおう と思った。
ので、やってみよう。


まぁ普通Velocity使うよねw

Twitter4Jをつかったら、Twitter連携がものすげー楽だった

下の叫ぶアプリ上でついでにTwitterに書き込むためにTwitter連携をした。


連携のライブラリはTwitter4Jを使用したのだが、
なんというか簡単すぎて困る。


RequestTokenを取得して、AccessToken取得して、終了ってw
簡単すぎるでしょう。
そんな簡単にしてくれちゃうライブラリすごいっす。
まぁOAuthそのものはベット勉強する機会があったので、それでって感じもするけど。


ちょっとtwitter4j.propertiesファイルを読むこむところがどうやっていいんだか迷ったけど、
InputStreamがあったのでファイル直指定してしまった。
GAEなので設定ファイルのXMLファイルに書けばいけるのかもしれないけど、
GAE詳しくないので、まぁいいやw


とりあえず、簡単だったと。



ただ、GAEのせいだと思うけど、
プロクシーの中にいると外に出て行けない気がする。
GAEにそんな設定あるのかな。
Eclipseのねっとわーく設定はやってあるのだが…。

叫ぶ

GAEでただ何かを入力するだけのものを作ってみた。
アプリ何個か作ったことあるので、
もう勉強がてらってわけでもなく…。


なんか思いついたから…、GAEは今のところタダだし。


FrameworkはSlim3だけど、
Modelは1個しか使ってないし、
Controllerは3つか4つ。


本当に無駄アプリだw


Twitterと連携して発言するような機能をつけようかな。

http://ssssshout.appspot.com/

GoogleAppEngineのEclipseのプラグインをインストール

GoogleAppEngineのEclipseプラグインをインストールするときに、
いつもわからなくなるのでリンクを貼っておこう。

http://code.google.com/intl/ja/eclipse/docs/download.html


PCが変わるとインストールされているEclipseもかわったりするし。

EclipseでTomcatが起動しない。

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ということは、mementoがxmlにつながる何かなんだろう と。
なので、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文字列であるということだ。
さっきまで、mementoはXMLのファイルパスだと思ったいたのだが、
この考えが間違っていた。


なので、本当のターゲットとなる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となってしまったのだ。
(実際ここまで深くロードしたときじゃなくて、

gourp by して 数字をくっつけながら取り出す

大分非効率で使い道があまりないロジックを思いついた。




よくあるのが、SQL上で groyp by しておいて、maxとかsumとかやることがあると思う。
そのときに、足すでもなく最小最大でもなく、
group by の対象となる数字を全部取り出す(数値のまま全部でも、全部文字列結合にしてでも)。




groupbyで使用できるセパレータ付文字列結合メソッドは確かなかったはずで、
(あったらそれを使えばいいさw)
大抵はgroupbyせず取り出しておいて、
プログラムでごりごり数値の配列で取り出したり文字列結合したりすると思う。
このときにgroup by するはずだったものをorder byにして順々処理したり、
Java的にいうとMapのListとかにしておいてgroup byのものをキーに、がつがつ詰めたり、
まぁそんなことをするのが定石でしょう。




今回はあえてgroup by する方法を思いついたので、それを書いてみる。


データとしては、こんな感じだろか。

table=test
┌──┬───┐
│LEFT│RIGHT │
├──┼───┤
│   1│     4│
│   1│     5│
│   2│     4│
│   2│     5│
│   3│     4│
│   3│     5│
│   4│     6│
│   4│     7│
│   4│     8│
│   4│     9│
│   4│    10│
│   4│    12│
│   5│     6│
│   5│     7│
│   5│     8│
│   5│     9│
│   5│    10│
│   5│    12│
│   6│    11│
│   7│    11│
│   8│    11│
└──┴───┘

これの LEFT に対する RIGHT が取り出したいとする


なので一旦RIGHTを2のRIGHT乗する。

SELECT LEFT, RIGHT, POWER( 2, RIGHT ) P FROM TEST

これを副問い合わせにして、
この結果をsumしながらgroup byする
(ついでに見やすいようにleftでorder byする)

SELECT * FROM (
	SELECT LEFT, SUM(P) FROM
    		( SELECT LEFT, RIGHT, POWER( 2, RIGHT ) P FROM TEST )
	GROUP BY LEFT
)
ORDER BY LEFT


結果がこれ

┌──┬───┐
│LEFT│SUM(P)│
├──┼───┤
│   148    │
│   248    │
│   348    │
│   46080  │
│   56080  │
│   62048  │
│   72048  │
│   82048  │
└──┴───┘

SQL的にはこれでおしまし。
LEFTが1行づつしか出てこないので、これがこいつの利点(?)。


今度はこのSUM(P)をプログラムで分割する。




2の累乗を使用してから加算しているので、
分割してもあいまいにはならず綺麗に分割できる。




分割に使用するのは、
2の0乗(1)
2の1乗(2)
2の2乗(4)
2の3乗(8)
2の4乗(16)
2の5乗(32)
2の6乗(64)
2の7乗(128)
2の8乗(256)
2の9乗(512)
2の10乗(1024)
...


この数値で分割すると、
48は32+16
6080は4096+1024+512+256+128+64
2048は2048
から成り立つことがわかる。(わけないよねwいやわかる人にはわかると思うけど。)




算出方法はロジカルに書くとこうなる。
(2の累乗の値はただ計算すればいいだけなのでそれは問題なく算出できるとする)


1.ターゲットの数値に対して、2の1乗から順に比較する。
2.比較し2のx乗の方がでかくなったら、その数字の1つ前の値を採用する。
(48->64でオーバーなので32、6080->8192でオーバーなので4096、2048->4096でオーバーなので2048)
3.採用した数値が2の何乗なのかを保持しておく
(32=2の5乗、4096=2の12乗、2048=2の11乗)
4.採用した数値で引く。
(48-32=16、6080-4096=1984、2048-2048=0)
5.1.2.3.4.を繰り返し、4.の結果が0になるまで続ける。
(48-32-16=0、6080-4096-1024-512-256-128-64=0、2048-2048=0)
6.保持している値を取り出す
(結果が48だったLEFTの1,2,3は=>5,4、結果が6080だったLEFTの4,5は=>12,10,9,8,7,6、結果が2048だったLEFTの6,7,8=>11)




という感じ。






うーん、2の累乗でごりごり逆算するのがなんとも非効率のような気がする。
2の累乗の結果がintとかlongとかの結果を超えるようなでかい数字は扱えないし。


そもそも
SQLでpowしてリストで取り出している時点で、
group byで複数行にしようが、group byなしで複数行取ろうが、あんまり変わりないよね。
個人的には、group byせずにリストにして取り出して、レコードをループでまわしながら
leftが変わるまでループし変わったら、集めて何か処理に移る っていうロジックが、
あまり好きじゃないので、何かないかなぁ って考えてたら、しょうもないのを思いついた。




あまり採用されないレベルだったなw