ninja.leaping.permissionsex.backend.AbstractDataStore.java Source code

Java tutorial

Introduction

Here is the source code for ninja.leaping.permissionsex.backend.AbstractDataStore.java

Source

/**
 * PermissionsEx
 * Copyright (C) zml and PermissionsEx contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ninja.leaping.permissionsex.backend;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.objectmapping.ObjectMapper;
import ninja.leaping.configurate.objectmapping.ObjectMappingException;
import ninja.leaping.permissionsex.PermissionsEx;
import ninja.leaping.permissionsex.data.CacheListenerHolder;
import ninja.leaping.permissionsex.data.Caching;
import ninja.leaping.permissionsex.data.ContextInheritance;
import ninja.leaping.permissionsex.data.ImmutableSubjectData;
import ninja.leaping.permissionsex.exception.PermissionsLoadingException;
import ninja.leaping.permissionsex.rank.RankLadder;
import ninja.leaping.permissionsex.util.Util;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

import static ninja.leaping.permissionsex.util.Translations.t;

/**
 * Base implementation of a data store that provides common points for other data stores to hook into.
 */
public abstract class AbstractDataStore implements DataStore {
    private PermissionsEx manager;
    private final Factory factory;
    private final CacheListenerHolder<Map.Entry<String, String>, ImmutableSubjectData> listeners = new CacheListenerHolder<>();
    private final CacheListenerHolder<String, RankLadder> rankLadderListeners = new CacheListenerHolder<>();
    private final CacheListenerHolder<Boolean, ContextInheritance> contextInheritanceListeners = new CacheListenerHolder<>();

    protected AbstractDataStore(Factory factory) {
        if (!factory.expectedClazz.equals(getClass())) {
            throw new ExceptionInInitializerError(
                    "Data store factory for wrong class " + factory.expectedClazz + " provided to a " + getClass());
        }
        this.factory = factory;
    }

    protected PermissionsEx getManager() {
        return this.manager;
    }

    @Override
    public final void initialize(PermissionsEx core) throws PermissionsLoadingException {
        this.manager = core;
        initializeInternal();
    }

    protected abstract void initializeInternal() throws PermissionsLoadingException;

    @Override
    public final ImmutableSubjectData getData(String type, String identifier,
            Caching<ImmutableSubjectData> listener) {
        Objects.requireNonNull(type, "type");
        Objects.requireNonNull(identifier, "identifier");

        try {
            ImmutableSubjectData ret = getDataInternal(type, identifier);
            if (listener != null) {
                listeners.addListener(Maps.immutableEntry(type, identifier), listener);
            }
            return ret;
        } catch (PermissionsLoadingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public final CompletableFuture<ImmutableSubjectData> setData(String type, String identifier,
            ImmutableSubjectData data) {
        Objects.requireNonNull(type, "type");
        Objects.requireNonNull(identifier, "identifier");

        final Map.Entry<String, String> lookupKey = Maps.immutableEntry(type, identifier);
        return setDataInternal(type, identifier, data).thenApply(newData -> {
            if (newData != null) {
                listeners.call(lookupKey, newData);
            }
            return newData;
        });
    }

    /**
     * Apply default data when creating a new file.
     *
     * This consists of
     * <ul>
     *     <li>Modifying default data to give all permissions to a user connecting locally</li>
     * </ul>
     */
    protected final void applyDefaultData() {
        setData(PermissionsEx.SUBJECTS_DEFAULTS, PermissionsEx.SUBJECTS_DEFAULTS,
                getData(PermissionsEx.SUBJECTS_DEFAULTS, PermissionsEx.SUBJECTS_DEFAULTS, null)
                        .setDefaultValue(ImmutableSet.of(Maps.immutableEntry("srcip", "127.0.0.1")), 1));
    }

    protected abstract ImmutableSubjectData getDataInternal(String type, String identifier)
            throws PermissionsLoadingException;

    protected abstract CompletableFuture<ImmutableSubjectData> setDataInternal(String type, String identifier,
            ImmutableSubjectData data);

    @Override
    public final Iterable<Map.Entry<String, ImmutableSubjectData>> getAll(final String type) {
        Objects.requireNonNull(type, "type");
        return Iterables.transform(getAllIdentifiers(type),
                input -> Maps.immutableEntry(input, getData(type, input, null)));
    }

    @Override
    public final <T> CompletableFuture<T> performBulkOperation(final Function<DataStore, T> function) {
        return Util.asyncFailableFuture(() -> performBulkOperationSync(function), getManager().getAsyncExecutor());
    }

    @Override
    public final RankLadder getRankLadder(String ladderName, Caching<RankLadder> listener) {
        Objects.requireNonNull(ladderName, "ladderName");
        RankLadder ladder = getRankLadderInternal(ladderName);
        if (listener != null) {
            rankLadderListeners.addListener(ladderName.toLowerCase(), listener);
        }
        return ladder;
    }

    @Override
    public final CompletableFuture<RankLadder> setRankLadder(final String identifier, RankLadder ladder) {
        return setRankLadderInternal(identifier, ladder).thenApply(newData -> {
            if (newData != null) {
                rankLadderListeners.call(identifier, newData);
            }
            return newData;
        });
    }

    protected abstract RankLadder getRankLadderInternal(String ladder);

    protected abstract CompletableFuture<RankLadder> setRankLadderInternal(String ladder, RankLadder newLadder);

    @Override
    public final ContextInheritance getContextInheritance(Caching<ContextInheritance> listener) {
        ContextInheritance inheritance = getContextInheritanceInternal();
        if (listener != null) {
            contextInheritanceListeners.addListener(true, listener);
        }
        return inheritance;
    }

    @Override
    public final CompletableFuture<ContextInheritance> setContextInheritance(
            ContextInheritance contextInheritance) {
        return setContextInheritanceInternal(contextInheritance).thenApply(newData -> {
            if (newData != null) {
                contextInheritanceListeners.call(true, newData);
            }
            return newData;
        });
    }

    protected abstract ContextInheritance getContextInheritanceInternal();

    protected abstract CompletableFuture<ContextInheritance> setContextInheritanceInternal(
            ContextInheritance contextInheritance);

    /**
     * Internally perform a bulk operation. Safe to call blocking operations from this method -- we're running it asyncly.
     *
     * @param function The function to run
     * @param <T> The
     * @return
     * @throws Exception
     */
    protected abstract <T> T performBulkOperationSync(Function<DataStore, T> function) throws Exception;

    @Override
    @SuppressWarnings("unchecked") // Correct types are verified in the constructor
    public String serialize(ConfigurationNode node) throws PermissionsLoadingException {
        Objects.requireNonNull(node, "node");
        try {
            ((ObjectMapper) factory.mapper).bind(this).serialize(node);
        } catch (ObjectMappingException e) {
            throw new PermissionsLoadingException(t("Error while serializing backend %s", node.getKey()), e);
        }
        return factory.type;
    }

    protected static class Factory implements DataStoreFactory {
        private final String type;
        private final Class<? extends AbstractDataStore> expectedClazz;
        private final ObjectMapper<? extends AbstractDataStore> mapper;

        public Factory(final String type, Class<? extends AbstractDataStore> clazz) {
            Objects.requireNonNull(type, "type");
            Objects.requireNonNull(clazz, "clazz");
            this.type = type;
            this.expectedClazz = clazz;
            try {
                mapper = ObjectMapper.forClass(clazz);
            } catch (ObjectMappingException e) {
                throw new ExceptionInInitializerError(e);
            }
        }

        @Override
        public DataStore createDataStore(String identifier, ConfigurationNode config)
                throws PermissionsLoadingException {
            try {
                return mapper.bindToNew().populate(config);
            } catch (ObjectMappingException e) {
                throw new PermissionsLoadingException(t("Error while deserializing backend %s", identifier), e);
            }
        }
    }
}