Log action serialization changes

This commit is contained in:
Luck 2019-08-25 17:16:26 +01:00
parent 4667ffc681
commit eff93c788b
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
7 changed files with 213 additions and 135 deletions

View File

@ -0,0 +1,105 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.actionlog;
import com.google.common.base.Preconditions;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import me.lucko.luckperms.api.actionlog.Action;
import me.lucko.luckperms.common.util.gson.JObject;
import java.time.Instant;
import java.util.UUID;
public final class ActionJsonSerializer {
private ActionJsonSerializer() {}
public static JsonObject serialize(Action logEntry) {
return new JObject()
.add("timestamp", new JsonPrimitive(logEntry.getTimestamp().getEpochSecond()))
.add("source", new JObject()
.add("uniqueId", new JsonPrimitive(logEntry.getSource().getUniqueId().toString()))
.add("name", new JsonPrimitive(logEntry.getSource().getName()))
)
.add("target", new JObject()
.add("type", new JsonPrimitive(logEntry.getTarget().getType().name())))
.consume(obj -> {
if (logEntry.getTarget().getUniqueId().isPresent()) {
obj.add("uniqueId", new JsonPrimitive(logEntry.getTarget().getUniqueId().get().toString()));
}
})
.add("name", new JsonPrimitive(logEntry.getTarget().getName())
)
.add("description", new JsonPrimitive(logEntry.getDescription()))
.toJson();
}
public static LoggedAction deserialize(JsonElement element) {
Preconditions.checkArgument(element.isJsonObject());
JsonObject data = element.getAsJsonObject();
LoggedAction.Builder builder = LoggedAction.build();
if (data.has("timestamp")) { // sigh - this wasn't included in the first implementations
builder.timestamp(Instant.ofEpochSecond(data.get("timestamp").getAsLong()));
}
if (data.has("source")) {
JsonObject source = data.get("source").getAsJsonObject();
builder.source(UUID.fromString(source.get("uniqueId").getAsString()));
builder.sourceName(source.get("name").getAsString());
} else {
builder.source(UUID.fromString(data.get("actor").getAsString()));
builder.sourceName(data.get("actorName").getAsString());
}
if (data.has("target")) {
JsonObject target = data.get("target").getAsJsonObject();
builder.targetType(LoggedAction.parseType(target.get("type").getAsString()));
if (target.has("uniqueId")) {
builder.target(UUID.fromString(target.get("uniqueId").getAsString()));
}
builder.targetName(target.get("name").getAsString());
} else {
builder.targetType(LoggedAction.parseType(data.get("type").getAsString()));
if (data.has("acted")) {
builder.target(UUID.fromString(data.get("acted").getAsString()));
}
builder.targetName(data.get("actedName").getAsString());
}
if (data.has("description")) {
builder.description(data.get("description").getAsString());
} else {
builder.description(data.get("action").getAsString());
}
return builder.build();
}
}

View File

@ -1,78 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.actionlog;
import com.google.common.base.Preconditions;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import me.lucko.luckperms.api.actionlog.Action;
import java.time.Instant;
import java.util.UUID;
public final class LogEntryJsonSerializer {
private LogEntryJsonSerializer() {}
public static JsonObject serialize(Action logEntry) {
JsonObject data = new JsonObject();
data.add("timestamp", new JsonPrimitive(logEntry.getTimestamp().getEpochSecond()));
data.add("actor", new JsonPrimitive(logEntry.getSource().getUniqueId().toString()));
data.add("actorName", new JsonPrimitive(logEntry.getSource().getName()));
data.add("type", new JsonPrimitive(logEntry.getTarget().getType().name()));
if (logEntry.getTarget().getUniqueId().isPresent()) {
data.add("acted", new JsonPrimitive(logEntry.getTarget().getUniqueId().get().toString()));
}
data.add("actedName", new JsonPrimitive(logEntry.getTarget().getName()));
data.add("action", new JsonPrimitive(logEntry.getDescription()));
return data;
}
public static LoggedAction deserialize(JsonElement element) {
Preconditions.checkArgument(element.isJsonObject());
JsonObject data = element.getAsJsonObject();
LoggedAction.Builder builder = LoggedAction.build();
if (data.has("timestamp")) { // sigh - this wasn't included in the first implementations
builder.timestamp(Instant.ofEpochSecond(data.get("timestamp").getAsLong()));
}
builder.source(UUID.fromString(data.get("actor").getAsString()));
builder.sourceName(data.get("actorName").getAsString());
builder.targetType(LoggedAction.parseType(data.get("type").getAsString()));
if (data.has("acted")) {
builder.source(UUID.fromString(data.get("acted").getAsString()));
}
builder.targetName(data.get("actedName").getAsString());
builder.description(data.get("action").getAsString());
return builder.build();
}
}

View File

@ -86,21 +86,18 @@ public class LoggedAction implements Action {
this.action = description; this.action = description;
} }
@NonNull
@Override @Override
public Instant getTimestamp() { public @NonNull Instant getTimestamp() {
return Instant.ofEpochSecond(this.timestamp); return Instant.ofEpochSecond(this.timestamp);
} }
@NonNull
@Override @Override
public Source getSource() { public @NonNull Source getSource() {
return this.source; return this.source;
} }
@NonNull
@Override @Override
public Target getTarget() { public @NonNull Target getTarget() {
return this.target; return this.target;
} }

View File

@ -29,7 +29,7 @@ import com.google.gson.JsonElement;
import me.lucko.luckperms.api.actionlog.Action; import me.lucko.luckperms.api.actionlog.Action;
import me.lucko.luckperms.api.messenger.message.type.ActionLogMessage; import me.lucko.luckperms.api.messenger.message.type.ActionLogMessage;
import me.lucko.luckperms.common.actionlog.LogEntryJsonSerializer; import me.lucko.luckperms.common.actionlog.ActionJsonSerializer;
import me.lucko.luckperms.common.messaging.LuckPermsMessagingService; import me.lucko.luckperms.common.messaging.LuckPermsMessagingService;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -45,7 +45,7 @@ public class ActionLogMessageImpl extends AbstractMessage implements ActionLogMe
throw new IllegalStateException("Missing content"); throw new IllegalStateException("Missing content");
} }
return new ActionLogMessageImpl(id, LogEntryJsonSerializer.deserialize(content)); return new ActionLogMessageImpl(id, ActionJsonSerializer.deserialize(content));
} }
private final Action logEntry; private final Action logEntry;
@ -63,7 +63,7 @@ public class ActionLogMessageImpl extends AbstractMessage implements ActionLogMe
@Override @Override
public @NonNull String asEncodedString() { public @NonNull String asEncodedString() {
return LuckPermsMessagingService.encodeMessageAsString( return LuckPermsMessagingService.encodeMessageAsString(
TYPE, getId(), LogEntryJsonSerializer.serialize(this.logEntry) TYPE, getId(), ActionJsonSerializer.serialize(this.logEntry)
); );
} }

View File

@ -154,7 +154,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
this.uuidDataFile = MoreFiles.createFileIfNotExists(this.dataDirectory.resolve("uuidcache.txt")); this.uuidDataFile = MoreFiles.createFileIfNotExists(this.dataDirectory.resolve("uuidcache.txt"));
this.uuidCache.load(this.uuidDataFile); this.uuidCache.load(this.uuidDataFile);
this.actionLogger.init(this.dataDirectory.resolve("actions.json")); this.actionLogger.init(this.dataDirectory.resolve("actions.txt"), this.dataDirectory.resolve("actions.json"));
} }
@Override @Override

View File

@ -28,19 +28,22 @@ package me.lucko.luckperms.common.storage.implementation.file;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import me.lucko.luckperms.api.actionlog.Action; import me.lucko.luckperms.api.actionlog.Action;
import me.lucko.luckperms.common.actionlog.ActionJsonSerializer;
import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.actionlog.LogEntryJsonSerializer;
import me.lucko.luckperms.common.cache.BufferedRequest; import me.lucko.luckperms.common.cache.BufferedRequest;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.util.gson.GsonProvider; import me.lucko.luckperms.common.util.gson.GsonProvider;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -69,8 +72,33 @@ public class FileActionLogger {
this.saveBuffer = new SaveBuffer(plugin); this.saveBuffer = new SaveBuffer(plugin);
} }
public void init(Path contentFile) { public void init(Path contentFile, Path legacyFile) {
this.contentFile = contentFile; this.contentFile = contentFile;
if (Files.exists(legacyFile)) {
// migrate
JsonArray array;
try (JsonReader reader = new JsonReader(Files.newBufferedReader(legacyFile, StandardCharsets.UTF_8))) {
array = GsonProvider.parser().parse(reader).getAsJsonArray();
} catch (IOException e) {
e.printStackTrace();
return;
}
for (JsonElement element : array) {
this.entryQueue.add(ActionJsonSerializer.deserialize(element));
}
flush();
try {
Files.delete(legacyFile);
} catch (IOException e) {
e.printStackTrace();
}
}
} }
public void logAction(Action entry) { public void logAction(Action entry) {
@ -87,30 +115,14 @@ public class FileActionLogger {
} }
try { try {
// read existing array data into memory List<String> toWrite = new ArrayList<>(this.entryQueue.size());
JsonArray array;
if (Files.exists(this.contentFile)) {
try (JsonReader reader = new JsonReader(Files.newBufferedReader(this.contentFile, StandardCharsets.UTF_8))) {
array = GsonProvider.parser().parse(reader).getAsJsonArray();
} catch (IOException e) {
e.printStackTrace();
array = new JsonArray();
}
} else {
array = new JsonArray();
}
// poll the queue for new entries // poll the queue for new entries
for (Action e; (e = this.entryQueue.poll()) != null; ) { for (Action e; (e = this.entryQueue.poll()) != null; ) {
array.add(LogEntryJsonSerializer.serialize(e)); toWrite.add(GsonProvider.normal().toJson(ActionJsonSerializer.serialize(e)));
} }
// write the full content back to the file Files.write(this.contentFile, toWrite, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
try (JsonWriter writer = new JsonWriter(Files.newBufferedWriter(this.contentFile, StandardCharsets.UTF_8))) {
writer.setIndent(" ");
GsonProvider.normal().toJson(array, writer);
}
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -120,13 +132,24 @@ public class FileActionLogger {
} }
public Log getLog() throws IOException { public Log getLog() throws IOException {
if (!Files.exists(this.contentFile)) {
return Log.empty();
}
Log.Builder log = Log.builder(); Log.Builder log = Log.builder();
try (JsonReader reader = new JsonReader(Files.newBufferedReader(this.contentFile, StandardCharsets.UTF_8))) {
JsonArray array = GsonProvider.parser().parse(reader).getAsJsonArray(); try (BufferedReader reader = Files.newBufferedReader(this.contentFile, StandardCharsets.UTF_8)) {
for (JsonElement element : array) { String line;
log.add(LogEntryJsonSerializer.deserialize(element)); while ((line = reader.readLine()) != null) {
try {
JsonElement parsed = GsonProvider.parser().parse(line);
log.add(ActionJsonSerializer.deserialize(parsed));
} catch (Exception e) {
e.printStackTrace();
} }
} }
}
return log.build(); return log.build();
} }

View File

@ -166,18 +166,25 @@ public class MongoStorage implements StorageImplementation {
@Override @Override
public void logAction(Action entry) { public void logAction(Action entry) {
MongoCollection<Document> c = this.database.getCollection(this.prefix + "action"); MongoCollection<Document> c = this.database.getCollection(this.prefix + "action");
Document doc = new Document() Document doc = new Document()
.append("timestamp", entry.getTimestamp().getEpochSecond()) .append("timestamp", entry.getTimestamp().getEpochSecond())
.append("actor", entry.getSource().getUniqueId()) .append("source", new Document()
.append("actorName", entry.getSource().getName()) .append("uniqueId", entry.getSource().getUniqueId())
.append("type", Character.toString(LoggedAction.getTypeCharacter(entry.getTarget().getType()))) .append("name", entry.getSource().getName())
.append("actedName", entry.getTarget().getName()) );
.append("action", entry.getDescription());
Document target = new Document()
.append("type", entry.getTarget().getType().name())
.append("name", entry.getTarget().getName());
if (entry.getTarget().getUniqueId().isPresent()) { if (entry.getTarget().getUniqueId().isPresent()) {
doc.append("acted", entry.getTarget().getUniqueId().get()); target.append("uniqueId", entry.getTarget().getUniqueId().get());
} }
doc.append("target", target);
doc.append("description", entry.getDescription());
c.insertOne(doc); c.insertOne(doc);
} }
@ -189,6 +196,29 @@ public class MongoStorage implements StorageImplementation {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
if (d.containsKey("source")) {
// new format
Document source = d.get("source", Document.class);
Document target = d.get("target", Document.class);
UUID targetUniqueId = null;
if (target.containsKey("uniqueId")) {
targetUniqueId = target.get("uniqueId", UUID.class);
}
LoggedAction e = LoggedAction.build()
.timestamp(Instant.ofEpochSecond(d.getLong("timestamp")))
.source(source.get("uniqueId", UUID.class))
.sourceName(source.getString("name"))
.targetType(LoggedAction.parseType(target.getString("type")))
.target(targetUniqueId)
.targetName(target.getString("name"))
.description(d.getString("description"))
.build();
log.add(e);
} else {
// old format
UUID actedUuid = null; UUID actedUuid = null;
if (d.containsKey("acted")) { if (d.containsKey("acted")) {
actedUuid = d.get("acted", UUID.class); actedUuid = d.get("acted", UUID.class);
@ -207,6 +237,7 @@ public class MongoStorage implements StorageImplementation {
log.add(e); log.add(e);
} }
} }
}
return log.build(); return log.build();
} }