Rewrite the MongoDB storage impl to use the modern node serialisation format

This commit is contained in:
Luck 2017-11-30 20:25:48 +00:00
parent c369ac6b90
commit 003ddc51de
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
3 changed files with 168 additions and 205 deletions

View File

@ -74,36 +74,6 @@ public class LegacyNodeFactory {
private static final Splitter.MapSplitter LEGACY_CONTEXT_PART_SPLITTER = Splitter.on(LEGACY_CONTEXT_PAIR_DELIM) private static final Splitter.MapSplitter LEGACY_CONTEXT_PART_SPLITTER = Splitter.on(LEGACY_CONTEXT_PAIR_DELIM)
.withKeyValueSeparator(Splitter.on(LEGACY_CONTEXT_PAIR_PART_DELIM)); .withKeyValueSeparator(Splitter.on(LEGACY_CONTEXT_PAIR_PART_DELIM));
public static String toSerializedNode(Node node) {
StringBuilder builder = new StringBuilder();
if (node.getServer().orElse(null) != null) {
builder.append(escapeDelimiters(node.getServer().orElse(null), SERVER_WORLD_DELIMITERS));
if (node.getWorld().orElse(null) != null) {
builder.append("-").append(escapeDelimiters(node.getWorld().orElse(null), SERVER_WORLD_DELIMITERS));
}
builder.append("/");
} else {
if (node.getWorld().orElse(null) != null) {
builder.append("global-").append(escapeDelimiters(node.getWorld().orElse(null), SERVER_WORLD_DELIMITERS)).append("/");
}
}
if (!node.getContexts().isEmpty()) {
builder.append("(");
for (Map.Entry<String, String> entry : node.getContexts().toSet()) {
builder.append(escapeDelimiters(entry.getKey(), CONTEXT_DELIMITERS))
.append("=").append(escapeDelimiters(entry.getValue(), CONTEXT_DELIMITERS)).append(",");
}
builder.deleteCharAt(builder.length() - 1);
builder.append(")");
}
builder.append(escapeDelimiters(node.getPermission(), PERMISSION_DELIMITERS));
if (node.isTemporary()) builder.append("$").append(node.getExpiryUnixTime());
return builder.toString();
}
public static Node fromLegacyString(String s, boolean b) { public static Node fromLegacyString(String s, boolean b) {
if (b) { if (b) {
return builderFromLegacyString(s, true).build(); return builderFromLegacyString(s, true).build();

View File

@ -394,8 +394,7 @@ public abstract class ConfigurateDao extends AbstractDao {
String name = object.getNode("name").getString(); String name = object.getNode("name").getString();
user.getPrimaryGroup().setStoredValue(object.getNode(this instanceof JsonDao ? "primaryGroup" : "primary-group").getString()); user.getPrimaryGroup().setStoredValue(object.getNode(this instanceof JsonDao ? "primaryGroup" : "primary-group").getString());
Set<NodeModel> data = readNodes(object); Set<Node> nodes = readNodes(object).stream().map(NodeModel::toNode).collect(Collectors.toSet());
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
user.setEnduringNodes(nodes); user.setEnduringNodes(nodes);
user.setName(name, true); user.setName(name, true);
@ -498,8 +497,7 @@ public abstract class ConfigurateDao extends AbstractDao {
ConfigurationNode object = readFile(StorageLocation.GROUP, name); ConfigurationNode object = readFile(StorageLocation.GROUP, name);
if (object != null) { if (object != null) {
Set<NodeModel> data = readNodes(object); Set<Node> nodes = readNodes(object).stream().map(NodeModel::toNode).collect(Collectors.toSet());
Set<Node> nodes = data.stream().map(NodeModel::toNode).collect(Collectors.toSet());
group.setEnduringNodes(nodes); group.setEnduringNodes(nodes);
} else { } else {
ConfigurationNode data = SimpleConfigurationNode.root(); ConfigurationNode data = SimpleConfigurationNode.root();

View File

@ -27,6 +27,7 @@ package me.lucko.luckperms.common.storage.dao.mongodb;
import lombok.Getter; import lombok.Getter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.mongodb.MongoClient; import com.mongodb.MongoClient;
import com.mongodb.MongoCredential; import com.mongodb.MongoCredential;
@ -34,11 +35,14 @@ import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.InsertOneOptions; import com.mongodb.client.model.UpdateOptions;
import me.lucko.luckperms.api.HeldPermission; import me.lucko.luckperms.api.HeldPermission;
import me.lucko.luckperms.api.LogEntry; import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.actionlog.ExtendedLogEntry; import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate; import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
@ -60,7 +64,6 @@ import org.bson.Document;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@ -68,10 +71,8 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class MongoDao extends AbstractDao { public class MongoDao extends AbstractDao {
private final StorageCredentials configuration; private final StorageCredentials configuration;
@ -96,20 +97,12 @@ public class MongoDao extends AbstractDao {
@Override @Override
public void init() { public void init() {
MongoCredential credential = null; MongoCredential credential = null;
if (!Strings.isNullOrEmpty(configuration.getUsername())) {
if (configuration.getUsername() != null && !configuration.getUsername().equals("") && configuration.getDatabase() != null && !configuration.getDatabase().equals("")) { credential = MongoCredential.createCredential(
if (configuration.getPassword() == null || configuration.getPassword().equals("")) { configuration.getUsername(),
credential = MongoCredential.createCredential( configuration.getDatabase(),
configuration.getUsername(), Strings.isNullOrEmpty(configuration.getPassword()) ? null : configuration.getPassword().toCharArray()
configuration.getDatabase(), null );
);
} else {
credential = MongoCredential.createCredential(
configuration.getUsername(),
configuration.getDatabase(),
configuration.getPassword().toCharArray()
);
}
} }
String[] addressSplit = configuration.getAddress().split(":"); String[] addressSplit = configuration.getAddress().split(":");
@ -161,7 +154,6 @@ public class MongoDao extends AbstractDao {
public boolean logAction(LogEntry entry) { public boolean logAction(LogEntry entry) {
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "action"); MongoCollection<Document> c = database.getCollection(prefix + "action");
Document doc = new Document() Document doc = new Document()
.append("timestamp", entry.getTimestamp()) .append("timestamp", entry.getTimestamp())
.append("actor", entry.getActor()) .append("actor", entry.getActor())
@ -174,7 +166,7 @@ public class MongoDao extends AbstractDao {
doc.append("acted", entry.getActed().get()); doc.append("acted", entry.getActed().get());
} }
c.insertOne(doc, new InsertOneOptions()); c.insertOne(doc);
} catch (Exception e) { } catch (Exception e) {
return reportException(e); return reportException(e);
} }
@ -186,7 +178,6 @@ public class MongoDao extends AbstractDao {
Log.Builder log = Log.builder(); Log.Builder log = Log.builder();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "action"); MongoCollection<Document> c = database.getCollection(prefix + "action");
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
@ -221,33 +212,23 @@ public class MongoDao extends AbstractDao {
try { try {
if (bulkUpdate.getDataType().isIncludingUsers()) { if (bulkUpdate.getDataType().isIncludingUsers()) {
MongoCollection<Document> c = database.getCollection(prefix + "users"); MongoCollection<Document> c = database.getCollection(prefix + "users");
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
UUID uuid = d.get("_id", UUID.class); UUID uuid = d.get("_id", UUID.class);
Map<String, Boolean> perms = revert((Map<String, Boolean>) d.get("perms")); Set<NodeModel> nodes = new HashSet<>(nodesFromDoc(d));
Set<NodeModel> nodes = new HashSet<>();
for (Map.Entry<String, Boolean> e : perms.entrySet()) {
Node node = LegacyNodeFactory.fromLegacyString(e.getKey(), e.getValue());
nodes.add(NodeModel.fromNode(node));
}
Set<NodeModel> results = nodes.stream() Set<NodeModel> results = nodes.stream()
.map(bulkUpdate::apply) .map(bulkUpdate::apply)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
if (!nodes.equals(results)) { if (!nodes.equals(results)) {
Document permsDoc = new Document(); List<Document> newNodes = results.stream()
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(results.stream().map(NodeModel::toNode).collect(Collectors.toList()))).entrySet()) { .map(MongoDao::nodeToDoc)
permsDoc.append(e.getKey(), e.getValue()); .collect(Collectors.toList());
}
d.put("perms", permsDoc); c.replaceOne(new Document("_id", uuid), d.append("permissions", newNodes));
c.replaceOne(new Document("_id", uuid), d);
} }
} }
} }
@ -255,33 +236,23 @@ public class MongoDao extends AbstractDao {
if (bulkUpdate.getDataType().isIncludingGroups()) { if (bulkUpdate.getDataType().isIncludingGroups()) {
MongoCollection<Document> c = database.getCollection(prefix + "groups"); MongoCollection<Document> c = database.getCollection(prefix + "groups");
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
String holder = d.getString("_id"); String holder = d.getString("_id");
Map<String, Boolean> perms = revert((Map<String, Boolean>) d.get("perms")); Set<NodeModel> nodes = new HashSet<>(nodesFromDoc(d));
Set<NodeModel> nodes = new HashSet<>();
for (Map.Entry<String, Boolean> e : perms.entrySet()) {
Node node = LegacyNodeFactory.fromLegacyString(e.getKey(), e.getValue());
nodes.add(NodeModel.fromNode(node));
}
Set<NodeModel> results = nodes.stream() Set<NodeModel> results = nodes.stream()
.map(bulkUpdate::apply) .map(bulkUpdate::apply)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
if (!nodes.equals(results)) { if (!nodes.equals(results)) {
Document permsDoc = new Document(); List<Document> newNodes = results.stream()
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(results.stream().map(NodeModel::toNode).collect(Collectors.toList()))).entrySet()) { .map(MongoDao::nodeToDoc)
permsDoc.append(e.getKey(), e.getValue()); .collect(Collectors.toList());
}
d.put("perms", permsDoc); c.replaceOne(new Document("_id", holder), d.append("permissions", newNodes));
c.replaceOne(new Document("_id", holder), d);
} }
} }
} }
@ -299,17 +270,16 @@ public class MongoDao extends AbstractDao {
user.getIoLock().lock(); user.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "users"); MongoCollection<Document> c = database.getCollection(prefix + "users");
try (MongoCursor<Document> cursor = c.find(new Document("_id", user.getUuid())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", user.getUuid())).iterator()) {
if (cursor.hasNext()) { if (cursor.hasNext()) {
// User exists, let's load. // User exists, let's load.
Document d = cursor.next(); Document d = cursor.next();
user.setEnduringNodes(revert((Map<String, Boolean>) d.get("perms")).entrySet().stream()
.map(e -> LegacyNodeFactory.fromLegacyString(e.getKey(), e.getValue()))
.collect(Collectors.toSet())
);
user.getPrimaryGroup().setStoredValue(d.getString("primaryGroup")); user.getPrimaryGroup().setStoredValue(d.getString("primaryGroup"));
user.setName(name, true); user.setName(d.getString("name"), true);
Set<Node> nodes = nodesFromDoc(d).stream().map(NodeModel::toNode).collect(Collectors.toSet());
user.setEnduringNodes(nodes);
boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
if (user.getName().isPresent() && (name == null || !user.getName().get().equalsIgnoreCase(name))) { if (user.getName().isPresent() && (name == null || !user.getName().get().equalsIgnoreCase(name))) {
@ -317,7 +287,7 @@ public class MongoDao extends AbstractDao {
} }
if (save) { if (save) {
c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); c.replaceOne(new Document("_id", user.getUuid()), userToDoc(user));
} }
} else { } else {
if (GenericUserManager.shouldSave(user)) { if (GenericUserManager.shouldSave(user)) {
@ -340,25 +310,17 @@ public class MongoDao extends AbstractDao {
public boolean saveUser(User user) { public boolean saveUser(User user) {
user.getIoLock().lock(); user.getIoLock().lock();
try { try {
if (!GenericUserManager.shouldSave(user)) {
MongoCollection<Document> c = database.getCollection(prefix + "users");
return c.deleteOne(new Document("_id", user.getUuid())).wasAcknowledged();
}
MongoCollection<Document> c = database.getCollection(prefix + "users"); MongoCollection<Document> c = database.getCollection(prefix + "users");
try (MongoCursor<Document> cursor = c.find(new Document("_id", user.getUuid())).iterator()) { if (!GenericUserManager.shouldSave(user)) {
if (!cursor.hasNext()) { return c.deleteOne(new Document("_id", user.getUuid())).wasAcknowledged();
c.insertOne(fromUser(user)); } else {
} else { return c.replaceOne(new Document("_id", user.getUuid()), userToDoc(user), new UpdateOptions().upsert(true)).wasAcknowledged();
c.replaceOne(new Document("_id", user.getUuid()), fromUser(user));
}
} }
} catch (Exception e) { } catch (Exception e) {
return reportException(e); return reportException(e);
} finally { } finally {
user.getIoLock().unlock(); user.getIoLock().unlock();
} }
return true;
} }
@Override @Override
@ -366,7 +328,6 @@ public class MongoDao extends AbstractDao {
Set<UUID> uuids = new HashSet<>(); Set<UUID> uuids = new HashSet<>();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "users"); MongoCollection<Document> c = database.getCollection(prefix + "users");
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
@ -374,6 +335,7 @@ public class MongoDao extends AbstractDao {
} }
} }
} catch (Exception e) { } catch (Exception e) {
reportException(e);
return null; return null;
} }
return uuids; return uuids;
@ -384,21 +346,17 @@ public class MongoDao extends AbstractDao {
ImmutableList.Builder<HeldPermission<UUID>> held = ImmutableList.builder(); ImmutableList.Builder<HeldPermission<UUID>> held = ImmutableList.builder();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "users"); MongoCollection<Document> c = database.getCollection(prefix + "users");
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
UUID holder = d.get("_id", UUID.class); UUID holder = d.get("_id", UUID.class);
Map<String, Boolean> perms = revert((Map<String, Boolean>) d.get("perms"));
for (Map.Entry<String, Boolean> e : perms.entrySet()) { Set<NodeModel> nodes = new HashSet<>(nodesFromDoc(d));
Node node = LegacyNodeFactory.fromLegacyString(e.getKey(), e.getValue()); for (NodeModel e : nodes) {
if (!node.getPermission().equalsIgnoreCase(permission)) { if (!e.getPermission().equalsIgnoreCase(permission)) {
continue; continue;
} }
held.add(NodeHeldPermission.of(holder, e));
held.add(NodeHeldPermission.of(holder, node));
} }
} }
} }
@ -415,17 +373,13 @@ public class MongoDao extends AbstractDao {
group.getIoLock().lock(); group.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "groups"); MongoCollection<Document> c = database.getCollection(prefix + "groups");
try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) {
if (cursor.hasNext()) { if (cursor.hasNext()) {
// Group exists, let's load.
Document d = cursor.next(); Document d = cursor.next();
group.setEnduringNodes(revert((Map<String, Boolean>) d.get("perms")).entrySet().stream() Set<Node> nodes = nodesFromDoc(d).stream().map(NodeModel::toNode).collect(Collectors.toSet());
.map(e -> LegacyNodeFactory.fromLegacyString(e.getKey(), e.getValue())) group.setEnduringNodes(nodes);
.collect(Collectors.toSet())
);
} else { } else {
c.insertOne(fromGroup(group)); c.insertOne(groupToDoc(group));
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -443,17 +397,14 @@ public class MongoDao extends AbstractDao {
group.getIoLock().lock(); group.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "groups"); MongoCollection<Document> c = database.getCollection(prefix + "groups");
try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) {
if (!cursor.hasNext()) { if (!cursor.hasNext()) {
return false; return false;
} }
Document d = cursor.next(); Document d = cursor.next();
group.setEnduringNodes(revert((Map<String, Boolean>) d.get("perms")).entrySet().stream() Set<Node> nodes = nodesFromDoc(d).stream().map(NodeModel::toNode).collect(Collectors.toSet());
.map(e -> LegacyNodeFactory.fromLegacyString(e.getKey(), e.getValue())) group.setEnduringNodes(nodes);
.collect(Collectors.toSet())
);
} }
} catch (Exception e) { } catch (Exception e) {
return reportException(e); return reportException(e);
@ -469,11 +420,9 @@ public class MongoDao extends AbstractDao {
List<String> groups = new ArrayList<>(); List<String> groups = new ArrayList<>();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "groups"); MongoCollection<Document> c = database.getCollection(prefix + "groups");
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
String name = cursor.next().getString("_id"); String name = cursor.next().getString("_id");
loadGroup(name);
groups.add(name); groups.add(name);
} }
} }
@ -482,6 +431,8 @@ public class MongoDao extends AbstractDao {
return false; return false;
} }
groups.forEach(this::loadGroup);
GroupManager gm = plugin.getGroupManager(); GroupManager gm = plugin.getGroupManager();
gm.getAll().values().stream() gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName())) .filter(g -> !groups.contains(g.getName()))
@ -494,7 +445,7 @@ public class MongoDao extends AbstractDao {
group.getIoLock().lock(); group.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "groups"); MongoCollection<Document> c = database.getCollection(prefix + "groups");
return c.replaceOne(new Document("_id", group.getName()), fromGroup(group)).wasAcknowledged(); return c.replaceOne(new Document("_id", group.getName()), groupToDoc(group), new UpdateOptions().upsert(true)).wasAcknowledged();
} catch (Exception e) { } catch (Exception e) {
return reportException(e); return reportException(e);
} finally { } finally {
@ -507,17 +458,12 @@ public class MongoDao extends AbstractDao {
group.getIoLock().lock(); group.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "groups"); MongoCollection<Document> c = database.getCollection(prefix + "groups");
if (!c.deleteOne(new Document("_id", group.getName())).wasAcknowledged()) { return c.deleteOne(new Document("_id", group.getName())).wasAcknowledged();
throw new RuntimeException();
}
} catch (Exception e) { } catch (Exception e) {
return reportException(e); return reportException(e);
} finally { } finally {
group.getIoLock().unlock(); group.getIoLock().unlock();
} }
plugin.getGroupManager().unload(group);
return true;
} }
@Override @Override
@ -525,21 +471,17 @@ public class MongoDao extends AbstractDao {
ImmutableList.Builder<HeldPermission<String>> held = ImmutableList.builder(); ImmutableList.Builder<HeldPermission<String>> held = ImmutableList.builder();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "groups"); MongoCollection<Document> c = database.getCollection(prefix + "groups");
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
String holder = d.getString("_id"); String holder = d.getString("_id");
Map<String, Boolean> perms = revert((Map<String, Boolean>) d.get("perms")); Set<NodeModel> nodes = new HashSet<>(nodesFromDoc(d));
for (NodeModel e : nodes) {
for (Map.Entry<String, Boolean> e : perms.entrySet()) { if (!e.getPermission().equalsIgnoreCase(permission)) {
Node node = LegacyNodeFactory.fromLegacyString(e.getKey(), e.getValue());
if (!node.getPermission().equalsIgnoreCase(permission)) {
continue; continue;
} }
held.add(NodeHeldPermission.of(holder, e));
held.add(NodeHeldPermission.of(holder, node));
} }
} }
} }
@ -556,12 +498,12 @@ public class MongoDao extends AbstractDao {
track.getIoLock().lock(); track.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "tracks"); MongoCollection<Document> c = database.getCollection(prefix + "tracks");
try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) {
if (!cursor.hasNext()) { if (!cursor.hasNext()) {
c.insertOne(fromTrack(track)); c.insertOne(trackToDoc(track));
} else { } else {
Document d = cursor.next(); Document d = cursor.next();
//noinspection unchecked
track.setGroups((List<String>) d.get("groups")); track.setGroups((List<String>) d.get("groups"));
} }
} }
@ -579,10 +521,10 @@ public class MongoDao extends AbstractDao {
track.getIoLock().lock(); track.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "tracks"); MongoCollection<Document> c = database.getCollection(prefix + "tracks");
try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) {
if (cursor.hasNext()) { if (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
//noinspection unchecked
track.setGroups((List<String>) d.get("groups")); track.setGroups((List<String>) d.get("groups"));
return true; return true;
} }
@ -600,11 +542,9 @@ public class MongoDao extends AbstractDao {
List<String> tracks = new ArrayList<>(); List<String> tracks = new ArrayList<>();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "tracks"); MongoCollection<Document> c = database.getCollection(prefix + "tracks");
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
String name = cursor.next().getString("_id"); String name = cursor.next().getString("_id");
loadTrack(name);
tracks.add(name); tracks.add(name);
} }
} }
@ -613,6 +553,8 @@ public class MongoDao extends AbstractDao {
return false; return false;
} }
tracks.forEach(this::loadTrack);
TrackManager tm = plugin.getTrackManager(); TrackManager tm = plugin.getTrackManager();
tm.getAll().values().stream() tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName())) .filter(t -> !tracks.contains(t.getName()))
@ -625,7 +567,7 @@ public class MongoDao extends AbstractDao {
track.getIoLock().lock(); track.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "tracks"); MongoCollection<Document> c = database.getCollection(prefix + "tracks");
return c.replaceOne(new Document("_id", track.getName()), fromTrack(track)).wasAcknowledged(); return c.replaceOne(new Document("_id", track.getName()), trackToDoc(track)).wasAcknowledged();
} catch (Exception e) { } catch (Exception e) {
return reportException(e); return reportException(e);
} finally { } finally {
@ -638,31 +580,19 @@ public class MongoDao extends AbstractDao {
track.getIoLock().lock(); track.getIoLock().lock();
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "tracks"); MongoCollection<Document> c = database.getCollection(prefix + "tracks");
if (!c.deleteOne(new Document("_id", track.getName())).wasAcknowledged()) { return c.deleteOne(new Document("_id", track.getName())).wasAcknowledged();
throw new RuntimeException();
}
} catch (Exception e) { } catch (Exception e) {
return reportException(e); return reportException(e);
} finally { } finally {
track.getIoLock().unlock(); track.getIoLock().unlock();
} }
plugin.getTrackManager().unload(track);
return true;
} }
@Override @Override
public boolean saveUUIDData(UUID uuid, String username) { public boolean saveUUIDData(UUID uuid, String username) {
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "uuid"); MongoCollection<Document> c = database.getCollection(prefix + "uuid");
c.replaceOne(new Document("_id", uuid), new Document("_id", uuid).append("name", username.toLowerCase()), new UpdateOptions().upsert(true));
try (MongoCursor<Document> cursor = c.find(new Document("_id", uuid)).iterator()) {
if (cursor.hasNext()) {
c.replaceOne(new Document("_id", uuid), new Document("_id", uuid).append("name", username.toLowerCase()));
} else {
c.insertOne(new Document("_id", uuid).append("name", username.toLowerCase()));
}
}
} catch (Exception e) { } catch (Exception e) {
return reportException(e); return reportException(e);
} }
@ -673,7 +603,6 @@ public class MongoDao extends AbstractDao {
public UUID getUUID(String username) { public UUID getUUID(String username) {
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "uuid"); MongoCollection<Document> c = database.getCollection(prefix + "uuid");
try (MongoCursor<Document> cursor = c.find(new Document("name", username.toLowerCase())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("name", username.toLowerCase())).iterator()) {
if (cursor.hasNext()) { if (cursor.hasNext()) {
return cursor.next().get("_id", UUID.class); return cursor.next().get("_id", UUID.class);
@ -690,7 +619,6 @@ public class MongoDao extends AbstractDao {
public String getName(UUID uuid) { public String getName(UUID uuid) {
try { try {
MongoCollection<Document> c = database.getCollection(prefix + "uuid"); MongoCollection<Document> c = database.getCollection(prefix + "uuid");
try (MongoCursor<Document> cursor = c.find(new Document("_id", uuid)).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", uuid)).iterator()) {
if (cursor.hasNext()) { if (cursor.hasNext()) {
return cursor.next().get("name", String.class); return cursor.next().get("name", String.class);
@ -703,59 +631,126 @@ public class MongoDao extends AbstractDao {
} }
} }
/* MongoDB does not allow '.' or '$' in key names. private static Document userToDoc(User user) {
See: https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names List<Document> nodes = user.getEnduringNodes().values().stream()
The following two methods convert the node maps so they can be stored. */ .map(NodeModel::fromNode)
.map(MongoDao::nodeToDoc)
.collect(Collectors.toList());
private static final Function<String, String> CONVERT_STRING = s -> s.replace(".", "[**DOT**]").replace("$", "[**DOLLAR**]"); return new Document("_id", user.getUuid())
private static final Function<String, String> REVERT_STRING = s -> s.replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$");
private static <V> Map<String, V> convert(Map<String, V> map) {
return map.entrySet().stream()
.collect(Collectors.toMap(e -> CONVERT_STRING.apply(e.getKey()), Map.Entry::getValue));
}
private static <V> Map<String, V> revert(Map<String, V> map) {
return map.entrySet().stream()
.collect(Collectors.toMap(e -> REVERT_STRING.apply(e.getKey()), Map.Entry::getValue));
}
private static Document fromUser(User user) {
Document main = new Document("_id", user.getUuid())
.append("name", user.getName().orElse("null")) .append("name", user.getName().orElse("null"))
.append("primaryGroup", user.getPrimaryGroup().getStoredValue().orElse("default")); .append("primaryGroup", user.getPrimaryGroup().getStoredValue().orElse("default"))
.append("permissions", nodes);
Document perms = new Document();
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(user.getEnduringNodes().values())).entrySet()) {
perms.append(e.getKey(), e.getValue());
}
main.append("perms", perms);
return main;
} }
private static Document fromGroup(Group group) { private static List<NodeModel> nodesFromDoc(Document document) {
Document main = new Document("_id", group.getName()); List<NodeModel> nodes = new ArrayList<>();
Document perms = new Document(); // legacy
for (Map.Entry<String, Boolean> e : convert(exportToLegacy(group.getEnduringNodes().values())).entrySet()) { if (document.containsKey("perms") && document.get("perms") instanceof Map) {
perms.append(e.getKey(), e.getValue()); //noinspection unchecked
Map<String, Boolean> permsMap = (Map<String, Boolean>) document.get("perms");
for (Map.Entry<String, Boolean> e : permsMap.entrySet()) {
// legacy permission key deserialisation
String permission = e.getKey().replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$");
nodes.add(NodeModel.fromNode(LegacyNodeFactory.fromLegacyString(permission, e.getValue())));
}
} }
main.append("perms", perms); // new format
return main; if (document.containsKey("permissions") && document.get("permissions") instanceof List) {
//noinspection unchecked
List<Document> permsList = (List<Document>) document.get("permissions");
for (Document d : permsList) {
nodes.add(nodeFromDoc(d));
}
}
return nodes;
} }
private static Document fromTrack(Track track) { private static Document groupToDoc(Group group) {
List<Document> nodes = group.getEnduringNodes().values().stream()
.map(NodeModel::fromNode)
.map(MongoDao::nodeToDoc)
.collect(Collectors.toList());
return new Document("_id", group.getName()).append("permissions", nodes);
}
private static Document trackToDoc(Track track) {
return new Document("_id", track.getName()).append("groups", track.getGroups()); return new Document("_id", track.getName()).append("groups", track.getGroups());
} }
public static Map<String, Boolean> exportToLegacy(Iterable<Node> nodes) { private static Document nodeToDoc(NodeModel node) {
Map<String, Boolean> m = new HashMap<>(); Document document = new Document();
for (Node node : nodes) {
//noinspection deprecation document.append("permission", node.getPermission());
m.put(LegacyNodeFactory.toSerializedNode(node), node.getValuePrimitive()); document.append("value", node.getValue());
if (!node.getServer().equals("global")) {
document.append("server", node.getServer());
} }
return m;
if (!node.getWorld().equals("global")) {
document.append("world", node.getWorld());
}
if (node.getExpiry() != 0L) {
document.append("expiry", node.getExpiry());
}
if (!node.getContexts().isEmpty()) {
document.append("context", contextSetToDocs(node.getContexts()));
}
return document;
} }
private static NodeModel nodeFromDoc(Document document) {
String permission = document.getString("permission");
boolean value = true;
String server = "global";
String world = "global";
long expiry = 0L;
ImmutableContextSet context = ImmutableContextSet.empty();
if (document.containsKey("value")) {
value = document.getBoolean("value");
}
if (document.containsKey("server")) {
server = document.getString("server");
}
if (document.containsKey("world")) {
world = document.getString("world");
}
if (document.containsKey("expiry")) {
expiry = document.getLong("expiry");
}
if (document.containsKey("context") && document.get("context") instanceof List) {
//noinspection unchecked
List<Document> contexts = (List<Document>) document.get("context");
context = docsToContextSet(contexts).makeImmutable();
}
return NodeModel.of(permission, value, server, world, expiry, context);
}
private static List<Document> contextSetToDocs(ContextSet contextSet) {
List<Document> contexts = new ArrayList<>();
for (Map.Entry<String, String> e : contextSet.toSet()) {
contexts.add(new Document().append("key", e.getKey()).append("value", e.getValue()));
}
return contexts;
}
private static MutableContextSet docsToContextSet(List<Document> documents) {
MutableContextSet map = MutableContextSet.create();
for (Document doc : documents) {
map.add(doc.getString("key"), doc.getString("value"));
}
return map;
}
} }