Tuesday, February 25, 2020

Maven multi-module project in IntelliJ IDEA - several ways to try when stuck with an issue . . .

"Nobody uses Maven, Maven uses you." - Venkat Subramaniyam

I heard at least few times Venkat saying that in his presentations on various topics in Conferences and at Java User Group meetups. I used to laugh with many when he would to say that after asking "Who uses Maven here?". I was happy NOT to be one to raise a hand for that question. I have been using Gradle for a longtime. Now, if I attend any of his sessions and happen to hear that question again, I will be one of those to raise a hand. "Welcome back!", Maven said and started using me ;(

I worked with Gradle-based projects and imported into IntelliJ, numerous times. I even brought multiple Gralde projects into multi-project Gradle builds under one project with proper project inter-dependencies. But I never had to break my head with issues for hours.

Environment: Java 13.01, Spring Boot 2.2.4.RELEASE, Maven 3.6.2, IntelliJ IDEA ULTIMATE 2019.3 on macOS High Sierra 10.13.6

The issue I ran into lately with a maven multi-module project

I recently had to start a new module in a multi-module maven project. The new module was a fairly simple Java application (executable jar) with just spring-boot-starter-batch dependency to start with and some other related dependencies as well. Suddenly, IntelliJ was unhappy by all means with the newly added module in dealing with dependencies and adding dependent libraries to the classpath for compilation. The module was just fine running maven tasks like compile, install, package etc. from the command line. I was even able to run the app from the executable jar created.

I literally ran out of all options dealing with this issue in IntelliJ. Several people seem to have faced similar issue and there was no consistent solution that worked for all. Some of the solutions that worked for some but not for all are:
  1) Invalidating caches and restarting IntelliJ (an option found under File menu item, beware that IntelliJ takes a while after restart to index depending on the size of all your projects imported into IntelliJ)
  2) Deleting the project from the initial welcome pane and importing again
  3) Deleting the project from the initial welcome pane, but instead of importing, opening it and going through steps.
  4) Clearing local maven cache all the way etc.

None of the above options worked for me. Phrases like: "That worked for me",  "This worked yesterday, doesn't work anymore." are quite commonly heard. This is a common pattern in Software Development. I call it a head-breaking pattern ;)

Insights of Maven multi-module project in IntelliJ

A multi-module maven project contains a root pom.xml with module specific pom.xml files. Easiest way is to import a maven project into IntelliJ IDEA is from the welcome pane. Click on Import Project and select the root pom.xml. If everything goes well, IntelliJ imports the project resolves each module's dependencies, compiles code and reports errors or issues. During this process it creates <module-name>.iml files under every module including the root project (the module-name is taken from the corresponding module's name property specified in it's pom.xml). If open any of these .iml XML files created, they contain dependency details with <orderEntry type="library" name="Maven: ..."/>. Also, from the dependencies, IntelliJ detects frameworks needed and sets up proper facets like Spring, JPA, Web etc. in getting all needed tool support for the module.

When stuck with an issue, there are many ways to add, delete, import specific module into IntelliJ IDEA

When there are issues with any specific module, that particular module can be deleted and added/imported again by few different ways in IntelliJ.

1) From the Project Tool Window (Did not not solve my iisue)

Right click the module and select Remove Module as shown below:



and import the removed module by adding the module to the project again as shown below:


2) From Project Settings - New Module (Did not not solve my issue)

Press Cmd + ; (⌘;) or go to File > Project Structure and delete module as shown below:


and add the module again by clicking + on the top. A window with two options pops up as shown below:



select New Module, then select Maven and go through steps from New Module Pane on the left as shown below:


Make sure Module SDK looks good, Next > Select Parent from the drop-down, enter same module name for Name: , and notice that the Location: gets updated with name as entered.

3) From Project Settings - Import Module (This solved my issue)

Press Cmd + ; (⌘;) or go to File > Project Structure and delete module as shown below:


and add the module again by clicking + on the top. A window with two options pops up as shown below:



select Import Module instead of the New Module. Select module's pom.xml, from the finder window opened and click open. Then, make sure correct directories of your module are marked for Mark as:  Sources (main/java), Tests(test/java), Resources(main/resources), Test Resources (if any) and click OK.

This properly updates IntelliJ's classpath in module's *.iml file that gets created and found under your module's root folder. Once imported, IntelliJ also recognizes frameworks appropriately from the dependencies. In my case it was just Spring framework/facet. When you right click on the module in your project structure pane, and mouseover the +Add option, you will see the list of frameworks/facets available as shown below:



Summary

We are living in times where a solution to an issue is just a google-search away. But sometimes, something that worked for others won't work for you. This is one such issue with which I was almost about to bang my head against the wall. After tirelessly exploring possible ways of deleting and creating modules, I finally found the option that worked for me consistently.

Software Development is not easy and will never become easy. This is a hard fact ;)


Saturday, February 15, 2020

Bank on Lombok in a Spring Boot application . . .

It's been over a decade since my eyes had seen Java boiler plate code like getters, setters, various overloaded constructors,  toString(), equals(), hashCode() methods etc. My brain and eyes got used to very quiet and clean code. Now, I suddenly realize the fact that I have been quietly (joyfully) coding in Groovy for a long time. I am back to Java and all that noise is back and started to bother both my brain and eyes :(

To push all that noise away from your eye-sight into compiled Java byte-code, there is this nice Java library called: Lombok. Java developers never say NO to another jar file dependency as Java world simply loves to have tonnes and tonnes of libraries in projects, anyway ;)

Lombok is a neat Java library, both developer and compiler friendly, saves a lot of time, makes code look less noisy, and increases the life of both keyboard and your fingers ;). It provides various useful annotations to generate all that boiler plate code into compiled byte-code to please Java compiler and many Java frameworks. There are many resources and blog-posts on Lombok. I am only describing few annotations that I have explored in the context of Spring Boot with JPA and thought would be useful across many Java projects. I will definitely take Lombok with me into every Java project that I get into.

Environment: Java 13.01, Spring Boot 2.2.4.RELEASE, Maven 3.6.2, IntelliJ IDEA ULTIMATE 2019.3 on macOS High Sierra 10.13.6

All you need to start leveraging Lombok in any Java project is just a dependency in your build configuration(maven/grade). That takes care of giving you the power to auto-generate all that noise and push it away into byte-code by annotating your code, when your code gets compiled as part of the build process. But, IDEs compile code as we write and may need a bit more setup in order for the compiled classes to have all boiler plate code generated into the bytecode.

IntelliJ IDEA Support and Setup

IntelliJ IDEA requires the following 2 steps:
  1. Install Lombok plugin.
      Press Cmd + , (⌘,) or go to IntelliJ IDEA > Preferences
      Click Plugins, Search for Lombok and install
  2. Enable Java compiler feature: Annotation Processors.
      Press Cmd + , (⌘,) or go to IntelliJ IDEA > Preferences
      Go to Build, Execution, Deployment > Compiler > Annotation Processors and Check Enable Annotation Processing


Eclipse based IDE Setup

Check this article: Setting up Lombok with Eclipse and IntelliJ

Some Useful Lombok Annotations


This annotation takes a Java POJO (Plain Old Java Object) nearer to Groovy POGO (Plain Old Groovy Object) by taking away lot of boiler plate methods. Typically, domain objects do not contain any logic other than fields/properties to carry data for persistence. JPA Entities or any kind of objects that carry data are good candidates to leverage this annotation. Annotate a class with this and forget all getters, setters, toString(), hashCode(), equals() etc.


Annotating a class with this, you don't have to worry about providing a constructor to initialize required object properties to initialize the object with. Very useful in Spring beans/components like Services where in you typically write an all args constructor that takes all dependency beans and set the required dependencies. This is preferred over using @Autowired for dependencies for various good reasons. In this case, if you add a new dependency to an existing service, you don't have to worry changing/missing-to-change the constructor.

Also, in Enums if you have extra properties set for each enum instance, you can skip writing and maintaining a constructor which is required by annotating an enum with this.


Usually no args constructor, also called default constructor comes free and provided by Java compiler. By writing specific constructors, this freebie is taken away. On those instances, you still may need to provide this constructor for frameworks that need it. This annotation is useful in such cases.

@RequiredArgsConstructor

Useful in a SpringBoot application when you use constructor based injection than field based  injection (@Autowired). Constructor based injection is preferable than field based injection anyway for various good reasons. In this case, you typically declare all required dependent beans as static final fields by providing a constructor that initializes all of those required beans. SpringBoot auto injects all those beans by calling the constructor.

This annotation is right for this kind of situation with which you don't need to write the constructor and maintain it as you add more dependency beans. Also, with this the moment you add another static final required bean dependency, somewhere in your unit tests where you had used this provided constructor to initialize dependencies fails to compile right away.


These flexible annotations for fields/properties of a class reduce Java bean noisy methods required by many Java frameworks like Hibernate. This itself is good relief for eyes!

@Builder

This annotation brings in builder pattern implemented into the bytecode. Oftentimes, simple POJOs contain many properties. Creating and object becomes bit complex by traditional POJO way of create an object and populate properties by calling setters one by one which may lead to missing setting some properties. A builder pattern brings in fluent object creation by using a builder method followed by setters and the end calling a method to build.


If you use builder pattern/support provided to facilitate readable complex object instantiation and have an object hierarchy, you need to annotate your super class(es) with this annotation for all the properties inherited from the super class to be available to build setter methods. Though it is still listed as an experimental feature, it is very useful and safe to use.


It is typical in Java code you may write or come across utility classes with just static methods. Code coverage tools like JaCoCo report the class definition line (e.g. public class MyUtilityClass {) as uncovered for these classes as there won't be an instance created. You can fool the tool by just creating an instance of it, but that's stupid to do to get coverage. Even if you make the class final and provide a private constructor to fully protect it from creating an instance (a typical utility class should be like this anyway), this additional noise will not get any coverage as there won't be any test for private constructor to get coverage. Also, there is NO reason to break your head to get coverage for private constructor.

So, the best way is to take away all that noise from code into byte-code and exempt it from coverage. The annotation @UtilityClass gives you exactly this by making the class final and providing a private constructor in the byte-code. It not only takes away the noisy boilerplate code away but also improves the coverage as you tell JaCoCo anyways to ignore Lombok generated methods in byte-code. Neat!

Code coverage is only a measure to see how much of code is covered in automated tests. But little things like these add up and bring down the total percentage way down in some cases. It's a time saver if all such nasty noise goes away into byte-code without even bothering about code coverage.

e.g.
/** * This class is lean and clean. The annotation takes away boiler-plate code like final with private constructor into bytecode. * Also, all public methods are static. * Once you write tests for all methods and conditions, you are guaranteed to get 100% coverage. */ @UtilityClass public class MyUtil { public final String MY_CONSTANT = "Just a constant!"; public void m1() { ... } public void m2() { ... } }


Java's NullPointerException is a billion dollar mistake. Though Java is strongly typed language, the weakness lies in the null type and compiler doesn't provide any mechanism to safeguard that null reference. Kotlin addresses this issue by distinguishing types further into nullable types and non- nullable types and enforcing checks during compilation time. This is one of Kotlin's selling and compelling features to Java developers.

Checking each argument of each method for null is so much of noise in code. Java 7's added Objects.requireNonNull() method may only lessen the noise by eliminating the need for if(arg != null){...} else {...} kind of checks with one statement per argument, but still is smelly and noisy.

Java SE 8 added another convenient class java.util.Optional<T> around this problem to deal with in code, which helps design better APIs by indicating that the users whether to expect a null and forcing them to unwrap Optional object to check for the value. Also, it provides some convenient methods to make code more readable. However, it is not a solution to replace every null reference in your codebase.

Lombok's annotation @NonNul comes to rescue. Every method argument that cannot be null can simply be annotated with this which eliminates all the noise and makes code lot more readable. The intent goes into method definition. Under the covers, it just wraps the method body with a similar if null else check that we write otherwise. All that is invisible and is only visible in bytecode. Using this annotation doesn't take away the response to write tests for these null conditional checks if you have code coverage tools like JaCoCo used which still sees all such if conditional check in the bytecode anyway. It doesn't make sense to add more boiler plate code in unit tests by writing test cases just to test those if null check generated into byte-code. Fortunately, there is a Lombok setting that can tell JaCoCo to ignore these wrapped if null checks in byte-code.

lombok.nonNull.exceptionType=JDK

@Generated

Though there is no mention of this in the list of annotations in Lombok's stable or experimental features, it's good to know that there is one like this not for developer's to use in the code, but is for tools to indicate tell not to bother checking for coverage. The api doc has enough details on this.

@Slf4j

Last but not least, Lombok comes with variations of logging annotation for all widely used logging implementations in Java.

Logging is absolutely a needed feature in any application. In Java world, this feature becomes noisy as your number of classes start to grow more than one. Every class/object that needs to log must order a logger object from the factory. The factory needs to know the class for which the logger is needed. This class you give to the factory is typically the class itself that is making an order to the factory. In doing so, every class that needs to log must have a static final logger field initialized with the classname passing to the log factory.

All that factory business was very exciting in the beginning of this millennium. After two decades, there is no reason to have all this routine noise from the log factories to be visible in the code. In my opinion, this one annotation alone is good enough for adding Lombok to a Java application. Annotate classes with this and move that business with factories into bytecode.

Tips


When a class is annotated with Builder annotation, make sure that in some cases for frameworks like Jackson used for JSON serialization that require no-are constructor. The @Builder Design Pattern takes away the default no-arg constructor and forces one to use builder method to create an object. This will make frameworks that leverage no-arg constructor fail.

In this case, your better option would be to add @NoArgsConstuctor and @AllArgsConstructor in addition to @Builder. Both constructor annotation are needed.

@Data, @Getter, @Setter - override specific getter(s) or setter(s)

When a class is annotated with @Data, or @Getter and @Setter, getter and setter methods are generated. If due to any reason if a custom/overriding getter or custom setter is needed for any property, simple provide one the way you would like to, following Java bean style. Lombok won't generate for those ones you have provided.

JaCoCo - code coverage

If you have Java Code Coverage tools like JaCoCo configured for your project and a high coverage threshold level is set, you will get disappointed with the coverage metrics showing the levels suddenly dropping down due to Lombok. This is all due to JaCoCo working at the bytecode level considering all methods including constructors, getter, setters, hashCode etc. that got generated by Lombok. This boiler plate code that got synthesized by Lombok during compilation time doesn't need code coverage. In order to tell JaCoCo not to consider Lombok generated code in the bytecode, create a file with name lombok.config at the root of your project and have the following properties. Your coverage numbers will come back to normal.

# Jacoco >= 0.8.0 and lombok >= 1.16.14 feature # This property adds annotation lombok.@Generated to relevant classes, methods and fields. Jacoco code-coverage # identifies, detects and ignores all Lombok generated boilerplate code getter, setters, hashCode, builder etc. lombok.addLombokGeneratedAnnotation = true # Set lombok to throw Just JDK NullPointerException (default anyway) in the wrapped code. # Also, let JaCoCo honor and not complain coverage for if(!null){} method wrapper generated in the byte-code lombok.nonNull.exceptionType=JDK # Stop Lombok from serching for config files further config.stopBubbling = true

Summary

Lombok is a pretty neat Java library which not only takes noise away from code into bytecode, but also makes code more readable by showing the intention clearly with annotated code. The minimalist phrase "Less is more" becomes a reality with Lombok's addition to a Java project.

Source Code is for Java developers, whereas bytecode is for Java virtual machine. Noise is noise for humans, but not for machines. Java is evolving and changing fast, but still is very noisy and verbose. Any little effort made to make code less noisy and more readable goes a long way in the life of any Java project by saving lot of developers time who read the code later. After all code is written once, but read many times by many in the life of a project.

"Lean and clean" is always beautiful, makes everyone smile and feel better ;)

Make friendship with Lombok, stay healthy, keep your eye-sight better, and your brain calmer!!

References




Saturday, February 08, 2020

Review of Java 13 "Preview Features" in Spring Boot app with Maven . . .

Writing code in Groovy for few years and now back to Java, I couldn't resist using one of the preview features added to Java 13 - Text Blocks. Multiline String literal is the most ugliest part of Java code polluted with escape characters and concatenation operator all over, to an extent that your brain cannot read the string actually. Writing a multiline XML/JSON string is a nightmare. There is no reason to live with such constraints in a language for this long. At last, Java 13 added support for multiline string as a preview feature. It's not going to go away but might go through some changes in future releases and is a pleasant to start using, infact. This blog post is not about text blocks, but several things that you need to do correctly in order to fully leverage preview features in a SpringBoot application with Maven as the build tool.

I was working on a Spring Boot micro-service app with Java 13 from ground up. I had to define a long String literal for a message. After enjoying Groovy's fantastic and superior support for defining multi-line text as a String for long enough, my eyes would certainly go blind if I do not leverage Java 13 Text Blocks in Java code for this. Thought it appears to be simple to just enable Java language preview feature with --enable-preview flag, in reality it goes beyond that simplicity. Not a surprise, after all technology only gets complex ;)

Environment: Java 13.01, Spring Boot 2.2.4.RELEASE, Maven 3.6.2, IntelliJ IDEA ULTIMATE 2019.3 on macOS High Sierra 10.13.6

Java 13 --enable-preview language flag

This is the flag you need to set for enabling Java 13 preview features, both for compiler (javac) and JVM launcher (java). Without this flag your code will not get compiled or run. With this flag you will still see a warning but can ignore safely. In order to use Java 13 features in a SpringBoot app with Maven, you need one or many or all of the following.

IntelliJ IDEA Setup

The moment I declared a String and assigned a multiline text literal, IntelliJ got unhappy. A warning bulb popped up and the message was: Text block literals are not supported at language level '13'. I did not understand what the message was saying but the link to Module settings took me to my API module level settings which was like:


It would have been much useful if the message was: Text block literals are not supported at language level '13 (No Preview)' and that current option chosen was: 13 (No Preview) language features. When I pulled down the option list, there I saw the 13 (Preview) option right below it:


That's IntelliJ way of setting the compiler flag --enable-preview to enable preview feature at the module level, if your are coding in a module in a multi-module maven project. Once I switched to the (Preview) one, IntelliJ was happy to compile my code.

NOTE: If you have a multi-module Maven project, you may need to set this Language level at the project level as well as at the each module level.

Maven Build Setup

IDE compiles code as we go on writing code and helps us with missing configurations and fixing errors. But the build system like Maven or Gradle is the one used at the end to clean, compile, package and run the app. Of course, maven build fails without additional setup for enabling preview features. The config setup needs the --enable-preview flag at few places depending on what plugins you have in it's pom.xml file. I had at least four places where I had to use this flag with some additional argument setup as well to get this feature fully enabled for my Spring Boot app.

1. Maven Compiler Plugin

Maven Compiler plugin compiles the project source code. You need to have an additional --enable-preview compiler argument in it's configuration to enable preview features as shown below:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <release>13</release> <compilerArgs> <arg>--enable-preview</arg> <arg>-Xlint:all</arg> </compilerArgs> </configuration> </plugin>

2. Maven Surefire Plugin

Surefire plugin is used during test phase to run unit tests. You need to have an additional --enable-preview compiler argument in it's configuration to enable preview features. In addition to this you also need to have ${argLine} without which you will not have code coverage reports generated if you are using code coverage libraries like JaCoCo. An example configuration setup is shown below:

<!-- surefire for unit tests --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> <configuration> <argLine>${argLine} --enable-preview</argLine> </configuration> <dependencies> <dependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>surefire-junit47</artifactId> <version>2.22.2</version> </dependency> </dependencies> </plugin>

3. Maven Failsafe Plugin

Failsafe plugin is used to during test phase to run integration tests. You need to have an additional --enable-preview compiler argument in it's configuration to enable preview features. In addition to this you also need to have ${argLine} without which you will not have code coverage reports generated if you are using code coverage libraries like JaCoCo. An example configuration  setup is shown below:

<!-- failsafe for integration tests --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>2.22.2</version> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> <configuration> <argLine>${argLine} --enable-preview</argLine> <additionalClasspathElements> <additionalClasspathElement>${basedir}/target/classes</additionalClasspathElement> </additionalClasspathElements> <includes> <include>**/*IT.java</include> </includes> </configuration> </plugin>

4. Springboot Maven Plugin

Springboot Maven plugin runs your application by launching the embedded Tomcat Server and JVM. You need to tell the JVM launcher to enable preview features by setting the flag as shown below:

<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> <execution> <!-- Useful info on /actuator/info --> <id>build-info</id> <goals> <goal>build-info</goal> </goals> </execution> </executions> <configuration> <mainClass>com.giri.api.Application</mainClass> <jvmArguments>${argLine} --enable-preview</jvmArguments> </configuration> </plugin>


TIP

In IntelliJ IDEA, if you do not have the (Preview) language level set, and if you mouseover the multiline text the warning message shown up like below is bit misleading.


However, when you click on the multi-line literal text, the error shown as below is more meaningful:

References