Category Archives: IntelliJ IDEA

IntelliJ: Handling SVN Global Ignore List

PROBLEM

Every time we check out a project from SVN for the first time, we always have to remember to set the SVN ignore list in IntelliJ.

SOLUTION

NOTE: We only need to perform these steps just once per development machine.

To do so, we configure the SVN global ignore list and have IntelliJ to conform to that rule.

  • In Mac, go to IntelliJ IDEA -> Preferences. In Windows, go to File -> Settings.
  • In the preference dialog, go to Version Control -> Subversion.
  • Check Use system default Subversion configuration directory.
  • Note down Subversion configuration directory.
  • Close preference dialog.
  • Open [SVN_CONFIG_DIR]/config in a text editor.
  • Scroll to the [miscellany] section that looks something like this:-

    ...
    [miscellany]
    ### Set global-ignores to a set of whitespace-delimited globs
    ### which Subversion will ignore in its 'status' output, and
    ### while importing or adding files and directories.
    ### '*' matches leading dots, e.g. '*.rej' matches '.foo.rej'.
    # global-ignores = *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo
    #   *.rej *~ #*# .#* .*.swp .DS_Store 
    ### Set log-encoding to the default encoding for log messages
    # log-encoding = latin1
    ...
    

  • Uncomment global-ignores statement by removing #:-

    ...
    [miscellany]
    ### Set global-ignores to a set of whitespace-delimited globs
    ### which Subversion will ignore in its 'status' output, and
    ### while importing or adding files and directories.
    ### '*' matches leading dots, e.g. '*.rej' matches '.foo.rej'.
    global-ignores = *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo
       *.rej *~ #*# .#* .*.swp .DS_Store
    ### Set log-encoding to the default encoding for log messages
    # log-encoding = latin1
    ...
    

  • Add the following patterns:-

    ...
    [miscellany]
    ### Set global-ignores to a set of whitespace-delimited globs
    ### which Subversion will ignore in its 'status' output, and
    ### while importing or adding files and directories.
    ### '*' matches leading dots, e.g. '*.rej' matches '.foo.rej'.
    global-ignores = *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo
       *.rej *~ #*# .#* .*.swp .DS_Store
       .idea target .git .classpath .project .settings *.iml *.log *.bak *.class *.jar *.war *.ear 
       node_modules pom.xml.versionsBackup
    ### Set log-encoding to the default encoding for log messages
    # log-encoding = latin1
    ...
    

  • Save and close this config file.

Now, all the unnecessary files will not appear under “Changes” section in IntelliJ and they will not be committed into SVN.

Advertisements

Maven: Deploying Java Sources to Nexus

By default, mvn deploy will only package and deploy class JAR to Nexus. To deploy the source code too, we need to add the following plugin into pom.xml:-

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-source-plugin</artifactId>
    <version>2.2.1</version>
    <executions>
        <execution>
            <id>attach-sources</id>
            <goals>
                <goal>jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Now, mvn deploy will deploy both class JAR and source code JAR to Nexus:-

In IntelliJ, when jumping into a class from the JAR, we are given the option to “Download Sources” or “Attach Sources…”.

For some reason, in order for “Download Sources” to work, we need to uncheck “Use Maven3 to import project”.

Now, when we click on “Download Sources”, it will download the sources directly from Nexus and display the source code.

IntelliJ: Auto-generating POJO Builder

I have been searching for an elegant way to auto-generate a POJO fluent builder with minimal typing and configuration. A builder is a great way to promote object immutability, however it is also a pain to create and mantain yet another class that looks almost like the POJO class we create.

There are a few promising fluent builders out there in the wild, however, almost all of them still require some manual typing. After poking around, it seems like IntelliJ has its own fluent builder generator too.

Here’s how to do it without too much of typing…

Step 1: Define Fields in a Bean

The first step is to define the fields we need in a bean.

public class PersonBean {
    private String name;
    private LocalDate birthDate;
    private Double height;
    private Double weight;
}

Step 2: Auto-Generate Constructor and Getter Methods

The constructor and getter methods can be easily auto generated directly from IntelliJ. Do not generate setter methods.

public class PersonBean {
    private String name;
    private LocalDate birthDate;
    private Double height;
    private Double weight;
		
    public PersonBean(String name, LocalDate birthDate, Double height, Double weight) {
        this.name = name;
        this.birthDate = birthDate;
        this.height = height;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public Double getHeight() {
        return height;
    }

    public Double getWeight() {
        return weight;
    }
		
}

Step 3: Auto-Generate Builder

Once we are satisfied with the constructor, right click on the contructor > Refactor > Replace Constructor with Builder….

In the popup dialog, click Refactor button.

By the way, if the bean allows optional fields, don’t bother hand typing the default values and checking the Optional Setter checkboxes right now because they are very tedious and they go against my “minimal typing” rule. My “Step 4” hack below will do exactly that.

IntelliJ will generate the following builder class in the same package as the POJO class:-

public class PersonBeanBuilder {
    private String name;
    private LocalDate birthDate;
    private Double height;
    private Double weight;

    public PersonBeanBuilder setName(String name) {
        this.name = name;
        return this;
    }

    public PersonBeanBuilder setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
        return this;
    }

    public PersonBeanBuilder setHeight(Double height) {
        this.height = height;
        return this;
    }

    public PersonBeanBuilder setWeight(Double weight) {
        this.weight = weight;
        return this;
    }

    public PersonBean createPersonBean() {
        return new PersonBean(name, birthDate, height, weight);
    }
}

Step 4: Create Nullable Fields in Builder Class

Do a regular expression “find and replace” to set null as the default value for all fields.

Find: (private \w+ \w+)(;)
Replace With: $1 = null$2

Now, we should see something like this:-

public class PersonBeanBuilder {
    private String name = null; 
    private LocalDate birthDate = null; 
    private Double height = null; 
    private Double weight = null; 

    public PersonBeanBuilder setName(String name) {
        this.name = name;
        return this;
    }

    public PersonBeanBuilder setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
        return this;
    }

    public PersonBeanBuilder setHeight(Double height) {
        this.height = height;
        return this;
    }

    public PersonBeanBuilder setWeight(Double weight) {
        this.weight = weight;
        return this;
    }

    public PersonBean createPersonBean() {
        return new PersonBean(name, birthDate, height, weight);
    }
}

Step 5: Reduce POJO Constructor Visibility

The very last step is to change the POJO’s public-access constructor to package-access constructor by removing public.

The key here is to have all consumers to rely on the builder class to create the POJO.

public class PersonBean {
    private String name;
    private LocalDate birthDate;
    private Double height;
    private Double weight;

    PersonBean(String name, LocalDate birthDate, Double height, Double weight) {
        this.name = name;
        this.birthDate = birthDate;
        this.height = height;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public Double getHeight() {
        return height;
    }

    public Double getWeight() {
        return weight;
    }
}

Step 6: Using the Builder

Finally, we can use the builder class to generate the POJO:-

PersonBean personBean = new PersonBeanBuilder()
        .setName("Hulk")
        .setBirthDate(new LocalDate(1900, 12, 1))
        .setHeight(7.5)
        .setWeight(250.0)
        .createPersonBean();

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.