From 06b794dcea806150770fb88d43e366a3496a9d0f Mon Sep 17 00:00:00 2001 From: lucko Date: Sat, 28 Jan 2023 11:07:45 +0000 Subject: Stream live data to the viewer using WebSockets (#294) --- .../me/lucko/spark/common/util/BytebinClient.java | 14 ++- .../me/lucko/spark/common/util/Configuration.java | 27 +++++ .../me/lucko/spark/common/util/MediaTypes.java | 29 +++++ .../spark/common/util/ws/BytesocksClient.java | 118 +++++++++++++++++++++ .../spark/common/util/ws/BytesocksClientImpl.java | 40 +++++++ 5 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 spark-common/src/main/java/me/lucko/spark/common/util/MediaTypes.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClient.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClientImpl.java (limited to 'spark-common/src/main/java/me/lucko/spark/common/util') diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java b/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java index e69b94e..8f11edc 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java @@ -32,6 +32,8 @@ import java.util.zip.GZIPOutputStream; /** * Utility for posting content to bytebin. + * + * @see https://github.com/lucko/bytebin */ public class BytebinClient { @@ -45,7 +47,7 @@ public class BytebinClient { this.userAgent = userAgent; } - private Content postContent(String contentType, Consumer consumer) throws IOException { + private Content postContent(String contentType, Consumer consumer, String userAgent) throws IOException { URL url = new URL(this.url + "post"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); try { @@ -55,7 +57,7 @@ public class BytebinClient { connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", contentType); - connection.setRequestProperty("User-Agent", this.userAgent); + connection.setRequestProperty("User-Agent", userAgent); connection.setRequestProperty("Content-Encoding", "gzip"); connection.connect(); @@ -74,14 +76,18 @@ public class BytebinClient { } } - public Content postContent(AbstractMessageLite proto, String contentType) throws IOException { + public Content postContent(AbstractMessageLite proto, String contentType, String userAgentExtra) throws IOException { return postContent(contentType, outputStream -> { try (OutputStream out = new GZIPOutputStream(outputStream)) { proto.writeTo(out); } catch (IOException e) { throw new RuntimeException(e); } - }); + }, this.userAgent + "/" + userAgentExtra); + } + + public Content postContent(AbstractMessageLite proto, String contentType) throws IOException { + return postContent(proto, contentType, this.userAgent); } public static final class Content { diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/Configuration.java b/spark-common/src/main/java/me/lucko/spark/common/util/Configuration.java index 32f3bc6..d19ba64 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/Configuration.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/Configuration.java @@ -22,6 +22,7 @@ package me.lucko.spark.common.util; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; @@ -32,6 +33,9 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public final class Configuration { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); @@ -103,6 +107,21 @@ public final class Configuration { return val.isBoolean() ? val.getAsInt() : def; } + public List getStringList(String path) { + JsonElement el = this.root.get(path); + if (el == null || !el.isJsonArray()) { + return Collections.emptyList(); + } + + List list = new ArrayList<>(); + for (JsonElement child : el.getAsJsonArray()) { + if (child.isJsonPrimitive()) { + list.add(child.getAsJsonPrimitive().getAsString()); + } + } + return list; + } + public void setString(String path, String value) { this.root.add(path, new JsonPrimitive(value)); } @@ -115,6 +134,14 @@ public final class Configuration { this.root.add(path, new JsonPrimitive(value)); } + public void setStringList(String path, List value) { + JsonArray array = new JsonArray(); + for (String str : value) { + array.add(str); + } + this.root.add(path, array); + } + public boolean contains(String path) { return this.root.has(path); } diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/MediaTypes.java b/spark-common/src/main/java/me/lucko/spark/common/util/MediaTypes.java new file mode 100644 index 0000000..2c49540 --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/util/MediaTypes.java @@ -0,0 +1,29 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package me.lucko.spark.common.util; + +public enum MediaTypes { + ; + + public static final String SPARK_SAMPLER_MEDIA_TYPE = "application/x-spark-sampler"; + public static final String SPARK_HEAP_MEDIA_TYPE = "application/x-spark-heap"; + +} diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClient.java b/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClient.java new file mode 100644 index 0000000..1db7a67 --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClient.java @@ -0,0 +1,118 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package me.lucko.spark.common.util.ws; + +import java.util.concurrent.CompletableFuture; + +/** + * A client that can interact with bytesocks. + * + * @see https://github.com/lucko/bytesocks + */ +public interface BytesocksClient { + + /** + * Creates a new {@link BytesocksClient}. + * + *

Returns {@code null} on Java versions before 11.

+ * + * @param host the host + * @param userAgent the user agent + * @return the client + */ + static BytesocksClient create(String host, String userAgent) { + try { + return new BytesocksClientImpl(host, userAgent); + } catch (UnsupportedOperationException e) { + return null; + } + } + + /** + * Creates a new bytesocks channel and returns a socket connected to it. + * + * @param listener the listener + * @return the socket + * @throws Exception if something goes wrong + */ + Socket createAndConnect(Listener listener) throws Exception; + + /** + * Connects to an existing bytesocks channel. + * + * @param channelId the channel id + * @param listener the listener + * @return the socket + * @throws Exception if something goes wrong + */ + Socket connect(String channelId, Listener listener) throws Exception; + + /** + * A socket connected to a bytesocks channel. + */ + interface Socket { + + /** + * Gets the id of the connected channel. + * + * @return the id of the channel + */ + String getChannelId(); + + /** + * Gets if the socket is open. + * + * @return true if the socket is open + */ + boolean isOpen(); + + /** + * Sends a message to the channel using the socket. + * + * @param msg the message to send + * @return a future to encapsulate the progress of sending the message + */ + CompletableFuture send(CharSequence msg); + + /** + * Closes the socket. + * + * @param statusCode the status code + * @param reason the reason + */ + void close(int statusCode, String reason); + } + + /** + * Socket listener + */ + interface Listener { + + default void onOpen() {} + + default void onError(Throwable error) {} + + default void onText(CharSequence data) {} + + default void onClose(int statusCode, String reason) {} + } + +} diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClientImpl.java b/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClientImpl.java new file mode 100644 index 0000000..cf1489c --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClientImpl.java @@ -0,0 +1,40 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package me.lucko.spark.common.util.ws; + +// Overridden by java 11 source set + +class BytesocksClientImpl implements BytesocksClient { + + BytesocksClientImpl(String host, String userAgent) { + throw new UnsupportedOperationException(); + } + + @Override + public Socket createAndConnect(Listener listener) throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + public Socket connect(String channelId, Listener listener) throws Exception { + throw new UnsupportedOperationException(); + } +} -- cgit From f5fd47e225b5e887ea7fbccf5bfa5a927be35619 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 28 Jan 2023 17:21:42 +0000 Subject: Fix bytebin user agent --- .../main/java/me/lucko/spark/common/util/BytebinClient.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/util') diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java b/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java index 8f11edc..b8a2053 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java @@ -47,7 +47,11 @@ public class BytebinClient { this.userAgent = userAgent; } - private Content postContent(String contentType, Consumer consumer, String userAgent) throws IOException { + private Content postContent(String contentType, Consumer consumer, String userAgentExtra) throws IOException { + String userAgent = userAgentExtra != null + ? this.userAgent + "/" + userAgentExtra + : this.userAgent; + URL url = new URL(this.url + "post"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); try { @@ -83,11 +87,11 @@ public class BytebinClient { } catch (IOException e) { throw new RuntimeException(e); } - }, this.userAgent + "/" + userAgentExtra); + }, userAgentExtra); } public Content postContent(AbstractMessageLite proto, String contentType) throws IOException { - return postContent(proto, contentType, this.userAgent); + return postContent(proto, contentType, null); } public static final class Content { -- cgit From 6ed41df89e109ef1bb8eef655e5d07843aca22e0 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 12 Feb 2023 13:02:08 +0000 Subject: Fix NPE in ClassFinder (#287) --- spark-common/src/main/java/me/lucko/spark/common/util/ClassFinder.java | 3 +++ 1 file changed, 3 insertions(+) (limited to 'spark-common/src/main/java/me/lucko/spark/common/util') diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/ClassFinder.java b/spark-common/src/main/java/me/lucko/spark/common/util/ClassFinder.java index 4481786..f132613 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/ClassFinder.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/ClassFinder.java @@ -44,6 +44,9 @@ public class ClassFinder { } catch (Exception e) { return; } + if (instrumentation == null) { + return; + } // obtain and cache loaded classes for (Class loadedClass : instrumentation.getAllLoadedClasses()) { -- cgit From 0f30d2983ec6ef487fd1966c1c22fa4a73e081f9 Mon Sep 17 00:00:00 2001 From: Luck Date: Tue, 7 Mar 2023 21:54:14 +0000 Subject: Don't use multi-release jar for websocket code --- .../spark/common/util/ws/BytesocksClient.java | 118 --------------------- .../spark/common/util/ws/BytesocksClientImpl.java | 40 ------- 2 files changed, 158 deletions(-) delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClient.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClientImpl.java (limited to 'spark-common/src/main/java/me/lucko/spark/common/util') diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClient.java b/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClient.java deleted file mode 100644 index 1db7a67..0000000 --- a/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClient.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of spark. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package me.lucko.spark.common.util.ws; - -import java.util.concurrent.CompletableFuture; - -/** - * A client that can interact with bytesocks. - * - * @see https://github.com/lucko/bytesocks - */ -public interface BytesocksClient { - - /** - * Creates a new {@link BytesocksClient}. - * - *

Returns {@code null} on Java versions before 11.

- * - * @param host the host - * @param userAgent the user agent - * @return the client - */ - static BytesocksClient create(String host, String userAgent) { - try { - return new BytesocksClientImpl(host, userAgent); - } catch (UnsupportedOperationException e) { - return null; - } - } - - /** - * Creates a new bytesocks channel and returns a socket connected to it. - * - * @param listener the listener - * @return the socket - * @throws Exception if something goes wrong - */ - Socket createAndConnect(Listener listener) throws Exception; - - /** - * Connects to an existing bytesocks channel. - * - * @param channelId the channel id - * @param listener the listener - * @return the socket - * @throws Exception if something goes wrong - */ - Socket connect(String channelId, Listener listener) throws Exception; - - /** - * A socket connected to a bytesocks channel. - */ - interface Socket { - - /** - * Gets the id of the connected channel. - * - * @return the id of the channel - */ - String getChannelId(); - - /** - * Gets if the socket is open. - * - * @return true if the socket is open - */ - boolean isOpen(); - - /** - * Sends a message to the channel using the socket. - * - * @param msg the message to send - * @return a future to encapsulate the progress of sending the message - */ - CompletableFuture send(CharSequence msg); - - /** - * Closes the socket. - * - * @param statusCode the status code - * @param reason the reason - */ - void close(int statusCode, String reason); - } - - /** - * Socket listener - */ - interface Listener { - - default void onOpen() {} - - default void onError(Throwable error) {} - - default void onText(CharSequence data) {} - - default void onClose(int statusCode, String reason) {} - } - -} diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClientImpl.java b/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClientImpl.java deleted file mode 100644 index cf1489c..0000000 --- a/spark-common/src/main/java/me/lucko/spark/common/util/ws/BytesocksClientImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of spark. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package me.lucko.spark.common.util.ws; - -// Overridden by java 11 source set - -class BytesocksClientImpl implements BytesocksClient { - - BytesocksClientImpl(String host, String userAgent) { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createAndConnect(Listener listener) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - public Socket connect(String channelId, Listener listener) throws Exception { - throw new UnsupportedOperationException(); - } -} -- cgit From 6cdd78a0c57d751d3a44b319703c20b034f8d675 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 19 Mar 2023 13:24:13 +0000 Subject: Tidy up thread factories and async sampler regex thread filter --- .../me/lucko/spark/common/util/SparkThreadFactory.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/util') diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/SparkThreadFactory.java b/spark-common/src/main/java/me/lucko/spark/common/util/SparkThreadFactory.java index 156fa0d..42dca12 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/SparkThreadFactory.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/SparkThreadFactory.java @@ -23,7 +23,13 @@ package me.lucko.spark.common.util; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; -public class SparkThreadFactory implements ThreadFactory, Thread.UncaughtExceptionHandler { +public class SparkThreadFactory implements ThreadFactory { + + public static final Thread.UncaughtExceptionHandler EXCEPTION_HANDLER = (t, e) -> { + System.err.println("Uncaught exception thrown by thread " + t.getName()); + e.printStackTrace(); + }; + private static final AtomicInteger poolNumber = new AtomicInteger(1); private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; @@ -36,14 +42,9 @@ public class SparkThreadFactory implements ThreadFactory, Thread.UncaughtExcepti public Thread newThread(Runnable r) { Thread t = new Thread(r, this.namePrefix + this.threadNumber.getAndIncrement()); - t.setUncaughtExceptionHandler(this); + t.setUncaughtExceptionHandler(EXCEPTION_HANDLER); t.setDaemon(true); return t; } - @Override - public void uncaughtException(Thread t, Throwable e) { - System.err.println("Uncaught exception thrown by thread " + t.getName()); - e.printStackTrace(); - } } -- cgit From a70ccb394839c63f13b3e6ff5539c0a042925d2f Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 19 Mar 2023 19:35:29 +0000 Subject: Allow platforms to provide their own TPS/MSPT calculations --- .../main/java/me/lucko/spark/common/util/RollingAverage.java | 11 ----------- .../java/me/lucko/spark/common/util/StatisticFormatter.java | 4 +++- 2 files changed, 3 insertions(+), 12 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/util') diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java b/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java index 65753bc..57dfdff 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java @@ -21,7 +21,6 @@ package me.lucko.spark.common.util; import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; -import me.lucko.spark.proto.SparkProtos; import java.math.BigDecimal; import java.math.RoundingMode; @@ -112,14 +111,4 @@ public class RollingAverage implements DoubleAverageInfo { return sortedSamples[rank].doubleValue(); } - public SparkProtos.RollingAverageValues toProto() { - return SparkProtos.RollingAverageValues.newBuilder() - .setMean(mean()) - .setMax(max()) - .setMin(min()) - .setMedian(median()) - .setPercentile95(percentile95th()) - .build(); - } - } diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/StatisticFormatter.java b/spark-common/src/main/java/me/lucko/spark/common/util/StatisticFormatter.java index 22ee9bb..b488f50 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/util/StatisticFormatter.java +++ b/spark-common/src/main/java/me/lucko/spark/common/util/StatisticFormatter.java @@ -22,6 +22,8 @@ package me.lucko.spark.common.util; import com.google.common.base.Strings; +import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; + import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.TextColor; @@ -55,7 +57,7 @@ public enum StatisticFormatter { return text((tps > 20.0 ? "*" : "") + Math.min(Math.round(tps * 100.0) / 100.0, 20.0), color); } - public static TextComponent formatTickDurations(RollingAverage average) { + public static TextComponent formatTickDurations(DoubleAverageInfo average) { return text() .append(formatTickDuration(average.min())) .append(text('/', GRAY)) -- cgit