Java tutorial
/* * 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 lombok.Getter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import me.lucko.luckperms.api.HeldPermission; import me.lucko.luckperms.api.LogEntry; import me.lucko.luckperms.api.Node; 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.data.Log; 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.backing.sqlprovider.SQLProvider; import me.lucko.luckperms.common.storage.backing.utils.LegacySchemaMigration; import me.lucko.luckperms.common.storage.backing.utils.NodeDataHolder; import me.lucko.luckperms.common.storage.holder.NodeHeldPermission; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.stream.Collectors; public class MSSQLBacking extends AbstractBacking { private static final Type LIST_STRING_TYPE = new TypeToken<List<String>>() { }.getType(); private static final String USER_PERMISSIONS_SELECT = "SELECT permission, value, server, world, expiry, contexts FROM user_permissions WHERE uuid=?"; private static final String USER_PERMISSIONS_DELETE_SPECIFIC = "DELETE FROM user_permissions WHERE uuid=? AND permission=? AND value=? AND server=? AND world=? AND expiry=? AND contexts=?"; private static final String USER_PERMISSIONS_DELETE = "DELETE FROM user_permissions WHERE uuid=?"; private static final String USER_PERMISSIONS_INSERT = "INSERT INTO user_permissions(uuid, permission, value, server, world, expiry, contexts) VALUES(?, ?, ?, ?, ?, ?, ?)"; private static final String USER_PERMISSIONS_SELECT_DISTINCT = "SELECT DISTINCT uuid FROM user_permissions"; private static final String USER_PERMISSIONS_SELECT_PERMISSION = "SELECT uuid, value, server, world, expiry, contexts FROM user_permissions WHERE permission=?"; private static final String PLAYER_SELECT = "SELECT username, primary_group FROM players WHERE uuid=?"; private static final String PLAYER_SELECT_UUID = "SELECT 1 uuid FROM players WHERE username=?"; private static final String PLAYER_SELECT_USERNAME = "SELECT 1 username FROM players WHERE uuid=?"; private static final String PLAYER_INSERT = "INSERT INTO players VALUES(?, ?, ?)"; private static final String PLAYER_UPDATE = "UPDATE players SET username=? WHERE uuid=?"; private static final String PLAYER_DELETE = "DELETE FROM players WHERE username=? AND NOT uuid=?"; private static final String PLAYER_UPDATE_PRIMARY_GROUP = "UPDATE players SET primary_group=? WHERE uuid=?"; private static final String GROUP_PERMISSIONS_SELECT = "SELECT permission, value, server, world, expiry, contexts FROM group_permissions WHERE name=?"; private static final String GROUP_PERMISSIONS_DELETE = "DELETE FROM group_permissions WHERE name=?"; private static final String GROUP_PERMISSIONS_DELETE_SPECIFIC = "DELETE FROM group_permissions WHERE name=? AND permission=? AND value=? AND server=? AND world=? AND expiry=? AND contexts=?"; private static final String GROUP_PERMISSIONS_INSERT = "INSERT INTO group_permissions(name, permission, value, server, world, expiry, contexts) VALUES(?, ?, ?, ?, ?, ?, ?)"; private static final String GROUP_PERMISSIONS_SELECT_PERMISSION = "SELECT name, value, server, world, expiry, contexts FROM group_permissions WHERE permission=?"; private static final String GROUP_SELECT_ALL = "SELECT name FROM groups"; private static final String GROUP_INSERT = "INSERT INTO groups VALUES(?)"; private static final String GROUP_DELETE = "DELETE FROM groups WHERE name=?"; private static final String TRACK_INSERT = "INSERT INTO tracks VALUES(?, ?)"; private static final String TRACK_SELECT = "SELECT groups FROM tracks WHERE name=?"; private static final String TRACK_SELECT_ALL = "SELECT * FROM tracks"; private static final String TRACK_UPDATE = "UPDATE tracks SET groups=? WHERE name=?"; private static final String TRACK_DELETE = "DELETE FROM tracks WHERE name=?"; private static final String ACTION_INSERT = "INSERT INTO actions(time, actor_uuid, actor_name, type, acted_uuid, acted_name, action) VALUES(?, ?, ?, ?, ?, ?, ?)"; private static final String ACTION_SELECT_ALL = "SELECT * FROM actions"; @Getter private final Gson gson; @Getter private final SQLProvider provider; //@Getter //private final Function<String, String> prefix; public MSSQLBacking(LuckPermsPlugin plugin, SQLProvider provider, String prefix) { super(plugin, provider.getName()); this.provider = provider; //this.prefix = s -> s.replace("{prefix}", prefix); gson = new Gson(); } private boolean tableExists(String table) throws SQLException { try (Connection connection = provider.getConnection()) { try (ResultSet rs = connection.getMetaData().getTables(null, null, "%", null)) { while (rs.next()) { if (rs.getString(3).equalsIgnoreCase(table)) { return true; } } return false; } } } @Override public void init() { try { provider.init(); // Init tables if (!tableExists("user_permissions")) { String schemaFileName = "lp-schema-" + provider.getName().toLowerCase() + ".sql"; try (InputStream is = plugin.getResourceStream(schemaFileName)) { if (is == null) { throw new Exception("Couldn't locate schema file for " + provider.getName()); } try (BufferedReader reader = new BufferedReader( new InputStreamReader(is, StandardCharsets.UTF_8))) { try (Connection connection = provider.getConnection()) { try (Statement s = connection.createStatement()) { StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { if (line.startsWith("--") || line.startsWith("#")) continue; sb.append(line); // check for end of declaration if (line.endsWith(";")) { sb.deleteCharAt(sb.length() - 1); String result = sb.toString().trim(); if (!result.isEmpty()) s.addBatch(result); // reset sb = new StringBuilder(); } } s.executeBatch(); } } } } // NO MIGRATION FOR YOU! // Try migration from legacy backing /*if (tableExists("lp_users")) { plugin.getLog().severe("===== Legacy Schema Migration ====="); plugin.getLog().severe("Starting migration from legacy schema. This could take a while...."); plugin.getLog().severe("Please do not stop your server while the migration takes place."); new LegacySchemaMigration(this).run(); }*/ } setAcceptingLogins(true); } catch (Exception e) { e.printStackTrace(); plugin.getLog().severe("Error occurred whilst initialising the database."); shutdown(); } } @Override public void shutdown() { try { provider.shutdown(); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean logAction(LogEntry entry) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(ACTION_INSERT)) { ps.setLong(1, entry.getTimestamp()); ps.setString(2, entry.getActor().toString()); ps.setString(3, entry.getActorName()); ps.setString(4, Character.toString(entry.getType())); ps.setString(5, entry.getActed() == null ? "null" : entry.getActed().toString()); ps.setString(6, entry.getActedName()); ps.setString(7, entry.getAction()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } @Override public Log getLog() { final Log.Builder log = Log.builder(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(ACTION_SELECT_ALL)) { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { final String actedUuid = rs.getString("acted_uuid"); LogEntry e = new LogEntry(rs.getLong("time"), UUID.fromString(rs.getString("actor_uuid")), rs.getString("actor_name"), rs.getString("type").toCharArray()[0], actedUuid.equals("null") ? null : UUID.fromString(actedUuid), rs.getString("acted_name"), rs.getString("action")); log.add(e); } } } } catch (SQLException e) { e.printStackTrace(); return null; } return log.build(); } @Override public boolean loadUser(UUID uuid, String username) { User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username)); user.getIoLock().lock(); try { List<NodeDataHolder> data = new ArrayList<>(); AtomicReference<String> primaryGroup = new AtomicReference<>(null); AtomicReference<String> userName = new AtomicReference<>(null); // Collect user permissions try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(USER_PERMISSIONS_SELECT)) { ps.setString(1, user.getUuid().toString()); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { String permission = rs.getString("permission"); boolean value = rs.getBoolean("value"); String server = rs.getString("server"); String world = rs.getString("world"); long expiry = rs.getLong("expiry"); String contexts = rs.getString("contexts"); data.add(NodeDataHolder.of(permission, value, server, world, expiry, contexts)); } } } } catch (SQLException e) { e.printStackTrace(); return false; } // Collect user meta (username & primary group) try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(PLAYER_SELECT)) { ps.setString(1, user.getUuid().toString()); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { userName.set(rs.getString("username")); primaryGroup.set(rs.getString("primary_group")); } } } } catch (SQLException e) { e.printStackTrace(); return false; } // update username & primary group String pg = primaryGroup.get(); if (pg == null) { pg = "default"; } user.setPrimaryGroup(pg); String name = userName.get(); if (name == null) { name = "null"; } // Update their username to what was in the storage if the one in the local instance is null if (user.getName() == null || user.getName().equalsIgnoreCase("null")) { user.setName(name); } // If the user has any data in storage if (!data.isEmpty()) { Set<Node> nodes = data.stream().map(NodeDataHolder::toNode).collect(Collectors.toSet()); user.setNodes(nodes); // Save back to the store if data was changed if (plugin.getUserManager().giveDefaultIfNeeded(user, false)) { // This should be fine, as the lock will be acquired by the same thread. saveUser(user); } } else { // User has no data in storage. if (GenericUserManager.shouldSave(user)) { user.clearNodes(); user.setPrimaryGroup(null); plugin.getUserManager().giveDefaultIfNeeded(user, false); } } return true; } finally { user.getIoLock().unlock(); user.getRefreshBuffer().requestDirectly(); } } @Override public boolean saveUser(User user) { user.getIoLock().lock(); try { // Empty data - just delete from the DB. if (!GenericUserManager.shouldSave(user)) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(USER_PERMISSIONS_DELETE)) { ps.setString(1, user.getUuid().toString()); ps.execute(); } try (PreparedStatement ps = c.prepareStatement(PLAYER_UPDATE_PRIMARY_GROUP)) { ps.setString(1, "default"); ps.setString(2, user.getUuid().toString()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } // Get a snapshot of current data. Set<NodeDataHolder> remote = new HashSet<>(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(USER_PERMISSIONS_SELECT)) { ps.setString(1, user.getUuid().toString()); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { String permission = rs.getString("permission"); boolean value = rs.getBoolean("value"); String server = rs.getString("server"); String world = rs.getString("world"); long expiry = rs.getLong("expiry"); String contexts = rs.getString("contexts"); remote.add(NodeDataHolder.of(permission, value, server, world, expiry, contexts)); } } } } catch (SQLException e) { return false; } Set<NodeDataHolder> local = user.getNodes().stream().map(NodeDataHolder::fromNode) .collect(Collectors.toSet()); Map.Entry<Set<NodeDataHolder>, Set<NodeDataHolder>> diff = compareSets(local, remote); Set<NodeDataHolder> toAdd = diff.getKey(); Set<NodeDataHolder> toRemove = diff.getValue(); if (!toAdd.isEmpty()) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(USER_PERMISSIONS_INSERT)) { for (NodeDataHolder nd : toAdd) { ps.setString(1, user.getUuid().toString()); ps.setString(2, nd.getPermission()); ps.setBoolean(3, nd.isValue()); ps.setString(4, nd.getServer()); ps.setString(5, nd.getWorld()); ps.setLong(6, nd.getExpiry()); ps.setString(7, nd.serialiseContext()); ps.addBatch(); } ps.executeBatch(); } } catch (SQLException e) { e.printStackTrace(); return false; } } if (!toRemove.isEmpty()) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(USER_PERMISSIONS_DELETE_SPECIFIC)) { for (NodeDataHolder nd : toRemove) { ps.setString(1, user.getUuid().toString()); ps.setString(2, nd.getPermission()); ps.setBoolean(3, nd.isValue()); ps.setString(4, nd.getServer()); ps.setString(5, nd.getWorld()); ps.setLong(6, nd.getExpiry()); ps.setString(7, nd.serialiseContext()); ps.addBatch(); } ps.executeBatch(); } } catch (SQLException e) { e.printStackTrace(); return false; } } try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(PLAYER_UPDATE_PRIMARY_GROUP)) { ps.setString(1, user.getPrimaryGroup() == null ? "default" : user.getPrimaryGroup()); ps.setString(2, user.getUuid().toString()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } finally { user.getIoLock().unlock(); } } @Override public boolean cleanupUsers() { return true; // TODO } @Override public Set<UUID> getUniqueUsers() { Set<UUID> uuids = new HashSet<>(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(USER_PERMISSIONS_SELECT_DISTINCT)) { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { String uuid = rs.getString("uuid"); uuids.add(UUID.fromString(uuid)); } } } } catch (SQLException e) { e.printStackTrace(); return null; } return uuids; } @Override public List<HeldPermission<UUID>> getUsersWithPermission(String permission) { ImmutableList.Builder<HeldPermission<UUID>> held = ImmutableList.builder(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(USER_PERMISSIONS_SELECT_PERMISSION)) { ps.setString(1, permission); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { UUID holder = UUID.fromString(rs.getString("uuid")); boolean value = rs.getBoolean("value"); String server = rs.getString("server"); String world = rs.getString("world"); long expiry = rs.getLong("expiry"); String contexts = rs.getString("contexts"); NodeDataHolder data = NodeDataHolder.of(permission, value, server, world, expiry, contexts); held.add(NodeHeldPermission.of(holder, data)); } } } } catch (SQLException e) { e.printStackTrace(); return null; } return held.build(); } @Override public boolean createAndLoadGroup(String name) { List<String> groups = new ArrayList<>(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_SELECT_ALL)) { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { groups.add(rs.getString("name").toLowerCase()); } } } } catch (SQLException e) { e.printStackTrace(); return false; } if (!groups.contains(name)) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_INSERT)) { ps.setString(1, name); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); } } return loadGroup(name); } @Override public boolean loadGroup(String name) { // Check the group actually exists List<String> groups = new ArrayList<>(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_SELECT_ALL)) { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { groups.add(rs.getString("name").toLowerCase()); } } } } catch (SQLException e) { e.printStackTrace(); return false; } if (!groups.contains(name)) { return false; } Group group = plugin.getGroupManager().getOrMake(name); group.getIoLock().lock(); try { List<NodeDataHolder> data = new ArrayList<>(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_PERMISSIONS_SELECT)) { ps.setString(1, group.getName()); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { String permission = rs.getString("permission"); boolean value = rs.getBoolean("value"); String server = rs.getString("server"); String world = rs.getString("world"); long expiry = rs.getLong("expiry"); String contexts = rs.getString("contexts"); data.add(NodeDataHolder.of(permission, value, server, world, expiry, contexts)); } } } } catch (SQLException e) { e.printStackTrace(); return false; } if (!data.isEmpty()) { Set<Node> nodes = data.stream().map(NodeDataHolder::toNode).collect(Collectors.toSet()); group.setNodes(nodes); } else { group.clearNodes(); } return true; } finally { group.getIoLock().unlock(); } } @Override public boolean loadAllGroups() { List<String> groups = new ArrayList<>(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_SELECT_ALL)) { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { groups.add(rs.getString("name").toLowerCase()); } } } } catch (SQLException e) { e.printStackTrace(); return false; } boolean success = true; for (String g : groups) { if (!loadGroup(g)) { success = false; } } if (success) { GroupManager gm = plugin.getGroupManager(); gm.getAll().values().stream().filter(g -> !groups.contains(g.getName())).forEach(gm::unload); } return success; } @Override public boolean saveGroup(Group group) { group.getIoLock().lock(); try { // Empty data, just delete. if (group.getNodes().isEmpty()) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_PERMISSIONS_DELETE)) { ps.setString(1, group.getName()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } // Get a snapshot of current data Set<NodeDataHolder> remote = new HashSet<>(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_PERMISSIONS_SELECT)) { ps.setString(1, group.getName()); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { String permission = rs.getString("permission"); boolean value = rs.getBoolean("value"); String server = rs.getString("server"); String world = rs.getString("world"); long expiry = rs.getLong("expiry"); String contexts = rs.getString("contexts"); remote.add(NodeDataHolder.of(permission, value, server, world, expiry, contexts)); } } } } catch (SQLException e) { e.printStackTrace(); return false; } Set<NodeDataHolder> local = group.getNodes().stream().map(NodeDataHolder::fromNode) .collect(Collectors.toSet()); Map.Entry<Set<NodeDataHolder>, Set<NodeDataHolder>> diff = compareSets(local, remote); Set<NodeDataHolder> toAdd = diff.getKey(); Set<NodeDataHolder> toRemove = diff.getValue(); if (!toAdd.isEmpty()) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_PERMISSIONS_INSERT)) { for (NodeDataHolder nd : toAdd) { ps.setString(1, group.getName()); ps.setString(2, nd.getPermission()); ps.setBoolean(3, nd.isValue()); ps.setString(4, nd.getServer()); ps.setString(5, nd.getWorld()); ps.setLong(6, nd.getExpiry()); ps.setString(7, nd.serialiseContext()); ps.addBatch(); } ps.executeBatch(); } } catch (SQLException e) { e.printStackTrace(); return false; } } if (!toRemove.isEmpty()) { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_PERMISSIONS_DELETE_SPECIFIC)) { for (NodeDataHolder nd : toRemove) { ps.setString(1, group.getName()); ps.setString(2, nd.getPermission()); ps.setBoolean(3, nd.isValue()); ps.setString(4, nd.getServer()); ps.setString(5, nd.getWorld()); ps.setLong(6, nd.getExpiry()); ps.setString(7, nd.serialiseContext()); ps.addBatch(); } ps.executeBatch(); } } catch (SQLException e) { e.printStackTrace(); return false; } } return true; } finally { group.getIoLock().unlock(); } } @Override public boolean deleteGroup(Group group) { group.getIoLock().lock(); try { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_PERMISSIONS_DELETE)) { ps.setString(1, group.getName()); ps.execute(); } try (PreparedStatement ps = c.prepareStatement(GROUP_DELETE)) { ps.setString(1, group.getName()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } finally { group.getIoLock().unlock(); } } @Override public List<HeldPermission<String>> getGroupsWithPermission(String permission) { ImmutableList.Builder<HeldPermission<String>> held = ImmutableList.builder(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(GROUP_PERMISSIONS_SELECT_PERMISSION)) { ps.setString(1, permission); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { String holder = rs.getString("name"); boolean value = rs.getBoolean("value"); String server = rs.getString("server"); String world = rs.getString("world"); long expiry = rs.getLong("expiry"); String contexts = rs.getString("contexts"); NodeDataHolder data = NodeDataHolder.of(permission, value, server, world, expiry, contexts); held.add(NodeHeldPermission.of(holder, data)); } } } } catch (SQLException e) { e.printStackTrace(); return null; } return held.build(); } @Override public boolean createAndLoadTrack(String name) { Track track = plugin.getTrackManager().getOrMake(name); track.getIoLock().lock(); try { AtomicBoolean exists = new AtomicBoolean(false); AtomicReference<String> groups = new AtomicReference<>(null); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(TRACK_SELECT)) { ps.setString(1, track.getName()); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { exists.set(true); groups.set(rs.getString("groups")); } } } } catch (SQLException e) { e.printStackTrace(); return false; } if (exists.get()) { // Track exists, let's load. track.setGroups(gson.fromJson(groups.get(), LIST_STRING_TYPE)); return true; } else { String json = gson.toJson(track.getGroups()); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(TRACK_INSERT)) { ps.setString(1, track.getName()); ps.setString(2, json); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } } finally { track.getIoLock().unlock(); } } @Override public boolean loadTrack(String name) { Track track = plugin.getTrackManager().getOrMake(name); track.getIoLock().lock(); try { AtomicReference<String> groups = new AtomicReference<>(null); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(TRACK_SELECT)) { ps.setString(1, track.getName()); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { groups.set(rs.getString("groups")); } else { return false; } } } } catch (SQLException e) { e.printStackTrace(); return false; } track.setGroups(gson.fromJson(groups.get(), LIST_STRING_TYPE)); return true; } finally { track.getIoLock().unlock(); } } @Override public boolean loadAllTracks() { List<String> tracks = new ArrayList<>(); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(TRACK_SELECT_ALL)) { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { tracks.add(rs.getString("name").toLowerCase()); } } } } catch (SQLException e) { e.printStackTrace(); return false; } boolean success = true; for (String t : tracks) { if (!loadTrack(t)) { success = false; } } if (success) { TrackManager tm = plugin.getTrackManager(); tm.getAll().values().stream().filter(t -> !tracks.contains(t.getName())).forEach(tm::unload); } return success; } @Override public boolean saveTrack(Track track) { track.getIoLock().lock(); try { String s = gson.toJson(track.getGroups()); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(TRACK_UPDATE)) { ps.setString(1, s); ps.setString(2, track.getName()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } finally { track.getIoLock().unlock(); } } @Override public boolean deleteTrack(Track track) { track.getIoLock().lock(); try { try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(TRACK_DELETE)) { ps.setString(1, track.getName()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } } finally { track.getIoLock().unlock(); } plugin.getTrackManager().unload(track); return true; } @Override public boolean saveUUIDData(String username, UUID uuid) { final String u = username.toLowerCase(); AtomicReference<String> remoteUserName = new AtomicReference<>(null); // cleanup any old values try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(PLAYER_DELETE)) { ps.setString(1, u); ps.setString(2, uuid.toString()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); } try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(PLAYER_SELECT_USERNAME)) { ps.setString(1, uuid.toString()); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { remoteUserName.set(rs.getString("username")); } } } } catch (SQLException e) { e.printStackTrace(); return false; } if (remoteUserName.get() != null) { // the value is already correct if (remoteUserName.get().equals(u)) { return true; } try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(PLAYER_UPDATE)) { ps.setString(1, u); ps.setString(2, uuid.toString()); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } else { // first time we've seen this uuid try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(PLAYER_INSERT)) { ps.setString(1, uuid.toString()); ps.setString(2, u); ps.setString(3, "default"); ps.execute(); } } catch (SQLException e) { e.printStackTrace(); return false; } return true; } } @Override public UUID getUUID(String username) { final String u = username.toLowerCase(); final AtomicReference<UUID> uuid = new AtomicReference<>(null); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(PLAYER_SELECT_UUID)) { ps.setString(1, u); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { uuid.set(UUID.fromString(rs.getString("uuid"))); } } } } catch (SQLException e) { e.printStackTrace(); return null; } return uuid.get(); } @Override public String getName(UUID uuid) { final AtomicReference<String> name = new AtomicReference<>(null); try (Connection c = provider.getConnection()) { try (PreparedStatement ps = c.prepareStatement(PLAYER_SELECT_USERNAME)) { ps.setString(1, uuid.toString()); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { name.set(rs.getString("username")); } } } } catch (SQLException e) { e.printStackTrace(); return null; } return name.get(); } /** * Compares two sets * @param local the local set * @param remote the remote set * @return the entries to add to remote, and the entries to remove from remote */ private static Map.Entry<Set<NodeDataHolder>, Set<NodeDataHolder>> compareSets(Set<NodeDataHolder> local, Set<NodeDataHolder> remote) { // entries in local but not remote need to be added // entries in remote but not local need to be removed Set<NodeDataHolder> toAdd = new HashSet<>(local); toAdd.removeAll(remote); Set<NodeDataHolder> toRemove = new HashSet<>(remote); toRemove.removeAll(local); return Maps.immutableEntry(toAdd, toRemove); } }