それでは実際に設定ファイル(classworlds.conf)を解析する、ConfigurationParser#parse(classworlds.conf)メソッドに入って行きます。

今回はmavenのぶらり旅なので、mavenの起動時に実際に読み込まれるclassworlds.conf(m2.conf)を見てみる。

main is org.apache.maven.cli.MavenCli from plexus.core

set maven.home default ${user.home}/m2

[plexus.core]
optionally ${maven.home}/lib/ext/*.jar
load       ${maven.home}/lib/*.jar
load       ${maven.home}/conf/logging

今から旅するのは、要するに上の設定ファイルをどの様にパースしていくか、を見ていくということになる訳だ。また、それぞれの記述が何を意味しているのかも探っていく必要がある。私がネット上で検索する限り、このclassworlds.confの仕様は見当たらなかった。

plexus-classworlds-*.jarを使うためには、classworlds.confを適切に設定してあげないといけないことは想像できるが、使用方法の説明もないので、どの様に書けば良いのかさっぱり分からん。こんな時どうすれば良いか。

通常、仕様や仕様方法のドキュメントがなければお手上げだよね。でも、今回は素晴らしいことにオープンソースなので、仕様方法がわからなければソースを読めば良いだけなのだ。下手なドキュメントを見るより、よっぽど明確に分かることもある。もちろん、仕様が複雑な場合はソースを読むのも大変な場合もあるが、今回のはそれほどでもなさそう。

では、まずは、parse()メソッドの全体を見渡してみしょ。引数のInputStreamからBufferdReaderを作成して、そのreaderから一行ずつ読み込んでいるようですな。while( ture )として、reader.readLine()がnullを返すまで全行読み込むという仕組みということだな。その後は、line.starsWith( MAIN_PREFIX )のように行の先頭の文字列が何であるのかによって、処理を分岐させて設定ファイルを解析している。

public void parse( InputStream is )
    throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException
{
    BufferedReader reader = new BufferedReader( new InputStreamReader( is, "UTF-8" ) );

    String line = null;

    int lineNo = 0;

    boolean mainSet = false;

    String curRealm = null;

    while ( true )
    {
        line = reader.readLine();

        if ( line == null )
        {
            break;
        }

        ++lineNo;
        line = line.trim();

        if ( canIgnore( line ) )
        {
            continue;
        }

        if ( line.startsWith( MAIN_PREFIX ) )
        {
            /* 詳細は後述 */
        }
        else if ( line.startsWith( SET_PREFIX ) )
        {
            /* 詳細は後述 */
        }
        else if ( line.startsWith( "[" ) )
        {
            /* 詳細は後述 */
        }
        else if ( line.startsWith( IMPORT_PREFIX ) )
        {
            /* 詳細は後述 */
        }
        else if ( line.startsWith( LOAD_PREFIX ) )
        {
            /* 詳細は後述 */
        }
        else if ( line.startsWith( OPTIONALLY_PREFIX ) )
        {
            /* 詳細は後述 */
        }
        else
        {
            throw new ConfigurationException( "Unhandled configuration", lineNo, line );
        }
    }
    reader.close();
}

処理の分岐は以下の6パターンあるようだ。

  1. if ( line.startsWith( MAIN_PREFIX ) )
  2. else if ( line.startsWith( SET_PREFIX ) )
  3. else if ( line.startsWith( "[" ) )
  4. else if ( line.startsWith( IMPORT_PREFIX ) )
  5. else if ( line.startsWith( LOAD_PREFIX ) )
  6. else if ( line.startsWith( OPTIONALLY_PREFIX ) )

そしてそれぞれの定数の定義は以下のようになっている。

public static final String MAIN_PREFIX = "main is";
public static final String SET_PREFIX = "set";
public static final String IMPORT_PREFIX = "import";
public static final String LOAD_PREFIX = "load";
public static final String OPTIONALLY_PREFIX = "optionally";

今回の解析対象である、m2.confの中には、IMPORT_PREFIX以外の全てが登場しているな。

では、最初の「MAIN_PREFIX = "main is"」から見てみますか。

main is org.apache.maven.cli.MavenCli from plexus.core

上が設定ファイルの解析対象行。下が解析ロジック。

if ( line.startsWith( MAIN_PREFIX ) )
{
    if ( mainSet )
    {
        throw new ConfigurationException( "Duplicate main configuration", lineNo, line );
    }

    String conf = line.substring( MAIN_PREFIX.length() ).trim();

    int fromLoc = conf.indexOf( "from" );

    if ( fromLoc < 0 )
    {
        throw new ConfigurationException( "Missing from clause", lineNo, line );
    }

    String mainClassName = filter( conf.substring( 0, fromLoc ).trim() );

    String mainRealmName = filter( conf.substring( fromLoc + 4 ).trim() );

    this.handler.setAppMain( mainClassName, mainRealmName );

    mainSet = true;
}

どうやらthrow new ConfigurationException( "Duplicate main configuration", lineNo, line );

とあるので、「main is」で始まるは一つしか書いてはいけないようだ。という感じで、このように例外をスローするようにコーディングしておくと、特にコメント等で書き表さずとも、ソースコードそのもので、仕様を明確に伝えられる。

さらには、throw new ConfigurationException( "Missing from clause", lineNo, line );

というロジックも続きます。ここでも例外をスローするようになっていて、ここから読み解ける仕様としては、「main is」で始まる行には、必ず「from」というキーワードが含まれるfrom句がなければならないようだ。

で、結局のところ、「from」より前をmainClassNameとして取り出し、fromより後を、mainRelmNameとして取り出している。

つまりは、以下のようになる訳だ。

String mainClassName = "org.apache.maven.cli.MavenCli";
String mainRealmName = "plexus.core";

mainClassNameというのは分かりやすいとして、mainRelmNameってのは何でしょね。。そのうち理解していくとしますか。

お次は以下のsetで始まる行のパース処理。

set maven.home default ${user.home}/m2

パースするコードは次の通り。

else if ( line.startsWith( SET_PREFIX ) )
{
    String conf = line.substring( SET_PREFIX.length() ).trim();

    int usingLoc = conf.indexOf( " using" ) + 1;

    String property = null;

    String propertiesFileName = null;

    if ( usingLoc > 0 )
    {
        property = conf.substring( 0, usingLoc ).trim();

        propertiesFileName = filter( conf.substring( usingLoc + 5 ).trim() );

        conf = propertiesFileName;
    }

    String defaultValue = null;

    int defaultLoc = conf.indexOf( " default" ) + 1;

    if ( defaultLoc > 0 )
    {
        defaultValue = filter( conf.substring( defaultLoc + 7 ).trim() );

        if ( property == null )
        {
            property = conf.substring( 0, defaultLoc ).trim();
        }
        else
        {
            propertiesFileName = conf.substring( 0, defaultLoc ).trim();
        }
    }

    String value = systemProperties.getProperty( property );

    if ( value != null )
    {
        continue;
    }

    if ( propertiesFileName != null )
    {
        File propertiesFile = new File( propertiesFileName );

        if ( propertiesFile.exists() )
        {
            Properties properties = new Properties();

            try
            {
                properties.load( new FileInputStream( propertiesFileName ) );

                value = properties.getProperty( property );
            }
            catch ( Exception e )
            {
                // do nothing
            }
        }
    }

    if ( value == null && defaultValue != null )
    {
        value = defaultValue;
    }

    if ( value != null )
    {
        value = filter( value );
        systemProperties.setProperty( property, value );
    }
}

今回は例外をスローしている箇所はないので、書式の厳密な仕様は特なさそう。書き方を誤れば何も起こらないとか、別の例外が発生するとかですかね。で結局のところ、「maven.home」という名前のキーでプロパティに値を設定する処理をしたいみたいですね。「using」というキーワードの後にプロパティファイルの名前が設定されていた場合は、そのファイルから「maven.home」の値を取り出すみたいだが、今回は「using」句は使っていない。その場合、「default」というキーワードにあるキー名を使って、SystemPropertiesから読み込むようだ。すでに設定されている場合は何もしない。設定されていない場合は、default値をSystemPropertiesに書き込む。この時のdefault値は「${user.home}/m2」。

ん?でも、ちょっと待て。「${user.home}/m2」ってなんだ??

上のソースをよーく見ると、filter()というメソッドに「${user.home}/m2」を渡して、その戻り値を使っているな。では、この

filter()メドッドくんを覗いてみよう。

protected String filter( String text )
    throws ConfigurationException
{
    String result = "";

    int cur = 0;
    int textLen = text.length();

    int propStart = -1;
    int propStop = -1;

    String propName = null;
    String propValue = null;

    while ( cur < textLen )
    {
        propStart = text.indexOf( "${", cur );

        if ( propStart < 0 )
        {
            break;
        }

        result += text.substring( cur, propStart );

        propStop = text.indexOf( "}", propStart );

        if ( propStop < 0 )
        {
            throw new ConfigurationException( "Unterminated property: " + text.substring( propStart ) );
        }

        propName = text.substring( propStart + 2, propStop );

        propValue = systemProperties.getProperty( propName );

        /* do our best if we are not running from surefire */
        if ( propName.equals( "basedir" ) && ( propValue == null || propValue.equals( "" ) ) )
        {
            propValue = ( new File( "" ) ).getAbsolutePath();

        }

        if ( propValue == null )
        {
            throw new ConfigurationException( "No such property: " + propName );
        }
        result += propValue;

        cur = propStop + 1;
    }

    result += text.substring( cur );

    return result;
}

ざっと見る限「${」と「}」で囲まれた部分、今回で言うと「user.home」という文字列を取り出して、この文字列をキーとしてSystemPropertiesから値を読み出してますね。「}」がなかったり、取り出した文字列のキーに該当する値がなかった場合は、それぞれ適切な例外をスローしてますな。で、結局のところ、システムプロパティのuser.homeに設定されているパス+「/m2」というパスを作成して呼び出し元に返しているってことのようだ。

なーのーでー、

set maven.home default ${user.home}/m2

という設定を読み込むとシステムプロパティに「mavne.home」というプロパティが追加されて、そこには「user.home/m2」というパスがセットされることになる。

これで最初の2行の解析処理が完了した。お次は以下の4行。

[plexus.core]
optionally ${maven.home}/lib/ext/*.jar
load       ${maven.home}/lib/*.jar
load       ${maven.home}/conf/logging

results matching ""

    No results matching ""