Java tutorial
/** * Copyright 2016-2017 Red Hat, Inc, and individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.aerogear.digger.client.services; import com.offbytwo.jenkins.JenkinsServer; import com.offbytwo.jenkins.model.*; import org.aerogear.digger.client.DiggerClient; import org.aerogear.digger.client.model.BuildTriggerStatus; import org.aerogear.digger.client.model.LogStreamingOptions; import org.aerogear.digger.client.util.DiggerClientException; import org.apache.commons.collections4.MapUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Provides functionality to trigger a build. **/ public class BuildService { private static final Logger LOG = LoggerFactory.getLogger(BuildService.class); /** * Default value of {@link #firstCheckDelay} */ public static final long DEFAULT_FIRST_CHECK_DELAY = 5 * 1000L; /** * Default value of {@link #pollPeriod} */ public static final long DEFAULT_POLL_PERIOD = 2 * 1000L; private long firstCheckDelay; private long pollPeriod; /** * @param firstCheckDelay how long should we wait (in milliseconds) before we start checking the queue item status * @param pollPeriod how long should we wait (in milliseconds) before checking the queue item status for next time */ public BuildService(long firstCheckDelay, long pollPeriod) { this.firstCheckDelay = firstCheckDelay; this.pollPeriod = pollPeriod; } /** * Get build logs for specific job and build number * * @param jobName name of the job * @param buildNumber job build number * @return String with file contents that can be saved or piped to socket * @throws DiggerClientException when problem with fetching artifacts from jenkins */ public String getBuildLogs(JenkinsServer jenkins, String jobName, int buildNumber) throws DiggerClientException, IOException { Build build = this.getBuild(jenkins, jobName, buildNumber); BuildWithDetails buildWithDetails = build.details(); return buildWithDetails.getConsoleOutputText(); } /** * Returns the build history for a job. As reported by {@link JobWithDetails#getBuilds()} it will return max 100 most-recent builds. * <p> * Please note that this approach will take some time since we first fetch the builds in 1 call, then fetch build details in 1 call per build. * * @param jenkins Jenkins server client * @param jobName name of the job * @return the build history * @throws DiggerClientException if connection problems occur */ public List<BuildWithDetails> getBuildHistory(JenkinsServer jenkins, String jobName) throws DiggerClientException { final JobWithDetails job; try { job = jenkins.getJob(jobName); if (job == null) { LOG.error("Cannot fetch job from jenkins {0}", jobName); throw new DiggerClientException("Cannot fetch job from jenkins"); } final List<Build> builds = job.getBuilds(); final List<BuildWithDetails> returnValue = new ArrayList<BuildWithDetails>(); for (Build build : builds) { try { returnValue.add(build.details()); } catch (IOException e) { LOG.error("Error while fetching the details for job {} with build number", jobName, build.getNumber(), e); // re-raise it so the the outer block catches it and creates a DiggerClientException out of it throw e; } } return returnValue; } catch (IOException e) { LOG.error("Problem when getting the build history for job {0}", jobName, e); throw new DiggerClientException(e); } } /** * * @param jenkinsServer Jenkins server client * @param jobName name of the job * @param params build parameters to override defaults in the job * @return The QueueReference * @throws IOException if connection problems occur during connecting to Jenkins * @throws InterruptedException if a problem occurs during sleeping between checks */ public BuildTriggerStatus triggerBuild(JenkinsServer jenkinsServer, String jobName, Map<String, String> params) throws IOException, InterruptedException { LOG.debug("Getting QueueReference for Job '{}'", jobName); QueueReference queueReference = null; JobWithDetails job = jenkinsServer.getJob(jobName); if (job == null) { LOG.debug("Unable to find job for name '{}'", jobName); throw new IllegalArgumentException("Unable to find job for name '" + jobName + "'"); } else { queueReference = MapUtils.isEmpty(params) ? job.build() : job.build(params); if (queueReference == null) { // this is probably an implementation problem we have here LOG.debug("Queue reference cannot be null!"); throw new IllegalStateException("Queue reference cannot be null!"); } LOG.debug("Queue item reference: {}", queueReference.getQueueItemUrlPart()); return new BuildTriggerStatus(BuildTriggerStatus.State.TRIGGERED, job.getNextBuildNumber(), queueReference); } } /** * See the documentation in {@link DiggerClient#build(String, long, Map)} * * @param jenkinsServer Jenkins server client * @param jobName name of the job * @param timeout timeout * @param params build parameters to override defaults in the job * @return the build status * @throws IOException if connection problems occur during connecting to Jenkins * @throws InterruptedException if a problem occurs during sleeping between checks * @see DiggerClient#build(String, long, Map) */ public BuildTriggerStatus pollBuild(JenkinsServer jenkinsServer, String jobName, QueueReference queueReference, long timeout, Map<String, String> params) throws IOException, InterruptedException { final long whenToTimeout = System.currentTimeMillis() + timeout; LOG.debug("Going to build job with name: {}", jobName); LOG.debug("Going to timeout in {} msecs if build didn't start executing", timeout); LOG.debug("Build triggered; queue item reference: {}", queueReference.getQueueItemUrlPart()); // wait for N seconds, then fetch the queue item. // do it until we have an executable. // we would have an executable when the build leaves queue and starts building. LOG.debug("Going to sleep {} msecs", firstCheckDelay); Thread.sleep(firstCheckDelay); QueueItem queueItem; while (true) { queueItem = jenkinsServer.getQueueItem(queueReference); LOG.debug("Queue item : {}", queueItem); if (queueItem == null) { // this is probably an implementation problem we have here LOG.debug("Queue item cannot be null!"); throw new IllegalStateException("Queue item cannot be null!"); } LOG.debug("Build item cancelled:{}, blocked:{}, buildable:{}, stuck:{}", queueItem.isCancelled(), queueItem.isBlocked(), queueItem.isBuildable(), queueItem.isStuck()); if (queueItem.isCancelled()) { LOG.debug("Queue item is cancelled. Returning CANCELLED_IN_QUEUE"); return new BuildTriggerStatus(BuildTriggerStatus.State.CANCELLED_IN_QUEUE, -1, queueReference); } else if (queueItem.isStuck()) { LOG.debug("Queue item is stuck. Returning STUCK_IN_QUEUE"); return new BuildTriggerStatus(BuildTriggerStatus.State.STUCK_IN_QUEUE, -1, queueReference); } // do not return -1 if blocked. // we will wait until it is unblocked. final Executable executable = queueItem.getExecutable(); if (executable != null) { LOG.debug("Build has an executable. Returning build number: {}", executable.getNumber()); return new BuildTriggerStatus(BuildTriggerStatus.State.STARTED_BUILDING, executable.getNumber().intValue(), queueReference); } else { LOG.debug("Build did not start executing yet."); if (whenToTimeout > System.currentTimeMillis()) { LOG.debug("Timeout period has not exceeded yet. Sleeping for {} msecs", pollPeriod); Thread.sleep(pollPeriod); } else { LOG.debug("Timeout period has exceeded. Returning TIMED_OUT."); return new BuildTriggerStatus(BuildTriggerStatus.State.TIMED_OUT, -1, queueReference); } } } } /** * See the documentation in {@link DiggerClient#build(String, long)} * * @param jenkinsServer Jenkins server client * @param jobName name of the job * @param timeout timeout * @return the build status * @throws IOException if connection problems occur during connecting to Jenkins * @throws InterruptedException if a problem occurs during sleeping between checks * @see DiggerClient#build(String, long) */ public BuildTriggerStatus build(JenkinsServer jenkinsServer, String jobName, long timeout) throws IOException, InterruptedException { BuildTriggerStatus buildTriggerStatus = this.triggerBuild(jenkinsServer, jobName, null); return this.pollBuild(jenkinsServer, jobName, buildTriggerStatus.getQueueReference(), timeout, null); } /** * Retrieve a build from a specific job * * @param jenkins the jenkins instance * @param jobName the name of the job * @param buildNumber the build number * @return {@link Build} * @throws DiggerClientException */ public Build getBuild(JenkinsServer jenkins, String jobName, int buildNumber) throws DiggerClientException { try { JobWithDetails job = jenkins.getJob(jobName); if (job == null) { LOG.error("Cannot fetch job from jenkins {0}", jobName); throw new DiggerClientException("Cannot fetch job from jenkins"); } Build build = job.getBuildByNumber(buildNumber); return build; } catch (IOException e) { LOG.error("Problem when fetching logs for {0} {1}", jobName, buildNumber, e); throw new DiggerClientException(e); } } /** * Get the build details of a job * * @param jenkins the jenkins instance * @param jobName the name of the job * @param buildNumber the build number * @return {@link BuildWithDetails} * @throws DiggerClientException */ public BuildWithDetails getBuildDetails(JenkinsServer jenkins, String jobName, int buildNumber) throws DiggerClientException, IOException { Build build = this.getBuild(jenkins, jobName, buildNumber); BuildWithDetails buildWithDetails = build.details(); return buildWithDetails; } /** * Cancel a build * @param jenkins the jenkins instance * @param jobName the name of the job * @param buildNumber the build number * @return {@link BuildWithDetails} * @throws DiggerClientException * @throws IOException */ public BuildWithDetails cancelBuild(JenkinsServer jenkins, String jobName, int buildNumber) throws DiggerClientException, IOException { Build build = this.getBuild(jenkins, jobName, buildNumber); build.Stop(); BuildWithDetails buildWithDetails = build.details(); return buildWithDetails; } /** * Start streaming the logs of the given build. * See {@link DiggerClient#streamLogs(String, int, LogStreamingOptions)} */ public void streamBuildLogs(JenkinsServer jenkins, String jobName, int buildNumber, LogStreamingOptions options) throws DiggerClientException, IOException, InterruptedException { BuildWithDetails buildDetails = this.getBuildDetails(jenkins, jobName, buildNumber); buildDetails.streamConsoleOutput(options.getStreamListener(), options.getPollingInterval(), options.getPollingTimeout()); } }