Trouble running exec-maven-plugin in a multi-module Maven project - java

I have a multi-module Maven project with a master pom and numerous sub-directories each containing projects (and pom.xml files) which refer to the master pom.
In projectA contains an invocation of exec-maven-plugin, which executes the java goal and successfully invokes a test class that resides somewhere in projectA (in projectA/test/com/mycompany/Testclass.java). The plugin is declared as follows:
org.codehaus.mojo
exec-maven-plugin
1.2.1
<execution>
<id>execute-test</id>
<phase>pre-integration-test</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>com.mycompany.Testclass</mainClass>
<classpathScope>test</classpathScope>
</configuration>
</execution>
</executions>
</plugin>
I execute maven with the following command, and it works fine:
mvn verify -P <profile name> -pl projectA -am
I have a second project, projectB, which depends on projectA. I have configured an exec-maven-plugin section in projectB's pom.xml file that is identical to the one above. When I run maven with the same command as above (except with projectB in the -pl parameter), I get the exception java.lang.ClassNotFoundException: com.mycompany.Testclass.
Clearly this is happening because the classpath when I run maven for projectB does not include the directories in projectA, even though the pom.xml has a dependency on projectA (actually it depends on projectA.1, which in turn depends on projectA).
I have tried switching to use the exec goal rather than the java goal, in projectB, and then providing a classpath in the arguments, like this:
<execution>
<id>execute-test</id>
<phase>pre-integration-test</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>java</executable>
<arguments>
<argument>-classpath</argument>
<argument>"../projectA/target/projectA-6.6.1-bSOURCE-tests.jar;../projectA/target/projectA-6.6.1-bSOURCE.jar"</argument>
<argument>com.mycompany.Testclass</argument>
</arguments>
<classpathScope>test</classpathScope>
</configuration>
</execution>
When I've done that I successfully load the class, but get a ClassNotFoundException on com.google.gson.JsonSyntaxException
That happens to be the same error I get if I run the class from the command line, as follows
java -classpath "projectA/target/projectA-6.6.1-bSOURCE-tests.jar;projectA/target/projectA-6.6.1-bSOURCE.jar" com.mycompany.Testclass
In projectB I think I want to use the java goal of the exec-maven-plugin rather than the exec goal, but one way or the other I have to be able to specify the classpath.
Any ideas how I can do that?
Thanks.

I figured this out myself, and am posting my answer in case anybody else needs it, or in case anybody wants to dispute it (or suggest something better).
What I did was add a dependency in the subordinate project, projectB, as follows:
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>projectA</artifactId>
<version>6.6-SNAPSHOT</version>
<scope>test</scope>
<classifier>tests</classifier>
</dependency>
I believe the clincher was the element, which directed maven to get the dependent classes from the jar file containing the test classes in projectA.

Related

Change maven profile from within project

I want to change maven project's active profile from within the maven project.
The java project has 3 classes having main methods and they are executed one by one, All of these require different set of dependencies.
I would like to run these classes with different maven profiles.
For example my project project1 is a maven project and is getting invoked by mvn clean package spring-boot:run command.
When the control comes to the project1 it executes the java classes one by one.
My requirement is that when ClassA is run it should change the current maven profile to profile1 (recompiling the same project is ok, performance is not priority). Similarly when ClassB gets invoked it should change the current maven profile to Profile2.
(Note these classes are dynamically picked from directory using reflection and only the class will know what profile it supports)
Is there any way maven allows this at runtime.
If not what is the best way to achieve this.
maven profiles are not designed for such tasks, what you are actually need to do is to setup different executions for each class:
...
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>run-clsa</id>
<goals>
<goal>run</goal>
</goals>
<phase>none</phase>
<configuration>
<mainClass>ClassA</mainClass>
</configuration>
</execution>
<execution>
<id>run-clsb</id>
<goals>
<goal>run</goal>
</goals>
<phase>none</phase>
<configuration>
<mainClass>ClassB</mainClass>
</configuration>
</execution>
</executions>
...
and run that in following way:
mvn clean package spring-boot:run#run-clsa
mvn clean package spring-boot:run#run-clsb

maven-jar-plugin: How to create a different artifact name for tests?

Let's say that I put the following plugin in my pom.xml file:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>make-a-jar</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<finalName>my-artifact-name</finalName>
</configuration>
</execution>
</executions>
</plugin>
and I run "mvn clean install".
Maven creates two jar files in target library.
The first jar file compiles the source files and the second jar compiles the test files. Both of these jar will have the same artifact name.
If I want to use the jar of source code as a dependency in another project, I can put the following dependency in the other project:
<dependency>
<groupId>groupId</groupId>
<artifactId>my-artifact-name</artifactId>
<scope>system</scope>
<type>jar</type>
<systemPath>${basedir}/lib/my-artifact-name.jar</systemPath>
</dependency>
So far so good.
A problem arises if I also want to add a dependency for the test files. In this case I will have two dependencies with the same groupId and artifactId and different systemPath. Maven will not read two dependencies with same groupId and artifactId. Only one of them will be read.
One solution that I can think of is to make Maven to give a different artifact name for test.
Do you know how to do it?
My general answer to that would be:
The tests in src/test/java are only for running them during the build. They need not be put into any jar.
If you need classes as helper classes for your tests, you can create a separate jar which contains these classes. This can then be used as test dependency.
In any case, try to avoid <systemPath>. If you build your project with mvn clean install on your computer, you can reference the resulting jar with a dependency like
<dependency>
<groupId>groupId</groupId>
<artifactId>my-artifact-name</artifactId>
<version>1.2.3</version>
</dependency>
anywhere on the same account/computer without giving a <systemPath>.

How to generate separate jar files for application, source, and documentation (for central.sonatype.org)

Sonatype has a repository that I want to deploy a jar file to, and they ask for separate files for application, sources, and javadocs:
Example:
example-application-1.4.7.pom
example-application-1.4.7.jar
example-application-1.4.7-sources.jar
example-application-1.4.7-javadoc.jar
In Scala SBT, I have a command called "package" that generates the jar file for the project, but that only generates "example-application-1.4.7.jar".
Question: What should I do to generate the other two jar files?
In Maven, in order to get the additional -sources and -javadoc artifacts, add to your POM file the following:
<build>
<plugins>
<!-- additional plugin configurations, if any.. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Note the snippet above:
We are invoking the Maven Source Plugin to create an additional jar files for sources
We are invoking the Maven Javadoc Plugin to create an additional jar files for javadoc
Executing
mvn clean package
You will find these two additional jars in the target folder.
The .pom file instead is generated during the install phase, but it is not placed under the target folder. Basically, it is a copy of your pom.xml file, with a different extension and used by Maven during the dependency mediation process to check which transitive dependencies are required by the concerned artifact.
Executing
mvn clean install
Maven will install the artifact in your local cache (in your machine), under path_to_cache/.m2/repository/your_groupId/your_artifactId/your_version/. In this folder, you will also find the .pom file, which normally you don't need to distribute (it is created automatically by Maven).
Further note: you probably don't want to generate these additional jar files at each and every build, so to speed up normal builds and have them only on demand, you could wrap the snippet above in a Maven profile.
You can achieve this by removing the snippet above from your build section and add a further section at the end of your pom:
<profiles>
<profile>
<id>prepare-distribution</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
So that normal builds would not create these jars anymore, but when executing the following:
mvn clean install -Pprepare-distribution
You would instead get them back. the -P option is actually activating on demand the profile defined with the id prepare-distribution.
With Maven 3 a default profile already comes as part of the super pom which perform exactly the same actions (sources and javadoc artifact), hence no need to add anything to your existing project. Simply run:
mvn clean install -Prelease-profile
Or, to activate it via a property
mvn clean install -DperformRelease=true
However, as also specified in the super pom, this profile may be removed in future releases (although there since first Maven 3 version till version 3.3.9 so far)
NOTE: The release profile will be removed from future versions of the super POM
The main reason behind this warning is most probably to push for the usage of the Maven Release Plugin, which indirectly makes use of this profile via the useReleaseProfile option of the release:perform goal.
As highlighted by comments, if you are not familiar with maven (especially via console) I would definitely recommend to
Go through the official Maven in 5 minutes documentation for a quick but worthy look.
Play with Maven from the command line, is there where Maven gives you its best. IDE integrations are great, but command line is the real turning point.
Then play with the POM customization above, to get familiar with some concepts and behaviors, first directly as part of your default build, then moved to a profile.
Then, and only then, move to the Maven Release Plugin usage. I recommend it as last step because you would already have acquired more confidence and understanding and see it as less magic and more reasonable approach.

Is it best to Mavenize your project jar files or put them in WEB-INF/lib?

I've been doing this for all of the jar files that my Spring MVC project needs:
call mvn install:install-file -DgroupId=vegetables -DartifactId=potatoes -Dversion=1.0 -Dfile=vegetables-1.0.jar -Dpackaging=jar -DgeneratePom=true
Recently I must have exceeded some limit on how many dependencies you can list in your pom.xml file because I got an error that said:
Your command line is too long
So I removed some dependencies from the pom.xml that my project no longer uses and I was able to run the project with maven again.
My question is, should I put install all jar files into my Maven repository as I have been doing so far? Or should I put some of them into the WEB-INF/lib directory?
What's the best practice here?
I've been doing the same that you do with the command line, but by configuring maven-install-plugin in my POM (please read the note at the end):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<executions>
<execution>
<id>install-vegetables</id>
<phase>initialize</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<file>${project.basedir}/lib/vegetables-1.0.jar</file>
<groupId>vegetables</groupId>
<artifactId>potatoes</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
</configuration>
</execution>
<execution>
<id>install-minerals</id>
<phase>initialize</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<file>${project.basedir}/lib/minerals-1.0.jar</file>
<groupId>minerals</groupId>
<artifactId>rocks</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
</configuration>
</execution>
</executions>
</plugin>
It is much less efficient, because files get installed over and over, but it is much less annoying than making it manually. Anyway, I think you should give it a try.
All your dependencies should reside under the local repository. According to the Maven convention/best practices, you should not keep jar files in your project.
Convert your project to a fully war based Maven project. This will place all your dependencies (jar files) under your webapp's WEB-INF/lib directory. Thus you will not have to worry about long paths.
You just need to add the dependencies in your pom.xml file, no need to install them manually. Maven will download the libraries and put it in your local repository whenever needed. Only if you want to use third party(custom) libraries, you may go for installing it in your local repository.

Generating a maven site including a Cobertura Report

I've got some projects that are already doing site generation via maven, and I want to integrate cobertura reports in them, but no maven goal I seem to run will generate a local preview for me to look at that includes the Cobertura reports in the site. I want to be sure they're generating correctly before I commit the pom changes to the repo and have broken site generated.
Below is what I've added to the maven poms (parent and module), but the site I see when I run mvn site:run does not include the cobertura reports:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<configuration>
<check>
<haltOnFailure>false</haltOnFailure>
<regexes>
<regex>
<pattern>parent-package-name-here.*</pattern>
<branchRate>80</branchRate>
<lineRate>80</lineRate>
</regex>
</regexes>
</check>
<instrumentation>
<includes>
<include>parent-package-name-here/**/*.class</include>
</includes>
</instrumentation>
</configuration>
<executions>
<execution>
<id>clean</id>
<phase>pre-site</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
<execution>
<id>instrument</id>
<phase>site</phase>
<goals>
<goal>instrument</goal>
<goal>cobertura</goal>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
</plugin>
</plugins>
</reporting>
...
</project>
What maven command should I use to generate the site with cobertura reports? Or, what should I add (additionally) to get the site generation to include the cobertura reports?
Should do:
mvn site
To elaborate, running mvn a:b runs the goal b in plugin a. Saying mvn c means to run the lifecycle phase c, which runs all of the bound goals in all of the phases up to c. As a result, this will trigger a lot more things to happen (such as doing the necessary preparation to produce cobertura reports).
I figured out how to do this.
It seems there are a lot of bugs in the link generation within the maven site generation plugin.
The only way I've found to make maven generate a local copy of the site with working module links is to modify the distributionManagement/site tag to point to some local directory instead of the real-live deploy directory, then use maven site:deploy.
Every attempt to use mvn site:stage generates broken links. Same goes for mvn site:run.
The report links work with mvn site:run / mvn site:stage but the links to modules do not.
mvn site
should do what you are looking for. You configure the plugin to run in the pre-site and site phases of the life cycle but your are then executing the site:run goal not site. We are doing similar things with clover (commercial coverage tool) and mvn site does the trick.
site:stage module links don't work in my experience either for multi module builds but site:deploy does. Try this:
Use a property for the site URL in the parent pom, e.g. ${site.url}. Then call this
mvn clean site site:deploy -Dsite.url=file://`pwd`/target/site-deployed
The pwd is a -nix command that will substitute the current directory. This is because the URL that you use must be absolute.
We use
mvn site-deploy
This builds the site and deploys it (copies it to the place we have configured).
mvn site:site should produce what you are after, in the target directory, there will be a site directory containing all reports linked with an index.html in that directory.

Categories