IntelliJ: Overriding Log4J Configuration Globally for JUnit

PROBLEM

Most of the time, we may have several Log4J configurations depending on the environments, for example:-

  • log4j.xml (Log4J) or log4j2.xml (Log4J2) – Production configuration using socket appender.
  • log4j-dev.xml (Log4J) or log4j2-dev.xml (Log4J2) – Development configuration using console appender.

Since log4j.xml and log4j2.xml are the default configuration files for Log4J and Log4J2, these configurations will always be used unless we override the configuration file path.

In another word, if we don’t override the configuration file path and we run our JUnit test cases offline from IntelliJ, it may take a very long time to execute them due to the broken socket connection. Further, it is not a good idea to clutter our production log files with non-production logs.

SOLUTION

To fix this, we need to configure IntelliJ to always use our development Log4J configuration.

First, on the menu bar, select Run -> Edit Configurations...

Then, delete all the existing JUnit configuration files.

Finally, expand Defaults -> JUnit.

Under VM options, specify the following system property:-

  • For Log4J, specify -Dlog4j.configuration=log4j-dev.xml
  • For Log4J2, specify -Dlog4j.configurationFile=log4j2-dev.xml

Please note the slight change on the system property name depending on the Log4J version.

Now, when we run our JUnit test cases from IntelliJ, it will always pick up the correct custom Log4J configuration file.

IntelliJ: Selectively Disable Line Wrap

PROBLEM

Sometimes, we have very lengthy statements that look like this:-

@Service
public class LengthOfStayHeuristicServiceImpl extends HeuristicService {
    @Override
    public Double compute(HeuristicBean heuristicBean) {
        ...
				
        setValue(map, surgeryLocationMap, LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_LUMBAR_OR_SACRAL_AND_LUMBOSACRAL_MINUS_CERVICAL_AND_NOT_SPECIFIED);
        setValue(map, surgeryLocationMap, LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_LUMBAR_OR_SACRAL_MINUS_LUMBOSACRAL);
        setValue(map, surgeryLocationMap, LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_CERVICAL_MINUS_NOT_SPECIFIED);

        return new LengthOfStayAlgorithm(map).run();
    }
}

When we reformat the code in IntelliJ, it becomes like this:-

@Service
public class LengthOfStayHeuristicServiceImpl extends HeuristicService {
    @Override
    public Double compute(HeuristicBean heuristicBean) {
        ...
				
        setValue(map,
                 surgeryLocationMap,
                 LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_LUMBAR_OR_SACRAL_AND_LUMBOSACRAL_MINUS_CERVICAL_AND_NOT_SPECIFIED);
        setValue(map,
                 surgeryLocationMap,
                 LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_LUMBAR_OR_SACRAL_MINUS_LUMBOSACRAL);
        setValue(map,
                 surgeryLocationMap,
                 LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_CERVICAL_MINUS_NOT_SPECIFIED);

        return new LengthOfStayAlgorithm(map).run();
    }
}

There are times we really don’t want the long statements to wrap around because they look very messy.

SOLUTION

While there is no option to selectively disable just the line wrap in IntelliJ, there is a way to selectively disable code formatting.

First, we need to enable the Formatter Control.

Now, we can annotate our code like this:-

@Service
public class LengthOfStayHeuristicServiceImpl extends HeuristicService {
    @Override
    public Double compute(HeuristicBean heuristicBean) {
        ...
				
        // @formatter:off
        setValue(map, surgeryLocationMap, LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_LUMBAR_OR_SACRAL_AND_LUMBOSACRAL_MINUS_CERVICAL_AND_NOT_SPECIFIED);
        setValue(map, surgeryLocationMap, LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_LUMBAR_OR_SACRAL_MINUS_LUMBOSACRAL);
        setValue(map, surgeryLocationMap, LengthOfStayAlgorithm.VariableEnum.SURGERY_LOCATION_CERVICAL_MINUS_NOT_SPECIFIED);
        // @formatter:on

        return new LengthOfStayAlgorithm(map).run();
    }
}

When we reformat the code, that portion of code will remain unformatted.

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.

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();