Monday, January 18, 2016

Using Maven Dependency Tree in troubleshooting

In this blog post, I'm going to explain how to check the  maven dependency tree and how it is useful in troubleshooting certain run time exception which we might encounter while running Spring applications.


Maven Dependency resolution:


When you use maven, it uses its own dependency resolution mechanism to decide which jar to use when there is a conflict.
Lets say you are using two dependencies, each of which have the same jar. Now, unless you pay attention, maven might end up using the incorrect jar version, and you will start getting exceptions like 'ClassNotFound' or 'NoSuchMethodError'.

So, it helps to know which version of jar has maven resolved and is added to our application. This is where the Maven dependency tree helps us.


Maven Dependency Tree:


A simple command, "mvn dependency:tree -Dverbose " will print out the entire maven dependency tree.

I will show with an example how to analyze this tree. Since is based on a issue I recently faced, I hope it will serve best to understand it better.


Practical Example:


Lets say we have a maven project for a very basic java spring project for illustration purpose.

Lets use power Mockito and Mockito core for unit test cases and the dependency is:

<dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>1.5.6</version>
      <scope>test</scope>
 </dependency>
   
 <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>1.5.6</version>
      <scope>test</scope>

 </dependency>

Also add a dependency on Mockito core:
<dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>1.9.5</version>
      <scope>test</scope>
</dependency>

Add a dependency for spring mockito annotations:
<dependency>
<groupId>org.kubek2k</groupId>
<artifactId>springockito-annotations</artifactId>
<version>${springockito-annotations-version}</version>
<scope>test</scope>

</dependency>

Create a Junit Test case as below:

@RunWith(PowerMockRunner.class)
public class MyLogicTest{

@Mock
private MyService service;

@Test
public void testMyMethod(){

}


}

Run the test case. You would get an error:

java.lang.NoSuchMethodError: org.mockito.internal.creation.MockSettingsImpl.setMockName(Lorg/mockito/mock/MockName;)Lorg/mockito/internal/creation/settings/CreationSettings;

You might have encountered similar NoSuchMethodError or ClassNotFound exceptions while running Spring applications. Most probably, those errors would have occurred because, the same jar (with different versions) would have been present in different dependencies. This might cause Spring to pick an incorrect version of the jar and thereby causing run time exceptions.

What is the actual issue here?


power-mockito 1.9.5 is declared in pom, but somehow older version is being used and causing problem. We need to find out which dependency is the culprit and causing this issue.

How to resolve this issue?



Run the below command which would display the tree structure of dependencies which maven has resolved.

mvn dependency:tree -Dverbose 

It would show a tree structure with parent and child nodes.
Search for "org.powermock" and you would notice the below:

[INFO] +- org.powermock:powermock-api-mockito:jar:1.5.6:test
[INFO] |  +- (org.mockito:mockito-all:jar:1.9.5:test - omitted for conflict with 1.9.0)

It is clearly saying it is using 1.9.0 instead of 1.9.5
Now, we need to find out which dependency is using 1.9.0. So search for 1.9.0 in the above output. You would find:

[INFO] +- org.kubek2k:springockito-annotations:jar:1.0.9:test
[INFO] |  \- org.mockito:mockito-all:jar:1.9.0:test

So it is clear that "org.kubek2k:springockito" is the culprit and it is adding the older version of power-mockito. To fix this issue, we need to tell maven to ignore or exclude "power-mockito" jar from "org.kubek2k:springockito".

How to instruct Maven to use the right jar version?


Use exclusions!!
So go to the pom and modify to add a exclusions element as below:

<dependency>
<groupId>org.kubek2k</groupId>
<artifactId>springockito-annotations</artifactId>
<version>${springockito-annotations-version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
 <groupId>org.mockito</groupId> 
 <artifactId>mockito-all</artifactId>
</exclusion>
</exclusions>
</dependency>

That's it!!. This should resolve the run time issue and the test case should start working. I hope this post was helpful in understanding the significance of Maven Dependency trees.