com.serena.rlc.provider.jenkins.JenkinsExecutionProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.serena.rlc.provider.jenkins.JenkinsExecutionProvider.java

Source

/*
 *
 * Copyright (c) 2015 SERENA Software, Inc. All Rights Reserved.
 *
 * This software is proprietary information of SERENA Software, Inc.
 * Use is subject to license terms.
 *
 * @author Ryan Cook
 */
package com.serena.rlc.provider.jenkins;

import com.serena.rlc.provider.BaseExecutionProvider;

import com.serena.rlc.provider.annotations.*;
import static com.serena.rlc.provider.data.model.IConstants.EXTENDED_FIELDS;
import com.serena.rlc.provider.domain.*;
import com.serena.rlc.provider.exceptions.ProviderException;
import com.serena.rlc.provider.jenkins.client.JenkinsClient;
import com.serena.rlc.provider.jenkins.domain.BuildInfo;
import com.serena.rlc.provider.jenkins.domain.Job;
import com.serena.rlc.provider.jenkins.domain.JobParameter;
import com.serena.rlc.provider.jenkins.exception.JenkinsClientException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.UUID;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;

public class JenkinsExecutionProvider extends BaseExecutionProvider {

    final static Logger logger = LoggerFactory.getLogger(JenkinsExecutionProvider.class);

    final static String BUILDJOB_ACTION = "buildJob";
    final static String BUILD_JOB_PARAM = "buildJobParam";

    //================================================================================
    // Configuration Properties
    // -------------------------------------------------------------------------------
    // The configuration properties are marked with the @ConfigProperty annotaion
    // and will be displayed in the provider administration page when creating a 
    // configuration of this plugin for use.
    //================================================================================
    @ConfigProperty(name = "execution_provider_name", displayName = "Execution Provider Name", description = "provider name", defaultValue = "Jenkins Provider", dataType = DataType.TEXT)
    private String providerName;

    @ConfigProperty(name = "execution_provider_description", displayName = "Execution Provider Description", description = "provider description", defaultValue = "", dataType = DataType.TEXT)
    private String providerDescription;

    @ConfigProperty(name = "jenkins_url", displayName = "Jenkins URL", description = "Jenkins Server URL.", defaultValue = "http://<servername>:8080/jenkins/", dataType = DataType.TEXT)
    private String jenkinsUrl;

    @ConfigProperty(name = "jenkins_serviceuser", displayName = "User Name", description = "Jenkins service username.", defaultValue = "", dataType = DataType.TEXT)
    private String serviceUser;

    @ConfigProperty(name = "jenkins_servicepassword", displayName = "Password", description = "Jenkins service password", defaultValue = "", dataType = DataType.PASSWORD)
    private String servicePassword;

    @ConfigProperty(name = "jenkins_jobfilter", displayName = "Job Filter", description = "Jenkins job filter. Used to filter the list of available jobs with a wildcard expression", defaultValue = "", dataType = DataType.TEXT)
    private String jobFilter;

    /*
     @ConfigProperty(name = "jenkins_job", displayName = "Job",
     description = "Jenkins build job",
     defaultValue = "",
     dataType = DataType.TEXT)
     private String jenkinsJob;
        
     @ConfigProperty(name = "jenkins_job_params", displayName = "Job Parameters",
     description = "Comma sepaerated list of job parameters",
     defaultValue = "param1,param2",
     dataType = DataType.TEXT)
     private String jenkinsJobParams;
     */
    @ConfigProperty(name = "execution_action_wait_for_callback", displayName = "Wait for Callback", description = "Set this to false to set the execution status to Completed when the action is executed and a build number is provided.", defaultValue = "false", dataType = DataType.TEXT)
    private String waitForCallback;

    @ConfigProperty(name = "jenkins_waitForBuildNumber", displayName = "Wait for Build Number", description = "If true the system will poll the queue until a build number can be retrieved. If false the system will have no knowledge of which build was started.", defaultValue = "true", dataType = DataType.TEXT)
    private String waitForBuildNumber;

    @ConfigProperty(name = "jenkins_pollSleepTime", displayName = "Poll Sleep Time", description = "Time in ms to sleep between polling the queue", defaultValue = "1000", dataType = DataType.TEXT)
    private String pollSleepTime;

    @ConfigProperty(name = "jenkins_maxPollCount", displayName = "Max Poll Count", description = "Maximum number of times that the queue will be checked for an available build number", defaultValue = "15", dataType = DataType.TEXT)
    private String maxPollCount;

    public String getPollSleepTime() {
        return pollSleepTime;
    }

    @Autowired(required = false)
    public void setPollSleepTime(String pollSleepTime) {
        this.pollSleepTime = pollSleepTime;
    }

    public String getMaxPollCount() {
        return maxPollCount;
    }

    @Autowired(required = false)
    public void setMaxPollCount(String maxPollCount) {
        this.maxPollCount = maxPollCount;
    }

    public String getWaitForBuildNumber() {
        return waitForBuildNumber;
    }

    @Autowired(required = false)
    public void setWaitForBuildNumber(String waitForBuildNumber) {
        this.waitForBuildNumber = waitForBuildNumber;
    }

    public String getJobFilter() {
        return jobFilter;
    }

    @Autowired(required = false)
    public void setJobFilter(String jobFilter) {
        this.jobFilter = jobFilter;
    }

    @Autowired(required = false)
    public void setWaitForCallback(String waitForCallback) {
        if (StringUtils.isNotEmpty(waitForCallback)) {
            this.waitForCallback = waitForCallback;
        }

    }

    public String getWaitForCallback() {
        return waitForCallback;
    }

    @Override
    public String getProviderName() {
        return this.providerName;
    }

    @Autowired(required = false)
    @Override
    public void setProviderName(String providerName) {
        if (StringUtils.isNotEmpty(providerName)) {
            providerName = providerName.trim();
        }

        this.providerName = providerName;
    }

    @Override
    public String getProviderDescription() {
        return this.providerDescription;
    }

    @Autowired(required = false)
    @Override
    public void setProviderDescription(String providerDescription) {
        if (StringUtils.isNotEmpty(providerDescription)) {
            providerDescription = providerDescription.trim();
        }

        this.providerDescription = providerDescription;
    }
    /*
     public String getJenkinsJob() {
     return jenkinsJob;
     }
        
     @Autowired(required = false)
     public void setJenkinsJob(String jenkinsJob) {
     if (!StringUtils.isEmpty(jenkinsJob)) {
     jenkinsJob = jenkinsJob.trim();
     }
        
     this.jenkinsJob = jenkinsJob;
     }
        
     public String getJenkinsJobParams() {
     return jenkinsJobParams;
     }
        
     @Autowired(required = false)
     public void setJenkinsJobParams(String jenkinsJobParams) {
     this.jenkinsJobParams = jenkinsJobParams;
     }
     */

    public String getJenkinsUrl() {
        return jenkinsUrl;
    }

    @Autowired(required = false)
    public void setJenkinsUrl(String jenkinsUrl) {
        if (StringUtils.isNotEmpty(jenkinsUrl)) {
            this.jenkinsUrl = jenkinsUrl.replaceAll("^\\s+", "");
        } else {
            this.jenkinsUrl = "http://localhost:8080/jenkins/";
        }
    }

    public String getServiceUser() {
        return serviceUser;
    }

    @Autowired(required = false)
    public void setServiceUser(String serviceUser) {
        if (!StringUtils.isEmpty(serviceUser)) {
            this.serviceUser = serviceUser.replaceAll("^\\s+", "");
        }
    }

    public String getServicePassword() {
        return servicePassword;
    }

    @Autowired(required = false)
    public void setServicePassword(String servicePassword) {
        if (!StringUtils.isEmpty(servicePassword)) {
            this.servicePassword = servicePassword.replaceAll("^\\s+", "");
        }
    }

    @Override
    @Service(name = EXECUTE, displayName = "Execute", description = "Execute Jenkins action.")
    @Params(params = {
            @Param(fieldName = ACTION, description = "Jenkins action to execute", required = true, dataType = DataType.SELECT),
            @Param(fieldName = PROPERTIES, description = "Jenkins action properties", required = true) })
    public ExecutionInfo execute(String action, String taskTitle, String taskDescription, List<Field> properties)
            throws ProviderException {
        return buildJob(taskTitle, taskDescription, properties);
    }

    @Service(name = VALIDATE, displayName = "Validate", description = "Validate")
    @Params(params = {
            @Param(fieldName = ACTION, displayName = "Action", description = "Action to validate", required = true, dataType = DataType.SELECT),
            @Param(fieldName = PROPERTIES, displayName = "Properties", description = "action properties", required = true) })
    @Override
    public ExecutionInfo validate(String action, String taskTitle, String taskDescription, List<Field> properties)
            throws ProviderException {
        return new ExecutionInfo("task is valid", true);

    }

    @Override
    public FieldInfo getFieldValues(String fieldName, List<Field> properties) throws ProviderException {
        if (fieldName.equalsIgnoreCase(BUILD_JOB_PARAM)) {
            return getJobFieldValues(BUILD_JOB_PARAM);
        } else if (fieldName.equalsIgnoreCase(EXTENDED_FIELDS)) {
            return getExtendedFieldValues(fieldName, properties);
        } else {
            return null;
        }
    }

    /*
     @Override
     public ActionInfoResult getActions()
     throws ProviderException {
        
     ArrayList<ActionInfo> actions = new ArrayList();
        
     ActionInfo action = AnnotationUtil.getActionInfo(this.getClass(), BUILDJOB_ACTION);
     action.setTitle(action.getDisplayName());
     ArrayList<Property> props = new ArrayList();
         
     String[] params = this.jenkinsJobParams.split(",");
        
     for(String s: params) {
     s = s.trim();
            
     Property p = new Property(s, s, true, false, true);
     props.add(p);
            
     }
        
     action.setProperties(props);
     actions.add(action);
        
     return new ActionInfoResult(0, actions.size(), actions.toArray(new ActionInfo[actions.size()]));
     }
     */
    /*
     @Override
     public ActionInfo getActionInfo(String action) throws ProviderException {
     ActionInfo ai = AnnotationUtil.getActionInfo(this.getClass(), BUILDJOB_ACTION);
     ai.setTitle(ai.getDisplayName());
     ArrayList<Property> props = new ArrayList();
         
     String[] params = this.jenkinsJobParams.split(",");
        
     for(String s: params) {
     s = s.trim();
            
     Property p = new Property(s, s, true, false, true);
     props.add(p);
            
     }
        
     ai.setProperties(props);
        
     return ai;
     }
     */
    @Action(name = BUILDJOB_ACTION, displayName = "Run Build Job", description = "Run a preconfigured Jenkins build job")
    @Params(params = {
            @Param(fieldName = BUILD_JOB_PARAM, displayName = "Job", description = "Jenkins build job", required = true, dataType = DataType.SELECT),
            @Param(fieldName = EXTENDED_FIELDS, displayName = "Extended Fields", description = "Jenkins job extended fields", required = false, environmentProperty = false, dataType = DataType.SELECT) })
    public ExecutionInfo buildJob(String taskTitle, String taskDescription, List<Field> properties)
            throws ProviderException {
        Field buildJobField = Field.getFieldByName(properties, BUILD_JOB_PARAM);
        if (buildJobField == null || StringUtils.isEmpty(buildJobField.getValue())) {
            throw new ProviderException("Missing required field: " + BUILD_JOB_PARAM);
        }

        String buildJob = buildJobField.getValue();

        // Create parameter string
        //String paramString = "{\"parameter\": [";
        // add taskExecutionId as default parameter
        UUID executionId = UUID.randomUUID();
        String paramString = "?RLC_EXECUTION_ID=" + executionId.toString();

        Field extendedFieldsField = Field.getFieldByName(properties, EXTENDED_FIELDS);
        if (extendedFieldsField != null) {
            List<Field> extendedFields = extendedFieldsField.getExtendedFields();

            int count = 0;

            for (Field extendedField : extendedFields) {
                /*if (count > 0) {
                paramString += ", ";
                }
                    
                paramString += "{\"name\":\"" + extendedField.getFieldName() + "\", \"value\":\"" + extendedField.getValue() + "\"}";
                */

                try {
                    paramString += "&" + extendedField.getFieldName() + "="
                            + URLEncoder.encode(extendedField.getValue(), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    logger.equals(e.getLocalizedMessage());
                }

                count++;
            }
        }

        //paramString += "]}";

        ExecutionInfo retVal = new ExecutionInfo();
        JenkinsClient client = new JenkinsClient(this.getJenkinsUrl(), this.getServiceUser(),
                this.getServicePassword());

        try {

            client.setPollSleepTime(tryParseInt(this.getPollSleepTime(), 1000));
            client.setMaxPollCount(tryParseInt(this.getMaxPollCount(), 15));

            BuildInfo result = client.startBuild(buildJob, paramString);
            Boolean statusSet = false;

            if (result.hasBuildNumber()) {
                retVal.setExecutionUrl(result.getBuildUrl());
                retVal.setMessage(String.format("Build Number: %d", result.getBuildNumber()));
                retVal.setExecutionId(executionId.toString());
            } else {
                retVal.setExecutionUrl(client.createUrl(this.getJenkinsUrl(), "job/" + buildJob));
                retVal.setExecutionId(executionId.toString());
                retVal.setMessage("Failed to start job and retrieve a build number. Message: " + result.getMessage()
                        + " - Queue: " + result.getQueueUrl());

                if (Boolean.parseBoolean(this.getWaitForBuildNumber())) {
                    retVal.setStatus(ExecutionStatus.FAILED);
                    statusSet = true;
                }
            }

            if (!statusSet) {
                if (Boolean.parseBoolean(getWaitForCallback())) {
                    retVal.setStatus(ExecutionStatus.PENDING);
                } else {
                    retVal.setStatus(ExecutionStatus.COMPLETED);
                }
            }

        } catch (JenkinsClientException ex) {
            logger.error(ex.getMessage(), ex);
            retVal.setStatus(ExecutionStatus.FAILED);
            retVal.setExecutionUrl(client.createUrl(this.getJenkinsUrl(), "job/" + buildJob));
            retVal.setMessage("Failed to start job and retrieve a build number");
        }

        return retVal;
    }

    private boolean matchesJobFilter(String jobName) {
        String pattern = getJobFilter();

        if (pattern == null || StringUtils.isEmpty(pattern)) {
            return true;
        } else {
            return jobName.matches(pattern.replace("?", ".?").replace("*", ".*?"));
        }
    }

    @Getter(name = BUILD_JOB_PARAM, displayName = "Job", description = "Jenkins build job")
    public FieldInfo getJobFieldValues(String fieldName) throws ProviderException {
        FieldInfo fieldInfo = new FieldInfo(fieldName);
        JenkinsClient client = new JenkinsClient(this.getJenkinsUrl(), this.getServiceUser(),
                this.getServicePassword());
        List<Job> jobs = new ArrayList<>();

        try {

            jobs = client.getJobs();

        } catch (JenkinsClientException ex) {
            logger.error(ex.getMessage(), ex);
            throw new ProviderException(ex);
        }

        if (jobs == null || jobs.size() < 1) {
            return null;
        }

        List<FieldValueInfo> values = new ArrayList<>();
        FieldValueInfo value;

        for (Job job : jobs) {
            if (matchesJobFilter(job.getName())) {
                value = new FieldValueInfo(job.getName(), job.getDisplayName());
                values.add(value);
            }
        }

        fieldInfo.setValues(values);
        return fieldInfo;
    }

    @Getter(name = EXTENDED_FIELDS, displayName = "Extended Fields", description = "Get Jenkins job field property values.")
    @Params(params = {
            @Param(fieldName = BUILD_JOB_PARAM, displayName = "Job", description = "Jenkins build job", required = true, dataType = DataType.SELECT) })
    public FieldInfo getExtendedFieldValues(String fieldName, List<Field> properties) throws ProviderException {
        if (properties == null || properties.size() < 1) {
            throw new ProviderException("Missing required field properties!");
        }

        Field field = Field.getFieldByName(properties, BUILD_JOB_PARAM);
        if (field == null || StringUtils.isEmpty(field.getValue())) {
            throw new ProviderException("Missing required field: " + BUILD_JOB_PARAM);
        }

        String buildJob = field.getValue();

        FieldInfo fieldInfo = new FieldInfo(fieldName);
        String result = "";

        //http://10.31.25.44:8080/jenkins/job/QLARIUS/api/json?tree=name,displayName,actions[parameterDefinitions[*]]
        String uri = jenkinsUrl;

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

            uri += "job/" + URLEncoder.encode(buildJob, "UTF-8").replace("+", "%20")
                    + "/api/json?tree=name,displayName,actions[parameterDefinitions[*,defaultParameterValue[*]]]";

            logger.debug("Start executing Jenkins GET request to url=\"{}\"", uri);

            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpGet getRequest = new HttpGet(uri);
            UsernamePasswordCredentials creds = new UsernamePasswordCredentials(getServiceUser(),
                    getServicePassword());
            getRequest.addHeader(BasicScheme.authenticate(creds, "US-ASCII", false));
            getRequest.addHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
            getRequest.addHeader(HttpHeaders.ACCEPT, "application/json");

            HttpResponse response = httpClient.execute(getRequest);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new ProviderException("HTTP Status Code: " + response.getStatusLine().getStatusCode());
            }

            BufferedReader br = new BufferedReader(new InputStreamReader((response.getEntity().getContent())));
            StringBuilder sb = new StringBuilder(1024);
            String output;
            while ((output = br.readLine()) != null) {
                sb.append(output);
            }
            result = sb.toString();
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new ProviderException(e);
        }

        List<JobParameter> jobParams = JobParameter.parse(result);

        /* if (jobParams == null || jobParams.size() < 1) {
         return null;
         }*/
        //FieldInfo fieldInfo = new FieldInfo(fieldName);
        List<FieldValueInfo> values = new ArrayList<>();
        FieldValueInfo value = new FieldValueInfo(EXTENDED_FIELDS, "Job Parameters");
        List<Field> fields = new ArrayList<>();
        Field paramField;

        if (jobParams != null && jobParams.size() > 0) {

            for (JobParameter param : jobParams) {
                if (param.getName().equals("RLC_EXECUTION_ID"))
                    continue;
                if ("StringParameterDefinition".equalsIgnoreCase(param.getType())) {
                    paramField = new Field(param.getName(), param.getName());
                    paramField.setType(DataType.TEXT);
                    paramField.setEditable(true);
                    paramField.setEnvironmentProperty(true);
                    paramField.setValue(param.getDefaultValue());
                    fields.add(paramField);
                }
                if ("TextParameterDefinition".equalsIgnoreCase(param.getType())) {
                    paramField = new Field(param.getName(), param.getName());
                    paramField.setType(DataType.TEXTAREA);
                    paramField.setEditable(true);
                    paramField.setEnvironmentProperty(true);
                    paramField.setValue(param.getDefaultValue());
                    fields.add(paramField);
                }
                if ("BooleanParameterDefinition".equalsIgnoreCase(param.getType())) {
                    paramField = new Field(param.getName(), param.getName());
                    paramField.setType(DataType.BOOLEAN);
                    paramField.setEditable(true);
                    paramField.setEnvironmentProperty(true);
                    paramField.setValue(param.getDefaultValue());
                    fields.add(paramField);
                }
                if ("PasswordParameterDefinition".equalsIgnoreCase(param.getType())) {
                    paramField = new Field(param.getName(), param.getName());
                    paramField.setType(DataType.PASSWORD);
                    paramField.setEditable(true);
                    paramField.setEnvironmentProperty(true);
                    paramField.setValue(param.getDefaultValue());
                    fields.add(paramField);
                }
            }

            value.setProperties(fields);
        }

        values.add(value);
        fieldInfo.setValues(values);

        return fieldInfo;
    }

    private int tryParseInt(String value, int defaultValue) {
        try {
            return Integer.parseInt(value, 10);
        } catch (NumberFormatException ex) {
            return defaultValue;
        }
    }

}