From d6c06ba85a5d50111430af85141a6bf094502a3f Mon Sep 17 00:00:00 2001 From: Hannes Achleitner Date: Thu, 11 Jul 2024 17:06:03 +0200 Subject: [PATCH] Kotlin DefaultPacketSender --- .../sdk/dispatcher/DefaultPacketSender.java | 151 ------------------ .../sdk/dispatcher/DefaultPacketSender.kt | 140 ++++++++++++++++ .../sdk/dispatcher/DispatcherFactory.java | 7 - .../sdk/dispatcher/DispatcherFactory.kt | 7 + .../matomo/sdk/dispatcher/PacketSender.java | 16 -- .../org/matomo/sdk/dispatcher/PacketSender.kt | 16 ++ .../java/org/matomo/sdk/tools/UrlHelper.java | 2 +- 7 files changed, 164 insertions(+), 175 deletions(-) delete mode 100644 tracker/src/main/java/org/matomo/sdk/dispatcher/DefaultPacketSender.java create mode 100644 tracker/src/main/java/org/matomo/sdk/dispatcher/DefaultPacketSender.kt delete mode 100644 tracker/src/main/java/org/matomo/sdk/dispatcher/DispatcherFactory.java create mode 100644 tracker/src/main/java/org/matomo/sdk/dispatcher/DispatcherFactory.kt delete mode 100644 tracker/src/main/java/org/matomo/sdk/dispatcher/PacketSender.java create mode 100644 tracker/src/main/java/org/matomo/sdk/dispatcher/PacketSender.kt diff --git a/tracker/src/main/java/org/matomo/sdk/dispatcher/DefaultPacketSender.java b/tracker/src/main/java/org/matomo/sdk/dispatcher/DefaultPacketSender.java deleted file mode 100644 index 3e8d7841..00000000 --- a/tracker/src/main/java/org/matomo/sdk/dispatcher/DefaultPacketSender.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.matomo.sdk.dispatcher; - -import org.matomo.sdk.Matomo; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.zip.GZIPOutputStream; - -import timber.log.Timber; - - -public class DefaultPacketSender implements PacketSender { - private static final String TAG = Matomo.tag(DefaultPacketSender.class); - private long mTimeout = Dispatcher.DEFAULT_CONNECTION_TIMEOUT; - private boolean mGzip = false; - - public boolean send(Packet packet) { - HttpURLConnection urlConnection = null; - try { - urlConnection = (HttpURLConnection) new URL(packet.getTargetURL()).openConnection(); - - Timber.tag(TAG).v("Connection is open to %s", urlConnection.getURL().toExternalForm()); - Timber.tag(TAG).v("Sending: %s", packet); - - urlConnection.setConnectTimeout((int) mTimeout); - urlConnection.setReadTimeout((int) mTimeout); - - // IF there is json data we have to do a post - if (packet.getPostData() != null) { // POST - urlConnection.setDoOutput(true); // Forces post - urlConnection.setRequestProperty("Content-Type", "application/json"); - urlConnection.setRequestProperty("charset", "utf-8"); - - final String toPost = packet.getPostData().toString(); - if (mGzip) { - - urlConnection.addRequestProperty("Content-Encoding", "gzip"); - ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); - - try (GZIPOutputStream gzipStream = new GZIPOutputStream(byteArrayOS)) { - gzipStream.write(toPost.getBytes(StandardCharsets.UTF_8)); - } - // If closing fails we assume the written data to be invalid. - // Don't catch the exception and let it abort the `send(Packet)` call. - - OutputStream outputStream = null; - try { - outputStream = urlConnection.getOutputStream(); - outputStream.write(byteArrayOS.toByteArray()); - } finally { - if (outputStream != null) { - try { - outputStream.close(); - } catch (IOException e) { - // Failing to close the stream is not enough to consider the transmission faulty. - Timber.tag(TAG).d(e, "Failed to close output stream after writing gzipped POST data."); - } - } - } - - } else { - - BufferedWriter writer = null; - try { - writer = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream(), StandardCharsets.UTF_8)); - writer.write(toPost); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException e) { - // Failing to close the stream is not enough to consider the transmission faulty. - Timber.tag(TAG).d(e, "Failed to close output stream after writing POST data."); - } - } - } - - } - - } else { // GET - urlConnection.setDoOutput(false); // Defaults to false, but for readability - } - - int statusCode = urlConnection.getResponseCode(); - Timber.tag(TAG).v("Transmission finished (code=%d).", statusCode); - final boolean successful = checkResponseCode(statusCode); - - if (successful) { - - // https://github.com/matomo-org/matomo-sdk-android/issues/226 - InputStream is = urlConnection.getInputStream(); - if (is != null) { - try { - is.close(); - } catch (IOException e) { - Timber.tag(TAG).d(e, "Failed to close the error stream."); - } - } - - } else { - // Consume the error stream (or at least close it) if the statuscode was non-OK (not 2XX) - final StringBuilder errorReason = new StringBuilder(); - BufferedReader errorReader = null; - try { - errorReader = new BufferedReader(new InputStreamReader(urlConnection.getErrorStream())); - String line; - while ((line = errorReader.readLine()) != null) errorReason.append(line); - } finally { - if (errorReader != null) { - try { - errorReader.close(); - } catch (IOException e) { - Timber.tag(TAG).d(e, "Failed to close the error stream."); - } - } - } - Timber.tag(TAG).w("Transmission failed (code=%d, reason=%s)", statusCode, errorReason.toString()); - } - - return successful; - } catch (Exception e) { - Timber.tag(TAG).e(e, "Transmission failed unexpectedly."); - return false; - } finally { - if (urlConnection != null) urlConnection.disconnect(); - } - } - - @Override - public void setTimeout(long timeout) { - mTimeout = timeout; - } - - @Override - public void setGzipData(boolean gzip) { - mGzip = gzip; - } - - private static boolean checkResponseCode(int code) { - return code == HttpURLConnection.HTTP_NO_CONTENT || code == HttpURLConnection.HTTP_OK; - } -} diff --git a/tracker/src/main/java/org/matomo/sdk/dispatcher/DefaultPacketSender.kt b/tracker/src/main/java/org/matomo/sdk/dispatcher/DefaultPacketSender.kt new file mode 100644 index 00000000..7abddb23 --- /dev/null +++ b/tracker/src/main/java/org/matomo/sdk/dispatcher/DefaultPacketSender.kt @@ -0,0 +1,140 @@ +package org.matomo.sdk.dispatcher + +import org.matomo.sdk.Matomo.Companion.tag +import timber.log.Timber +import java.io.BufferedReader +import java.io.BufferedWriter +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStreamReader +import java.io.OutputStream +import java.io.OutputStreamWriter +import java.net.HttpURLConnection +import java.net.URL +import java.nio.charset.StandardCharsets +import java.util.zip.GZIPOutputStream + +class DefaultPacketSender : PacketSender { + private var mTimeout = Dispatcher.DEFAULT_CONNECTION_TIMEOUT.toLong() + private var mGzip = false + + override fun send(packet: Packet): Boolean { + var urlConnection: HttpURLConnection? = null + try { + urlConnection = URL(packet.targetURL).openConnection() as HttpURLConnection + + Timber.tag(TAG).v("Connection is open to %s", urlConnection.url.toExternalForm()) + Timber.tag(TAG).v("Sending: %s", packet) + + urlConnection.connectTimeout = mTimeout.toInt() + urlConnection.readTimeout = mTimeout.toInt() + + // IF there is json data we have to do a post + if (packet.postData != null) { // POST + urlConnection.doOutput = true // Forces post + urlConnection.setRequestProperty("Content-Type", "application/json") + urlConnection.setRequestProperty("charset", "utf-8") + + val toPost = packet.postData.toString() + if (mGzip) { + urlConnection.addRequestProperty("Content-Encoding", "gzip") + val byteArrayOS = ByteArrayOutputStream() + + GZIPOutputStream(byteArrayOS).use { gzipStream -> + gzipStream.write(toPost.toByteArray(StandardCharsets.UTF_8)) + } + // If closing fails we assume the written data to be invalid. + // Don't catch the exception and let it abort the `send(Packet)` call. + var outputStream: OutputStream? = null + try { + outputStream = urlConnection.outputStream + outputStream.write(byteArrayOS.toByteArray()) + } finally { + if (outputStream != null) { + try { + outputStream.close() + } catch (e: IOException) { + // Failing to close the stream is not enough to consider the transmission faulty. + Timber.tag(TAG).d(e, "Failed to close output stream after writing gzipped POST data.") + } + } + } + } else { + var writer: BufferedWriter? = null + try { + writer = BufferedWriter(OutputStreamWriter(urlConnection.outputStream, StandardCharsets.UTF_8)) + writer.write(toPost) + } finally { + if (writer != null) { + try { + writer.close() + } catch (e: IOException) { + // Failing to close the stream is not enough to consider the transmission faulty. + Timber.tag(TAG).d(e, "Failed to close output stream after writing POST data.") + } + } + } + } + } else { // GET + urlConnection.doOutput = false // Defaults to false, but for readability + } + + val statusCode = urlConnection.responseCode + Timber.tag(TAG).v("Transmission finished (code=%d).", statusCode) + val successful = checkResponseCode(statusCode) + + if (successful) { + // https://github.com/matomo-org/matomo-sdk-android/issues/226 + + val `is` = urlConnection.inputStream + if (`is` != null) { + try { + `is`.close() + } catch (e: IOException) { + Timber.tag(TAG).d(e, "Failed to close the error stream.") + } + } + } else { + // Consume the error stream (or at least close it) if the status code was non-OK (not 2XX) + val errorReason = StringBuilder() + var errorReader: BufferedReader? = null + try { + errorReader = BufferedReader(InputStreamReader(urlConnection.errorStream)) + var line: String? + while ((errorReader.readLine().also { line = it }) != null) errorReason.append(line) + } finally { + if (errorReader != null) { + try { + errorReader.close() + } catch (e: IOException) { + Timber.tag(TAG).d(e, "Failed to close the error stream.") + } + } + } + Timber.tag(TAG).w("Transmission failed (code=%d, reason=%s)", statusCode, errorReason.toString()) + } + + return successful + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Transmission failed unexpectedly.") + return false + } finally { + urlConnection?.disconnect() + } + } + + override fun setTimeout(timeout: Long) { + mTimeout = timeout + } + + override fun setGzipData(gzip: Boolean) { + mGzip = gzip + } + + companion object { + private val TAG = tag(DefaultPacketSender::class.java) + private fun checkResponseCode(code: Int): Boolean { + return code == HttpURLConnection.HTTP_NO_CONTENT || code == HttpURLConnection.HTTP_OK + } + } +} diff --git a/tracker/src/main/java/org/matomo/sdk/dispatcher/DispatcherFactory.java b/tracker/src/main/java/org/matomo/sdk/dispatcher/DispatcherFactory.java deleted file mode 100644 index 74af252e..00000000 --- a/tracker/src/main/java/org/matomo/sdk/dispatcher/DispatcherFactory.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.matomo.sdk.dispatcher; - -import org.matomo.sdk.Tracker; - -public interface DispatcherFactory { - Dispatcher build(Tracker tracker); -} diff --git a/tracker/src/main/java/org/matomo/sdk/dispatcher/DispatcherFactory.kt b/tracker/src/main/java/org/matomo/sdk/dispatcher/DispatcherFactory.kt new file mode 100644 index 00000000..303bb729 --- /dev/null +++ b/tracker/src/main/java/org/matomo/sdk/dispatcher/DispatcherFactory.kt @@ -0,0 +1,7 @@ +package org.matomo.sdk.dispatcher + +import org.matomo.sdk.Tracker + +interface DispatcherFactory { + fun build(tracker: Tracker?): Dispatcher? +} diff --git a/tracker/src/main/java/org/matomo/sdk/dispatcher/PacketSender.java b/tracker/src/main/java/org/matomo/sdk/dispatcher/PacketSender.java deleted file mode 100644 index c5061ed1..00000000 --- a/tracker/src/main/java/org/matomo/sdk/dispatcher/PacketSender.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.matomo.sdk.dispatcher; - - -public interface PacketSender { - /** - * @return true if successful - */ - boolean send(Packet packet); - - /** - * @param timeout in milliseconds - */ - void setTimeout(long timeout); - - void setGzipData(boolean gzip); -} diff --git a/tracker/src/main/java/org/matomo/sdk/dispatcher/PacketSender.kt b/tracker/src/main/java/org/matomo/sdk/dispatcher/PacketSender.kt new file mode 100644 index 00000000..5b9ba5cb --- /dev/null +++ b/tracker/src/main/java/org/matomo/sdk/dispatcher/PacketSender.kt @@ -0,0 +1,16 @@ +package org.matomo.sdk.dispatcher + + +interface PacketSender { + /** + * @return true if successful + */ + fun send(packet: Packet?): Boolean + + /** + * @param timeout in milliseconds + */ + fun setTimeout(timeout: Long) + + fun setGzipData(gzip: Boolean) +} diff --git a/tracker/src/main/java/org/matomo/sdk/tools/UrlHelper.java b/tracker/src/main/java/org/matomo/sdk/tools/UrlHelper.java index d9014f1a..d7a90e88 100644 --- a/tracker/src/main/java/org/matomo/sdk/tools/UrlHelper.java +++ b/tracker/src/main/java/org/matomo/sdk/tools/UrlHelper.java @@ -34,7 +34,7 @@ public class UrlHelper { public static List> parse(@NonNull final URI uri, @Nullable final String encoding) { List> result = Collections.emptyList(); final String query = uri.getRawQuery(); - if (query != null && query.length() > 0) { + if (query != null && !query.isEmpty()) { result = new ArrayList<>(); parse(result, new Scanner(query), encoding); }