[New] H2 Db Support + improvements (#774) by Fuzzlemann

* Adding H2 Database Support

* Making HtmlCompressor a constant so it is not created new each time it is used.
Note: It is thread-safe (see: https://code.google.com/archive/p/htmlcompressor/ "HtmlCompressor and XmlCompressor classes are considered thread safe* and can be used in multi-thread environment")

* Adds the H2 Database to the used libraries in the README.md

* Changes all invalid license headers

* Refactors many methods into an enum named DBType
These methods are:
 -> getName()
 -> getConfigName()
 -> supportsMySQLQueries()

 to a check if the DBType is H2

* KeepAliveTask splitted into separate class to remove duplicate code

* Fixes compilation error

* Refactors Database Tests into one Common class with all generic Database Tests and specific Database Tests

* Fixes some JavaDocs

* Adds license headers

* Optimizing comparisons to enum comparisons
This commit is contained in:
Fuzzlemann 2018-11-03 15:49:55 +01:00 committed by Risto Lahtela
parent 798a0e77eb
commit 471a830c9f
47 changed files with 1930 additions and 1319 deletions

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.djrapitops</groupId>
<artifactId>Plan</artifactId>
<version>4.5.0-SNAPSHOT</version>
<version>4.5.0-SNAPSHOT</version>
<build>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
@ -20,7 +20,7 @@
<include>**/*.js</include>
<include>**/*.css</include>
<include>locale/*.txt</include>
<include>**/*.ico</include>
<include>**/*.ico</include>
</includes>
</resource>
</resources>
@ -32,28 +32,28 @@
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.17</version>
</path>
<path>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0-SNAPSHOT</version>
</path>
<path>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
</path>
</annotationProcessorPaths>
<annotationProcessorPaths>
<path>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.17</version>
</path>
<path>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0-SNAPSHOT</version>
</path>
<path>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
@ -76,7 +76,7 @@
<exclude>org.mockito:*</exclude>
<exclude>org.easymock:*</exclude>
<exclude>junit:*</exclude>
<exclued>org.slf4j:*</exclued>
<exclued>org.slf4j:*</exclued>
</excludes>
</artifactSet>
<relocations>
@ -99,6 +99,10 @@
<pattern>com.zaxxer</pattern>
<shadedPattern>plan.com.zaxxer</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>plan.org.h2</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>com.djrapitops.plan.utilities.metrics</shadedPattern>
@ -162,13 +166,13 @@
<url>https://repo.spongepowered.org/maven</url>
</repository>
<repository>
<id>velocity-repo</id>
<url>https://repo.velocitypowered.com/snapshots/</url>
<id>velocity-repo</id>
<url>https://repo.velocitypowered.com/snapshots/</url>
</repository>
<repository>
<id>md_5-snapshots</id>
<url>http://repo.md-5.net/content/repositories/snapshots/</url>
</repository>
<repository>
<id>md_5-snapshots</id>
<url>http://repo.md-5.net/content/repositories/snapshots/</url>
</repository>
<repository>
<id>bstats-repo</id>
<url>http://repo.bstats.org/content/repositories/releases/</url>
@ -312,109 +316,109 @@
</exclusions>
</dependency>
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>text</artifactId>
<groupId>net.kyori</groupId>
</exclusion>
<exclusion>
<artifactId>toml4j</artifactId>
<groupId>com.moandjiezana.toml</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>checker-qual</artifactId>
<groupId>org.checkerframework</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>text</artifactId>
<groupId>net.kyori</groupId>
</exclusion>
<exclusion>
<artifactId>toml4j</artifactId>
<groupId>com.moandjiezana.toml</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>checker-qual</artifactId>
<groupId>org.checkerframework</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.github.ben-manes.caffeine</groupId>
</exclusion>
<exclusion>
<artifactId>plugin-meta</artifactId>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.github.ben-manes.caffeine</groupId>
</exclusion>
<exclusion>
<artifactId>plugin-meta</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-hocon</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-gson</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-yaml</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>flow-math</artifactId>
<groupId>com.flowpowered</groupId>
</exclusion>
<exclusion>
<artifactId>flow-noise</artifactId>
<groupId>com.flowpowered</groupId>
</exclusion>
<exclusion>
<artifactId>jsr305</artifactId>
<groupId>com.google.code.findbugs</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
</exclusion>
<exclusion>
<artifactId>configurate-hocon</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-gson</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-yaml</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>flow-math</artifactId>
<groupId>com.flowpowered</groupId>
</exclusion>
<exclusion>
<artifactId>flow-noise</artifactId>
<groupId>com.flowpowered</groupId>
</exclusion>
<exclusion>
<artifactId>jsr305</artifactId>
<groupId>com.google.code.findbugs</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.imaginarycode.minecraft</groupId>
<artifactId>RedisBungee</artifactId>
<version>0.3.8-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>

View File

@ -171,6 +171,13 @@
<version>1.5.2</version>
</dependency>
<!-- H2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version> <!-- This version is necessary so Sponge will not crash -->
</dependency>
<!-- Mockito (Test Dependency) -->
<dependency>
<groupId>org.mockito</groupId>
@ -179,6 +186,7 @@
<type>jar</type>
<scope>test</scope>
</dependency>
<!-- SQLite (Test Dependency) -->
<dependency>
<groupId>org.xerial</groupId>
@ -256,7 +264,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
@ -302,6 +310,10 @@
<pattern>com.zaxxer</pattern>
<shadedPattern>plan.com.zaxxer</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>plan.org.h2</shadedPattern>
</relocation>
<!--<relocation>-->
<!--<pattern>org.slf4j</pattern>-->
<!--<shadedPattern>plan.org.slf4j</shadedPattern>-->

View File

@ -26,6 +26,6 @@ import com.djrapitops.plan.system.database.databases.Database;
public class UnsupportedTransferDatabaseException extends WebException {
public UnsupportedTransferDatabaseException(Database db) {
super(db.getName() + " does not support Transfer operations!");
super(db.getType().getName() + " does not support Transfer operations!");
}
}

View File

@ -76,7 +76,7 @@ public class InfoCommand extends CommandNode {
"",
locale.getString(CommandLang.INFO_VERSION, plugin.getVersion()),
locale.getString(CommandLang.INFO_UPDATE, updateAvailable),
locale.getString(CommandLang.INFO_DATABASE, dbSystem.getDatabase().getName()),
locale.getString(CommandLang.INFO_DATABASE, dbSystem.getDatabase().getType().getName()),
locale.getString(CommandLang.INFO_BUNGEE_CONNECTION, connectedToBungee),
"",
">"

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
@ -93,7 +94,7 @@ public class ManageBackupCommand extends CommandNode {
String dbName = args[0].toLowerCase();
boolean isCorrectDB = Verify.equalsOne(dbName, "sqlite", "mysql");
boolean isCorrectDB = DBType.exists(dbName);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, dbName)));

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -78,7 +79,7 @@ public class ManageClearCommand extends CommandNode {
String dbName = args[0].toLowerCase();
boolean isCorrectDB = "sqlite".equals(dbName) || "mysql".equals(dbName);
boolean isCorrectDB = DBType.exists(dbName);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, dbName)));

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -73,11 +74,11 @@ public class ManageHotSwapCommand extends CommandNode {
String dbName = args[0].toLowerCase();
boolean isCorrectDB = Verify.equalsOne(dbName, "sqlite", "mysql");
boolean isCorrectDB = DBType.exists(dbName);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, dbName)));
Verify.isFalse(dbName.equals(dbSystem.getDatabase().getConfigName()),
Verify.isFalse(dbName.equals(dbSystem.getDatabase().getType().getConfigName()),
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_SAME_DB)));
try {

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -77,12 +78,12 @@ public class ManageMoveCommand extends CommandNode {
() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ARGS, 2, Arrays.toString(this.getArguments()))));
String fromDB = args[0].toLowerCase();
boolean isCorrectDB = Verify.equalsOne(fromDB, "sqlite", "mysql");
boolean isCorrectDB = DBType.exists(fromDB);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, fromDB)));
String toDB = args[1].toLowerCase();
isCorrectDB = Verify.equalsOne(toDB, "sqlite", "mysql");
isCorrectDB = DBType.exists(toDB);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, toDB)));
@ -115,9 +116,9 @@ public class ManageMoveCommand extends CommandNode {
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
boolean movingToCurrentDB = toDatabase.getConfigName().equalsIgnoreCase(dbSystem.getDatabase().getConfigName());
boolean movingToCurrentDB = toDatabase.getType() == dbSystem.getDatabase().getType();
if (movingToCurrentDB) {
sender.sendMessage(locale.getString(ManageLang.HOTSWAP_REMINDER, toDatabase.getConfigName()));
sender.sendMessage(locale.getString(ManageLang.HOTSWAP_REMINDER, toDatabase.getType().getConfigName()));
}
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);

View File

@ -110,7 +110,7 @@ public class ManageRemoveCommand extends CommandNode {
if (!Verify.contains("-a", args)) {
sender.sendMessage(
locale.getString(ManageLang.CONFIRMATION,
locale.getString(ManageLang.CONFIRM_REMOVAL, db.getName())
locale.getString(ManageLang.CONFIRM_REMOVAL, db.getType().getName())
)
);
return;

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.file.PlanFiles;
@ -84,7 +85,7 @@ public class ManageRestoreCommand extends CommandNode {
String backupDbName = args[0];
String dbName = args[1].toLowerCase();
boolean isCorrectDB = Verify.equalsOne(dbName, "sqlite", "mysql");
boolean isCorrectDB = DBType.exists(dbName);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, dbName)));
@ -96,7 +97,7 @@ public class ManageRestoreCommand extends CommandNode {
database.init();
if (!Verify.contains("-a", args)) {
sender.sendMessage(locale.getString(ManageLang.CONFIRMATION, locale.getString(ManageLang.CONFIRM_OVERWRITE, database.getName())));
sender.sendMessage(locale.getString(ManageLang.CONFIRMATION, locale.getString(ManageLang.CONFIRM_OVERWRITE, database.getType().getName())));
return;
}

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.database;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.MySQLDB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
@ -44,15 +45,17 @@ public class BukkitDBSystem extends DBSystem {
Locale locale,
MySQLDB mySQLDB,
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2DB,
PlanConfig config,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(locale, sqLiteDB, logger, timings, errorHandler);
super(locale, sqLiteDB, h2DB, logger, timings, errorHandler);
this.config = config;
databases.add(mySQLDB);
databases.add(h2DB.usingDefaultFile());
databases.add(sqLiteDB.usingDefaultFile());
}

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.system.SubSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
@ -44,6 +45,7 @@ public abstract class DBSystem implements SubSystem {
protected final Locale locale;
private final SQLiteDB.Factory sqLiteFactory;
private final H2DB.Factory h2Factory;
protected final PluginLogger logger;
protected final Timings timings;
protected final ErrorHandler errorHandler;
@ -54,12 +56,14 @@ public abstract class DBSystem implements SubSystem {
public DBSystem(
Locale locale,
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2Factory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
this.locale = locale;
sqLiteFactory = sqLiteDB;
this.sqLiteFactory = sqLiteDB;
this.h2Factory = h2Factory;
this.logger = logger;
this.timings = timings;
this.errorHandler = errorHandler;
@ -68,7 +72,7 @@ public abstract class DBSystem implements SubSystem {
public Database getActiveDatabaseByName(String dbName) {
for (Database database : getDatabases()) {
String dbConfigName = database.getConfigName();
String dbConfigName = database.getType().getConfigName();
if (Verify.equalsIgnoreCase(dbName, dbConfigName)) {
return database;
}
@ -100,11 +104,11 @@ public abstract class DBSystem implements SubSystem {
try {
db.init();
db.scheduleClean(20L);
logger.info(locale.getString(PluginLang.ENABLED_DATABASE, db.getName()));
logger.info(locale.getString(PluginLang.ENABLED_DATABASE, db.getType().getName()));
} catch (DBInitException e) {
Throwable cause = e.getCause();
String message = cause == null ? e.getMessage() : cause.getMessage();
throw new EnableException((db != null ? db.getName() : "Database") + " init failure: " + message, cause);
throw new EnableException((db != null ? db.getType().getName() : "Database") + " init failure: " + message, cause);
}
}
@ -116,4 +120,8 @@ public abstract class DBSystem implements SubSystem {
public SQLiteDB.Factory getSqLiteFactory() {
return sqLiteFactory;
}
public H2DB.Factory getH2Factory() {
return h2Factory;
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.system.database;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.MySQLDB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
@ -39,11 +40,12 @@ public class ProxyDBSystem extends DBSystem {
Locale locale,
MySQLDB mySQLDB,
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2DB,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(locale, sqLiteDB, logger, timings, errorHandler);
super(locale, sqLiteDB, h2DB, logger, timings, errorHandler);
databases.add(mySQLDB);
db = mySQLDB;
}

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.database;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.database.databases.sql.SpongeMySQLDB;
import com.djrapitops.plan.system.locale.Locale;
@ -44,16 +45,18 @@ public class SpongeDBSystem extends DBSystem {
Locale locale,
SpongeMySQLDB spongeMySQLDB,
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2DB,
PlanConfig config,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(locale, sqLiteDB, logger, timings, errorHandler);
super(locale, sqLiteDB, h2DB, logger, timings, errorHandler);
this.config = config;
databases.add(spongeMySQLDB);
databases.add(sqLiteDB.usingDefaultFile());
databases.add(h2DB.usingDefaultFile());
}
@Override

View File

@ -0,0 +1,106 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases;
import java.util.Optional;
/**
* An enum which stores the name, the config name and if the Database supports MySQL Queries
*
* @author Fuzzlemann
* @since 4.5.1
*/
public enum DBType {
MySQL("MySQL", true),
SQLite("SQLite", false),
H2("H2", true);
private final String name;
private final String configName;
private final boolean supportingMySQLQueries;
DBType(String name, boolean supportingMySQLQueries) {
this.name = name;
this.configName = name.toLowerCase().trim();
this.supportingMySQLQueries = supportingMySQLQueries;
}
/**
* Gets the name of the {@code DBType}
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Gets the config name of the {@code DBType}
*
* @return the config name
*/
public String getConfigName() {
return configName;
}
/**
* Used to check if the {@code DBType} supports <b>most</b> MySQL Queries.<p>
* When specific Statements are not compatible, the {@code DBType} should be checked.
*
* @return if the database supports MySQL Queries
*/
public boolean supportsMySQLQueries() {
return supportingMySQLQueries;
}
/**
* Gets an {@code Optional<DBType>} which matches {@code name}.<p>
* This method is case-insensitive.<p>
* The {@code Optional<DBType>} is empty when no {@code DBType} is found.
*
* @param name the name of the {@code DBType}
* @return an {@code Optional<DBType>}
*/
public static Optional<DBType> getForName(String name) {
for (DBType dbType : DBType.values()) {
if (dbType.getName().equalsIgnoreCase(name)) {
return Optional.of(dbType);
}
}
return Optional.empty();
}
/**
* Checks if the name of a {@code DBType} corresponds to {@code name}.<p>
* This method is case-insensitive.
*
* @param name the name of the {@code DBType}
* @return if the {@code DBType} exists
* @see DBType#getForName(String)
*/
public static boolean exists(String name) {
for (DBType dbType : DBType.values()) {
if (dbType.getName().equalsIgnoreCase(name)) {
return true;
}
}
return false;
}
}

View File

@ -47,18 +47,13 @@ public abstract class Database {
public abstract SaveOperations save();
public abstract String getName();
/**
* Used to get the config name of the database type.
* <p>
* Thread safe.
* Used to get the {@code DBType} of the Database
*
* @return sqlite/mysql
* @return the {@code DBType}
* @see DBType
*/
public String getConfigName() {
return getName().toLowerCase().trim();
}
public abstract DBType getType();
public abstract void close() throws DBException;

View File

@ -0,0 +1,240 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plugin.benchmarking.Timings;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.PluginTask;
import com.djrapitops.plugin.task.RunnableFactory;
import dagger.Lazy;
import org.h2.jdbcx.JdbcDataSource;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;
/**
* Implementation of the H2 database
*
* @author Fuzzlemann
* @since 4.5.1
*/
public class H2DB extends SQLDB {
private final File databaseFile;
private final String dbName;
private Connection connection;
private PluginTask connectionPingTask;
private H2DB(
File databaseFile,
Locale locale,
PlanConfig config,
Lazy<ServerInfo> serverInfo,
NetworkContainer.Factory networkContainerFactory,
RunnableFactory runnableFactory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, timings, errorHandler);
dbName = databaseFile.getName();
this.databaseFile = databaseFile;
}
@Override
public void setupDataSource() throws DBInitException {
try {
Class.forName("org.h2.Driver");
} catch (ClassNotFoundException e) {
errorHandler.log(L.CRITICAL, this.getClass(), e);
}
try {
connection = getNewConnection(databaseFile);
} catch (SQLException e) {
throw new DBInitException(e);
}
execute("SET REFERENTIAL_INTEGRITY FALSE");
startConnectionPingTask();
}
public Connection getNewConnection(File dbFile) throws SQLException {
String dbFilePath = dbFile.getAbsolutePath();
Connection newConnection = getConnectionFor(dbFilePath);
logger.debug("H2 " + dbName + ": Opened a new Connection");
newConnection.setAutoCommit(false);
return newConnection;
}
private Connection getConnectionFor(String dbFilePath) throws SQLException {
String username = config.getString(Settings.DB_USER);
String password = config.getString(Settings.DB_PASS);
JdbcDataSource jdbcDataSource = new JdbcDataSource();
jdbcDataSource.setURL("jdbc:h2:file:" + dbFilePath + ";mode=MySQL");
jdbcDataSource.setUser(username);
jdbcDataSource.setPassword(password);
return jdbcDataSource.getConnection();
}
private void startConnectionPingTask() {
stopConnectionPingTask();
try {
// Maintains Connection.
connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getType().getName(),
new KeepAliveTask(connection, () -> getNewConnection(databaseFile), logger, errorHandler)
).runTaskTimerAsynchronously(60L * 20L, 60L * 20L);
} catch (Exception ignored) {
}
}
private void stopConnectionPingTask() {
if (connectionPingTask != null) {
try {
connectionPingTask.cancel();
} catch (Exception ignored) {
}
}
}
@Override
public DBType getType() {
return DBType.H2;
}
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
connection = getNewConnection(databaseFile);
}
return connection;
}
@Override
public void close() {
stopConnectionPingTask();
if (connection != null) {
logger.debug("H2DB " + dbName + ": Closed Connection");
MiscUtils.close(connection);
}
super.close();
}
@Override
public void commit(Connection connection) {
try {
connection.commit();
} catch (SQLException e) {
if (!e.getMessage().contains("cannot commit")) {
errorHandler.log(L.ERROR, this.getClass(), e);
}
}
}
@Override
public void returnToPool(Connection connection) {
// Connection pool not in use, no action required.
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
H2DB h2DB = (H2DB) o;
return Objects.equals(dbName, h2DB.dbName);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), dbName);
}
@Singleton
public static class Factory {
private final Locale locale;
private final PlanConfig config;
private final Lazy<ServerInfo> serverInfo;
private final NetworkContainer.Factory networkContainerFactory;
private final RunnableFactory runnableFactory;
private final PluginLogger logger;
private final Timings timings;
private final ErrorHandler errorHandler;
private PlanFiles files;
@Inject
public Factory(
Locale locale,
PlanConfig config,
PlanFiles files,
Lazy<ServerInfo> serverInfo,
NetworkContainer.Factory networkContainerFactory,
RunnableFactory runnableFactory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
this.locale = locale;
this.config = config;
this.files = files;
this.serverInfo = serverInfo;
this.networkContainerFactory = networkContainerFactory;
this.runnableFactory = runnableFactory;
this.logger = logger;
this.timings = timings;
this.errorHandler = errorHandler;
}
public H2DB usingDefaultFile() {
return usingFileCalled("h2database");
}
public H2DB usingFileCalled(String fileName) {
return usingFile(files.getFileFromPluginFolder(fileName));
}
public H2DB usingFile(File databaseFile) {
return new H2DB(databaseFile,
locale, config, serverInfo,
networkContainerFactory,
runnableFactory, logger, timings, errorHandler
);
}
}
}

View File

@ -0,0 +1,59 @@
package com.djrapitops.plan.system.database.databases.sql;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.AbsRunnable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* The task which handles the upkeep of the {@code Connection}
*
* @author Fuzzlemann
* @since 4.5.1
*/
public class KeepAliveTask extends AbsRunnable {
private Connection connection;
private final IReconnect iReconnect;
private final PluginLogger logger;
private final ErrorHandler errorHandler;
public KeepAliveTask(Connection connection, IReconnect iReconnect, PluginLogger logger, ErrorHandler errorHandler) {
this.connection = connection;
this.iReconnect = iReconnect;
this.logger = logger;
this.errorHandler = errorHandler;
}
@Override
public void run() {
Statement statement = null;
ResultSet resultSet = null;
try {
if (connection != null && !connection.isClosed()) {
statement = connection.createStatement();
resultSet = statement.executeQuery("/* ping */ SELECT 1");
}
} catch (SQLException e) {
logger.debug("Something went wrong during SQL Connection upkeep task.");
try {
connection = iReconnect.reconnect();
} catch (SQLException e1) {
errorHandler.log(L.ERROR, this.getClass(), e1);
logger.error("SQL connection maintaining task had to be closed due to exception.");
this.cancel();
}
} finally {
MiscUtils.close(statement, resultSet);
}
}
public interface IReconnect {
Connection reconnect() throws SQLException;
}
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.system.database.databases.sql;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
@ -67,12 +68,9 @@ public class MySQLDB extends SQLDB {
increment++;
}
/**
* @return the name of the Database
*/
@Override
public String getName() {
return "MySQL";
public DBType getType() {
return DBType.MySQL;
}
/**
@ -158,11 +156,6 @@ public class MySQLDB extends SQLDB {
returnToPool(connection);
}
@Override
public boolean isUsingMySQL() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -419,10 +419,6 @@ public abstract class SQLDB extends Database {
return pingTable;
}
public boolean isUsingMySQL() {
return false;
}
@Override
public BackupOperations backup() {
return backupOps;
@ -468,12 +464,12 @@ public abstract class SQLDB extends Database {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SQLDB sqldb = (SQLDB) o;
return getName().equals(sqldb.getName());
return getType() == sqldb.getType();
}
@Override
public int hashCode() {
return Objects.hash(getName());
return Objects.hash(getType().getName());
}
public Supplier<UUID> getServerUUIDSupplier() {

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.system.database.databases.sql;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
@ -28,7 +29,6 @@ import com.djrapitops.plugin.benchmarking.Timings;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.PluginTask;
import com.djrapitops.plugin.task.RunnableFactory;
import dagger.Lazy;
@ -36,7 +36,9 @@ import dagger.Lazy;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
/**
@ -104,30 +106,9 @@ public class SQLiteDB extends SQLDB {
stopConnectionPingTask();
try {
// Maintains Connection.
connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getName(), new AbsRunnable() {
@Override
public void run() {
Statement statement = null;
ResultSet resultSet = null;
try {
if (connection != null && !connection.isClosed()) {
statement = connection.createStatement();
resultSet = statement.executeQuery("/* ping */ SELECT 1");
}
} catch (SQLException e) {
logger.debug("Something went wrong during SQLite Connection upkeep task.");
try {
connection = getNewConnection(databaseFile);
} catch (SQLException e1) {
errorHandler.log(L.ERROR, this.getClass(), e1);
logger.error("SQLite connection maintaining task had to be closed due to exception.");
this.cancel();
}
} finally {
MiscUtils.close(statement, resultSet);
}
}
}).runTaskTimerAsynchronously(60L * 20L, 60L * 20L);
connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getType().getName(),
new KeepAliveTask(connection, () -> getNewConnection(databaseFile), logger, errorHandler)
).runTaskTimerAsynchronously(60L * 20L, 60L * 20L);
} catch (Exception ignored) {
}
}
@ -141,12 +122,9 @@ public class SQLiteDB extends SQLDB {
}
}
/**
* @return the name of the Database
*/
@Override
public String getName() {
return "SQLite";
public DBType getType() {
return DBType.SQLite;
}
@Override
@ -250,4 +228,5 @@ public class SQLiteDB extends SQLDB {
}
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.system.database.databases.sql.patches;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
import com.djrapitops.plan.system.database.databases.sql.processing.QueryAllStatement;
import com.djrapitops.plan.system.database.databases.sql.processing.QueryStatement;
@ -30,11 +31,11 @@ import java.util.UUID;
public abstract class Patch {
protected final SQLDB db;
protected final boolean usingMySQL;
protected final DBType dbType;
public Patch(SQLDB db) {
this.db = db;
usingMySQL = db.isUsingMySQL();
this.dbType = db.getType();
}
public abstract boolean hasBeenApplied();
@ -46,15 +47,25 @@ public abstract class Patch {
}
public boolean hasTable(String tableName) {
String sql = usingMySQL ?
"SELECT * FROM information_schema.TABLES WHERE table_name=? AND TABLE_SCHEMA=? LIMIT 1" :
"SELECT tbl_name FROM sqlite_master WHERE tbl_name=?";
boolean secondParameter;
String sql;
if (dbType == DBType.H2) {
sql = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?";
secondParameter = false;
} else if (dbType.supportsMySQLQueries()) {
sql = "SELECT * FROM information_schema.TABLES WHERE table_name=? AND TABLE_SCHEMA=? LIMIT 1";
secondParameter = true;
} else {
sql = "SELECT tbl_name FROM sqlite_master WHERE tbl_name=?";
secondParameter = false;
}
return query(new QueryStatement<Boolean>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tableName);
if (usingMySQL) {
if (secondParameter) {
statement.setString(2, db.getConfig().getString(Settings.DB_DATABASE));
}
}
@ -67,36 +78,49 @@ public abstract class Patch {
}
protected boolean hasColumn(String tableName, String columnName) {
return usingMySQL ?
query(new QueryStatement<Boolean>("SELECT * FROM information_schema.COLUMNS" +
" WHERE TABLE_NAME=? AND COLUMN_NAME=? AND TABLE_SCHEMA=?") {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tableName);
statement.setString(2, columnName);
if (dbType.supportsMySQLQueries()) {
String query;
if (dbType == DBType.H2) {
query = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
" WHERE TABLE_NAME=? AND COLUMN_NAME=?";
} else {
query = "SELECT * FROM information_schema.COLUMNS" +
" WHERE TABLE_NAME=? AND COLUMN_NAME=? AND TABLE_SCHEMA=?";
}
return query(new QueryStatement<Boolean>(query) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tableName);
statement.setString(2, columnName);
if (dbType != DBType.H2) {
statement.setString(3, db.getConfig().getString(Settings.DB_DATABASE));
}
}
@Override
public Boolean processResults(ResultSet set) throws SQLException {
return set.next();
}
}) :
query(new QueryAllStatement<Boolean>("PRAGMA table_info(" + tableName + ")") {
@Override
public Boolean processResults(ResultSet set) throws SQLException {
while (set.next()) {
if (columnName.equals(set.getString("name"))) {
return true;
}
@Override
public Boolean processResults(ResultSet set) throws SQLException {
return set.next();
}
});
} else {
return query(new QueryAllStatement<Boolean>("PRAGMA table_info(" + tableName + ")") {
@Override
public Boolean processResults(ResultSet set) throws SQLException {
while (set.next()) {
if (columnName.equals(set.getString("name"))) {
return true;
}
return false;
}
});
return false;
}
});
}
}
protected void addColumn(String tableName, String columnInfo) {
db.executeUnsafe("ALTER TABLE " + tableName + " ADD " + (usingMySQL ? "" : "COLUMN ") + columnInfo);
db.executeUnsafe("ALTER TABLE " + tableName + " ADD " + (dbType.supportsMySQLQueries() ? "" : "COLUMN ") + columnInfo);
}
protected void dropTable(String name) {
@ -104,7 +128,7 @@ public abstract class Patch {
}
protected void renameTable(String from, String to) {
String sql = usingMySQL ?
String sql = dbType.supportsMySQLQueries() ?
"RENAME TABLE " + from + " TO " + to :
"ALTER TABLE " + from + " RENAME TO " + to;
db.execute(sql);

View File

@ -109,7 +109,7 @@ public class Version10Patch extends Patch {
" FROM " + tempNickTableName;
db.execute(statement);
try {
if (usingMySQL) {
if (dbType.supportsMySQLQueries()) {
db.execute("SET foreign_key_checks = 0");
}
statement = "INSERT INTO plan_kills " +
@ -118,7 +118,7 @@ public class Version10Patch extends Patch {
" FROM " + tempKillsTableName;
db.execute(statement);
} finally {
if (usingMySQL) {
if (dbType.supportsMySQLQueries()) {
db.execute("SET foreign_key_checks = 1");
}
}

View File

@ -62,11 +62,11 @@ public class CommandUseTable extends Table {
public void createTable() throws DBInitException {
ServerTable serverTable = db.getServerTable();
createTable(TableSqlParser.createTable(tableName)
.primaryKeyIDColumn(usingMySQL, Col.COMMAND_ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.COMMAND_ID)
.column(Col.COMMAND, Sql.varchar(20)).notNull()
.column(Col.TIMES_USED, Sql.INT).notNull()
.column(Col.SERVER_ID, Sql.INT).notNull()
.primaryKey(usingMySQL, Col.COMMAND_ID)
.primaryKey(supportsMySQLQueries, Col.COMMAND_ID)
.foreignKey(Col.SERVER_ID, serverTable.toString(), ServerTable.Col.SERVER_ID)
.toString()
);

View File

@ -81,8 +81,6 @@ public class NicknamesTable extends UserIDTable {
/**
* Get nicknames of the user on a server.
* <p>
* Get's nicknames from other servers as well.
*
* @param uuid UUID of the Player
* @param serverUUID UUID of the server
@ -119,8 +117,6 @@ public class NicknamesTable extends UserIDTable {
/**
* Get nicknames of the user on THIS server.
* <p>
* Get's nicknames from other servers as well.
*
* @param uuid UUID of the Player
* @return The nicknames of the User

View File

@ -56,14 +56,14 @@ public class PingTable extends UserIDTable {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(TABLE_NAME)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.column(Col.USER_ID, Sql.INT).notNull()
.column(Col.SERVER_ID, Sql.INT).notNull()
.column(Col.DATE, Sql.LONG).notNull()
.column(Col.MAX_PING, Sql.INT).notNull()
.column(Col.MIN_PING, Sql.INT).notNull()
.column(Col.AVG_PING, Sql.DOUBLE).notNull()
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.foreignKey(Col.USER_ID, usersTable.getTableName(), UsersTable.Col.ID)
.foreignKey(Col.SERVER_ID, ServerTable.TABLE_NAME, ServerTable.Col.SERVER_ID)
.toString());

View File

@ -63,13 +63,13 @@ public class ServerTable extends Table {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(tableName)
.primaryKeyIDColumn(usingMySQL, Col.SERVER_ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.SERVER_ID)
.column(Col.SERVER_UUID, Sql.varchar(36)).notNull().unique()
.column(Col.NAME, Sql.varchar(100))
.column(Col.WEBSERVER_ADDRESS, Sql.varchar(100))
.column(Col.INSTALLED, Sql.BOOL).notNull().defaultValue(true)
.column(Col.MAX_PLAYERS, Sql.INT).notNull().defaultValue("-1")
.primaryKey(usingMySQL, Col.SERVER_ID)
.primaryKey(supportsMySQLQueries, Col.SERVER_ID)
.toString()
);
}

View File

@ -66,7 +66,7 @@ public class SessionsTable extends UserIDTable {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(this.tableName)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.column(Col.USER_ID, Sql.INT).notNull()
.column(Col.SERVER_ID, Sql.INT).notNull()
.column(Col.SESSION_START, Sql.LONG).notNull()
@ -76,7 +76,7 @@ public class SessionsTable extends UserIDTable {
.column(Col.AFK_TIME, Sql.LONG).notNull()
.foreignKey(Col.USER_ID, usersTable.getTableName(), UsersTable.Col.ID)
.foreignKey(Col.SERVER_ID, serverTable.getTableName(), ServerTable.Col.SERVER_ID)
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.toString()
);
}

View File

@ -37,7 +37,7 @@ public abstract class Table {
protected final String tableName;
protected final SQLDB db;
protected final boolean usingMySQL;
protected final boolean supportsMySQLQueries;
/**
* Constructor.
@ -48,7 +48,7 @@ public abstract class Table {
public Table(String name, SQLDB db) {
this.tableName = name;
this.db = db;
this.usingMySQL = db != null && db.isUsingMySQL();
this.supportsMySQLQueries = db != null && db.getType().supportsMySQLQueries();
}
public abstract void createTable() throws DBInitException;
@ -118,13 +118,13 @@ public abstract class Table {
protected void addColumns(String... columnInfo) {
for (int i = 0; i < columnInfo.length; i++) {
columnInfo[i] = "ALTER TABLE " + tableName + " ADD " + (usingMySQL ? "" : "COLUMN ") + columnInfo[i];
columnInfo[i] = "ALTER TABLE " + tableName + " ADD " + (supportsMySQLQueries ? "" : "COLUMN ") + columnInfo[i];
}
executeUnsafe(columnInfo);
}
protected void removeColumns(String... columnNames) {
if (usingMySQL) {
if (supportsMySQLQueries) {
StringBuilder sqlBuild = new StringBuilder();
sqlBuild.append("ALTER TABLE ").append(tableName);
for (int i = 0; i < columnNames.length; i++) {
@ -147,7 +147,7 @@ public abstract class Table {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Table table = (Table) o;
return usingMySQL == table.usingMySQL &&
return supportsMySQLQueries == table.supportsMySQLQueries &&
Objects.equal(tableName, table.tableName) &&
Objects.equal(db, table.db);
}
@ -162,7 +162,7 @@ public abstract class Table {
@Override
public int hashCode() {
return Objects.hashCode(tableName, db, usingMySQL);
return Objects.hashCode(tableName, db, supportsMySQLQueries);
}
protected boolean execute(ExecStatement statement) {

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.database.databases.sql.tables;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
import com.djrapitops.plan.system.database.databases.sql.processing.ExecStatement;
import com.djrapitops.plan.system.database.databases.sql.processing.QueryStatement;
@ -51,15 +52,33 @@ public class TransferTable extends Table {
super(TABLE_NAME, db);
serverTable = db.getServerTable();
insertStatementNoParts = "REPLACE INTO " + tableName + " (" +
Col.SENDER_ID + ", " +
Col.EXPIRY + ", " +
Col.INFO_TYPE + ", " +
Col.EXTRA_VARIABLES + ", " +
Col.CONTENT +
") VALUES (" +
serverTable.statementSelectServerID + ", " +
"?, ?, ?, ?)";
if (db.getType() == DBType.H2) {
insertStatementNoParts = "INSERT INTO " + tableName + " (" +
Col.SENDER_ID + ", " +
Col.EXPIRY + ", " +
Col.INFO_TYPE + ", " +
Col.EXTRA_VARIABLES + ", " +
Col.CONTENT +
") VALUES (" +
serverTable.statementSelectServerID + ", " +
"?, ?, ?, ?)" +
" ON DUPLICATE KEY UPDATE" +
" " + Col.EXPIRY + "=?," +
" " + Col.INFO_TYPE + "=?," +
" " + Col.EXTRA_VARIABLES + "=?," +
" " + Col.CONTENT + "=?";
} else {
insertStatementNoParts = "REPLACE INTO " + tableName + " (" +
Col.SENDER_ID + ", " +
Col.EXPIRY + ", " +
Col.INFO_TYPE + ", " +
Col.EXTRA_VARIABLES + ", " +
Col.CONTENT +
") VALUES (" +
serverTable.statementSelectServerID + ", " +
"?, ?, ?, ?)";
}
selectStatement = "SELECT * FROM " + tableName +
" WHERE " + Col.INFO_TYPE + "= ?" +
@ -75,7 +94,7 @@ public class TransferTable extends Table {
.column(Col.EXPIRY, Sql.LONG).notNull().defaultValue("0")
.column(Col.INFO_TYPE, Sql.varchar(100)).notNull()
.column(Col.EXTRA_VARIABLES, Sql.varchar(255)).defaultValue("''")
.column(Col.CONTENT, usingMySQL ? "MEDIUMTEXT" : Sql.varchar(1)) // SQLite does not enforce varchar limits.
.column(Col.CONTENT, supportsMySQLQueries ? "MEDIUMTEXT" : Sql.varchar(1)) // SQLite does not enforce varchar limits.
.column(Col.PART, Sql.LONG).notNull().defaultValue("0")
.foreignKey(Col.SENDER_ID, serverTable.toString(), ServerTable.Col.SERVER_ID)
.toString()
@ -111,11 +130,21 @@ public class TransferTable extends Table {
execute(new ExecStatement(insertStatementNoParts) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
long expiration = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1L);
statement.setString(1, getServerUUID().toString());
statement.setLong(2, System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1L));
statement.setLong(2, expiration);
statement.setString(3, "configSettings");
statement.setString(4, null);
statement.setString(5, encodedSettingString);
if (db.getType() == DBType.H2) {
statement.setLong(6, expiration);
statement.setString(7, "configSettings");
statement.setString(8, null);
statement.setString(9, encodedSettingString);
}
}
});
}

View File

@ -64,7 +64,7 @@ public class UserInfoTable extends UserIDTable {
public void registerUserInfo(UUID uuid, long registered) {
if (!usersTable.isRegistered(uuid)) {
usersTable.registerUser(uuid, registered, "Waiting for Update..");
usersTable.registerUser(uuid, registered, "waitingForUpdate");
}
String sql = "INSERT INTO " + tableName + " (" +

View File

@ -59,12 +59,12 @@ public class UsersTable extends UserIDTable {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(tableName)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.column(Col.UUID, Sql.varchar(36)).notNull().unique()
.column(Col.REGISTERED, Sql.LONG).notNull()
.column(Col.USER_NAME, Sql.varchar(16)).notNull()
.column(Col.TIMES_KICKED, Sql.INT).notNull().defaultValue("0")
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.toString()
);
}
@ -261,13 +261,13 @@ public class UsersTable extends UserIDTable {
* @return a list of distinct names.
*/
public List<String> getMatchingNames(String name) {
String searchString = "%" + name.toLowerCase() + "%";
String searchString = "%" + name + "%";
NicknamesTable nicknamesTable = db.getNicknamesTable();
String sql = "SELECT DISTINCT " + Col.USER_NAME + " FROM " + tableName +
" WHERE " + Col.USER_NAME + " LIKE ?" +
" WHERE LOWER(" + Col.USER_NAME + ") LIKE LOWER(?)" +
" UNION SELECT DISTINCT " + Col.USER_NAME + " FROM " + tableName +
" INNER JOIN " + nicknamesTable + " on " + Col.ID + "=" + nicknamesTable + "." + NicknamesTable.Col.USER_ID +
" WHERE " + NicknamesTable.Col.NICKNAME + " LIKE ?";
" WHERE LOWER(" + NicknamesTable.Col.NICKNAME + ") LIKE LOWER(?)";
return query(new QueryStatement<List<String>>(sql, 5000) {
@Override

View File

@ -57,10 +57,10 @@ public class WorldTable extends Table {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(tableName)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.column(Col.NAME, Sql.varchar(100)).notNull()
.column(Col.SERVER_ID, Sql.INT).notNull()
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.foreignKey(Col.SERVER_ID, ServerTable.TABLE_NAME, ServerTable.Col.SERVER_ID)
.toString()
);

View File

@ -37,7 +37,7 @@ public class TransferTable extends Table {
}
protected void renameTable(String from, String to) {
String sql = usingMySQL ?
String sql = supportsMySQLQueries ?
"RENAME TABLE " + from + " TO " + to :
"ALTER TABLE " + from + " RENAME TO " + to;
execute(sql);

View File

@ -27,6 +27,12 @@ import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
*/
public class PageResponse extends Response {
private static final HtmlCompressor HTML_COMPRESSOR = new HtmlCompressor();
static {
HTML_COMPRESSOR.setRemoveIntertagSpaces(true);
}
public PageResponse(ResponseType type) {
super(type);
}
@ -36,8 +42,6 @@ public class PageResponse extends Response {
@Override
public void setContent(String content) {
HtmlCompressor compressor = new HtmlCompressor();
compressor.setRemoveIntertagSpaces(true);
super.setContent(compressor.compress(content));
super.setContent(HTML_COMPRESSOR.compress(content));
}
}

View File

@ -227,7 +227,7 @@ public class DebugPage implements Page {
.append(" (").append(serverProperties.getVersion());
content.append(")<br>");
content.append("**Database:** ").append(database.getName());
content.append("**Database:** ").append(database.getType().getName());
content.append("<br><br>");
Properties properties = System.getProperties();

View File

@ -41,7 +41,7 @@ public class BStatsBukkit {
if ("CraftBukkit".equals(serverType) && Check.isSpigotAvailable()) {
serverType = "Spigot";
}
String databaseType = plugin.getSystem().getDatabaseSystem().getDatabase().getName();
String databaseType = plugin.getSystem().getDatabaseSystem().getDatabase().getType().getName();
addStringSettingPie("server_type", serverType);
addStringSettingPie("database_type", databaseType);

View File

@ -46,7 +46,7 @@ public class BStatsBungee {
private void registerConfigSettingGraphs() {
String serverType = plugin.getProxy().getName();
String databaseType = database.getName();
String databaseType = database.getType().getName();
addStringSettingPie("server_type", serverType);
addStringSettingPie("database_type", databaseType);

View File

@ -39,7 +39,7 @@ public class BStatsSponge {
private void registerConfigSettingGraphs() {
String serverType = "Sponge";
String databaseType = database.getName();
String databaseType = database.getType().getName();
addStringSettingPie("server_type", serverType);
addStringSettingPie("database_type", databaseType);

View File

@ -1,7 +1,18 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.data;

View File

@ -0,0 +1,56 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
/**
* Test for the H2 database
*
* @author Rsl1122, Fuzzlemann
* @see SQLiteTest
* @since 4.5.1
*/
@RunWith(MockitoJUnitRunner.Silent.class)
public class H2Test extends CommonDBTest {
@BeforeClass
public static void setUpClass() throws Exception {
handleSetup("H2");
}
@AfterClass
public static void tearDownClass() {
system.disable();
}
@Test
public void testH2GetConfigName() {
assertEquals("h2", db.getType().getConfigName());
}
@Test
public void testH2GetName() {
assertEquals("H2", db.getType().getName());
}
}

View File

@ -1,7 +1,18 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities;

View File

@ -1,7 +1,18 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities.html;

View File

@ -1,7 +1,18 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities.html;

View File

@ -27,3 +27,4 @@ Documentation can be found [On the Wiki](https://github.com/Rsl1122/Plan-PlayerA
- **[jQuery Datatables](https://datatables.net/)** | [MIT License](https://datatables.net/license/mit)
- **[Font Awesome Icons](http://fontawesome.io/icons/)** | [SIL Open Font License](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL)
- **[MaxMind GeoIP2](https://www.maxmind.com/en/geoip-demo)** | [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/)
- **[H2 Database](http://www.h2database.com)** | [MPL 2.0](http://www.h2database.com/html/license.html#mpl2) or [EPL 1.0](http://www.h2database.com/html/license.html#eclipse_license)