com.cloudbees.eclipse.core.JenkinsService.java Source code

Java tutorial

Introduction

Here is the source code for com.cloudbees.eclipse.core.JenkinsService.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Cloud Bees, Inc.
 * All rights reserved. 
 * This program is made available under the terms of the 
 * Eclipse Public License v1.0 which accompanies this distribution, 
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Cloud Bees, Inc. - initial API and implementation 
 *******************************************************************************/
package com.cloudbees.eclipse.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import org.apache.http.Header;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;

import com.cloudbees.eclipse.core.domain.JenkinsInstance;
import com.cloudbees.eclipse.core.jenkins.api.JenkinsBuildDetailsResponse;
import com.cloudbees.eclipse.core.jenkins.api.JenkinsConfigParser;
import com.cloudbees.eclipse.core.jenkins.api.JenkinsConsoleLogResponse;
import com.cloudbees.eclipse.core.jenkins.api.JenkinsInstanceResponse;
import com.cloudbees.eclipse.core.jenkins.api.JenkinsJobAndBuildsResponse;
import com.cloudbees.eclipse.core.jenkins.api.JenkinsJobsResponse;
import com.cloudbees.eclipse.core.jenkins.api.JenkinsScmConfig;
import com.cloudbees.eclipse.core.util.Utils;
import com.google.gson.Gson;

/**
 * Service to access Jenkins instances
 * 
 * @author ahti
 */
public class JenkinsService {

    enum ResponseType {
        STRING, STREAM, HTTP
    }

    /*private String url;
    private String label;*/
    private final JenkinsInstance jenkins;

    private final Map<String, JenkinsScmConfig> scms = new HashMap<String, JenkinsScmConfig>();

    public JenkinsService(final JenkinsInstance jenkins) {
        this.jenkins = jenkins;
    }

    /**
     * @param viewUrl
     *          full url for the view. <code>null</code> if all jobs from this instance.
     * @return
     * @throws CloudBeesException
     */
    public JenkinsJobsResponse getJobs(final String viewUrl, final IProgressMonitor monitor)
            throws CloudBeesException {
        assertCorrectUrl(viewUrl);

        try {
            monitor.beginTask("Fetching Job list for '" + this.jenkins.label + "'...", 10);

            Gson g = Utils.createGson();

            String reqUrl = viewUrl; // != null ? viewUrl : jenkins.url;

            if (!reqUrl.endsWith("/")) {
                reqUrl += "/";
            }

            String uri = reqUrl + "api/json";

            HttpPost post = new HttpPost(uri);
            //post.setHeader("Accept", "application/json");
            post.setHeader("Content-type", "application/x-www-form-urlencoded");

            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
            nameValuePairs.add(new BasicNameValuePair("tree", JenkinsJobsResponse.QTREE));

            //StringEntity se = new StringEntity("tree="+JenkinsJobsResponse.QTREE, "UTF-8");
            //post.setEntity(se);
            post.setEntity(new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8));

            monitor.worked(1);

            DefaultHttpClient httpclient = Utils.getAPIClient(uri);

            String bodyResponse = retrieveWithLogin(httpclient, post, null, false,
                    new SubProgressMonitor(monitor, 5));

            JenkinsJobsResponse views = null;
            try {
                views = g.fromJson(bodyResponse, JenkinsJobsResponse.class);
            } catch (Exception e) {
                String body = bodyResponse;
                if (body != null && body.length() > 1000) {
                    body = body.substring(0, 1000);
                }
                throw new CloudBeesException("Illegal JSON response for '" + uri + "': " + body, e);
            }

            if (views != null) {
                views.viewUrl = viewUrl;
            }

            if (views.jobs == null && views.primaryView != null && views.primaryView.jobs != null) {
                views.jobs = views.primaryView.jobs;
            }

            monitor.worked(4);

            return views;

        } catch (Exception e) {
            throw new CloudBeesException("Failed to get Jenkins jobs for '" + this.jenkins.url + "'.", e);
        } finally {
            monitor.done();
        }

    }

    private void assertCorrectUrl(String viewUrl) throws CloudBeesException {
        if (viewUrl != null && !viewUrl.startsWith(this.jenkins.url)) {
            if (viewUrl != null && this.jenkins.alternativeUrl != null
                    && !viewUrl.startsWith(this.jenkins.alternativeUrl)) {
                throw new CloudBeesException(
                        "Unexpected url provided! Service url: " + this.jenkins.url + "; view url: " + viewUrl);
            }
        }
    }

    public JenkinsInstanceResponse getInstance(final IProgressMonitor monitor) throws CloudBeesException {
        StringBuffer errMsg = new StringBuffer();

        String reqUrl = this.jenkins.url;
        if (!reqUrl.endsWith("/")) {
            reqUrl += "/";
        }
        reqUrl += "api/json";

        try {
            Gson g = Utils.createGson();

            HttpPost post = new HttpPost(reqUrl);
            //post.setHeader("Accept", "application/json");
            post.setHeader("Content-type", "application/x-www-form-urlencoded");

            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
            nameValuePairs.add(new BasicNameValuePair("tree", JenkinsInstanceResponse.QTREE));
            post.setEntity(new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8));

            DefaultHttpClient httpclient = Utils.getAPIClient(reqUrl);

            String bodyResponse = retrieveWithLogin(httpclient, post, null, false,
                    new SubProgressMonitor(monitor, 10));

            if (bodyResponse == null) {
                throw new CloudBeesException("Failed to receive response from server");
            }

            JenkinsInstanceResponse response = g.fromJson(bodyResponse, JenkinsInstanceResponse.class);

            if (response != null) {
                response.viewUrl = this.jenkins.url;
                response.atCloud = this.jenkins.atCloud;

                jenkins.alternativeUrl = response.primaryView.url; // Initializing jenkins instance alternativeUrl for lookups. Not perfectly nice solution in terms of reverse logic but looks like most feasible atm.

                if (response.views != null) {
                    for (int i = 0; i < response.views.length; i++) {
                        response.views[i].response = response;
                        response.views[i].isPrimary = response.primaryView.url.equals(response.views[i].url);
                    }
                }
            }

            return response;

        } catch (OperationCanceledException e) {
            throw e;
        } catch (Exception e) {
            throw new CloudBeesException("Failed to get Jenkins views for '" + reqUrl + "'. "
                    + (errMsg.length() > 0 ? " (" + errMsg + ")" : ""), e);
        }
    }

    synchronized private String retrieveWithLogin(final DefaultHttpClient httpclient, final HttpRequestBase post,
            final List<NameValuePair> params, final boolean expectRedirect, final SubProgressMonitor monitor)
            throws UnsupportedEncodingException, IOException, ClientProtocolException, CloudBeesException,
            Exception {
        return (String) retrieveWithLogin(httpclient, post, params, expectRedirect, monitor, ResponseType.STRING);
    }

    synchronized private Object retrieveWithLogin(final DefaultHttpClient httpclient, final HttpRequestBase post,
            final List<NameValuePair> params, final boolean expectRedirect, final SubProgressMonitor monitor,
            final ResponseType responseType) throws UnsupportedEncodingException, IOException,
            ClientProtocolException, CloudBeesException, Exception {
        Object bodyResponse = null;

        if (this.jenkins.username != null && this.jenkins.username.trim().length() > 0
                && this.jenkins.password != null && this.jenkins.password.trim().length() > 0) {
            post.addHeader("Authorization",
                    "Basic " + Utils.toB64(this.jenkins.username + ":" + this.jenkins.password));
        }

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        if (params != null) {
            nvps.addAll(params);
        }

        if (post instanceof HttpEntityEnclosingRequest) {
            if (((HttpEntityEnclosingRequest) post).getEntity() == null) {
                ((HttpEntityEnclosingRequest) post).setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
            }
        }

        //CloudBeesCorePlugin.getDefault().getLogger().info("Jenkins request: " + post.getURI());

        if (post instanceof HttpPost) {
            HttpPost pp = (HttpPost) post;

            String s;
            try {
                s = new Scanner(pp.getEntity().getContent()).useDelimiter("\\A").next();
            } catch (java.util.NoSuchElementException e) {
                s = "";
            }

            //CloudBeesCorePlugin.getDefault().getLogger().info("Jenkins request post params: " + s);

        }

        HttpResponse resp = httpclient.execute(post);
        switch (responseType) {
        case STRING:
            bodyResponse = Utils.getResponseBody(resp);
            break;
        case STREAM:
            bodyResponse = resp.getEntity().getContent();
            break;
        case HTTP:
            bodyResponse = resp;
            break;
        }

        Utils.checkResponseCode(resp, expectRedirect, jenkins.atCloud);

        return bodyResponse;
    }

    public String getLabel() {
        return this.jenkins.label;
    }

    public String getUrl() {
        return this.jenkins.url;
    }

    public boolean isCloud() {
        return this.jenkins.atCloud;
    }

    /**
     * Returns url that was assigned by the JSON request by jenkins
     * 
     * @return
     */
    public String getAlternativeUrl() {
        return this.jenkins.alternativeUrl;
    }

    @Override
    public String toString() {
        if (this.jenkins != null) {
            return "JenkinsService[jenkinsInstance=" + this.jenkins + "]";
        }
        return super.toString();
    }

    public JenkinsBuildDetailsResponse getJobDetails(final String jobUrl, final IProgressMonitor monitor)
            throws CloudBeesException {
        monitor.setTaskName("Fetching Job details...");

        assertCorrectUrl(jobUrl);

        StringBuffer errMsg = new StringBuffer();

        String reqUrl = jobUrl;

        if (!reqUrl.endsWith("/")) {
            reqUrl = reqUrl + "/";
        }

        String reqStr = reqUrl + "api/json";

        try {

            Gson g = Utils.createGson();

            HttpPost post = new HttpPost(reqStr);

            //post.setHeader("Accept", "application/json");
            post.setHeader("Content-type", "application/x-www-form-urlencoded");

            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
            nameValuePairs.add(new BasicNameValuePair("tree", JenkinsBuildDetailsResponse.QTREE));
            post.setEntity(new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8));

            DefaultHttpClient httpclient = Utils.getAPIClient(reqStr);

            String bodyResponse = retrieveWithLogin(httpclient, post, null, false,
                    new SubProgressMonitor(monitor, 10));

            JenkinsBuildDetailsResponse details = g.fromJson(bodyResponse, JenkinsBuildDetailsResponse.class);

            if (details.getDisplayName() == null) {
                throw new CloudBeesException("Response does not contain required fields!");
            }

            if (details.viewUrl == null) {
                details.viewUrl = jobUrl;
            }

            return details;

        } catch (Exception e) {
            throw new CloudBeesException("Failed to get Jenkins jobs for '" + jobUrl + "'. "
                    + (errMsg.length() > 0 && errMsg.length() < 1000 ? " (" + errMsg + ")" : "") + "Request string:"
                    + reqStr, e);
        }

    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (this.jenkins == null ? 0 : this.jenkins.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        JenkinsService other = (JenkinsService) obj;
        if (this.jenkins == null) {
            if (other.jenkins != null) {
                return false;
            }
        } else if (!this.jenkins.equals(other.jenkins)) {
            return false;
        }
        return true;
    }

    public JenkinsJobAndBuildsResponse getJobBuilds(final String jobUrl, final IProgressMonitor monitor)
            throws CloudBeesException {
        monitor.setTaskName("Fetching Job builds...");

        assertCorrectUrl(jobUrl);

        StringBuffer errMsg = new StringBuffer();

        String reqUrl = jobUrl;

        if (!reqUrl.endsWith("/")) {
            reqUrl = reqUrl + "/";
        }

        String reqStr = reqUrl + "api/json";

        try {

            Gson g = Utils.createGson();

            HttpPost post = new HttpPost(reqStr);
            //post.setHeader("Accept", "application/json");
            post.setHeader("Content-type", "application/x-www-form-urlencoded");

            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
            nameValuePairs.add(new BasicNameValuePair("tree", JenkinsJobAndBuildsResponse.QTREE));
            post.setEntity(new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8));

            DefaultHttpClient httpclient = Utils.getAPIClient(reqStr);
            String bodyResponse = retrieveWithLogin(httpclient, post, null, false,
                    new SubProgressMonitor(monitor, 10));

            JenkinsJobAndBuildsResponse details = g.fromJson(bodyResponse, JenkinsJobAndBuildsResponse.class);

            if (details.name == null) {
                throw new CloudBeesException("Response does not contain required fields!");
            }

            details.viewUrl = jobUrl; // this.jenkins.url;

            return details;

        } catch (Exception e) {
            throw new CloudBeesException("Failed to get Jenkins jobs for '" + jobUrl + "'. "
                    + (errMsg.length() > 0 && errMsg.length() < 1000 ? " (" + errMsg + ")" : "") + "Request string:"
                    + reqStr, e);
        }
    }

    private JenkinsScmConfig getJobScmConfig(final String jobUrl, final IProgressMonitor monitor)
            throws CloudBeesException {
        monitor.setTaskName("Fetching Job SCM config...");

        assertCorrectUrl(jobUrl);

        StringBuffer errMsg = new StringBuffer();

        String reqUrl = jobUrl;

        if (!reqUrl.endsWith("/")) {
            reqUrl = reqUrl + "/";
        }

        String reqStr = reqUrl + "config.xml";
        String bodyResponse = null;
        try {
            DefaultHttpClient httpclient = Utils.getAPIClient(reqStr);

            HttpGet post = new HttpGet(reqStr);
            post.setHeader("Accept", "text/html,application/xhtml+xml,application/xml");

            bodyResponse = retrieveWithLogin(httpclient, post, null, false, new SubProgressMonitor(monitor, 10));

            JenkinsScmConfig config = JenkinsConfigParser.parse(bodyResponse);

            return config;
        } catch (Exception e) {
            throw new CloudBeesException("Failed to get Jenkins SCM config from '" + reqStr + "'. "
                    + (errMsg.length() > 0 ? " (" + errMsg + ")" : "")
                    + (bodyResponse == null ? "" : " - Response: " + bodyResponse), e);
        }
    }

    public void invokeBuild(final String jobUrl, final Map<String, String> props, final IProgressMonitor monitor)
            throws CloudBeesException {
        monitor.setTaskName("Invoking build request...");

        assertCorrectUrl(jobUrl);

        StringBuffer errMsg = new StringBuffer();

        String reqUrl = jobUrl;

        if (!reqUrl.endsWith("/")) {
            reqUrl = reqUrl + "/";
        }

        String reqStr;
        if (props == null || props.isEmpty()) {
            reqStr = reqUrl + "build";
        } else {
            reqStr = reqUrl + "buildWithParameters";
        }

        List<NameValuePair> params = null;
        if (props != null) {
            for (Map.Entry<String, String> entry : props.entrySet()) {
                if (params == null) {
                    params = new ArrayList<NameValuePair>();
                }
                params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
        }

        try {
            DefaultHttpClient httpclient = Utils.getAPIClient(reqStr);

            HttpPost post = new HttpPost(reqStr);

            retrieveWithLogin(httpclient, post, params, true, new SubProgressMonitor(monitor, 10));
        } catch (Exception e) {
            throw new CloudBeesException("Failed to get invoke JenkinsBuild for '" + jobUrl + "'. "
                    + (errMsg.length() > 0 ? " (" + errMsg + ")" : "") + "Request string:" + reqStr, e);
        }
    }

    public JenkinsScmConfig getJenkinsScmConfig(final String jobUrl, final IProgressMonitor monitor)
            throws CloudBeesException {
        // TODO invalidate old items
        JenkinsScmConfig scm = this.scms.get(jobUrl);
        if (scm == null) {
            scm = getJobScmConfig(jobUrl, monitor);
            if (scm != null) {
                this.scms.put(jobUrl, scm);
            }
        }
        return scm;
    }

    public InputStream getTestReport(final String url, final IProgressMonitor monitor) throws CloudBeesException {
        monitor.setTaskName("Fetching Jenkins build Test Report...");

        assertCorrectUrl(url);

        StringBuffer errMsg = new StringBuffer();

        String reqUrl = url;

        if (!reqUrl.endsWith("/")) {
            reqUrl = reqUrl + "/";
        }

        String reqStr = reqUrl + "testReport/api/xml";

        InputStream bodyResponse = null;
        try {
            DefaultHttpClient httpclient = Utils.getAPIClient(reqStr);

            HttpGet post = new HttpGet(reqStr);
            post.setHeader("Accept", "text/html,application/xhtml+xml,application/xml");

            bodyResponse = (InputStream) retrieveWithLogin(httpclient, post, null, false,
                    new SubProgressMonitor(monitor, 10), ResponseType.STREAM);

            return bodyResponse;

        } catch (Exception e) {
            throw new CloudBeesException("Failed to get Jenkins job test report for '" + url + "'. "
                    + (errMsg.length() > 0 ? " (" + errMsg + ")" : "") + "Request string: " + reqStr
                    + " - Response: " + Utils.readString(bodyResponse), e);
        }
    }

    public void createJenkinsJob(final String jobName, final String configXML, final IProgressMonitor monitor)
            throws CloudBeesException {
        try {
            monitor.setTaskName("Preparing new job request");

            String encodedJobName = URLEncoder.encode(jobName, "UTF-8");
            String url = this.jenkins.url.endsWith("/") ? this.jenkins.url : this.jenkins.url + "/";

            String reqUrl = url + "createItem?name=" + encodedJobName;

            HttpPost post = new HttpPost(reqUrl);
            StringEntity strEntity = new StringEntity(configXML, "application/xml", "UTF-8");
            post.setEntity(strEntity);

            DefaultHttpClient httpClient = Utils.getAPIClient(reqUrl);

            monitor.setTaskName("Creating new Jenkins job...");

            retrieveWithLogin(httpClient, post, null, false, new SubProgressMonitor(monitor, 10));

        } catch (Exception e) {
            throw new CloudBeesException("Failed to create new Jenkins job", e);
        }
    }

    public void deleteJenkinsJob(final String joburl, final IProgressMonitor monitor) throws CloudBeesException {
        try {
            monitor.setTaskName("Preparing delete request");

            String url = joburl.endsWith("/") ? joburl : joburl + "/";

            String reqUrl = url + "doDelete";

            HttpPost post = new HttpPost(reqUrl);

            post.setEntity(new StringEntity(""));

            DefaultHttpClient httpClient = Utils.getAPIClient(reqUrl);

            monitor.setTaskName("Deleting Jenkins job...");

            retrieveWithLogin(httpClient, post, null, true, new SubProgressMonitor(monitor, 10));

        } catch (Exception e) {
            throw new CloudBeesException("Failed to delete Jenkins job", e);
        }
    }

    public JenkinsConsoleLogResponse getBuildLog(final JenkinsConsoleLogResponse request,
            final IProgressMonitor monitor) throws CloudBeesException {
        monitor.setTaskName("Fetching Job build log...");

        String url = request.viewUrl;
        assertCorrectUrl(url);

        StringBuffer errMsg = new StringBuffer();

        String reqUrl = url;

        if (!reqUrl.endsWith("/")) {
            reqUrl = reqUrl + "/";
        }

        String reqStr = reqUrl + "logText/progressiveText?start=" + request.start;

        try {
            DefaultHttpClient httpclient = Utils.getAPIClient(reqStr);

            HttpPost post = new HttpPost(reqStr);
            if (request.annotator != null) {
                post.setHeader("X-ConsoleAnnotator", request.annotator);
            }

            HttpResponse response = (HttpResponse) retrieveWithLogin(httpclient, post, null, false,
                    new SubProgressMonitor(monitor, 10), ResponseType.HTTP);

            //      for (Header head : response.getAllHeaders()) {
            //        System.out.println("header: " + head);
            //      }

            request.logPart = response.getEntity().getContent();
            try {
                Header moreData = response.getLastHeader("X-More-Data");
                request.hasMore = moreData != null ? "true".equalsIgnoreCase(moreData.getValue()) : false;
                Header textSize = response.getLastHeader("X-Text-Size");
                request.start = textSize != null ? Long.parseLong(textSize.getValue()) : Long.MAX_VALUE;
                Header annotator = response.getLastHeader("X-ConsoleAnnotator");
                if (annotator != null) {
                    request.annotator = annotator.getValue();
                }
            } catch (Exception e) {
                e.printStackTrace();
                request.hasMore = false;
            }

            return request;
        } catch (Exception e) {
            throw new CloudBeesException("Failed to get Jenkins build log for '" + url + "'. "
                    + (errMsg.length() > 0 && errMsg.length() < 1000 ? " (" + errMsg + ")" : "") + "Request string:"
                    + reqStr, e);
        }
    }

    public InputStream getArtifact(final String url, final IProgressMonitor monitor) throws CloudBeesException {
        monitor.setTaskName("Fetching Jenkins artifact...");

        assertCorrectUrl(url);

        StringBuffer errMsg = new StringBuffer();

        InputStream bodyResponse = null;
        try {
            DefaultHttpClient httpclient = Utils.getAPIClient(url);

            HttpGet post = new HttpGet(url);

            bodyResponse = (InputStream) retrieveWithLogin(httpclient, post, null, false,
                    new SubProgressMonitor(monitor, 10), ResponseType.STREAM);

            return bodyResponse;

        } catch (Exception e) {
            String readString = Utils.readString(bodyResponse);
            if (readString != null && readString.length() > 100) {
                readString = readString.substring(0, 100);
            }
            throw new CloudBeesException("Failed to get Jenkins artifact '" + url + "'. "
                    + (errMsg.length() > 0 ? " (" + errMsg + ")" : "") + " - Response: " + readString, e);
        }
    }

}