From a5e41e479a48a074b9d335ad82b92c22d5100a76 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 7 Dec 2017 19:49:04 +0000 Subject: [PATCH] Use static empty context set where appropriate, cache reversed comparator instances --- .../api/context/ImmutableContextSet.java | 19 ++-- .../api/context/MutableContextSet.java | 1 + .../bukkit/BukkitSchedulerAdapter.java | 46 ++++++++-- .../luckperms/bukkit/LPBukkitPlugin.java | 6 +- .../common/commands/utils/MetaComparator.java | 2 +- .../common/contexts/ContextSetComparator.java | 8 +- .../ContextSetConfigurateSerializer.java | 6 +- .../contexts/ContextSetJsonSerializer.java | 21 ++++- .../luckperms/common/node/NodeComparator.java | 12 ++- .../luckperms/common/node/NodeModel.java | 14 +-- .../node/NodeWithContextComparator.java | 42 ++------- .../common/storage/dao/sql/SqlDao.java | 4 +- .../common/utils/CollationKeyCache.java | 91 +++++++++++++++++++ .../service/storage/SubjectStorageModel.java | 6 +- 14 files changed, 200 insertions(+), 78 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/utils/CollationKeyCache.java diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java index 8344daf58..c350f65f6 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java @@ -163,27 +163,28 @@ public final class ImmutableContextSet implements ContextSet { return true; } + @Nonnull @Override @Deprecated // This set is already immutable! - @Nonnull public ImmutableContextSet makeImmutable() { return this; } - @Override @Nonnull + @Override public MutableContextSet mutableCopy() { return MutableContextSet.fromSet(this); } - @Override @Nonnull + @Override public Set> toSet() { return map.entries(); } - @Override @Nonnull + @Override + @Deprecated public Map toMap() { ImmutableMap.Builder m = ImmutableMap.builder(); for (Map.Entry e : map.entries()) { @@ -193,33 +194,33 @@ public final class ImmutableContextSet implements ContextSet { return m.build(); } - @Override @Nonnull + @Override public Multimap toMultimap() { return map; } - @Override @Nonnull + @Override public boolean containsKey(@Nonnull String key) { return map.containsKey(checkNotNull(key, "key").toLowerCase().intern()); } - @Override @Nonnull + @Override public Set getValues(@Nonnull String key) { Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); } - @Override @Nonnull + @Override public boolean has(@Nonnull String key, @Nonnull String value) { return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern()); } - @Override @Nonnull + @Override public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) { value = checkNotNull(value, "value").intern(); Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); diff --git a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java index 5d39ffaef..740b67c9b 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java @@ -198,6 +198,7 @@ public final class MutableContextSet implements ContextSet { @Nonnull @Override + @Deprecated public Map toMap() { ImmutableMap.Builder m = ImmutableMap.builder(); for (Map.Entry e : map.entries()) { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSchedulerAdapter.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSchedulerAdapter.java index 389d89a30..83dba25af 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSchedulerAdapter.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSchedulerAdapter.java @@ -45,7 +45,7 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter { @Getter @Accessors(fluent = true) - private ExecutorService asyncLp; + private ExecutorService asyncFallback; @Getter @Accessors(fluent = true) @@ -61,17 +61,17 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter { @Getter @Setter - private boolean useBukkitAsync = false; + private boolean useFallback = true; private final Set tasks = ConcurrentHashMap.newKeySet(); public BukkitSchedulerAdapter(LPBukkitPlugin plugin) { this.plugin = plugin; - this.asyncLp = Executors.newCachedThreadPool(); - this.asyncBukkit = r -> plugin.getServer().getScheduler().runTaskAsynchronously(plugin, r); - this.sync = r -> plugin.getServer().getScheduler().runTask(plugin, r); - this.async = r -> (useBukkitAsync ? asyncBukkit : asyncLp).execute(r); + this.sync = new SyncExecutor(); + this.asyncFallback = Executors.newCachedThreadPool(); + this.asyncBukkit = new BukkitAsyncExecutor(); + this.async = new AsyncExecutor(); } @Override @@ -103,19 +103,45 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter { @Override public void syncLater(Runnable runnable, long delayTicks) { - plugin.getServer().getScheduler().runTaskLater(plugin, runnable, delayTicks); + plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, runnable, delayTicks); } @Override public void shutdown() { tasks.forEach(BukkitTask::cancel); + // wait for executor - asyncLp.shutdown(); + asyncFallback.shutdown(); try { - asyncLp.awaitTermination(30, TimeUnit.SECONDS); + asyncFallback.awaitTermination(30, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } - } + + private final class SyncExecutor implements Executor { + @Override + public void execute(Runnable runnable) { + plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, runnable); + } + } + + private final class AsyncExecutor implements Executor { + @Override + public void execute(Runnable runnable) { + if (useFallback || !plugin.isEnabled()) { + asyncFallback.execute(runnable); + } else { + asyncBukkit.execute(runnable); + } + } + } + + private final class BukkitAsyncExecutor implements Executor { + @Override + public void execute(Runnable runnable) { + plugin.getServer().getScheduler().runTaskAsynchronously(plugin, runnable); + } + } + } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java index 1c6a47b43..fda9e6e37 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -294,7 +294,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { } // replace the temporary executor when the Bukkit one starts - getServer().getScheduler().runTaskAsynchronously(this, () -> scheduler.setUseBukkitAsync(true)); + getServer().getScheduler().runTaskAsynchronously(this, () -> scheduler.setUseFallback(false)); // Load any online users (in the case of a reload) for (Player player : getServer().getOnlinePlayers()) { @@ -323,8 +323,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { return; } - // Switch back to the LP executor, the bukkit one won't allow new tasks - scheduler.setUseBukkitAsync(false); + // Switch back to the fallback executor, the bukkit one won't allow new tasks + scheduler.setUseFallback(true); started = false; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/utils/MetaComparator.java b/common/src/main/java/me/lucko/luckperms/common/commands/utils/MetaComparator.java index cc122bb99..b3ca5b016 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/utils/MetaComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/utils/MetaComparator.java @@ -40,7 +40,7 @@ public class MetaComparator implements Comparator { - private static final ContextSetComparator INSTANCE = new ContextSetComparator(); - public static Comparator get() { + private static final Comparator INSTANCE = new ContextSetComparator(); + private static final Comparator REVERSE = INSTANCE.reversed(); + + public static Comparator normal() { return INSTANCE; } public static Comparator reverse() { - return INSTANCE.reversed(); + return REVERSE; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetConfigurateSerializer.java b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetConfigurateSerializer.java index 15206e7f2..5a52beb80 100644 --- a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetConfigurateSerializer.java +++ b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetConfigurateSerializer.java @@ -61,10 +61,14 @@ public class ContextSetConfigurateSerializer { return data; } - public static MutableContextSet deserializeContextSet(ConfigurationNode data) { + public static ContextSet deserializeContextSet(ConfigurationNode data) { Preconditions.checkArgument(data.hasMapChildren()); Map dataMap = data.getChildrenMap(); + if (dataMap.isEmpty()) { + return ContextSet.empty(); + } + MutableContextSet map = MutableContextSet.create(); for (Map.Entry e : dataMap.entrySet()) { String k = e.getKey().toString(); diff --git a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetJsonSerializer.java b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetJsonSerializer.java index 0e2ccd766..b015ef20c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetJsonSerializer.java +++ b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetJsonSerializer.java @@ -28,6 +28,7 @@ package me.lucko.luckperms.common.contexts; import lombok.experimental.UtilityClass; import com.google.common.base.Preconditions; +import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -66,10 +67,28 @@ public class ContextSetJsonSerializer { return data; } - public static MutableContextSet deserializeContextSet(JsonElement element) { + public static ContextSet deserializeContextSet(Gson gson, String json) { + Preconditions.checkNotNull(json, "json"); + if (json.equals("{}")) { + return ContextSet.empty(); + } + + JsonObject context = gson.fromJson(json, JsonObject.class); + if (context == null || context.size() == 0) { + return ContextSet.empty(); + } + + return deserializeContextSet(context); + } + + public static ContextSet deserializeContextSet(JsonElement element) { Preconditions.checkArgument(element.isJsonObject()); JsonObject data = element.getAsJsonObject(); + if (data.size() == 0) { + return ContextSet.empty(); + } + MutableContextSet map = MutableContextSet.create(); for (Map.Entry e : data.entrySet()) { String k = e.getKey(); diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeComparator.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeComparator.java index ebdd8fc98..9e2fc8a4a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeComparator.java @@ -26,17 +26,21 @@ package me.lucko.luckperms.common.node; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.common.utils.CollationKeyCache; import java.util.Comparator; public class NodeComparator implements Comparator { - private static final NodeComparator INSTANCE = new NodeComparator(); - public static Comparator get() { + + private static final Comparator INSTANCE = new NodeComparator(); + private static final Comparator REVERSE = INSTANCE.reversed(); + + public static Comparator normal() { return INSTANCE; } public static Comparator reverse() { - return INSTANCE.reversed(); + return REVERSE; } @Override @@ -61,7 +65,7 @@ public class NodeComparator implements Comparator { return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1; } - return NodeWithContextComparator.get().compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1; + return CollationKeyCache.compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java index 5c194a0f3..b5441bdc6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java @@ -74,13 +74,13 @@ public final class NodeModel { public synchronized Node toNode() { if (node == null) { - Node.Builder builder = NodeFactory.newBuilder(permission); - builder.setValue(value); - builder.setServer(server); - builder.setWorld(world); - builder.setExpiry(expiry); - builder.withExtraContext(contexts); - node = builder.build(); + node = NodeFactory.newBuilder(permission) + .setValue(value) + .setServer(server) + .setWorld(world) + .setExpiry(expiry) + .withExtraContext(contexts) + .build(); } return node; diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeWithContextComparator.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeWithContextComparator.java index 8beaa38b0..be4508b02 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeWithContextComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeWithContextComparator.java @@ -28,16 +28,11 @@ package me.lucko.luckperms.common.node; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; - import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.common.utils.CollationKeyCache; -import java.text.CollationKey; -import java.text.Collator; import java.util.Comparator; -import java.util.Locale; /** * Compares permission nodes based upon their supposed "priority". @@ -45,16 +40,16 @@ import java.util.Locale; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class NodeWithContextComparator implements Comparator { - private static final NodeWithContextComparator INSTANCE = new NodeWithContextComparator(); - public static NodeWithContextComparator get() { + private static final Comparator INSTANCE = new NodeWithContextComparator(); + private static final Comparator REVERSE = INSTANCE.reversed(); + + public static Comparator normal() { return INSTANCE; } - public static Comparator reverse() { - return INSTANCE.reversed(); - } - private final Collator collator = Collator.getInstance(Locale.ENGLISH); - private final LoadingCache collationKeyCache = Caffeine.newBuilder().build(collator::getCollationKey); + public static Comparator reverse() { + return REVERSE; + } @Override public int compare(LocalizedNode one, LocalizedNode two) { @@ -97,27 +92,8 @@ public class NodeWithContextComparator implements Comparator { return o1.getWildcardLevel() > o2.getWildcardLevel() ? 1 : -1; } - return compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1; + return CollationKeyCache.compareStrings(o1.getPermission(), o2.getPermission()) == 1 ? -1 : 1; } - public int compareStrings(String o1, String o2) { - if (o1.equals(o2)) { - return 0; - } - try { - CollationKey o1c = collationKeyCache.get(o1); - CollationKey o2c = collationKeyCache.get(o2); - int i = o1c.compareTo(o2c); - if (i != 0) { - return i; - } - - // fallback to standard string comparison - return o1.compareTo(o2); - } catch (Exception e) { - // ignored - } - return 1; - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/dao/sql/SqlDao.java b/common/src/main/java/me/lucko/luckperms/common/storage/dao/sql/SqlDao.java index 099613a3b..47950e82c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/dao/sql/SqlDao.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/dao/sql/SqlDao.java @@ -30,7 +30,6 @@ import lombok.Getter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.gson.Gson; -import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import me.lucko.luckperms.api.HeldPermission; @@ -1129,7 +1128,6 @@ public class SqlDao extends AbstractDao { } private NodeModel deserializeNode(String permission, boolean value, String server, String world, long expiry, String contexts) { - JsonObject context = gson.fromJson(contexts, JsonObject.class); - return NodeModel.of(permission, value, server, world, expiry, ContextSetJsonSerializer.deserializeContextSet(context).makeImmutable()); + return NodeModel.of(permission, value, server, world, expiry, ContextSetJsonSerializer.deserializeContextSet(gson, contexts).makeImmutable()); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/CollationKeyCache.java b/common/src/main/java/me/lucko/luckperms/common/utils/CollationKeyCache.java new file mode 100644 index 000000000..d15555412 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/utils/CollationKeyCache.java @@ -0,0 +1,91 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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.utils; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; + +import java.text.CollationKey; +import java.text.Collator; +import java.util.Comparator; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public final class CollationKeyCache implements Comparator { + private static final CollationKeyCache INSTANCE = new CollationKeyCache(); + + private static final Collator COLLATOR = Collator.getInstance(Locale.ENGLISH); + + static { + COLLATOR.setStrength(Collator.IDENTICAL); + COLLATOR.setDecomposition(Collator.FULL_DECOMPOSITION); + } + + private static final LoadingCache CACHE = Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterAccess(5, TimeUnit.MINUTES) + .build(COLLATOR::getCollationKey); + + public static Comparator comparator() { + return INSTANCE; + } + + private CollationKeyCache() { + + } + + @Override + public int compare(String o1, String o2) { + return compareStrings(o1, o2); + } + + public static int compareStrings(String o1, String o2) { + //noinspection StringEquality + if (o1 == o2) { + return 0; + } + + try { + CollationKey o1c = CACHE.get(o1); + CollationKey o2c = CACHE.get(o2); + + if (o1c != null && o2c != null) { + int i = o1c.compareTo(o2c); + if (i != 0) { + return i; + } + } + + // fallback to standard string comparison + return o1.compareTo(o2); + } catch (Exception e) { + // ignored + } + + // shrug + return 0; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java index f1d03469c..fd5fd99ea 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/storage/SubjectStorageModel.java @@ -37,7 +37,7 @@ import com.google.gson.JsonObject; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.contexts.ContextSetComparator; import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer; -import me.lucko.luckperms.common.node.NodeWithContextComparator; +import me.lucko.luckperms.common.utils.CollationKeyCache; import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.SubjectReference; @@ -195,7 +195,7 @@ public class SubjectStorageModel { // sort alphabetically. List> perms = new ArrayList<>(e.getValue().entrySet()); - perms.sort((o1, o2) -> NodeWithContextComparator.get().compareStrings(o1.getKey(), o2.getKey())); + perms.sort(Map.Entry.comparingByKey(CollationKeyCache.comparator())); for (Map.Entry ent : perms) { data.addProperty(ent.getKey(), ent.getValue()); @@ -219,7 +219,7 @@ public class SubjectStorageModel { // sort alphabetically. List> opts = new ArrayList<>(e.getValue().entrySet()); - opts.sort((o1, o2) -> NodeWithContextComparator.get().compareStrings(o1.getKey(), o2.getKey())); + opts.sort(Map.Entry.comparingByKey(CollationKeyCache.comparator())); for (Map.Entry ent : opts) { data.addProperty(ent.getKey(), ent.getValue());