001/* 002 * PermissionsEx 003 * Copyright (C) zml and PermissionsEx contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package ca.stellardrift.permissionsex.impl.backend.memory; 018 019import ca.stellardrift.permissionsex.datastore.DataStoreContext; 020import ca.stellardrift.permissionsex.impl.backend.AbstractDataStore; 021import ca.stellardrift.permissionsex.impl.config.FilePermissionsExConfiguration; 022import ca.stellardrift.permissionsex.datastore.DataStore; 023import ca.stellardrift.permissionsex.datastore.DataStoreFactory; 024import ca.stellardrift.permissionsex.datastore.ProtoDataStore; 025import ca.stellardrift.permissionsex.context.ContextValue; 026import ca.stellardrift.permissionsex.context.ContextInheritance; 027import ca.stellardrift.permissionsex.exception.PermissionsLoadingException; 028import ca.stellardrift.permissionsex.impl.util.PCollections; 029import ca.stellardrift.permissionsex.subject.ImmutableSubjectData; 030import ca.stellardrift.permissionsex.impl.rank.FixedRankLadder; 031import ca.stellardrift.permissionsex.rank.RankLadder; 032import ca.stellardrift.permissionsex.subject.SubjectRef; 033import com.google.auto.service.AutoService; 034import org.checkerframework.checker.nullness.qual.Nullable; 035import org.spongepowered.configurate.BasicConfigurationNode; 036import org.spongepowered.configurate.objectmapping.ConfigSerializable; 037import org.spongepowered.configurate.objectmapping.meta.Comment; 038import org.spongepowered.configurate.objectmapping.meta.Setting; 039 040import java.util.Collection; 041import java.util.Map; 042import java.util.Set; 043import java.util.concurrent.CompletableFuture; 044import java.util.concurrent.ConcurrentHashMap; 045import java.util.concurrent.ConcurrentMap; 046import java.util.function.Function; 047import java.util.stream.Collectors; 048import java.util.stream.Stream; 049 050import static org.spongepowered.configurate.util.UnmodifiableCollections.immutableMapEntry; 051 052/** 053 * A data store backed entirely in memory 054 */ 055public class MemoryDataStore extends AbstractDataStore<MemoryDataStore, MemoryDataStore.Config> { 056 @ConfigSerializable 057 static class Config { 058 @Setting 059 @Comment("Whether or not this data store will store subjects being set") 060 boolean track = true; 061 } 062 063 @AutoService(DataStoreFactory.class) 064 public static final class Factory extends AbstractDataStore.Factory<MemoryDataStore, Config> { 065 static final String TYPE = "memory"; 066 067 public Factory() { 068 super(TYPE, Config.class, MemoryDataStore::new); 069 } 070 } 071 072 073 private final ConcurrentMap<Map.Entry<String, String>, ImmutableSubjectData> data = new ConcurrentHashMap<>(); 074 private final ConcurrentMap<String, RankLadder> rankLadders = new ConcurrentHashMap<>(); 075 private volatile ContextInheritance inheritance = new MemoryContextInheritance(); 076 077 public static ProtoDataStore<?> create(final String identifier) { 078 try { 079 return DataStoreFactory.forType(Factory.TYPE) 080 .create(identifier, BasicConfigurationNode.root(FilePermissionsExConfiguration.PEX_OPTIONS)); 081 } catch (final PermissionsLoadingException ex) { 082 // Not possible to have loading errors when we're not loading anything 083 throw new RuntimeException(ex); 084 } 085 } 086 087 public MemoryDataStore(final DataStoreContext context, final ProtoDataStore<Config> properties) { 088 super(context, properties); 089 } 090 091 @Override 092 protected void load() { 093 this.markFirstRun(); 094 } 095 096 @Override 097 public void close() { 098 } 099 100 @Override 101 public CompletableFuture<ImmutableSubjectData> getDataInternal(final String type, final String identifier) { 102 final Map.Entry<String, String> key = immutableMapEntry(type, identifier); 103 ImmutableSubjectData ret = data.get(key); 104 if (ret == null) { 105 ret = new MemorySubjectData(); 106 if (config().track) { 107 final @Nullable ImmutableSubjectData existingData = data.putIfAbsent(key, ret); 108 if (existingData != null) { 109 ret = existingData; 110 } 111 } 112 } 113 return completedFuture(ret); 114 } 115 116 @Override 117 public CompletableFuture<ImmutableSubjectData> setDataInternal(final String type, final String identifier, final ImmutableSubjectData data) { 118 if (config().track) { 119 this.data.put(immutableMapEntry(type, identifier), data); 120 } 121 return completedFuture(data); 122 } 123 124 @Override 125 protected CompletableFuture<RankLadder> getRankLadderInternal(String name) { 126 RankLadder ladder = rankLadders.get(name.toLowerCase()); 127 if (ladder == null) { 128 ladder = new FixedRankLadder(name, PCollections.vector()); 129 } 130 return completedFuture(ladder); 131 } 132 133 @Override 134 protected CompletableFuture<RankLadder> setRankLadderInternal(final String ladder, final @Nullable RankLadder newLadder) { 135 if (newLadder == null) { 136 this.rankLadders.remove(ladder); 137 } else { 138 this.rankLadders.put(ladder, newLadder); 139 } 140 return completedFuture(newLadder); 141 } 142 143 private <T> CompletableFuture<T> completedFuture(T i) { 144 return CompletableFuture.supplyAsync(() -> i, this.context().asyncExecutor()); 145 } 146 147 @Override 148 public CompletableFuture<Boolean> isRegistered(String type, String identifier) { 149 return completedFuture(data.containsKey(immutableMapEntry(type, identifier))); 150 } 151 152 @Override 153 public Stream<String> getAllIdentifiers(final String type) { 154 return data.keySet().stream() 155 .filter(inp -> inp.getKey().equals(type)) 156 .map(Map.Entry::getValue); 157 } 158 159 @Override 160 public Set<String> getRegisteredTypes() { 161 return this.data.keySet().stream() 162 .map(Map.Entry::getKey) 163 .collect(PCollections.toPSet()); 164 } 165 166 @Override 167 public CompletableFuture<Set<String>> getDefinedContextKeys() { 168 return CompletableFuture.completedFuture(data.values().stream() 169 .flatMap(data -> data.activeContexts().stream()) 170 .flatMap(Collection::stream) 171 .map(ContextValue::key) 172 .collect(Collectors.toSet())); 173 } 174 175 @Override 176 public Stream<Map.Entry<SubjectRef<?>, ImmutableSubjectData>> getAll() { 177 return this.data.entrySet().stream() 178 .map(entry -> immutableMapEntry(this.context().deserializeSubjectRef(entry.getKey()), entry.getValue())); 179 } 180 181 @Override 182 public Stream<String> getAllRankLadders() { 183 return this.rankLadders.keySet().stream(); 184 } 185 186 @Override 187 public CompletableFuture<Boolean> hasRankLadder(String ladder) { 188 return completedFuture(rankLadders.containsKey(ladder.toLowerCase())); 189 } 190 191 @Override 192 public CompletableFuture<ContextInheritance> getContextInheritanceInternal() { 193 return completedFuture(this.inheritance); 194 } 195 196 @Override 197 public CompletableFuture<ContextInheritance> setContextInheritanceInternal(final ContextInheritance inheritance) { 198 this.inheritance = inheritance; 199 return completedFuture(this.inheritance); 200 } 201 202 @Override 203 protected <T> T performBulkOperationSync(Function<DataStore, T> function) { 204 return function.apply(this); 205 } 206}