Intellij v13.1 Bug: NoClassDefFoundError when Running Unit Test With JaCoCo Configured

If you are using IntelliJ and your Maven project relies on JaCoCo Maven Plugin, please consider voting up issue IDEA-122895 to get it fixed by JetBrain as soon as possible.

Even if you are not using IntelliJ or JaCoCo, but you find this shitty blog to be helpful, please consider voting up this issue to help this poor author. You will be rewarded with a thousand colorful unicorns that will help to troubleshoot your future programming obstacles.

Here’s how to do it…

Step 1

Visit IDEA-122895.

Step 2

Log into JetBrain’s YouTrack.

Step 3

Click on the vote button at the bottom of the page.

Step 4

Rejoiced! … and wait for the flying “problem-solving” colorful unicorns to arrive in 3 business days…

IntelliJ: Preventing Wildcard Imports

PROBLEM

I have been getting the following PMD warning that I can’t seem to suppress with PMD’s @SuppressWarnings

RequestServiceImpl.java:1, UnnecessaryFullyQualifiedName, Priority: Normal

Unnecessary use of fully qualified name 'com.choonchernlim.epicapp.service.impl' 
due to existing import 'com.choonchernlim.epicapp.service.*'.

The reason I’m getting this warning is because IntelliJ will automatically replace all single imports from the same package with a wildcard ‘*’ when the class count hits a certain limit.

By using wildcard imports, it also becomes difficult for my peers to understand where the classes are coming from.

SOLUTION

To fix this permanently in IntelliJ:-

  1. Go to Preferences…Code StyleJava.
  2. Go to Imports tab.
  3. Check Use single class import
  4. Set a ridiculous high number on Class count to use import with ‘*’ and Names count to use static import with ‘*’ because these fields cannot be empty. In my case, I set 100 for each field.
  5. Select OK.

Finally, perform an Optimize Imports… on that Java file in question.

IntelliJ: Configuring SVN Ignored Files

OVERVIEW

Whenever we share a project in SVN or checks out a project from SVN, we have to perform a one-time configuration on the project to ignore selected files from being committed into SVN. That said, this configuration will only work if the files are not already in SVN.

PROBLEM

Let’s assume we have a Maven project that contains an EAR module and a WAR module:-

EpicApp
├── .idea
│   └── ...
├── EpicApp.iml
├── epicapp-ear
│   ├── epicapp-ear.iml
│   ├── pom.xml
│   └── src
│       └── ...
├── epicapp-war
│   ├── epicapp-war.iml
│   ├── pom.xml
│   └── src
│       └── ...
└── pom.xml

IntelliJ generates .idea folder and *.iml files to store our project settings and module configuration.

When we run mvn clean compile, the target folders are created:-

EpicApp
├── .idea
│   └── ...
├── EpicApp.iml
├── epicapp-ear
│   ├── epicapp-ear.iml
│   ├── pom.xml
│   ├── src
│   │   └── ...
│   └── target
│       └── ...
├── epicapp-war
│   ├── epicapp-war.iml
│   ├── pom.xml
│   ├── src
│   │   └── ...
│   └── target
│       └── ...
├── pom.xml
└── target # Depending on how the root pom.xml is configured, this folder may or may not appear.
    └── ...

If we look at the overall project structure, there are tons of generated files that are not created by us:-

EpicApp
├── .idea
│   └── ...
├── EpicApp.iml
├── epicapp-ear
│   ├── epicapp-ear.iml
│   ├── pom.xml
│   ├── src
│   │   └── ...
│   └── target
│       └── ...
├── epicapp-war
│   ├── epicapp-war.iml
│   ├── pom.xml
│   ├── src
│   │   └── ...
│   └── target
│       └── ...
├── pom.xml
└── target # Depending on how the root pom.xml is configured, this folder may or may not appear.
    └── ...

First, we definitely want to ignore target folders from being committed into SVN.

Now, regarding whether to ignore .idea folder and *.iml files, there are two camps out there:-

  • Camp PINK will commit the IntelliJ generated files into SVN so that the project configuration is shared among team members.
  • Camp BLUE will NOT commit files that are not created by them.

What’s my take? I’m with Camp BLUE because I don’t like pink in the first place. In the past, I tend to commit all IntelliJ generated files into SVN. However, it was a nightmare working in a team environment because every team member kept overwriting these files from one other, resulting endless SVN conflicts. Since we encourage folks to only commit the files they modify (or create), I see no absolute reason to commit these IntelliJ generated files. We saw more cons than pros based on our past experience.

SOLUTION 1: Add Ignored Files Using IntelliJ

Based on the example above, there are 5 things we need to ignore:-

  • epicapp-ear/target/ directory
  • epicapp-war/target/ directory
  • target/ directory
  • .idea/ directory
  • *.iml files

So, in IntelliJ…

  1. Select “Changes” tab at the bottom.
  2. Select “Local” subtab.
  3. Click on “Configure Ignored Files” button.
    "Changes" tab
  4. In “Configure Ignored Files” dialog, click on the “+” button.
  5. Add the ignored file (or directory).
    "Configure Ignored Files" dialog

  6. Repeat step 4 and 5 until all ignored files are added.

SOLUTION 2: Add Ignored Files Directly in .idea/workspace.xml

If you have quite a few files and directories to ignore, the above solution can be rather tedious.

So, an alternative solution is to “hack” .idea/workspace.xml by entering all the ignored paths under “ChangeListManager” component:-

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <component name="ChangeListManager">
        ...
        <ignored path="epicapp-war/target/" />
        <ignored path="epicapp-ear/target/" />
        <ignored path=".idea/" />
        <ignored path=".settings/" />
        <ignored path="target/" />
        <ignored path=".DS_Store" />
        <ignored path=".classpath" />
        <ignored path=".project" />
        <ignored mask="*.iws" />
        <ignored mask="*~" />
        <ignored mask="*.bak" />
        <ignored mask="*.log" />
        <ignored mask="*.iml" />
    </component>
    ...
</project>

In this example, we ignore all target folders, all IntelliJ generated files, all Eclipse generated files, all OS-specific generated files and some useless files.

When we save .idea/workspace.xml, IntelliJ immediately detects a change made to this file (because it is intelligent) and it will prompt us to reload the project. Once reloaded, the “Configure Ignored Files” dialog will reflect the changes made.

java.lang.OutOfMemoryError: PermGen space When Running Maven on IntelliJ

NOTE

This is not a Groovy related problem, but I’m using it to illustrate my problem and solution here.

PROBLEM

I recently tried mixing some Groovy code into my existing JEE project. I created a simple POGO that looks as sophisticated as this:-

class GroovyStuff {
	String name
}

Then, I configured one of my controllers to invoke that POGO:-

@Controller
@RequestMapping(value = "/")
public class HomeController {

    @RequestMapping(method = RequestMethod.GET)
    public String main() {
        GroovyStuff stuff = new GroovyStuff();
        stuff.setName("Hello Groovy");
        System.out.println(stuff.getName());
        return "home";
    }
}

After starting up Jetty, I hit that controller from the web and I get this infamous exception:-

java.lang.OutOfMemoryError: PermGen space
    at java.lang.ClassLoader.findBootstrapClass(Native Method)
    at java.lang.ClassLoader.findBootstrapClassOrNull(ClassLoader.java:926)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:297)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:239)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:230)
    at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:407)
    at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:383)
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2475)
    at java.lang.Class.getDeclaredMethods(Class.java:1818)
    at org.codehaus.groovy.reflection.CachedClass$3$1.run(CachedClass.java:84)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.codehaus.groovy.reflection.CachedClass$3.initValue(CachedClass.java:81)
    at org.codehaus.groovy.reflection.CachedClass$3.initValue(CachedClass.java:79)
    at org.codehaus.groovy.util.LazyReference.getLocked(LazyReference.java:46)
    at org.codehaus.groovy.util.LazyReference.get(LazyReference.java:33)
    at org.codehaus.groovy.reflection.CachedClass.getMethods(CachedClass.java:250)
    at org.codehaus.groovy.runtime.m12n.SimpleExtensionModule.createMetaMethods(SimpleExtensionModule.java:111)
    at org.codehaus.groovy.runtime.m12n.SimpleExtensionModule.getMetaMethods(SimpleExtensionModule.java:93)
    at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.registerExtensionModuleFromProperties(MetaClassRegistryImpl.java:192)
    at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.registerExtensionModuleFromMetaInf(MetaClassRegistryImpl.java:174)
    at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.registerClasspathModules(MetaClassRegistryImpl.java:156)
    at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.<init>(MetaClassRegistryImpl.java:111)
    at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.<init>(MetaClassRegistryImpl.java:73)
    at groovy.lang.GroovySystem.<clinit>(GroovySystem.java:33)
    at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:162)
    at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:192)
    at myproject.bean.GroovyStuff.$getStaticMetaClass(Stuff.groovy)
    at myproject.bean.GroovyStuff.<init>(Stuff.groovy)
    at myproject.controller.HomeController.main(HomeController.java:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

I tried both GMaven Plugin and Groovy-Eclipse Compiler Plugin, and both gave me the same problem.

I checked my MAVEN_OPTS variable, and I have set a rather big max perm size, but it doesn’t fix the problem.

export MAVEN_OPTS="-Xms2048m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m"

Then, I rebooted my laptop since that’s what most tech supports in the world would tell me to do when they encounter a problem… but it doesn’t fix the problem.

When I commented out the Groovy calls from the controller, everything works fine:-

@Controller
@RequestMapping(value = "/")
public class HomeController {

    @RequestMapping(method = RequestMethod.GET)
    public String main() {
        //GroovyStuff stuff = new GroovyStuff();
        //stuff.setName("Hello Groovy");
        //System.out.println(stuff.getName());
        return "home";
    }
}

After venting my frustration at the ping pong table and cursing at the laptop for many hours both at work and at home, I decided to run Jetty directly from the command line:-

mvn clean jetty:run

I was rather surprised that I no longer see the error.

SOLUTION

After poking around, I realized that IntelliJ doesn’t seem to honor my MAVEN_OPTS setting. When I run jetty:run goal from IntelliJ, I see the following output in the console:-

/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java 
-Dmaven.home=/usr/local/apache-maven-3.0.3 -Dclassworlds.conf=/usr/local/apache-maven-3.0.3/bin/m2.conf 
-Didea.launcher.port=7533 "-Didea.launcher.bin.path=/Applications/IntelliJ IDEA 12.app/bin" 
-Dfile.encoding=UTF-8 -classpath "/usr/local/apache-maven-3.0.3/boot/plexus-classworlds-2.4.jar:/Applications/IntelliJ IDEA 12.app/lib/idea_rt.jar" 
com.intellij.rt.execution.application.AppMain org.codehaus.classworlds.Launcher --fail-fast --strict-checksums 
org.mortbay.jetty:jetty-maven-plugin:8.1.8.v20121106:run

So, I went to IntelliJ’s Preferences... -> Maven -> Runner and set my MAVEN_OPTS values under VM Options:-

IntelliJ's Maven VM Options

When I run jetty:run goal from IntelliJ again, now I see the following output in the console:-

/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java 
-Xms2048m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m 
-Dmaven.home=/usr/local/apache-maven-3.0.3 -Dclassworlds.conf=/usr/local/apache-maven-3.0.3/bin/m2.conf 
-Didea.launcher.port=7534 "-Didea.launcher.bin.path=/Applications/IntelliJ IDEA 12.app/bin" 
-Dfile.encoding=UTF-8 -classpath "/usr/local/apache-maven-3.0.3/boot/plexus-classworlds-2.4.jar:/Applications/IntelliJ IDEA 12.app/lib/idea_rt.jar" 
com.intellij.rt.execution.application.AppMain org.codehaus.classworlds.Launcher --fail-fast --strict-checksums 
org.mortbay.jetty:jetty-maven-plugin:8.1.8.v20121106:run

Now, when I hit my controller again, the error went away.

The moral of the story is… if you still cannot fix your coding problem after spending many hours, go play ping pong.

Understanding How Maven Dependencies Work

Let’s assume we have the following dependencies:-

<dependencies>
    <dependency>
        <groupId>dbunit</groupId>
        <artifactId>dbunit</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>3.2.1.RELEASE</version>
    </dependency>
</dependencies>

In this example, both dbunit and spring-core have a dependency on commons-logging, but they rely on a different version:-

dbunit      -> commons-logging v1.0.4
spring-core -> commons-logging v1.1.1

Based on the above configuration, what version of commons-logging does Maven choose?

If your answer is v1.1.1, then you are absolutely right…. that you need to read the Maven documentation again.

If you are using IntelliJ, you can easily create a “Dependencies” diagram that looks like this:-
maven-dependencies-1

If you click on commons-logging used by spring-core, there is an arrow pointing to commons-logging used by dbunit. In another word, v1.0.4 is used.

maven-dependencies-2

The Maven documentation says:-

... "nearest definition" means that the version used will be the closest one to your project in the tree of dependencies, eg. if dependencies for A, B, and C are defined as A -> B -> C -> D 2.0 and A -> E -> D 1.0, then D 1.0 will be used when building A because the path from A to D through E is shorter ...

Since the two dependency versions for commons-logging are at the same depth in the dependency tree, the order of the declaration becomes very important. The first declaration will be chosen and the rest will be ignored.

To proof that we are not on crack, we will swap the dependency order this time:-

<dependencies>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-core</artifactId>
	    <version>3.2.1.RELEASE</version>
	</dependency>
    <dependency>
        <groupId>dbunit</groupId>
        <artifactId>dbunit</artifactId>
        <version>2.2</version>
    </dependency>
</dependencies>

In this case, the arrow points from commons-logging used by dbunit to commons-logging used by spring-core. In another word, v1.1.1 is used.

maven-dependencies-3

To avoid any further confusion on which dependency version will be chosen by Maven, you can always explicitly define that dependency in pom.xml:-

<dependencies>
	<dependency>
	    <groupId>commons-logging</groupId>
	    <artifactId>commons-logging</artifactId>
	    <version>1.1.2</version>
	</dependency>
    <dependency>
        <groupId>dbunit</groupId>
        <artifactId>dbunit</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>3.2.1.RELEASE</version>
    </dependency>
</dependencies>

In this case, we just force both dbunit and spring-core to use v1.1.2:-

maven-dependencies-4

maven-dependencies-5

RECOMMENDED SOLUTION

My recommended solution is to ALWAYS define the dependencies under dependencyManagement tag.

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>dbunit</groupId>
            <artifactId>dbunit</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.2.1.RELEASE</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- Don't specify the dependency version here -->
<dependencies>
    <dependency>
        <groupId>dbunit</groupId>
        <artifactId>dbunit</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
    </dependency>
</dependencies>

Just because you have dependencies defined under dependencyManagement tag, it doesn’t get included into your project classpath automatically. All it is saying is SHOULD you need to use, say dependency A, you will get version X.

If you define all your dependencies under dependencyManagement tag, you generally don’t define the versions under dependencies tag unless you want to override it.

In the above example, notice that you don’t need to define commons-logging under dependencies tag, unless you need it to compile or run your code. That said, when you run mvn clean package on a WAR module, commons-logging will be included in the WEB-INF/lib.

If we generate the “Dependencies” diagram again, all dependencies on commons-logging will now point to v1.1.2. There is zero confusion and we don’t have to worry about our peers from wrecking the project when they add new dependencies in the wrong order or upgrade existing dependency versions that may introduce potential version conflicts.

maven-dependencies-6

maven-dependencies-7

Why Should You Always Use dependencyManagement?

Let’s assume your team works on a multi-module project that looks like this:-

myproject
|- ear module     <- for wrapping the war module
   |- pom.xml      
|- war module     <- web application
   |- pom.xml
|- jar module #1  <- for other teams to reuse the API
   |- pom.xml
|- jar module #2  <- used by cron job
   |- pom.xml
|- pom.xml        <- parent pom     

The main goal when working on a multi-module project is to ensure every module uses the same dependency version, if possible, to ensure there are no compatibility problems. The last thing you want is to have Team A building jar module #1 with Spring 3.x and Team B building jar module #2 with Spring 2.x.

In this situation, you should always consider defining all the dependencies under dependencyManagement tag in the parent pom and configure all child module poms to extend the parent pom. This simple configuration enforces dependency version standardization across all modules within the project.