Remove the locks in NodeMap to ease thread contention when lots of processes are resolving inheritance & refactor the way LocalizedNodes are created (#734)

This commit is contained in:
Luck 2018-05-04 16:16:12 +01:00
parent 55d59bb1c4
commit 2dbbea4993
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
13 changed files with 219 additions and 320 deletions

View File

@ -556,7 +556,7 @@ public interface Node {
*
* @param value the value
* @return the builder
* @see Node#getValuePrimitive()
* @see Node#getValue()
*/
@Nonnull
Builder setValue(boolean value);

View File

@ -44,7 +44,7 @@ public enum StandardNodeEquality implements NodeEqualityPredicate {
/**
* All attributes must match, except for
* {@link Node#getValuePrimitive() value}, which is ignored.
* {@link Node#getValue() value}, which is ignored.
*/
IGNORE_VALUE,
@ -59,14 +59,14 @@ public enum StandardNodeEquality implements NodeEqualityPredicate {
/**
* All attributes must match, except for
* {@link Node#getValuePrimitive() value} and the
* {@link Node#getValue() value} and the
* {@link Node#getExpiry() expiry time}, which are ignored.
*/
IGNORE_EXPIRY_TIME_AND_VALUE,
/**
* All attributes must match, except for
* {@link Node#getValuePrimitive() value} and the if the node is
* {@link Node#getValue() value} and the if the node is
* {@link Node#isTemporary() temporary}, which are ignored.
*/
IGNORE_VALUE_OR_IF_TEMPORARY;

View File

@ -46,6 +46,7 @@ import me.lucko.luckperms.common.model.NodeMapType;
import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator;
import me.lucko.luckperms.common.node.factory.NodeFactory;
import me.lucko.luckperms.common.node.utils.MetaType;
import me.lucko.luckperms.common.node.utils.NodeTools;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
@ -104,13 +105,15 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol
@Nonnull
@Override
public ImmutableSetMultimap<ImmutableContextSet, Node> getNodes() {
return this.handle.enduringData().immutable();
//noinspection unchecked
return (ImmutableSetMultimap) this.handle.enduringData().immutable();
}
@Nonnull
@Override
public ImmutableSetMultimap<ImmutableContextSet, Node> getTransientNodes() {
return this.handle.transientData().immutable();
//noinspection unchecked
return (ImmutableSetMultimap) this.handle.transientData().immutable();
}
@Nonnull
@ -183,7 +186,7 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol
@Override
public Map<String, Boolean> exportNodes(@Nonnull Contexts contexts, boolean lowerCase) {
Objects.requireNonNull(contexts, "contexts");
return ImmutableMap.copyOf(this.handle.exportNodesAndShorthand(contexts, lowerCase));
return ImmutableMap.copyOf(this.handle.exportPermissions(contexts, lowerCase, true));
}
@Nonnull
@ -234,14 +237,26 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol
@Override
public boolean inheritsGroup(@Nonnull me.lucko.luckperms.api.Group group) {
Objects.requireNonNull(group, "group");
return this.handle.inheritsGroup(ApiGroup.cast(group));
Group g = ApiGroup.cast(group);
if (this.handle.getType().isGroup() && g.getName().equals(this.handle.getObjectName())) {
return true;
}
return this.handle.hasPermission(NodeMapType.ENDURING, NodeFactory.buildGroupNode(g.getName()).build(), StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean();
}
@Override
public boolean inheritsGroup(@Nonnull me.lucko.luckperms.api.Group group, @Nonnull ContextSet contextSet) {
Objects.requireNonNull(group, "group");
Objects.requireNonNull(contextSet, "contextSet");
return this.handle.inheritsGroup(ApiGroup.cast(group), contextSet);
Group g = ApiGroup.cast(group);
if (this.handle.getType().isGroup() && g.getName().equals(this.handle.getObjectName())) {
return true;
}
return this.handle.hasPermission(NodeMapType.ENDURING, NodeFactory.buildGroupNode(g.getName()).withExtraContext(contextSet).build(), StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean();
}
@Nonnull

View File

@ -190,7 +190,7 @@ public abstract class AbstractCachedData implements CachedData {
Objects.requireNonNull(contexts, "contexts");
//noinspection ConstantConditions
return this.permission.get(contexts).join();
return this.permission.synchronous().get(contexts);
}
@Nonnull
@ -199,7 +199,7 @@ public abstract class AbstractCachedData implements CachedData {
Objects.requireNonNull(contexts, "contexts");
//noinspection ConstantConditions
return this.meta.get(contexts).join();
return this.meta.synchronous().get(contexts);
}
@Nonnull

View File

@ -29,6 +29,7 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.caching.MetaContexts;
import me.lucko.luckperms.common.caching.type.MetaAccumulator;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.PermissionHolder;
import java.util.Map;
@ -60,12 +61,12 @@ public abstract class HolderCachedData<T extends PermissionHolder> extends Abstr
@Override
protected Map<String, Boolean> resolvePermissions() {
return this.holder.exportNodesAndShorthand(true);
return this.holder.exportPermissions(true, this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND));
}
@Override
protected Map<String, Boolean> resolvePermissions(Contexts contexts) {
return this.holder.exportNodesAndShorthand(contexts, true);
return this.holder.exportPermissions(contexts, true, this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND));
}
@Override

View File

@ -25,6 +25,8 @@
package me.lucko.luckperms.common.commands.generic.parent;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.StandardNodeEquality;
import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
import me.lucko.luckperms.common.command.CommandResult;
import me.lucko.luckperms.common.command.abstraction.SharedSubCommand;
@ -36,6 +38,7 @@ import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.locale.command.CommandSpec;
import me.lucko.luckperms.common.locale.message.Message;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.NodeMapType;
import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.factory.NodeFactory;
@ -80,9 +83,10 @@ public class UserSwitchPrimaryGroup extends SharedSubCommand {
return CommandResult.STATE_ERROR;
}
if (!user.inheritsGroup(group)) {
Node node = NodeFactory.buildGroupNode(group.getName()).build();
if (!user.hasPermission(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_VALUE).asBoolean()) {
Message.USER_PRIMARYGROUP_ERROR_NOTMEMBER.send(sender, user.getFriendlyName(), group.getName());
user.setPermission(NodeFactory.buildGroupNode(group.getName()).build());
user.setPermission(node);
}
user.getPrimaryGroup().setStoredValue(group.getName());

View File

@ -54,7 +54,7 @@ public class LogNotify extends SubCommand<Log> {
return false;
}
Optional<Node> ret = user.getOwnNodes().stream()
Optional<? extends Node> ret = user.getOwnNodes().stream()
.filter(n -> n.getPermission().equalsIgnoreCase("luckperms.log.notify.ignoring"))
.findFirst();

View File

@ -170,17 +170,17 @@ public final class EventFactory {
fireEventAsync(event);
}
public void handleNodeAdd(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) {
public void handleNodeAdd(Node node, PermissionHolder target, Collection<? extends Node> before, Collection<? extends Node> after) {
EventNodeAdd event = new EventNodeAdd(node, getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEventAsync(event);
}
public void handleNodeClear(PermissionHolder target, Collection<Node> before, Collection<Node> after) {
public void handleNodeClear(PermissionHolder target, Collection<? extends Node> before, Collection<? extends Node> after) {
EventNodeClear event = new EventNodeClear(getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEventAsync(event);
}
public void handleNodeRemove(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) {
public void handleNodeRemove(Node node, PermissionHolder target, Collection<? extends Node> before, Collection<? extends Node> after) {
EventNodeRemove event = new EventNodeRemove(node, getDelegate(target), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEventAsync(event);
}

View File

@ -82,7 +82,7 @@ public class InheritanceHandler {
@Override
public Iterable<? extends PermissionHolder> successors(PermissionHolder holder) {
Set<Group> successors = new TreeSet<>(holder.getInheritanceComparator());
List<Node> nodes = holder.getOwnGroupNodes();
List<? extends Node> nodes = holder.getOwnGroupNodes();
for (Node n : nodes) {
Group g = this.plugin.getGroupManager().getIfLoaded(n.getGroupName());
if (g != null) {
@ -109,7 +109,7 @@ public class InheritanceHandler {
@Override
public Iterable<? extends PermissionHolder> successors(PermissionHolder holder) {
Set<Group> successors = new TreeSet<>(holder.getInheritanceComparator());
List<Node> nodes = holder.getOwnGroupNodes(this.context.getContexts());
List<? extends Node> nodes = holder.getOwnGroupNodes(this.context.getContexts());
for (Node n : nodes) {
// effectively: if not (we're applying global groups or it's specific anyways)
if (!((this.context.hasSetting(LookupSetting.APPLY_PARENTS_SET_WITHOUT_SERVER) || n.isServerSpecific()) && (this.context.hasSetting(LookupSetting.APPLY_PARENTS_SET_WITHOUT_WORLD) || n.isWorldSpecific()))) {

View File

@ -25,9 +25,10 @@
package me.lucko.luckperms.common.model;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SortedSetMultimap;
import me.lucko.luckperms.api.LocalizedNode;
@ -50,7 +51,8 @@ import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
@ -66,6 +68,8 @@ import javax.annotation.Nullable;
* <p>Each holder has two of these maps, one for enduring and transient nodes.</p>
*/
public final class NodeMap {
@SuppressWarnings("Guava")
private static final Supplier<SortedSet<LocalizedNode>> VALUE_SET_SUPPLIER = () -> new ConcurrentSkipListSet<>(NodeComparator.reverse());
/**
* The holder which this map is for
@ -80,24 +84,19 @@ public final class NodeMap {
* key, and finally by the overall size of the set. Nodes are ordered according to the priority rules
* defined in {@link NodeComparator}.</p>
*/
private final SortedSetMultimap<ImmutableContextSet, Node> map = MultimapBuilder
.treeKeys(ContextSetComparator.reverse())
.treeSetValues(NodeComparator.reverse())
.build();
private final SortedSetMultimap<ImmutableContextSet, LocalizedNode> map = Multimaps.newSortedSetMultimap(
new ConcurrentSkipListMap<>(ContextSetComparator.reverse()),
VALUE_SET_SUPPLIER
);
/**
* Copy of {@link #map} which only contains group nodes
* @see Node#isGroupNode()
*/
private final SortedSetMultimap<ImmutableContextSet, Node> inheritanceMap = MultimapBuilder
.treeKeys(ContextSetComparator.reverse())
.treeSetValues(NodeComparator.reverse())
.build();
/**
* The lock which synchronizes the instance
*/
private final ReentrantLock lock = new ReentrantLock();
private final SortedSetMultimap<ImmutableContextSet, LocalizedNode> inheritanceMap = Multimaps.newSortedSetMultimap(
new ConcurrentSkipListMap<>(ContextSetComparator.reverse()),
VALUE_SET_SUPPLIER
);
/**
* A cache which holds an immutable copy of the backing map
@ -108,82 +107,41 @@ public final class NodeMap {
this.holder = holder;
}
public List<Node> asList() {
this.lock.lock();
try {
return new ArrayList<>(this.map.values());
} finally {
this.lock.unlock();
}
public List<LocalizedNode> asList() {
return new ArrayList<>(this.map.values());
}
public LinkedHashSet<Node> asSet() {
this.lock.lock();
try {
return new LinkedHashSet<>(this.map.values());
} finally {
this.lock.unlock();
}
public LinkedHashSet<LocalizedNode> asSet() {
return new LinkedHashSet<>(this.map.values());
}
public SortedSet<LocalizedNode> asSortedSet() {
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
copyToLocalized(ret);
copyTo(ret);
return ret;
}
public void copyTo(Collection<? super Node> collection) {
this.lock.lock();
try {
collection.addAll(this.map.values());
} finally {
this.lock.unlock();
}
public void copyTo(Collection<? super LocalizedNode> collection) {
collection.addAll(this.map.values());
}
public void copyTo(Collection<? super Node> collection, ContextSet filter) {
this.lock.lock();
try {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.map.asMap().entrySet()) {
if (e.getKey().isSatisfiedBy(filter)) {
collection.addAll(e.getValue());
}
public void copyTo(Collection<? super LocalizedNode> collection, ContextSet filter) {
for (Map.Entry<ImmutableContextSet, Collection<LocalizedNode>> e : this.map.asMap().entrySet()) {
if (e.getKey().isSatisfiedBy(filter)) {
collection.addAll(e.getValue());
}
} finally {
this.lock.unlock();
}
}
public void copyGroupNodesTo(Collection<? super Node> collection) {
this.lock.lock();
try {
collection.addAll(this.inheritanceMap.values());
} finally {
this.lock.unlock();
}
public void copyGroupNodesTo(Collection<? super LocalizedNode> collection) {
collection.addAll(this.inheritanceMap.values());
}
public void copyGroupNodesTo(Collection<? super Node> collection, ContextSet filter) {
this.lock.lock();
try {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : this.inheritanceMap.asMap().entrySet()) {
if (e.getKey().isSatisfiedBy(filter)) {
collection.addAll(e.getValue());
}
public void copyGroupNodesTo(Collection<? super LocalizedNode> collection, ContextSet filter) {
for (Map.Entry<ImmutableContextSet, Collection<LocalizedNode>> e : this.inheritanceMap.asMap().entrySet()) {
if (e.getKey().isSatisfiedBy(filter)) {
collection.addAll(e.getValue());
}
} finally {
this.lock.unlock();
}
}
public void copyToLocalized(Collection<LocalizedNode> collection) {
this.lock.lock();
try {
for (Node node : this.map.values()) {
collection.add(ImmutableLocalizedNode.of(node, this.holder.getObjectName()));
}
} finally {
this.lock.unlock();
}
}
@ -192,7 +150,7 @@ public final class NodeMap {
*
* @return an immutable copy
*/
public ImmutableSetMultimap<ImmutableContextSet, Node> immutable() {
public ImmutableSetMultimap<ImmutableContextSet, LocalizedNode> immutable() {
return this.cache.get();
}
@ -203,161 +161,108 @@ public final class NodeMap {
this.cache.invalidate();
}
void add(Node node) {
this.lock.lock();
try {
ImmutableContextSet context = node.getFullContexts().makeImmutable();
this.map.put(context, node);
if (node.isGroupNode() && node.getValue()) {
this.inheritanceMap.put(context, node);
private LocalizedNode localise(Node node) {
if (node instanceof LocalizedNode) {
LocalizedNode localizedNode = (LocalizedNode) node;
if (this.holder.getObjectName().equals(localizedNode.getLocation())) {
return localizedNode;
}
} finally {
this.lock.unlock();
}
// localise
return ImmutableLocalizedNode.of(node, this.holder.getObjectName());
}
void add(Node node) {
ImmutableContextSet context = node.getFullContexts().makeImmutable();
LocalizedNode n = localise(node);
this.map.put(context, n);
if (node.isGroupNode() && node.getValue()) {
this.inheritanceMap.put(context, n);
}
}
void remove(Node node) {
this.lock.lock();
try {
ImmutableContextSet context = node.getFullContexts().makeImmutable();
this.map.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE));
if (node.isGroupNode()) {
this.inheritanceMap.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE));
}
} finally {
this.lock.unlock();
ImmutableContextSet context = node.getFullContexts().makeImmutable();
this.map.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE));
if (node.isGroupNode()) {
this.inheritanceMap.get(context).removeIf(e -> e.equals(node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE));
}
}
private void removeExact(Node node) {
this.lock.lock();
try {
ImmutableContextSet context = node.getFullContexts().makeImmutable();
this.map.remove(context, node);
if (node.isGroupNode() && node.getValue()) {
this.inheritanceMap.remove(context, node);
}
} finally {
this.lock.unlock();
ImmutableContextSet context = node.getFullContexts().makeImmutable();
this.map.remove(context, node);
if (node.isGroupNode() && node.getValue()) {
this.inheritanceMap.remove(context, node);
}
}
void replace(Node node, Node previous) {
this.lock.lock();
try {
removeExact(previous);
add(node);
} finally {
this.lock.unlock();
}
removeExact(previous);
add(node);
}
void clear() {
this.lock.lock();
try {
this.map.clear();
this.inheritanceMap.clear();
} finally {
this.lock.unlock();
}
this.map.clear();
this.inheritanceMap.clear();
}
void clear(ContextSet contextSet) {
this.lock.lock();
try {
ImmutableContextSet context = contextSet.makeImmutable();
this.map.removeAll(context);
this.inheritanceMap.removeAll(context);
} finally {
this.lock.unlock();
ImmutableContextSet context = contextSet.makeImmutable();
this.map.removeAll(context);
this.inheritanceMap.removeAll(context);
}
void setContent(Collection<? extends Node> set) {
this.map.clear();
this.inheritanceMap.clear();
for (Node n : set) {
add(n);
}
}
void setContent(Set<Node> set) {
this.lock.lock();
try {
this.map.clear();
this.inheritanceMap.clear();
for (Node n : set) {
add(n);
}
} finally {
this.lock.unlock();
}
}
void setContent(Multimap<ImmutableContextSet, Node> multimap) {
this.lock.lock();
try {
this.map.clear();
this.inheritanceMap.clear();
this.map.putAll(multimap);
for (Map.Entry<ImmutableContextSet, Node> entry : this.map.entries()) {
if (entry.getValue().isGroupNode() && entry.getValue().getValue()) {
this.inheritanceMap.put(entry.getKey(), entry.getValue());
}
}
} finally {
this.lock.unlock();
}
void setContent(Multimap<ImmutableContextSet, ? extends Node> multimap) {
setContent(multimap.values());
}
boolean removeIf(Predicate<? super Node> predicate) {
this.lock.lock();
try {
boolean ret = this.map.values().removeIf(predicate);
if (ret) {
this.inheritanceMap.values().removeIf(predicate);
}
return ret;
} finally {
this.lock.unlock();
}
boolean ret = this.map.values().removeIf(predicate);
this.inheritanceMap.values().removeIf(predicate);
return ret;
}
boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate) {
this.lock.lock();
try {
ImmutableContextSet context = contextSet.makeImmutable();
SortedSet<Node> nodes = this.map.get(context);
boolean ret = nodes.removeIf(predicate);
if (ret) {
this.inheritanceMap.get(context).removeIf(predicate);
}
return ret;
} finally {
this.lock.unlock();
}
ImmutableContextSet context = contextSet.makeImmutable();
SortedSet<LocalizedNode> nodes = this.map.get(context);
boolean ret = nodes.removeIf(predicate);
this.inheritanceMap.get(context).removeIf(predicate);
return ret;
}
boolean auditTemporaryNodes(@Nullable Set<Node> removed) {
boolean work = false;
this.lock.lock();
try {
Iterator<Node> it = this.map.values().iterator();
while (it.hasNext()) {
Node entry = it.next();
if (entry.hasExpired()) {
if (removed != null) {
removed.add(entry);
}
if (entry.isGroupNode() && entry.getValue()) {
this.inheritanceMap.remove(entry.getFullContexts().makeImmutable(), entry);
}
work = true;
it.remove();
Iterator<? extends Node> it = this.map.values().iterator();
while (it.hasNext()) {
Node entry = it.next();
if (entry.hasExpired()) {
if (removed != null) {
removed.add(entry);
}
if (entry.isGroupNode() && entry.getValue()) {
this.inheritanceMap.remove(entry.getFullContexts().makeImmutable(), entry);
}
work = true;
it.remove();
}
} finally {
this.lock.unlock();
}
return work;
}
private static final class NodeMapCache extends Cache<ImmutableSetMultimap<ImmutableContextSet, Node>> {
private static final class NodeMapCache extends Cache<ImmutableSetMultimap<ImmutableContextSet, LocalizedNode>> {
private final NodeMap handle;
private NodeMapCache(NodeMap handle) {
@ -366,13 +271,8 @@ public final class NodeMap {
@Nonnull
@Override
protected ImmutableSetMultimap<ImmutableContextSet, Node> supply() {
this.handle.lock.lock();
try {
return ImmutableSetMultimap.copyOf(this.handle.map);
} finally {
this.handle.lock.unlock();
}
protected ImmutableSetMultimap<ImmutableContextSet, LocalizedNode> supply() {
return ImmutableSetMultimap.copyOf(this.handle.map);
}
}

View File

@ -46,8 +46,6 @@ import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.inheritance.InheritanceComparator;
import me.lucko.luckperms.common.inheritance.InheritanceGraph;
import me.lucko.luckperms.common.node.comparator.NodeWithContextComparator;
import me.lucko.luckperms.common.node.factory.NodeFactory;
import me.lucko.luckperms.common.node.model.ImmutableLocalizedNode;
import me.lucko.luckperms.common.node.utils.InheritanceInfo;
import me.lucko.luckperms.common.node.utils.MetaType;
import me.lucko.luckperms.common.node.utils.NodeTools;
@ -218,39 +216,39 @@ public abstract class PermissionHolder {
reloadCachedData();
}
public void setNodes(NodeMapType type, Set<Node> set) {
public void setNodes(NodeMapType type, Set<? extends Node> set) {
getData(type).setContent(set);
invalidateCache();
}
public void replaceNodes(NodeMapType type, Multimap<ImmutableContextSet, Node> multimap) {
public void replaceNodes(NodeMapType type, Multimap<ImmutableContextSet, ? extends Node> multimap) {
getData(type).setContent(multimap);
invalidateCache();
}
public List<Node> getOwnNodes() {
List<Node> ret = new ArrayList<>();
public List<LocalizedNode> getOwnNodes() {
List<LocalizedNode> ret = new ArrayList<>();
this.transientNodes.copyTo(ret);
this.enduringNodes.copyTo(ret);
return ret;
}
public List<Node> getOwnNodes(ContextSet filter) {
List<Node> ret = new ArrayList<>();
public List<LocalizedNode> getOwnNodes(ContextSet filter) {
List<LocalizedNode> ret = new ArrayList<>();
this.transientNodes.copyTo(ret, filter);
this.enduringNodes.copyTo(ret, filter);
return ret;
}
public List<Node> getOwnGroupNodes() {
List<Node> ret = new ArrayList<>();
public List<LocalizedNode> getOwnGroupNodes() {
List<LocalizedNode> ret = new ArrayList<>();
this.transientNodes.copyGroupNodesTo(ret);
this.enduringNodes.copyGroupNodesTo(ret);
return ret;
}
public List<Node> getOwnGroupNodes(ContextSet filter) {
List<Node> ret = new ArrayList<>();
public List<LocalizedNode> getOwnGroupNodes(ContextSet filter) {
List<LocalizedNode> ret = new ArrayList<>();
this.transientNodes.copyGroupNodesTo(ret, filter);
this.enduringNodes.copyGroupNodesTo(ret, filter);
return ret;
@ -258,17 +256,17 @@ public abstract class PermissionHolder {
public SortedSet<LocalizedNode> getOwnNodesSorted() {
SortedSet<LocalizedNode> ret = new TreeSet<>(NodeWithContextComparator.reverse());
this.transientNodes.copyToLocalized(ret);
this.enduringNodes.copyToLocalized(ret);
this.transientNodes.copyTo(ret);
this.enduringNodes.copyTo(ret);
return ret;
}
public boolean removeIf(Predicate<Node> predicate) {
public boolean removeIf(Predicate<? super Node> predicate) {
return removeIf(predicate, null);
}
public boolean removeIf(Predicate<Node> predicate, Runnable taskIfSuccess) {
ImmutableCollection<Node> before = enduringData().immutable().values();
public boolean removeIf(Predicate<? super Node> predicate, Runnable taskIfSuccess) {
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
if (!this.enduringNodes.removeIf(predicate)) {
return false;
}
@ -276,18 +274,18 @@ public abstract class PermissionHolder {
taskIfSuccess.run();
}
invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeClear(this, before, after);
return true;
}
public boolean removeIf(ContextSet contextSet, Predicate<Node> predicate) {
public boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate) {
return removeIf(contextSet, predicate, null);
}
public boolean removeIf(ContextSet contextSet, Predicate<Node> predicate, Runnable taskIfSuccess) {
ImmutableCollection<Node> before = enduringData().immutable().values();
public boolean removeIf(ContextSet contextSet, Predicate<? super Node> predicate, Runnable taskIfSuccess) {
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
if (!this.enduringNodes.removeIf(contextSet, predicate)) {
return false;
}
@ -295,13 +293,13 @@ public abstract class PermissionHolder {
taskIfSuccess.run();
}
invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeClear(this, before, after);
return true;
}
public boolean removeIfTransient(Predicate<Node> predicate) {
public boolean removeIfTransient(Predicate<? super Node> predicate) {
boolean result = this.transientNodes.removeIf(predicate);
if (result) {
invalidateCache();
@ -309,15 +307,12 @@ public abstract class PermissionHolder {
return result;
}
public void accumulateInheritancesTo(List<LocalizedNode> accumulator, Contexts context) {
public void accumulateInheritancesTo(List<? super LocalizedNode> accumulator, Contexts context) {
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(context);
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
for (PermissionHolder holder : traversal) {
List<Node> nodes = holder.getOwnNodes(context.getContexts());
for (Node node : nodes) {
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, holder.getObjectName());
accumulator.add(localizedNode);
}
List<? extends LocalizedNode> nodes = holder.getOwnNodes(context.getContexts());
accumulator.addAll(nodes);
}
}
@ -327,15 +322,12 @@ public abstract class PermissionHolder {
return accumulator;
}
public void accumulateInheritancesTo(List<LocalizedNode> accumulator) {
public void accumulateInheritancesTo(List<? super LocalizedNode> accumulator) {
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph();
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
for (PermissionHolder holder : traversal) {
List<Node> nodes = holder.getOwnNodes();
for (Node node : nodes) {
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(node, holder.getObjectName());
accumulator.add(localizedNode);
}
List<? extends LocalizedNode> nodes = holder.getOwnNodes();
accumulator.addAll(nodes);
}
}
@ -350,10 +342,7 @@ public abstract class PermissionHolder {
if (context.hasSetting(LookupSetting.RESOLVE_INHERITANCE)) {
accumulateInheritancesTo(entries, context);
} else {
for (Node n : getOwnNodes(context.getContexts())) {
ImmutableLocalizedNode localizedNode = ImmutableLocalizedNode.of(n, getObjectName());
entries.add(localizedNode);
}
entries.addAll(getOwnNodes(context.getContexts()));
}
if (!context.hasSetting(LookupSetting.INCLUDE_NODES_SET_WITHOUT_SERVER)) {
@ -366,42 +355,34 @@ public abstract class PermissionHolder {
return entries;
}
public Map<String, Boolean> exportNodesAndShorthand(Contexts context, boolean lowerCase) {
public Map<String, Boolean> exportPermissions(Contexts context, boolean convertToLowercase, boolean resolveShorthand) {
List<LocalizedNode> entries = getAllEntries(context);
return processExportedPermissions(entries, convertToLowercase, resolveShorthand);
}
Map<String, Boolean> perms = new HashMap<>();
boolean applyShorthand = this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND);
public Map<String, Boolean> exportPermissions(boolean convertToLowercase, boolean resolveShorthand) {
List<LocalizedNode> entries = resolveInheritances();
return processExportedPermissions(entries, convertToLowercase, resolveShorthand);
}
private static ImmutableMap<String, Boolean> processExportedPermissions(List<LocalizedNode> entries, boolean convertToLowercase, boolean resolveShorthand) {
Map<String, Boolean> perms = new HashMap<>(entries.size());
for (Node node : entries) {
String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission();
if (perms.putIfAbsent(perm, node.getValue()) == null) {
if (applyShorthand) {
List<String> shorthand = node.resolveShorthand();
if (!shorthand.isEmpty()) {
for (String s : shorthand) {
perms.putIfAbsent(lowerCase ? s.toLowerCase() : s, node.getValue());
}
}
}
if (convertToLowercase) {
perms.putIfAbsent(node.getPermission().toLowerCase(), node.getValue());
} else {
perms.putIfAbsent(node.getPermission(), node.getValue());
}
}
return ImmutableMap.copyOf(perms);
}
public Map<String, Boolean> exportNodesAndShorthand(boolean lowerCase) {
List<LocalizedNode> entries = resolveInheritances();
Map<String, Boolean> perms = new HashMap<>();
boolean applyShorthand = this.plugin.getConfiguration().get(ConfigKeys.APPLYING_SHORTHAND);
for (Node node : entries) {
String perm = lowerCase ? node.getPermission().toLowerCase().intern() : node.getPermission();
if (perms.putIfAbsent(perm, node.getValue()) == null && applyShorthand) {
if (resolveShorthand) {
for (Node node : entries) {
List<String> shorthand = node.resolveShorthand();
if (!shorthand.isEmpty()) {
for (String s : shorthand) {
perms.putIfAbsent((lowerCase ? s.toLowerCase() : s).intern(), node.getValue());
for (String s : shorthand) {
if (convertToLowercase) {
perms.putIfAbsent(s.toLowerCase(), node.getValue());
} else {
perms.putIfAbsent(s, node.getValue());
}
}
}
@ -418,8 +399,8 @@ public abstract class PermissionHolder {
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph(context);
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
for (PermissionHolder holder : traversal) {
List<Node> nodes = holder.getOwnNodes(context.getContexts());
for (Node node : nodes) {
List<? extends LocalizedNode> nodes = holder.getOwnNodes(context.getContexts());
for (LocalizedNode node : nodes) {
if (!node.getValue()) continue;
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
@ -427,7 +408,7 @@ public abstract class PermissionHolder {
continue;
}
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, holder.getObjectName()));
accumulator.accumulateNode(node);
}
OptionalInt w = holder.getWeight();
@ -447,12 +428,12 @@ public abstract class PermissionHolder {
InheritanceGraph graph = this.plugin.getInheritanceHandler().getGraph();
Iterable<PermissionHolder> traversal = graph.traverse(this.plugin.getConfiguration().get(ConfigKeys.INHERITANCE_TRAVERSAL_ALGORITHM), this);
for (PermissionHolder holder : traversal) {
List<Node> nodes = holder.getOwnNodes();
for (Node node : nodes) {
List<? extends LocalizedNode> nodes = holder.getOwnNodes();
for (LocalizedNode node : nodes) {
if (!node.getValue()) continue;
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, holder.getObjectName()));
accumulator.accumulateNode(node);
}
OptionalInt w = getWeight();
@ -474,7 +455,7 @@ public abstract class PermissionHolder {
// we don't call events for transient nodes
boolean transientWork = this.transientNodes.auditTemporaryNodes(null);
ImmutableCollection<Node> before = enduringData().immutable().values();
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
Set<Node> removed = new HashSet<>();
boolean enduringWork = this.enduringNodes.auditTemporaryNodes(removed);
@ -483,7 +464,7 @@ public abstract class PermissionHolder {
invalidateCache();
// call event
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
for (Node r : removed) {
this.plugin.getEventFactory().handleNodeRemove(r, this, before, after);
}
@ -496,8 +477,8 @@ public abstract class PermissionHolder {
return transientWork || enduringWork;
}
private Optional<Node> searchForMatch(NodeMapType type, Node node, NodeEqualityPredicate equalityPredicate) {
for (Node n : getData(type).immutable().values()) {
private Optional<LocalizedNode> searchForMatch(NodeMapType type, Node node, NodeEqualityPredicate equalityPredicate) {
for (LocalizedNode n : getData(type).immutable().values()) {
if (n.equals(node, equalityPredicate)) {
return Optional.of(n);
}
@ -559,10 +540,10 @@ public abstract class PermissionHolder {
return DataMutateResult.ALREADY_HAS;
}
ImmutableCollection<Node> before = enduringData().immutable().values();
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.add(node);
invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
return DataMutateResult.SUCCESS;
@ -579,7 +560,7 @@ public abstract class PermissionHolder {
if (node.isTemporary()) {
if (modifier == TemporaryModifier.ACCUMULATE) {
// Try to accumulate with an existing node
Optional<Node> existing = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE);
Optional<? extends Node> existing = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE);
// An existing node was found
if (existing.isPresent()) {
@ -589,10 +570,10 @@ public abstract class PermissionHolder {
Node newNode = node.toBuilder().setExpiry(previous.getExpiryUnixTime() + node.getSecondsTilExpiry()).build();
// Remove the old node & add the new one.
ImmutableCollection<Node> before = enduringData().immutable().values();
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.replace(newNode, previous);
invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeAdd(newNode, this, before, after);
return Maps.immutableEntry(DataMutateResult.SUCCESS, newNode);
@ -600,7 +581,7 @@ public abstract class PermissionHolder {
} else if (modifier == TemporaryModifier.REPLACE) {
// Try to replace an existing node
Optional<Node> existing = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE);
Optional<? extends Node> existing = searchForMatch(NodeMapType.ENDURING, node, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE);
// An existing node was found
if (existing.isPresent()) {
@ -609,10 +590,10 @@ public abstract class PermissionHolder {
// Only replace if the new expiry time is greater than the old one.
if (node.getExpiryUnixTime() > previous.getExpiryUnixTime()) {
ImmutableCollection<Node> before = enduringData().immutable().values();
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.replace(node, previous);
invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeAdd(node, this, before, after);
return Maps.immutableEntry(DataMutateResult.SUCCESS, node);
@ -652,10 +633,10 @@ public abstract class PermissionHolder {
return DataMutateResult.LACKS;
}
ImmutableCollection<Node> before = enduringData().immutable().values();
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.remove(node);
invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
this.plugin.getEventFactory().handleNodeRemove(node, this, before, after);
return DataMutateResult.SUCCESS;
@ -676,22 +657,14 @@ public abstract class PermissionHolder {
return DataMutateResult.SUCCESS;
}
public boolean inheritsGroup(Group group) {
return group.getName().equalsIgnoreCase(this.getObjectName()) || hasPermission(NodeMapType.ENDURING, NodeFactory.buildGroupNode(group.getName()).build(), StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean();
}
public boolean inheritsGroup(Group group, ContextSet contextSet) {
return group.getName().equalsIgnoreCase(this.getObjectName()) || hasPermission(NodeMapType.ENDURING, NodeFactory.buildGroupNode(group.getName()).withExtraContext(contextSet).build(), StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE).asBoolean();
}
/**
* Clear all of the holders permission nodes
*/
public boolean clearNodes() {
ImmutableCollection<Node> before = enduringData().immutable().values();
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.clear();
invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
if (before.size() == after.size()) {
return false;
@ -702,10 +675,10 @@ public abstract class PermissionHolder {
}
public boolean clearNodes(ContextSet contextSet) {
ImmutableCollection<Node> before = enduringData().immutable().values();
ImmutableCollection<? extends Node> before = enduringData().immutable().values();
this.enduringNodes.clear(contextSet);
invalidateCache();
ImmutableCollection<Node> after = enduringData().immutable().values();
ImmutableCollection<? extends Node> after = enduringData().immutable().values();
if (before.size() == after.size()) {
return false;

View File

@ -39,6 +39,12 @@ public final class ImmutableLocalizedNode extends ForwardingNode implements Loca
public static ImmutableLocalizedNode of(Node node, String location) {
Objects.requireNonNull(node, "node");
Objects.requireNonNull(location, "location");
// unwrap
while (node instanceof LocalizedNode) {
node = ((LocalizedNode) node).getNode();
}
return new ImmutableLocalizedNode(node, location);
}

View File

@ -72,7 +72,7 @@ public class HolderSubjectData implements LPSubjectData {
this.parentSubject = parentSubject;
}
private Stream<Node> streamNodes() {
private Stream<? extends Node> streamNodes() {
return this.holder.getData(this.type).immutable().values().stream();
}
@ -94,7 +94,7 @@ public class HolderSubjectData implements LPSubjectData {
@Override
public ImmutableMap<ImmutableContextSet, ImmutableMap<String, Boolean>> getAllPermissions() {
ImmutableMap.Builder<ImmutableContextSet, ImmutableMap<String, Boolean>> ret = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Collection<Node>> entry : this.holder.getData(this.type).immutable().asMap().entrySet()) {
for (Map.Entry<ImmutableContextSet, ? extends Collection<? extends Node>> entry : this.holder.getData(this.type).immutable().asMap().entrySet()) {
ImmutableMap.Builder<String, Boolean> builder = ImmutableMap.builder();
for (Node n : entry.getValue()) {
builder.put(n.getPermission(), n.getValue());
@ -183,7 +183,7 @@ public class HolderSubjectData implements LPSubjectData {
@Override
public ImmutableMap<ImmutableContextSet, ImmutableList<LPSubjectReference>> getAllParents() {
ImmutableMap.Builder<ImmutableContextSet, ImmutableList<LPSubjectReference>> ret = ImmutableMap.builder();
for (Map.Entry<ImmutableContextSet, Collection<Node>> entry : this.holder.getData(this.type).immutable().asMap().entrySet()) {
for (Map.Entry<ImmutableContextSet, ? extends Collection<? extends Node>> entry : this.holder.getData(this.type).immutable().asMap().entrySet()) {
ImmutableList.Builder<LPSubjectReference> builder = ImmutableList.builder();
for (Node n : entry.getValue()) {
if (n.isGroupNode()) {