ぶらコード:maven:その4(Plexusその3):入り口に入るの巻
今回は、入り口をめっけたので、次回はこの入り口をぶらつきつつ、その先に進んで行くとします。
前回見つけた入り口に入って行きたいのだが、ソースコードを如何に貼り付けて、ぶらり旅の記録を残していくか試行錯誤中。
とりあえず、gist使ってソースを貼ってみる。
まずは、main()メソッドの5行目で、次のmainWithExitCode()というメソッドに入っていく。
なんでわざわざmainWithExitCode()メソッドに分岐するのかってのは、main()メソッドの構造を明確にしたいからですかね。
mainWithExitCode()でくくり出した、一番やりたいことの外で、例外のハンドリングして、
正常時と異常時にそれぞれ適切な終了コードを、System.exit()を使って処理していますな。
ところで、異常時には「100」という値を返してますが、この値が意味するところは、、、特になさそうだな。
で、mainWithExitCode()に入ってみると、
冒頭で以下のコードに遭遇。
String classworldsConf = System.getProperty( CLASSWORLDS_CONF );
CLASSWORLDS_CONFというのは同じファイル内で、
protected static final String CLASSWORLDS_CONF = "classworlds.conf";
と定義されている。
こいつは、システムプロパティから読み込みを行っている訳だが、
以前登場した、mvnコマンドの「/usr/local/Cellar/maven/3.3.3/libexec/bin/mvn」というシェルスクリプトの最後に記載のある、
起動時の以下のコードでplexusの起動時に「"-Dclassworlds.conf=${M2_HOME}/bin/m2.conf" \」
として渡しているものを、ここで受け取っていることになる。
exec "$JAVACMD" \ $MAVEN_OPTS \ -classpath "${M2_HOME}"/boot/plexus-classworlds-*.jar \ "-Dclassworlds.conf=${M2_HOME}/bin/m2.conf" \ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${CLASSWORLDS_LAUNCHER} "$@"
ようやくmavenとも繋がってきたな。
ぶらコード:maven:その3(Plexusその2):「旅の入り口を発見」の巻
で、以前たどり着いた、「plexus-classworlds-*.jar」が何者かってことから入っていこう。
Plexus Classworlds – About
によると、どうやら「クラスローダーフレームワーク」らしい。
「クラスローダーフレームワーク」って単語はあまり聞きなれないけど、
要はクラスをローディングしてくれる便利ツールなんでしょう。
早速ソースを探す。
Plexus Classworlds – Source Repository
てなページがあったので見てみると、どうやらGitHubでソース管理している様子。
なので以下のURLからソースをゲット。
http://github.com/codehaus-plexus/plexus-classworlds
ローカルで「mvn eclipse:eclipse」してeclipseに取り込む。
でもちょっとその前に、今回の「plexus-classworlds-*.jar」君はjarファイルなので、
このjarの中のどのクラスがエントリポイントなのかを探さないといけない。
jarの中を見てみるのもいいけど、ちょうど良くpom.xmlがあったので、
「jar」というキーワードで検索してみたところ、以下の記述を発見。
<plugin> <artifactId>maven-jar-plugin</artifactId> <version>2.5</version> <configuration> <archive> <manifest> <mainClass>org.codehaus.plexus.classworlds.launcher.Launcher</mainClass> </manifest> </archive> </configuration> </plugin>
pomの記載通り、mainClassは「org.codehaus.plexus.classworlds.launcher.Launcher」らしいので、
こいつのmain()メソッドが今回の旅の入り口(エントリポイント)ってことになるわけだっ。
で、以下のがその入り口です。
/** * Launch the launcher from the command line. * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception, * or some other code for an application error. * * @param args The application command-line arguments. */ public static void main( String[] args ) { try { int exitCode = mainWithExitCode( args ); System.exit( exitCode ); } catch ( Exception e ) { e.printStackTrace(); System.exit( 100 ); } } /** * Launch the launcher. * * @param args The application command-line arguments. * @return an integer exit code * @throws Exception If an error occurs. */ public static int mainWithExitCode( String[] args ) throws Exception { String classworldsConf = System.getProperty( CLASSWORLDS_CONF ); InputStream is; Launcher launcher = new Launcher(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); launcher.setSystemClassLoader( cl ); if ( classworldsConf != null ) { is = new FileInputStream( classworldsConf ); } else { if ( "true".equals( System.getProperty( "classworlds.bootstrapped" ) ) ) { is = cl.getResourceAsStream( UBERJAR_CONF_DIR + CLASSWORLDS_CONF ); } else { is = cl.getResourceAsStream( CLASSWORLDS_CONF ); } } if ( is == null ) { throw new Exception( "classworlds configuration not specified nor found in the classpath" ); } launcher.configure( is ); is.close(); try { launcher.launch( args ); } catch ( InvocationTargetException e ) { ClassRealm realm = launcher.getWorld().getRealm( launcher.getMainRealmName() ); URL[] constituents = realm.getURLs(); System.out.println( "---------------------------------------------------" ); for ( int i = 0; i < constituents.length; i++ ) { System.out.println( "constituent[" + i + "]: " + constituents[i] ); } System.out.println( "---------------------------------------------------" ); // Decode ITE (if we can) Throwable t = e.getTargetException(); if ( t instanceof Exception ) { throw (Exception) t; } if ( t instanceof Error ) { throw (Error) t; } // Else just toss the ITE throw e; } return launcher.getExitCode(); }
今回は、入り口をめっけたので、次回はこの入り口をぶらつきつつ、その先に進んで行くとします。
ぶらコード:maven:その2(Plexusその1)
Plexus周辺をぶらぶらしてみる。
前回は、
Plexus – Plexus Overview
という認識だったが、どうやらIoCするライブラリでもないようだ。
概要はこちら
Plexus – Plexus Overview
最初はIoCのライブラリだったけど、今はIoCの部分は「Eclipse Sisu」ってのに置き換わっていて、
別の方向を目指しているようだ。
google翻訳がかなりいい線いってたので、とりあえず貼っておく。
重要なお知らせ
Plexusプロジェクトは以前はIoC Plexus Containerに重点を置いていました。現在はEclipse Sisuに置き換えられており、Google Guiceの拡張機能として書き直されています。IoCの部分が廃止されたとしても、私たちは参照のためにIoC全体の内容を保持しました。Plexusプロジェクトは、ソフトウェアプロジェクトを作成して実行するための完全なソフトウェアスタックを提供します。Plexusコンテナに基づいて、アプリケーションはコンポーネント指向プログラミングを利用して、モジュール化された再利用可能なコンポーネントを構築し、容易に組み立てて再利用することができます。
Plexusは、Spring Frameworkのような他のIoC(Inversion-of-Control)や依存性注入フレームワークと似ていますが、以下のような多くの機能をサポートする本格的なコンテナです。
・コンポーネントのライフサイクル
・コンポーネントのインスタンス化の戦略
・ネストされたコンテナ
・コンポーネント構成
・自動配線
・コンポーネントの依存関係
・コンストラクタインジェクション、セッターインジェクション、およびプライベートフィールドインジェクションを含むさまざまな依存性注入技術。Plexusと他の製品との違いの詳細については、機能の比較ページを参照してください。
プレクサスプロジェクトは、このような突堤、ベロシティ、Hibernateは、国際化、およびなどの一般的なタスクとツールキットのために事前に構築されたコンポーネントの数を提供し、より多くの。しかし、PlexusはSpring、Avalon、Pico Containerなどの他のIoCフレームワーク用に書かれた既存のコンポーネントをそのまま使用することができます。また、既存のコードをPlexus Container内で再利用することもできます。
Plexusには、アプリケーションを実行できるアプリケーションサーバーが付属しているため、必要に応じてサービスの実行などの一般的な機能を備えたスタンドアロンの実行可能な配布を簡単に作成できます。ただし、Plexusアプリケーションは、Java EEアプリケーションやWebアプリケーションを含む既存のアプリケーションにコンテナを埋め込むことで、どのような環境でも実行できます。
PlexusのコンポーネントはJavaで記述する必要はなく、Jython、JRuby、Beanshell、Groovyのコンポーネントファクトリが存在します。
Plexusコンテナは現在、いくつかのアプリケーションとフレームワーク、特にMaven 2.0とWebwork 2.2(Struts Action Framework 2.0)で使用されています。
Plexus に関するよくある質問への回答はFAQをご覧ください。
どうやら、ソフトウェア開発のフレームワークってことみたいだね。
でもやっぱりマイナーだよね。ほとんど聞いたことない。
と思ってたら、FAQに以下のような自虐的とも取れる記述がw
Plexus – Frequently Asked Questions
なぜPlexusは普及していないのですか?
Plexusは何年も前から存在していましたが、スタンドアロン製品として広く普及したことはありません。開発の焦点は、Maven 2.0のプラグインモデルなどの高度なアプリケーションアーキテクチャのニーズを容易にすることができるコンテナを提供することでした。Maven 2.0の安定版リリースでは、Maven 2.0プラグインの作成に使用されるため、スタンドアロン製品としてのPlexusが注目を集めており、一般的なアプリケーション開発に提供できるメリットが見られます。
さらに、Plexusはアプリケーションコンテナを必要とする人にとって魅力的になってきましたが、Springのような一般的なフレームワークには必要な機能がありません。
今後のPlexus 1.0のリリース、およびその高度な機能と他のフレームワークとの統合により、Plexusは今後すべてのアプリケーション開発者にとって人気の高い選択肢になることを願っています。
まあ、そういうことのようです。
でも、少なくともmavenという偉大なアプリケーションを影で支えているわけですから、ぶらついてみる価値はあるでしょう。
その他、Plexusサイトの情報を一通り読んでみましたが、イマイチピンと来ませぬ。
なので、実際にmavenで使っているとこを読んでみて、感覚を掴んでいきたいと思います。
意外と良さそうであれば、今後自分でも使うかもしれないしねぇ。
編集モードは後から切り替えられん。なぜ?
うむむ。シンタックスハイライトされてませんな。 また、今度調べましょ。
うむむ。後から切り替えることはできないのね。 仕方ない。とりあえずはあきらめる。 次からは気をつける。
なんで切り替えられんのだ?レンダリングし直すだけでは?
オリジナルの文字列を保持しておらんのか?
いや、そんなことはないな。再編集できてるもんな。
でもそれは「見たまま」編集モードだから?
試しにこのエントリーを「見たまま」モードで編集しつつ、
別タブでも記事を書こうとするとこのエントリーが「Markdown」モードの編集画面で表示される。
この時、残念ながらたくさーーんの、htmlタグとその中に埋め込まれた、
cssのスタイル指定がごにょごにょゴニョォーっと、蠢いているのが見えてしまった。
<blockquote> <p style="margin: 10px 0px; padding: 0px; font-weight: normal; color: #666666; font-family: sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;"> うむむ。 <a class="keyword" style="color: inherit !important; text-decoration: none; word-break: break-word; cursor: pointer !important; word-wrap: break-word; font-size: 16px; font-weight: inherit !important; font-style: inherit !important; pointer-events: auto !important;" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%F3%A5%BF%A5%C3%A5%AF%A5%B9"> シンタックス</a>ハイライトされてませんな。</p> <p style="margin: 10px 0px; padding: 0px; font-weight: normal; color: #666666; font-family: sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;">また、今度調べましょ。</p> </blockquote> <p>うむむ。後から切り替えることはできないのね。</p> <p>仕方ない。とりあえずはあきらめる。</p> <p>次からは気をつける。</p> <p> </p> <p>なんで切り替えられんのだ?</p> <p>レンダリングし直すだけでは?</p> <p>オリジナルの文字列を保持しておらんのか?</p> <p>いや、そんなことはないな。再編集できてるもんな。</p>
つまりは、オリジナルの文字列は保持しておらず、変換後の文字列だけ持っているから、後から変えられんということか。
よくよく考えると見たままモードってのは、裏でhtmlを生成してくれているだけで、
やれ太文字だのアンダーラインだのを引くってのは、
その対象となる文字列をマークアップしているってことなわけだから、 プレーンテキストで保持するのは難しいですわな。
はてなブログのソース読んで確かめたい。 はてなのエンジニアの皆様、変なとこ覗いてしまってすみません。。。
こんなところが気になるのも、エンジニアの性ということでお許しを。
ぶらコード:maven:その1
mavenさんのソースコードを、いつだったかローカルに落としてきてる。
きちんとしたコンテンツにまとめる際には、どっから落としてきたのか、とかちゃんと書いておく必要があるよね。
とりあえず落としてしてきてビルドを通そうと思ったけど、結構苦労した。
さらにmvn eclipse:eclipseやってeclipseに取り込んだりしたけど、
さて、どっから読もうか。。。
単純に考えるとmain()メソッドがどこかにあって、そこがエントリポイントなはずなんだけど、モジュールもたくさんあるし、どっからどこが呼ばれるのか、とかはただ眺めてても分からんもんだ。
で、正攻法としていつも使っている使い方を試みて、そいつを実行した時に何が動くのかを見れば良いと考えるわけだ。
とりあえずこの記事を書いているのは、MacなのでMac上で以下のコマンドを打つ。
which mvn
/usr/local/bin/mvn
で、上記のmvnさんについて調べると、どうやらシェルスクリプトのようですね。
$ file /usr/local/bin/mvn
/usr/local/bin/mvn: Bourne-Again shell script text executable
では、早速中身を拝見してみましょう。
$ less /usr/local/bin/mvn
#!/bin/bash
JAVA_HOME="${JAVA_HOME:-$(/usr/libexec/java_home)}" exec "/usr/local/Cellar/maven/3.3.3/libexec/bin/mvn" "$@"
うーーん。なんかたらい回しにされてる??
環境変数のJAVA_HOMEを設定した後に、別のコマンドを実行しているみたいっすね。
では、こやつが何者なのかを調べてみると、結局さらに別のシェルスクリプトを実行していると。
$ file /usr/local/Cellar/maven/3.3.3/libexec/bin/mvn
/usr/local/Cellar/maven/3.3.3/libexec/bin/mvn: POSIX shell script text executable
で、「/usr/local/Cellar/maven/3.3.3/libexec/bin/mvn」の中身を見るとちょっと長いスクリプトになっているから、単純に貼り付けるも見通し悪そう。
ごにょごにょと色々やってるようだけど、JAVA_HOMEとかM2_HOMEとかチェックしたり、設定したりしてますね。cygwinようの分岐とかもあったりする。
で、紆余曲折を経て、結局やりたいのは、以下に転記した最後の数行なんだろう。
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "${M2_HOME}"/boot/plexus-classworlds-*.jar \
"-Dclassworlds.conf=${M2_HOME}/bin/m2.conf" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${CLASSWORLDS_LAUNCHER} "$@"
変数を参照してるから分かりにくくなってるけど、「plexus-classworlds-*.jar」とかいうjarファイルを指定して、javaコマンドを実行しているということだな。あとは実行時のオプションだろう。
で、つまりは、このjarが何者なのかということと、渡しているオプションを把握せんと先に進めないということだ。やれやれ。(やれやれと思いながらも、実は楽しい)
で、このjarが何者なのかを調べないといけない。
キーワードは、plexus。
とりあえずググってみたところ、以下のサイトを見つけた。
どうやら、maven用に開発されたIoC(Inversion of Contorl)するライブラリのようだね。
日本語の情報が出てこないから、かなりマイナーっぽいな。
次回は、こいつを調べるところから始めるとしよう。
最初のぶらコード(まずはmavenのソースを読むことにしようかなという、だらだらとした話)
ブログのタイトル的にはダメな感じなんでしょうね。
読者を意識していない、キャッチーでないタイトル・・
ま、それはさて置き、とりあえず最初のぶらコード。
(ぶらコード=コードの世界をぶらぶらすること)
JVMが面白い。Javaという言語というより、その下のJVM(Java Virtual Machine)という環境がおもろい。複数のOSを抽象化しているレイヤーとか面白いし、中間コードとしてのバイトコードが面白い。
JVM上で動作するいわゆるJVM言語はJavaとかScalaに限らず、JRubyとかJythonとか、色んな言語のスタイルで書きつつ、最後はJavaバイトコードに変換してしまえば、もれなくJVMで動かせてしまうというのが面白い。
Ruby環境のc言語実装とか、JavascriptのV8Engine とか色んな仮想マシンがあると思うが、一番利用されていて、実績もあってその結果一番進化しているのがJVMではなかろうか。
VMは大体がGC(Garbage Collection)を搭載してると思うが、JavaのG1GCとかあれ程進化した実装を持っている環境もなかなかないだろう。
そんでもって、JIT(Just In Time Conpiler)もおもろい。実行中のJavaバイトコードを動的にプロファイリングしながら動的に最適化してしまうってところが面白い。その分性能テストとかで挙動が掴みにくくなったりして苦労することもあるが、動的最適化によって、c++よりも早くなることがあったりするところが、また面白い。
このバイトコードのレイヤーがあるから、バイトコードインジェクションとかAOPとか出来ちゃうところが、また面白い。
cとかc++とかも好きだけど、動的にコード書き変えちゃうとか、VMならではの面白い展開を考えると、やっぱりJava関係のソース読んでいく方が展開が楽しそう。
でも、JVM自体はそのほとんどがc++で書かれているわけだし、どうしたってJVMの下のOSについても知りたくなるわけで、そうなるとcのソースも読むことになるので、言語を限定しているわけでもない。
ただ単に、JVMの上(TomcatとかJavaで実装されたアプリケーション)とか、下(JVMが抽象化しているLinuxとかWindows)とか、横(V8エンジンとかの他のVM)とかをぶらつくと面白いソースがいっぱいありそうで、ワクワクするので、JVM周辺をぶらつこうかと。
ということでとりあえずJVM、さらに言語的には、とりあえずど真ん中のJavaで実装された何かをターゲットにぶらついてみたい。
で適当に物色していると、結局ソースをダウンロードとかしてきて、ローカルでビルドしたり、eclipseに取り込んでみたりするわけだが、そこでまずビルドでつまずく、、、。
大体が、mavenのpom.xmlが用意されていたりするので、プロジェクトの構成とかはある程度つかめるんだが、マルチモジュール構成になっていたり、依存関係の解決とかに失敗してビルド失敗する。
mavenは普段の開発でも使っているから、不慣れなワケでもないが、分かっていないことも多い。トラブった時の解決に時間がかかる。
なので、いっその事mavenのソースを一通り、読んでしまって、mavenへの苦手意識を薄めていくことから、始めるとしますか。(ここに来て、ようやく結論。なげーよ。)