From 6af65ae08715a42e41b89a2c1b6286146a9a09bd Mon Sep 17 00:00:00 2001 From: Ali Kemal Ocalan Date: Mon, 16 Mar 2026 12:28:36 +0100 Subject: [PATCH 1/8] Add workflows --- .github/dependabot.yml | 11 +++++++++ .github/workflows/maven-unit-tests.yml | 29 ++++++++++++++++++++++ .github/workflows/maven.yml | 34 ++++++++++++++++++++++++++ pom.xml | 5 +++- 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/maven-unit-tests.yml create mode 100644 .github/workflows/maven.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..9c837ba --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/maven-unit-tests.yml b/.github/workflows/maven-unit-tests.yml new file mode 100644 index 0000000..83a23cb --- /dev/null +++ b/.github/workflows/maven-unit-tests.yml @@ -0,0 +1,29 @@ +name: Maven Unit Tests + +on: + push: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + java: [11, 17] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: ${{ matrix.java }} + cache: maven + + - name: Run unit tests + run: mvn -B -ntp test diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..1554e33 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,34 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Java CI with Maven + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn -B package --file pom.xml \ No newline at end of file diff --git a/pom.xml b/pom.xml index ff56f01..20b945f 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,9 @@ org.apache.maven.plugins maven-javadoc-plugin 2.9.1 + + false + attach-javadocs @@ -53,7 +56,7 @@ - + org.apache.maven.plugins maven-gpg-plugin From 0fa8eb97120a8a0d462097a050bc9024155e6583 Mon Sep 17 00:00:00 2001 From: Ali Kemal Ocalan Date: Mon, 16 Mar 2026 13:02:27 +0100 Subject: [PATCH 2/8] Update dependency --- pom.xml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 20b945f..4f05d6d 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.4.0 attach-sources @@ -44,7 +44,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 3.12.0 false @@ -60,7 +60,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 3.2.8 sign-artifacts @@ -74,7 +74,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.7.0 true ossrh @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-release-plugin - 2.5 + 3.3.1 true false @@ -121,28 +121,28 @@ com.cognitect transit-java - 0.8.311 + 1.1.403 org.apache.httpcomponents fluent-hc - 4.5.2 + 4.5.14 org.glassfish javax.json - 1.0.4 + 1.1.4 junit junit - 4.5 + 4.13.2 test - 1.7 - 1.7 + 1.8 + 1.8 From 541cea6844dc3db443d90ca3b422c2ded8f2d034 Mon Sep 17 00:00:00 2001 From: Ali Kemal Ocalan Date: Mon, 16 Mar 2026 15:31:53 +0100 Subject: [PATCH 3/8] Remove apache fluent hc and use Java 11 native http client --- pom.xml | 13 +- .../java/com/stitchdata/client/Buffer.java | 21 +-- .../com/stitchdata/client/StitchClient.java | 164 ++++++++++-------- .../client/StitchClientBuilder.java | 53 ++---- .../com/stitchdata/client/StitchResponse.java | 6 +- .../client/examples/CallbackExample.java | 73 -------- .../client/examples/SimpleExample.java | 64 ------- .../com/stitchdata/client/BufferTest.java | 26 +-- .../client/CallbackExampleTest.java | 87 ++++++++++ .../stitchdata/client/SimpleExampleTest.java | 78 +++++++++ .../stitchdata/client/StitchClientTest.java | 56 +++--- 11 files changed, 330 insertions(+), 311 deletions(-) delete mode 100644 src/main/java/com/stitchdata/client/examples/CallbackExample.java delete mode 100644 src/main/java/com/stitchdata/client/examples/SimpleExample.java create mode 100644 src/test/java/com/stitchdata/client/CallbackExampleTest.java create mode 100644 src/test/java/com/stitchdata/client/SimpleExampleTest.java diff --git a/pom.xml b/pom.xml index 4f05d6d..ab58852 100644 --- a/pom.xml +++ b/pom.xml @@ -123,15 +123,10 @@ transit-java 1.1.403 - - org.apache.httpcomponents - fluent-hc - 4.5.14 - org.glassfish - javax.json - 1.1.4 + jakarta.json + 2.0.1 junit @@ -142,7 +137,7 @@ - 1.8 - 1.8 + 11 + 11 diff --git a/src/main/java/com/stitchdata/client/Buffer.java b/src/main/java/com/stitchdata/client/Buffer.java index fa37308..8ccfccf 100644 --- a/src/main/java/com/stitchdata/client/Buffer.java +++ b/src/main/java/com/stitchdata/client/Buffer.java @@ -1,16 +1,9 @@ package com.stitchdata.client; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Queue; -import java.util.LinkedList; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import com.cognitect.transit.Writer; -import com.cognitect.transit.TransitFactory; -import com.cognitect.transit.Reader; public class Buffer { @@ -31,9 +24,9 @@ synchronized List take(int batchSizeBytes, int batchDelayMillis) { } boolean ready = - availableBytes >= batchSizeBytes || - queue.size() >= MAX_MESSAGES_PER_BATCH || - System.currentTimeMillis() - queue.peek().entryTime >= batchDelayMillis; + availableBytes >= batchSizeBytes || + queue.size() >= MAX_MESSAGES_PER_BATCH || + System.currentTimeMillis() - queue.peek().entryTime >= batchDelayMillis; if (!ready) { return null; @@ -44,7 +37,7 @@ synchronized List take(int batchSizeBytes, int batchDelayMillis) { // Start size at 2 to allow for opening and closing brackets int size = 2; while (!queue.isEmpty() && - size + queue.peek().bytes.length < MAX_BATCH_SIZE_BYTES) { + size + queue.peek().bytes.length < MAX_BATCH_SIZE_BYTES) { Entry entry = queue.remove(); // Add size of record plus the comma delimiter size += entry.bytes.length + 1; @@ -69,8 +62,8 @@ static class Entry { // We need two extra bytes for the [ and ] wrapping the record. if (bytes.length > MAX_BATCH_SIZE_BYTES - 2) { throw new IllegalArgumentException( - "Can't accept a record larger than " + (MAX_BATCH_SIZE_BYTES - 2) - + " bytes"); + "Can't accept a record larger than " + (MAX_BATCH_SIZE_BYTES - 2) + + " bytes"); } } } diff --git a/src/main/java/com/stitchdata/client/StitchClient.java b/src/main/java/com/stitchdata/client/StitchClient.java index 228b87f..6d5820c 100644 --- a/src/main/java/com/stitchdata/client/StitchClient.java +++ b/src/main/java/com/stitchdata/client/StitchClient.java @@ -1,38 +1,31 @@ package com.stitchdata.client; -import java.io.Closeable; -import java.io.EOFException; -import java.io.Flushable; -import java.io.IOException; -import java.io.ByteArrayOutputStream; -import java.io.ByteArrayInputStream; -import java.io.UnsupportedEncodingException; - -import java.util.List; +import com.cognitect.transit.Reader; +import com.cognitect.transit.TransitFactory; +import com.cognitect.transit.WriteHandler; +import com.cognitect.transit.Writer; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonReader; + +import java.io.*; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.ArrayList; -import java.util.Map; import java.util.HashMap; -import org.apache.http.client.fluent.Request; -import org.apache.http.client.fluent.Response; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.entity.ContentType; -import org.apache.http.StatusLine; -import org.apache.http.HttpResponse; -import org.apache.http.HttpEntity; -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonReader; -import com.cognitect.transit.Writer; -import com.cognitect.transit.WriteHandler; -import com.cognitect.transit.TransitFactory; -import com.cognitect.transit.Reader; +import java.util.List; +import java.util.Map; /** * Client for Stitch. * *

Callers should use {@link StitchClientBuilder} to construct * instances of {@link StitchClient}.

- * + *

* A StitchClient takes messages (instances of {@link StitchMessage}) * and submits them to Stitch in batches. A call to {@link * StitchClient#push(StitchMessage)} adds a message to the current @@ -46,7 +39,7 @@ * batchSizeBytes to 0 will effectively disable batching and cause * each call to {@link #push(StitchMessage)} to send the message * immediatley. - * + *

* You should open the client in a try-with-resources statement to * ensure that it is closed, otherwise you will lose any messages that * have been added to the buffer but not yet delivered. @@ -72,7 +65,7 @@ * } * } * - * + *

* Instances of StitchClient are thread-safe. If buffering is enabled * (which it is by default), then multiple threads will accumulate * records into the same batch. When one of those threads makes a call @@ -88,14 +81,14 @@ public class StitchClient implements Flushable, Closeable { // HTTP constants public static final String PUSH_URL - = "https://api.stitchdata.com/v2/import/push"; + = "https://api.stitchdata.com/v2/import/push"; private static final int HTTP_CONNECT_TIMEOUT = 1000 * 60 * 2; - private static final ContentType CONTENT_TYPE = - ContentType.create("application/transit+json"); + private static final String CONTENT_TYPE = "application/transit+json"; // HTTP properties private final int connectTimeout = HTTP_CONNECT_TIMEOUT; private final String stitchUrl; + private final HttpClient httpClient; // Client-specific message values private final int clientId; @@ -111,7 +104,7 @@ public class StitchClient implements Flushable, Closeable { private final Buffer buffer; private final FlushHandler flushHandler; - private final Map> writeHandlers; + private final Map> writeHandlers; private static void putWithDefault(Map map, String key, Object value, Object defaultValue) { map.put(key, value != null ? value : defaultValue); @@ -128,15 +121,16 @@ private byte[] messageToBytes(StitchMessage message) { HashMap map = new HashMap(); switch (message.getAction()) { - case UPSERT: - map.put("action", "upsert"); - putWithDefault(map, "key_names", message.getKeyNames(), keyNames); - putIfNotNull(map, "data", message.getData()); - break; - case SWITCH_VIEW: - map.put("action", "switch_view"); - break; - default: throw new IllegalArgumentException("Action must not be null"); + case UPSERT: + map.put("action", "upsert"); + putWithDefault(map, "key_names", message.getKeyNames(), keyNames); + putIfNotNull(map, "data", message.getData()); + break; + case SWITCH_VIEW: + map.put("action", "switch_view"); + break; + default: + throw new IllegalArgumentException("Action must not be null"); } map.put("client_id", clientId); @@ -153,17 +147,16 @@ private byte[] messageToBytes(StitchMessage message) { } StitchClient( - String stitchUrl, - int clientId, - String token, - String namespace, - String tableName, - List keyNames, - int batchSizeBytes, - int batchDelayMillis, - FlushHandler flushHandler, - Map> writeHandlers) - { + String stitchUrl, + int clientId, + String token, + String namespace, + String tableName, + List keyNames, + int batchSizeBytes, + int batchDelayMillis, + FlushHandler flushHandler, + Map> writeHandlers) { this.stitchUrl = stitchUrl; this.clientId = clientId; this.token = token; @@ -175,6 +168,10 @@ private byte[] messageToBytes(StitchMessage message) { this.buffer = new Buffer(); this.flushHandler = flushHandler; this.writeHandlers = TransitFactory.writeHandlerMap(writeHandlers); + this.httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofMillis(connectTimeout)) + .version(HttpClient.Version.HTTP_1_1) + .build(); } /** @@ -192,8 +189,8 @@ private byte[] messageToBytes(StitchMessage message) { * @param message the message * @throws StitchException if Stitch rejected or was unable to * process the message - * @throws IOException if there was an error communicating with - * Stitch + * @throws IOException if there was an error communicating with + * Stitch */ public void push(StitchMessage message) throws StitchException, IOException { push(message, message); @@ -217,13 +214,13 @@ public void push(StitchMessage message) throws StitchException, IOException { * sent immediately and this function will block until it is * delivered.

* - * @param message the message - * @param callbackArg flush handler will be invoked with this as + * @param message the message + * @param callbackArg flush handler will be invoked with this as * one of the callbackArgs. * @throws StitchException if Stitch rejected or was unable to * process the message - * @throws IOException if there was an error communicating with - * Stitch + * @throws IOException if there was an error communicating with + * Stitch */ public void push(StitchMessage message, Object callbackArg) throws StitchException, IOException { buffer.put(new Buffer.Entry(messageToBytes(message), callbackArg)); @@ -234,28 +231,45 @@ public void push(StitchMessage message, Object callbackArg) throws StitchExcepti } StitchResponse sendToStitch(String body) throws IOException { - Request request = Request.Post(stitchUrl) - .connectTimeout(connectTimeout) - .addHeader("Authorization", "Bearer " + token) - .bodyString(body, CONTENT_TYPE); - - HttpResponse response = request.execute().returnResponse(); - int statusCode = response.getStatusLine().getStatusCode(); - String reasonPhrase = response.getStatusLine().getReasonPhrase(); - ContentType contentType = ContentType.get(response.getEntity()); + HttpRequest request = HttpRequest.newBuilder(URI.create(stitchUrl)) + .header("Authorization", "Bearer " + token) + .header("Content-Type", CONTENT_TYPE) + .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8)) + .build(); + + HttpResponse response; + try { + response = httpClient.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted while sending request to Stitch", e); + } + + int statusCode = response.statusCode(); + String reasonPhrase = ""; + String contentType = response.headers().firstValue("Content-Type").orElse(null); JsonObject content = null; // Don't attempt to parse body for 5xx responses or if the // Content-Type doesn't explicitly state application/json. - if (statusCode < 500 && - contentType != null && - ContentType.APPLICATION_JSON.getMimeType().equals(contentType.getMimeType())) { - JsonReader rdr = Json.createReader(response.getEntity().getContent()); + if (statusCode < 500 && isJsonContentType(contentType)) { + JsonReader rdr = Json.createReader(new StringReader(response.body())); content = rdr.readObject(); } return new StitchResponse(statusCode, reasonPhrase, content); } + private static boolean isJsonContentType(String contentTypeHeader) { + if (contentTypeHeader == null) { + return false; + } + int separator = contentTypeHeader.indexOf(';'); + String mimeType = separator >= 0 + ? contentTypeHeader.substring(0, separator).trim() + : contentTypeHeader.trim(); + return "application/json".equalsIgnoreCase(mimeType); + } + void sendBatch(List batch) throws IOException { String body = serializeEntries(batch); @@ -285,7 +299,7 @@ static String serializeEntries(List entries) throws UnsupportedEnc for (Buffer.Entry entry : entries) { ByteArrayInputStream bais = new ByteArrayInputStream(entry.bytes); Reader reader = TransitFactory.reader(TransitFactory.Format.JSON, bais); - messages.add((Map)reader.read()); + messages.add((Map) reader.read()); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -299,8 +313,8 @@ static String serializeEntries(List entries) throws UnsupportedEnc * * @throws StitchException if Stitch rejected or was unable to * process the message - * @throws IOException if there was an error communicating with - * Stitch + * @throws IOException if there was an error communicating with + * Stitch */ public void flush() throws IOException { while (true) { @@ -317,8 +331,8 @@ public void flush() throws IOException { * * @throws StitchException if Stitch rejected or was unable to * process the message - * @throws IOException if there was an error communicating with - * Stitch + * @throws IOException if there was an error communicating with + * Stitch */ public void close() throws IOException { flush(); diff --git a/src/main/java/com/stitchdata/client/StitchClientBuilder.java b/src/main/java/com/stitchdata/client/StitchClientBuilder.java index 46c030b..bd5ffaa 100644 --- a/src/main/java/com/stitchdata/client/StitchClientBuilder.java +++ b/src/main/java/com/stitchdata/client/StitchClientBuilder.java @@ -1,41 +1,17 @@ package com.stitchdata.client; -import java.io.ByteArrayOutputStream; -import java.io.ByteArrayInputStream; -import java.io.UnsupportedEncodingException; -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.InputStreamReader; -import java.io.IOException; -import java.util.Collection; +import com.cognitect.transit.WriteHandler; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.HashMap; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import com.cognitect.transit.Writer; -import com.cognitect.transit.WriteHandler; -import com.cognitect.transit.TransitFactory; -import com.cognitect.transit.Reader; -import org.apache.http.client.fluent.Request; -import org.apache.http.client.fluent.Response; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.entity.ContentType; -import org.apache.http.StatusLine; -import org.apache.http.HttpResponse; -import org.apache.http.HttpEntity; - -import javax.json.Json; -import javax.json.JsonReader; /** * Use this to build instances of StitchClient. * *

Basic usage

- * + *

* Every client must have a client id, access token, and * namespace. You should have gotten these parameters when you set up * the integration at http://stitchdata.com. You must set them with @@ -53,7 +29,7 @@ * * *

Optionally set message defaults

- * + *

* If your application will send messages into only one table, you can * set the table name and key names here with {@link * #withTableName(String)} and {@link #withKeyNames(List)}, {@link @@ -75,7 +51,7 @@ * * *

Optionally tune batch parameters

- * + *

* A StitchClient takes records (instances of {@link StitchMessage}) * and submits them to Stitch in batches. A call to {@link * StitchClient#push(StitchMessage)} adds a record to the current @@ -133,7 +109,8 @@ public class StitchClientBuilder { private int batchDelayMillis = DEFAULT_BATCH_DELAY_MILLIS; private FlushHandler flushHandler = null; private String pushUrl = StitchClient.PUSH_URL; - private Map> writeHandlers = null; + private Map> writeHandlers = null; + /** * Specify your Stitch client id. This is a required setting. * @@ -219,7 +196,7 @@ public StitchClientBuilder withKeyNames(String... keyNames) { * key name here. Otherwise, you can set it individually on each * message with {@link StitchMessage#withKeyNames}. * - * @param keyName key names + * @param keyName key names * @return this object */ public StitchClientBuilder withKeyName(String keyName) { @@ -277,7 +254,7 @@ public StitchClientBuilder withPushUrl(String pushUrl) { * @param writeHandlers the write handlers * @return this object */ - public StitchClientBuilder withWriteHandlers(Map> writeHandlers) { + public StitchClientBuilder withWriteHandlers(Map> writeHandlers) { this.writeHandlers = writeHandlers; return this; } @@ -289,11 +266,11 @@ public StitchClientBuilder withWriteHandlers(Map> writeH */ public StitchClient build() { return new StitchClient( - pushUrl, clientId, token, namespace, - tableName, keyNames, - batchSizeBytes, - batchDelayMillis, - flushHandler, - writeHandlers); + pushUrl, clientId, token, namespace, + tableName, keyNames, + batchSizeBytes, + batchDelayMillis, + flushHandler, + writeHandlers); } } diff --git a/src/main/java/com/stitchdata/client/StitchResponse.java b/src/main/java/com/stitchdata/client/StitchResponse.java index 0d287c7..ae79d9e 100644 --- a/src/main/java/com/stitchdata/client/StitchResponse.java +++ b/src/main/java/com/stitchdata/client/StitchResponse.java @@ -1,7 +1,7 @@ package com.stitchdata.client; -import javax.json.JsonReader; -import javax.json.Json; -import javax.json.JsonObject; + + +import jakarta.json.JsonObject; /** * Encapsulates a response received from Stitch. diff --git a/src/main/java/com/stitchdata/client/examples/CallbackExample.java b/src/main/java/com/stitchdata/client/examples/CallbackExample.java deleted file mode 100644 index e854a5e..0000000 --- a/src/main/java/com/stitchdata/client/examples/CallbackExample.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.stitchdata.client.examples; - -import java.io.IOException; -import com.stitchdata.client.StitchClient; -import com.stitchdata.client.StitchClientBuilder; -import com.stitchdata.client.StitchException; -import com.stitchdata.client.StitchMessage; -import com.stitchdata.client.FlushHandler; -import java.util.List; -import java.util.Map; -import java.util.HashMap; - -public class CallbackExample { - - private static Map makePerson(int id, String name) { - Map result = new HashMap(); - result.put("id", id); - result.put("name", name); - return result; - } - - public static void exitWithError(String message) { - System.err.println(message); - System.exit(-1); - } - - public static void main(String ...args) { - if (args.length != 3) { - System.err.println("Usage: CLIENT_ID TOKEN NAMESPACE"); - System.exit(-1); - } - - Integer clientId = Integer.parseInt(args[0]); - String token = args[1]; - String namespace = args[2]; - - Map[] people = new Map[] { - makePerson(1, "Jerry Garcia"), - makePerson(2, "Omar Rodgriguez Lopez"), - makePerson(3, "Nina Simone"), - makePerson(4, "Joni Mitchell"), - makePerson(5, "David Bowie") - }; - - try (StitchClient stitch = new StitchClientBuilder() - .withClientId(clientId) - .withToken(token) - .withNamespace(namespace) - .withTableName("people") - .withKeyNames("id") - .withFlushHandler(new FlushHandler() { - public void onFlush(List names) { - for (Object name : names) { - System.out.println(name); - } - } - }) - .build()) { - for (Map person : people) { - StitchMessage message = StitchMessage.newUpsert() - .withSequence(System.currentTimeMillis()) - .withData(person); - stitch.push(message, person.get("name")); - } - } - catch (StitchException e) { - exitWithError("Stitch error " + e.getMessage()); - } - catch (IOException e) { - exitWithError(e.getMessage()); - } - } -} diff --git a/src/main/java/com/stitchdata/client/examples/SimpleExample.java b/src/main/java/com/stitchdata/client/examples/SimpleExample.java deleted file mode 100644 index 5f1f253..0000000 --- a/src/main/java/com/stitchdata/client/examples/SimpleExample.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.stitchdata.client.examples; - -import java.io.IOException; -import com.stitchdata.client.StitchClient; -import com.stitchdata.client.StitchClientBuilder; -import com.stitchdata.client.StitchException; -import com.stitchdata.client.StitchMessage; -import java.util.Map; -import java.util.HashMap; - -public class SimpleExample { - - private static Map makePerson(int id, String name) { - Map result = new HashMap(); - result.put("id", id); - result.put("name", name); - return result; - } - - public static void exitWithError(String message) { - System.err.println(message); - System.exit(-1); - } - - public static void main(String ...args) { - if (args.length != 3) { - System.err.println("Usage: CLIENT_ID TOKEN NAMESPACE"); - System.exit(-1); - } - - Integer clientId = Integer.parseInt(args[0]); - String token = args[1]; - String namespace = args[2]; - - Map[] people = new Map[] { - makePerson(1, "Jerry Garcia"), - makePerson(2, "Omar Rodgriguez Lopez"), - makePerson(3, "Nina Simone"), - makePerson(4, "Joni Mitchell"), - makePerson(5, "David Bowie") - }; - - try (StitchClient stitch = new StitchClientBuilder() - .withClientId(clientId) - .withToken(token) - .withNamespace(namespace) - .withTableName("people") - .withKeyNames("id") - .build()) { - for (Map person : people) { - stitch.push( - StitchMessage.newUpsert() - .withSequence(System.currentTimeMillis()) - .withData(person)); - } - } - catch (StitchException e) { - exitWithError("Stitch error " + e.getMessage()); - } - catch (IOException e) { - exitWithError(e.getMessage()); - } - } -} diff --git a/src/test/java/com/stitchdata/client/BufferTest.java b/src/test/java/com/stitchdata/client/BufferTest.java index 84f81c2..f2a45dc 100644 --- a/src/test/java/com/stitchdata/client/BufferTest.java +++ b/src/test/java/com/stitchdata/client/BufferTest.java @@ -1,20 +1,22 @@ package com.stitchdata.client; +import com.cognitect.transit.TransitFactory; +import com.cognitect.transit.Writer; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.io.ByteArrayOutputStream; -import com.cognitect.transit.Writer; -import com.cognitect.transit.WriteHandler; -import com.cognitect.transit.TransitFactory; -import com.cognitect.transit.Reader; import java.util.Arrays; -import java.util.List; import java.util.HashMap; +import java.util.List; import java.util.Map; -import org.junit.*; + import static org.junit.Assert.*; -public class BufferTest { +public class BufferTest { static final Map tinyRecord = new HashMap(); static final Map bigRecord = new HashMap(); @@ -50,7 +52,7 @@ public void putMessage(Map record) { } public String takeBatchBody(int batchSizeBytes, int batchDelayMillis) - throws UnsupportedEncodingException { + throws UnsupportedEncodingException { List entries = buffer.take(batchSizeBytes, batchDelayMillis); return entries == null ? null : StitchClient.serializeEntries(entries); } @@ -69,8 +71,8 @@ public void testWithholdUntilBytesAvailable() throws IOException { assertNull(takeBatchBody(36, Integer.MAX_VALUE)); putMessage(tinyRecord); assertEquals( - "[" + tinyResult + "," + tinyResult + "," + tinyResult + "]", - takeBatchBody(36, Integer.MAX_VALUE)); + "[" + tinyResult + "," + tinyResult + "," + tinyResult + "]", + takeBatchBody(36, Integer.MAX_VALUE)); } @Test @@ -99,7 +101,7 @@ public void testDoesNotExceedMaxBatchSize() throws IOException { assertNull(batch3); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void assertCantPutRecordLargerThanMaxMessageSize() { putMessage(hugeRecord); } diff --git a/src/test/java/com/stitchdata/client/CallbackExampleTest.java b/src/test/java/com/stitchdata/client/CallbackExampleTest.java new file mode 100644 index 0000000..adbc6c8 --- /dev/null +++ b/src/test/java/com/stitchdata/client/CallbackExampleTest.java @@ -0,0 +1,87 @@ +package com.stitchdata.client; + +import com.cognitect.transit.Reader; +import com.cognitect.transit.TransitFactory; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class CallbackExampleTest { + + private static Map makePerson(int id, String name) { + Map result = new HashMap(); + result.put("id", id); + result.put("name", name); + return result; + } + + private static class CollectingFlushHandler implements FlushHandler { + final List names = new ArrayList(); + + public void onFlush(List arg) { + names.addAll(arg); + } + } + + private static class DummyStitchClient extends StitchClient { + final List receivedPeople = new ArrayList(); + + DummyStitchClient(FlushHandler flushHandler) { + super("", 1, "token", "namespace", "people", Arrays.asList(new String[]{"id"}), + StitchClientBuilder.DEFAULT_BATCH_SIZE_BYTES, + StitchClientBuilder.DEFAULT_BATCH_DELAY_MILLIS, + flushHandler, + null); + } + + @Override + StitchResponse sendToStitch(String body) throws IOException { + ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); + Reader reader = TransitFactory.reader(TransitFactory.Format.JSON, bais); + List records = (List) reader.read(); + + for (Object record : records) { + Map data = (Map) ((Map) record).get("data"); + receivedPeople.add(data); + } + + return new StitchResponse(200, "ok", null); + } + } + + @Test + public void callbackExampleFlowShouldPushPeopleAndInvokeCallbackWithNames() throws IOException { + Map[] people = new Map[]{ + makePerson(1, "Jerry Garcia"), + makePerson(2, "Omar Rodgriguez Lopez"), + makePerson(3, "Nina Simone"), + makePerson(4, "Joni Mitchell"), + makePerson(5, "David Bowie") + }; + + CollectingFlushHandler flushHandler = new CollectingFlushHandler(); + DummyStitchClient stitch = new DummyStitchClient(flushHandler); + + try (stitch) { + for (Map person : people) { + StitchMessage message = StitchMessage.newUpsert() + .withSequence(System.currentTimeMillis()) + .withData(person); + stitch.push(message, person.get("name")); + } + } + + assertEquals(people.length, stitch.receivedPeople.size()); + assertEquals(Arrays.asList( + "Jerry Garcia", + "Omar Rodgriguez Lopez", + "Nina Simone", + "Joni Mitchell", + "David Bowie"), flushHandler.names); + } +} diff --git a/src/test/java/com/stitchdata/client/SimpleExampleTest.java b/src/test/java/com/stitchdata/client/SimpleExampleTest.java new file mode 100644 index 0000000..58e36d0 --- /dev/null +++ b/src/test/java/com/stitchdata/client/SimpleExampleTest.java @@ -0,0 +1,78 @@ +package com.stitchdata.client; + +import com.cognitect.transit.Reader; +import com.cognitect.transit.TransitFactory; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class SimpleExampleTest { + + private static Map makePerson(int id, String name) { + Map result = new HashMap<>(); + result.put("id", id); + result.put("name", name); + return result; + } + + private static class DummyStitchClient extends StitchClient { + final List> receivedPeople = new ArrayList>(); + + DummyStitchClient() { + super("", 1, "token", "namespace", "people", Arrays.asList("id"), + StitchClientBuilder.DEFAULT_BATCH_SIZE_BYTES, + StitchClientBuilder.DEFAULT_BATCH_DELAY_MILLIS, + null, + null); + } + + @Override + StitchResponse sendToStitch(String body) throws IOException { + ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); + Reader reader = TransitFactory.reader(TransitFactory.Format.JSON, bais); + List records = (List) reader.read(); + + for (Object record : records) { + Map row = (Map) record; + Map data = (Map) row.get("data"); + receivedPeople.add(data); + } + + return new StitchResponse(200, "ok", null); + } + } + + @Test + public void simpleExampleFlowShouldPushAllPeople() throws IOException { + Map[] people = new Map[]{ + makePerson(1, "Jerry Garcia"), + makePerson(2, "Omar Rodgriguez Lopez"), + makePerson(3, "Nina Simone"), + makePerson(4, "Joni Mitchell"), + makePerson(5, "David Bowie") + }; + + DummyStitchClient stitch = new DummyStitchClient(); + try (DummyStitchClient ignored = stitch) { + for (Map person : people) { + stitch.push( + StitchMessage.newUpsert() + .withSequence(System.currentTimeMillis()) + .withData(person)); + } + } + + assertEquals(people.length, stitch.receivedPeople.size()); + for (int i = 0; i < people.length; i++) { + Number expectedId = (Number) people[i].get("id"); + Number actualId = (Number) stitch.receivedPeople.get(i).get("id"); + assertEquals(expectedId.longValue(), actualId.longValue()); + assertEquals(people[i].get("name"), stitch.receivedPeople.get(i).get("name")); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/stitchdata/client/StitchClientTest.java b/src/test/java/com/stitchdata/client/StitchClientTest.java index 75a33ab..6a9b45a 100644 --- a/src/test/java/com/stitchdata/client/StitchClientTest.java +++ b/src/test/java/com/stitchdata/client/StitchClientTest.java @@ -1,19 +1,19 @@ package com.stitchdata.client; +import com.cognitect.transit.Reader; +import com.cognitect.transit.TransitFactory; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.List; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; +import java.lang.reflect.Field; +import java.net.http.HttpClient; +import java.util.*; import java.util.concurrent.ConcurrentSkipListSet; -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import com.cognitect.transit.TransitFactory; -import com.cognitect.transit.Reader; -import org.junit.*; -import static org.junit.Assert.*; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; /** * Attempts to exercise concurrent calls to {@link @@ -26,7 +26,7 @@ * some debug print statements that we have used to show that batches * at least contain records from multiple threads. */ -public class StitchClientTest { +public class StitchClientTest { private static final int NUM_THREADS = 4; private static final int NUM_RECORDS_PER_THREAD = 10000; @@ -43,7 +43,7 @@ public class StitchClientTest { private class DummyStitchClient extends StitchClient { DummyStitchClient(FlushHandler flushHandler) { - super("", 0, null, null, null, Arrays.asList(new String[] { "id" }), StitchClientBuilder.DEFAULT_BATCH_SIZE_BYTES, 60000000, flushHandler, null); + super("", 0, null, null, null, Arrays.asList(new String[]{"id"}), StitchClientBuilder.DEFAULT_BATCH_SIZE_BYTES, 60000000, flushHandler, null); } @Override @@ -57,8 +57,8 @@ StitchResponse sendToStitch(String body) throws IOException { counts[i] = 0; } for (Object record : records) { - Map data = (Map) ((Map)record).get("data"); - int threadId = ((Long) ((Map)data).get("threadId")).intValue(); + Map data = (Map) ((Map) record).get("data"); + int threadId = ((Long) ((Map) data).get("threadId")).intValue(); counts[threadId]++; numRecordsByThreadId.get(threadId).incrementAndGet(); } @@ -106,16 +106,14 @@ public void run() { record.put("recordId", recordId); try { StitchMessage message = StitchMessage.newUpsert() - .withSequence(0) - .withData(record); + .withSequence(0) + .withData(record); if (useCallback) { stitch.push(message, String.format("thread-%d-record-%d", threadId, recordId)); - } - else { + } else { stitch.push(message); } - } - catch (IOException e) { + } catch (IOException e) { throw new RuntimeException(e); } } @@ -153,9 +151,21 @@ public void testConcurrentPushesWithoutCallback() throws IOException { } + @Test + public void testUsesHttp11Client() throws Exception { + try (StitchClient stitch = new DummyStitchClient(null)) { + Field httpClientField = StitchClient.class.getDeclaredField("httpClient"); + httpClientField.setAccessible(true); + HttpClient httpClient = (HttpClient) httpClientField.get(stitch); + + assertEquals(HttpClient.Version.HTTP_1_1, httpClient.version()); + } + } + private static class SetFlushHandler implements FlushHandler { final ConcurrentSkipListSet callbackArgsReceived = - new ConcurrentSkipListSet(); + new ConcurrentSkipListSet(); + public void onFlush(List arg) { callbackArgsReceived.addAll(arg); } From c703ccabb51f3419535a1f95b570c7853c7b6c9a Mon Sep 17 00:00:00 2001 From: Ali Kemal Ocalan Date: Thu, 30 Apr 2026 09:55:17 +0200 Subject: [PATCH 4/8] Fix maven repo url issue --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ab58852..d61d670 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,7 @@ central Maven Repository Switchboard default - http://repo1.maven.org/maven2 + https://repo.maven.apache.org/maven2 false From 5a5335f7380ef2428868ba446c6b07641aacafe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Kemal=20=C3=96calan?= Date: Wed, 6 May 2026 14:07:07 +0200 Subject: [PATCH 5/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/maven.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 1554e33..709174e 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -10,9 +10,9 @@ name: Java CI with Maven on: push: - branches: [ "master" ] + branches: [ "master", "main" ] pull_request: - branches: [ "master" ] + branches: [ "master", "main" ] permissions: contents: read From 5c6d87b19ee85fafc61e914295798610bf3f9d2b Mon Sep 17 00:00:00 2001 From: Ali Kemal Ocalan Date: Wed, 6 May 2026 14:34:37 +0200 Subject: [PATCH 6/8] feat: Reformat code --- .github/workflows/maven-unit-tests.yml | 1 + pom.xml | 9 +++ .../java/com/stitchdata/client/Buffer.java | 4 +- .../com/stitchdata/client/StitchClient.java | 60 ++++++++++++++++--- .../client/StitchClientBuilder.java | 6 +- .../com/stitchdata/client/StitchMessage.java | 8 +-- .../com/stitchdata/client/StitchResponse.java | 8 +-- .../com/stitchdata/client/BufferTest.java | 4 +- 8 files changed, 75 insertions(+), 25 deletions(-) diff --git a/.github/workflows/maven-unit-tests.yml b/.github/workflows/maven-unit-tests.yml index 83a23cb..9b110cd 100644 --- a/.github/workflows/maven-unit-tests.yml +++ b/.github/workflows/maven-unit-tests.yml @@ -2,6 +2,7 @@ name: Maven Unit Tests on: push: + pull_request: permissions: contents: read diff --git a/pom.xml b/pom.xml index d61d670..73aec54 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,15 @@ deploy + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + 11 + 11 + + diff --git a/src/main/java/com/stitchdata/client/Buffer.java b/src/main/java/com/stitchdata/client/Buffer.java index 8ccfccf..37c1e2d 100644 --- a/src/main/java/com/stitchdata/client/Buffer.java +++ b/src/main/java/com/stitchdata/client/Buffer.java @@ -10,7 +10,7 @@ public class Buffer { static final int MAX_BATCH_SIZE_BYTES = 4000000; static final int MAX_MESSAGES_PER_BATCH = 10000; - private final Queue queue = new LinkedList(); + private final Queue queue = new LinkedList<>(); private int availableBytes = 0; synchronized void put(Entry entry) { @@ -32,7 +32,7 @@ synchronized List take(int batchSizeBytes, int batchDelayMillis) { return null; } - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); // Start size at 2 to allow for opening and closing brackets int size = 2; diff --git a/src/main/java/com/stitchdata/client/StitchClient.java b/src/main/java/com/stitchdata/client/StitchClient.java index 6d5820c..a3c32da 100644 --- a/src/main/java/com/stitchdata/client/StitchClient.java +++ b/src/main/java/com/stitchdata/client/StitchClient.java @@ -85,8 +85,6 @@ public class StitchClient implements Flushable, Closeable { private static final int HTTP_CONNECT_TIMEOUT = 1000 * 60 * 2; private static final String CONTENT_TYPE = "application/transit+json"; - // HTTP properties - private final int connectTimeout = HTTP_CONNECT_TIMEOUT; private final String stitchUrl; private final HttpClient httpClient; @@ -100,7 +98,6 @@ public class StitchClient implements Flushable, Closeable { // Buffer flush time parameters private final int batchSizeBytes; private final int batchDelayMillis; - private long lastFlushTime = System.currentTimeMillis(); private final Buffer buffer; private final FlushHandler flushHandler; @@ -169,7 +166,7 @@ private byte[] messageToBytes(StitchMessage message) { this.flushHandler = flushHandler; this.writeHandlers = TransitFactory.writeHandlerMap(writeHandlers); this.httpClient = HttpClient.newBuilder() - .connectTimeout(Duration.ofMillis(connectTimeout)) + .connectTimeout(Duration.ofMillis(HTTP_CONNECT_TIMEOUT)) .version(HttpClient.Version.HTTP_1_1) .build(); } @@ -246,7 +243,6 @@ StitchResponse sendToStitch(String body) throws IOException { } int statusCode = response.statusCode(); - String reasonPhrase = ""; String contentType = response.headers().firstValue("Content-Type").orElse(null); JsonObject content = null; @@ -256,9 +252,55 @@ StitchResponse sendToStitch(String body) throws IOException { JsonReader rdr = Json.createReader(new StringReader(response.body())); content = rdr.readObject(); } + + // Build a more informative reason phrase + String reasonPhrase = getReasonPhrase(statusCode, content); + return new StitchResponse(statusCode, reasonPhrase, content); } + private static String getReasonPhrase(int statusCode, JsonObject jsonContent) { + // Get standard HTTP reason phrase + String standardPhrase = getStandardReasonPhrase(statusCode); + + // Try to extract more context from JSON response body if available + if (jsonContent != null && jsonContent.containsKey("message")) { + String message = jsonContent.getString("message"); + return standardPhrase + " - " + message; + } + + return standardPhrase; + } + + private static String getStandardReasonPhrase(int statusCode) { + switch (statusCode) { + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 409: + return "Conflict"; + case 413: + return "Payload Too Large"; + case 429: + return "Too Many Requests"; + case 500: + return "Internal Server Error"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + default: + return statusCode < 500 ? "Client Error" : "Server Error"; + } + } + private static boolean isJsonContentType(String contentTypeHeader) { if (contentTypeHeader == null) { return false; @@ -289,23 +331,23 @@ void sendBatch(List batch) throws IOException { } } - static String serializeEntries(List entries) throws UnsupportedEncodingException { + static String serializeEntries(List entries) { if (entries == null) { return null; } - ArrayList messages = new ArrayList(); + ArrayList messages = new ArrayList<>(); for (Buffer.Entry entry : entries) { ByteArrayInputStream bais = new ByteArrayInputStream(entry.bytes); Reader reader = TransitFactory.reader(TransitFactory.Format.JSON, bais); - messages.add((Map) reader.read()); + messages.add(reader.read()); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); Writer writer = TransitFactory.writer(TransitFactory.Format.JSON, baos); writer.write(messages); - return baos.toString("UTF-8"); + return baos.toString(StandardCharsets.UTF_8); } /** diff --git a/src/main/java/com/stitchdata/client/StitchClientBuilder.java b/src/main/java/com/stitchdata/client/StitchClientBuilder.java index bd5ffaa..4c53fac 100644 --- a/src/main/java/com/stitchdata/client/StitchClientBuilder.java +++ b/src/main/java/com/stitchdata/client/StitchClientBuilder.java @@ -10,7 +10,7 @@ /** * Use this to build instances of StitchClient. * - *

Basic usage

+ *

Basic usage

*

* Every client must have a client id, access token, and * namespace. You should have gotten these parameters when you set up @@ -28,7 +28,7 @@ * } * * - *

Optionally set message defaults

+ *

Optionally set message defaults

*

* If your application will send messages into only one table, you can * set the table name and key names here with {@link @@ -50,7 +50,7 @@ * } * * - *

Optionally tune batch parameters

+ *

Optionally tune batch parameters

*

* A StitchClient takes records (instances of {@link StitchMessage}) * and submits them to Stitch in batches. A call to {@link diff --git a/src/main/java/com/stitchdata/client/StitchMessage.java b/src/main/java/com/stitchdata/client/StitchMessage.java index de1d488..8072cec 100644 --- a/src/main/java/com/stitchdata/client/StitchMessage.java +++ b/src/main/java/com/stitchdata/client/StitchMessage.java @@ -20,7 +20,7 @@ */ public class StitchMessage { - public static enum Action { UPSERT, SWITCH_VIEW }; + public static enum Action {UPSERT, SWITCH_VIEW} private Action action; private String tableName; @@ -131,7 +131,7 @@ public StitchMessage withKeyName(String keyName) { * will only update it if the sequence number on the incoming * record is greater than the sequenc enumber of the existing * record. - * + *

* For example, suppose we send in the following messages: * *

@@ -151,9 +151,9 @@ public StitchMessage withKeyName(String keyName) {
      *   .withKeyNames("order_id")
      *   .withData(data)
      *   .withSequence(2);
-     *}
+     * }
      * 
- * + *

* Regardless of the order in which the records are processed by * the loader, the end result will be that "status" for order * number 123 will be "completed". diff --git a/src/main/java/com/stitchdata/client/StitchResponse.java b/src/main/java/com/stitchdata/client/StitchResponse.java index ae79d9e..47d83b5 100644 --- a/src/main/java/com/stitchdata/client/StitchResponse.java +++ b/src/main/java/com/stitchdata/client/StitchResponse.java @@ -21,9 +21,9 @@ public StitchResponse(int httpStatusCode, String httpReasonPhrase, JsonObject co * Returns true if the request succeeded. * * @return

    - *
  • true - if the request succeeded
  • - *
  • false - if the request failed
  • - *
+ *
  • true - if the request succeeded
  • + *
  • false - if the request failed
  • + * */ public boolean isOk() { return httpStatusCode < 300; @@ -43,7 +43,7 @@ public JsonObject getContent() { public String toString() { String result = "HTTP Status Code " + httpStatusCode + - " (" + httpReasonPhrase + ")"; + " (" + httpReasonPhrase + ")"; if (content != null) { result += ": " + content.toString(); } diff --git a/src/test/java/com/stitchdata/client/BufferTest.java b/src/test/java/com/stitchdata/client/BufferTest.java index f2a45dc..ba6abb2 100644 --- a/src/test/java/com/stitchdata/client/BufferTest.java +++ b/src/test/java/com/stitchdata/client/BufferTest.java @@ -8,7 +8,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -51,8 +50,7 @@ public void putMessage(Map record) { buffer.put(new Buffer.Entry(baos.toByteArray(), null)); } - public String takeBatchBody(int batchSizeBytes, int batchDelayMillis) - throws UnsupportedEncodingException { + public String takeBatchBody(int batchSizeBytes, int batchDelayMillis) { List entries = buffer.take(batchSizeBytes, batchDelayMillis); return entries == null ? null : StitchClient.serializeEntries(entries); } From 8e4ecbdd038d7b834475db9a4058a00f60866826 Mon Sep 17 00:00:00 2001 From: Ali Kemal Ocalan Date: Wed, 6 May 2026 15:15:40 +0200 Subject: [PATCH 7/8] feat Fix Ttypos --- .github/workflows/maven.yml | 18 +++++++++--------- .../com/stitchdata/client/StitchClient.java | 8 +++++++- .../stitchdata/client/CallbackExampleTest.java | 4 ++-- .../stitchdata/client/SimpleExampleTest.java | 2 +- .../stitchdata/client/StitchClientTest.java | 13 ------------- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 709174e..c038d3b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -23,12 +23,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: maven - - name: Build with Maven - run: mvn -B package --file pom.xml \ No newline at end of file + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn -B package --file pom.xml diff --git a/src/main/java/com/stitchdata/client/StitchClient.java b/src/main/java/com/stitchdata/client/StitchClient.java index a3c32da..2710c5d 100644 --- a/src/main/java/com/stitchdata/client/StitchClient.java +++ b/src/main/java/com/stitchdata/client/StitchClient.java @@ -297,7 +297,13 @@ private static String getStandardReasonPhrase(int statusCode) { case 504: return "Gateway Timeout"; default: - return statusCode < 500 ? "Client Error" : "Server Error"; + if (statusCode >= 400 && statusCode < 500) { + return "Client Error"; + } + if (statusCode >= 500) { + return "Server Error"; + } + return ""; } } diff --git a/src/test/java/com/stitchdata/client/CallbackExampleTest.java b/src/test/java/com/stitchdata/client/CallbackExampleTest.java index adbc6c8..25dc26a 100644 --- a/src/test/java/com/stitchdata/client/CallbackExampleTest.java +++ b/src/test/java/com/stitchdata/client/CallbackExampleTest.java @@ -58,7 +58,7 @@ StitchResponse sendToStitch(String body) throws IOException { public void callbackExampleFlowShouldPushPeopleAndInvokeCallbackWithNames() throws IOException { Map[] people = new Map[]{ makePerson(1, "Jerry Garcia"), - makePerson(2, "Omar Rodgriguez Lopez"), + makePerson(2, "Omar Rodriguez Lopez"), makePerson(3, "Nina Simone"), makePerson(4, "Joni Mitchell"), makePerson(5, "David Bowie") @@ -79,7 +79,7 @@ public void callbackExampleFlowShouldPushPeopleAndInvokeCallbackWithNames() thro assertEquals(people.length, stitch.receivedPeople.size()); assertEquals(Arrays.asList( "Jerry Garcia", - "Omar Rodgriguez Lopez", + "Omar Rodriguez Lopez", "Nina Simone", "Joni Mitchell", "David Bowie"), flushHandler.names); diff --git a/src/test/java/com/stitchdata/client/SimpleExampleTest.java b/src/test/java/com/stitchdata/client/SimpleExampleTest.java index 58e36d0..736c223 100644 --- a/src/test/java/com/stitchdata/client/SimpleExampleTest.java +++ b/src/test/java/com/stitchdata/client/SimpleExampleTest.java @@ -51,7 +51,7 @@ StitchResponse sendToStitch(String body) throws IOException { public void simpleExampleFlowShouldPushAllPeople() throws IOException { Map[] people = new Map[]{ makePerson(1, "Jerry Garcia"), - makePerson(2, "Omar Rodgriguez Lopez"), + makePerson(2, "Omar Rodriguez Lopez"), makePerson(3, "Nina Simone"), makePerson(4, "Joni Mitchell"), makePerson(5, "David Bowie") diff --git a/src/test/java/com/stitchdata/client/StitchClientTest.java b/src/test/java/com/stitchdata/client/StitchClientTest.java index 6a9b45a..79d476d 100644 --- a/src/test/java/com/stitchdata/client/StitchClientTest.java +++ b/src/test/java/com/stitchdata/client/StitchClientTest.java @@ -7,8 +7,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; -import java.lang.reflect.Field; -import java.net.http.HttpClient; import java.util.*; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicInteger; @@ -151,17 +149,6 @@ public void testConcurrentPushesWithoutCallback() throws IOException { } - @Test - public void testUsesHttp11Client() throws Exception { - try (StitchClient stitch = new DummyStitchClient(null)) { - Field httpClientField = StitchClient.class.getDeclaredField("httpClient"); - httpClientField.setAccessible(true); - HttpClient httpClient = (HttpClient) httpClientField.get(stitch); - - assertEquals(HttpClient.Version.HTTP_1_1, httpClient.version()); - } - } - private static class SetFlushHandler implements FlushHandler { final ConcurrentSkipListSet callbackArgsReceived = new ConcurrentSkipListSet(); From 77792223135a710fec445d9e33fe2adc022bc29f Mon Sep 17 00:00:00 2001 From: Ali Kemal Ocalan Date: Wed, 6 May 2026 16:41:09 +0200 Subject: [PATCH 8/8] feat Fix typos --- .../com/stitchdata/client/StitchClient.java | 19 +++++++++++-------- .../stitchdata/client/StitchClientTest.java | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/stitchdata/client/StitchClient.java b/src/main/java/com/stitchdata/client/StitchClient.java index 2710c5d..3f9b01b 100644 --- a/src/main/java/com/stitchdata/client/StitchClient.java +++ b/src/main/java/com/stitchdata/client/StitchClient.java @@ -114,10 +114,14 @@ private static void putIfNotNull(Map map, String key, Object value) { } - private byte[] messageToBytes(StitchMessage message) { + private byte[] messageToBytes(StitchMessage message) throws IllegalArgumentException { HashMap map = new HashMap(); - switch (message.getAction()) { + StitchMessage.Action action = message.getAction(); + if (action == null) { + throw new IllegalArgumentException("Action must not be null"); + } + switch (action) { case UPSERT: map.put("action", "upsert"); putWithDefault(map, "key_names", message.getKeyNames(), keyNames); @@ -126,8 +130,6 @@ private byte[] messageToBytes(StitchMessage message) { case SWITCH_VIEW: map.put("action", "switch_view"); break; - default: - throw new IllegalArgumentException("Action must not be null"); } map.put("client_id", clientId); @@ -219,7 +221,7 @@ public void push(StitchMessage message) throws StitchException, IOException { * @throws IOException if there was an error communicating with * Stitch */ - public void push(StitchMessage message, Object callbackArg) throws StitchException, IOException { + public void push(StitchMessage message, Object callbackArg) throws StitchException, IOException, IllegalArgumentException { buffer.put(new Buffer.Entry(messageToBytes(message), callbackArg)); List batch = buffer.take(this.batchSizeBytes, this.batchDelayMillis); if (batch != null) { @@ -249,8 +251,9 @@ StitchResponse sendToStitch(String body) throws IOException { // Don't attempt to parse body for 5xx responses or if the // Content-Type doesn't explicitly state application/json. if (statusCode < 500 && isJsonContentType(contentType)) { - JsonReader rdr = Json.createReader(new StringReader(response.body())); - content = rdr.readObject(); + try (JsonReader rdr = Json.createReader(new StringReader(response.body()))) { + content = rdr.readObject(); + } } // Build a more informative reason phrase @@ -303,7 +306,7 @@ private static String getStandardReasonPhrase(int statusCode) { if (statusCode >= 500) { return "Server Error"; } - return ""; + return "OK"; } } diff --git a/src/test/java/com/stitchdata/client/StitchClientTest.java b/src/test/java/com/stitchdata/client/StitchClientTest.java index 79d476d..943063d 100644 --- a/src/test/java/com/stitchdata/client/StitchClientTest.java +++ b/src/test/java/com/stitchdata/client/StitchClientTest.java @@ -56,7 +56,7 @@ StitchResponse sendToStitch(String body) throws IOException { } for (Object record : records) { Map data = (Map) ((Map) record).get("data"); - int threadId = ((Long) ((Map) data).get("threadId")).intValue(); + int threadId = ((Long) data.get("threadId")).intValue(); counts[threadId]++; numRecordsByThreadId.get(threadId).incrementAndGet(); }