How can I provide custom logic in a Maven archetype? - maven

I'm interested in creating a Maven archetype, and I think I have most of the basics down. However, one thing I'm stuck on is that sometimes I want to use custom logic to fill in a template. For example, if somebody generates my archetype and specifies the artifactId as hello-world, I'd like to generate a class named HelloWorld that simply prints out "Hello World!" to the console. If another person generates it with artifactId = howdy-there, the genned class would be HowdyThere and it would print out "Howdy There!".
I know that under the covers, Maven's archetype mechanism leverages the Velocity Template Engine, so I read this article on creating custom directives. This seemed to be what I was looking for, so I created a class called HyphenatedToCamelCaseDirective that extends org.apache.velocity.runtime.directive.Directive. In that class, my getName() implementation returns "hyphenatedCamelCase". In my archetype-metadata.xml file, I have the following...
<requiredProperties>
<requiredProperty key="userdirective">
<defaultValue>com.jlarge.HyphenatedToCamelCaseDirective</defaultValue>
</requiredProperty>
</requiredProperties>
My template class looks like this...
package ${package};
public class #hyphenatedToCamelCase('$artifactId') {
// userdirective = $userdirective
public static void main(String[] args) {
System.out.println("#hyphenatedToCamelCase('$artifactId')"));
}
}
After I install my archetype and then do an archetype:generate by specifying artifactId = howdy-there and groupId = f1.f2, the resulting class looks like this...
package f1.f2;
public class #hyphenatedToCamelCase('howdy-there') {
// userdirective = com.jlarge.HyphenatedToCamelCaseDirective
public static void main(String[] args) {
System.out.println("#hyphenatedToCamelCase('howdy-there')"));
}
}
The result shows that even though userdirective is being set the way I expected it to, It's not evaulating the #hyphenatedToCamelCase directives like I was hoping. In the directive class, I have the render method logging a message to System.out, but that message doesn't show up in the console, so that leads me to believe that the method never got executed during archetype:generate.
Am I missing something simple here, or is this approach just not the way to go?

The required properties section of the archetype-metatadata xml is used to pass additional properties to the velocity context, it is not meant for passing velocity engine configuration. So setting a property called userDirective will only make the variable $userDirective availble and not add a custom directive to the velocity engine.
If you see the source code, the velocity engine used by maven-archetype plugin does not depend on any external property source for its configuration. The code that generates the project relies on an autowired (by the plexus container) implementation of VelocityComponent.
This is the code where the velocity engine is initialized:
public void initialize()
throws InitializationException
{
engine = new VelocityEngine();
// avoid "unable to find resource 'VM_global_library.vm' in any resource loader."
engine.setProperty( "velocimacro.library", "" );
engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, this );
if ( properties != null )
{
for ( Enumeration e = properties.propertyNames(); e.hasMoreElements(); )
{
String key = e.nextElement().toString();
String value = properties.getProperty( key );
engine.setProperty( key, value );
getLogger().debug( "Setting property: " + key + " => '" + value + "'." );
}
}
try
{
engine.init();
}
catch ( Exception e )
{
throw new InitializationException( "Cannot start the velocity engine: ", e );
}
}
There is a hacky way of adding your custom directive. The properties you see above are read from the components.xml file in the plexus-velocity-1.1.8.jar. So open this file and add your configuration property
<component-set>
<components>
<component>
<role>org.codehaus.plexus.velocity.VelocityComponent</role>
<role-hint>default</role-hint>
<implementation>org.codehaus.plexus.velocity.DefaultVelocityComponent</implementation>
<configuration>
<properties>
<property>
<name>resource.loader</name>
<value>classpath,site</value>
</property>
...
<property>
<name>userdirective</name>
<value>com.jlarge.HyphenatedToCamelCaseDirective</value>
</property>
</properties>
</configuration>
</component>
</components>
</component-set>
Next add your custom directive class file to this jar and run archetype:generate.
As you see this is very fraglie and you will need to figure a way to distribute this hacked plexus-velocity jar. Depending on what you are planning to use this archetype for it might be worth the effort.

Related

Configuring a custom Gradle sourceSet using a closure

I'm trying to develop a Gradle plugin for a language I use (SystemVerilog). I'm still experimenting and figuring things out. Before I write the entire thing as a plugin, I thought it would be best to try out the different parts I need inside a build script, to get a feel of how things should work.
I'm trying to define a container of source sets, similar to how the Java plugin does it. I'd like to be able to use a closure when configuring a source set. Concretely, I'd like to be able to do the following:
sourceSets {
main {
sv {
include '*.sv'
}
}
}
I defined my own sourceSet class:
class SourceSet implements Named {
final String name
final ObjectFactory objectFactory
#Inject
SourceSet(String name, ObjectFactory objectFactory) {
this.name = name
this.objectFactory = objectFactory
}
SourceDirectorySet getSv() {
SourceDirectorySet sv = objectFactory.sourceDirectorySet('sv',
'SystemVerilog source')
sv.srcDir("src/${name}/sv")
return sv
}
SourceDirectorySet sv(#Nullable Closure configureClosure) {
configure(configureClosure, getSv());
return this;
}
}
I'm using org.gradle.api.file.SourceDirectorySet because that already implements PatternFilterable, so it should give me access to include, exclude, etc.
If I understand the concept correctly, the sv(#Nullable Closure configureClosure) method is the one that gives me the ability to write sv { ... } to configure via a closure.
To add the sourceSets property to the project, I did the following:
project.extensions.add("sourceSets",
project.objects.domainObjectContainer(SourceSet.class))
As per the Gradle docs, this should give me the possibility to configure sourceSets using a closure. This site, which details using custom types, states that by using NamedDomainObjectContainer, Gradle will provide a DSL that build scripts can use to define and configure elements. This would be the sourceSets { ... } part. This should also be the sourceSets { main { ... } } part.
If I create a sourceSet for main and use it in a task, then everything works fine:
project.sourceSets.create('main')
task compile(type: Task) {
println 'Compiling source files'
println project.sourceSets.main.sv.files
}
If I try to configure the main source set to only include files with the .sv extension, then I get an error:
sourceSets {
main {
sv {
include '*.sv'
}
}
}
I get the following error:
No signature of method: build_47mnuak4y5k86udjcp7v5dkwm.sourceSets() is applicable for argument types: (build_47mnuak4y5k86udjcp7v5dkwm$_run_closure1) values: [build_47mnuak4y5k86udjcp7v5dkwm$_run_closure1#effb286]
I don't know what I'm doing wrong. I'm sure it's just a simple thing that I'm forgetting. Does anyone have an idea of what that might be?
I figured out what was going wrong. It was a combination of poor copy/paste skills and the fact that Groovy is a dynamic language.
First, let's look at the definition of the sv(Closure) function again:
SourceDirectorySet sv(#Nullable Closure configureClosure) {
configure(configureClosure, getSv());
return this;
}
Once I moved this code to an own Groovy file and used the IDE to show me what is getting called, I noticed that it wasn't calling the function I expected. I was expecting a call to org.gradle.util.ConfigureUtil.configure. Since this is part of the public API, I expected it to be imported by default in the build script. As this page states, this is not the case.
To solve the issue, it's enough to add the following import:
import static org.gradle.util.ConfigureUtil.configure
This will get rid of the cryptic closure related error. It is replaced by the following error, though:
Cannot cast object 'SourceSet_Decorated#a6abab9' with class 'SourceSet_Decorated' to class 'org.gradle.api.file.SourceDirectorySet'
This is caused by the copy/paste error I mentioned. When I wrote the SourceSet class, I drew heavily from org.gradle.api.tasks.SourceSet (and org.gradle.api.internal.tasks.DefaultSourceSet). If we look at the java(Closure) method there, we'll see it has the following signature:
SourceSet java(#Nullable Closure configureClosure);
Notice that it returns SourceSet and not SourceDirectorySet like in my code. Using the proper return type fixes the issue:
SourceSet sv(#Nullable Closure configureClosure)
With this new return type, let's look again at the configuration code for the source set:
sourceSets {
main {
sv {
include '*.sv'
}
}
}
Initially, I thought it was supposed to work as follows: pass main { ... } as a Closure to sourceSets, pass sv { ... } as a Closure to main, and handle the include ... part inside sourceDirectorySet. I banged my head against the wall for a while, because I couldn't find any code in that class hierarchy that takes closures like this.
Now, I think the flow is slightly different: pass main { ... } as a Closure to sourceSets (as initially thought), but call the sv(Closure) function on main (of type sourceSet), passing it { include ... } as the argument.
Bonus: There was one more issue that wasn't related to the "compile" errors I was having.
Even after getting the code to run without errors, it still wasn't behaving as expected. I had some files with the *.svh extension that were still getting picked up. This is because, when calling getSv(), it was creating a new SourceDirectorySet each time. Any configuration that was done previously was getting thrown away each time that this function was called.
Making the sourceDirectorySet a class member and moving its creation to the constructor fixed the issue:
private SourceDirectorySet sv
SourceSet(String name, ObjectFactory objectFactory) {
// ...
sv = objectFactory.sourceDirectorySet('sv',
'SystemVerilog source')
sv.srcDir("src/${name}/sv")
}
SourceDirectorySet getSv() {
return sv
}

And in multiple file conditions does not work on Maven profile

I'm using Maven 3.5+ that I've read that Maven 3.2.2+ supports And condition in activation of profiles. So I've added multiple condition in activation tag of a profile as below:
<activation>
<file>
<exists>${basedir}/src/main/resources/static/index.html</exists>
<missing>${basedir}/src/main/resources/static/app/gen-src/metadata.json</missing>
</file>
</activation>
And I put it in parent's pom.xml. and the profile should execute when the child project contains index.html but does not have metadata.json.
When I compile the child project that has both index.html and metadata.json, the profile activated and plugins going to execute. but the profile should not active in this situation. I think conditions ORed by maven.
Looking at the v3.5.0 ActivationFile javadoc (couldn't find the source yet) and FileProfileActivator sources, currently that does not seem to be possible with multiple files, and there's this issue open.
The file-activation-configuration accepts 2 parameters, one for existing and one for missing file. So both the parameters affect the same configuration, and you can only have one such configuration.
As a result, it will look for either an existing or a missing file, in this order if both values are set, but not for the both of them. Unfortunately I couldn't find a work-around so far...
1) ActivationFile javadoc:
public class ActivationFile
extends Object
implements Serializable, Cloneable, InputLocationTracker
This is the file specification used to activate the profile. The missing value is the location of a file that needs to exist, and if it doesn't, the profile will be activated. On the other hand, exists will test for the existence of the file and if it is there, the profile will be activated.
Variable interpolation for these file specifications is limited to ${basedir}, System properties and request properties.
2) FileProfileActivator sources (please note that I've omitted some interpolation code for the sake of brevity)
#Override
public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
Activation activation = profile.getActivation();
if (activation == null) {
return false;
}
ActivationFile file = activation.getFile();
if (file == null) {
return false;
}
String path;
boolean missing;
if (StringUtils.isNotEmpty(file.getExists())) {
path = file.getExists();
missing = false;
} else if (StringUtils.isNotEmpty(file.getMissing())) {
path = file.getMissing();
missing = true;
} else {
return false;
}
/* ===> interpolation code omitted for the sake of brevity <=== */
// replace activation value with interpolated value
if (missing) {
file.setMissing(path);
} else {
file.setExists(path);
}
File f = new File(path);
if (!f.isAbsolute()) {
return false;
}
boolean fileExists = f.exists();
return missing ? !fileExists : fileExists;
}

Gradle extension as NamedDomainObjectContainer of Closure

I'm trying to build a Gradle plugin that would allow the following:
myPluginConfig {
something1 {
// this is a closure
}
somethingElse {
// this is another closure
}
// more closures here
}
To achieve this I'm fairly certain I need to use a NamedDomainObjectContainer to wrap a Closure collection, so I've set up the following plugin:
class SwitchDependenciesPlugin implements Plugin<Project> {
void apply(Project project) {
// add the extension
project.getExtensions().myPluginConfig = project.container(Closure)
// read the current configuration
NamedDomainObjectContainer<Closure> config = project.myPluginConfig
// test it out, always returns []
System.out.println(config)
}
}
What am I doing wrong, do I need to use project.extensions.create instead? If so, how?
EDIT: my use case consists in adding dependencies according to some variables defined in the project hierarchy. For example, the following configuration would add the red project if the variable red is defined on project.ext, or gson otherwise:
myPluginConfig {
redTrue {
compile project(':red')
}
redFalse {
compile 'com.google.code.gson:gson:2.4'
}
greenTrue {
compile project(':green')
}
}
For this use case I need to have dynamic names for myPluginConfig, and therefore either a Map or a NamedDomainObjectContainer.
Can you elaborate what you try to model here? I think you have two options. One is to use NamedDomainObjectContainer. Here you need a class that represents "something". Have a look at the Gradle userguide chapter about maintaining multiple domain objects (see https://docs.gradle.org/current/userguide/custom_plugins.html#N175CF) in the sample of the userguide, the "thing" is 'Book'. The build-in configuration syntax like you described above comes for free.
If you want to have a syntax like above without the need for maintaining multiple domain objects, you can simply add a method that takes a Closure as a parameter to your Extension class:
void somethingToConfigure(Closure) {
}
You cannot have Closure as a type for NamedDomainObjectContainer simply because the type you use must have a property called name and a public constructor with a single String parameter.
To overcome this, you may create a wrapper around Closure with a name field added.

SONAR 3.7.3 - PMD XPath rule <my custom rule> can't be imported automatically. The rule must be created manually through the SonarQube web interface

I am working on trying to get some custom pmd rules onto our SONAR server so they will show up in our nightly tests. I have an xml file with a bunch of custom rules like so:
<rule class="net.sourceforge.pmd.rules.XPathRule" dfa="false" externalInfoUrl="" message="System.out.print is used" name="MyOrganisation_SystemPrintln" typeResolution="true">
<description>System.(out|err).print is used, consider using a logger.</description>
<priority>5</priority>
<properties>
<property name="xpath">
<value><![CDATA[
//Name[
starts-with(#Image, 'System.out.print')
or
starts-with(#Image, 'System.err.print')
]
]]></value>
</property>
</properties>
<example><![CDATA[
class Foo{
Logger log = Logger.getLogger(Foo.class.getName());
public void testA () {
System.out.println("Entering test");
// Better use this
log.fine("Entering test");
}
}
]]></example>
When I go to the Quality profiles page and make a new profile, giving it the xml file I get a bunch of errors like this:
PMD XPath rule 'MyOrganisation_SystemPrintln' can't be imported
automatically. The rule must be created manually through the SonarQube
web interface.
Which seems clear enough, however when I try and create a new rule by copying the generic xpath rule that is there already and changing it there is nowhere to put the "example" part. (There is only Name, message, xpathQuery and Description) I was wondering if I am missing something that might be the cause of this, and how I can get these rules onto the sonar server?
Thanks very much.
Edit: The PMD version is 1.3, as is the java plugin
Edit2: Another example of a rule:
<rule class="net.sourceforge.pmd.rules.UnusedPrivateFieldRule" dfa="false" externalInfoUrl="" message="Avoid unused private fields such as ''{0}''" name="MyOrganisation_UnusedPrivateField" typeResolution="true">
<description>Detects when a private field is declared and/or assigned a value, but not used.</description>
<priority>5</priority>
<example><![CDATA[
public class Something {
private static int FOO = 2; // Unused
private int i = 5; // Unused
private int j = 6;
public int addOne() {
return j++;
}
}
]]></example>
</rule>
Indeed, there is no possibility in the SonarQube custom rules to declare examples like on the PMD custom rules, you will have to put the examples in the description, e.g using blockquote elements.

Enterprise Library Validation Block - Should validation be placed on class or interface?

I am not sure where the best place to put validation (using the Enterprise Library Validation Block) is? Should it be on the class or on the interface?
Things that may effect it
Validation rules would not be changed in classes which inherit from the interface.
Validation rules would not be changed in classes which inherit from the class.
Inheritance will occur from the class in most cases - I suspect some fringe cases to inherit from the interface (but I would try and avoid it).
The interface main use is for DI which will be done with the Unity block.
The way you are trying to use the Validation Block with DI, I dont think its a problem if you set the attributes at interface level. Also, I dont think it should create problems in the inheritance chain. However, I have mostly seen this block used at class level, with an intent to keep interfaces not over specify things. IMO i dont see a big threat in doing this.
Be very careful here, your test is too simple.
This will not work as you expect for SelfValidation Validators or Class Validators, only for the simple property validators like you have there.
Also, if you are using the PropertyProxyValidator in an ASP.NET page, iI don;t believe it will work either, because it only looks a field validators, not inherited/implemented validators...
Yes big holes in the VAB if you ask me..
For the sake of completeness I decided to write a small test to make sure it would work as expected and it does, I'm just posting it here in case anyone else wants it in future.
using System;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ISpike spike = new Spike();
spike.Name = "A really long name that will fail.";
ValidationResults r = Validation.Validate<ISpike>(spike);
if (!r.IsValid)
{
throw new InvalidOperationException("Validation error found.");
}
}
}
public class Spike : ConsoleApplication1.ISpike
{
public string Name { get; set; }
}
interface ISpike
{
[StringLengthValidator(2, 5)]
string Name { get; set; }
}
}
What version of Enterprise Library are you using for your code example? I tried it using Enterprise Library 5.0, but it didn't work.
I tracked it down to the following section of code w/in the EL5.0 source code:
[namespace Microsoft.Practices.EnterpriseLibrary.Validation]
[public static class Validation]
public static ValidationResults Validate<T>(T target, ValidationSpecificationSource source)
{
Type targetType = target != null ? target.GetType() : typeof(T);
Validator validator = ValidationFactory.CreateValidator(targetType, source);
return validator.Validate(target);
}
If the target object is defined, then target.GetType() will return the most specific class definition, NOT the interface definition.
My workaround is to replace your line:
ValidationResults r = Validation.Validate<ISpike>(spike);
With:
ValidationResults r ValidationFactory.CreateValidator<ISpike>().Validate(spike);
This got it working for me.

Resources