Java tutorial
/* * * 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; } } }