I am having trouble running a java application (static void main) with the m2e plugin. I have created a multi-module project through the ordinary eclipse wizard. It seems as if code in the test directory of one module cannot reference code in the test directory of another module at runtime (compiling works just fine).
Version information
I have created an extremely simple (and contrived) example to demonstrate my problem. Please go easy on me for the pointlessness of this code. Also, forgive me for the verbosity here. Sadly maven questions almost require it.
Here is the project directory structure:
--Project Explorer
|--animals/
|--src/test/java/
|--com.example.problem.animals
|--Animal.java
|--JRE System Library [JavaSE-1.8]
|--src/
|--target/
|--pom.xml
|--dogs/
|--src/test/java/
|--com.example.problem.animals
|--Beagle.java
|--JRE System Library [JavaSE-1.8]
|--Maven Dependencies
|--animals/
|--src/
|--target/
|--pom.xml
|--parent/
|--animals/
|--dogs/
|--src/
|--pom.xml
The parent module pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.problem</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Parent</name>
<description>Parent module for example project.</description>
<modules>
<module>animals</module>
<module>dogs</module>
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
The animals module pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example.problem</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>animals</artifactId>
<name>Animals</name>
<description>Module to hold common animal code.</description>
</project>
The dogs module pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example.problem</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>dogs</artifactId>
<name>Dogs</name>
<description>Module to hold dog specific code.</description>
<dependencies>
<dependency>
<groupId>com.example.problem</groupId>
<artifactId>animals</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
The idea here is to have the Beagle object annotated with @Animal. The annotation lives under the test directory of "animals", while our java runnable class Beagle lives under the test directory of "dogs".
Animal.java:
package com.example.problem.animals;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Simple annotation to denote an animal.
*
* @author Rich - created 03/Sep/2014
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Animal {
String noise();
}
Beagle.java:
package com.example.problem.animals;
/*
* Runnable java class that simply prints the animal noise for Beagle to the console.
*
* @author Rich - created 03/Sep/2014
*/
@Animal(noise = "Woof!")
public class Beagle {
public static void main(String[] args) {
Animal animal = Beagle.class.getAnnotation(Animal.class);
System.out.println(animal.noise());
}
}
In order to run the Beagle class, I have created a new run configuration using the eclipse wizard. The configuration type is "Maven Build". The "JRE" tab has the "Runtime JRE" set to be "Workspace default JRE (jre1.8.0_20)". The following settings are made on the "Main" tab of the configuration:
Running this configuration ultimately fails and produces the following console output:
[INFO] Scanning for projects...
[INFO]
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Dogs 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.3.2:java (default-cli) @ dogs ---
[WARNING] Warning: killAfter is now deprecated. Do you need it ? Please comment on MEXEC-6.
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: com/example/problem/animals/Animal
at com.example.problem.animals.Beagle.main(Beagle.java:6)
... 6 more
Caused by: java.lang.ClassNotFoundException: com.example.problem.animals.Animal
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 7 more
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.282 s
[INFO] Finished at: 2014-09-13T18:25:51-08:00
[INFO] Final Memory: 9M/155M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.3.2:java (default-cli) on project dogs: An exception occured while executing the Java class. null: InvocationTargetException: com/example/problem/animals/Animal: com.example.problem.animals.Animal -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
After running the configuration with "Debug Output" set, I dug through the console and found the following interesting lines:
[DEBUG] Configuring mojo 'org.codehaus.mojo:exec-maven-plugin:1.3.2:java' with basic configurator -->
[DEBUG] (f) arguments = []
[DEBUG] (f) classpathScope = test
[DEBUG] (f) cleanupDaemonThreads = true
[DEBUG] (f) daemonThreadJoinTimeout = 15000
[DEBUG] (f) includePluginDependencies = false
[DEBUG] (f) includeProjectDependencies = true
[DEBUG] (f) keepAlive = false
[DEBUG] (f) killAfter = 1
[DEBUG] (f) localRepository = id: local
...
[DEBUG] Project Dependencies will be included.
[DEBUG] Collected project artifacts [com.example.problem:animals:jar:0.0.1-SNAPSHOT:compile]
[DEBUG] Collected project classpath [C:\Users\cairnsjr13\workspace\parent\dogs\target\test-classes, C:\Users\cairnsjr13\workspace\parent\dogs\target\classes]
[DEBUG] Adding to classpath : file:/C:/Users/cairnsjr13/workspace/parent/dogs/target/test-classes/
[DEBUG] Adding to classpath : file:/C:/Users/cairnsjr13/workspace/parent/dogs/target/classes/
[DEBUG] Adding project dependency artifact: animals to classpath
The reason I am so confused is the fact that it looks like it is trying to include the "animals" dependency. I suspect its including the main dependency and not the test dependency. So... after this extremely long winded info dump... Does anyone have any idea how to get eclipse (m2e) to execute this situation? I have gotten it configured properly to handle the test-test compile dependency, but cannot for the life of me get the runtime dependency to work.
After a ton of incremental changes and trial-and-error, I've figured out how to get this to work. The nice thing about this approach is that you can still actively develop in the animals project without having to export it as a test-jar everytime. The <dependency> tag has a type attribute which must be set. The dogs pom.xml file then becomes:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example.problem</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>dogs</artifactId>
<name>Dogs</name>
<description>Module to hold dog specific code.</description>
<dependencies>
<dependency>
<groupId>com.example.problem</groupId>
<artifactId>animals</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>test-jar</type>
</dependency>
</dependencies>
</project>
Executing the original run configuration yields the following output:
[INFO] Scanning for projects...
[INFO]
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Dogs 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.3.2:java (default-cli) @ dogs ---
[WARNING] Warning: killAfter is now deprecated. Do you need it ? Please comment on MEXEC-6.
Woof!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.310 s
[INFO] Finished at: 2014-09-21T13:53:21-08:00
[INFO] Final Memory: 9M/155M
[INFO] ------------------------------------------------------------------------
There are a couple of things to note here.
The scope attribute is not required to be set to test to get this to run. Adding the <scope>test</scope> tag to the dependency will cause the dependency to be omitted in a non-test scope. Since we are running in test scope it does not matter.
This approach will NOT include a dependency on the main code from animals. In the example in the original question, this did not matter because there was no main code source directory. In the event that the test code in animals depends on main code in animals you must add an additional dependency in dogs to pick that up. Here is what the dogs pom.xml file would look like in that situation:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example.problem</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>dogs</artifactId>
<name>Dogs</name>
<description>Module to hold dog specific code.</description>
<dependencies>
<!-- Including both dependencies for jar and test-jar to get main src included. -->
<dependency>
<groupId>com.example.problem</groupId>
<artifactId>animals</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.example.problem</groupId>
<artifactId>animals</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>test-jar</type>
</dependency>
</dependencies>
</project>