Getting Started with the Jenkins API

This code includes all the curl commands to test connection through Git Bash (or other command line tool). This code also includes a main function to run and test connectivity.

The pom.xml dependency to use would be:


        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
            <version>1.5</version>
        </dependency>

Remember to substitute your url for my.jenkins.com.


import java.util.ArrayList;
import java.util.List;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

/**
 * API: 
 * http://my.jenkins.com/api/?
 * 
 * LIST JOBS:
 * http://my.jenkins.com/api/xml
 * 
 * COPY JOB: Create AA_TEST_JOB1 by copying AA_TEST_JOB0
 * curl -H "Content-Type:application/xml" "http://my.jenkins.com/createItem?name=AA_TEST_JOB1&mode=copy&from=AA_TEST_JOB0" 
 * 
 * DELETE JOB:
 * curl -X POST "http://my.jenkins.com/job/AA_TEST_JOB1/doDelete"
 * 
 * READ JOB:
 * curl "http://my.jenkins.com/job/AA_TEST_JOB0/config.xml" > config.xml
 * 
 * CREATE JOB: Create AA_TEST_JOB2 by using xml for configuration
 * curl -X POST -H "Content-Type:application/xml" -d "<project><builders/><publishers/><buildWrappers/></project>" "http://my.jenkins.com/createItem?name=AA_TEST_JOB2"
 * curl -X POST -H "Content-Type:application/xml" -d @config.xml "http://my.jenkins.com/createItem?name=AA_TEST_JOB3" 
 * 
 * @author sizu
 *
 */
public class JenkinsTest {
	public static void main(String[] args) {
		System.out.println("Check 0: List Jobs");
		List<String> jobList = listJobs("http://my.jenkins.com");
		System.out.println("First Job:"+jobList.get(0));

		System.out.println("Check 1: Delete AA_TEST_JOB1 (created manually)");
		deleteJob("http://my.jenkins.com", "AA_TEST_JOB1");

		System.out.println("Check 2: Create AA_TEST_JOB2 by copying first job");
		copyJob("http://my.jenkins.com", "AA_TEST_JOB2", jobList.get(0));
//		deleteJob("http://my.jenkins.com", "AA_TEST_JOB2");

		System.out.println("Check 3: Create AA_TEST_JOB3 by using a generic xml configuration");
		createJob("http://my.jenkins.com", "AA_TEST_JOB3", "<project><builders/><publishers/><buildWrappers/></project>");
//		deleteJob("http://my.jenkins.com", "AA_TEST_JOB3");
		
		System.out.println("Check 4: Create AA_TEST_JOB4 by using the xml configuration from the first job (similar to copyJob)");
		String configXML = readJob("http://my.jenkins.com", jobList.get(0));
		createJob("http://my.jenkins.com", "AA_TEST_JOB4", configXML);
//		deleteJob("http://my.jenkins.com", "AA_TEST_JOB4");
	}
	
	public static List<String> listJobs(String url) {
		Client client = Client.create();
//		client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(USERNAME, PASSWORD));
		WebResource webResource = client.resource(url+"/api/xml");
		ClientResponse response = webResource.get(ClientResponse.class);
		String jsonResponse = response.getEntity(String.class);
		client.destroy();
//		System.out.println("Response listJobs:::::"+jsonResponse);
		
		// Assume jobs returned are in xml format, TODO using an XML Parser would be better here
		// Get name from <job><name>...
		List<String> jobList = new ArrayList<String>();
		String[] jobs = jsonResponse.split("job>"); // 1, 3, 5, 7, etc will contain jobs
		for(String job: jobs){
			String[] names = job.split("name>");
			if(names.length == 3) {
				String name = names[1];
				name = name.substring(0,name.length()-2); // Take off </ for the closing name tag: </name>
				jobList.add(name);
//				System.out.println("name:"+name);
			}
//			System.out.println("job:"+job);
//			for(String name: names){
//				System.out.println("name:"+name);
//			}
		}
		return jobList;
	}

	public static String deleteJob(String url, String jobName) {
		Client client = Client.create();
//		client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(USERNAME, PASSWORD));
		WebResource webResource = client.resource(url+"/job/"+jobName+"/doDelete");
		ClientResponse response = webResource.post(ClientResponse.class);
		String jsonResponse = response.getEntity(String.class);
		client.destroy();
//		System.out.println("Response deleteJobs:::::"+jsonResponse);
		return jsonResponse;
	}
	
	public static String copyJob(String url, String newJobName, String oldJobName){
		Client client = Client.create();
//		client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(USERNAME, PASSWORD));
		WebResource webResource = client.resource(url+"/createItem?name="+newJobName+"&mode=copy&from="+oldJobName);
		ClientResponse response = webResource.type("application/xml").get(ClientResponse.class);
		String jsonResponse = response.getEntity(String.class);
		client.destroy();
//		System.out.println("Response copyJob:::::"+jsonResponse);
		return jsonResponse;
	}
	
	public static String createJob(String url, String newJobName, String configXML){
		Client client = Client.create();
//		client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(USERNAME, PASSWORD));
		WebResource webResource = client.resource(url+"/createItem?name="+newJobName);
		ClientResponse response = webResource.type("application/xml").post(ClientResponse.class, configXML);
		String jsonResponse = response.getEntity(String.class);
		client.destroy();
		System.out.println("Response createJob:::::"+jsonResponse);
		return jsonResponse;
	}

	public static String readJob(String url, String jobName){
		Client client = Client.create();
//		client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(USERNAME, PASSWORD));
		WebResource webResource = client.resource(url+"/job/"+jobName+"/config.xml");
		ClientResponse response = webResource.get(ClientResponse.class);
		String jsonResponse = response.getEntity(String.class);
		client.destroy();
//		System.out.println("Response readJob:::::"+jsonResponse);
		return jsonResponse;
	}

A Discussion on the Adapter Design Pattern

Many times, organizations use third party code. Reuse of code is excellent, especially when the tools are commonly used. I believe that if my code can’t help someone else do their job faster, they shouldn’t use my code. In general, this same philosophy is true. If our product can’t make your life easier, and help you do what you are already doing, faster, smoother, etc, why use our product?

The community which developed the tool will commonly have forums and you can find a lot of support online, to help get started. Support is priceless. Months of hitting your head against the wall can be reduced to weeks or even days, with support, since most people hit the same bottle necks and see the same problems.

It is not uncommon for third party tools to take 2 weeks to get started with. This is the type of timeline I have seen when an organization brings in a new tool such as Git, Jenkins, JIRA API, etc. So, what should occur during this effort? Why not lend a helping hand to others getting started with the tool?

While you may have a specific application for the code, it is good to create a module, specifically designed to pulling in the resources necessary to get started with the new code. This is part of the adapter design pattern. The code is fixed, from the third party and you build a module that tailors it to your organization. The third party code may have a billion features, but you only need to use a small subset. Your adapter module can pull in the resources and provide demos that actually link to your in house servers or machines.

This concept can be applied to bring in a third party tool into an organization or bring a jar into a project. When you isolate the dependencies, updates from the third party code or jar are limited to a specific module or package. This makes updates easier to handle.

What does this do? This allows new teams to get up and running with the new tool within 1 hour. This should be your goal. Download code, compile and run within an hour. That is the standard for a team’s project and should be the standard to demonstrate a new tool’s capability using an adapter module. You can also document this new tool within a Wiki page. Why create more documentation for a tool widely documented? The purpose of the documentation is to show the adaptation. How does the tool relate to the organization? How was it used? What challenges and setup instructions were needed?

If you create the adapter module (can be a jar file) and adapter wiki, you will enable other teams to:

  1. use your setup instructions to use the same capability
  2. use your demos to see the capability of the third party tool
  3. use your demos to get up and running with the third party tool
  4. use your adapter module

Using WebResource and JiraRestClient to connect to JIRA

This article was generated with the help from these links:

The article is a follow on to these other articles:

See links above for pom.xml dependencies.


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;
import com.atlassian.jira.rest.client.api.JiraRestClient;
import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
import com.atlassian.jira.rest.client.api.domain.BasicProject;
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;

public class JiraConnectivityCheck {
	private static String HTTP = "https://";
	private static String HOST = "jirap.mycompany.org";
	private static String USERNAME = "sizu";
	private static String PASSWORD = "testPassword";
	
	public static void main(String[] args) throws Exception {
		JiraLoginCredentials jlc = new JiraLoginCredentials(HTTP, HOST, USERNAME, PASSWORD);
		new SSLHandler().setupSSLContextByInstallingCertification(jlc.getHost());
		
		System.out.println("Testing JiraWebResource...");
		String jsonResponse = getResource(jlc, "/rest/api/2/project");
		JSONArray projectArray = new JSONArray(jsonResponse);
		for (int i = 0; i < projectArray.length(); i++) {
			JSONObject proj = projectArray.getJSONObject(i);
			System.out.println("JSONObject Project Name:"+proj.getString("name"));
		}
		
		System.out.println("Testing JiraRestClient...");
		Iterable basicProjects = findProjects(jlc);
		for(BasicProject project: basicProjects) {
			System.out.println("BasicProject Project Name:"+project.getName());
		}
		
	}
	
	/**
	 * Uses WebResource to connect to JIRA
	 * @param lc
	 * @param resource
	 * @return
	 */
	private static String getResource(JiraLoginCredentials lc, String resource) {
		Client client = Client.create();
		client.addFilter(new HTTPBasicAuthFilter(lc.getUsername(), lc.getPassword()));
		WebResource webResource = client.resource(lc.getUrl()+resource);
		ClientResponse response = webResource.type("application/json").accept("application/json").get(ClientResponse.class);
		String jsonResponse = response.getEntity(String.class);
		client.destroy();
		return jsonResponse;
	}
	
	/**
	 * Uses JiraRestClient to connect to JIRA
	 * @param lc
	 * @return
	 */
	public static Iterable findProjects(JiraLoginCredentials lc) {
		final JiraRestClientFactory jiraRestClientFactory = new AsynchronousJiraRestClientFactory();
		JiraRestClient jiraRestClient = jiraRestClientFactory.createWithBasicHttpAuthentication(URI.create(lc.getUrl()),
						lc.getUsername(), lc.getPassword());
		Iterable projects = null;
		try {
			projects = jiraRestClient.getProjectClient().getAllProjects().get();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			jiraRestClient.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return projects;
	}
	
	public static class JiraLoginCredentials {
		private String httpPrefix;
		private String host;
		private String username;
		private String password;
		public JiraLoginCredentials(String httpPrefix, String host, String username, String password) {
			super();
			this.httpPrefix = httpPrefix;
			this.host = host;
			this.username = username;
			this.password = password;
		}
		public String getUrl() {
			return httpPrefix+host;
		}
		public String getHost() {
			return host;
		}
		public String getUsername() {
			return username;
		}
		public String getPassword() {
			return password;
		}
	}
	
	public static class SSLHandler {
		/**
		 * Installs C:\Program Files\Java\jdk1.7.0_51\jre\lib\security\jssecacerts file
		 * @param host
		 * @throws Exception
		 */
		public void setupSSLContextByInstallingCertification(String host) {
			System.out.println("Installing SSLContext Certification into Java Home...");
			try {
		        int port = 443;
		        char[] passphrase = "changeit".toCharArray();
				
		        char SEP = File.separatorChar;
		        File file = new File("jssecacerts");
		        if (file.isFile() == false) {
		            File dir = new File(System.getProperty("java.home") + SEP
		                    + "lib" + SEP + "security");
		            file = new File(dir, "jssecacerts");
		            if (file.isFile() == false) {
		                file = new File(dir, "cacerts");
		            }
		        }
		        
		        System.out.println("Loading KeyStore " + file + "...");
		        InputStream in = new FileInputStream(file);
		        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
		        ks.load(in, passphrase);
		        in.close();
		 
		        TrustManagerFactory tmf =
		                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
		        tmf.init(ks);
		        final X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
		        final List chainList = new ArrayList();
		        
				TrustManager tm = new X509TrustManager() {
					public void checkClientTrusted(X509Certificate[] arg0, String arg1)
									throws CertificateException { }
					public void checkServerTrusted(X509Certificate[] arg0, String arg1)
							throws CertificateException {
							chainList.clear();
							if(arg0 != null) {
								for (int i = 0; i < arg0.length; i++) {
									chainList.add(arg0[i]);
								}
							}
							defaultTrustManager.checkServerTrusted(arg0, arg1); }
					public X509Certificate[] getAcceptedIssuers() { return null; }
				};
				
		        SSLContext context = SSLContext.getInstance("TLS");
		        context.init(null, new TrustManager[]{tm}, null);
		        SSLSocketFactory factory = context.getSocketFactory();
		 
		        System.out.println("Opening connection to " + host + ":" + port + "...");
		        SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
		        socket.setSoTimeout(10000);
		        try {
		            socket.startHandshake();
		            System.out.println("No errors, certificate is already trusted");
		        } catch (SSLException e) { }
		        socket.close();
		 
		        if (chainList.isEmpty()) {
		            System.out.println("Could not obtain server certificate chain");
		            return;
		        }
		 
		        System.out.println("Server sent " + chainList.size() + " certificate(s):");
	
		        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
		        MessageDigest md5 = MessageDigest.getInstance("MD5");
		        for (int i = 0; i < chainList.size(); i++) {
		            X509Certificate cert = chainList.get(i);
		            System.out.println
		                    (" " + (i + 1) + " Subject " + cert.getSubjectDN());
		            System.out.println("   Issuer  " + cert.getIssuerDN());
		            sha1.update(cert.getEncoded());
		            System.out.println("   sha1    " + toHexString(sha1.digest()));
		            md5.update(cert.getEncoded());
		            System.out.println("   md5     " + toHexString(md5.digest()));
		        }
		        
		        int k = 0;
		        X509Certificate cert = chainList.get(k);
		        String alias = host + "-" + (k + 1);
		        ks.setCertificateEntry(alias, cert);
		 
		        File dir = new File(System.getProperty("java.home") + SEP
		                + "lib" + SEP + "security");
		        file = new File(dir, "jssecacerts");
		        OutputStream out = new FileOutputStream(file);
		        ks.store(out, passphrase);
		        out.close();
		        
		        System.out.println("Installed SSLContext Certification into Java Home: "+file.getAbsolutePath());
			} catch (Exception ex) { }
		}
	 
	    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
	 
	    private static String toHexString(byte[] bytes) {
	        StringBuilder sb = new StringBuilder(bytes.length * 3);
	        for (int b : bytes) {
	            b &= 0xff;
	            sb.append(HEXDIGITS[b >> 4]);
	            sb.append(HEXDIGITS[b & 15]);
	            sb.append(' ');
	        }
	        return sb.toString();
	    }
	}
}

Common errors when running this code are listed below:

  • WebResource – HTTP – Exception in thread “main” com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
  • WebResource – HTTP – org.codehaus.jettison.json.JSONException: A JSONArray text must start with ‘[‘ at character 0 of
  • WebResource – HOST – Exception in thread “main” com.sun.jersey.api.client.ClientHandlerException: java.net.UnknownHostException:
  • WebResource – ACTION – org.codehaus.jettison.json.JSONException: A JSONArray text must start with ‘[‘ at character 1 of {“message”:”null for uri: …”,”status-code”:404}
  • WebResource – SSL – Exception in thread “main” com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
  • JiraRestClient – HTTP – java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.net.ConnectException: Connection refused: no further information
  • JiraRestClient – HOST – java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.net.UnknownHostException:
  • JiraRestClient – SSL – javax.net.ssl.SSLHandshakeException: General SSLEngine problem

For HTTP errors, you might need “http://&#8221; or “https://&#8221; or to configure the JIRA server. For HOST errors, check the host is accessible using curl. For ACTION errors (WebResource), make sure the resource is accessible using curl. For SSL errors, you can use the SSLHandler code above to install the certification.

You might not need a certificate installed, in which case, instead of using setupSSLContextByInstallingCertification, you might try setupSSLContextUsingGenericTrustManager from the following code:


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class SSLHandler {

	public void setupSSLContextUsingGenericTrustManager() {
		System.out.println("Setting SSLContext using Generic Trust Manager");
		TrustManager trustManager = new X509TrustManager() {
			public void checkClientTrusted(X509Certificate[] arg0, String arg1)
							throws CertificateException { }
			public void checkServerTrusted(X509Certificate[] arg0, String arg1)
					throws CertificateException { }
			public X509Certificate[] getAcceptedIssuers() { return null; }
		};
				
		try {
			SSLContext sslcontext = SSLContext.getInstance("TLS");
			sslcontext.init(null, new TrustManager[] { trustManager }, null);
			SSLContext.setDefault(sslcontext);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (KeyManagementException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Installs C:\Program Files\Java\jdk1.7.0_51\jre\lib\security\jssecacerts file
	 * @param host
	 * @throws Exception
	 */
	public void setupSSLContextByInstallingCertification(String host) {
		System.out.println("Installing SSLContext Certification into Java Home...");
		try {
	        int port = 443;
	        char[] passphrase = "changeit".toCharArray();
			
	        char SEP = File.separatorChar;
	        File file = new File("jssecacerts");
	        if (file.isFile() == false) {
	            File dir = new File(System.getProperty("java.home") + SEP
	                    + "lib" + SEP + "security");
	            file = new File(dir, "jssecacerts");
	            if (file.isFile() == false) {
	                file = new File(dir, "cacerts");
	            }
	        }
	        
	        System.out.println("Loading KeyStore " + file + "...");
	        InputStream in = new FileInputStream(file);
	        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
	        ks.load(in, passphrase);
	        in.close();
	 
	        TrustManagerFactory tmf =
	                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
	        tmf.init(ks);
	        final X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
	        final List chainList = new ArrayList();
	        
			TrustManager tm = new X509TrustManager() {
				public void checkClientTrusted(X509Certificate[] arg0, String arg1)
								throws CertificateException { }
				public void checkServerTrusted(X509Certificate[] arg0, String arg1)
						throws CertificateException {
						chainList.clear();
						if(arg0 != null) {
							for (int i = 0; i < arg0.length; i++) {
								chainList.add(arg0[i]);
							}
						}
						defaultTrustManager.checkServerTrusted(arg0, arg1); }
				public X509Certificate[] getAcceptedIssuers() { return null; }
			};
			
	        SSLContext context = SSLContext.getInstance("TLS");
	        context.init(null, new TrustManager[]{tm}, null);
	        SSLSocketFactory factory = context.getSocketFactory();
	 
	        System.out.println("Opening connection to " + host + ":" + port + "...");
	        SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
	        socket.setSoTimeout(10000);
	        try {
	            socket.startHandshake();
	            System.out.println("No errors, certificate is already trusted");
	        } catch (SSLException e) { }
	        socket.close();
	 
	        if (chainList.isEmpty()) {
	            System.out.println("Could not obtain server certificate chain");
	            return;
	        }
	 
	        System.out.println("Server sent " + chainList.size() + " certificate(s):");

	        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
	        MessageDigest md5 = MessageDigest.getInstance("MD5");
	        for (int i = 0; i < chainList.size(); i++) {
	            X509Certificate cert = chainList.get(i);
	            System.out.println
	                    (" " + (i + 1) + " Subject " + cert.getSubjectDN());
	            System.out.println("   Issuer  " + cert.getIssuerDN());
	            sha1.update(cert.getEncoded());
	            System.out.println("   sha1    " + toHexString(sha1.digest()));
	            md5.update(cert.getEncoded());
	            System.out.println("   md5     " + toHexString(md5.digest()));
	        }
	        
	        int k = 0;
	        X509Certificate cert = chainList.get(k);
	        String alias = host + "-" + (k + 1);
	        ks.setCertificateEntry(alias, cert);
	 
	        File dir = new File(System.getProperty("java.home") + SEP
	                + "lib" + SEP + "security");
	        file = new File(dir, "jssecacerts");
	        OutputStream out = new FileOutputStream(file);
	        ks.store(out, passphrase);
	        out.close();
	        
	        System.out.println("Installed SSLContext Certification into Java Home: "+file.getAbsolutePath());
		} catch (Exception ex) { }
	}
 
    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
 
    private static String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 3);
        for (int b : bytes) {
            b &= 0xff;
            sb.append(HEXDIGITS[b >> 4]);
            sb.append(HEXDIGITS[b & 15]);
            sb.append(' ');
        }
        return sb.toString();
    }
}

When using the JIRA API, you must setup the SSL Context, Authenticate (insert your login credentials into the above code) and choose a connection mechanism (WebResource or JiraRestClient). See the previous articles for more code samples.