diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 2d3ad6d6..94243f29 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -2,6 +2,13 @@ ## Release 0.3.8 (NOT RELEASED YET) + + * [Fixed Issue 298][issue-298] + + Added Closeable support to JenkinsServer and JenkinsHttpClient so that + underlying resources can be cleaned up when instances no longer required + + * [JENKINS-46472](https://issues.jenkins-ci.org/browse/JENKINS-46472) Added ability to modify offline cause for offline computers. 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 0a6f79a4..6f707824 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java @@ -43,11 +43,12 @@ 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. */ -public class JenkinsServer { +public class JenkinsServer implements Closeable { private final Logger LOGGER = LoggerFactory.getLogger(getClass()); private final JenkinsHttpClient client; @@ -882,6 +883,18 @@ public void renameJob(FolderJob folder, String oldJobName, String newJobName, Bo + "/doRename?newName=" + EncodingUtils.encodeParam(newJobName), crumbFlag); } + + + + /** + * Closes underlying resources. + * Closed instances should no longer be used + * Closing an already closed instance has no side effects + */ + @Override + public void close() { + client.close(); + } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpClient.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpClient.java index 37414993..ce7428c8 100755 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpClient.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpClient.java @@ -49,11 +49,12 @@ import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; import com.offbytwo.jenkins.client.util.ResponseUtils; import com.offbytwo.jenkins.client.util.UrlUtils; +import java.io.Closeable; import static org.apache.commons.lang.StringUtils.isNotBlank; //import com.offbytwo.jenkins.client.util.HttpResponseContentExtractor; -public class JenkinsHttpClient { +public class JenkinsHttpClient implements Closeable { private final Logger LOGGER = LoggerFactory.getLogger(getClass()); private URI uri; @@ -498,7 +499,27 @@ public boolean isJenkinsVersionSet() { return !EMPTY_VERSION.equals( this.jenkinsVersion ); } + + + + /** + * Closes underlying resources. + * Any I/O errors whilst closing are logged with debug log level + * Closed instances should no longer be used + * Closing an already closed instance has no side effects + */ + @Override + public void close() { + try { + client.close(); + } catch (final IOException ex) { + LOGGER.debug("I/O exception closing client", ex); + } + } + + + private void releaseConnection(HttpRequestBase httpRequestBase) { httpRequestBase.releaseConnection(); } @@ -524,4 +545,6 @@ protected HttpContext getLocalContext() { protected void setLocalContext(HttpContext localContext) { this.localContext = localContext; } + + } diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsServerTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsServerTest.java index 444d6d3b..e9853b30 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsServerTest.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsServerTest.java @@ -32,6 +32,7 @@ import com.offbytwo.jenkins.model.JobWithDetails; import com.offbytwo.jenkins.model.MainView; import com.offbytwo.jenkins.model.View; +import java.net.URI; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -328,6 +329,20 @@ public void getVersionShouldNotFailWithNPE() } + + @Test(expected=IllegalStateException.class) + public void testClose() throws Exception { + final String uri = "http://localhost/jenkins"; + JenkinsServer srv = new JenkinsServer(new URI(uri)); + srv.close(); + srv.close(); //check multiple calls yield no errors + srv.getComputers(); + } + + + + + private void shouldGetFolderJobs(String... jobNames) throws IOException { // given String path = "http://localhost/jobs/someFolder/"; diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/client/JenkinsHttpClientTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/JenkinsHttpClientTest.java index a55e0560..b4a121d6 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/client/JenkinsHttpClientTest.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/JenkinsHttpClientTest.java @@ -51,6 +51,16 @@ public void testGet_String() throws Exception { final String s = jclient.get("job/someJob"); assertEquals("someJson", s); } + + + + @Test(expected=IllegalStateException.class) + public void testClose() throws Exception { + final JenkinsHttpClient jclient = new JenkinsHttpClient(new URI(URI)); + jclient.close(); + jclient.close(); //check multiple calls yield no errors + jclient.get("job/someJob"); + } } \ No newline at end of file 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 59100302..d7f19a10 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 @@ -183,4 +183,26 @@ public void testUpdateJob() throws Exception { String confirmXml = server.getJobXml(JENKINS_TEST_JOB); assertTrue(confirmXml.contains(description)); } + + + @Test(expected=IllegalStateException.class) + public void testClose_ReuseAfterClosed() throws Exception { + final FreeStyleProject proj = jenkinsRule.getInstance().createProject( + FreeStyleProject.class, JENKINS_TEST_JOB); + final Map jobs = server.getJobs(); + assertNotNull(jobs.get(proj.getName())); + server.close(); + server.getJobs(); + } + + + @Test + public void testClose_CloseMultipleTimes() throws Exception { + final FreeStyleProject proj = jenkinsRule.getInstance().createProject( + FreeStyleProject.class, JENKINS_TEST_JOB); + final Map jobs = server.getJobs(); + assertNotNull(jobs.get(proj.getName())); + server.close(); + server.close(); + } }