问题
I am creating an archetype and want to create a project wide available property containing things like the current date and an all lower case variant of the artifact id. I found the following post on Stackoverflow, that shows how it normally should be possible.
I tried adding this to the archetype-metadata.xml like so:
...
<requiredProperty key="artifactIdLower">
<defaultValue>${artifactId.toLowerCase()}</defaultValue>
</requiredProperty>
<requiredProperty key="ldt">
<defaultValue>${package.getClass().forName("java.time.LocalDateTime").getMethod("now").invoke(null)}</defaultValue>
</requiredProperty>
<requiredProperty key="dtf">
<defaultValue>${package.getClass().forName("java.time.format.DateTimeFormatter").getMethod("ofPattern", $package.getClass().forName("java.lang.String")).invoke(null, "dd MMM yyyy")}</defaultValue>
</requiredProperty>
<requiredProperty key="date">
<defaultValue>${ldt.format($dtf)}</defaultValue>
</requiredProperty>
...
The property artifactIdLower
works like a charm, but the ldt
, dtf
and date
don't seem to work, giving the error:
Null reference [template 'dtf', line 1, column 1] : ${package.getClass().forName("java.time.format.DateTimeFormatter").getMethod("ofPattern", $package.getClass().forName("java.lang.String")).invoke(null, "dd MMM yyyy")} cannot be resolved.
After that I tried to see where in the chain this null reference comes from. I was able to set pkgClass
to $package.getClass()
(or to $package.Class
),
but after that I was unable to set $strClass
to $package.getClass().forName("java.lang.String")
or to $pkgClass.forName("java.lang.String")
(funnily enough, both pkgClass
and strClass
should be a Class object).
This made me wonder if there is a restriction on using reflection inside the archetype-metadata.xml.
My question is: how can I set dynamically generated property values (like above) that can be used project wide?
I don't want to have to define these properties in every file that I create, because there might be more properties that I want to add later.
Edit:
I tried to instead create a generalproperties.vm
file that contained the #set directives. This file would then be loaded by every file on the first line using #parse("generalproperties.vm"). While I did get the file to be parsed from within the pom.xml file, it didnt behave as I wanted.
The following input
test
$null
#set( $ldtClass = $package.getClass().forName("java.time.LocalDateTime") )
$ldtClass.Name
$ldtClass.getMethod("now")
#set( $ldtNowMethod = $ldtClass.getMethod("now") )
$ldtNowMethod.Name
#set( $clsLoader = $package.getClass().getClassLoader() )
$clsLoader
#set( $ldtClass2 = $clsLoader.loadClass("java.time.LocalDateTime") )
$ldtClass2.Name
$ldtClass2.getMethod("now")
#set( $ldtNowMethod2 = $ldtClass2.getMethod("now") )
$ldtNowMethod2.Name
#set( $ldt = $ldtNowMethod2.invoke($null) )
$ldt
#set( $dtf = $package.getClass().forName("java.time.format.DateTimeFormatter").getMethod("ofPattern", $package.getClass().forName("java.lang.String")).invoke($null, "yyyy/MM/dd HH:mm:ss") )
$dtf
Generated the following output:
test
$null
java.time.LocalDateTime
$ldtClass.getMethod("now")
$ldtNowMethod.Name
$clsLoader
$ldtClass2.Name
$ldtClass2.getMethod("now")
$ldtNowMethod2.Name
$ldt
$dtf
The first 3 outputs are as expected, but after that I don't get the results I want. If someone is able to solve either of the above issues (with the metadata file or the generalproperties file), it would be highly appreciated.
回答1:
This should work:
...
<requiredProperty key="date">
<defaultValue>${package.getClass().forName("java.time.LocalDateTime").getMethod("now").invoke(null).format($package.Class.forName("java.time.format.DateTimeFormatter").getMethod("ofPattern", $package.Class).invoke(null, "dd MMM yyyy"))}</defaultValue>
</requiredProperty>
...
回答2:
Maybe it could help somebody what I noticed.
When I had something like:
<requiredProperty key="packageId"></requiredProperty>
<requiredProperty key="packageIdInPathFormat">
<defaultValue>${package.getClass().getMethod("replace").invoke($packageId, ".", "/")}</defaultValue>
</requiredProperty>
It was not evaluating expression in property packageIdInPathFormat
.
But when I did something like:
<requiredProperty key="packageId"></requiredProperty>
<requiredProperty key="tempPackageId">
<defaultValue>${packageId}</defaultValue>
</requiredProperty>
<requiredProperty key="packageIdInPathFormat">
<defaultValue>${tempPackageId.replace(".", "/")}</defaultValue>
</requiredProperty>
Expression was evaluated.
回答3:
The only restriction I could think of is if you are using the SecureUberspector
instead of the standard Uberspector in your Velocity configuration properties. But you wouldn't be able to get the java.time.LocalDateTime
class, so it's not the case, unless maybe if you are using an old Velocity version. With Velocity 1.7 I am able to get a reference to the now()
method and to call it.
Also, are you sure that the direct Java version is working properly? For instance, I read in the Class.getClassLoader()
javadoc that "some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader."
来源:https://stackoverflow.com/questions/44350467/setting-dynamic-velocity-properties-for-archetype-maven-projects