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.subject; 018 019import ca.stellardrift.permissionsex.impl.PermissionsEx; 020import ca.stellardrift.permissionsex.datastore.DataStore; 021import ca.stellardrift.permissionsex.subject.CalculatedSubject; 022import ca.stellardrift.permissionsex.subject.SubjectRef; 023import ca.stellardrift.permissionsex.subject.SubjectType; 024import ca.stellardrift.permissionsex.subject.SubjectTypeCollection; 025import com.github.benmanes.caffeine.cache.AsyncLoadingCache; 026import com.github.benmanes.caffeine.cache.Caffeine; 027 028import java.util.Collection; 029import java.util.Collections; 030import java.util.concurrent.CompletableFuture; 031import java.util.stream.Stream; 032 033public class SubjectTypeCollectionImpl<I> implements SubjectTypeCollection<I> { 034 private final PermissionsEx<?> pex; 035 private final SubjectType<I> type; 036 private final SubjectDataCacheImpl<I> persistentData; 037 private final SubjectDataCacheImpl<I> transientData; 038 private final AsyncLoadingCache<I, CalculatedSubject> cache; 039 040 public SubjectTypeCollectionImpl(PermissionsEx<?> pex, final SubjectType<I> type, SubjectDataCacheImpl<I> persistentData, SubjectDataCacheImpl<I> transientData) { 041 this.pex = pex; 042 this.type = type; 043 this.persistentData = persistentData; 044 this.transientData = transientData; 045 this.cache = Caffeine.newBuilder() 046 .executor(pex.asyncExecutor()) 047 .buildAsync((key, executor) -> { 048 CalculatedSubjectImpl<I> subj = new CalculatedSubjectImpl<>( 049 SubjectDataBaker.inheritance(), 050 SubjectRef.subject(this.type, key), 051 this); 052 053 return persistentData.referenceTo(key, false) 054 .thenCombine( 055 transientData.referenceTo(key, false), 056 (persistentRef, transientRef) -> { 057 subj.initialize(persistentRef, transientRef); 058 return subj; 059 }); 060 }); 061 } 062 063 @Override 064 public SubjectType<I> type() { 065 return this.type; 066 } 067 068 @Override 069 public void cacheAll() { 070 this.persistentData.cacheAll(); 071 this.transientData.cacheAll(); 072 } 073 074 @Override 075 public Collection<CalculatedSubject> activeSubjects() { 076 return Collections.unmodifiableCollection(this.cache.synchronous().asMap().values()); 077 } 078 079 @Override 080 public void uncache(final I identifier) { 081 this.persistentData.invalidate(identifier); 082 this.transientData.invalidate(identifier); 083 this.cache.synchronous().invalidate(identifier); 084 } 085 086 @Override 087 public CompletableFuture<CalculatedSubject> get(final I identifier) { 088 return this.cache.get(identifier); 089 } 090 091 @Override 092 public SubjectDataCacheImpl<I> transientData() { 093 return this.transientData; 094 } 095 096 @Override 097 public SubjectDataCacheImpl<I> persistentData() { 098 return this.persistentData; 099 } 100 101 PermissionsEx<?> engine() { 102 return this.pex; 103 } 104 105 /** 106 * For internal use only. Update the data store associated with this cache, when for example the 107 * active data store is changed in the engine. 108 * 109 * @param newDataStore The new data store to work off of 110 */ 111 public void update(final DataStore newDataStore) { 112 this.persistentData.update(newDataStore); 113 } 114 115 @Override 116 public void load(final I identifier) { 117 this.persistentData.load(identifier); 118 this.transientData.load(identifier); 119 } 120 121 @Override 122 public CompletableFuture<Boolean> isRegistered(final I identifier) { 123 return this.persistentData.isRegistered(identifier) 124 .thenCombine(this.transientData.isRegistered(identifier), Boolean::logicalAnd); 125 } 126 127 @Override 128 public Stream<I> allIdentifiers() { 129 return Stream.concat( 130 this.persistentData.getAllIdentifiers(), 131 this.transientData.getAllIdentifiers()) 132 .distinct(); 133 } 134}