From 7a79a18d8f18d5c5b676a8a0a1010408675699ad Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 4 Jan 2021 01:30:59 -0800 Subject: [PATCH] Add functional interfaces --- .../java/org/kohsuke/github/GHRepository.java | 4 +- src/main/java/org/kohsuke/github/GitHub.java | 2 +- .../org/kohsuke/github/GitHubResponse.java | 16 +------ .../java/org/kohsuke/github/Requester.java | 45 ++++++++++++++++--- .../github/function/ConsumerThrows.java | 9 ++++ .../github/function/FunctionThrows.java | 9 ++++ .../github/function/InputStreamConsumer.java | 12 +++++ .../github/function/InputStreamFunction.java | 12 +++++ 8 files changed, 86 insertions(+), 23 deletions(-) create mode 100644 src/main/java/org/kohsuke/github/function/ConsumerThrows.java create mode 100644 src/main/java/org/kohsuke/github/function/FunctionThrows.java create mode 100644 src/main/java/org/kohsuke/github/function/InputStreamConsumer.java create mode 100644 src/main/java/org/kohsuke/github/function/InputStreamFunction.java diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 35541a990..be8a0b807 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -1783,7 +1783,7 @@ public class GHRepository extends GHObject { return root.createRequest() .withHeader("Accept", "application/vnd.github.v3.raw") .withUrlPath(target) - .fetchStream(); + .fetchStream(Requester::copyInputStream); } /** @@ -2810,7 +2810,7 @@ public class GHRepository extends GHObject { .with("mode", mode == null ? null : mode.toString()) .with("context", getFullName()) .withUrlPath("/markdown") - .fetchStream(), + .fetchStream(Requester::copyInputStream), "UTF-8"); } diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index 382d62926..9273fc4c2 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -1203,7 +1203,7 @@ public class GitHub { .with(new ByteArrayInputStream(text.getBytes("UTF-8"))) .contentType("text/plain;charset=UTF-8") .withUrlPath("/markdown/raw") - .fetchStream(), + .fetchStream(Requester::copyInputStream), "UTF-8"); } diff --git a/src/main/java/org/kohsuke/github/GitHubResponse.java b/src/main/java/org/kohsuke/github/GitHubResponse.java index 9e82eb80d..d86dcba19 100644 --- a/src/main/java/org/kohsuke/github/GitHubResponse.java +++ b/src/main/java/org/kohsuke/github/GitHubResponse.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.JsonMappingException; import org.apache.commons.io.IOUtils; +import org.kohsuke.github.function.FunctionThrows; import java.io.Closeable; import java.io.IOException; @@ -194,24 +195,11 @@ class GitHubResponse { /** * Represents a supplier of results that can throw. * - *

- * This is a functional interface whose functional method is - * {@link #apply(ResponseInfo)}. - * * @param * the type of results supplied by this supplier */ @FunctionalInterface - interface BodyHandler { - - /** - * Gets a result. - * - * @return a result - * @throws IOException - * if an I/O Exception occurs. - */ - T apply(ResponseInfo input) throws IOException; + interface BodyHandler extends FunctionThrows { } /** diff --git a/src/main/java/org/kohsuke/github/Requester.java b/src/main/java/org/kohsuke/github/Requester.java index 816632351..ed770f149 100644 --- a/src/main/java/org/kohsuke/github/Requester.java +++ b/src/main/java/org/kohsuke/github/Requester.java @@ -23,7 +23,10 @@ */ package org.kohsuke.github; +import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.commons.io.IOUtils; +import org.kohsuke.github.function.InputStreamConsumer; +import org.kohsuke.github.function.InputStreamFunction; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -106,15 +109,45 @@ class Requester extends GitHubRequest.Builder { * Response input stream. There are scenarios where direct stream reading is needed, however it is better to use * {@link #fetch(Class)} where possible. * - * @return the input stream * @throws IOException * the io exception */ - public InputStream fetchStream() throws IOException { - return client - .sendRequest(this, - (responseInfo) -> new ByteArrayInputStream(IOUtils.toByteArray(responseInfo.bodyStream()))) - .body(); + public void fetchStream(@Nonnull InputStreamConsumer consumer) throws IOException { + fetchStream((inputStream) -> { + consumer.accept(inputStream); + return null; + }); + } + + /** + * Response input stream. There are scenarios where direct stream reading is needed, however it is better to use + * {@link #fetch(Class)} where possible. + * + * @throws IOException + * the io exception + */ + public T fetchStream(@Nonnull InputStreamFunction handler) throws IOException { + return client.sendRequest(this, (responseInfo) -> handler.apply(responseInfo.bodyStream())).body(); + } + + /** + * Helper function to make it easy to pull streams. + * + * Copies an input stream to an in-memory input stream. The performance on this is not great but + * {@link GitHubResponse.ResponseInfo#bodyStream()} is closed at the end of every call to + * {@link GitHubClient#sendRequest(GitHubRequest, GitHubResponse.BodyHandler)}, so any reads to the original input + * stream must be completed before then. There are a number of deprecated methods that return {@link InputStream}. + * This method keeps all of them using the same code path. + * + * @param inputStream + * the input stream to be copied + * @return an in-memory copy of the passed input stream + * @throws IOException + * if an error occurs while copying the stream + */ + @NonNull + public static InputStream copyInputStream(InputStream inputStream) throws IOException { + return new ByteArrayInputStream(IOUtils.toByteArray(inputStream)); } /** diff --git a/src/main/java/org/kohsuke/github/function/ConsumerThrows.java b/src/main/java/org/kohsuke/github/function/ConsumerThrows.java new file mode 100644 index 000000000..ed5eb44c1 --- /dev/null +++ b/src/main/java/org/kohsuke/github/function/ConsumerThrows.java @@ -0,0 +1,9 @@ +package org.kohsuke.github.function; + +/** + * A functional interface, equivalent to {@link java.util.function.Consumer} but that allows throwing {@link Throwable} + */ +@FunctionalInterface +public interface ConsumerThrows { + void accept(T input) throws E; +} diff --git a/src/main/java/org/kohsuke/github/function/FunctionThrows.java b/src/main/java/org/kohsuke/github/function/FunctionThrows.java new file mode 100644 index 000000000..bfa5d6619 --- /dev/null +++ b/src/main/java/org/kohsuke/github/function/FunctionThrows.java @@ -0,0 +1,9 @@ +package org.kohsuke.github.function; + +/** + * A functional interface, equivalent to {@link java.util.function.Function} but that allows throwing {@link Throwable} + */ +@FunctionalInterface +public interface FunctionThrows { + R apply(T input) throws E; +} diff --git a/src/main/java/org/kohsuke/github/function/InputStreamConsumer.java b/src/main/java/org/kohsuke/github/function/InputStreamConsumer.java new file mode 100644 index 000000000..4c71c113b --- /dev/null +++ b/src/main/java/org/kohsuke/github/function/InputStreamConsumer.java @@ -0,0 +1,12 @@ +package org.kohsuke.github.function; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A functional interface, equivalent to {@link java.util.function.Consumer} but that takes an {@link InputStream} and + * can throw an {@link IOException} + */ +@FunctionalInterface +public interface InputStreamConsumer extends ConsumerThrows { +} diff --git a/src/main/java/org/kohsuke/github/function/InputStreamFunction.java b/src/main/java/org/kohsuke/github/function/InputStreamFunction.java new file mode 100644 index 000000000..ff342097d --- /dev/null +++ b/src/main/java/org/kohsuke/github/function/InputStreamFunction.java @@ -0,0 +1,12 @@ +package org.kohsuke.github.function; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A functional interface, equivalent to {@link java.util.function.Function} but that allows throwing {@link Throwable} + * + */ +@FunctionalInterface +public interface InputStreamFunction extends FunctionThrows { +}