Add logging, prepare for import/export system

This commit is contained in:
Luck
2016-08-15 01:51:36 +02:00
Unverified
parent fbf062933f
commit e216c235ce
139 changed files with 4240 additions and 1439 deletions
@@ -1,154 +0,0 @@
/*
* 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.data;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import java.util.UUID;
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class Datastore {
protected final LuckPermsPlugin plugin;
@Getter
public final String name;
@Getter
@Setter
private boolean acceptingLogins = false;
/**
* Execute a runnable asynchronously
* @param r the task to run
*/
private void doAsync(Runnable r) {
plugin.doAsync(r);
}
/**
* Execute a runnable synchronously
* @param r the task to run
*/
private void doSync(Runnable r) {
plugin.doSync(r);
}
private <T> void runCallback(T t, Callback<T> callback) {
doSync(() -> callback.onComplete(t));
}
/*
These methods are called immediately and in the same thread as they are called in.
*/
public abstract void init();
public abstract void shutdown();
public abstract boolean loadOrCreateUser(UUID uuid, String username);
public abstract boolean loadUser(UUID uuid);
public abstract boolean saveUser(User user);
public abstract boolean createAndLoadGroup(String name);
public abstract boolean loadGroup(String name);
public abstract boolean loadAllGroups();
public abstract boolean saveGroup(Group group);
public abstract boolean deleteGroup(Group group);
public abstract boolean createAndLoadTrack(String name);
public abstract boolean loadTrack(String name);
public abstract boolean loadAllTracks();
public abstract boolean saveTrack(Track track);
public abstract boolean deleteTrack(Track track);
public abstract boolean saveUUIDData(String username, UUID uuid);
public abstract UUID getUUID(String username);
/*
These methods will schedule the operation to run async. The callback will be ran when the task is complete.
Callbacks are ran on the main Bukkit server thread (if applicable)
*/
public void loadOrCreateUser(UUID uuid, String username, Callback<Boolean> callback) {
doAsync(() -> runCallback(loadOrCreateUser(uuid, username), callback));
}
public void loadUser(UUID uuid, Callback<Boolean> callback) {
doAsync(() -> runCallback(loadUser(uuid), callback));
}
public void saveUser(User user, Callback<Boolean> callback) {
doAsync(() -> runCallback(saveUser(user), callback));
}
public void createAndLoadGroup(String name, Callback<Boolean> callback) {
doAsync(() -> runCallback(createAndLoadGroup(name), callback));
}
public void loadGroup(String name, Callback<Boolean> callback) {
doAsync(() -> runCallback(loadGroup(name), callback));
}
public void loadAllGroups(Callback<Boolean> callback) {
doAsync(() -> runCallback(loadAllGroups(), callback));
}
public void saveGroup(Group group, Callback<Boolean> callback) {
doAsync(() -> runCallback(saveGroup(group), callback));
}
public void deleteGroup(Group group, Callback<Boolean> callback) {
doAsync(() -> runCallback(deleteGroup(group), callback));
}
public void createAndLoadTrack(String name, Callback<Boolean> callback) {
doAsync(() -> runCallback(createAndLoadTrack(name), callback));
}
public void loadTrack(String name, Callback<Boolean> callback) {
doAsync(() -> runCallback(loadTrack(name), callback));
}
public void loadAllTracks(Callback<Boolean> callback) {
doAsync(() -> runCallback(loadAllTracks(), callback));
}
public void saveTrack(Track track, Callback<Boolean> callback) {
doAsync(() -> runCallback(saveTrack(track), callback));
}
public void deleteTrack(Track track, Callback<Boolean> callback) {
doAsync(() -> runCallback(deleteTrack(track), callback));
}
public void saveUUIDData(String username, UUID uuid, Callback<Boolean> callback) {
doAsync(() -> runCallback(saveUUIDData(username, uuid), callback));
}
public void getUUID(String username, Callback<UUID> callback) {
doAsync(() -> runCallback(getUUID(username), callback));
}
}
@@ -0,0 +1,209 @@
/*
* 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.data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.constants.Permission;
import java.util.*;
import java.util.stream.Collectors;
/**
* Executes a list of commands sequentially in a single thread.
*/
@RequiredArgsConstructor
public class Importer { // TODO: implement this
@Getter
private boolean running = false;
private final CommandManager commandManager;
private Sender executor = null;
private List<String> commands = null;
private Map<Integer, Result> cmdResult = null;
private long lastMsg = 0;
private int executing = -1;
public synchronized void start(Sender executor, List<String> commands) {
if (isRunning()) {
throw new IllegalStateException("Import already running.");
}
running = true;
this.executor = executor;
this.commands = commands.stream()
.map(s -> s.startsWith("/") ? s.substring(1) : s)
.map(s -> s.startsWith("perms ") ? s.substring(5) : s)
.collect(Collectors.toList());
cmdResult = new HashMap<>();
run();
}
private void cleanup() {
executor = null;
commands = null;
cmdResult = null;
lastMsg = 0;
executing = -1;
running = false;
}
public void run() {
long startTime = System.currentTimeMillis();
Message.IMPORT_START.send(executor);
final Sender fake = new FakeSender(this);
int index = 0;
for (String command : commands) {
if (lastMsg < (System.currentTimeMillis() / 1000) - 5) {
lastMsg = System.currentTimeMillis() / 1000;
sendProgress(index);
}
executing = index;
try {
CommandResult result = commandManager.onCommand(fake, "perms", Arrays.asList(Patterns.SPACE.split(command)));
getResult(index).setResult(result);
} catch (Exception e) {
getResult(index).setResult(CommandResult.FAILURE);
e.printStackTrace();
}
index++;
}
long endTime = System.currentTimeMillis();
double seconds = (endTime - startTime) / 1000.0;
int errors = 0;
for (Map.Entry<Integer, Result> e : cmdResult.entrySet()) {
if (e.getValue().getResult() != null && !e.getValue().getResult().booleanValue()) {
errors++;
}
}
if (errors == 0) {
Message.IMPORT_END_COMPLETE.send(executor, seconds);
} else if (errors == 1) {
Message.IMPORT_END_COMPLETE_ERR_SIN.send(executor, seconds, errors);
} else {
Message.IMPORT_END_COMPLETE_ERR.send(executor, seconds, errors);
}
int errIndex = 1;
for (Map.Entry<Integer, Result> e : cmdResult.entrySet()) {
if (e.getValue().getResult() != null && !e.getValue().getResult().booleanValue()) {
Message.IMPORT_END_ERROR_HEADER.send(executor, errIndex, e.getKey());
for (String s : e.getValue().getOutput()) {
Message.IMPORT_END_ERROR_CONTENT.send(executor, s);
}
Message.IMPORT_END_ERROR_FOOTER.send(executor);
errIndex++;
}
}
cleanup();
}
private void sendProgress(int executing) {
int percent = (executing / commands.size()) * 100;
int errors = 0;
for (Map.Entry<Integer, Result> e : cmdResult.entrySet()) {
if (e.getValue().getResult() != null && !e.getValue().getResult().booleanValue()) {
errors++;
}
}
if (errors == 1) {
Message.IMPORT_PROGRESS_SIN.send(executor, percent, executing, commands.size(), errors);
} else {
Message.IMPORT_PROGRESS.send(executor, percent, executing, commands.size(), errors);
}
}
private Result getResult(int executing) {
if (!cmdResult.containsKey(executing)) {
cmdResult.put(executing, new Result());
}
return cmdResult.get(executing);
}
private void logMessage(String msg) {
getResult(executing).getOutput().add(Util.stripColor(msg));
}
private static class FakeSender implements Sender {
private final Importer instance;
public FakeSender(Importer instance) {
this.instance = instance;
}
@Override
public String getName() {
return Constants.getImporterName();
}
@Override
public UUID getUuid() {
return Constants.getImporterUUID();
}
@Override
public void sendMessage(String s) {
instance.logMessage(s);
}
@Override
public boolean hasPermission(Permission permission) {
return true;
}
}
private static class Result {
@Getter
private final List<String> output = new ArrayList<>();
@Getter
@Setter
private CommandResult result = CommandResult.FAILURE;
}
}
@@ -0,0 +1,194 @@
/*
* 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.data;
import com.google.common.collect.ImmutableSortedSet;
import lombok.Getter;
import me.lucko.luckperms.api.LogEntry;
import java.util.*;
import java.util.stream.Collectors;
public class Log {
public static Builder builder() {
return new Builder();
}
private static final int PAGE_ENTRIES = 5;
@Getter
private final SortedSet<LogEntry> content;
public Log(SortedSet<LogEntry> content) {
this.content = ImmutableSortedSet.copyOf(content);
}
public SortedSet<LogEntry> getRecent() {
return content;
}
public SortedMap<Integer, LogEntry> getRecent(int pageNo) {
return getPage(content, pageNo, PAGE_ENTRIES);
}
public int getRecentMaxPages() {
return getMaxPages(content.size(), PAGE_ENTRIES);
}
public SortedSet<LogEntry> getRecent(UUID actor) {
return content.stream()
.filter(e -> e.getActor().equals(actor))
.collect(Collectors.toCollection(TreeSet::new));
}
public SortedMap<Integer, LogEntry> getRecent(int pageNo, UUID actor) {
return getPage(getRecent(actor), pageNo, PAGE_ENTRIES);
}
public int getRecentMaxPages(UUID actor) {
return getMaxPages(content.stream()
.filter(e -> e.getActor().equals(actor))
.count(), PAGE_ENTRIES);
}
public SortedSet<LogEntry> getUserHistory(UUID uuid) {
return content.stream()
.filter(e -> e.getType() == 'U')
.filter(e -> e.getActed().equals(uuid))
.collect(Collectors.toCollection(TreeSet::new));
}
public SortedMap<Integer, LogEntry> getUserHistory(int pageNo, UUID uuid) {
return getPage(getUserHistory(uuid), pageNo, PAGE_ENTRIES);
}
public int getUserHistoryMaxPages(UUID uuid) {
return getMaxPages(content.stream()
.filter(e -> e.getType() == 'U')
.filter(e -> e.getActed().equals(uuid))
.count(), PAGE_ENTRIES);
}
public SortedSet<LogEntry> getGroupHistory(String name) {
return content.stream()
.filter(e -> e.getType() == 'G')
.filter(e -> e.getActedName().equals(name))
.collect(Collectors.toCollection(TreeSet::new));
}
public SortedMap<Integer, LogEntry> getGroupHistory(int pageNo, String name) {
return getPage(getGroupHistory(name), pageNo, PAGE_ENTRIES);
}
public int getGroupHistoryMaxPages(String name) {
return getMaxPages(content.stream()
.filter(e -> e.getType() == 'G')
.filter(e -> e.getActedName().equals(name))
.count(), PAGE_ENTRIES);
}
public SortedSet<LogEntry> getTrackHistory(String name) {
return content.stream()
.filter(e -> e.getType() == 'T')
.filter(e -> e.getActedName().equals(name))
.collect(Collectors.toCollection(TreeSet::new));
}
public SortedMap<Integer, LogEntry> getTrackHistory(int pageNo, String name) {
return getPage(getTrackHistory(name), pageNo, PAGE_ENTRIES);
}
public int getTrackHistoryMaxPages(String name) {
return getMaxPages(content.stream()
.filter(e -> e.getType() == 'T')
.filter(e -> e.getActedName().equals(name))
.count(), PAGE_ENTRIES);
}
public SortedSet<LogEntry> getSearch(String query) {
return content.stream()
.filter(e -> e.matchesSearch(query))
.collect(Collectors.toCollection(TreeSet::new));
}
public SortedMap<Integer, LogEntry> getSearch(int pageNo, String query) {
return getPage(getSearch(query), pageNo, PAGE_ENTRIES);
}
public int getSearchMaxPages(String query) {
return getMaxPages(content.stream()
.filter(e -> e.matchesSearch(query))
.count(), PAGE_ENTRIES);
}
private static SortedMap<Integer, LogEntry> getPage(Set<LogEntry> set, int pageNo, int entries) {
if (pageNo < 1) {
throw new IllegalArgumentException("pageNo cannot be less than 1: " + pageNo);
}
int minimumEntries = ((pageNo * 5) - entries) + 1;
if (set.size() < minimumEntries) {
throw new IllegalStateException("Log does not contain that many entries. " +
"Requested: " + minimumEntries + ", Log Count: " + set.size());
}
final SortedMap<Integer, LogEntry> out = new TreeMap<>();
final int max = minimumEntries + entries - 1;
int index = 0;
for (LogEntry e : set) {
index++;
if (index >= minimumEntries) {
out.put(index, e);
}
if (index == max) {
break;
}
}
return out;
}
private static int getMaxPages(int size, int entries) {
return (int) Math.ceil((double) size / entries);
}
private static int getMaxPages(long size, int entries) {
return (int) Math.ceil((double) size / entries);
}
@SuppressWarnings("WeakerAccess")
public static class Builder {
private final SortedSet<LogEntry> content = new TreeSet<>();
public Builder add(LogEntry e) {
content.add(e);
return this;
}
public Log build() {
return new Log(content);
}
}
}
@@ -0,0 +1,173 @@
/*
* 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.data;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class LogEntryBuilder {
private long timestamp = 0L;
private UUID actor = null;
private String actorName = null;
private char type = Character.MIN_VALUE;
private UUID acted = null;
private String actedName = null;
private String action = null;
public static LogEntryBuilder get() {
return new LogEntryBuilder();
}
public LogEntryBuilder timestamp(long timestamp) {
this.timestamp = timestamp;
return this;
}
public LogEntryBuilder actor(UUID actor) {
this.actor = actor;
return this;
}
public LogEntryBuilder actorName(String actorName) {
this.actorName = actorName;
return this;
}
public LogEntryBuilder actor(Sender actor) {
this.actorName = actor.getName();
this.actor = actor.getUuid();
return this;
}
public LogEntryBuilder type(char type) {
this.type = type;
return this;
}
public LogEntryBuilder type(String type) {
this.type = type.toCharArray()[0];
return this;
}
public LogEntryBuilder type(Object object) {
if (object instanceof User) {
this.type = 'U';
} else if (object instanceof Group) {
this.type = 'G';
} else if (object instanceof Track) {
this.type = 'T';
} else {
throw new IllegalArgumentException();
}
return this;
}
public LogEntryBuilder acted(UUID acted) {
this.acted = acted;
return this;
}
public LogEntryBuilder actedName(String actedName) {
this.actedName = actedName;
return this;
}
public LogEntryBuilder acted(PermissionHolder acted) {
if (acted instanceof User) {
this.actedName = ((User) acted).getName();
this.acted = ((User) acted).getUuid();
this.type = 'U';
} else if (acted instanceof Group) {
this.actedName = ((Group) acted).getName();
this.type = 'G';
}
return this;
}
public LogEntryBuilder acted(Track track) {
this.actedName = track.getName();
this.type = 'T';
return this;
}
public LogEntryBuilder action(String action) {
this.action = action;
return this;
}
private Set<String> getEmpty() {
Set<String> s = new HashSet<>();
if (actor == null) {
s.add("actor");
}
if (actorName == null) {
s.add("actorname");
}
if (type == Character.MIN_VALUE) {
s.add("type");
}
if (actedName == null) {
s.add("actedname");
}
if (action == null) {
s.add("action");
}
return s;
}
public void submit(LuckPermsPlugin plugin) {
final LogEntry entry = build();
plugin.getDatastore().logAction(entry, Callback.empty());
final String msg = entry.getFormatted();
plugin.getSenders().stream()
.filter(Permission.LOG_NOTIFY::isAuthorized)
.filter(s -> !plugin.getIgnoringLogs().contains(s.getUuid()))
.forEach(s -> Message.LOG.send(s, msg));
}
public LogEntry build() {
if (timestamp == 0L) {
timestamp = System.currentTimeMillis() / 1000;
}
if (!getEmpty().isEmpty()) {
throw new IllegalStateException("Missing values: " + getEmpty().toString());
}
return new LogEntry(timestamp, actor, actorName, type, acted, actedName, action);
}
}
@@ -1,37 +0,0 @@
/*
* 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.data;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class MySQLConfiguration implements me.lucko.luckperms.api.data.MySQLConfiguration {
private final String address;
private final String database;
private final String username;
private final String password;
}
@@ -1,547 +0,0 @@
/*
* 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.data.methods;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.data.Datastore;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@SuppressWarnings({"ResultOfMethodCallIgnored", "UnnecessaryLocalVariable"})
public class FlatfileDatastore extends Datastore {
private Map<String, String> uuidCache = new ConcurrentHashMap<>();
private final File pluginDir;
private File usersDir;
private File groupsDir;
private File tracksDir;
private File uuidData;
public FlatfileDatastore(LuckPermsPlugin plugin, File pluginDir) {
super(plugin, "Flatfile - JSON");
this.pluginDir = pluginDir;
}
private boolean doWrite(File file, WriteOperation writeOperation) {
boolean success = false;
try {
@Cleanup FileWriter fileWriter = new FileWriter(file);
@Cleanup BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
@Cleanup JsonWriter jsonWriter = new JsonWriter(bufferedWriter);
jsonWriter.setIndent(" ");
success = writeOperation.onRun(jsonWriter);
jsonWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
private boolean doRead(File file, ReadOperation readOperation) {
boolean success = false;
try {
@Cleanup FileReader fileReader = new FileReader(file);
@Cleanup BufferedReader bufferedReader = new BufferedReader(fileReader);
@Cleanup JsonReader jsonReader = new JsonReader(bufferedReader);
success = readOperation.onRun(jsonReader);
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
@Override
public void init() {
try {
makeFiles();
} catch (IOException e) {
e.printStackTrace();
return;
}
uuidCache.putAll(getUUIDCache());
setAcceptingLogins(true);
}
private void makeFiles() throws IOException {
File data = new File(pluginDir, "data");
data.mkdirs();
usersDir = new File(data, "users");
usersDir.mkdir();
groupsDir = new File(data, "groups");
groupsDir.mkdir();
tracksDir = new File(data, "tracks");
tracksDir.mkdir();
uuidData = new File(data, "uuidcache.txt");
uuidData.createNewFile();
}
@Override
public void shutdown() {
saveUUIDCache(uuidCache);
}
@Override
public boolean loadOrCreateUser(UUID uuid, String username) {
User user = plugin.getUserManager().makeUser(uuid, username);
File userFile = new File(usersDir, uuid.toString() + ".json");
if (!userFile.exists()) {
try {
userFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
plugin.getUserManager().giveDefaults(user);
boolean success = doWrite(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 : user.getNodes().entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
if (!success) return false;
}
boolean success = doRead(userFile, reader -> {
reader.beginObject();
reader.nextName(); // uuid record
reader.nextString(); // uuid
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // primaryGroup record
user.setPrimaryGroup(reader.nextString()); // primaryGroup
reader.nextName(); //perms
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
user.getNodes().put(node, b);
}
reader.endObject();
reader.endObject();
return true;
});
if (success) plugin.getUserManager().updateOrSetUser(user);
return success;
}
@Override
public boolean loadUser(UUID uuid) {
User user = plugin.getUserManager().makeUser(uuid);
File userFile = new File(usersDir, uuid.toString() + ".json");
if (!userFile.exists()) {
return false;
}
boolean success = doRead(userFile, reader -> {
reader.beginObject();
reader.nextName(); // uuid record
reader.nextString(); // uuid
reader.nextName(); // name record
user.setName(reader.nextString()); // name
reader.nextName(); // primaryGroup record
user.setPrimaryGroup(reader.nextString()); // primaryGroup
reader.nextName(); // perms record
reader.beginObject();
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
user.getNodes().put(node, b);
}
reader.endObject();
reader.endObject();
return true;
});
if (success) plugin.getUserManager().updateOrSetUser(user);
return success;
}
@Override
public boolean saveUser(User user) {
File userFile = new File(usersDir, user.getUuid().toString() + ".json");
if (!userFile.exists()) {
try {
userFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(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 : user.getNodes().entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
return success;
}
@Override
public boolean createAndLoadGroup(String name) {
Group group = plugin.getGroupManager().makeGroup(name);
File groupFile = new File(groupsDir, name + ".json");
if (!groupFile.exists()) {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
boolean success = doWrite(groupFile, writer -> {
writer.beginObject();
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : group.getNodes().entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
if (!success) return false;
}
boolean success = doRead(groupFile, 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();
group.getNodes().put(node, b);
}
reader.endObject();
reader.endObject();
return true;
});
if (success) plugin.getGroupManager().updateOrSetGroup(group);
return success;
}
@Override
public boolean loadGroup(String name) {
Group group = plugin.getGroupManager().makeGroup(name);
File groupFile = new File(groupsDir, name + ".json");
if (!groupFile.exists()) {
return false;
}
boolean success = doRead(groupFile, 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();
group.getNodes().put(node, b);
}
reader.endObject();
reader.endObject();
return true;
});
if (success) plugin.getGroupManager().updateOrSetGroup(group);
return success;
}
@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());
plugin.getGroupManager().unloadAll();
groups.forEach(this::loadGroup);
return true;
}
@Override
public boolean saveGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".json");
if (!groupFile.exists()) {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(groupFile, writer -> {
writer.beginObject();
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
for (Map.Entry<String, Boolean> e : group.getNodes().entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
return success;
}
@Override
public boolean deleteGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".json");
if (groupFile.exists()) {
groupFile.delete();
}
return true;
}
@Override
public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().makeTrack(name);
List<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".json");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
boolean success = doWrite(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;
});
if (!success) return false;
}
boolean success = doRead(trackFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // groups record
reader.beginArray();
while (reader.hasNext()) {
groups.add(reader.nextString());
}
reader.endArray();
reader.endObject();
return true;
});
track.setGroups(groups);
if (success) plugin.getTrackManager().updateOrSetTrack(track);
return success;
}
@Override
public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().makeTrack(name);
List<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".json");
if (!trackFile.exists()) {
return false;
}
boolean success = doRead(trackFile, reader -> {
reader.beginObject();
reader.nextName(); // name record
reader.nextString(); // name
reader.nextName(); // groups record
reader.beginArray();
while (reader.hasNext()) {
groups.add(reader.nextString());
}
reader.endArray();
reader.endObject();
return true;
});
track.setGroups(groups);
if (success) plugin.getTrackManager().updateOrSetTrack(track);
return success;
}
@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());
plugin.getTrackManager().unloadAll();
tracks.forEach(this::loadTrack);
return true;
}
@Override
public boolean saveTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".json");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean success = doWrite(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;
});
return success;
}
@Override
public boolean deleteTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".json");
if (trackFile.exists()) {
trackFile.delete();
}
return true;
}
private Map<String, String> getUUIDCache() {
Map<String, String> cache = new HashMap<>();
try {
@Cleanup FileReader fileReader = new FileReader(uuidData);
@Cleanup BufferedReader bufferedReader = new BufferedReader(fileReader);
Properties props = new Properties();
props.load(bufferedReader);
for (String key : props.stringPropertyNames()) {
cache.put(key, props.getProperty(key));
}
} catch (IOException e) {
e.printStackTrace();
}
return cache;
}
private void saveUUIDCache(Map<String, String> cache) {
try {
@Cleanup FileWriter fileWriter = new FileWriter(uuidData);
@Cleanup BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
Properties properties = new Properties();
properties.putAll(cache);
properties.store(bufferedWriter, null);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean saveUUIDData(String username, UUID uuid) {
username = username.toLowerCase();
uuidCache.put(username, uuid.toString());
return true;
}
@Override
public UUID getUUID(String username) {
username = username.toLowerCase();
if (uuidCache.get(username) == null) return null;
return UUID.fromString(uuidCache.get(username));
}
interface WriteOperation {
boolean onRun(JsonWriter writer) throws IOException;
}
interface ReadOperation {
boolean onRun(JsonReader reader) throws IOException;
}
}
@@ -1,126 +0,0 @@
/*
* 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.data.methods;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.data.MySQLConfiguration;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class MySQLDatastore extends SQLDatastore {
private static final String CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;";
private static final String CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;";
private static final String CREATETABLE_GROUPS = "CREATE TABLE IF NOT EXISTS `lp_groups` (`name` VARCHAR(36) NOT NULL, `perms` TEXT NULL, PRIMARY KEY (`name`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;";
private static final String CREATETABLE_TRACKS = "CREATE TABLE IF NOT EXISTS `lp_tracks` (`name` VARCHAR(36) NOT NULL, `groups` TEXT NULL, PRIMARY KEY (`name`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;";
private final MySQLConfiguration configuration;
private HikariDataSource hikari;
public MySQLDatastore(LuckPermsPlugin plugin, MySQLConfiguration configuration) {
super(plugin, "MySQL");
this.configuration = configuration;
}
@Override
public void init() {
hikari = new HikariDataSource();
final String address = configuration.getAddress();
final String database = configuration.getDatabase();
final String username = configuration.getUsername();
final String password = configuration.getPassword();
hikari.setMaximumPoolSize(10);
hikari.setDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
hikari.addDataSourceProperty("serverName", address.split(":")[0]);
hikari.addDataSourceProperty("port", address.split(":")[1]);
hikari.addDataSourceProperty("databaseName", database);
hikari.addDataSourceProperty("user", username);
hikari.addDataSourceProperty("password", password);
if (!setupTables(CREATETABLE_UUID, CREATETABLE_USERS, CREATETABLE_GROUPS, CREATETABLE_TRACKS)) {
plugin.getLog().severe("Error occurred whilst initialising the database. All connections are disallowed.");
shutdown();
} else {
setAcceptingLogins(true);
}
}
@Override
boolean runQuery(QueryPS queryPS) {
boolean success = false;
try {
@Cleanup Connection connection = getConnection();
if (connection == null || connection.isClosed()) {
throw new IllegalStateException("SQL connection is null");
}
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery());
queryPS.onRun(preparedStatement);
preparedStatement.execute();
success = true;
} catch (SQLException e) {
e.printStackTrace();
}
return success;
}
@Override
boolean runQuery(QueryRS queryRS) {
boolean success = false;
try {
@Cleanup Connection connection = getConnection();
if (connection == null || connection.isClosed()) {
throw new IllegalStateException("SQL connection is null");
}
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryRS.getQuery());
queryRS.onRun(preparedStatement);
preparedStatement.execute();
@Cleanup ResultSet resultSet = preparedStatement.executeQuery();
success = queryRS.onResult(resultSet);
} catch (SQLException e) {
e.printStackTrace();
}
return success;
}
@Override
public void shutdown() {
if (hikari != null) {
hikari.close();
}
}
@Override
Connection getConnection() throws SQLException {
return hikari.getConnection();
}
}
@@ -1,468 +0,0 @@
/*
* 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.data.methods;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.data.Datastore;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.groups.GroupManager;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.User;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@SuppressWarnings("UnnecessaryLocalVariable")
abstract class SQLDatastore extends Datastore {
private static final Type NM_TYPE = new TypeToken<Map<String, Boolean>>(){}.getType();
private static final Type T_TYPE = new TypeToken<List<String>>(){}.getType();
private static final String USER_INSERT = "INSERT INTO lp_users VALUES(?, ?, ?, ?)";
private static final String USER_SELECT = "SELECT * FROM lp_users WHERE uuid=?";
private static final String USER_UPDATE = "UPDATE lp_users SET name=?, primary_group = ?, perms=? WHERE uuid=?";
private static final String GROUP_INSERT = "INSERT INTO lp_groups VALUES(?, ?)";
private static final String GROUP_SELECT = "SELECT perms FROM lp_groups WHERE name=?";
private static final String GROUP_SELECT_ALL = "SELECT * FROM lp_groups";
private static final String GROUP_UPDATE = "UPDATE lp_groups SET perms=? WHERE name=?";
private static final String GROUP_DELETE = "DELETE FROM lp_groups WHERE name=?";
private static final String TRACK_INSERT = "INSERT INTO lp_tracks VALUES(?, ?)";
private static final String TRACK_SELECT = "SELECT groups FROM lp_tracks WHERE name=?";
private static final String TRACK_SELECT_ALL = "SELECT * FROM lp_tracks";
private static final String TRACK_UPDATE = "UPDATE lp_tracks SET groups=? WHERE name=?";
private static final String TRACK_DELETE = "DELETE FROM lp_tracks WHERE name=?";
private static final String UUIDCACHE_INSERT = "INSERT INTO lp_uuid VALUES(?, ?)";
private static final String UUIDCACHE_SELECT = "SELECT uuid FROM lp_uuid WHERE name=?";
private static final String UUIDCACHE_UPDATE = "UPDATE lp_uuid SET uuid=? WHERE name=?";
private final Gson gson;
SQLDatastore(LuckPermsPlugin plugin, String name) {
super(plugin, name);
gson = new Gson();
}
abstract Connection getConnection() throws SQLException;
abstract boolean runQuery(QueryPS queryPS);
abstract boolean runQuery(QueryRS queryRS);
boolean setupTables(String... tableQueries) {
boolean success = true;
for (String q : tableQueries) {
if (!runQuery(new Query(q))) success = false;
}
return success;
}
@Override
public boolean loadUser(UUID uuid) {
User user = plugin.getUserManager().makeUser(uuid);
boolean success = runQuery(new QueryRS(USER_SELECT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, uuid.toString());
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
user.setName(resultSet.getString("name"));
user.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
user.setPrimaryGroup(resultSet.getString("primary_group"));
return true;
}
return false;
}
});
if (success) plugin.getUserManager().updateOrSetUser(user);
return success;
}
@Override
public boolean loadOrCreateUser(UUID uuid, String username) {
User user = plugin.getUserManager().makeUser(uuid, username);
boolean success = runQuery(new QueryRS(USER_SELECT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, user.getUuid().toString());
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
boolean success = true;
if (!resultSet.next()) {
plugin.getUserManager().giveDefaults(user);
success = runQuery(new QueryPS(USER_INSERT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, user.getUuid().toString());
preparedStatement.setString(2, user.getName());
preparedStatement.setString(3, user.getPrimaryGroup());
preparedStatement.setString(4, gson.toJson(user.getNodes()));
}
});
} else {
user.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
user.setPrimaryGroup(resultSet.getString("primary_group"));
}
return success;
}
});
if (success) plugin.getUserManager().updateOrSetUser(user);
return success;
}
@Override
public boolean saveUser(User user) {
boolean success = runQuery(new QueryPS(USER_UPDATE) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, user.getName());
preparedStatement.setString(2, user.getPrimaryGroup());
preparedStatement.setString(3, gson.toJson(user.getNodes()));
preparedStatement.setString(4, user.getUuid().toString());
}
});
return success;
}
@Override
public boolean createAndLoadGroup(String name) {
Group group = plugin.getGroupManager().makeGroup(name);
boolean success = runQuery(new QueryRS(GROUP_SELECT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, group.getName());
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
boolean success = true;
if (!resultSet.next()) {
success = runQuery(new QueryPS(GROUP_INSERT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, group.getName());
preparedStatement.setString(2, gson.toJson(group.getNodes()));
}
});
} else {
group.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
}
return success;
}
});
if (success) plugin.getGroupManager().updateOrSetGroup(group);
return success;
}
@Override
public boolean loadGroup(String name) {
Group group = plugin.getGroupManager().makeGroup(name);
boolean success = runQuery(new QueryRS(GROUP_SELECT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, name);
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
group.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
return true;
}
return false;
}
});
if (success) plugin.getGroupManager().updateOrSetGroup(group);
return success;
}
@Override
public boolean loadAllGroups() {
List<Group> groups = new ArrayList<>();
boolean success = runQuery(new QueryRS(GROUP_SELECT_ALL) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
while (resultSet.next()) {
Group group = plugin.getGroupManager().makeGroup(resultSet.getString("name"));
group.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
groups.add(group);
}
return true;
}
});
if (success) {
GroupManager gm = plugin.getGroupManager();
gm.unloadAll();
groups.forEach(gm::setGroup);
}
return success;
}
@Override
public boolean saveGroup(Group group) {
boolean success = runQuery(new QueryPS(GROUP_UPDATE) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, gson.toJson(group.getNodes()));
preparedStatement.setString(2, group.getName());
}
});
return success;
}
@Override
public boolean deleteGroup(Group group) {
boolean success = runQuery(new QueryPS(GROUP_DELETE) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, group.getName());
}
});
if (success) plugin.getGroupManager().unloadGroup(group);
return success;
}
@Override
public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().makeTrack(name);
boolean success = runQuery(new QueryRS(TRACK_SELECT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, track.getName());
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
boolean success = true;
if (!resultSet.next()) {
success = runQuery(new QueryPS(TRACK_INSERT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, track.getName());
preparedStatement.setString(2, gson.toJson(track.getGroups()));
}
});
} else {
track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE));
}
return success;
}
});
if (success) plugin.getTrackManager().updateOrSetTrack(track);
return success;
}
@Override
public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().makeTrack(name);
boolean success = runQuery(new QueryRS(TRACK_SELECT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, name);
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE));
return true;
}
return false;
}
});
if (success) plugin.getTrackManager().updateOrSetTrack(track);
return success;
}
@Override
public boolean loadAllTracks() {
List<Track> tracks = new ArrayList<>();
boolean success = runQuery(new QueryRS(TRACK_SELECT_ALL) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
while (resultSet.next()) {
Track track = plugin.getTrackManager().makeTrack(resultSet.getString("name"));
track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE));
tracks.add(track);
}
return true;
}
});
if (success) {
TrackManager tm = plugin.getTrackManager();
tm.unloadAll();
tracks.forEach(tm::setTrack);
}
return success;
}
@Override
public boolean saveTrack(Track track) {
boolean success = runQuery(new QueryPS(TRACK_UPDATE) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, gson.toJson(track.getGroups()));
preparedStatement.setString(2, track.getName());
}
});
return success;
}
@Override
public boolean deleteTrack(Track track) {
boolean success = runQuery(new QueryPS(TRACK_DELETE) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, track.getName());
}
});
if (success) plugin.getTrackManager().unloadTrack(track);
return success;
}
@Override
public boolean saveUUIDData(String username, UUID uuid) {
final String u = username.toLowerCase();
boolean success = runQuery(new QueryRS(UUIDCACHE_SELECT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, u);
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
boolean success;
if (resultSet.next()) {
success = runQuery(new QueryPS(UUIDCACHE_UPDATE) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, uuid.toString());
preparedStatement.setString(2, u);
}
});
} else {
success = runQuery(new QueryPS(UUIDCACHE_INSERT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, u);
preparedStatement.setString(2, uuid.toString());
}
});
}
return success;
}
});
return success;
}
@Override
public UUID getUUID(String username) {
final String u = username.toLowerCase();
final UUID[] uuid = {null};
boolean success = runQuery(new QueryRS(UUIDCACHE_SELECT) {
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, u);
}
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
uuid[0] = UUID.fromString(resultSet.getString("uuid"));
return true;
}
return false;
}
});
return success ? uuid[0] : null;
}
private class Query extends QueryPS {
Query(String query) {
super(query);
}
@Override
void onRun(PreparedStatement preparedStatement) throws SQLException {
// Do nothing
}
}
@Getter
@AllArgsConstructor
abstract class QueryPS {
private final String query;
abstract void onRun(PreparedStatement preparedStatement) throws SQLException;
}
@Getter
@AllArgsConstructor
abstract class QueryRS {
private final String query;
abstract void onRun(PreparedStatement preparedStatement) throws SQLException;
abstract boolean onResult(ResultSet resultSet) throws SQLException;
}
}
@@ -1,119 +0,0 @@
/*
* 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.data.methods;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import java.io.File;
import java.sql.*;
public class SQLiteDatastore extends SQLDatastore {
private static final String CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`));";
private static final String CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`));";
private static final String CREATETABLE_GROUPS = "CREATE TABLE IF NOT EXISTS `lp_groups` (`name` VARCHAR(36) NOT NULL, `perms` TEXT NULL, PRIMARY KEY (`name`));";
private static final String CREATETABLE_TRACKS = "CREATE TABLE IF NOT EXISTS `lp_tracks` (`name` VARCHAR(36) NOT NULL, `groups` TEXT NULL, PRIMARY KEY (`name`));";
private final File file;
private Connection connection = null;
public SQLiteDatastore(LuckPermsPlugin plugin, File file) {
super(plugin, "SQLite");
this.file = file;
}
@Override
public void init() {
if (!setupTables(CREATETABLE_UUID, CREATETABLE_USERS, CREATETABLE_GROUPS, CREATETABLE_TRACKS)) {
plugin.getLog().severe("Error occurred whilst initialising the database. All connections are disallowed.");
shutdown();
} else {
setAcceptingLogins(true);
}
}
@Override
boolean runQuery(QueryPS queryPS) {
boolean success = false;
try {
Connection connection = getConnection();
if (connection == null || connection.isClosed()) {
throw new IllegalStateException("SQL connection is null");
}
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery());
queryPS.onRun(preparedStatement);
preparedStatement.execute();
success = true;
} catch (SQLException e) {
e.printStackTrace();
}
return success;
}
@Override
boolean runQuery(QueryRS queryRS) {
boolean success = false;
try {
Connection connection = getConnection();
if (connection == null || connection.isClosed()) {
throw new IllegalStateException("SQL connection is null");
}
@Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryRS.getQuery());
queryRS.onRun(preparedStatement);
preparedStatement.execute();
@Cleanup ResultSet resultSet = preparedStatement.executeQuery();
success = queryRS.onResult(resultSet);
} catch (SQLException e) {
e.printStackTrace();
}
return success;
}
@Override
public void shutdown() {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
Connection getConnection() throws SQLException {
if (connection == null || connection.isClosed()) {
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException ignored) {}
connection = DriverManager.getConnection("jdbc:sqlite:" + file.getAbsolutePath());
}
return connection;
}
}