diff --git a/java11/pom.xml b/java11/pom.xml
new file mode 100644
index 0000000..dd811f6
--- /dev/null
+++ b/java11/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ org.mineskin
+ client-parent
+ PARENT
+
+
+ java-client-java11
+ ${revision}
+
+
+
+ org.mineskin
+ java-client
+ ${revision}
+
+
+
+
\ No newline at end of file
diff --git a/java11/src/main/java/org/mineskin/Java11RequestHandler.java b/java11/src/main/java/org/mineskin/Java11RequestHandler.java
new file mode 100644
index 0000000..0759932
--- /dev/null
+++ b/java11/src/main/java/org/mineskin/Java11RequestHandler.java
@@ -0,0 +1,168 @@
+package org.mineskin;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import org.mineskin.exception.MineSkinRequestException;
+import org.mineskin.exception.MineskinException;
+import org.mineskin.request.RequestHandler;
+import org.mineskin.response.MineSkinResponse;
+import org.mineskin.response.ResponseConstructor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.http.HttpRequest.BodyPublishers;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+public class Java11RequestHandler extends RequestHandler {
+
+ private final Gson gson;
+ private final HttpClient httpClient;
+
+ public Java11RequestHandler(String userAgent, String apiKey, int timeout, Gson gson) {
+ super(userAgent, apiKey, timeout, gson);
+ this.gson = gson;
+
+ HttpClient.Builder clientBuilder = HttpClient.newBuilder()
+ .connectTimeout(java.time.Duration.ofMillis(timeout));
+
+ if (userAgent != null) {
+ clientBuilder.followRedirects(HttpClient.Redirect.NORMAL);
+ }
+ this.httpClient = clientBuilder.build();
+ }
+
+ private > R wrapResponse(HttpResponse response, Class clazz, ResponseConstructor constructor) throws IOException {
+ String rawBody = response.body();
+ try {
+ JsonObject jsonBody = gson.fromJson(rawBody, JsonObject.class);
+ R wrapped = constructor.construct(
+ response.statusCode(),
+ lowercaseHeaders(response.headers().map()),
+ jsonBody,
+ gson, clazz
+ );
+ if (!wrapped.isSuccess()) {
+ throw new MineSkinRequestException(wrapped.getError().orElse("Request Failed"), wrapped);
+ }
+ return wrapped;
+ } catch (JsonParseException e) {
+ MineSkinClientImpl.LOGGER.log(Level.WARNING, "Failed to parse response body: " + rawBody, e);
+ throw new MineskinException("Failed to parse response", e);
+ }
+ }
+
+ private Map lowercaseHeaders(Map> headers) {
+ return headers.entrySet().stream()
+ .collect(Collectors.toMap(
+ entry -> entry.getKey().toLowerCase(),
+ entry -> String.join(", ", entry.getValue())
+ ));
+ }
+
+ public > R getJson(String url, Class clazz, ResponseConstructor constructor) throws IOException {
+ MineSkinClientImpl.LOGGER.fine("GET " + url);
+
+ HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .GET()
+ .header("User-Agent", this.userAgent);
+ HttpRequest request;
+ if (apiKey != null) {
+ request = requestBuilder
+ .header("Authorization", "Bearer " + apiKey)
+ .header("Accept", "application/json").build();
+ } else {
+ request = requestBuilder.build();
+ }
+ HttpResponse response;
+ try {
+ response = this.httpClient.send(request, BodyHandlers.ofString());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return wrapResponse(response, clazz, constructor);
+ }
+
+ public > R postJson(String url, JsonObject data, Class clazz, ResponseConstructor constructor) throws IOException {
+ MineSkinClientImpl.LOGGER.fine("POST " + url);
+
+ HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .POST(BodyPublishers.ofString(gson.toJson(data)))
+ .header("Content-Type", "application/json")
+ .header("User-Agent", this.userAgent);
+ HttpRequest request;
+ if (apiKey != null) {
+ request = requestBuilder
+ .header("Authorization", "Bearer " + apiKey)
+ .header("Accept", "application/json").build();
+ } else {
+ request = requestBuilder.build();
+ }
+
+ HttpResponse response;
+ try {
+ response = this.httpClient.send(request, BodyHandlers.ofString());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return wrapResponse(response, clazz, constructor);
+ }
+
+ public > R postFormDataFile(String url, String key, String filename, InputStream in, Map data, Class clazz, ResponseConstructor constructor) throws IOException {
+ MineSkinClientImpl.LOGGER.fine("POST " + url);
+
+ String boundary = "mineskin-" + System.currentTimeMillis();
+ StringBuilder bodyBuilder = new StringBuilder();
+
+ // add form fields
+ for (Map.Entry entry : data.entrySet()) {
+ bodyBuilder.append("--").append(boundary).append("\r\n")
+ .append("Content-Disposition: form-data; name=\"").append(entry.getKey()).append("\"\r\n\r\n")
+ .append(entry.getValue()).append("\r\n");
+ }
+
+ // add file
+ byte[] fileContent = in.readAllBytes();
+ bodyBuilder.append("--").append(boundary).append("\r\n")
+ .append("Content-Disposition: form-data; name=\"").append(key)
+ .append("\"; filename=\"").append(filename).append("\"\r\n")
+ .append("Content-Type: image/png\r\n\r\n");
+ byte[] bodyStart = bodyBuilder.toString().getBytes();
+ byte[] boundaryEnd = ("\r\n--" + boundary + "--\r\n").getBytes();
+ byte[] bodyString = new byte[bodyStart.length + fileContent.length + boundaryEnd.length];
+ System.arraycopy(bodyStart, 0, bodyString, 0, bodyStart.length);
+ System.arraycopy(fileContent, 0, bodyString, bodyStart.length, fileContent.length);
+ System.arraycopy(boundaryEnd, 0, bodyString, bodyStart.length + fileContent.length, boundaryEnd.length);
+
+ HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .POST(HttpRequest.BodyPublishers.ofByteArray(bodyString))
+ .header("Content-Type", "multipart/form-data; boundary=" + boundary)
+ .header("User-Agent", this.userAgent);
+ HttpRequest request;
+ if (apiKey != null) {
+ request = requestBuilder
+ .header("Authorization", "Bearer " + apiKey)
+ .header("Accept", "application/json").build();
+ } else {
+ request = requestBuilder.build();
+ }
+
+ HttpResponse response;
+ try {
+ response = this.httpClient.send(request, BodyHandlers.ofString());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return wrapResponse(response, clazz, constructor);
+ }
+}
diff --git a/pom.xml b/pom.xml
index 57dc24f..d2d6e08 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,7 @@
core
apache
jsoup
+ java11
tests
diff --git a/tests/pom.xml b/tests/pom.xml
index e68c7ec..8bfcfc1 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -28,6 +28,12 @@
java-client-apache
${revision}
+
+ org.mineskin
+ java-client-java11
+ 2.0.0-SNAPSHOT
+ test
+
junit
diff --git a/tests/src/test/java/test/GenerateTest.java b/tests/src/test/java/test/GenerateTest.java
index 814beb1..4752879 100644
--- a/tests/src/test/java/test/GenerateTest.java
+++ b/tests/src/test/java/test/GenerateTest.java
@@ -4,12 +4,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import org.mineskin.ApacheRequestHandler;
-import org.mineskin.GenerateOptions;
-import org.mineskin.ImageUtil;
-import org.mineskin.JsoupRequestHandler;
-import org.mineskin.MineSkinClient;
-import org.mineskin.MineSkinClientImpl;
+import org.mineskin.*;
import org.mineskin.data.GeneratedSkin;
import org.mineskin.data.Skin;
import org.mineskin.data.Visibility;
@@ -44,6 +39,12 @@ public class GenerateTest {
.apiKey(System.getenv("MINESKIN_API_KEY"))
.generateExecutor(EXECUTOR)
.build();
+ private static final MineSkinClient JAVA11 = MineSkinClient.builder()
+ .requestHandler(Java11RequestHandler::new)
+ .userAgent("MineSkinClient-Java11/Tests")
+ .apiKey(System.getenv("MINESKIN_API_KEY"))
+ .generateExecutor(EXECUTOR)
+ .build();
static {
MineSkinClientImpl.LOGGER.setLevel(Level.ALL);
@@ -60,7 +61,8 @@ public void before() throws InterruptedException {
private static Stream clients() {
return Stream.of(
Arguments.of(APACHE),
- Arguments.of(JSOUP)
+ Arguments.of(JSOUP),
+ Arguments.of(JAVA11)
);
}
diff --git a/tests/src/test/java/test/GetTest.java b/tests/src/test/java/test/GetTest.java
index 0082e8e..6e5a562 100644
--- a/tests/src/test/java/test/GetTest.java
+++ b/tests/src/test/java/test/GetTest.java
@@ -3,10 +3,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import org.mineskin.ApacheRequestHandler;
-import org.mineskin.JsoupRequestHandler;
-import org.mineskin.MineSkinClient;
-import org.mineskin.MineSkinClientImpl;
+import org.mineskin.*;
import org.mineskin.data.ExistingSkin;
import org.mineskin.exception.MineSkinRequestException;
import org.mineskin.response.GetSkinResponse;
@@ -30,6 +27,11 @@ public class GetTest {
.userAgent("MineSkinClient-Jsoup/Tests")
.apiKey(System.getenv("MINESKIN_API_KEY"))
.build();
+ private static final MineSkinClient JAVA11 = MineSkinClient.builder()
+ .requestHandler(Java11RequestHandler::new)
+ .userAgent("MineSkinClient-Java11/Tests")
+ .apiKey(System.getenv("MINESKIN_API_KEY"))
+ .build();
static {
MineSkinClientImpl.LOGGER.setLevel(Level.ALL);
ConsoleHandler handler = new ConsoleHandler();
@@ -40,7 +42,8 @@ public class GetTest {
private static Stream clients() {
return Stream.of(
Arguments.of(APACHE),
- Arguments.of(JSOUP)
+ Arguments.of(JSOUP),
+ Arguments.of(JAVA11)
);
}