Thursday, January 12, 2012

Remove duplicate classes the agile way: Maven Duplicate Finder Plugin

Without discussing the alternatives I think a Java project cannot live without checking for dependency conflicts. The conflicts can be buried in external jar files so the check must be performed by fully qualified name on absolutely all classes included in the project.

Of course such a check should stop the build until a resolution.

The Maven Duplicate Finder Plugin does exactly that.

Just clone the repo, build the maven project and upload the plugin jar file to your Maven repository. Then configure your application to use the plugin:
...
<build>
  <plugins>
   <plugin>
              <groupId>com.ning.maven.plugins</groupId>
              <artifactId>maven-duplicate-finder-plugin</artifactId>
              <configuration>
                <failBuildInCaseOfConflict>false</failBuildInCaseOfConflict>
              </configuration>
              <executions>
                <execution>
                  <phase>verify</phase>
                  <goals>
                    <goal>check</goal>
                  </goals>
                </execution>
              </executions>
            </plugin>
...

If you run it with the failBuildInCaseOfConflict flag turned off as above you can get the messages as WARNING without stopping the project from building.
[INFO] [duplicate-finder:check {execution: default}]
[INFO] Checking compile classpath
[WARNING] Found duplicate classes in [commons-beanutils:commons-beanutils:1.8.3,commons-collections:commons-collections:3.2.1] :
[WARNING]   org.apache.commons.collections.ArrayStack
[WARNING]   org.apache.commons.collections.Buffer
[WARNING]   org.apache.commons.collections.BufferUnderflowException
[WARNING]   org.apache.commons.collections.FastHashMap
[WARNING] Found duplicate classes in [net.objectlab.kit:datecalc-common:1.2.0,net.objectlab.kit:datecalc-jdk:1.2.0] :
[WARNING]   net.objectlab.kit.datecalc.common.AbstractDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.AbstractIMMDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.AbstractKitCalculatorsFactory
[WARNING]   net.objectlab.kit.datecalc.common.DateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.DefaultHolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.ExcelDateUtil
[WARNING]   net.objectlab.kit.datecalc.common.HolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.HolidayHandler
[WARNING]   net.objectlab.kit.datecalc.common.HolidayHandlerType
[WARNING]   net.objectlab.kit.datecalc.common.IMMDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.IMMPeriod
[WARNING]   net.objectlab.kit.datecalc.common.ImmutableHolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.KitCalculatorsFactory
[WARNING]   net.objectlab.kit.datecalc.common.PeriodCountBasis
[WARNING]   net.objectlab.kit.datecalc.common.PeriodCountCalculator
[WARNING]   net.objectlab.kit.datecalc.common.StandardTenor
[WARNING]   net.objectlab.kit.datecalc.common.Tenor
[WARNING]   net.objectlab.kit.datecalc.common.TenorCode
[WARNING]   net.objectlab.kit.datecalc.common.Utils
[WARNING]   net.objectlab.kit.datecalc.common.WorkingWeek
[WARNING] Found duplicate resources in [net.sf.jasperreports:jasperreports:4.1.3,net.sf.jasperreports:jasperreports-fonts:4.1.3] :
[WARNING]   jasperreports_extension.properties
[WARNING] Found duplicate resources in [org.bouncycastle:bcmail-jdk14:1.38,org.bouncycastle:bcprov-jdk14:1.38,org.bouncycastle:bctsp-jdk14:1.38] :
[WARNING]   META-INF/BCKEY.DSA
[WARNING]   META-INF/BCKEY.SF
[INFO] Checking runtime classpath
[WARNING] Found duplicate classes in [commons-beanutils:commons-beanutils:1.8.3,commons-collections:commons-collections:3.2.1] :
[WARNING]   org.apache.commons.collections.ArrayStack
[WARNING]   org.apache.commons.collections.Buffer
[WARNING]   org.apache.commons.collections.BufferUnderflowException
[WARNING]   org.apache.commons.collections.FastHashMap
[WARNING] Found duplicate classes in [net.objectlab.kit:datecalc-common:1.2.0,net.objectlab.kit:datecalc-jdk:1.2.0] :
[WARNING]   net.objectlab.kit.datecalc.common.AbstractDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.AbstractIMMDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.AbstractKitCalculatorsFactory
[WARNING]   net.objectlab.kit.datecalc.common.DateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.DefaultHolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.ExcelDateUtil
[WARNING]   net.objectlab.kit.datecalc.common.HolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.HolidayHandler
[WARNING]   net.objectlab.kit.datecalc.common.HolidayHandlerType
[WARNING]   net.objectlab.kit.datecalc.common.IMMDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.IMMPeriod
[WARNING]   net.objectlab.kit.datecalc.common.ImmutableHolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.KitCalculatorsFactory
[WARNING]   net.objectlab.kit.datecalc.common.PeriodCountBasis
[WARNING]   net.objectlab.kit.datecalc.common.PeriodCountCalculator
[WARNING]   net.objectlab.kit.datecalc.common.StandardTenor
[WARNING]   net.objectlab.kit.datecalc.common.Tenor
[WARNING]   net.objectlab.kit.datecalc.common.TenorCode
[WARNING]   net.objectlab.kit.datecalc.common.Utils
[WARNING]   net.objectlab.kit.datecalc.common.WorkingWeek
[WARNING] Found duplicate resources in [net.sf.jasperreports:jasperreports:4.1.3,net.sf.jasperreports:jasperreports-fonts:4.1.3] :
[WARNING]   jasperreports_extension.properties
[WARNING] Found duplicate resources in [org.bouncycastle:bcmail-jdk14:1.38,org.bouncycastle:bcprov-jdk14:1.38,org.bouncycastle:bctsp-jdk14:1.38] :
[WARNING]   META-INF/BCKEY.DSA
[WARNING]   META-INF/BCKEY.SF
[INFO] Checking test classpath
[WARNING] Found duplicate classes in [commons-beanutils:commons-beanutils:1.8.3,commons-collections:commons-collections:3.2.1] :
[WARNING]   org.apache.commons.collections.ArrayStack
[WARNING]   org.apache.commons.collections.Buffer
[WARNING]   org.apache.commons.collections.BufferUnderflowException
[WARNING]   org.apache.commons.collections.FastHashMap
[WARNING] Found duplicate classes in [net.objectlab.kit:datecalc-common:1.2.0,net.objectlab.kit:datecalc-jdk:1.2.0] :
[WARNING]   net.objectlab.kit.datecalc.common.AbstractDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.AbstractIMMDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.AbstractKitCalculatorsFactory
[WARNING]   net.objectlab.kit.datecalc.common.DateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.DefaultHolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.ExcelDateUtil
[WARNING]   net.objectlab.kit.datecalc.common.HolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.HolidayHandler
[WARNING]   net.objectlab.kit.datecalc.common.HolidayHandlerType
[WARNING]   net.objectlab.kit.datecalc.common.IMMDateCalculator
[WARNING]   net.objectlab.kit.datecalc.common.IMMPeriod
[WARNING]   net.objectlab.kit.datecalc.common.ImmutableHolidayCalendar
[WARNING]   net.objectlab.kit.datecalc.common.KitCalculatorsFactory
[WARNING]   net.objectlab.kit.datecalc.common.PeriodCountBasis
[WARNING]   net.objectlab.kit.datecalc.common.PeriodCountCalculator
[WARNING]   net.objectlab.kit.datecalc.common.StandardTenor
[WARNING]   net.objectlab.kit.datecalc.common.Tenor
[WARNING]   net.objectlab.kit.datecalc.common.TenorCode
[WARNING]   net.objectlab.kit.datecalc.common.Utils
[WARNING]   net.objectlab.kit.datecalc.common.WorkingWeek
[WARNING] Found duplicate resources in [net.sf.jasperreports:jasperreports:4.1.3,net.sf.jasperreports:jasperreports-fonts:4.1.3] :
[WARNING]   jasperreports_extension.properties
[WARNING] Found duplicate resources in [org.bouncycastle:bcmail-jdk14:1.38,org.bouncycastle:bcprov-jdk14:1.38,org.bouncycastle:bctsp-jdk14:1.38] :
[WARNING]   META-INF/BCKEY.DSA
[WARNING]   META-INF/BCKEY.SF

The above is the result of a project that I cleaned up so actually the errors you are seeing are the result of "accepted" conflicts. Since we are aware of them then we proceed (at our own risk) to exclude them from being checked as exceptions while setting failBuildInCaseOfConflict to true. That way no developer will be able to include a conflicting dependency without resolving it first.
...
<build>
  <plugins>
    <plugin>
              <groupId>com.ning.maven.plugins</groupId>
              <artifactId>maven-duplicate-finder-plugin</artifactId>
              <configuration>
                <failBuildInCaseOfConflict>true</failBuildInCaseOfConflict>
                <ignoredResources>
                  <ignoredResource>META-INF.*</ignoredResource>
                </ignoredResources>
                <exceptions>
                  <exception>
                    <conflictingDependencies>
                      <dependency>
                        <groupId>commons-beanutils</groupId>
                        <artifactId>commons-beanutils</artifactId>
                      </dependency>
                      <dependency>
                        <groupId>commons-collections</groupId>
                        <artifactId>commons-collections</artifactId>
                      </dependency>
                    </conflictingDependencies>
                    <packages>
                      <package>org.apache.commons.collections</package>
                    </packages>
                  </exception>
                  <exception>
                    <conflictingDependencies>
                      <dependency>
                        <groupId>net.objectlab.kit</groupId>
                        <artifactId>datecalc-common</artifactId>
                      </dependency>
                      <dependency>
                        <groupId>net.objectlab.kit</groupId>
                        <artifactId>datecalc-jdk</artifactId>
                      </dependency>
                    </conflictingDependencies>
                    <packages>
                      <package>net.objectlab.kit.datecalc.common</package>
                    </packages>
                  </exception>
                  <exception>
                    <conflictingDependencies>
                      <dependency>
                        <groupId>net.sf.jasperreports</groupId>
                        <artifactId>jasperreports</artifactId>
                      </dependency>
                      <dependency>
                        <groupId>net.sf.jasperreports</groupId>
                        <artifactId>jasperreports-fonts</artifactId>
                      </dependency>
                    </conflictingDependencies>
                    <resources>
                      <resource>jasperreports_extension.properties</resource>
                    </resources>
                  </exception>
                </exceptions>
              </configuration>
              <executions>
                <execution>
                  <phase>verify</phase>
                  <goals>
                    <goal>check</goal>
                  </goals>
                </execution>
              </executions>
     </plugin>
...

Note how I have globally ignored any resource inside META-INF and I have ignored the properties file explicitly from the artifacts where they were found.

Also I have not used the classes node which would allow me to specify the individual classes that I approve to ignore. This can be bad but I feel lazy today ;-)

Finally I did not specify the version number for the conflicting dependencies. This can be bad as well.

No comments:

Followers