How to expand variable into nested directory in ma

2020-03-25 13:24发布

问题:

Suppose I have set groupId to com.example, and artifactId to fancy.project, and now I want to create a archetype, such that when created, expands into the following structure:

|--src
  |--main
    |--com
      |--example
        |-fancy
          |-project
            |-App.java

That is, I wonder how to expand a variable into a nested directory.

I understand that the dual-underscore-wrapped variables will be substituted in file/directory names, but I can only get the following with __groupId__.

|--src
  |--main
    |--com.example
      |-fancy.project
        |-App.java

回答1:

As input to the archetype you can specify the package option (which would then follow your input, the groupId and artifactId concatenation if you want, even though it is not always the case and hence provide even more flexibility).

Then, in your archetype you can use the packageInPathFormat option (available since archetype 2.2) which would replace any dot . into slash \ and as such transforming it to a path into the generated project.

However, the option is not officially documented (pity) even though supported and works fine for such a scenario.

Looking at the code, the DefaultFilesetArchetypeGenerator and its getPackageInPathFormat provide the concerned transformation from the package option to a path, while the org.apache.maven.archetype.common.Constants.PACKAGE_IN_PATH_FORMAT is the official entry point for this option.

Some externals pointers on the usage of this option:

  • http://geekofficedog.blogspot.be/2013/08/creating-maven-archetypes-tutorial.html
  • http://www.theotherian.com/2012/05/maven-archetypes-part-2-how-do-i-create.html

To further explain:

  • You can have the __packageInPathFormat__ folder under your src/main/java, for example
  • The __packageInPathFormat would then be replaced by the package option transforming dots into slashes
  • The package option has a default value to groupId, so if you don't specify it, for a groupId with value com.sample, the path would be com/sample
  • You can hence specify at invocation time the package desired via -Dpackage=your.package repeating the values for -DgroupId and -DartifactId (a bit verbose and error prone though), the final result will actually be what you expected (transformed to correct path).
  • You can specify new default values via a archetype-metada.xml file, as specified in the official documentation, via the requiredProperties section, you could have something like:

    <requiredProperties>
        <requiredProperty key="package">
            <defaultValue>__groupId__.__artifactId__</defaultValue>
        </requiredProperty>
    </requiredProperties>
    

    However, the generated path would then be com.sample/artifactid rather than com/sample/artifactid. Hence it would not work as expected due to the processing workflow which would replace the placeholders after transforming it to a path (pity!).
    (Note: it would transform the dot we provided as configured value, but would then not transform dots into the replaced placeholders).

    As of a quick code analysis, seems like the DefaultFilesetArchetypeGenerator class in its generateArchetype method is preparing the context too early (in its prepareVelocityContext method, where the packageInPathFormat is transformed and added to the context), then the context is passed to processArchetypeTemplate* methods which would eventually invoke the Velocity engine (which is going to replace placeholders then). I am not a Velocity expert though, hence I may miss some glue, but the observed behavior and the code workflow seem to lead to this conclusion.