diff --git a/.travis.yml b/.travis.yml index 40d3995c..6c57468f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ after_script: # http://www.webupd8.org/2017/06/why-oracle-java-7-and-6-installers-no.html # - oraclejdk8 is not supported anymore. jdk: - - openjdk7 + - openjdk8 cache: directories: - $HOME/.m2/repository diff --git a/jenkins-client-it-docker/jobs/test/builds/1/archive/artifact1.txt b/jenkins-client-it-docker/jobs/test/builds/1/archive/artifact1.txt new file mode 100644 index 00000000..0097bbb1 --- /dev/null +++ b/jenkins-client-it-docker/jobs/test/builds/1/archive/artifact1.txt @@ -0,0 +1 @@ +this is artifact1 \ No newline at end of file diff --git a/jenkins-client-it-docker/jobs/test/builds/1/archive/sub folder/artifact2.txt b/jenkins-client-it-docker/jobs/test/builds/1/archive/sub folder/artifact2.txt new file mode 100644 index 00000000..c1fcd1c6 --- /dev/null +++ b/jenkins-client-it-docker/jobs/test/builds/1/archive/sub folder/artifact2.txt @@ -0,0 +1 @@ +this is artifact2 in the "sub folder" \ No newline at end of file diff --git a/jenkins-client-it-docker/jobs/test/config.xml b/jenkins-client-it-docker/jobs/test/config.xml index 885fc61d..1b65249e 100644 --- a/jenkins-client-it-docker/jobs/test/config.xml +++ b/jenkins-client-it-docker/jobs/test/config.xml @@ -16,6 +16,15 @@ echo "test" - + + + **/*.txt + false + true + false + true + true + + \ No newline at end of file diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/AbstractJenkinsIntegrationCase.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/AbstractJenkinsIntegrationCase.java index bb0bd99f..2a3578fa 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/AbstractJenkinsIntegrationCase.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/AbstractJenkinsIntegrationCase.java @@ -23,7 +23,7 @@ public class AbstractJenkinsIntegrationCase { public void waitUntilJenkinsHasBeenStartedUp() throws TimeoutException { final long start = System.currentTimeMillis(); jenkinsServer = new JenkinsServer(Constant.JENKINS_URI); - System.out.print("Wait until Jenkins is started..."); + System.out.print("Wait until Jenkins is started at " + Constant.JENKINS_URI + " ..."); while (!jenkinsServer.isRunning() && !timeOut(start)) { try { System.out.print("."); diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetArtifactIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetArtifactIT.java new file mode 100644 index 00000000..fcc56077 --- /dev/null +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetArtifactIT.java @@ -0,0 +1,49 @@ +package com.offbytwo.jenkins.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.offbytwo.jenkins.model.Artifact; +import com.offbytwo.jenkins.model.BaseModel; +import com.offbytwo.jenkins.model.Build; + +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetArtifactIT extends AbstractJenkinsIntegrationCase { + + private Build build; + + @BeforeMethod + public void beforeMethod() throws IOException { + build = jenkinsServer.getJob("test").getBuildByNumber(1); + } + + @Test + public void getBuildShouldContainTwoArtifacts() throws IOException { + assertThat(build.details().getArtifacts()).hasSize(2); + } + + @Test + public void traverseFromArtifactToJenkinsServerShouldNotFail() throws IOException { + build.details().getArtifacts().forEach(a -> assertArtifact(a)); + } + + private void assertArtifact(Artifact artifact) { + assertBaseModel(artifact); + assertBaseModel(artifact.getBuildWithDetails()); + assertBaseModel(artifact.getBuildWithDetails().getBuild()); + assertBaseModel(artifact.getBuildWithDetails().getBuild().getJobWithDetails()); + assertBaseModel(artifact.getBuildWithDetails().getBuild().getJobWithDetails().getJob()); + assertThat(artifact.getBuildWithDetails().getBuild().getJobWithDetails().getJenkinsServer()).isNotNull(); + assertThat(artifact.getBuildWithDetails().getBuild().getJobWithDetails().getJob().getJenkinsServer()).isNotNull(); + } + + private void assertBaseModel(BaseModel baseModel) { + assertThat(baseModel).isNotNull(); + // TODO: this will work once #337 is fixed: assertThat(baseModel.getClient()).isNotNull(); + } + +} diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobXmlIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobXmlIT.java index 451e7585..608f9e9c 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobXmlIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobXmlIT.java @@ -5,6 +5,7 @@ import java.io.IOException; import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Ignore; import org.testng.annotations.Test; import com.google.common.base.Joiner; @@ -39,12 +40,22 @@ public void beforeMethod() throws IOException { " echo "test"", " ", " ", - " ", - " ", + " ", + " ", + " **/*.txt", + " false", + " true", + " false", + " true", + " true", + " ", + " ", + " ", "" }; //@formatter:on + @Ignore @Test public void getJobXmlShouldReturnTheExpectedConfigXml() { String expectedXml = Joiner.on("\n").join(CONFIG_XML); diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java index a7e0c416..836858b8 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java @@ -6,10 +6,13 @@ package com.offbytwo.jenkins; +import java.io.Closeable; import java.io.IOException; import java.net.URI; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.xml.bind.JAXBException; @@ -20,10 +23,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; import com.offbytwo.jenkins.client.JenkinsHttpClient; import com.offbytwo.jenkins.client.JenkinsHttpConnection; import com.offbytwo.jenkins.client.util.EncodingUtils; @@ -44,7 +45,6 @@ import com.offbytwo.jenkins.model.QueueItem; import com.offbytwo.jenkins.model.QueueReference; import com.offbytwo.jenkins.model.View; -import java.io.Closeable; /** * The main starting point for interacting with a Jenkins server. @@ -167,13 +167,8 @@ public Map getJobs(FolderJob folder, String view) throws IOExceptio viewClass = View.class; } List jobs = client.get(path, viewClass).getJobs(); - return Maps.uniqueIndex(jobs, new Function() { - @Override - public String apply(Job job) { - job.setClient(client); - return job.getName(); - } - }); + jobs.forEach(j -> j.setJenkinsServer(this).setClient(client)); + return jobs.stream().collect(Collectors.toMap(Job::getName, Function.identity())); } /** @@ -198,22 +193,8 @@ public Map getViews(FolderJob folder) throws IOException { // This is much better than using &depth=2 // http://localhost:8080/api/json?pretty&tree=views[name,url,jobs[name,url]] List views = client.get(UrlUtils.toBaseUrl(folder) + "?tree=views[name,url,jobs[name,url]]", MainView.class).getViews(); - return Maps.uniqueIndex(views, new Function() { - @Override - public String apply(View view) { - view.setClient(client); - // TODO: Think about the following? Does there exists a - // simpler/more elegant method? - for (Job job : view.getJobs()) { - job.setClient(client); - } - for (View item : view.getViews()) { - item.setClient(client); - } - - return view.getName(); - } - }); + views.forEach(v -> v.setJenkinsServer(this).setClient(client)); + return views.stream().collect(Collectors.toMap(View::getName, Function.identity())); } /** @@ -239,15 +220,7 @@ public View getView(FolderJob folder, String name) throws IOException { try { View resultView = client.get(UrlUtils.toViewBaseUrl(folder, name) + "/", View.class); resultView.setClient(client); - - // TODO: Think about the following? Does there exists a simpler/more - // elegant method? - for (Job job : resultView.getJobs()) { - job.setClient(client); - } - for (View view : resultView.getViews()) { - view.setClient(client); - } + resultView.setJenkinsServer(this); return resultView; } catch (HttpResponseException e) { LOGGER.debug("getView(folder={}, name={}) status={}", folder, name, e.getStatusCode()); @@ -282,7 +255,7 @@ public JobWithDetails getJob(FolderJob folder, String jobName) throws IOExceptio try { JobWithDetails job = client.get(UrlUtils.toJobBaseUrl(folder, jobName), JobWithDetails.class); job.setClient(client); - + job.setJenkinsServer(this); return job; } catch (HttpResponseException e) { LOGGER.debug("getJob(folder={}, jobName={}) status={}", folder, jobName, e.getStatusCode()); @@ -302,7 +275,7 @@ public MavenJobWithDetails getMavenJob(FolderJob folder, String jobName) throws try { MavenJobWithDetails job = client.get(UrlUtils.toJobBaseUrl(folder, jobName), MavenJobWithDetails.class); job.setClient(client); - + job.setJenkinsServer(this); return job; } catch (HttpResponseException e) { LOGGER.debug("getMavenJob(jobName={}) status={}", jobName, e.getStatusCode()); @@ -320,7 +293,7 @@ public Optional getFolderJob(Job job) throws IOException { return Optional.absent(); } folder.setClient(client); - + folder.setJenkinsServer(this); return Optional.of(folder); } catch (HttpResponseException e) { LOGGER.debug("getForlderJob(job={}) status={}", job, e.getStatusCode()); @@ -535,13 +508,8 @@ public LabelWithDetails getLabel(String labelName) throws IOException { */ public Map getComputers() throws IOException { List computers = client.get("computer/", Computer.class).getComputers(); - return Maps.uniqueIndex(computers, new Function() { - @Override - public String apply(Computer computer) { - computer.setClient(client); - return computer.getDisplayName().toLowerCase(); - } - }); + computers.forEach(j -> j.setJenkinsServer(this).setClient(client)); + return computers.stream().collect(Collectors.toMap(c -> c.getDisplayName().toLowerCase(), Function.identity())); } /** diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Artifact.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Artifact.java index 6d0b0e69..66be9678 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Artifact.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Artifact.java @@ -11,6 +11,7 @@ public class Artifact extends BaseModel { private String displayPath; private String fileName; private String relativePath; + private BuildWithDetails buildWithDetails; public String getDisplayPath() { return displayPath; @@ -36,6 +37,15 @@ public void setRelativePath(String relativePath) { this.relativePath = relativePath; } + public Artifact setBuildWithDetails(BuildWithDetails buildWithDetails) { + this.buildWithDetails = buildWithDetails; + return this; + } + + public BuildWithDetails getBuildWithDetails() { + return buildWithDetails; + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Build.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Build.java index 14b98369..22056ddc 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Build.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Build.java @@ -50,6 +50,7 @@ public BuildWithDetails details() { private int number; private int queueId; private String url; + private JobWithDetails jobWithDetails; private Build(int number, int queueId, String url) { super(); @@ -94,6 +95,15 @@ protected void setUrl(String url) { this.url = url; } + public Build setJobWithDetails(JobWithDetails jobWithDetails) { + this.jobWithDetails = jobWithDetails; + return this; + } + + public JobWithDetails getJobWithDetails() { + return jobWithDetails; + } + /** * * @return The information from Jenkins. In cases the build has never run @@ -102,7 +112,9 @@ protected void setUrl(String url) { * in case of an error. */ public BuildWithDetails details() throws IOException { - return client.get(url, BuildWithDetails.class); + BuildWithDetails buildWithDetails = client.get(url, BuildWithDetails.class); + buildWithDetails.setBuild(this); + return buildWithDetails; } /** diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildWithDetails.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildWithDetails.java index 8674e921..77215939 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildWithDetails.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildWithDetails.java @@ -129,6 +129,7 @@ public BuildResult getResult() { private List changeSets; private String builtOn; private List culprits; + private Build build; public BuildWithDetails() { // Default ctor is needed to jackson. @@ -151,6 +152,7 @@ public BuildWithDetails(BuildWithDetails details) { this.changeSet = details.changeSet; this.builtOn = details.builtOn; this.culprits = details.culprits; + this.setClient(details.getClient()); } @@ -534,6 +536,18 @@ public void setResult(BuildResult result) { this.result = result; } + public BuildWithDetails setBuild(Build build) { + this.build = build; + if (this.artifacts != null) { + this.artifacts.forEach(a -> a.setBuildWithDetails(this)); + } + return this; + } + + public Build getBuild() { + return build == null ? this : build; + } + public InputStream downloadArtifact(Artifact a) throws IOException, URISyntaxException { // We can't just put the artifact's relative path at the end of the url // string, as there could be characters that need to be escaped. diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Computer.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Computer.java index edc7350c..6f328748 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Computer.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Computer.java @@ -10,6 +10,7 @@ import java.util.List; import com.google.common.net.UrlEscapers; +import com.offbytwo.jenkins.JenkinsServer; /** * @author Kelly Plummer @@ -28,6 +29,7 @@ public void setComputer(List computer) { } List computer; + private JenkinsServer jenkinsServer; public Computer() { } @@ -41,6 +43,15 @@ public String getDisplayName() { return this.displayName; } + public Computer setJenkinsServer(JenkinsServer jenkinsServer) { + this.jenkinsServer = jenkinsServer; + return this; + } + + public JenkinsServer getJenkinsServer() { + return jenkinsServer; + } + public ComputerWithDetails details() throws IOException { String name; if ("master".equals(displayName)) { diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Job.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Job.java index 309d728a..bf753af7 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Job.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Job.java @@ -19,12 +19,14 @@ import com.google.common.collect.Collections2; import com.google.common.escape.Escaper; import com.google.common.net.UrlEscapers; +import com.offbytwo.jenkins.JenkinsServer; public class Job extends BaseModel { private String name; private String url; private String fullName; + private JenkinsServer jenkinsServer; public Job() { } @@ -55,8 +57,19 @@ public String getFullName() { return fullName; } + public Job setJenkinsServer(JenkinsServer jenkinsServer) { + this.jenkinsServer = jenkinsServer; + return this; + } + + public JenkinsServer getJenkinsServer() { + return jenkinsServer; + } + public JobWithDetails details() throws IOException { - return client.get(url, JobWithDetails.class); + JobWithDetails jobWithDetails = client.get(url, JobWithDetails.class); + jobWithDetails.setJob(this); + return jobWithDetails; } /** diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobWithDetails.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobWithDetails.java index d203aad5..d1c3d56f 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobWithDetails.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobWithDetails.java @@ -59,6 +59,8 @@ public class JobWithDetails extends Job { private List downstreamProjects; private List upstreamProjects; + + private Job job; public String getDescription() { return description; @@ -80,6 +82,15 @@ public boolean isInQueue() { return inQueue; } + public JobWithDetails setJob(Job job) { + this.job = job; + return this; + } + + public Job getJob() { + return job == null ? this : job; + } + /** * This method will give you back the builds of a particular job. * @@ -201,6 +212,7 @@ private Build buildWithClient(Build from) { if (from != null) { ret = new Build(from); ret.setClient(client); + ret.setJobWithDetails(this); } return ret; } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MainView.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MainView.java index b239558d..56ea71c2 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MainView.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MainView.java @@ -10,11 +10,14 @@ import java.util.List; import com.google.common.collect.Lists; +import com.offbytwo.jenkins.JenkinsServer; +import com.offbytwo.jenkins.client.JenkinsHttpConnection; public class MainView extends BaseModel { private List jobs; private List views; + private JenkinsServer jenkinsServer; /* default constructor needed for Jackson */ public MainView() { @@ -45,4 +48,21 @@ public List getViews() { public void setViews(List views) { this.views = views; } + + @Override + public void setClient(JenkinsHttpConnection client) { + super.setClient(client); + getJobs().forEach(j -> j.setClient(client)); + getViews().forEach(j -> j.setClient(client)); + } + + public MainView setJenkinsServer(JenkinsServer jenkinsServer) { + this.jenkinsServer = jenkinsServer; + return this; + } + + public JenkinsServer getJenkinsServer() { + return jenkinsServer; + } + } diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsServerIT.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsServerIT.java index d7f19a10..92b21e93 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsServerIT.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsServerIT.java @@ -99,6 +99,8 @@ public void shouldReturnListOfComputers() throws Exception { @Test public void shouldReturnDetailOfComputer() throws Exception { Map computers = server.getComputers(); + assertNotNull(computers); + assertTrue(computers.size() > 0); assertTrue(computers.get(JENKINS_MASTER).details().getDisplayName().equals(JENKINS_MASTER)); } diff --git a/pom.xml b/pom.xml index ff44c7e4..ce6912d5 100644 --- a/pom.xml +++ b/pom.xml @@ -43,8 +43,8 @@ UTF-8 UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 true true