From 90c5f2a6f8f9a12111cbf3374a08d8e77ee978b5 Mon Sep 17 00:00:00 2001 From: Paul Reilly Date: Fri, 12 May 2023 23:30:08 -0500 Subject: [PATCH] Datura - Data Manager --- .../me/totalfreedom/base/CommonsBase.java | 5 + .../me/totalfreedom/base/Registration.java | 12 +- .../java/me/totalfreedom/security/Node.java | 12 +- .../main/java/me/totalfreedom/sql/SQL.java | 7 +- .../me/totalfreedom/utils/FreedomLogger.java | 131 ++++++++++ Datura/.gitignore | 42 +++ Datura/build.gradle | 21 ++ .../java/me/totalfreedom/datura/Datura.java | 16 ++ .../datura/perms/DefaultNodes.java | 43 ++++ .../datura/perms/FreedomGroup.java | 240 ++++++++++++++++++ .../datura/perms/FreedomUser.java | 206 +++++++++++++++ .../datura/perms/PermissionNode.java | 53 ++++ .../datura/perms/PermissionNodeBuilder.java | 63 +++++ .../me/totalfreedom/datura/sql/MySQL.java | 121 +++++++++ settings.gradle | 1 + 15 files changed, 961 insertions(+), 12 deletions(-) create mode 100644 Commons/src/main/java/me/totalfreedom/utils/FreedomLogger.java create mode 100644 Datura/.gitignore create mode 100644 Datura/build.gradle create mode 100644 Datura/src/main/java/me/totalfreedom/datura/Datura.java create mode 100644 Datura/src/main/java/me/totalfreedom/datura/perms/DefaultNodes.java create mode 100644 Datura/src/main/java/me/totalfreedom/datura/perms/FreedomGroup.java create mode 100644 Datura/src/main/java/me/totalfreedom/datura/perms/FreedomUser.java create mode 100644 Datura/src/main/java/me/totalfreedom/datura/perms/PermissionNode.java create mode 100644 Datura/src/main/java/me/totalfreedom/datura/perms/PermissionNodeBuilder.java create mode 100644 Datura/src/main/java/me/totalfreedom/datura/sql/MySQL.java diff --git a/Commons/src/main/java/me/totalfreedom/base/CommonsBase.java b/Commons/src/main/java/me/totalfreedom/base/CommonsBase.java index fb9d489..22f449b 100644 --- a/Commons/src/main/java/me/totalfreedom/base/CommonsBase.java +++ b/Commons/src/main/java/me/totalfreedom/base/CommonsBase.java @@ -9,6 +9,7 @@ import org.bukkit.plugin.java.JavaPlugin; public class CommonsBase extends JavaPlugin { private final EventBus eventBus = new EventBus(this); + private final Registration registration = new Registration(); public static CommonsBase getInstance() { @@ -34,4 +35,8 @@ public class CommonsBase extends JavaPlugin { return Bukkit.getServicesManager().getRegistration(EventBus.class); } + + public Registration getRegistrations() { + return registration; + } } diff --git a/Commons/src/main/java/me/totalfreedom/base/Registration.java b/Commons/src/main/java/me/totalfreedom/base/Registration.java index 65195fa..73777aa 100644 --- a/Commons/src/main/java/me/totalfreedom/base/Registration.java +++ b/Commons/src/main/java/me/totalfreedom/base/Registration.java @@ -1,9 +1,6 @@ package me.totalfreedom.base; -import me.totalfreedom.data.CommandRegistry; -import me.totalfreedom.data.EventRegistry; -import me.totalfreedom.data.ServiceRegistry; -import me.totalfreedom.data.UserRegistry; +import me.totalfreedom.data.*; public class Registration { @@ -11,6 +8,7 @@ public class Registration private final EventRegistry eventRegistry; private final UserRegistry userRegistry; private final ServiceRegistry serviceRegistry; + private final ModuleRegistry moduleRegistry; public Registration() { @@ -18,6 +16,12 @@ public class Registration this.eventRegistry = new EventRegistry(); this.userRegistry = new UserRegistry(); this.serviceRegistry = new ServiceRegistry(); + this.moduleRegistry = new ModuleRegistry(); + } + + public ModuleRegistry getModuleRegistry() + { + return moduleRegistry; } public CommandRegistry getCommandRegistry() diff --git a/Commons/src/main/java/me/totalfreedom/security/Node.java b/Commons/src/main/java/me/totalfreedom/security/Node.java index 0519b39..af7a014 100644 --- a/Commons/src/main/java/me/totalfreedom/security/Node.java +++ b/Commons/src/main/java/me/totalfreedom/security/Node.java @@ -7,17 +7,17 @@ import javax.annotation.concurrent.Immutable; @Immutable public interface Node { - String getKey(); + String key(); - boolean getValue(); + boolean value(); Permission bukkit(); - NodeType getType(); + NodeType type(); boolean compare(Node node); - long getExpiry(); + long expiry(); boolean isExpired(); @@ -25,7 +25,7 @@ public interface Node boolean isTemporary(); - boolean isWildcard(); + boolean wildcard(); - boolean isNegated(); + boolean negated(); } diff --git a/Commons/src/main/java/me/totalfreedom/sql/SQL.java b/Commons/src/main/java/me/totalfreedom/sql/SQL.java index ac0858c..db0f176 100644 --- a/Commons/src/main/java/me/totalfreedom/sql/SQL.java +++ b/Commons/src/main/java/me/totalfreedom/sql/SQL.java @@ -1,6 +1,7 @@ package me.totalfreedom.sql; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.concurrent.CompletableFuture; @@ -8,11 +9,13 @@ public interface SQL { CompletableFuture getConnection(String url); + CompletableFuture prepareStatement(String query, Object... args); + CompletableFuture executeQuery(String query, Object... args); - CompletableFuture executeUpdate(String query, Object... args); + CompletableFuture executeUpdate(String query, Object... args); - CompletableFuture execute(String query, Object... args); + CompletableFuture execute(String query, Object... args); CompletableFuture createTable(String table, String... columns); } diff --git a/Commons/src/main/java/me/totalfreedom/utils/FreedomLogger.java b/Commons/src/main/java/me/totalfreedom/utils/FreedomLogger.java new file mode 100644 index 0000000..c0deaa1 --- /dev/null +++ b/Commons/src/main/java/me/totalfreedom/utils/FreedomLogger.java @@ -0,0 +1,131 @@ +package me.totalfreedom.utils; + +import net.kyori.adventure.text.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.function.Supplier; + +public class FreedomLogger +{ + private final Logger logger; + private boolean debug = false; + + private FreedomLogger(String moduleName) + { + this.logger = LoggerFactory.getLogger("FreedomNetworkSuite::" + moduleName); + } + + public static FreedomLogger getLogger(String moduleName) + { + return new FreedomLogger(moduleName); + } + + public void setDebugMode(boolean debug) + { + this.debug = debug; + } + + /** + * This method allows you to log a message to the console. + * + * @param message The message to send. + */ + public void info(String message) + { + logger.info(message); + } + + /** + * This method allows you to log a message to the console, + * while also returning a Component that could be used to + * message a player. + * + * @param message The message to send. + * @return A component representation of the message. + */ + public Component info(Supplier message) + { + logger.info(message.get()); + return Component.text(message.get()); + } + + /** + * This method allows you to log a warning to the console. + * + * @param message The message to send. + */ + public void warn(String message) + { + logger.warn(message); + } + + /** + * This method logs an error message to the console. + * It is highly recommended to deconstruct the stack trace and pass it + * in a more readable format to this method. + * + * @param message The message to send. + */ + public void error(String message) + { + logger.error(message); + } + + /** + * This method allows you to log an exception directly to the console. + * + * @param th The exception to log. + */ + public void error(Throwable th) + { + logger.error("An error occurred:\n", th); + } + + /** + * This method allows you to log an error message to the console, + * while also returning a Component that could be used to + * message a player. It is highly recommended that you deconstruct and limit + * the stack trace before passing it to this method, if you are intending to + * use it for player communication. + * + * @param message The message to send. + * @return A component representation of the message. + */ + public Component error(Supplier message) + { + logger.error(message.get()); + return Component.text(message.get()); + } + + /** + * This method allows you to log a debug message to the console. + * This method will only log if debug mode is enabled. + * + * @param message The message to send. + */ + public void debug(String message) + { + if (debug) + logger.debug(message); + } + + /** + * This method allows you to log a debug message to the console, + * while also returning a Component that could be used to + * message a player. This method will only log if debug mode is enabled. + * If debug mode is not enabled, this method will return an empty component. + * + * @param message The message to send. + * @return A component representation of the message. + */ + public Component debug(Supplier message) + { + if (debug) + { + logger.debug(message.get()); + return Component.text(message.get()); + } + return Component.empty(); + } +} diff --git a/Datura/.gitignore b/Datura/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/Datura/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/Datura/build.gradle b/Datura/build.gradle new file mode 100644 index 0000000..da7c50b --- /dev/null +++ b/Datura/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' +} + +group = 'me.totalfreedom' +version = '1.0.0' + +repositories { + mavenCentral() +} + +dependencies { + compileOnly project(":Commons") + + testImplementation platform('org.junit:junit-bom:5.9.1') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/Datura/src/main/java/me/totalfreedom/datura/Datura.java b/Datura/src/main/java/me/totalfreedom/datura/Datura.java new file mode 100644 index 0000000..bd5c8b0 --- /dev/null +++ b/Datura/src/main/java/me/totalfreedom/datura/Datura.java @@ -0,0 +1,16 @@ +package me.totalfreedom.datura; + +import me.totalfreedom.base.CommonsBase; +import org.bukkit.plugin.java.JavaPlugin; + +public class Datura extends JavaPlugin +{ + @Override + public void onEnable() + { + CommonsBase.getInstance() + .getRegistrations() + .getModuleRegistry() + .addModule(this); + } +} diff --git a/Datura/src/main/java/me/totalfreedom/datura/perms/DefaultNodes.java b/Datura/src/main/java/me/totalfreedom/datura/perms/DefaultNodes.java new file mode 100644 index 0000000..af259bf --- /dev/null +++ b/Datura/src/main/java/me/totalfreedom/datura/perms/DefaultNodes.java @@ -0,0 +1,43 @@ +package me.totalfreedom.datura.perms; + +import me.totalfreedom.security.Node; +import me.totalfreedom.security.NodeType; + +public class DefaultNodes +{ + private DefaultNodes() { + throw new AssertionError(); + } + + public static final Node OP = new PermissionNodeBuilder() + .key("freedom.master_key") + .value(true) + .type(NodeType.PERMISSION) + .negated(false) + .wildcard(true) + .build(); + + public static final Node NON_OP = new PermissionNodeBuilder() + .key("freedom.default") + .value(true) + .type(NodeType.PERMISSION) + .negated(false) + .wildcard(false) + .build(); + + public static final Node ALL = new PermissionNodeBuilder() + .key("*") + .value(true) + .type(NodeType.PERMISSION) + .negated(false) + .wildcard(true) + .build(); + + public static final Node NONE = new PermissionNodeBuilder() + .key("freedom.none") + .value(true) + .type(NodeType.PERMISSION) + .negated(false) + .wildcard(false) + .build(); +} diff --git a/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomGroup.java b/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomGroup.java new file mode 100644 index 0000000..9c6b98b --- /dev/null +++ b/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomGroup.java @@ -0,0 +1,240 @@ +package me.totalfreedom.datura.perms; + +import me.totalfreedom.base.CommonsBase; +import me.totalfreedom.security.Group; +import me.totalfreedom.security.Node; +import net.kyori.adventure.text.Component; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public class FreedomGroup implements Group +{ + private final Component name; + private final Component prefix; + private final Component abbreviation; + private final int weight; + private final boolean isDefault; + private final boolean isHidden; + private final Set permissions; + private final PermissionAttachment attachment; + + public FreedomGroup(Component name, + Component prefix, + Component abbreviation, + int weight, + boolean isDefault, + boolean isHidden) + { + this.name = name; + this.prefix = prefix; + this.abbreviation = abbreviation; + this.weight = weight; + this.isDefault = isDefault; + this.isHidden = isHidden; + this.permissions = new HashSet<>(); + this.attachment = new PermissionAttachment(CommonsBase.getInstance(), this); + } + + + @Override + public Component getName() + { + return name; + } + + @Override + public Component getPrefix() + { + return prefix; + } + + @Override + public Component getAbbreviation() + { + return abbreviation; + } + + @Override + public int getWeight() + { + return weight; + } + + @Override + public boolean isDefault() + { + return isDefault; + } + + @Override + public boolean isHidden() + { + return isHidden; + } + + @Override + public UUID getUniqueId() + { + return UUID.nameUUIDFromBytes(getName().toString().getBytes()); + } + + @Override + public Set permissions() + { + return permissions; + } + + @Override + public boolean addPermission(Node node) + { + return permissions().add(node); + } + + @Override + public boolean removePermission(Node node) + { + return permissions().remove(node); + } + + @Override + public boolean isPermissionSet(@NotNull String name) + { + Node node = permissions().stream() + .filter(n -> n.key().equalsIgnoreCase(name)) + .findFirst() + .orElse(null); + + return node != null && node.value(); + } + + @Override + public boolean isPermissionSet(@NotNull Permission perm) + { + Node node = permissions() + .stream() + .filter(n -> n.bukkit().equals(perm)) + .findFirst() + .orElse(null); + + return node != null && node.value(); + } + + @Override + public boolean hasPermission(@NotNull String name) + { + Node node = permissions().stream() + .filter(n -> n.key().equalsIgnoreCase(name)) + .findFirst() + .orElse(null); + + return node != null && node.value(); + } + + @Override + public boolean hasPermission(@NotNull Permission perm) + { + Node node = permissions() + .stream() + .filter(n -> n.bukkit().equals(perm)) + .findFirst() + .orElse(null); + + return node != null && node.value(); + } + + /** + * Adds a permission to the relative PermissionAttachment for this group. + * This method is not thread-safe and should not be called asynchronously. + *

+ * This method is only here for compatibility with the Bukkit API. + * + * @param plugin The plugin responsible for this attachment. May not be null + * or disabled. + * @param name Name of the permission to attach + * @param value Value of the permission + * @return This group's PermissionAttachment. + */ + @Override + public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) + { + attachment.setPermission(name, value); + return attachment; + } + + @Override + public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin) + { + return new PermissionAttachment(plugin, this); + } + + @Override + public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) + { + attachment.setPermission(name, value); + return attachment; + } + + @Override + public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) + { + return new PermissionAttachment(plugin, this); + } + + @Override + public void removeAttachment(@NotNull PermissionAttachment attachment) + { + // This method shouldn't do anything, because we don't want to remove our attachment. + } + + @Override + public void recalculatePermissions() + { + // Not sure what this method should do, so I'm leaving it empty. + } + + @Override + public @NotNull Set getEffectivePermissions() + { + return permissions() + .stream() + .map(n -> new PermissionAttachmentInfo( + this, + n.key(), + attachment, + n.value())) + .collect(Collectors.toSet()); + } + + @Override + public boolean isOp() + { + Node node = permissions() + .stream() + .filter(n -> n.equals(DefaultNodes.OP)) + .findFirst() + .orElse(null); + + return node != null && node.value(); + } + + @Override + public void setOp(boolean value) + { + if (value) + { + permissions().add(DefaultNodes.OP); + } else + { + permissions().remove(DefaultNodes.OP); + } + } +} diff --git a/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomUser.java b/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomUser.java new file mode 100644 index 0000000..6de1239 --- /dev/null +++ b/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomUser.java @@ -0,0 +1,206 @@ +package me.totalfreedom.datura.perms; + +import me.totalfreedom.base.CommonsBase; +import me.totalfreedom.security.Node; +import me.totalfreedom.user.User; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * The superinterface User extends PermissionHolder, + * which is an extension of {@link org.bukkit.permissions.Permissible}. + * This means that our permission data can be interchanged with other permission plugins. + */ +public class FreedomUser implements User +{ + private final UUID uuid; + private final Set permissions; + private final Map bukkitAttachments = new HashMap<>(); + private final Component displayName; + private final String NOT_ONLINE = "Player is not online"; + + public FreedomUser(Player player) + { + this.uuid = player.getUniqueId(); + this.permissions = new HashSet<>(); + this.displayName = player.displayName(); + + } + + @Override + public UUID getUniqueId() + { + return uuid; + } + + @Override + public Set permissions() + { + return permissions; + } + + @Override + public boolean addPermission(Node node) + { + PermissionAttachment attachment = addAttachment(CommonsBase.getInstance(), node.key(), node.value()); + bukkitAttachments.put(node, attachment); + return permissions().add(node); + } + + @Override + public boolean removePermission(Node node) + { + removeAttachment(bukkitAttachments.get(node)); + bukkitAttachments.remove(node); + return permissions.remove(node); + } + + @Override + public Component getDisplayName() + { + return displayName; + } + + @Override + public boolean isOnline() + { + return Bukkit.getPlayer(uuid) != null; + } + + @Override + public boolean isPermissionSet(@NotNull String name) + { + Player player = Bukkit.getPlayer(uuid); + return player != null && player.isPermissionSet(name); + } + + @Override + public boolean isPermissionSet(@NotNull Permission perm) + { + Player player = Bukkit.getPlayer(uuid); + return player != null && player.isPermissionSet(perm); + } + + @Override + public boolean hasPermission(@NotNull String name) + { + Player player = Bukkit.getPlayer(uuid); + return player != null && player.hasPermission(name); + } + + @Override + public boolean hasPermission(@NotNull Permission perm) + { + Player player = Bukkit.getPlayer(uuid); + return player != null && player.hasPermission(perm); + } + + @Override + public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) + { + Player player = Bukkit.getPlayer(uuid); + if (player != null) + { + return player.addAttachment(plugin, name, value); + } + + throw new IllegalStateException(NOT_ONLINE); + } + + @Override + public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin) + { + Player player = Bukkit.getPlayer(uuid); + if (player != null) + { + return player.addAttachment(plugin); + } + + throw new IllegalStateException(NOT_ONLINE); + } + + @Override + public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) + { + Player player = Bukkit.getPlayer(uuid); + if (player != null) + { + return player.addAttachment(plugin, name, value, ticks); + } + + throw new IllegalStateException(NOT_ONLINE); + } + + @Override + public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) + { + Player player = Bukkit.getPlayer(uuid); + if (player != null) + { + return player.addAttachment(plugin, ticks); + } + + throw new IllegalStateException(NOT_ONLINE); + } + + @Override + public void removeAttachment(@NotNull PermissionAttachment attachment) + { + Player player = Bukkit.getPlayer(uuid); + if (player != null) + { + player.removeAttachment(attachment); + } + + throw new IllegalStateException(NOT_ONLINE); + } + + @Override + public void recalculatePermissions() + { + Player player = Bukkit.getPlayer(uuid); + if (player != null) + { + player.recalculatePermissions(); + } + + throw new IllegalStateException(NOT_ONLINE); + } + + @Override + public @NotNull Set getEffectivePermissions() + { + Player player = Bukkit.getPlayer(uuid); + if (player != null) + { + return player.getEffectivePermissions(); + } + + throw new IllegalStateException(NOT_ONLINE); + } + + @Override + public boolean isOp() + { + return permissions().contains(DefaultNodes.OP); + } + + @Override + public void setOp(boolean value) + { + if (value) { + permissions().add(DefaultNodes.OP); + } else { + permissions().remove(DefaultNodes.OP); + } + } +} diff --git a/Datura/src/main/java/me/totalfreedom/datura/perms/PermissionNode.java b/Datura/src/main/java/me/totalfreedom/datura/perms/PermissionNode.java new file mode 100644 index 0000000..0e97d98 --- /dev/null +++ b/Datura/src/main/java/me/totalfreedom/datura/perms/PermissionNode.java @@ -0,0 +1,53 @@ +package me.totalfreedom.datura.perms; + +import me.totalfreedom.security.Node; +import me.totalfreedom.security.NodeType; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; + +record PermissionNode(String key, + boolean value, + long expiry, + NodeType type, + boolean wildcard, + boolean negated) implements Node +{ + + @Override + public Permission bukkit() + { + return new Permission(key(), + value() ? PermissionDefault.TRUE : PermissionDefault.FALSE); + } + + @Override + public boolean compare(Node node) + { + return node.key().equalsIgnoreCase(key()) + && node.value() == value() + && node.type() == type(); + } + + @Override + public boolean isExpired() + { + if (isPermanent()) + { + return false; + } + + return System.currentTimeMillis() > expiry(); + } + + @Override + public boolean isPermanent() + { + return expiry() == -1; + } + + @Override + public boolean isTemporary() + { + return !isPermanent(); + } +} diff --git a/Datura/src/main/java/me/totalfreedom/datura/perms/PermissionNodeBuilder.java b/Datura/src/main/java/me/totalfreedom/datura/perms/PermissionNodeBuilder.java new file mode 100644 index 0000000..75dcc77 --- /dev/null +++ b/Datura/src/main/java/me/totalfreedom/datura/perms/PermissionNodeBuilder.java @@ -0,0 +1,63 @@ +package me.totalfreedom.datura.perms; + +import me.totalfreedom.security.Node; +import me.totalfreedom.security.NodeBuilder; +import me.totalfreedom.security.NodeType; + +public class PermissionNodeBuilder implements NodeBuilder +{ + private String key = "freedom.default"; + private boolean value = true; + private long expiry = -1; + private NodeType type = NodeType.PERMISSION; + private boolean wildcard = false; + private boolean negated = false; + + @Override + public NodeBuilder key(String key) + { + this.key = key; + return this; + } + + @Override + public NodeBuilder value(boolean value) + { + this.value = value; + return this; + } + + @Override + public NodeBuilder expiry(long expiry) + { + this.expiry = expiry; + return this; + } + + @Override + public NodeBuilder type(NodeType type) + { + this.type = type; + return this; + } + + @Override + public NodeBuilder wildcard(boolean wildcard) + { + this.wildcard = wildcard; + return this; + } + + @Override + public NodeBuilder negated(boolean negated) + { + this.negated = negated; + return this; + } + + @Override + public Node build() + { + return new PermissionNode(key, value, expiry, type, wildcard, negated); + } +} diff --git a/Datura/src/main/java/me/totalfreedom/datura/sql/MySQL.java b/Datura/src/main/java/me/totalfreedom/datura/sql/MySQL.java new file mode 100644 index 0000000..5adbe09 --- /dev/null +++ b/Datura/src/main/java/me/totalfreedom/datura/sql/MySQL.java @@ -0,0 +1,121 @@ +package me.totalfreedom.datura.sql; + +import me.totalfreedom.sql.SQL; + +import java.sql.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +public class MySQL implements SQL +{ + private String url = "jdbc:mysql://"; + + public MySQL(String host, int port, String database) { + url += host + ":" + port + "/" + database; + } + + /** + * Adds credentials to the MySQL URL. + * If the URL already contains credentials, they will be overwritten. + * + * @param username The username to add + * @param password The password to add + */ + public void addCredentials(String username, String password) { + if (url.contains("?user=")) { + url = url.split("\\x3f")[0]; + } + + url += "?user=" + username + "&password=" + password; + } + + @Override + public CompletableFuture getConnection(String url) + { + return CompletableFuture.supplyAsync(() -> { + try { + return DriverManager.getConnection(url); + } catch (SQLException ex) { + throw new CompletionException("Failed to connect to the database: " + + url + "\n", ex); + } + }); + } + + @Override + public CompletableFuture prepareStatement(String query, Object... args) + { + return getConnection(url) + .thenApplyAsync(connection -> { + try { + PreparedStatement statement = connection.prepareStatement(query); + for (int i = 0; i < args.length; i++) { + statement.setObject(i + 1, args[i]); + } + return statement; + } catch (SQLException ex) { + throw new CompletionException("Failed to prepare statement: " + + query + "\n", ex); + } + }); + } + + @Override + public CompletableFuture executeQuery(String query, Object... args) + { + return prepareStatement(query, args) + .thenApplyAsync(statement -> { + try { + return statement.executeQuery(); + } catch (SQLException ex) { + throw new CompletionException("Failed to retrieve a result set from query: " + + query + "\n", ex); + } + }); + } + + @Override + public CompletableFuture executeUpdate(String query, Object... args) + { + return prepareStatement(query, args) + .thenApplyAsync(statement -> { + try { + return statement.executeUpdate(); + } catch (SQLException ex) { + throw new CompletionException("Failed to execute update: " + + query + "\n", ex); + } + }); + } + + @Override + public CompletableFuture execute(String query, Object... args) + { + return prepareStatement(query, args) + .thenApplyAsync(statement -> { + try { + return statement.execute(); + } catch (SQLException ex) { + throw new CompletionException("Failed to execute statement: " + + query + "\n", ex); + } + }); + } + + @Override + public CompletableFuture createTable(String table, String... columns) + { + StringBuilder query = new StringBuilder(); + + query.append("CREATE TABLE IF NOT EXISTS ? ("); + for (int i = 0; i < columns.length; i++) { + query.append("?"); + if (i != columns.length - 1) { + query.append(", "); + } + } + query.append(")"); + + return execute(query.toString(), table, columns); + } +} diff --git a/settings.gradle b/settings.gradle index 98eb1f5..046d807 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ rootProject.name = 'FreedomNetworkSuite' include 'Commons' +include 'Datura'