11

I'm creating a Mojo which doesn't need a project to run.

I would like to use something similar to org.apache.maven.model.FileSet (providing multiple directories with includes and excludes) as a @parameter but my problem is that I need to be able to set those values using command line.

Any idea how to achieve this?

Gerold Broser
  • 14,080
  • 5
  • 48
  • 107
Cristiano
  • 1,414
  • 15
  • 22

2 Answers2

7

See:

POM

  <groupId>so</groupId>
  <artifactId>multiple-values-maven-plugin</artifactId>
  <version>1.0</version>
  <packaging>maven-plugin</packaging>

  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <version>3.3.3</version>
    </dependency>

    <dependency>
      <groupId>org.apache.maven.plugin-tools</groupId>
      <artifactId>maven-plugin-annotations</artifactId>
      <version>3.4</version>
      <scope>provided</scope><!-- annotations are needed only to build the plugin -->
    </dependency>
  </dependencies>

  <!-- This latest plugin has to be used if using Java 8 classes. -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-plugin-plugin</artifactId>
        <version>3.4</version>
      </plugin>
    </plugins>
  </build>

Mojo

package so;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.maven.model.FileSet;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

@Mojo( name = "values", requiresProject = false )
public class MultipleValuesMojo extends AbstractMojo
    {
    @Parameter( property = "array", required = true )
    private String[] array;

    @Parameter( property = "list", required = true )
    private List<String> list;

    @Parameter( property = "set", required = true )
    private String[] setElements;
    private Set<String> set;

    @Parameter( property = "map", required = true )
    private String[] mapEntries;
    private Map<String, String> map;

    @Parameter( property = "includes", required = true )
    private List<String> includes;

    @Parameter( property = "excludes", required = true )
    private List<String> excludes;

    @Override
    public void execute() throws MojoExecutionException
        {
        getLog().info( "Array: " + Arrays.toString( array ) );
        getLog().info( " List: " + list.toString() );
        set = Arrays.stream( setElements ).collect( Collectors.toSet() ); // with Java >=8
        addSetElementsToSet(); // with Java <8
        getLog().info( "  Set: " + set.toString() );
        map = Arrays.stream( mapEntries ).collect( Collectors.toMap( s -> s, s -> s ) ); // with Java >=8
        putMapEntriesToMap(); // with Java <8
        getLog().info( "  Map: " + map.toString() );

        getLog().info( "Includes: " + includes.toString() );
        getLog().info( "Excludes: " + excludes.toString() );

        FileSet fileSet = new FileSet();
        fileSet.setIncludes( includes );
        fileSet.setExcludes( excludes );
        getLog().info( " FileSet: " + fileSet.toString() );
        } // execute()

    private void addSetElementsToSet()
        {
        set = new HashSet<String>( setElements.length );
        for ( String entry : setElements )
            {
            set.add( entry );
            }
        } // addSetElementsToSet()

    private void putMapEntriesToMap()
        {
        map = new HashMap<String, String>( mapEntries.length );
        for ( String entry : mapEntries )
            {
            int equalsPosition = entry.indexOf( "=" );
            map.put(
                entry.substring( 0, equalsPosition ),
                entry.substring( equalsPosition + 1 ) );
            }
        } // putMapEntriesToMap()

    } // MultipleValuesMojo

Run

mvn so:multiple-value-maven-plugin:values
  -Darray=VALUE_1,VALUE_2,VALUE_3
  -Dlist=VALUE_1,VALUE_2,VALUE_3
  -Dset=VALUE_1,VALUE_2,VALUE_3
  -Dmap=KEY_1=VALUE_1,KEY_2=VALUE_2,KEY_3=VALUE_3
  -Dincludes=/,/usr/*
  -Dexcludes=/root,/tmp 

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building multiple-values-maven-plugin 1.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- multiple-values-maven-plugin:1.0:values (default-cli) @ multiple-values-maven-plugin ---
[INFO] Array: [VALUE_1, VALUE_2, VALUE_3]
[INFO]  List: [VALUE_1, VALUE_2, VALUE_3]
[INFO]   Set: [VALUE_3, VALUE_2, VALUE_1]
[INFO]   Map: {KEY_1=VALUE_1, KEY_3=VALUE_3, KEY_2=VALUE_2}
[INFO] Includes: [/, /usr/*]
[INFO] Excludes: [/root, /tmp]
[INFO]  FileSet: FileSet {directory: null, PatternSet [includes: {/, /usr/*}, excludes: {/root, /tmp}]}
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.671 s
[INFO] Finished at: 2015-07-25T21:44:09+02:00
[INFO] Final Memory: 11M/115M
[INFO] ------------------------------------------------------------------------
Gerold Broser
  • 14,080
  • 5
  • 48
  • 107
  • 1
    Impressive example of how to pass parameters on the command line, except Fileset isn't one of them. – Kyle Jan 19 '16 at 18:16
  • That actually doesn't answer the OP's problem even a little bit. Downvoting. – Jimadilo Feb 04 '16 at 10:52
  • @kyl191 Is it better now? – Gerold Broser Apr 25 '16 at 21:11
  • @Jimadilo Is it better now? – Gerold Broser Apr 25 '16 at 21:11
  • @GeroldBroser it is closer, but you still have to create the FileSet object out of the includes and excludes. I mean, it's not a single parameter where you can define all the properties needed for a FileSet. Maybe the answer is that it's not possible? – Miguel Ferreira Jul 28 '16 at 13:07
  • @MiguelFerreira I think "_single parameter_" vs. "_properties_" (in plural) is a contradiction in itself. (Remember, `-D` stands for _define property_.) Sure, one could pack multiple properties into a single parameter by using a separator and tokenizing the values inside the program, like `-Dproperties=/,/usr/*|/root,/tmp` but what's the point of that? It has the disadvantages that 1) it is not clear what is what on the command line at first sight, 2) the order of the values is vital, 3) it complicates the code by having to implement the tokenizing, and maybe even more I'm not thinking of atm. – Gerold Broser Jul 29 '16 at 14:50
  • @GeroldBroser This is not a matter of contriving examples and then arguing about their merits. It's a very practical question. For example, the jacoco maven plugin offers a goal named merge that takes a FileSet as a mandatory parameter. If you want to run that goal from the cmd line, you do need to specify 1 single property with the required value to instantiate the FileSet. Do you not? (see http://www.eclemma.org/jacoco/trunk/doc/merge-mojo.html#fileSets) – Miguel Ferreira Jul 31 '16 at 15:51
  • @MiguelFerreira [Jacoco's `merge` mojo](https://github.com/jacoco/jacoco/blob/master/jacoco-maven-plugin/src/org/jacoco/maven/MergeMojo.java) makes use of Maven's [_Mapping Complex Objects_ and _Mapping Lists_](https://maven.apache.org/guides/mini/guide-configuring-plugins.html#Mapping_Complex_Objects) (but requires a project the POM of which requires a `fileSet` parameter with its according structure). [to be continued] – Gerold Broser Aug 01 '16 at 05:58
  • 2
    @MiguelFerreira [continued] I'm pretty sure that an object's (hierarchical) member variables structure cannot be represented by _one_ single property value other than by introducing and evaluating an according structure inside this property value yourself – by using JSON or XML, for instance, to extend the simpler example mentioned in my previous comment. So, yes, you're right insofar as it is not possbile easily and directly when passing values of complex objects via command line is desired. – Gerold Broser Aug 01 '16 at 06:13
  • 2
    @MiguelFerreira The [`jacoco.fileSets` parameter](https://github.com/jacoco/jacoco/pull/739) has been removed finally [due to](https://groups.google.com/d/msg/jacoco/E_YQtRRNNcs/NGGCT1uQCAAJ) "_serious doubts that specification of this property via command line ever worked since initial implementation of "merge" mojo, because backing structure is not a plain String, but a complex object_". – Gerold Broser Jun 16 '20 at 16:59
  • @GeroldBroserreinstatesMonica would you like to update your answer? I would then accept it as the answer to this question. – Miguel Ferreira Jun 24 '20 at 08:35
  • @MiguelFerreira Update with what exactly? The question doesn't mention Jacoco. And how are you going to accept it? It's not your question but Cristiano's. Or ist this you, too? – Gerold Broser Nov 20 '20 at 00:46
  • @GeroldBroser yes you are right, it's not my question. – Miguel Ferreira Jan 19 '21 at 15:48
1

Anyone looking for the solution:

-D<some-property>.fileSet=['path-to-fileset']

did the trick for me. It might require slight modification, depending on a plugin you're configuring, but you get the idea.

Tested on Maven 3.5.0

  • How do you declare `` in the Mojo? Where did you get this `.fileSet=['path-to-fileset']` syntax? I couldn't make this work except with a `/////${fileSet}` and a `/path-to-fileset` declaration in the POM which can be overriden on the command line with `-DfileSet=other-path-to-fileset`. But that's not what the OP wants: "_a Mojo which doesn't need a project to run_". – Gerold Broser Jun 14 '20 at 16:27
  • 1
    I did some research to remember why exactly I needed this. This is the source of my answer: https://github.com/jeremylong/DependencyCheck/issues/1425#issuecomment-503492150 – Alexander Biryukov Jun 16 '20 at 15:15
  • 1
    Thank you for digging this out from 2018. However, after a lot of try & error with my own code here this syntax seems to be just fake. I tried the `org.owasp:dependency-check-maven` plugin with `-DscanSet.fileSet=src/main` and got a `BUILD SUCCESS`, too. Then I tried `-DscanSet.fileSet=['src/main'] -X` and found in the build log: `[DEBUG] (f) scanSet = []`. The normal output of the plugin doesn't show any difference with either parameter, so you don't recognize this without `-X` and searching through almost one thousand debug lines. – Gerold Broser Jun 16 '20 at 21:13
  • 1
    I [created an according issue](https://github.com/jeremylong/DependencyCheck/issues/2671) and [it has been changed accordingly](https://github.com/jeremylong/DependencyCheck/commit/795916c06b32f72f9fe630e55ca1dd9cab21eaaa). – Gerold Broser Jun 20 '20 at 09:15