Operations 13 min read

Setting Up Java 8 CI with Ant, Jenkins, SonarQube, and JaCoCo

This tutorial explains how to install and configure Java 8, Ant, JUnit 4, JaCoCo, Jenkins, and SonarQube on Windows or Linux to create a fully functional CI server with code‑coverage and quality analysis for a modular Java project.

FunTester
FunTester
FunTester
Setting Up Java 8 CI with Ant, Jenkins, SonarQube, and JaCoCo

Technical environment: CI is essential for security‑driven projects; the proof‑of‑concept demonstrates that Java 8, NetBeans 8.0, Ant, JUnit 4, JaCoCo 0.7.1, Jenkins and Sonar 4.2 can work together.

Java 8 & NetBeans & Ant: the tutorial builds a modular multi‑layer application using Ant (Maven can also be used) and describes the module suite structure.

JUnit 4 & JaCoCo: unit tests are written with JUnit 4 and code coverage is collected with JaCoCo, which fully supports Java 8.

Jenkins & Sonar: Jenkins serves as the CI engine, integrating the above tools; SonarQube performs quality analysis and is compatible with Java 8. The Sonar Ant task is added via a small library or Maven plugin.

Project configuration steps: install Java 8, create a module suite with classes and JUnit tests, commit to version control, set up JaCoCo and Sonar Ant task folders, and add the following Ant build file (sonar‑jacoco‑module.xml):

<?xml version="1.0" encoding="UTF-8"?>
<!-- ... -->
<project name="sonar-jacoco-module" basedir="." xmlns:jacoco="antlib:org.jacoco.ant" xmlns:sonar="antlib:org.sonar.ant">
    <description>Builds the module suite otherSuite.</description>
    <property name="jacoco.dir" location="${nbplatform.default.harness.dir}/jacoco-0.7.1"/>
    <property name="result.exec.file" location="${jacoco.dir}/jacoco.exec"/>
    <property name="build.test.results.dir" location="build/test/unit/results"/>
    <property file="nbproject/project.properties"/>
    <!-- Step 1: Import JaCoCo Ant tasks -->
    <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
        <classpath path="${jacoco.dir}/jacocoant.jar"/>
    </taskdef>
    <target name="-do-junit" depends="test-init">
        <echo message="Doing testing for jacoco"/>
        <macrodef name="junit-impl">
            <attribute name="test.type"/>
            <attribute name="disable.apple.ui" default="false"/>
            <sequential>
                <jacoco:coverage destfile="${build.test.results.dir}/${code.name.base}_jacoco.exec">
                    <junit showoutput="true" fork="true" failureproperty="tests.failed" errorproperty="tests.failed" filtertrace="${test.filter.trace}" tempdir="${build.test.@{test.type}.results.dir}" timeout="${test.timeout}">
                        <batchtest todir="${build.test.@{test.type}.results.dir}">
                            <fileset dir="${build.test.@{test.type}.classes.dir}" includes="${test.includes}" excludes="${test.excludes}"/>
                        </batchtest>
                        <classpath refid="test.@{test.type}.run.cp"/>
                        <syspropertyset refid="test.@{test.type}.properties"/>
                        <jvmarg value="${test.bootclasspath.prepend.args}"/>
                        <jvmarg line="${test.run.args}"/>
                        <sysproperty key="apple.awt.UIElement" value="@{disable.apple.ui}"/>
                        <formatter type="brief" usefile="false"/>
                        <formatter type="xml"/>
                    </junit>
                </jacoco:coverage>
                <copy file="${build.test.results.dir}/${code.name.base}_jacoco.exec" todir="${suite.dir}/build/coverage"/>
                <copy todir="${suite.dir}/build/test-results">
                    <fileset dir="${build.test.results.dir}">
                        <include name="**/TEST*.xml"/>
                    </fileset>
                </copy>
                <fail if="tests.failed" unless="continue.after.failing.tests">Some tests failed; see details above.</fail>
            </sequential>
        </macrodef>
        <junit-impl test.type="${run.test.type}" disable.apple.ui="${disable.apple.ui}"/>
    </target>
</project>

A second Ant file (sonar‑jacoco‑suite.xml) defines the SonarQube configuration at the suite level, including database connection properties, project keys, source, binary and test paths, and the tasks to merge JaCoCo reports and run Sonar analysis.

<?xml version="1.0" encoding="UTF-8"?>
<project name="sonar-jacoco-suite" basedir="." xmlns:jacoco="antlib:org.jacoco.ant" xmlns:sonar="antlib:org.sonar.ant">
    <description>Builds the module suite otherSuite.</description>
    <property name="jacoco.dir" location="${nbplatform.default.harness.dir}/jacoco-0.7.1"/>
    <property name="result.exec.file" location="build/coverage"/>
    <property name="sonar.jdbc.url" value="jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8"/>
    <property name="sonar.jdbc.username" value="sonar"/>
    <property name="sonar.jdbc.password" value="sonar"/>
    <property name="sonar.projectKey" value="org.codehaus.sonar:example-java-ant"/>
    <property name="sonar.projectName" value="Simple Java Project analyzed with the SonarQube Ant Task"/>
    <property name="sonar.projectVersion" value="1.0"/>
    <property name="sonar.language" value="java"/>
    <property file="nbproject/project.properties"/>
    <script language="javascript"><![CDATA[
        modulesName = project.getProperty("modules");
        modulesName = modulesName.replace(":",",");
        res = modulesName.split(",");
        srcModules = "";
        binariesModules = "";
        testModules = "";
        for (var i=0; i<res.length; i++) {
            srcModules += res[i]+"/src,";
            binariesModules += res[i]+"/build/classes,";
            testModules += res[i]+"/test,";
        }
        srcModules = srcModules.substring(0, srcModules.length - 1);
        binariesModules = binariesModules.substring(0, binariesModules.length - 1);
        testModules = testModules.substring(0, testModules.length - 1);
        project.setProperty("srcModulesPath",srcModules);
        project.setProperty("binariesModulesPath",binariesModules);
        project.setProperty("testModulesPath",testModules);
    ]]></script>
    <property name="sonar.sources" value="${srcModulesPath}"/>
    <property name="sonar.binaries" value="${binariesModulesPath}"/>
    <property name="sonar.tests" value="${testModulesPath}"/>
    <property name="sonar.dynamicAnalysis" value="reuseReports"/>
    <property name="sonar.junit.reportsPath" value="build/test-results"/>
    <property name="sonar.java.coveragePlugin" value="jacoco"/>
    <property name="sonar.jacoco.reportPath" value="${result.exec.file}/merged.exec"/>
    <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
        <classpath path="${jacoco.dir}/jacocoant.jar"/>
    </taskdef>
    <target name="merge-coverage">
        <jacoco:merge destfile="${result.exec.file}/merged.exec">
            <fileset dir="${result.exec.file}" includes="*.exec"/>
        </jacoco:merge>
    </target>
    <target name="sonar">
        <taskdef uri="antlib:org.sonar.ant" resource="org/sonar/ant/antlib.xml">
            <classpath path="${harness.dir}/sonar-ant-task-2.1/sonar-ant-task-2.1.jar"/>
        </taskdef>
        <sonar:sonar/>
    </target>
</project>

Additional steps include installing Jenkins plugins (JaCoCo, Mercurial/Subversion, Sonar), configuring the Jenkins system settings, creating a freestyle job that invokes the Ant build files, and adding a post‑build action to record JaCoCo coverage. Finally, the SonarQube database is prepared with a GRANT statement and the sonar.properties file is edited to enable MySQL.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaCISonarQubeJaCoCoJenkinsAnt
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.