me.lucko.luckperms.common.storage.backing.JSONBacking.java Source code

Java tutorial

Introduction

Here is the source code for me.lucko.luckperms.common.storage.backing.JSONBacking.java

Source

/*
 * Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
 *
 *  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.storage.backing;

import com.google.common.collect.ImmutableList;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import me.lucko.luckperms.api.HeldPermission;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.UserIdentifier;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.Track;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.impl.GenericUserManager;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.holder.NodeHeldPermission;
import me.lucko.luckperms.common.utils.ThrowingFunction;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

import static me.lucko.luckperms.common.core.model.PermissionHolder.exportToLegacy;

@SuppressWarnings("ResultOfMethodCallIgnored")
public class JSONBacking extends FlatfileBacking {
    private static <T> T call(Callable<T> c, T def) {
        try {
            return c.call();
        } catch (Exception e) {
            e.printStackTrace();
            return def;
        }
    }

    public JSONBacking(LuckPermsPlugin plugin, File pluginDir) {
        super(plugin, "JSON", pluginDir);
    }

    private boolean fileToWriter(File file, ThrowingFunction<JsonWriter, Boolean> writeOperation) {
        boolean success = false;
        try {
            try (FileOutputStream outputStream = new FileOutputStream(file)) {
                try (OutputStreamWriter outputWriter = new OutputStreamWriter(outputStream,
                        StandardCharsets.UTF_8)) {
                    try (BufferedWriter bufferedWriter = new BufferedWriter(outputWriter)) {
                        try (JsonWriter jsonWriter = new JsonWriter(bufferedWriter)) {
                            jsonWriter.setIndent("    ");
                            success = writeOperation.apply(jsonWriter);
                            jsonWriter.flush();
                        }
                    }
                }
            }
        } catch (Exception e) {
            plugin.getLog().warn("Exception whilst writing to file: " + file.getAbsolutePath());
            e.printStackTrace();
        }
        return success;
    }

    private boolean fileToReader(File file, ThrowingFunction<JsonReader, Boolean> readOperation) {
        boolean success = false;
        try {
            try (FileInputStream fileInput = new FileInputStream(file)) {
                try (InputStreamReader inputReader = new InputStreamReader(fileInput, StandardCharsets.UTF_8)) {
                    try (BufferedReader bufferedReader = new BufferedReader(inputReader)) {
                        try (JsonReader jsonReader = new JsonReader(bufferedReader)) {
                            success = readOperation.apply(jsonReader);
                        }
                    }
                }
            }
        } catch (Exception e) {
            plugin.getLog().warn("Exception whilst reading from file: " + file.getAbsolutePath());
            e.printStackTrace();
        }
        return success;
    }

    @Override
    public boolean loadUser(UUID uuid, String username) {
        User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
        user.getIoLock().lock();
        try {
            return call(() -> {
                File userFile = new File(usersDir, uuid.toString() + ".json");
                if (userFile.exists()) {
                    return fileToReader(userFile, reader -> {
                        reader.beginObject();
                        reader.nextName(); // uuid record
                        reader.nextString(); // uuid
                        reader.nextName(); // name record
                        String name = reader.nextString(); // name
                        reader.nextName(); // primaryGroup record
                        user.setPrimaryGroup(reader.nextString()); // primaryGroup
                        reader.nextName(); // perms
                        reader.beginObject();
                        Map<String, Boolean> map = new HashMap<>();
                        while (reader.hasNext()) {
                            String node = reader.nextName();
                            boolean b = reader.nextBoolean();
                            map.put(node, b);
                        }
                        user.setNodes(map);
                        reader.endObject();
                        reader.endObject();

                        boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);

                        if (user.getName() == null || user.getName().equalsIgnoreCase("null")) {
                            user.setName(name);
                        } else {
                            if (!name.equalsIgnoreCase(user.getName())) {
                                save = true;
                            }
                        }

                        if (save) {
                            fileToWriter(userFile, writer -> {
                                writer.beginObject();
                                writer.name("uuid").value(user.getUuid().toString());
                                writer.name("name").value(user.getName());
                                writer.name("primaryGroup").value(user.getPrimaryGroup());
                                writer.name("perms");
                                writer.beginObject();
                                for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) {
                                    writer.name(e.getKey()).value(e.getValue().booleanValue());
                                }
                                writer.endObject();
                                writer.endObject();
                                return true;
                            });
                        }
                        return true;
                    });
                } else {
                    if (GenericUserManager.shouldSave(user)) {
                        user.clearNodes();
                        user.setPrimaryGroup(null);
                        plugin.getUserManager().giveDefaultIfNeeded(user, false);
                    }
                    return true;
                }
            }, false);
        } finally {
            user.getIoLock().unlock();
            user.getRefreshBuffer().requestDirectly();
        }
    }

    @Override
    public boolean saveUser(User user) {
        user.getIoLock().lock();
        try {
            return call(() -> {
                File userFile = new File(usersDir, user.getUuid().toString() + ".json");
                if (!GenericUserManager.shouldSave(user)) {
                    if (userFile.exists()) {
                        userFile.delete();
                    }
                    return true;
                }

                if (!userFile.exists()) {
                    try {
                        userFile.createNewFile();
                    } catch (IOException e) {
                        e.printStackTrace();
                        return false;
                    }
                }

                return fileToWriter(userFile, writer -> {
                    writer.beginObject();
                    writer.name("uuid").value(user.getUuid().toString());
                    writer.name("name").value(user.getName());
                    writer.name("primaryGroup").value(user.getPrimaryGroup());
                    writer.name("perms");
                    writer.beginObject();
                    for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) {
                        writer.name(e.getKey()).value(e.getValue().booleanValue());
                    }
                    writer.endObject();
                    writer.endObject();
                    return true;
                });
            }, false);
        } finally {
            user.getIoLock().unlock();
        }
    }

    @Override
    public boolean cleanupUsers() {
        return call(() -> {
            File[] files = usersDir.listFiles((dir, name1) -> name1.endsWith(".json"));
            if (files == null)
                return false;

            for (File file : files) {
                Map<String, Boolean> nodes = new HashMap<>();
                fileToReader(file, reader -> {
                    reader.beginObject();
                    reader.nextName(); // uuid record
                    reader.nextString(); // uuid
                    reader.nextName(); // name record
                    reader.nextString(); // name
                    reader.nextName(); // primaryGroup record
                    reader.nextString(); // primaryGroup
                    reader.nextName(); //perms
                    reader.beginObject();
                    while (reader.hasNext()) {
                        String node = reader.nextName();
                        boolean b = reader.nextBoolean();
                        nodes.put(node, b);
                    }

                    reader.endObject();
                    reader.endObject();
                    return true;
                });

                boolean shouldDelete = false;
                if (nodes.size() == 1) {
                    for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
                        // There's only one
                        shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue();
                    }
                }

                if (shouldDelete) {
                    file.delete();
                }
            }
            return true;
        }, false);
    }

    @Override
    public Set<UUID> getUniqueUsers() {
        String[] fileNames = usersDir.list((dir, name) -> name.endsWith(".json"));
        if (fileNames == null)
            return null;
        return Arrays.stream(fileNames).map(s -> s.substring(0, s.length() - 5)).map(UUID::fromString)
                .collect(Collectors.toSet());
    }

    @Override
    public List<HeldPermission<UUID>> getUsersWithPermission(String permission) {
        ImmutableList.Builder<HeldPermission<UUID>> held = ImmutableList.builder();
        boolean success = call(() -> {
            File[] files = usersDir.listFiles((dir, name1) -> name1.endsWith(".json"));
            if (files == null)
                return false;

            for (File file : files) {
                UUID holder = UUID.fromString(file.getName().substring(0, file.getName().length() - 5));
                Map<String, Boolean> nodes = new HashMap<>();
                fileToReader(file, reader -> {
                    reader.beginObject();
                    reader.nextName(); // uuid record
                    reader.nextString(); // uuid
                    reader.nextName(); // name record
                    reader.nextString(); // name
                    reader.nextName(); // primaryGroup record
                    reader.nextString(); // primaryGroup
                    reader.nextName(); //perms
                    reader.beginObject();
                    while (reader.hasNext()) {
                        String node = reader.nextName();
                        boolean b = reader.nextBoolean();
                        nodes.put(node, b);
                    }

                    reader.endObject();
                    reader.endObject();
                    return true;
                });

                for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
                    Node node = NodeFactory.fromSerialisedNode(e.getKey(), e.getValue());
                    if (!node.getPermission().equalsIgnoreCase(permission)) {
                        continue;
                    }

                    held.add(NodeHeldPermission.of(holder, node));
                }
            }
            return true;
        }, false);
        return success ? held.build() : null;
    }

    @Override
    public boolean createAndLoadGroup(String name) {
        Group group = plugin.getGroupManager().getOrMake(name);
        group.getIoLock().lock();
        try {
            return call(() -> {
                File groupFile = new File(groupsDir, name + ".json");
                if (groupFile.exists()) {
                    return fileToReader(groupFile, reader -> {
                        reader.beginObject();
                        reader.nextName(); // name record
                        reader.nextString(); // name
                        reader.nextName(); //perms
                        reader.beginObject();
                        Map<String, Boolean> map = new HashMap<>();
                        while (reader.hasNext()) {
                            String node = reader.nextName();
                            boolean b = reader.nextBoolean();
                            map.put(node, b);
                        }
                        group.setNodes(map);

                        reader.endObject();
                        reader.endObject();
                        return true;
                    });
                } else {
                    try {
                        groupFile.createNewFile();
                    } catch (IOException e) {
                        e.printStackTrace();
                        return false;
                    }

                    return fileToWriter(groupFile, writer -> {
                        writer.beginObject();
                        writer.name("name").value(group.getName());
                        writer.name("perms");
                        writer.beginObject();
                        for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) {
                            writer.name(e.getKey()).value(e.getValue().booleanValue());
                        }
                        writer.endObject();
                        writer.endObject();
                        return true;
                    });
                }
            }, false);
        } finally {
            group.getIoLock().unlock();
        }
    }

    @Override
    public boolean loadGroup(String name) {
        Group group = plugin.getGroupManager().getOrMake(name);
        group.getIoLock().lock();
        try {
            return call(() -> {
                File groupFile = new File(groupsDir, name + ".json");
                return groupFile.exists() && fileToReader(groupFile, reader -> {
                    reader.beginObject();
                    reader.nextName(); // name record
                    reader.nextString(); // name
                    reader.nextName(); // perms
                    reader.beginObject();
                    Map<String, Boolean> map = new HashMap<>();
                    while (reader.hasNext()) {
                        String node = reader.nextName();
                        boolean b = reader.nextBoolean();
                        map.put(node, b);
                    }
                    group.setNodes(map);
                    reader.endObject();
                    reader.endObject();
                    return true;
                });
            }, false);
        } finally {
            group.getIoLock().unlock();
        }
    }

    @Override
    public boolean loadAllGroups() {
        String[] fileNames = groupsDir.list((dir, name) -> name.endsWith(".json"));
        if (fileNames == null)
            return false;
        List<String> groups = Arrays.stream(fileNames).map(s -> s.substring(0, s.length() - 5))
                .collect(Collectors.toList());

        groups.forEach(this::loadGroup);

        GroupManager gm = plugin.getGroupManager();
        gm.getAll().values().stream().filter(g -> !groups.contains(g.getName())).forEach(gm::unload);
        return true;
    }

    @Override
    public boolean saveGroup(Group group) {
        group.getIoLock().lock();
        try {
            return call(() -> {
                File groupFile = new File(groupsDir, group.getName() + ".json");
                if (!groupFile.exists()) {
                    try {
                        groupFile.createNewFile();
                    } catch (IOException e) {
                        e.printStackTrace();
                        return false;
                    }
                }

                return fileToWriter(groupFile, writer -> {
                    writer.beginObject();
                    writer.name("name").value(group.getName());
                    writer.name("perms");
                    writer.beginObject();
                    for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) {
                        writer.name(e.getKey()).value(e.getValue().booleanValue());
                    }
                    writer.endObject();
                    writer.endObject();
                    return true;
                });
            }, false);
        } finally {
            group.getIoLock().unlock();
        }
    }

    @Override
    public boolean deleteGroup(Group group) {
        group.getIoLock().lock();
        try {
            return call(() -> {
                File groupFile = new File(groupsDir, group.getName() + ".json");
                if (groupFile.exists()) {
                    groupFile.delete();
                }
                return true;
            }, false);
        } finally {
            group.getIoLock().unlock();
        }
    }

    @Override
    public List<HeldPermission<String>> getGroupsWithPermission(String permission) {
        ImmutableList.Builder<HeldPermission<String>> held = ImmutableList.builder();
        boolean success = call(() -> {
            File[] files = groupsDir.listFiles((dir, name1) -> name1.endsWith(".json"));
            if (files == null)
                return false;

            for (File file : files) {
                String holder = file.getName().substring(0, file.getName().length() - 5);
                Map<String, Boolean> nodes = new HashMap<>();
                fileToReader(file, reader -> {
                    reader.beginObject();
                    reader.nextName(); // name record
                    reader.nextString(); // name
                    reader.nextName(); // perms
                    reader.beginObject();
                    while (reader.hasNext()) {
                        String node = reader.nextName();
                        boolean b = reader.nextBoolean();
                        nodes.put(node, b);
                    }

                    reader.endObject();
                    reader.endObject();
                    return true;
                });

                for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
                    Node node = NodeFactory.fromSerialisedNode(e.getKey(), e.getValue());
                    if (!node.getPermission().equalsIgnoreCase(permission)) {
                        continue;
                    }

                    held.add(NodeHeldPermission.of(holder, node));
                }
            }
            return true;
        }, false);
        return success ? held.build() : null;
    }

    @Override
    public boolean createAndLoadTrack(String name) {
        Track track = plugin.getTrackManager().getOrMake(name);
        track.getIoLock().lock();
        try {
            return call(() -> {
                File trackFile = new File(tracksDir, name + ".json");
                if (trackFile.exists()) {
                    return fileToReader(trackFile, reader -> {
                        reader.beginObject();
                        reader.nextName(); // name record
                        reader.nextString(); // name
                        reader.nextName(); // groups record
                        reader.beginArray();
                        List<String> groups = new ArrayList<>();
                        while (reader.hasNext()) {
                            groups.add(reader.nextString());
                        }
                        track.setGroups(groups);
                        reader.endArray();
                        reader.endObject();
                        return true;
                    });
                } else {
                    try {
                        trackFile.createNewFile();
                    } catch (IOException e) {
                        e.printStackTrace();
                        return false;
                    }

                    return fileToWriter(trackFile, writer -> {
                        writer.beginObject();
                        writer.name("name").value(track.getName());
                        writer.name("groups");
                        writer.beginArray();
                        for (String s : track.getGroups()) {
                            writer.value(s);
                        }
                        writer.endArray();
                        writer.endObject();
                        return true;
                    });
                }
            }, false);
        } finally {
            track.getIoLock().unlock();
        }
    }

    @Override
    public boolean loadTrack(String name) {
        Track track = plugin.getTrackManager().getOrMake(name);
        track.getIoLock().lock();
        try {
            return call(() -> {
                File trackFile = new File(tracksDir, name + ".json");
                return trackFile.exists() && fileToReader(trackFile, reader -> {
                    reader.beginObject();
                    reader.nextName(); // name record
                    reader.nextString(); // name
                    reader.nextName(); // groups
                    reader.beginArray();
                    List<String> groups = new ArrayList<>();
                    while (reader.hasNext()) {
                        groups.add(reader.nextString());
                    }
                    track.setGroups(groups);
                    reader.endArray();
                    reader.endObject();
                    return true;
                });

            }, false);
        } finally {
            track.getIoLock().unlock();
        }
    }

    @Override
    public boolean loadAllTracks() {
        String[] fileNames = tracksDir.list((dir, name) -> name.endsWith(".json"));
        if (fileNames == null)
            return false;
        List<String> tracks = Arrays.stream(fileNames).map(s -> s.substring(0, s.length() - 5))
                .collect(Collectors.toList());

        tracks.forEach(this::loadTrack);

        TrackManager tm = plugin.getTrackManager();
        tm.getAll().values().stream().filter(t -> !tracks.contains(t.getName())).forEach(tm::unload);
        return true;
    }

    @Override
    public boolean saveTrack(Track track) {
        track.getIoLock().lock();
        try {
            return call(() -> {
                File trackFile = new File(tracksDir, track.getName() + ".json");
                if (!trackFile.exists()) {
                    try {
                        trackFile.createNewFile();
                    } catch (IOException e) {
                        e.printStackTrace();
                        return false;
                    }
                }

                return fileToWriter(trackFile, writer -> {
                    writer.beginObject();
                    writer.name("name").value(track.getName());
                    writer.name("groups");
                    writer.beginArray();
                    for (String s : track.getGroups()) {
                        writer.value(s);
                    }
                    writer.endArray();
                    writer.endObject();
                    return true;
                });
            }, false);
        } finally {
            track.getIoLock().unlock();
        }
    }

    @Override
    public boolean deleteTrack(Track track) {
        track.getIoLock().lock();
        try {
            return call(() -> {
                File trackFile = new File(tracksDir, track.getName() + ".json");
                if (trackFile.exists()) {
                    trackFile.delete();
                }
                return true;
            }, false);
        } finally {
            track.getIoLock().unlock();
        }
    }
}