diff options
Diffstat (limited to 'src')
17 files changed, 218 insertions, 355 deletions
diff --git a/src/main/java/com/keuin/blame/Blame.java b/src/main/java/com/keuin/blame/Blame.java index 91da4fd..23a1830 100644 --- a/src/main/java/com/keuin/blame/Blame.java +++ b/src/main/java/com/keuin/blame/Blame.java @@ -8,8 +8,7 @@ import com.keuin.blame.command.BlameBlockCommand; import com.keuin.blame.command.BlameLimitCommand; import com.keuin.blame.command.BlameStatCommand; import com.keuin.blame.config.BlameConfig; -import com.keuin.blame.lookup.LookupManager; -import com.keuin.blame.util.DatabaseUtil; +import com.keuin.blame.lookup.QueryExecutor; import com.keuin.blame.util.PrintUtil; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.LongArgumentType; @@ -39,6 +38,8 @@ public class Blame implements ModInitializer { public static BlameConfig config; + public static QueryExecutor queryExecutor; + public static boolean loadConfig() { String configFileName = "blame.json"; try { @@ -69,14 +70,14 @@ public class Blame implements ModInitializer { if (!loadConfig()) return; - logger.info("Register event hooks..."); + queryExecutor = new QueryExecutor(); - DatabaseUtil.disableMongoSpamming(); + logger.info("Register event hooks..."); // hook disable event ServerLifecycleEvents.SERVER_STOPPING.register(minecraftServer -> { logger.info("Stopping LookupManager..."); - LookupManager.INSTANCE.stop(); +// LookupManager.INSTANCE.stop(); logger.info("Stopping SubmitWorker..."); SubmitWorker.INSTANCE.stop(); diff --git a/src/main/java/com/keuin/blame/SubmitWorker.java b/src/main/java/com/keuin/blame/SubmitWorker.java index ba4a922..2bca5e5 100644 --- a/src/main/java/com/keuin/blame/SubmitWorker.java +++ b/src/main/java/com/keuin/blame/SubmitWorker.java @@ -1,7 +1,9 @@ package com.keuin.blame; -import com.clickhouse.client.*; -import com.clickhouse.client.config.ClickHouseClientOption; +import com.clickhouse.client.ClickHouseClient; +import com.clickhouse.client.ClickHouseException; +import com.clickhouse.client.ClickHouseRequest; +import com.clickhouse.client.ClickHouseResponse; import com.clickhouse.data.ClickHouseDataStreamFactory; import com.clickhouse.data.ClickHouseFormat; import com.keuin.blame.data.entry.LogEntry; @@ -16,8 +18,6 @@ import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; -import static com.clickhouse.client.ClickHouseCredentials.fromUserAndPassword; - public class SubmitWorker { public static final SubmitWorker INSTANCE = new SubmitWorker(); @@ -117,29 +117,19 @@ public class SubmitWorker { } private void run() { - var config = DatabaseUtil.DB_CONFIG; - logger.info("Database: " + config.toString()); - var server = ClickHouseNode.builder() - .host(config.address()) - .port(ClickHouseProtocol.HTTP, config.port()) - // .port(ClickHouseProtocol.GRPC, Integer.getInteger("chPort", 9100)) - // .port(ClickHouseProtocol.TCP, Integer.getInteger("chPort", 9000)) - .database(config.database()) - .credentials(fromUserAndPassword(config.username(), config.password())) - .build(); - + var server = DatabaseUtil.getServer(); var batchBuffer = new ArrayList<LogEntry>(batchSize); boolean writeImmediately = false; workLoop: while (true) { - try (var client = ClickHouseClient.newInstance(server.getProtocol())) { + try (var client = DatabaseUtil.getClient(server)) { writeLoop: while (true) { var req = client.read(server).write() - .table(config.table()) + .table(DatabaseUtil.DB_CONFIG.table()) .format(ClickHouseFormat.RowBinary) // .option(ClickHouseClientOption.ASYNC, false) - .option(ClickHouseClientOption.COMPRESS, false); + ; var result = work(client, req, batchBuffer, writeImmediately); switch (result) { case CONTINUE -> { diff --git a/src/main/java/com/keuin/blame/command/BlameBlockCommand.java b/src/main/java/com/keuin/blame/command/BlameBlockCommand.java index dc54487..695f9ad 100644 --- a/src/main/java/com/keuin/blame/command/BlameBlockCommand.java +++ b/src/main/java/com/keuin/blame/command/BlameBlockCommand.java @@ -1,10 +1,7 @@ package com.keuin.blame.command; -import com.keuin.blame.data.WorldPos; -import com.keuin.blame.data.entry.LogEntry; -import com.keuin.blame.lookup.*; +import com.keuin.blame.Blame; import com.keuin.blame.util.MinecraftUtil; -import com.keuin.blame.util.PrettyUtil; import com.keuin.blame.util.PrintUtil; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.LongArgumentType; @@ -15,7 +12,6 @@ import net.minecraft.command.argument.BlockPosArgumentType; import net.minecraft.entity.Entity; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; @@ -99,20 +95,8 @@ public class BlameBlockCommand { amountLimit = BlameLimitCommand.DEFAULT_LOOKUP_LIMIT; } -// String world = MinecraftUtil.worldToString(playerEntity.world); - WorldPos blockPos = new WorldPos(world, x, y, z); - AbstractLookupFilter filter; - if (timeRange >= 0) { - filter = LookupFilters.compoundedFilter(new TimeLookupFilter(timeRange), new BlockPosLookupFilter(blockPos)); - } else { - filter = new BlockPosLookupFilter(blockPos); - } - - LookupManager.INSTANCE.lookup( - filter, - new Callback(context), - amountLimit - ); + Blame.queryExecutor.byBlockPos(world, x, y, z, (msg) -> + context.getSource().sendFeedback(msg, false)); return SUCCESS; } @@ -122,42 +106,4 @@ public class BlameBlockCommand { return SUCCESS; } - private static class Callback implements LookupCallback { - - private final CommandContext<ServerCommandSource> context; - - private Callback(CommandContext<ServerCommandSource> context) { - this.context = context; - } - - @Override - public void onLookupFinishes(Iterable<LogEntry> logEntries) { - int printCount = 0; - PrintUtil.Printer printer = PrintUtil.newPrinter(); - boolean isFirst = true; - for (LogEntry logEntry : logEntries) { - if (!isFirst) - printer.newline(); - printer.append(Formatting.YELLOW, "Time: ", PrettyUtil.timestampToString(logEntry.timeMillis)).newline() - .append(Formatting.YELLOW, "Subject: ", Formatting.AQUA, logEntry.subjectId, "{", logEntry.subjectUUID, "} @ ", logEntry.subjectPos).newline() - .append(Formatting.YELLOW, "Action: ", Formatting.AQUA, logEntry.actionType).newline() - .append(Formatting.YELLOW, "Object: ", Formatting.AQUA, logEntry.objectType, "[", logEntry.objectId, "] @ ", logEntry.objectPos).newline() - .append(Formatting.YELLOW, "Log version: ", logEntry.version).newline() - .append(Formatting.YELLOW, "Game version: ", logEntry.gameVersion).newline() - .append("================"); - ++printCount; - isFirst = false; - } - if (printCount > 0) { - printer.sendTo(context); - PrintUtil.message(context, - "Showed " + printCount + " event items. ", - Formatting.ITALIC, - "Use `/blame limit` to change print count limit."); - } else { - PrintUtil.message(context, "No logs found."); - } - } - } - } diff --git a/src/main/java/com/keuin/blame/command/BlameStatCommand.java b/src/main/java/com/keuin/blame/command/BlameStatCommand.java index 295ef01..34c3faf 100644 --- a/src/main/java/com/keuin/blame/command/BlameStatCommand.java +++ b/src/main/java/com/keuin/blame/command/BlameStatCommand.java @@ -2,15 +2,9 @@ package com.keuin.blame.command; import com.google.common.base.Strings; import com.keuin.blame.data.entry.LogEntry; -import com.keuin.blame.data.helper.VersionedLogEntryHelper; import com.keuin.blame.util.DatabaseUtil; import com.keuin.blame.util.PrintUtil; import com.mojang.brigadier.context.CommandContext; -import com.mongodb.MongoClientException; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.Formatting; import org.jetbrains.annotations.Nullable; @@ -52,29 +46,29 @@ public class BlameStatCommand { @Override public void run() { logger.info("Collecting statistics..."); - try (final MongoClient mongoClient = MongoClients.create(DatabaseUtil.CLIENT_SETTINGS)) { - final MongoDatabase db = mongoClient.getDatabase( - DatabaseUtil.DB_CONFIG.getDatabaseName() - ); - final MongoCollection<LogEntry> collection = db.getCollection( - DatabaseUtil.DB_CONFIG.getTableName(), LogEntry.class - ); - Collection<String> ids = VersionedLogEntryHelper.getLoggedSubjectsId(collection); - - // count by distinct subjects - Map<String, Long> countMap = new HashMap<>(); - for (String subjectId : ids) { - long count = VersionedLogEntryHelper.countBySubjectId(collection, subjectId); - countMap.put(subjectId, count); - } - - // invoke callback - callback.showStat(new BlameStat(countMap)); - } catch (MongoClientException exception) { - logger.severe("Failed when querying the database: " + exception + - ". Failed to get statistics."); - callback.showStat(null); - } +// try (final MongoClient mongoClient = MongoClients.create(DatabaseUtil.CLIENT_SETTINGS)) { +// final MongoDatabase db = mongoClient.getDatabase( +// DatabaseUtil.DB_CONFIG.getDatabaseName() +// ); +// final MongoCollection<LogEntry> collection = db.getCollection( +// DatabaseUtil.DB_CONFIG.getTableName(), LogEntry.class +// ); +// Collection<String> ids = VersionedLogEntryHelper.getLoggedSubjectsId(collection); +// +// // count by distinct subjects +// Map<String, Long> countMap = new HashMap<>(); +// for (String subjectId : ids) { +// long count = VersionedLogEntryHelper.countBySubjectId(collection, subjectId); +// countMap.put(subjectId, count); +// } +// +// // invoke callback +// callback.showStat(new BlameStat(countMap)); +// } catch (MongoClientException exception) { +// logger.severe("Failed when querying the database: " + exception + +// ". Failed to get statistics."); +// callback.showStat(null); +// } } }, "BlameStatCommandThread").start(); } diff --git a/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java b/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java deleted file mode 100644 index 064ae87..0000000 --- a/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.keuin.blame.data.helper; - -import com.keuin.blame.data.entry.LogEntry; -import com.keuin.blame.data.entry.LogEntryNames; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.Filters; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class VersionedLogEntryHelper { - public static Collection<String> getLoggedSubjectsId(MongoCollection<LogEntry> collection) { - List<String> list = new ArrayList<>(); - for (String s : collection.distinct(LogEntryNames.SUBJECT_ID, String.class)) - list.add(s); - return list; - } - public static long countBySubjectId(MongoCollection<LogEntry> collection, String subjectId) { - return collection.countDocuments(Filters.eq(LogEntryNames.SUBJECT_ID, subjectId)); - } -} diff --git a/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java b/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java deleted file mode 100644 index 7b667bb..0000000 --- a/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.keuin.blame.lookup; - -import com.keuin.blame.data.WorldPos; -import com.keuin.blame.data.entry.LogEntryNames; -import com.mongodb.client.model.Filters; -import org.bson.conversions.Bson; - -public class BlockPosLookupFilter implements AbstractLookupFilter { - private final WorldPos blockPos; - - public BlockPosLookupFilter(WorldPos blockPos) { - this.blockPos = blockPos; - } - - @Override - public Bson filter() { - return Filters.and( - Filters.eq(LogEntryNames.VERSION, 1), - Filters.eq(LogEntryNames.OBJECT_POS, blockPos) - ); - } -} diff --git a/src/main/java/com/keuin/blame/lookup/DummyFilter.java b/src/main/java/com/keuin/blame/lookup/DummyFilter.java deleted file mode 100644 index 55b0283..0000000 --- a/src/main/java/com/keuin/blame/lookup/DummyFilter.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.keuin.blame.lookup; - -import org.bson.BsonDocument; -import org.bson.conversions.Bson; - -public class DummyFilter implements AbstractLookupFilter { - - @Override - public Bson filter() { - return new BsonDocument(); - } -} diff --git a/src/main/java/com/keuin/blame/lookup/LookupCallback.java b/src/main/java/com/keuin/blame/lookup/LookupCallback.java deleted file mode 100644 index 3c203b2..0000000 --- a/src/main/java/com/keuin/blame/lookup/LookupCallback.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.keuin.blame.lookup; - -import com.keuin.blame.data.entry.LogEntry; - -public interface LookupCallback { - void onLookupFinishes(Iterable<LogEntry> logEntries); -} diff --git a/src/main/java/com/keuin/blame/lookup/LookupFilterWithCallback.java b/src/main/java/com/keuin/blame/lookup/LookupFilterWithCallback.java deleted file mode 100644 index f2b07cb..0000000 --- a/src/main/java/com/keuin/blame/lookup/LookupFilterWithCallback.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.keuin.blame.lookup; - -import java.util.Objects; - -class LookupFilterWithCallback { - - private final LookupCallback callback; - private final AbstractLookupFilter filter; - private final int limit; - - LookupFilterWithCallback(LookupCallback callback, AbstractLookupFilter filter, int limit) { - if (callback == null) - throw new IllegalArgumentException("callback cannot be null"); - if (filter == null) - throw new IllegalArgumentException("filter cannot be null"); - if (limit <= 0) - throw new IllegalArgumentException("limit must be positive"); - this.callback = callback; - this.filter = filter; - this.limit = limit; - } - - public LookupCallback getCallback() { - return callback; - } - - public AbstractLookupFilter getFilter() { - return filter; - } - - public int getLimit() { - return limit; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - LookupFilterWithCallback that = (LookupFilterWithCallback) o; - return limit == that.limit && - callback.equals(that.callback) && - filter.equals(that.filter); - } - - @Override - public int hashCode() { - return Objects.hash(callback, filter, limit); - } -} diff --git a/src/main/java/com/keuin/blame/lookup/LookupFilters.java b/src/main/java/com/keuin/blame/lookup/LookupFilters.java deleted file mode 100644 index 2f24138..0000000 --- a/src/main/java/com/keuin/blame/lookup/LookupFilters.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.keuin.blame.lookup; - -import com.mongodb.client.model.Filters; -import org.bson.conversions.Bson; - -import java.util.ArrayList; -import java.util.List; - -public class LookupFilters { - public static AbstractLookupFilter compoundedFilter(AbstractLookupFilter... filters) { - return () -> { - List<Bson> list = new ArrayList<>(); - for (AbstractLookupFilter filter : filters) { - Bson bson = filter.filter(); - list.add(bson); - } - return Filters.and(list); - }; - } -} diff --git a/src/main/java/com/keuin/blame/lookup/LookupManager.java b/src/main/java/com/keuin/blame/lookup/LookupManager.java deleted file mode 100644 index 5574dc5..0000000 --- a/src/main/java/com/keuin/blame/lookup/LookupManager.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.keuin.blame.lookup; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingDeque; - -public class LookupManager { - - public static final LookupManager INSTANCE = new LookupManager(); - - private final BlockingQueue<LookupFilterWithCallback> queue = new LinkedBlockingDeque<>(); - private final List<LookupWorker> workers = new ArrayList<>(); - - private LookupManager() { - // initialize workers - for (int i = 0; i < 10; ++i) { - LookupWorker worker = new LookupWorker(i, queue); - worker.start(); - workers.add(worker); - } - } - - public void stop() { - workers.forEach(LookupWorker::disable); - } - - public void lookup(AbstractLookupFilter filter, LookupCallback callback, int lookUpLimit) { - if (lookUpLimit <= 0) - throw new IllegalArgumentException("lookup limit must be positive"); - queue.add(new LookupFilterWithCallback(callback, filter, lookUpLimit)); - } - -} diff --git a/src/main/java/com/keuin/blame/lookup/LookupWorker.java b/src/main/java/com/keuin/blame/lookup/LookupWorker.java deleted file mode 100644 index 3ce21b8..0000000 --- a/src/main/java/com/keuin/blame/lookup/LookupWorker.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.keuin.blame.lookup; - -import com.keuin.blame.data.entry.LogEntry; -import com.keuin.blame.data.entry.LogEntryNames; -import com.keuin.blame.util.DatabaseUtil; -import com.mongodb.client.*; -import com.mongodb.client.model.Sorts; - -import java.util.concurrent.BlockingQueue; -import java.util.logging.Logger; - -import static com.keuin.blame.util.DatabaseUtil.CLIENT_SETTINGS; - -public class LookupWorker extends Thread { - - private final Logger logger; - private final BlockingQueue<LookupFilterWithCallback> queue; - private boolean running = true; - - public LookupWorker(int id, BlockingQueue<LookupFilterWithCallback> queue) { - this.queue = queue; - this.logger = Logger.getLogger(String.format("LookupWorker-%d", id)); - } - - public void disable() { - interrupt(); - running = false; - } - - @Override - public void run() { - try (final MongoClient mongoClient = MongoClients.create(CLIENT_SETTINGS)) { - final MongoDatabase db = mongoClient.getDatabase( - DatabaseUtil.DB_CONFIG.getDatabaseName() - ); - final MongoCollection<LogEntry> collection = db.getCollection( - DatabaseUtil.DB_CONFIG.getTableName(), LogEntry.class - ); - long time; - while (running) { - LookupFilterWithCallback item = queue.take(); - LookupCallback callback = item.getCallback(); - AbstractLookupFilter filter = item.getFilter(); - - time = System.currentTimeMillis(); - FindIterable<LogEntry> find = collection - .find() - .filter(filter.filter()) - .sort(Sorts.descending(LogEntryNames.TIMESTAMP_MILLIS)) - .limit(item.getLimit()); -// FindIterable<LogEntry> find = collection.find();//.sort(Sorts.descending("timestamp_millis")); - time = System.currentTimeMillis() - time; - logger.info(String.format("Lookup finished in %d ms.", time)); - callback.onLookupFinishes(find); - } - } catch (InterruptedException e) { - logger.info("Interrupted. Quitting..."); - } - } -} diff --git a/src/main/java/com/keuin/blame/lookup/QueryExecutor.java b/src/main/java/com/keuin/blame/lookup/QueryExecutor.java new file mode 100644 index 0000000..f0dae9f --- /dev/null +++ b/src/main/java/com/keuin/blame/lookup/QueryExecutor.java @@ -0,0 +1,61 @@ +package com.keuin.blame.lookup; + +import com.clickhouse.client.ClickHouseParameterizedQuery; +import com.clickhouse.data.ClickHouseFormat; +import com.clickhouse.data.value.ClickHouseLongValue; +import com.clickhouse.data.value.ClickHouseStringValue; +import com.keuin.blame.util.DatabaseUtil; +import com.keuin.blame.util.TablePrinter; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.function.Consumer; + +public class QueryExecutor { + + private final Logger logger = LogManager.getLogger(); + + public void byBlockPos(String world, long x, long y, long z, Consumer<Text> callback) { + var server = DatabaseUtil.getServer(); + var stmt = ClickHouseParameterizedQuery.of(server.getConfig(), + "select subject_id, object_id, action_type, fromUnixTimestamp(toUInt64(ts/1000)) " + + "from :table where subject_world=:world and object_x=:x and object_y=:y and object_z=:z"); + try (var client = DatabaseUtil.getClient(server)) { + var resp = client.read(server) + .format(ClickHouseFormat.RowBinaryWithNamesAndTypes) + .query(stmt) + .params( + ClickHouseStringValue.of(DatabaseUtil.DB_CONFIG.table()), + ClickHouseStringValue.of(world), + ClickHouseLongValue.of(x), + ClickHouseLongValue.of(y), + ClickHouseLongValue.of(z) + ) + .execute() + .get(); + int cnt = 0; + var sb = new StringBuilder(); + sb.append(String.format("Result for block at %s (%d, %d, %d):\n", world, x, y, z)); + final int columns = 4; + var table = new TablePrinter(columns); + for (var row : resp.records()) { + var rowStrings = new String[row.size()]; + int i = 0; + for (var v : row) { + rowStrings[i] = v.asString().replaceFirst("^minecraft:", ""); + i++; + } + table.add(new TablePrinter.Row(rowStrings)); + cnt++; + } + sb.append(table); + sb.append(String.format("%d records in total.", cnt)); + callback.accept(new LiteralText(sb.toString())); + } catch (Exception ex) { + logger.error("Query block failed", ex); + callback.accept(new LiteralText("Query failed: " + ex)); + } + } +} diff --git a/src/main/java/com/keuin/blame/lookup/TimeLookupFilter.java b/src/main/java/com/keuin/blame/lookup/TimeLookupFilter.java deleted file mode 100644 index 4d891c7..0000000 --- a/src/main/java/com/keuin/blame/lookup/TimeLookupFilter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.keuin.blame.lookup; - -import com.keuin.blame.data.entry.LogEntryNames; -import com.mongodb.client.model.Filters; -import org.bson.conversions.Bson; - -public class TimeLookupFilter implements AbstractLookupFilter { - private final long seconds; - - public TimeLookupFilter(long seconds) { - this.seconds = seconds; - } - - @Override - public Bson filter() { - return Filters.gte( - LogEntryNames.TIMESTAMP_MILLIS, - System.currentTimeMillis() - seconds * 1000); - } -} diff --git a/src/main/java/com/keuin/blame/util/DatabaseUtil.java b/src/main/java/com/keuin/blame/util/DatabaseUtil.java index 1611a39..1597bb5 100644 --- a/src/main/java/com/keuin/blame/util/DatabaseUtil.java +++ b/src/main/java/com/keuin/blame/util/DatabaseUtil.java @@ -1,9 +1,36 @@ package com.keuin.blame.util; +import com.clickhouse.client.ClickHouseClient; +import com.clickhouse.client.ClickHouseNode; +import com.clickhouse.client.ClickHouseProtocol; +import com.clickhouse.client.config.ClickHouseClientOption; import com.keuin.blame.Blame; import com.keuin.blame.config.DatabaseConfig; +import static com.clickhouse.client.ClickHouseCredentials.fromUserAndPassword; + public class DatabaseUtil { public static final DatabaseConfig DB_CONFIG = Blame.config.getMongoConfig(); + + public static ClickHouseNode getServer() { + final var config = DatabaseUtil.DB_CONFIG; + return ClickHouseNode.builder() + .host(config.address()) + .port(ClickHouseProtocol.HTTP, config.port()) + // .port(ClickHouseProtocol.GRPC, Integer.getInteger("chPort", 9100)) + // .port(ClickHouseProtocol.TCP, Integer.getInteger("chPort", 9000)) + .database(config.database()) + .credentials(fromUserAndPassword(config.username(), config.password())) + .addOption(ClickHouseClientOption.COMPRESS.getKey(), "false") + .build(); + } + + public static ClickHouseClient getClient() { + return getClient(getServer()); + } + + public static ClickHouseClient getClient(ClickHouseNode server) { + return ClickHouseClient.newInstance(server.getProtocol()); + } } diff --git a/src/main/java/com/keuin/blame/util/TablePrinter.java b/src/main/java/com/keuin/blame/util/TablePrinter.java new file mode 100644 index 0000000..8b8eca3 --- /dev/null +++ b/src/main/java/com/keuin/blame/util/TablePrinter.java @@ -0,0 +1,76 @@ +package com.keuin.blame.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; + +public class TablePrinter { + private final int columns; + + private final List<Row> rows = new ArrayList<>(16); + + private Function<String, Integer> widthSupplier = (String::length); + + private final int BORDER = 1; + + public TablePrinter(int columns) { + this.columns = columns; + } + + public void add(Row row) { + if (row.get().length != columns) { + throw new IllegalArgumentException("invalid row columns, expected " + columns); + } + rows.add(row); + } + + public TablePrinter setWidthSupplier(Function<String, Integer> widthSupplier) { + this.widthSupplier = widthSupplier; + return this; + } + + @Override + public String toString() { + var maxWidths = new int[columns]; + for (var r : rows) { + for (int i = 0; i < r.row.length; i++) { + var len = widthSupplier.apply(r.row[i]); + maxWidths[i] = Math.max(maxWidths[i], len); + } + } + var sb = new StringBuilder(); + for (var r : rows) { + var i = 0; + for (String s : r.row) { + sb.append('|'); + sb.append(' '); + sb.append(s); + var n = maxWidths[i] - s.length(); + if (n > 0) { + sb.append(" ".repeat(n)); + } + sb.append(' '); + i++; + } + if (r.row.length == 0) { + sb.append('|'); + } + sb.append("|"); + sb.append('\n'); + } + return sb.toString(); + } + + public static class Row { + private final String[] row; + + public Row(String... row) { + this.row = row; + } + + private String[] get() { + return row; + } + } +} diff --git a/src/main/java/com/keuin/blame/util/TablePrinterTest.java b/src/main/java/com/keuin/blame/util/TablePrinterTest.java new file mode 100644 index 0000000..3527933 --- /dev/null +++ b/src/main/java/com/keuin/blame/util/TablePrinterTest.java @@ -0,0 +1,14 @@ +package com.keuin.blame.util; + +import static org.junit.jupiter.api.Assertions.*; + +class TablePrinterTest { + + @org.junit.jupiter.api.Test + void testToString() { + var table = new TablePrinter(3); + table.add(new TablePrinter.Row("1", "233332", "333")); + table.add(new TablePrinter.Row("1111", "232", "344433")); + System.out.println(table); + } +}
\ No newline at end of file |