diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..30ab597
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+### IDEA ###
+.idea
+*.iml
+
+### MAVEN ###
+target/
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..8a994ba
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,88 @@
+
+
+ 4.0.0
+
+ de.garrus.cloudnet
+ cloudnet-postgres-database-provider
+ 1.0
+
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+
+
+ releases
+ https://repo.cloudnetservice.eu/repository/releases/
+
+
+
+
+ snapshots
+ https://repo.cloudnetservice.eu/repository/snapshots/
+
+
+
+
+
+ garrus-repository
+ ftp://repo-upload.garrus.de/repo/releases
+
+
+ garrus-repository
+ ftp://repo-upload.garrus.de/repo/shapshot
+
+
+
+
+
+
+ org.apache.maven.wagon
+ wagon-ftp
+ 3.4.1
+
+
+
+
+
+
+
+ de.dytanic.cloudnet
+ cloudnet
+ 3.3.0-RELEASE
+ provided
+
+
+
+
+ de.dytanic.cloudnet
+ cloudnet-driver
+ 3.3.0-RELEASE
+ provided
+
+
+
+
+ org.postgresql
+ postgresql
+ 42.2.12
+
+
+ com.zaxxer
+ HikariCP
+ 3.4.2
+
+
+ org.javassist
+ javassist
+ 3.27.0-GA
+
+
+
+
diff --git a/src/main/java/de/garrus/cloudnet/database/postgres/provider/CloudNetPostgresDatabaseModule.java b/src/main/java/de/garrus/cloudnet/database/postgres/provider/CloudNetPostgresDatabaseModule.java
new file mode 100644
index 0000000..0ac9a29
--- /dev/null
+++ b/src/main/java/de/garrus/cloudnet/database/postgres/provider/CloudNetPostgresDatabaseModule.java
@@ -0,0 +1,40 @@
+package de.garrus.cloudnet.database.postgres.provider;
+
+import de.dytanic.cloudnet.database.AbstractDatabaseProvider;
+import de.dytanic.cloudnet.driver.module.ModuleLifeCycle;
+import de.dytanic.cloudnet.driver.module.ModuleTask;
+import de.dytanic.cloudnet.module.NodeCloudNetModule;
+
+public class CloudNetPostgresDatabaseModule extends NodeCloudNetModule {
+ private static CloudNetPostgresDatabaseModule instance;
+
+ public static CloudNetPostgresDatabaseModule getInstance() {
+ return instance;
+ }
+
+ @ModuleTask(order = 127, event = ModuleLifeCycle.LOADED)
+ public void init() {
+ instance = this;
+ }
+
+ @ModuleTask(order = 126, event = ModuleLifeCycle.LOADED)
+ public void initConfig() {
+ this.getConfig().getString("addresse", "jdbc:postgresql://127.0.0.1:5432/database");
+ this.getConfig().getString("username", "root");
+ this.getConfig().getString("password", "root");
+ this.getConfig().getInt("connectionPoolSize", 15);
+ this.getConfig().getInt("connectionTimeout", 5000);
+ this.getConfig().getInt("validationTimeout", 5000);
+ this.saveConfig();
+ }
+
+ @ModuleTask(order = 125, event = ModuleLifeCycle.LOADED)
+ public void registerDatabaseProvider() {
+ this.getRegistry().registerService(AbstractDatabaseProvider.class, "postgres", new PostgresSQLDatabaseProvider(getConfig(),null));
+ }
+
+ @ModuleTask(order = 127, event = ModuleLifeCycle.STOPPED)
+ public void unregisterDatabaseProvider() {
+ this.getRegistry().unregisterService(AbstractDatabaseProvider.class, "postgres");
+ }
+}
diff --git a/src/main/java/de/garrus/cloudnet/database/postgres/provider/PostgresDatabase.java b/src/main/java/de/garrus/cloudnet/database/postgres/provider/PostgresDatabase.java
new file mode 100644
index 0000000..13476b5
--- /dev/null
+++ b/src/main/java/de/garrus/cloudnet/database/postgres/provider/PostgresDatabase.java
@@ -0,0 +1,12 @@
+package de.garrus.cloudnet.database.postgres.provider;
+
+import de.dytanic.cloudnet.database.sql.SQLDatabase;
+import de.dytanic.cloudnet.database.sql.SQLDatabaseProvider;
+
+import java.util.concurrent.ExecutorService;
+
+public class PostgresDatabase extends SQLDatabase {
+ public PostgresDatabase(SQLDatabaseProvider databaseProvider, String name, ExecutorService executorService) {
+ super(databaseProvider, name, executorService);
+ }
+}
diff --git a/src/main/java/de/garrus/cloudnet/database/postgres/provider/PostgresSQLDatabaseProvider.java b/src/main/java/de/garrus/cloudnet/database/postgres/provider/PostgresSQLDatabaseProvider.java
new file mode 100644
index 0000000..f7e04a3
--- /dev/null
+++ b/src/main/java/de/garrus/cloudnet/database/postgres/provider/PostgresSQLDatabaseProvider.java
@@ -0,0 +1,221 @@
+package de.garrus.cloudnet.database.postgres.provider;
+
+import com.google.common.base.Preconditions;
+import com.zaxxer.hikari.HikariDataSource;
+import de.dytanic.cloudnet.common.collection.NetorHashMap;
+import de.dytanic.cloudnet.common.collection.Pair;
+import de.dytanic.cloudnet.common.concurrent.IThrowableCallback;
+import de.dytanic.cloudnet.common.document.gson.JsonDocument;
+import de.dytanic.cloudnet.database.IDatabase;
+import de.dytanic.cloudnet.database.sql.SQLDatabaseProvider;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+
+public class PostgresSQLDatabaseProvider extends SQLDatabaseProvider {
+ private static final long NEW_CREATION_DELAY = 600000L;
+ protected final NetorHashMap cachedDatabaseInstances = new NetorHashMap<>();
+ protected final HikariDataSource hikariDataSource = new HikariDataSource();
+ private final JsonDocument config;
+ private String address;
+
+ public PostgresSQLDatabaseProvider(JsonDocument config,ExecutorService executorService) {
+ super(executorService);
+ this.config = config;
+ }
+
+ public boolean init() {
+ this.address = this.config.getString("addresse");
+ this.hikariDataSource.setJdbcUrl(address);
+ this.hikariDataSource.setUsername(this.config.getString("username"));
+ this.hikariDataSource.setPassword(this.config.getString("password"));
+ this.hikariDataSource.setDriverClassName("org.postgresql.Driver");
+ this.hikariDataSource.setMaximumPoolSize(this.config.getInt("connectionPoolSize"));
+ this.hikariDataSource.setConnectionTimeout(this.config.getInt("connectionTimeout"));
+ this.hikariDataSource.setValidationTimeout(this.config.getInt("validationTimeout"));
+ this.hikariDataSource.validate();
+ return true;
+ }
+
+ public IDatabase getDatabase(String name) {
+ Preconditions.checkNotNull(name);
+ this.removedOutdatedEntries();
+ if (!this.cachedDatabaseInstances.contains(name)) {
+ this.cachedDatabaseInstances.add(name, System.currentTimeMillis() + NEW_CREATION_DELAY, new PostgresDatabase(this, name,super.executorService) {
+ });
+ }
+
+ return this.cachedDatabaseInstances.getSecond(name);
+ }
+
+ public boolean containsDatabase(String name) {
+ Preconditions.checkNotNull(name);
+ this.removedOutdatedEntries();
+
+ return getDatabaseNames().contains(name);
+ }
+
+ public boolean deleteDatabase(String name) {
+ Preconditions.checkNotNull(name);
+ this.cachedDatabaseInstances.remove(name);
+ if (this.containsDatabase(name)) {
+ return false;
+ }
+
+ try {
+ Connection connection = getConnection();
+ PreparedStatement preparedStatement = connection.prepareStatement("DROP TABLE " + name);
+ boolean deleted = preparedStatement.executeUpdate() != -1;
+
+ if (!preparedStatement.isClosed()) {
+ preparedStatement.close();
+ }
+
+ if (!connection.isClosed()) {
+ connection.close();
+ }
+
+ return deleted;
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ }
+
+ return false;
+ }
+
+ public List getDatabaseNames() {
+ return this.executeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='PUBLIC'", resultSet -> {
+ List collection = new ArrayList<>();
+
+ while (resultSet.next()) {
+ collection.add(resultSet.getString("table_name"));
+ }
+
+ return collection;
+ });
+ }
+
+ public String getName() {
+ return this.config.getString("database");
+ }
+
+ public void close() {
+ this.hikariDataSource.close();
+ }
+
+ private void removedOutdatedEntries() {
+ Iterator>> var1 = this.cachedDatabaseInstances.entrySet().iterator();
+
+ while (var1.hasNext()) {
+ Map.Entry> entry = var1.next();
+ if ((Long) ((Pair) entry.getValue()).getFirst() < System.currentTimeMillis()) {
+ this.cachedDatabaseInstances.remove(entry.getKey());
+ }
+ }
+
+ }
+
+ public Connection getConnection() throws SQLException {
+ return this.hikariDataSource.getConnection();
+ }
+
+ public NetorHashMap getCachedDatabaseInstances() {
+ return this.cachedDatabaseInstances;
+ }
+
+ public HikariDataSource getHikariDataSource() {
+ return this.hikariDataSource;
+ }
+
+ public JsonDocument getConfig() {
+ return this.config;
+ }
+
+ public String getAddress() {
+ return this.address;
+ }
+
+ public int executeUpdate(String query, Object... objects) {
+ Preconditions.checkNotNull(query);
+ Preconditions.checkNotNull(objects);
+
+ try {
+ Connection connection = getConnection();
+ PreparedStatement preparedStatement = connection.prepareStatement(query);
+
+ for (int i = 0; i < objects.length; i++) {
+ preparedStatement.setString(i + 1, objects[i].toString());
+ }
+ int i = preparedStatement.executeUpdate();
+
+ if (!preparedStatement.isClosed()) {
+ preparedStatement.close();
+ }
+ if (!connection.isClosed()) {
+ connection.close();
+ }
+
+ return i;
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ return -1;
+ }
+
+ }
+
+ public T executeQuery(String query, IThrowableCallback callback, Object... objects) {
+ Preconditions.checkNotNull(query);
+ Preconditions.checkNotNull(callback);
+ Preconditions.checkNotNull(objects);
+
+ try {
+ Connection connection = getConnection();
+ PreparedStatement preparedStatement = connection.prepareStatement(query);
+
+ for (int i = 0; i < objects.length; i++) {
+ preparedStatement.setString(i + 1, objects[i].toString());
+ }
+
+ ResultSet resultSet = preparedStatement.executeQuery();
+
+ try {
+ T call = callback.call(resultSet);
+
+ if (!resultSet.isClosed()) {
+ resultSet.close();
+ }
+ if (!preparedStatement.isClosed()) {
+ preparedStatement.close();
+ }
+ if (!connection.isClosed()) {
+ connection.close();
+ }
+ return call;
+
+ } catch (Throwable throwable) {
+ if (!resultSet.isClosed()) {
+ resultSet.close();
+ }
+ if (!preparedStatement.isClosed()) {
+ preparedStatement.close();
+ }
+ if (!connection.isClosed()) {
+ connection.close();
+ }
+ throwable.printStackTrace();
+ return null;
+ }
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ return null;
+ }
+
+ }
+}
diff --git a/src/main/resources/module.json b/src/main/resources/module.json
new file mode 100644
index 0000000..dd85489
--- /dev/null
+++ b/src/main/resources/module.json
@@ -0,0 +1,43 @@
+{
+ "runtimeModule": true,
+ "group": "de.garrus.cloudnet",
+ "name": "Cloudnet-Postgres-Database-Provider",
+ "version": "1.0",
+ "author": "garrus.de",
+ "website": "https://garrus.de",
+ "description": "CloudNet extension, which includes the database support for PostgreSQL database to store",
+ "main": "de.garrus.cloudnet.database.postgres.provider.CloudNetPostgresDatabaseModule",
+ "storesSensitiveData": true,
+ "dependencies": [
+ {
+ "group": "org.postgresql",
+ "name": "postgresql",
+ "version": "42.2.12",
+ "repo": "maven"
+ },
+ {
+ "group": "com.zaxxer",
+ "name": "HikariCP",
+ "version": "3.4.2",
+ "repo": "maven"
+ },
+ {
+ "group": "org.javassist",
+ "name": "javassist",
+ "version": "3.27.0-GA",
+ "repo": "maven"
+ },
+ {
+ "group": "org.slf4j",
+ "name": "slf4j-api",
+ "version": "1.7.25",
+ "repo": "maven"
+ },
+ {
+ "group": "org.slf4j",
+ "name": "slf4j-nop",
+ "version": "1.7.25",
+ "repo": "maven"
+ }
+ ]
+}