Managing Third-Party Libraries

Steven J Zeil

Last modified: Sep 14, 2017
Contents:
1 Issues
2 Basic Concepts
2.1 Repositories
2.2 Identifying Packages
2.3 Scopes
3 Adding Libraries to a Java Project in Different Build Managers
3.1 Maven
3.2 Ivy (ant)
3.3 Gradle
4 Eclipse and Library Management
4.1 Eclipse and Maven
4.2 Eclipse and Ant/Ivy
4.3 Eclipse and Gradle
5 Complicating Factors
5.1 Plug-ins
5.2 Publishing
5.3 Login Credentials

Abstract

Modern build managers provide support for

so that all of these can be handled quickly and efficiently as part of the normal build.

1 Issues

Baselines may include multiple 3rd-party libraries.

 


Example: Report Accumulator

A project of mine, this page lists the 3rd party libraries required by the project.

2 Basic Concepts

2.1 Repositories

A repository is a collection of packages (usually libraries) and metadata about packages

2.1.1 Common Java Repositories

The Java world is dominated by

2.1.2 Local Repositories

It’s common for development organizations to host their own repository.

Local repositories can be

2.2 Identifying Packages

Packages are typically identified by

2.3 Scopes

Not every library that your project needs is needed all of the time.

So build managers allow you to import libraries in selected scopes which indicate when the library is actually needed.

This affects

3 Adding Libraries to a Java Project in Different Build Managers

3.1 Maven

3.1.1 Specifying a Dependency

3.1.2 Fetching Dependencies

When the build is run (e.g. mvn package):

3.1.3 Transitive Dependencies

How does Maven know whether junit itself depends on other libraries?

3.1.4 Choosing Repositories


3.1.5 Example: specifying a repository

In pom.xml:

  <repositories>
     <repository>
        <snapshots>
           <enabled>false</enabled>
        </snapshots>
        <id>central</id>
           <name>libs-release</name>
           <url>http://archive350.cs.odu.edu:8080/artifactory/libs-release</url>
    </repository>
  </repositories>

3.2 Ivy (ant)

YAAP1 , Ivy adds the dependency management features pioneered by Maven to ant

3.2.1 Getting Started with Ivy

Again, let’s start with the build.xml for an Ant project.

E.g.,

build.xml0.listing
<project name="codeAnnotation" basedir="." default="build">

  <record name="ant.log" action="start" append="false" />

  <taskdef classpath="JFlex.jar" classname="JFlex.anttask.JFlexTask" name="jflex" /> ➀
	
  <echo>loading build-${os.name}.paths</echo>
  <include file="build-${os.name}.paths"/>  ➁
	

  <target name="generateSource">
    <mkdir dir="src/main/java"/>
    <jflex file="src/main/jflex/code2html.flex"
	   destdir="src/main/java"/>
  	<jflex file="src/main/jflex/code2tex.flex"
  	       destdir="src/main/java"/>
    <jflex file="src/main/jflex/list2html.flex"
	   destdir="src/main/java"/>
  	<jflex file="src/main/jflex/list2tex.flex"
  	       destdir="src/main/java"/>
  </target>
  

  <target name="compile" depends="generateSource">
    <mkdir dir="target/classes"/>
    <javac srcdir="src/main/java" destdir="target/classes"
	   source="1.7" includeantruntime="false"/>
  </target>

  <target name="compile-tests" depends="compile">
    <mkdir dir="target/test-classes"/>
    <javac srcdir="src/test/java" destdir="target/test-classes"
	   source="1.7" includeantruntime="false">
      <classpath refid="testCompilationPath"/>
    </javac>
  </target>

  <target name="test" depends="compile-tests">
  	<property name="mypath" refid="testExecutionPath"/>
  	<echo>testExecutioPath is ${mypath}</echo>
  	<echoproperties/>
    <mkdir dir="target/test-results/details"/>
    <junit printsummary="yes" 
	   haltonfailure="yes" fork="no"
	   >
        <classpath refid="testExecutionPath"/>
      <formatter type="xml"/>
      <batchtest todir="target/test-results/details">
	     <fileset dir="target/test-classes">
	        <include name="**/*Test*.class"/>
	     </fileset>
      </batchtest>
    </junit>
    <junitreport todir="target/test-results">
      <fileset dir="target/test-results/details">
        <include name="TEST-*.xml"/>
      </fileset>
      <report format="frames" todir="target/test-results/html"/>
    </junitreport>
  </target>

  <target name="build"  depends="test">
  	<jar destfile="codeAnnotation.jar" basedir="target/classes">
  	    <manifest>
  		    <attribute name="Main-Class"
  		               value="edu.odu.cs.code2html.Code2HTML"/>
  		</manifest>
  	</jar>
  </target>

  <target name="clean">
    <delete dir="target"/>
  </target>

  
</project>

Remove any hard-coded paths to libraries,. e.g.

<project>
    <path id="testCompilationPath">
      <pathelement location="/usr/share/java/junit4.jar"/>
      <pathelement path="target/classes"/>
    </path>

    <path id="testExecutionPath">
      <pathelement location="/usr/share/java/junit4.jar"/>
      <pathelement path="target/classes"/>
      <pathelement path="target/test-classes"/>
    </path>
</project>

3.2.2 Fetching Ivy

Start by having our build file actually load Ivy

build.xml1.listing
<project name="codeAnnotation" basedir="." default="build"
	 xmlns:ivy="antlib:org.apache.ivy.ant">

  <record name="ant.log" action="start" append="false" />
	

  <property name="ivy.install.version" value="2.3.0"/>
  <property name="ivy.jar.dir" value="${basedir}/ivy"/>
  <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar"/>

  <target name="download-ivy" unless="skip.download">
    <mkdir dir="${ivy.jar.dir}"/>
    <echo message="installing ivy..."/>
    <get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar" 
	 dest="${ivy.jar.file}" skipexisting="true"/>
  </target>

  <target name="install-ivy" depends="download-ivy" 
	  description="--> install ivy">
    <path id="ivy.lib.path">
      <fileset dir="${ivy.jar.dir}" includes="*.jar"/>
    </path>
    <taskdef resource="org/apache/ivy/ant/antlib.xml" 
	     uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
  </target>

    ⋮

  <target name="generateSource">
    ⋮
  </target>
  

  <target name="compile" depends="generateSource">
    ⋮
</project>

3.2.3 Specifying our Desired Libraries

We list the libraries our project needs in a separate ivy.xml file

<ivy-module version="2.0">
  <dependencies>
    <dependency org="de.jflex" name="jflex" rev="1.4.3"/>
    <dependency org="junit" name="junit" rev="4.10"/>
  </dependencies>
</ivy-module>

This should look familiar to the section from the Maven pom:

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  

3.2.4 Invoking Ivy

Once Ivy is installed, we can invoke it:

build.xml2.listing
<project name="codeAnnotation" basedir="." default="build"
	 xmlns:ivy="antlib:org.apache.ivy.ant">
     ⋮

  <target name="resolve-ivy" depends="install-ivy">
    <ivy:resolve/>                         ➀
    <ivy:cachepath pathid="ivy.path" />  ➁
    <taskdef classpath="JFlex.jar" 
	     classname="JFlex.anttask.JFlexTask" name="jflex" /> ➂
  </target>

  <target name="generateSource" depends="resolve-ivy">
    <mkdir dir="target/gen/java"/>
    <jflex file="src/main/jflex/code2html.flex"    ➃
	   destdir="target/gen/java"/>
    <jflex file="src/main/jflex/code2tex.flex"
	   destdir="target/gen/java"/>

        ⋮

3.2.5 Set Up Classpaths

… to use the newly downloaded libraries.

build.xml3.listing
<project name="codeAnnotation" basedir="." default="build"
	 xmlns:ivy="antlib:org.apache.ivy.ant">

  <record name="ant.log" action="start" append="false" />
	
  <path id="testCompilationPath">
    <pathelement path="target/classes"/>
    <path refid="ivy.path"/>
  </path>

  <path id="testExecutionPath">
    <pathelement path="target/classes"/>
    <pathelement path="target/test-classes"/>
    <path refid="ivy.path"/>
  </path>


  <property name="ivy.install.version" value="2.3.0"/>
  <property name="ivy.jar.dir" value="${basedir}/ivy"/>
  <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar"/>
  <property name="build.dir" value="build"/>
  <property name="src.dir" value="src"/>

  <target name="download-ivy" unless="skip.download">
    <mkdir dir="${ivy.jar.dir}"/>
    <echo message="installing ivy..."/>
    <get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar" 
	 dest="${ivy.jar.file}" usetimestamp="true"/>
  </target>

  <target name="install-ivy" depends="download-ivy" 
	  description="--> install ivy">
    <path id="ivy.lib.path">
      <fileset dir="${ivy.jar.dir}" includes="*.jar"/>
    </path>
    <taskdef resource="org/apache/ivy/ant/antlib.xml" 
	     uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
  </target>

  <target name="resolve-ivy" depends="install-ivy">
    <ivy:retrieve/>
    <echo>ivy.default.ivy.user.dir is ${ivy.default.ivy.user.dir}</echo>
    <taskdef classpath="JFlex.jar" 
	     classname="JFlex.anttask.JFlexTask" name="jflex" />
  </target>

  <target name="generateSource" depends="resolve-ivy">
    <mkdir dir="target/gen/java"/>
    <jflex file="src/main/jflex/code2html.flex"
	   destdir="target/gen/java"/>
    <jflex file="src/main/jflex/code2tex.flex"
	   destdir="target/gen/java"/>
    <jflex file="src/main/jflex/list2html.flex"
	   destdir="target/gen/java"/>
    <jflex file="src/main/jflex/list2tex.flex"
	   destdir="target/gen/java"/>
  </target>
  

  <target name="compile" depends="generateSource">
    <mkdir dir="target/classes"/>
    <javac srcdir="target/gen/java" destdir="target/classes"
	   source="1.6" includeantruntime="false"/>
    <javac srcdir="src/main/java" destdir="target/classes"
	   source="1.6" includeantruntime="false"/>
  </target>

  <target name="compile-tests" depends="compile">
    <mkdir dir="target/test-classes"/>
    <javac srcdir="src/test/java" destdir="target/test-classes"
	   source="1.6" includeantruntime="false">
      <classpath refid="testCompilationPath"/>
    </javac>
  </target>

  <target name="test" depends="compile-tests">
    <mkdir dir="target/test-results/details"/>
    <junit printsummary="yes" 
	   haltonfailure="yes" fork="no"
	   >
      <classpath refid="testExecutionPath"/>
      <formatter type="xml"/>
      <batchtest todir="target/test-results/details">
	     <fileset dir="target/test-classes">
	        <include name="**/*Test*.class"/>
	     </fileset>
      </batchtest>
    </junit>
    <junitreport todir="target/test-results">
      <fileset dir="target/test-results/details">
        <include name="TEST-*.xml"/>
      </fileset>
      <report format="frames" todir="target/test-results/html"/>
    </junitreport>
  </target>

  <target name="build"  depends="test">
  	<jar destfile="codeAnnotation.jar" basedir="target/classes">
  	    <manifest>
              <attribute name="Main-Class"
                value="edu.odu.cs.code2html.Code2HTML"/>
  	    </manifest>
  	</jar>
  	<zip destfile="target/codeAnnotation-src.zip">
	  <fileset dir=".">
	    <include name="*.xml"/>
	    <include name="test.cpp"/>
	    <include name="*.css.cpp"/>
	    <include name="src/**/*"/>
	    <exclude name="**/*~"/>
	    <exclude name="target/**/*"/>
	  </fileset>
  	</zip>
  </target>

  <target name="clean">
    <delete dir="target"/>
  </target>

  <target name="cleaner" depends="clean">
    <delete dir="lib"/>
  </target>

  <target name="cleanest" depends="cleaner">
    <delete dir="ivy"/>
  </target>
  
</project>

We have made sure that ivy.path is a list of all 3rd party libraries downloaded by Ivy.

3.2.6 Ivy & Repositories

An example of an ivysettings.xml file:

ivysettings.xml.listing
<?xml version="1.0" encoding="UTF-8"?>
<ivy-settings>
  <settings defaultResolver="default" />
 <resolvers>
    <chain name="default">   ➀
      <ibiblio name="central" m2compatible="true"/> ➁
    </chain>
  </resolvers>
</ivy-settings>

3.2.7 Ivy & Eclipse

The developers of Ivy provide an Eclipse plugin, IvyDE.

3.3 Gradle

As is often the case, gradle tends to provide simpler syntax to do much the same things as Maven and Ant.

3.3.1 Selecting libraries in Gradle

In build.gradle:

apply plugin: 'java'


⋮

repositories {
    jcenter()   ➀

    // Use my own CS dept repo
    ivy {      ➁
        url 'https://secweb.cs.odu.edu/~zeil/ivyrepo'
    }
}



dependencies { ➂
    compile 'net.sf.saxon:saxon-dom:8.7+'
    testCompile 'junit:junit:4.12'
    testRuntime 'edu.odu.cs.zeil:reportAccumulator:1.1+'

}

Here you see both the choice of libraries and of repositories , .

The dependencies indicate

4 Eclipse and Library Management

Each of the build/library managers we have discussed have support in Eclipse

4.1 Eclipse and Maven

4.2 Eclipse and Ant/Ivy

4.3 Eclipse and Gradle

Eclipse has various Gradle plugins available.

The most popular seems to be the BuildShip package, available from the Gradle Marketplace (in the Eclipse Help menu).

5 Complicating Factors

5.1 Plug-ins

Plug-ins to build managers are generally handled separately from other items, though the syntax is often similar.

5.2 Publishing

Publishing artifacts to a repository is also supported by maven, Ant/Ivy, & Gradle.


Example: in Gradle build.xml

publishing {
    publications {
        ivyJava(IvyPublication) {
            organisation 'edu.odu.cs.zeil'
            module 'cowem-plugin'
            revision project.version
            descriptor.status = 'integration'   // milestone, release
            descriptor.branch = 'v1'

            from components.java
        }
    }

    repositories {
        ivy {
            name 'ivyRepo'
            url 'sftp://atria.cs.odu.edu:22/home/zeil/secure_html/ivyrepo'
            // Readable via https://www.cs.odu.edu/~zeil/ivyrepo
            credentials {
                ⋮
            }
        }
    }
}

5.3 Login Credentials

Providing login credentials to a build is problematic.

A common work-around is to store a few build “properties” in a file in the user’s home directory, not kept as part of the package version control.


Example: in Gradle build.xml

// Credentials are loaded from ~/.gradle/gradle.properties
if(project.hasProperty("ivyRepoUser")){        ➀
    ext.ivyRepoUser = "$ivyRepoUser";
} else {
    ext.ivyRepoUser = "user";
}
if(project.hasProperty("ivyRepoPass")){
    ext.ivyRepoPass = "$ivyRepoPass";
} else {
    ext.ivyRepoPass = "password";
}

⋮

publishing {
    ⋮
            credentials {                    ➁
                username project.ivyRepoUser
                password project.ivyRepoPass
            }
    ⋮
}

1: Yet Another Apache Project