[Merge] Version 4.8.2 (#1036)

This commit is contained in:
Risto Lahtela 2019-05-10 09:45:49 +03:00 committed by GitHub
commit b0717f7ff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
250 changed files with 5447 additions and 31269 deletions

View File

@ -1,8 +1,8 @@
plugins {
id "com.jfrog.bintray" version "1.8.1"
id "com.jfrog.bintray" version "1.8.4"
}
ext.apiVersion = '0.0.3'
ext.apiVersion = '0.0.4'
bintray {
user = System.getenv('BINTRAY_USER')

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 GNU 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
* GNU 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.capability;
import java.util.Optional;
/**
* List of different capabilities current version provides.
* <p>
* The enum is package private to restrict direct access. This is to avoid NoClassDefFoundError in case
* a wanted Capability is not provided in an earlier version.
* <p>
* Use {@link CapabilityService#hasCapability(String)} with name of a Capability to figure out if an API is available.
* Example usage: {@code CapabilityService.getInstance().hasCapability("DATA_EXTENSION_VALUES")}.
* <p>
* If a capability is not available, attempting to use the capability might lead to exceptions.
*
* @author Rsl1122
*/
enum Capability {
/**
* ExtensionService, DataExtension API base package, PluginInfo, Conditional, Tab, TabInfo, TabOrder and BooleanProvider, DoubleProvider, PercentageProvider, NumberProvider, StringProvider annotations.
*/
DATA_EXTENSION_VALUES,
/**
* DataExtension API table package, TableProvider, Table and Table.Factory
*/
DATA_EXTENSION_TABLES;
static Optional<Capability> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {
return Optional.empty();
}
}
}

View File

@ -0,0 +1,76 @@
/*
* 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 GNU 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
* GNU 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.capability;
import java.util.Optional;
import java.util.function.Consumer;
/**
* Service for figuring out provided API capabilities.
* <p>
* {@link CapabilityService#registerEnableListener(Consumer)} to be notified of Plan reloads
* {@link CapabilityService#hasCapability(String)} to check if a capability is available.
* <p>
* See {@link Capability} for list of capabilities provided by the current version.
*
* @author Rsl1122
*/
public interface CapabilityService {
/**
* Obtain instance of CapabilityService.
*
* @return CapabilityService implementation.
* @throws NoClassDefFoundError If Plan is not installed and this class can not be found or if older Plan version is installed.
* @throws IllegalStateException If Plan is installed, but not enabled.
*/
static CapabilityService getInstance() {
return Optional.ofNullable(CapabilityServiceHolder.service)
.orElseThrow(() -> new IllegalStateException("CapabilityService has not been initialised yet."));
}
/**
* Register a method to be called when Plan reloads.
*
* @param isEnabledListener The boolean given to the method tells if Plan has enabled successfully.
*/
void registerEnableListener(Consumer<Boolean> isEnabledListener);
/**
* Check if the API on the current version provides a capability.
*
* @param capabilityName Name of a capability
* @return true if the capability is available.
* @see Capability for different capabilityNames.
*/
default boolean hasCapability(String capabilityName) {
return Capability.getByName(capabilityName).isPresent();
}
class CapabilityServiceHolder {
static CapabilityService service;
private CapabilityServiceHolder() {
/* Static variable holder */
}
static void set(CapabilityService service) {
CapabilityServiceHolder.service = service;
}
}
}

View File

@ -32,14 +32,14 @@ public enum ElementOrder {
* Represents text - value pair box.
*/
VALUES,
/**
* Represents tables.
*/
TABLE,
/**
* Represents graphs.
*/
GRAPH;
GRAPH,
/**
* Represents tables.
*/
TABLE;
public static String serialize(ElementOrder[] order) {
StringBuilder builder = new StringBuilder();

View File

@ -43,6 +43,9 @@ public enum FormatType {
NONE;
public static Optional<FormatType> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -0,0 +1,49 @@
/*
* 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 GNU 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
* GNU 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.extension.annotation;
import com.djrapitops.plan.extension.icon.Color;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Method annotation to provide a Table.
* <p>
* Usage: {@code @TableProvider Table method(UUID playerUUID)}
* <p>
* Tables about players can have up to 4 columns.
* Tables about server can have up to 5 columns.
* <p>
* It is recommended to place each table on their own tab with a {@link Tab} annotation on the same method.
*
* @author Rsl1122
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TableProvider {
/**
* Determine the color of the table header.
*
* @return Preferred color. If none are specified defaults are used.
*/
Color tableColor() default Color.NONE;
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.extension.extractor;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.Group;
import com.djrapitops.plan.extension.annotation.*;
import com.djrapitops.plan.extension.table.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@ -103,6 +104,8 @@ public final class ExtensionExtractor {
MethodAnnotations.get(method, Conditional.class).ifPresent(annotation -> methodAnnotations.put(method, Conditional.class, annotation));
MethodAnnotations.get(method, Tab.class).ifPresent(annotation -> methodAnnotations.put(method, Tab.class, annotation));
MethodAnnotations.get(method, TableProvider.class).ifPresent(annotation -> methodAnnotations.put(method, TableProvider.class, annotation));
}
if (methodAnnotations.isEmpty()) {
@ -166,6 +169,7 @@ public final class ExtensionExtractor {
validateDoubleProviderAnnotations();
validatePercentageProviderAnnotations();
validateStringProviderAnnotations();
validateTableProviderAnnotations();
}
private void validateBooleanProviderAnnotations() {
@ -238,6 +242,13 @@ public final class ExtensionExtractor {
}
}
private void validateTableProviderAnnotations() {
for (Method method : methodAnnotations.getMethodAnnotations(TableProvider.class).keySet()) {
validateReturnType(method, Table.class);
validateMethodArguments(method, false, UUID.class, String.class, Group.class);
}
}
private void validateConditionals() {
Collection<Conditional> conditionals = methodAnnotations.getAnnotations(Conditional.class);
Collection<BooleanProvider> conditionProviders = methodAnnotations.getAnnotations(BooleanProvider.class);

View File

@ -48,6 +48,9 @@ public enum Color {
NONE;
public static Optional<Color> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -29,6 +29,9 @@ public enum Family {
BRAND;
public static Optional<Family> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -64,8 +64,9 @@ public class Icon {
return color;
}
public void setColor(Color color) {
public Icon setColor(Color color) {
this.color = color;
return this;
}
@Override

View File

@ -0,0 +1,213 @@
/*
* 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 GNU 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
* GNU 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.extension.table;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Object for giving Plan table data.
* <p>
* Usage: {@code Table.builder().columnOne("columnName", new Icon(...)).addRow("Your", "Row", "Data").build()}
* <p>
* Tables about players can have up to 4 columns.
* Tables about server can have up to 5 columns.
* <p>
* Icon colors are ignored.
* <p>
* If a row has more values than the column limit, they are ignored.
* If a row has less values than table columns, a '-' is displayed to distinguish a missing value.
* <p>
* If a table has no columns or rows, it is ignored.
*
* @author Rsl1122
* @see com.djrapitops.plan.extension.annotation.TableProvider for associated annotation.
*/
public final class Table {
private final String[] columns;
private final Icon[] icons;
private final List<Object[]> rows;
private Table() {
/* Tables are constructed with the factory. */
columns = new String[5];
icons = new Icon[5];
rows = new ArrayList<>();
}
/**
* Create a new Table Factory.
*
* @return a new Table Factory.
*/
public static Table.Factory builder() {
return new Table.Factory();
}
public String[] getColumns() {
return columns;
}
public int getMaxColumnSize() {
int columnCount = 0;
for (String column : columns) {
if (column == null) {
break; // Prevent having one null column between two columns
}
columnCount++;
}
return columnCount;
}
public Icon[] getIcons() {
return icons;
}
public List<Object[]> getRows() {
return rows;
}
/**
* Factory for creating new {@link Table} objects.
*/
public static final class Factory {
private final Table building;
// These variable are related to implementation, and is used when the table is being fetched from the database.
Color color; // Table color is defined with TableProvider annotation.
String tableName; // tableName is defined by method name annotated by TableProvider.
String tabName; // Tab name is defined with Tab annotation
int tabPriority; // Tab priority is defined with TabOrder annotation
ElementOrder[] tabOrder; // Tab element order is defined with TabInfo annotation
Icon tabIcon; // Tab icon is defined with TabInfo annotation
private Factory() {
building = new Table();
}
private Factory column(int indx, String columnName, Icon icon) {
building.columns[indx] = columnName;
building.icons[indx] = icon != null ? Icon.called(icon.getName()).of(icon.getFamily()).build() : Icon.called("question").build();
return this;
}
/**
* Set first column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnOne(String columnName, Icon icon) {
return column(0, columnName, icon);
}
/**
* Set second column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnTwo(String columnName, Icon icon) {
return column(1, columnName, icon);
}
/**
* Set third column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnThree(String columnName, Icon icon) {
return column(2, columnName, icon);
}
/**
* Set fourth column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnFour(String columnName, Icon icon) {
return column(3, columnName, icon);
}
/**
* Set fifth column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnFive(String columnName, Icon icon) {
return column(4, columnName, icon);
}
/**
* Add a row of values to the table.
*
* @param values One value per column you have defined, {@code Object#toString()} will be called on the objects.
* @return Factory.
* @throws IllegalArgumentException If given varargs for 'values' is null.
*/
public Factory addRow(Object... values) {
if (values == null) {
throw new IllegalArgumentException("'values' for Table#addRow can not be null!");
}
if (values.length == 0 || areAllValuesNull(values)) {
return this; // Ignore row when all values are null or no values are present.
}
building.rows.add(Arrays.copyOf(values, 5));
return this;
}
private boolean areAllValuesNull(Object[] values) {
boolean allNull = true;
for (Object value : values) {
if (value != null) {
allNull = false;
break;
}
}
return allNull;
}
/**
* Finish building the table.
*
* @return Finished Table object.
*/
public Table build() {
return building;
}
}
}

View File

@ -166,6 +166,20 @@ class ExtensionExtractorTest {
assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: java.lang.String", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
@Test
void tableProviderMustReturnTable() {
@PluginInfo(name = "Extension")
class Extension implements DataExtension {
@TableProvider
public Double method(UUID playerUUID) {
return null;
}
}
ExtensionExtractor underTest = new ExtensionExtractor(new Extension());
assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: com.djrapitops.plan.extension.table.Table", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
@Test
void booleanProviderCanNotSupplyItsOwnConditional() {
@PluginInfo(name = "Extension")

View File

@ -2,9 +2,9 @@ plugins {
id "java"
id "jacoco"
id "checkstyle"
id "org.sonarqube" version "2.6.2"
id "net.ltgt.apt" version "0.19"
id "net.ltgt.apt-idea" version "0.19"
id "org.sonarqube" version "2.7"
id "net.ltgt.apt" version "0.21"
id "net.ltgt.apt-idea" version "0.21"
id "com.github.johnrengelman.shadow" version "5.0.0"
}
@ -12,7 +12,7 @@ allprojects {
wrapper.gradleVersion = "5.0"
group "com.djrapitops"
version "4.8.1"
version "4.8.2"
test {
useJUnitPlatform()
@ -43,11 +43,11 @@ subprojects {
sourceCompatibility = 1.8
targetCompatibility = 1.8
ext.daggerVersion = "2.21"
ext.daggerCompilerVersion = "2.21"
ext.daggerVersion = "2.22.1"
ext.daggerCompilerVersion = "2.22.1"
ext.abstractPluginFrameworkVersion = "3.4.1"
ext.planPluginBridgeVersion = "4.7.0"
ext.planPluginBridgeVersion = "4.8.2-R0.3"
ext.bukkitVersion = "1.12.2-R0.1-SNAPSHOT"
ext.spigotVersion = "1.12.2-R0.1-SNAPSHOT"
@ -61,12 +61,12 @@ subprojects {
ext.commonsTextVersion = "1.6"
ext.htmlCompressorVersion = "1.5.2"
ext.caffeineVersion = "2.7.0"
ext.h2Version = "1.4.196"
ext.h2Version = "1.4.199"
ext.hikariVersion = "3.3.1"
ext.slf4jVersion = "1.7.26"
ext.geoIpVersion = "2.12.0"
ext.guavaVersion = "26.0-jre"
ext.bstatsVersion = "1.2"
ext.bstatsVersion = "1.4"
repositories {
mavenCentral()
@ -89,7 +89,7 @@ subprojects {
url = "https://repo.velocitypowered.com/snapshots/"
}
maven { // bStats Repository
url = "http://repo.bstats.org/content/repositories/releases/"
url = "https://repo.codemc.org/repository/maven-public"
}
maven { // PlanPluginBridge Repository
url = "https://dl.bintray.com/rsl1122/Plan-repository"
@ -103,18 +103,18 @@ subprojects {
testAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerCompilerVersion"
// Test Tooling Dependencies
testCompile "org.junit.jupiter:junit-jupiter-engine:5.4.1" // JUnit 5
testCompile "org.junit.platform:junit-platform-runner:1.4.1" // JUnit 4 runner for JUnit 5 tests
testCompile "org.junit.vintage:junit-vintage-engine:5.4.1" // JUnit 4 compatibility for JUnit 5
testCompile "org.junit.jupiter:junit-jupiter-params:5.4.1" // JUnit 5, parameterized tests
testCompile "org.mockito:mockito-core:2.25.1" // Mockito Core
testCompile "org.mockito:mockito-junit-jupiter:2.25.1" // Mockito JUnit 5 Extension
testCompile "org.junit.jupiter:junit-jupiter-engine:5.4.2" // JUnit 5
testCompile "org.junit.platform:junit-platform-runner:1.4.2" // JUnit 4 runner for JUnit 5 tests
testCompile "org.junit.vintage:junit-vintage-engine:5.4.2" // JUnit 4 compatibility for JUnit 5
testCompile "org.junit.jupiter:junit-jupiter-params:5.4.2" // JUnit 5, parameterized tests
testCompile "org.mockito:mockito-core:2.27.0" // Mockito Core
testCompile "org.mockito:mockito-junit-jupiter:2.27.0" // Mockito JUnit 5 Extension
testCompile "org.seleniumhq.selenium:selenium-java:3.141.59" // Selenium (Browser tests)
testCompile "com.jayway.awaitility:awaitility:1.7.0" // Awaitility (Concurrent wait conditions)
// Testing dependencies required by Plan
testCompile "org.xerial:sqlite-jdbc:3.27.2.1" // SQLite
testCompile "mysql:mysql-connector-java:8.0.15" // MySQL
testCompile "mysql:mysql-connector-java:8.0.16" // MySQL
}
configurations {

View File

@ -26,6 +26,7 @@ import com.djrapitops.plan.system.settings.theme.PlanColorScheme;
import com.djrapitops.plugin.BukkitPlugin;
import com.djrapitops.plugin.benchmarking.Benchmark;
import com.djrapitops.plugin.command.ColorScheme;
import com.djrapitops.plugin.task.AbsRunnable;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.logging.Level;
@ -52,7 +53,7 @@ public class Plan extends BukkitPlugin implements PlanPlugin {
locale = system.getLocaleSystem().getLocale();
system.enable();
new BStatsBukkit(this).registerMetrics();
registerMetrics();
logger.debug("Verbose debug messages are enabled.");
String benchTime = " (" + timings.end("Enable").map(Benchmark::toDurationString).orElse("-") + ")";
@ -80,6 +81,19 @@ public class Plan extends BukkitPlugin implements PlanPlugin {
}
}
private void registerMetrics() {
Plan plugin = this;
// Spigot 1.14 requires Sync events to be fired from a server thread.
// Registering a service fires a sync event, and bStats registers a service,
// so this has to be run on the server thread.
runnableFactory.create("Register Metrics task", new AbsRunnable() {
@Override
public void run() {
new BStatsBukkit(plugin).registerMetrics();
}
}).runTask();
}
@Override
public ColorScheme getColorScheme() {
return PlanColorScheme.create(system.getConfigSystem().getConfig(), logger);

View File

@ -33,10 +33,8 @@ import com.djrapitops.plan.system.importing.data.BukkitUserImportRefiner;
import com.djrapitops.plan.system.importing.data.ServerImportData;
import com.djrapitops.plan.system.importing.data.UserImportData;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.utilities.SHA256Hash;
import com.djrapitops.plugin.utilities.Verify;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -177,11 +175,11 @@ public abstract class BukkitImporter implements Importer {
}
private BaseUser toBaseUser(UserImportData userImportData) {
UUID uuid = userImportData.getUuid();
String name = userImportData.getName();
UUID playerUUID = userImportData.getUuid();
String playerName = userImportData.getName();
long registered = userImportData.getRegistered();
int timesKicked = userImportData.getTimesKicked();
return new BaseUser(uuid, name, registered, timesKicked);
return new BaseUser(playerUUID, playerName, registered, timesKicked);
}
private UserInfo toUserInfo(UserImportData userImportData) {
@ -211,11 +209,7 @@ public abstract class BukkitImporter implements Importer {
return userImportData.getIps().parallelStream()
.map(ip -> {
String geoLoc = geolocationCache.getCountry(ip);
try {
return new GeoInfo(ip, geoLoc, date, new SHA256Hash(ip).create());
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
return new GeoInfo(ip, geoLoc, date);
}).collect(Collectors.toList());
}
}

View File

@ -27,7 +27,6 @@ public class BukkitServerProperties extends ServerProperties {
public BukkitServerProperties(Server server) {
super(
server.getServerId(),
server.getName(),
server.getPort(),
server.getVersion(),

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.system.listeners;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.events.PlanBukkitEnableEvent;
import com.djrapitops.plan.capability.CapabilityServiceImplementation;
import com.djrapitops.plan.system.listeners.bukkit.*;
import com.djrapitops.plan.system.status.Status;
import org.bukkit.Bukkit;
@ -83,7 +84,9 @@ public class BukkitListenerSystem extends ListenerSystem {
@Override
public void callEnableEvent(PlanPlugin plugin) {
PlanBukkitEnableEvent event = new PlanBukkitEnableEvent(plugin.isSystemEnabled());
boolean isEnabled = plugin.isSystemEnabled();
PlanBukkitEnableEvent event = new PlanBukkitEnableEvent(isEnabled);
Bukkit.getServer().getPluginManager().callEvent(event);
CapabilityServiceImplementation.notifyAboutEnable(isEnabled);
}
}

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.ShutdownHook;
import com.djrapitops.plan.db.tasks.DBCleanTask;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.DataGatheringSettings;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.system.tasks.bukkit.BukkitTPSCountTimer;
import com.djrapitops.plan.system.tasks.bukkit.PaperTPSCountTimer;
@ -90,9 +91,12 @@ public class BukkitTaskSystem extends ServerTaskSystem {
public void enable() {
super.enable();
try {
plugin.registerListener(pingCountTimer);
long startDelay = TimeAmount.toTicks(config.get(TimeSettings.PING_SERVER_ENABLE_DELAY), TimeUnit.MILLISECONDS);
registerTask(pingCountTimer).runTaskTimer(startDelay, 40L);
Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) {
plugin.registerListener(pingCountTimer);
long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS);
registerTask(pingCountTimer).runTaskTimer(startDelay, 40L);
}
} catch (ExceptionInInitializerError | NoClassDefFoundError ignore) {
// Running CraftBukkit
}

View File

@ -64,7 +64,6 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
//the server is pinging the client every 40 Ticks (2 sec) - so check it then
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178
public static final int PING_INTERVAL = 2 * 20;
private static final boolean PING_METHOD_AVAILABLE;
@ -196,6 +195,10 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
Player player = joinEvent.getPlayer();
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) {
return;
}
runnableFactory.create("Add Player to Ping list", new AbsRunnable() {
@Override
public void run() {
@ -203,7 +206,7 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
addPlayer(player);
}
}
}).runTaskLater(TimeAmount.toTicks(config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY), TimeUnit.MILLISECONDS));
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
}
@EventHandler

View File

@ -122,7 +122,7 @@ public final class Reflection {
try {
return (T) field.get(target);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access reflection.", e);
throw new IllegalStateException("Cannot access reflection.", e);
}
}
@ -131,7 +131,7 @@ public final class Reflection {
try {
field.set(target, value);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access reflection.", e);
throw new IllegalStateException("Cannot access reflection.", e);
}
}
@ -197,7 +197,7 @@ public final class Reflection {
try {
return method.invoke(target, arguments);
} catch (Exception e) {
throw new RuntimeException("Cannot invoke method " + method, e);
throw new IllegalStateException("Cannot invoke method " + method, e);
}
};
}
@ -240,7 +240,7 @@ public final class Reflection {
try {
return constructor.newInstance(arguments);
} catch (Exception e) {
throw new RuntimeException("Cannot invoke constructor " + constructor, e);
throw new IllegalStateException("Cannot invoke constructor " + constructor, e);
}
};
}

View File

@ -17,7 +17,10 @@
package com.djrapitops.plan;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.system.PlanSystem;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.settings.ConfigSettingKeyTest;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.WebserverSettings;
@ -31,9 +34,11 @@ import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import rules.BukkitComponentMocker;
import rules.ComponentMocker;
import utilities.OptionalAssert;
import utilities.RandomData;
import java.util.Collection;
import java.util.Optional;
import static org.junit.Assert.assertTrue;
@ -71,6 +76,21 @@ public class BukkitSystemTest {
}
}
@Test
public void correctWebAddressInDatabaseAfterEnable() throws EnableException {
try {
system.enable();
Database database = system.getDatabaseSystem().getDatabase();
String expectedAddress = system.getWebServerSystem().getWebServer().getAccessAddress();
Optional<String> found = database.query(ServerQueries.fetchServerMatchingIdentifier(system.getServerInfo().getServerUUID()))
.map(Server::getWebAddress);
OptionalAssert.equals(expectedAddress, found);
} finally {
system.disable();
}
}
@Test
public void bukkitSystemHasDefaultConfigValuesAfterEnable() throws EnableException, IllegalAccessException {
try {

View File

@ -31,7 +31,6 @@ public class BungeeServerProperties extends ServerProperties {
public BungeeServerProperties(ProxyServer server, PlanConfig config) {
super(
server.getServers().toString(),
"BungeeCord",
-1,
server.getVersion(),

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.system.listeners;
import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.events.PlanBungeeEnableEvent;
import com.djrapitops.plan.capability.CapabilityServiceImplementation;
import com.djrapitops.plan.system.listeners.bungee.PlayerOnlineListener;
import javax.inject.Inject;
@ -46,7 +47,9 @@ public class BungeeListenerSystem extends ListenerSystem {
@Override
public void callEnableEvent(PlanPlugin plugin) {
PlanBungeeEnableEvent event = new PlanBungeeEnableEvent(plugin.isSystemEnabled());
boolean isEnabled = plugin.isSystemEnabled();
PlanBungeeEnableEvent event = new PlanBungeeEnableEvent(isEnabled);
((PlanBungee) plugin).getProxy().getPluginManager().callEvent(event);
CapabilityServiceImplementation.notifyAboutEnable(isEnabled);
}
}

View File

@ -20,6 +20,7 @@ import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.db.tasks.DBCleanTask;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.DataGatheringSettings;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.system.tasks.bungee.BungeeTPSCountTimer;
import com.djrapitops.plan.system.tasks.bungee.PingCountTimerBungee;
@ -87,9 +88,12 @@ public class BungeeTaskSystem extends TaskSystem {
registerTask(networkPageRefreshTask).runTaskTimerAsynchronously(1500, TimeAmount.toTicks(5L, TimeUnit.MINUTES));
registerTask(logsFolderCleanTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS));
plugin.registerListener(pingCountTimer);
long startDelay = TimeAmount.toTicks(config.get(TimeSettings.PING_SERVER_ENABLE_DELAY), TimeUnit.MILLISECONDS);
registerTask(pingCountTimer).runTaskTimer(startDelay, PingCountTimerBungee.PING_INTERVAL);
Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) {
plugin.registerListener(pingCountTimer);
long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS);
registerTask(pingCountTimer).runTaskTimer(startDelay, 40L);
}
registerTask(playersPageRefreshTask)
.runTaskTimerAsynchronously(TimeAmount.toTicks(5L, TimeUnit.MINUTES), TimeAmount.toTicks(5L, TimeUnit.MINUTES));

View File

@ -52,10 +52,6 @@ import java.util.concurrent.TimeUnit;
@Singleton
public class PingCountTimerBungee extends AbsRunnable implements Listener {
//the server is pinging the client every 40 Ticks (2 sec) - so check it then
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178
public static final int PING_INTERVAL = 2 * 20;
private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final PlanConfig config;
@ -121,6 +117,10 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener {
@EventHandler
public void onPlayerJoin(ServerConnectedEvent joinEvent) {
ProxiedPlayer player = joinEvent.getPlayer();
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) {
return;
}
runnableFactory.create("Add Player to Ping list", new AbsRunnable() {
@Override
public void run() {
@ -128,7 +128,7 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener {
addPlayer(player);
}
}
}).runTaskLater(TimeAmount.toTicks(config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY), TimeUnit.MILLISECONDS));
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
}
@EventHandler

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.db.SQLiteDB;
import com.djrapitops.plan.system.PlanSystem;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
import com.djrapitops.plan.system.settings.paths.ProxySettings;
import com.djrapitops.plan.system.settings.paths.WebserverSettings;
import com.google.common.util.concurrent.MoreExecutors;
@ -33,9 +34,11 @@ import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import rules.BungeeComponentMocker;
import rules.ComponentMocker;
import utilities.CIProperties;
import utilities.RandomData;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
/**
* Test for Bungee PlanSystem.
@ -69,6 +72,7 @@ public class BungeeSystemTest {
dbSystem.setActiveDatabase(db);
bungeeSystem.enable();
assertTrue(bungeeSystem.isEnabled());
} finally {
bungeeSystem.disable();
}
@ -90,8 +94,7 @@ public class BungeeSystemTest {
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
dbSystem.setActiveDatabase(db);
bungeeSystem.enable();
assertTrue(bungeeSystem.isEnabled());
bungeeSystem.enable(); // Throws EnableException
} finally {
bungeeSystem.disable();
}
@ -107,7 +110,31 @@ public class BungeeSystemTest {
config.set(WebserverSettings.PORT, TEST_PORT_NUMBER);
config.set(ProxySettings.IP, "8.8.8.8");
bungeeSystem.enable(); // Throws EnableException
} finally {
bungeeSystem.disable();
}
}
@Test
public void testEnableWithMySQL() throws EnableException {
boolean isCI = Boolean.parseBoolean(System.getenv(CIProperties.IS_CI_SERVICE));
assumeTrue(isCI);
PlanSystem bungeeSystem = component.getPlanSystem();
try {
PlanConfig config = bungeeSystem.getConfigSystem().getConfig();
config.set(DatabaseSettings.MYSQL_DATABASE, "Plan");
config.set(DatabaseSettings.MYSQL_USER, "travis");
config.set(DatabaseSettings.MYSQL_PASS, "");
config.set(DatabaseSettings.MYSQL_HOST, "127.0.0.1");
config.set(DatabaseSettings.TYPE, "MySQL");
config.set(WebserverSettings.PORT, TEST_PORT_NUMBER);
config.set(ProxySettings.IP, "8.8.8.8");
bungeeSystem.enable();
assertTrue(bungeeSystem.isEnabled());
} finally {
bungeeSystem.disable();
}

View File

@ -14,31 +14,31 @@
* 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.pluginbridge.plan.redprotect;
package com.djrapitops.plan.api.exceptions;
import com.djrapitops.plan.data.plugin.HookHandler;
import com.djrapitops.pluginbridge.plan.Hook;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
/**
* Hook for RedProtect plugin.
* Exception that is thrown when a call to a DataExtension method throws an exception.
*
* @author Rsl1122
*/
@Singleton
public class RedProtectHook extends Hook {
public class DataExtensionMethodCallException extends IllegalStateException {
@Inject
public RedProtectHook() {
super("br.net.fabiozumbi12.RedProtect.Bukkit.RedProtect");
private final String pluginName;
private final MethodWrapper method;
public DataExtensionMethodCallException(Throwable cause, String pluginName, MethodWrapper method) {
super(cause);
this.pluginName = pluginName;
this.method = method;
}
@Override
public void hook(HookHandler handler) throws NoClassDefFoundError {
if (enabled) {
handler.addPluginDataSource(new RedProtectData());
}
public String getPluginName() {
return pluginName;
}
public MethodWrapper getMethod() {
return method;
}
}

View File

@ -0,0 +1,57 @@
/*
* 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 GNU 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
* GNU 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.capability;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* Singleton instance implementation for {@link CapabilityService}.
* <p>
* Only one instance exists per runtime in order to notify others when the plugin enables.
*
* @author Rsl1122
*/
public class CapabilityServiceImplementation implements CapabilityService {
private List<Consumer<Boolean>> enableListeners;
private CapabilityServiceImplementation() {
/* Inject required for dagger */
CapabilityServiceHolder.set(this);
enableListeners = new ArrayList<>();
}
private static CapabilityServiceImplementation get() {
if (CapabilityServiceHolder.service == null) {
return new CapabilityServiceImplementation();
}
return (CapabilityServiceImplementation) CapabilityServiceHolder.service;
}
public static void notifyAboutEnable(boolean isEnabled) {
for (Consumer<Boolean> enableListener : get().enableListeners) {
enableListener.accept(isEnabled);
}
}
@Override
public void registerEnableListener(Consumer<Boolean> enableListener) {
enableListeners.add(enableListener);
}
}

View File

@ -32,7 +32,6 @@ import com.djrapitops.plan.system.locale.lang.ManageLang;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.webserver.WebServer;
import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.command.ColorScheme;
import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
@ -82,7 +81,7 @@ public class ManageConDebugCommand extends CommandNode {
this.webServer = webServer;
this.dbSystem = dbSystem;
setShortHelp(locale.getString(Check.isBungeeAvailable() || Check.isVelocityAvailable() ? CmdHelpLang.CON : CmdHelpLang.MANAGE_CON));
setShortHelp(locale.getString(serverInfo.getServer().isProxy() ? CmdHelpLang.CON : CmdHelpLang.MANAGE_CON));
setInDepthHelp(locale.getArray(DeepHelpLang.MANAGE_CON));
}

View File

@ -60,7 +60,7 @@ public class ManageDisableCommand extends CommandNode {
Verify.isTrue(args.length >= 1,
() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ONE_ARG, Arrays.toString(this.getArguments()))));
if ("kickcount".equals(args[0].toLowerCase())) {
if ("kickcount".equalsIgnoreCase(args[0])) {
status.setCountKicks(false);
sender.sendMessage(locale.getString(CommandLang.FEATURE_DISABLED, "Kick Counting"));
} else {

View File

@ -18,13 +18,11 @@ package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.store.objects.DateHolder;
import com.djrapitops.plan.data.store.objects.DateMap;
import com.djrapitops.plan.utilities.SHA256Hash;
import com.google.common.base.Objects;
import java.io.Serializable;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.security.NoSuchAlgorithmException;
/**
* Data class that contains information about IP and Geolocation.
@ -35,18 +33,16 @@ public class GeoInfo implements DateHolder, Serializable {
private final String ip;
private final String geolocation;
private final String ipHash;
private final long date;
public GeoInfo(InetAddress address, String geolocation, long lastUsed) throws NoSuchAlgorithmException {
this(formatIP(address), geolocation, lastUsed, new SHA256Hash(address.getHostAddress()).create());
public GeoInfo(InetAddress address, String geolocation, long lastUsed) {
this(formatIP(address), geolocation, lastUsed);
}
public GeoInfo(String ip, String geolocation, long date, String ipHash) {
public GeoInfo(String ip, String geolocation, long date) {
this.ip = ip;
this.geolocation = geolocation;
this.date = date;
this.ipHash = ipHash;
}
public static DateMap<GeoInfo> intoDateMap(Iterable<GeoInfo> geoInfo) {
@ -106,23 +102,18 @@ public class GeoInfo implements DateHolder, Serializable {
return date;
}
public String getIpHash() {
return ipHash;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GeoInfo geoInfo = (GeoInfo) o;
return Objects.equal(ip, geoInfo.ip) &&
Objects.equal(geolocation, geoInfo.geolocation) &&
Objects.equal(ipHash, geoInfo.ipHash);
Objects.equal(geolocation, geoInfo.geolocation);
}
@Override
public int hashCode() {
return Objects.hashCode(ip, geolocation, ipHash);
return Objects.hashCode(ip, geolocation);
}
@Override
@ -130,7 +121,6 @@ public class GeoInfo implements DateHolder, Serializable {
return "GeoInfo{" +
"ip='" + ip + '\'' +
", geolocation='" + geolocation + '\'' +
", ipHash='" + ipHash + '\'' +
", date=" + date +
'}';
}

View File

@ -92,10 +92,7 @@ public class TableContainer {
if (i > maxIndex) {
body.append("<td>-");
} else {
Serializable value = row[i];
Formatter formatter = formatters[i];
body.append("<td").append(formatter != null ? " data-order=\"" + value + "\">" : ">");
body.append(formatter != null ? formatter.apply(value) : (value != null ? value : '-'));
appendValue(body, row[i], formatters[i]);
}
body.append("</td>");
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
@ -105,6 +102,15 @@ public class TableContainer {
body.append("</tr>");
}
private void appendValue(StringBuilder body, Serializable value, Formatter formatter) {
body.append("<td").append(formatter != null ? " data-order=\"" + value + "\">" : ">");
if (formatter != null) {
body.append(formatter.apply(value));
} else {
body.append(value != null ? value : '-');
}
}
public final void setColor(String color) {
this.color = color;
}

View File

@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
public abstract class AbstractHealthInfo {
protected final String subNote = "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
protected static final String SUB_NOTE = "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
protected final List<String> notes;
protected final long now;
@ -109,7 +109,7 @@ public abstract class AbstractHealthInfo {
StringBuilder remainNote = new StringBuilder();
if (activeFWAGNum != 0) {
remainNote.append(subNote);
remainNote.append(SUB_NOTE);
if (percRemain > 0.5) {
remainNote.append(Icons.GREEN_THUMB);
} else if (percRemain > 0.2) {

View File

@ -124,7 +124,7 @@ public class HealthInformation extends AbstractHealthInfo {
double aboveThreshold = tpsMutator.percentageTPSAboveThreshold(lowTPSThreshold);
long tpsSpikeMonth = analysisContainer.getValue(AnalysisKeys.TPS_SPIKE_MONTH).orElse(0);
StringBuilder avgLowThresholdString = new StringBuilder(subNote);
StringBuilder avgLowThresholdString = new StringBuilder(SUB_NOTE);
if (aboveThreshold >= 0.96) {
avgLowThresholdString.append(Icons.GREEN_THUMB);
} else if (aboveThreshold >= 0.9) {

View File

@ -112,7 +112,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo {
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH);
Server server = c.getUnsafe(serverKey);
return subNote + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
return SUB_NOTE + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon + " " + decimalFormatter.apply(average) + uniquePlayersNote + subNotes.toString());
@ -139,7 +139,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo {
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH);
Server server = c.getUnsafe(serverKey);
return subNote + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
return SUB_NOTE + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon + " " + decimalFormatter.apply(average) + newPlayersNote + subNotes.toString());
@ -179,7 +179,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo {
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.PLAYERS_MONTH);
Server server = c.getUnsafe(serverKey);
return subNote + (playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
return SUB_NOTE + (playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon.toHtml() + " " + uniquePlayersNote + subNotes.toString());

View File

@ -92,7 +92,7 @@ public class H2DB extends SQLDB {
String password = config.get(DatabaseSettings.MYSQL_PASS);
JdbcDataSource jdbcDataSource = new JdbcDataSource();
jdbcDataSource.setURL("jdbc:h2:file:" + dbFilePath + ";mode=MySQL");
jdbcDataSource.setURL("jdbc:h2:file:" + dbFilePath + ";mode=MySQL;DATABASE_TO_UPPER=false");
jdbcDataSource.setUser(username);
jdbcDataSource.setPassword(password);

View File

@ -115,6 +115,10 @@ public abstract class SQLDB extends AbstractDatabase {
transactionExecutor.shutdown();
try {
Long waitMs = config.getOrDefault(TimeSettings.DB_TRANSACTION_FINISH_WAIT_DELAY, TimeUnit.SECONDS.toMillis(20L));
if (waitMs > TimeUnit.MINUTES.toMillis(5L)) {
logger.warn(TimeSettings.DB_TRANSACTION_FINISH_WAIT_DELAY.getPath() + " was set to over 5 minutes, using 5 min instead.");
waitMs = TimeUnit.MINUTES.toMillis(5L);
}
if (!transactionExecutor.awaitTermination(waitMs, TimeUnit.MILLISECONDS)) {
List<Runnable> unfinished = transactionExecutor.shutdownNow();
int unfinishedCount = unfinished.size();
@ -149,9 +153,9 @@ public abstract class SQLDB extends AbstractDatabase {
new UserInfoOptimizationPatch(),
new GeoInfoOptimizationPatch(),
new TransferTableRemovalPatch(),
new IPHashPatch(),
new IPAnonPatch(),
new BadAFKThresholdValuePatch()
new BadAFKThresholdValuePatch(),
new DeleteIPHashesPatch()
};
}

View File

@ -27,6 +27,7 @@ import com.djrapitops.plan.db.access.ExecBatchStatement;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.sql.tables.*;
import com.djrapitops.plugin.utilities.Verify;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@ -88,7 +89,7 @@ public class DataStoreQueries {
* @throws IllegalArgumentException If {@link Session#endSession(long)} has not yet been called.
*/
public static Executable storeSession(Session session) {
session.getValue(SessionKeys.END).orElseThrow(() -> new IllegalArgumentException("Attempted to save a session that has not ended."));
Verify.isTrue(session.supports(SessionKeys.END), () -> new IllegalArgumentException("Attempted to save a session that has not ended."));
return connection -> {
storeSessionInformation(session).execute(connection);
storeSessionKills(session).execute(connection);
@ -164,8 +165,7 @@ public class DataStoreQueries {
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, geoInfo.getDate());
statement.setString(2, playerUUID.toString());
statement.setString(3, geoInfo.getIpHash());
statement.setString(4, geoInfo.getGeolocation());
statement.setString(3, geoInfo.getGeolocation());
}
};
}
@ -176,9 +176,8 @@ public class DataStoreQueries {
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
statement.setString(2, geoInfo.getIp());
statement.setString(3, geoInfo.getIpHash());
statement.setString(4, geoInfo.getGeolocation());
statement.setLong(5, geoInfo.getDate());
statement.setString(3, geoInfo.getGeolocation());
statement.setLong(4, geoInfo.getDate());
}
};
}

View File

@ -97,15 +97,13 @@ public class LargeStoreQueries {
// Every GeoInfo
for (GeoInfo info : playerEntry.getValue()) {
String ip = info.getIp();
String ipHash = info.getIpHash();
String geoLocation = info.getGeolocation();
long lastUsed = info.getDate();
statement.setString(1, playerUUID.toString());
statement.setString(2, ip);
statement.setString(3, ipHash);
statement.setString(4, geoLocation);
statement.setLong(5, lastUsed);
statement.setString(3, geoLocation);
statement.setLong(4, lastUsed);
statement.addBatch();
}

View File

@ -0,0 +1,82 @@
/*
* 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 GNU 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
* GNU 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.db.access.queries.containers;
import com.djrapitops.plan.data.container.BaseUser;
import com.djrapitops.plan.data.container.GeoInfo;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.queries.objects.BaseUserQueries;
import com.djrapitops.plan.db.access.queries.objects.GeoInfoQueries;
import com.djrapitops.plan.db.access.queries.objects.SessionQueries;
import com.djrapitops.plan.db.access.queries.objects.UserInfoQueries;
import java.util.*;
/**
* Optimized version of {@link ServerPlayerContainersQuery} for /server page Players table.
*
* @author Rsl1122
* @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For what needs to be included.
*/
public class ServerPlayersTableContainersQuery implements Query<List<PlayerContainer>> {
private final UUID serverUUID;
public ServerPlayersTableContainersQuery(UUID serverUUID) {
this.serverUUID = serverUUID;
}
@Override
public List<PlayerContainer> executeQuery(SQLDB db) {
List<PlayerContainer> containers = new ArrayList<>();
Collection<BaseUser> baseUsers = db.query(BaseUserQueries.fetchServerBaseUsers(serverUUID));
Map<UUID, List<GeoInfo>> geoInformation = db.query(GeoInfoQueries.fetchServerGeoInformation(serverUUID));
Map<UUID, List<Session>> sessions = db.query(SessionQueries.fetchSessionsOfServer(serverUUID));
Set<UUID> bannedUsers = db.query(UserInfoQueries.fetchBannedUUIDsOfServer(serverUUID));
for (BaseUser user : baseUsers) {
PlayerContainer container = new PlayerContainer();
// BaseUser
UUID uuid = user.getUuid();
container.putRawData(PlayerKeys.UUID, uuid);
container.putRawData(PlayerKeys.NAME, user.getName());
container.putRawData(PlayerKeys.REGISTERED, user.getRegistered());
container.putRawData(PlayerKeys.BANNED, bannedUsers.contains(uuid));
// GeoInfo
container.putRawData(PlayerKeys.GEO_INFO, geoInformation.getOrDefault(uuid, new ArrayList<>()));
container.putCachingSupplier(PlayerKeys.SESSIONS, () -> {
List<Session> playerSessions = sessions.getOrDefault(uuid, new ArrayList<>());
container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add);
return playerSessions;
}
);
containers.add(container);
}
return containers;
}
}

View File

@ -49,7 +49,6 @@ public class GeoInfoQueries {
GeoInfoTable.IP + ", " +
GeoInfoTable.GEOLOCATION + ", " +
GeoInfoTable.LAST_USED + ", " +
GeoInfoTable.IP_HASH + ", " +
GeoInfoTable.USER_UUID +
" FROM " + GeoInfoTable.TABLE_NAME;
@ -64,9 +63,8 @@ public class GeoInfoQueries {
String ip = set.getString(GeoInfoTable.IP);
String geolocation = set.getString(GeoInfoTable.GEOLOCATION);
String ipHash = set.getString(GeoInfoTable.IP_HASH);
long lastUsed = set.getLong(GeoInfoTable.LAST_USED);
userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash));
userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed));
geoInformation.put(uuid, userGeoInfo);
}
@ -97,9 +95,8 @@ public class GeoInfoQueries {
while (set.next()) {
String ip = set.getString(GeoInfoTable.IP);
String geolocation = set.getString(GeoInfoTable.GEOLOCATION);
String ipHash = set.getString(GeoInfoTable.IP_HASH);
long lastUsed = set.getLong(GeoInfoTable.LAST_USED);
geoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash));
geoInfo.add(new GeoInfo(ip, geolocation, lastUsed));
}
return geoInfo;
}
@ -110,8 +107,7 @@ public class GeoInfoQueries {
String sql = "SELECT " + GeoInfoTable.TABLE_NAME + "." + GeoInfoTable.USER_UUID + ", " +
GeoInfoTable.GEOLOCATION + ", " +
GeoInfoTable.LAST_USED + ", " +
GeoInfoTable.IP + ", " +
GeoInfoTable.IP_HASH +
GeoInfoTable.IP +
" FROM " + GeoInfoTable.TABLE_NAME +
" INNER JOIN " + UserInfoTable.TABLE_NAME + " on " +
GeoInfoTable.TABLE_NAME + "." + GeoInfoTable.USER_UUID + "=" + UserInfoTable.TABLE_NAME + "." + UserInfoTable.USER_UUID +
@ -132,9 +128,8 @@ public class GeoInfoQueries {
String ip = set.getString(GeoInfoTable.IP);
String geolocation = set.getString(GeoInfoTable.GEOLOCATION);
String ipHash = set.getString(GeoInfoTable.IP_HASH);
long lastUsed = set.getLong(GeoInfoTable.LAST_USED);
userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash));
userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed));
geoInformation.put(uuid, userGeoInfo);
}

View File

@ -114,6 +114,12 @@ public class UserInfoQueries {
};
}
/**
* Query database for all User information of a specific server.
*
* @param serverUUID UUID of the Plan server.
* @return Map: Player UUID - user information
*/
public static Query<Map<UUID, UserInfo>> fetchUserInformationOfServer(UUID serverUUID) {
String sql = SELECT +
UserInfoTable.REGISTERED + ", " +
@ -146,4 +152,34 @@ public class UserInfoQueries {
}
};
}
/**
* Query database for UUIDs of banned players on a server.
*
* @param serverUUID UUID of the Plan server.
* @return Set: Player UUID of a banned player.
*/
public static Query<Set<UUID>> fetchBannedUUIDsOfServer(UUID serverUUID) {
String sql = SELECT +
UserInfoTable.USER_UUID +
FROM + UserInfoTable.TABLE_NAME +
WHERE + UserInfoTable.SERVER_UUID + "=?" +
AND + UserInfoTable.BANNED + "=?";
return new QueryStatement<Set<UUID>>(sql, 1000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
statement.setBoolean(2, true);
}
@Override
public Set<UUID> processResults(ResultSet set) throws SQLException {
Set<UUID> bannedUsers = new HashSet<>();
while (set.next()) {
bannedUsers.add(UUID.fromString(set.getString(UserInfoTable.USER_UUID)));
}
return bannedUsers;
}
};
}
}

View File

@ -157,4 +157,8 @@ public abstract class Transaction {
public String toString() {
return getClass().getSimpleName() + (success ? " (finished)" : "");
}
public boolean wasSuccessful() {
return success;
}
}

View File

@ -47,6 +47,9 @@ public class RemoveEverythingTransaction extends Transaction {
clearTable(ExtensionPlayerValueTable.TABLE_NAME);
clearTable(ExtensionServerValueTable.TABLE_NAME);
clearTable(ExtensionProviderTable.TABLE_NAME);
clearTable(ExtensionPlayerTableValueTable.TABLE_NAME);
clearTable(ExtensionServerTableValueTable.TABLE_NAME);
clearTable(ExtensionTableProviderTable.TABLE_NAME);
clearTable(ExtensionTabTable.TABLE_NAME);
clearTable(ExtensionPluginTable.TABLE_NAME);
clearTable(ExtensionIconTable.TABLE_NAME);

View File

@ -53,5 +53,8 @@ public class CreateTablesTransaction extends OperationCriticalTransaction {
execute(ExtensionProviderTable.createTableSQL(dbType));
execute(ExtensionPlayerValueTable.createTableSQL(dbType));
execute(ExtensionServerValueTable.createTableSQL(dbType));
execute(ExtensionTableProviderTable.createTableSQL(dbType));
execute(ExtensionPlayerTableValueTable.createTableSQL(dbType));
execute(ExtensionServerTableValueTable.createTableSQL(dbType));
}
}

View File

@ -23,7 +23,6 @@ import com.djrapitops.plan.db.access.queries.objects.TPSQueries;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.PingTable;
import com.djrapitops.plan.db.sql.tables.TPSTable;
import com.djrapitops.plugin.api.TimeAmount;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@ -38,11 +37,17 @@ import java.util.UUID;
public class RemoveOldSampledDataTransaction extends Transaction {
private final UUID serverUUID;
private final long deleteTPSOlderThanMs;
private final long deletePingOlderThanMs;
public RemoveOldSampledDataTransaction(
UUID serverUUID
UUID serverUUID,
long deleteTPSOlderThanMs,
long deletePingOlderThanMs
) {
this.serverUUID = serverUUID;
this.deleteTPSOlderThanMs = deleteTPSOlderThanMs;
this.deletePingOlderThanMs = deletePingOlderThanMs;
}
@Override
@ -61,9 +66,7 @@ public class RemoveOldSampledDataTransaction extends Transaction {
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
// More than 3 Months ago.
long threeMonths = TimeAmount.MONTH.toMillis(3L);
statement.setLong(1, System.currentTimeMillis() - threeMonths);
statement.setLong(1, System.currentTimeMillis() - deleteTPSOlderThanMs);
statement.setInt(2, allTimePlayerPeak);
}
};
@ -77,8 +80,7 @@ public class RemoveOldSampledDataTransaction extends Transaction {
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
long twoWeeks = TimeAmount.WEEK.toMillis(2L);
statement.setLong(1, System.currentTimeMillis() - twoWeeks);
statement.setLong(1, System.currentTimeMillis() - deletePingOlderThanMs);
}
};
}

View File

@ -0,0 +1,73 @@
/*
* 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 GNU 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
* GNU 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.db.patches;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement;
import com.djrapitops.plan.db.sql.tables.GeoInfoTable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Patch for removing ip_hash values from plan_ips table.
* <p>
* The patch is a response to a concern:
* "Hashed IP addresses are pseudonymised not anonymised and can be easily decoded using a rainbow table".
*
* @author Rsl1122
*/
public class DeleteIPHashesPatch extends Patch {
private static final String IP_HASH = "ip_hash";
private boolean hasNoHashColumn;
@Override
public boolean hasBeenApplied() {
hasNoHashColumn = !hasColumn(GeoInfoTable.TABLE_NAME, IP_HASH);
String sql = SELECT + "COUNT(1) as c" + FROM + GeoInfoTable.TABLE_NAME +
WHERE + IP_HASH + IS_NOT_NULL;
return hasNoHashColumn || !query(new HasMoreThanZeroQueryStatement(sql) {
@Override
public void prepare(PreparedStatement statement) {
/* No variables needed */
}
});
}
@Override
protected void applyPatch() {
if (hasNoHashColumn) {
return;
}
String sql = "UPDATE " + GeoInfoTable.TABLE_NAME + " SET ip_hash=?" + WHERE + IP_HASH + IS_NOT_NULL;
execute(new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setNull(1, Types.VARCHAR);
}
});
}
}

View File

@ -44,13 +44,11 @@ public class GeoInfoOptimizationPatch extends Patch {
execute("INSERT INTO " + tableName + " (" +
GeoInfoTable.USER_UUID + ", " +
GeoInfoTable.IP + ", " +
GeoInfoTable.IP_HASH + ", " +
GeoInfoTable.LAST_USED + ", " +
GeoInfoTable.GEOLOCATION +
") SELECT " +
"(SELECT plan_users.uuid FROM plan_users WHERE plan_users.id = " + tempTableName + ".user_id LIMIT 1), " +
GeoInfoTable.IP + ", " +
GeoInfoTable.IP_HASH + ", " +
GeoInfoTable.LAST_USED + ", " +
GeoInfoTable.GEOLOCATION +
" FROM " + tempTableName

View File

@ -24,7 +24,6 @@ import com.djrapitops.plan.db.sql.tables.GeoInfoTable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -73,8 +72,7 @@ public class IPAnonPatch extends Patch {
private void anonymizeIPs(Map<UUID, List<GeoInfo>> allGeoInfo) {
String sql = "UPDATE " + GeoInfoTable.TABLE_NAME + " SET " +
GeoInfoTable.IP + "=?, " +
GeoInfoTable.IP_HASH + "=? " +
GeoInfoTable.IP + "=? " +
"WHERE " + GeoInfoTable.IP + "=?";
execute(new ExecBatchStatement(sql) {
@ -99,11 +97,10 @@ public class IPAnonPatch extends Patch {
geoInfo.getDate()
);
statement.setString(1, updatedInfo.getIp());
statement.setString(2, updatedInfo.getIpHash());
statement.setString(3, geoInfo.getIp());
statement.setString(2, geoInfo.getIp());
statement.addBatch();
} catch (UnknownHostException | NoSuchAlgorithmException ignore) {
// This ip is already anonymised or completely unusable.
} catch (UnknownHostException ignore) {
// This ip is completely unusable.
}
}
});
@ -120,11 +117,11 @@ public class IPAnonPatch extends Patch {
String identifiers = hasUserIdColumn ? userIdColumn : "id, uuid";
execute("INSERT INTO plan_ips (" +
identifiers + ", ip, ip_hash, geolocation, last_used" +
identifiers + ", ip, geolocation, last_used" +
") SELECT " +
identifiers + ", ip, ip_hash, geolocation, MAX(last_used) FROM plan_ips_temp GROUP BY ip_hash, " +
identifiers + ", ip, geolocation, MAX(last_used) FROM plan_ips_temp GROUP BY ip, " +
(hasUserIdColumn ? userIdColumn : "uuid") +
", ip, geolocation");
", geolocation");
dropTable(tempTableName);
}

View File

@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
@ -54,9 +55,15 @@ public class ExtensionIconTable {
}
public static void set3IconValuesToStatement(PreparedStatement statement, int parameterIndex, Icon icon) throws SQLException {
statement.setString(parameterIndex, StringUtils.truncate(icon.getName(), 50));
statement.setString(parameterIndex + 1, icon.getFamily().name());
statement.setString(parameterIndex + 2, icon.getColor().name());
if (icon != null) {
statement.setString(parameterIndex, StringUtils.truncate(icon.getName(), 50));
statement.setString(parameterIndex + 1, icon.getFamily().name());
statement.setString(parameterIndex + 2, icon.getColor().name());
} else {
statement.setNull(parameterIndex, Types.VARCHAR);
statement.setNull(parameterIndex + 1, Types.VARCHAR);
statement.setNull(parameterIndex + 2, Types.VARCHAR);
}
}
private ExtensionIconTable() {

View File

@ -0,0 +1,60 @@
/*
* 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 GNU 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
* GNU 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.db.sql.tables;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
import com.djrapitops.plan.db.sql.parsing.Sql;
import static com.djrapitops.plan.db.sql.parsing.Sql.INT;
/**
* Table information about 'plan_extension_user_table_values'.
*
* @author Rsl1122
*/
public class ExtensionPlayerTableValueTable {
public static final String TABLE_NAME = "plan_extension_user_table_values";
public static final String ID = "id";
public static final String TABLE_ID = "table_id";
public static final String USER_UUID = "uuid";
// All values can be null
public static final String VALUE_1 = "col_1_value";
public static final String VALUE_2 = "col_2_value";
public static final String VALUE_3 = "col_3_value";
public static final String VALUE_4 = "col_4_value";
private ExtensionPlayerTableValueTable() {
/* Static information class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableParser.create(TABLE_NAME, dbType)
.column(ID, INT).primaryKey()
.column(USER_UUID, Sql.varchar(36)).notNull()
.column(VALUE_1, Sql.varchar(50))
.column(VALUE_2, Sql.varchar(50))
.column(VALUE_3, Sql.varchar(50))
.column(VALUE_4, Sql.varchar(50))
.column(TABLE_ID, INT).notNull()
.foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID)
.build();
}
}

View File

@ -37,7 +37,7 @@ public class ExtensionProviderTable {
public static final String ID = "id";
public static final String PROVIDER_NAME = "name";
public static final String TEXT = "text"; // Can be null
public static final String TEXT = "text";
public static final String DESCRIPTION = "description"; // Can be null
public static final String PRIORITY = "priority";
public static final String GROUPABLE = "groupable"; // default false

View File

@ -0,0 +1,62 @@
/*
* 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 GNU 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
* GNU 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.db.sql.tables;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
import com.djrapitops.plan.db.sql.parsing.Sql;
import static com.djrapitops.plan.db.sql.parsing.Sql.INT;
/**
* Table information about 'plan_extension_server_table_values'.
*
* @author Rsl1122
*/
public class ExtensionServerTableValueTable {
public static final String TABLE_NAME = "plan_extension_server_table_values";
public static final String ID = "id";
public static final String TABLE_ID = "table_id";
public static final String SERVER_UUID = "uuid";
// All values can be null
public static final String VALUE_1 = "col_1_value";
public static final String VALUE_2 = "col_2_value";
public static final String VALUE_3 = "col_3_value";
public static final String VALUE_4 = "col_4_value";
public static final String VALUE_5 = "col_5_value";
private ExtensionServerTableValueTable() {
/* Static information class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableParser.create(TABLE_NAME, dbType)
.column(ID, INT).primaryKey()
.column(SERVER_UUID, Sql.varchar(36)).notNull()
.column(VALUE_1, Sql.varchar(50))
.column(VALUE_2, Sql.varchar(50))
.column(VALUE_3, Sql.varchar(50))
.column(VALUE_4, Sql.varchar(50))
.column(VALUE_5, Sql.varchar(50))
.column(TABLE_ID, INT).notNull()
.foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID)
.build();
}
}

View File

@ -0,0 +1,100 @@
/*
* 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 GNU 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
* GNU 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.db.sql.tables;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
import com.djrapitops.plan.db.sql.parsing.Sql;
import com.djrapitops.plan.extension.icon.Color;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Table information about 'plan_extension_tables'.
*
* @author Rsl1122
*/
public class ExtensionTableProviderTable {
public static final String TABLE_NAME = "plan_extension_tables";
public static final String ID = "id";
public static final String PROVIDER_NAME = "name";
public static final String COLOR = "color";
public static final String CONDITION = "condition_name"; // Can be null, related to @Conditional
public static final String PLUGIN_ID = "plugin_id";
public static final String TAB_ID = "tab_id"; // Can be null, related to @Tab
// All columns can be null
public static final String COL_1 = "col_1_name";
public static final String COL_2 = "col_2_name";
public static final String COL_3 = "col_3_name";
public static final String COL_4 = "col_4_name";
public static final String COL_5 = "col_5_name";
// All icons can be null
public static final String ICON_1_ID = "icon_1_id";
public static final String ICON_2_ID = "icon_2_id";
public static final String ICON_3_ID = "icon_3_id";
public static final String ICON_4_ID = "icon_4_id";
public static final String ICON_5_ID = "icon_5_id";
public static final String STATEMENT_SELECT_TABLE_ID = "(" + SELECT + ID + FROM + TABLE_NAME +
WHERE + PROVIDER_NAME + "=?" +
AND + PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")";
private ExtensionTableProviderTable() {
/* Static information class */
}
public static void set3PluginValuesToStatement(PreparedStatement statement, int parameterIndex, String providerName, String pluginName, UUID serverUUID) throws SQLException {
statement.setString(parameterIndex, providerName);
ExtensionPluginTable.set2PluginValuesToStatement(statement, parameterIndex + 1, pluginName, serverUUID);
}
public static String createTableSQL(DBType dbType) {
return CreateTableParser.create(TABLE_NAME, dbType)
.column(ID, INT).primaryKey()
.column(PROVIDER_NAME, Sql.varchar(50)).notNull()
.column(COLOR, Sql.varchar(25)).notNull().defaultValue("'" + Color.NONE.name() + "'")
.column(CONDITION, Sql.varchar(54)) // 50 + 4 for "not_"
.column(COL_1, Sql.varchar(50))
.column(COL_2, Sql.varchar(50))
.column(COL_3, Sql.varchar(50))
.column(COL_4, Sql.varchar(50))
.column(COL_5, Sql.varchar(50))
.column(PLUGIN_ID, INT).notNull()
.column(ICON_1_ID, INT)
.column(ICON_2_ID, INT)
.column(ICON_3_ID, INT)
.column(ICON_4_ID, INT)
.column(ICON_5_ID, INT)
.column(TAB_ID, INT)
.foreignKey(PLUGIN_ID, ExtensionPluginTable.TABLE_NAME, ExtensionPluginTable.ID)
.foreignKey(ICON_1_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(ICON_2_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(ICON_3_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(ICON_4_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(ICON_5_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(TAB_ID, ExtensionTabTable.TABLE_NAME, ExtensionTabTable.ID)
.build();
}
}

View File

@ -28,8 +28,8 @@ import com.djrapitops.plan.db.sql.parsing.Sql;
* {@link Version10Patch}
* {@link GeoInfoLastUsedPatch}
* {@link IPAnonPatch}
* {@link IPHashPatch}
* {@link GeoInfoOptimizationPatch}
* {@link DeleteIPHashesPatch}
*
* @author Rsl1122
*/
@ -40,22 +40,19 @@ public class GeoInfoTable {
public static final String ID = "id";
public static final String USER_UUID = "uuid";
public static final String IP = "ip";
public static final String IP_HASH = "ip_hash";
public static final String GEOLOCATION = "geolocation";
public static final String LAST_USED = "last_used";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " ("
+ USER_UUID + ", "
+ IP + ", "
+ IP_HASH + ", "
+ GEOLOCATION + ", "
+ LAST_USED
+ ") VALUES (?, ?, ?, ?, ?)";
+ ") VALUES (?, ?, ?, ?)";
public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET "
+ LAST_USED + "=?" +
" WHERE " + USER_UUID + "=?" +
" AND " + IP_HASH + "=?" +
" AND " + GEOLOCATION + "=?";
private GeoInfoTable() {
@ -68,7 +65,6 @@ public class GeoInfoTable {
.column(USER_UUID, Sql.varchar(36)).notNull()
.column(IP, Sql.varchar(39)).notNull()
.column(GEOLOCATION, Sql.varchar(50)).notNull()
.column(IP_HASH, Sql.varchar(200))
.column(LAST_USED, Sql.LONG).notNull().defaultValue("0")
.toString();
}

View File

@ -24,7 +24,8 @@ import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransacti
import com.djrapitops.plan.db.access.transactions.init.RemoveDuplicateUserInfoTransaction;
import com.djrapitops.plan.db.access.transactions.init.RemoveOldSampledDataTransaction;
import com.djrapitops.plan.db.sql.tables.SessionsTable;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalResultsTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalPlayerResultsTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalServerResultsTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
@ -84,9 +85,14 @@ public class DBCleanTask extends AbsRunnable {
Database database = dbSystem.getDatabase();
try {
if (database.getState() != Database.State.CLOSED) {
database.executeTransaction(new RemoveOldSampledDataTransaction(serverInfo.getServerUUID()));
database.executeTransaction(new RemoveOldSampledDataTransaction(
serverInfo.getServerUUID(),
config.get(TimeSettings.DELETE_TPS_DATA_AFTER),
config.get(TimeSettings.DELETE_PING_DATA_AFTER)
));
database.executeTransaction(new RemoveDuplicateUserInfoTransaction());
database.executeTransaction(new RemoveUnsatisfiedConditionalResultsTransaction());
database.executeTransaction(new RemoveUnsatisfiedConditionalPlayerResultsTransaction());
database.executeTransaction(new RemoveUnsatisfiedConditionalServerResultsTransaction());
int removed = cleanOldPlayers(database);
if (removed > 0) {
logger.info(locale.getString(PluginLang.DB_NOTIFY_CLEAN, removed));
@ -101,7 +107,7 @@ public class DBCleanTask extends AbsRunnable {
@VisibleForTesting
public int cleanOldPlayers(Database database) {
long now = System.currentTimeMillis();
long keepActiveAfter = now - config.get(TimeSettings.KEEP_INACTIVE_PLAYERS);
long keepActiveAfter = now - config.get(TimeSettings.DELETE_INACTIVE_PLAYERS_AFTER);
List<UUID> inactivePlayers = database.query(fetchInactivePlayerUUIDs(keepActiveAfter));
for (UUID uuid : inactivePlayers) {

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension;
import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.data.plugin.PluginsConfigSection;
import com.djrapitops.plan.extension.implementation.CallerImplementation;
import com.djrapitops.plan.extension.implementation.DataProviderExtractor;
@ -94,11 +95,11 @@ public class ExtensionServiceImplementation implements ExtensionService {
logger.warn("DataExtension API implementation mistake for " + pluginName + ": " + warning);
}
ProviderValueGatherer gatherer = new ProviderValueGatherer(extension, extractor, dbSystem, serverInfo, logger);
ProviderValueGatherer gatherer = new ProviderValueGatherer(extension, extractor, dbSystem, serverInfo);
gatherer.storeExtensionInformation();
extensionGatherers.put(pluginName, gatherer);
updateServerValues(gatherer, CallEvents.SERVER_EXTENSION_REGISTER);
processing.submitNonCritical(() -> updateServerValues(gatherer, CallEvents.SERVER_EXTENSION_REGISTER));
logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, pluginName + " extension registered.");
return Optional.of(new CallerImplementation(gatherer, this, processing));
@ -111,7 +112,6 @@ public class ExtensionServiceImplementation implements ExtensionService {
if (extensionGatherers.remove(pluginName) != null) {
logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, pluginName + " extension unregistered.");
}
}
private boolean shouldNotAllowRegistration(String pluginName) {
@ -150,16 +150,29 @@ public class ExtensionServiceImplementation implements ExtensionService {
gatherer.updateValues(playerUUID, playerName);
logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, "Gathering completed: " + playerName);
} catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
logger.warn(gatherer.getPluginName() + " ran into (but failed safely) " + e.getClass().getSimpleName() +
" when updating value for '" + playerName +
"', (You can disable integration with setting 'Plugins." + gatherer.getPluginName() + ".Enabled')" +
" reason: '" + e.getMessage() +
} catch (DataExtensionMethodCallException methodCallFailed) {
logFailure(playerName, methodCallFailed);
gatherer.disableMethodFromUse(methodCallFailed.getMethod());
// Try again
updatePlayerValues(gatherer, playerUUID, playerName, event);
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) {
logger.warn(gatherer.getPluginName() + " ran into unexpected error (please report this)" + unexpectedError +
" (but failed safely) when updating value for '" + playerName +
"', stack trace to follow:");
errorHandler.log(L.WARN, gatherer.getClass(), e);
errorHandler.log(L.WARN, gatherer.getClass(), unexpectedError);
}
}
private void logFailure(String playerName, DataExtensionMethodCallException methodCallFailed) {
Throwable cause = methodCallFailed.getCause();
String causeName = cause.getClass().getSimpleName();
logger.warn(methodCallFailed.getPluginName() + " ran into " + causeName +
" (but failed safely) when updating value for '" + playerName +
"', the method was disabled temporarily (won't be called until next Plan reload)" +
", stack trace to follow:");
errorHandler.log(L.WARN, getClass(), cause);
}
public void updateServerValues(CallEvents event) {
for (ProviderValueGatherer gatherer : extensionGatherers.values()) {
updateServerValues(gatherer, event);
@ -176,13 +189,15 @@ public class ExtensionServiceImplementation implements ExtensionService {
gatherer.updateValues();
logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, "Gathering completed for server");
} catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
logger.warn(gatherer.getPluginName() + " ran into (but failed safely) " + e.getClass().getSimpleName() +
" when updating value for server" +
", (You can disable integration with setting 'Plugins." + gatherer.getPluginName() + ".Enabled')" +
" reason: '" + e.getMessage() +
"', stack trace to follow:");
errorHandler.log(L.WARN, gatherer.getClass(), e);
} catch (DataExtensionMethodCallException methodCallFailed) {
logFailure("server", methodCallFailed);
gatherer.disableMethodFromUse(methodCallFailed.getMethod());
// Try again
updateServerValues(gatherer, event);
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) {
logger.warn(gatherer.getPluginName() + " ran into unexpected error (please report this)" + unexpectedError +
" (but failed safely) when updating value for server, stack trace to follow:");
errorHandler.log(L.WARN, gatherer.getClass(), unexpectedError);
}
}
}

View File

@ -47,7 +47,7 @@ public class CallerImplementation implements Caller {
}
@Override
public void updatePlayerData(UUID playerUUID, String playerName) throws IllegalArgumentException {
public void updatePlayerData(UUID playerUUID, String playerName) {
Verify.nullCheck(playerUUID, () -> new IllegalArgumentException("'playerUUID' can not be null!"));
Verify.nullCheck(playerName, () -> new IllegalArgumentException("'playerName' can not be null!"));
processing.submitNonCritical(() -> extensionServiceImplementation.updatePlayerValues(gatherer, playerUUID, playerName, CallEvents.MANUAL));

View File

@ -124,6 +124,7 @@ public class DataProviderExtractor {
extractDataProviders(pluginInfo, tabs, conditions, PercentageProvider.class, PercentageDataProvider::placeToDataProviders);
extractDataProviders(pluginInfo, tabs, conditions, NumberProvider.class, NumberDataProvider::placeToDataProviders);
extractDataProviders(pluginInfo, tabs, conditions, StringProvider.class, StringDataProvider::placeToDataProviders);
extractDataProviders(pluginInfo, tabs, conditions, TableProvider.class, TableDataProvider::placeToDataProviders);
}
private <T extends Annotation> void extractDataProviders(PluginInfo pluginInfo, Map<Method, Tab> tabs, Map<Method, Conditional> conditions, Class<T> ofKind, DataProviderFactory<T> factory) {

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive;
import org.apache.commons.lang3.StringUtils;
import java.util.Objects;
import java.util.Optional;
/**
@ -48,10 +49,38 @@ public class ProviderInformation extends ExtensionDescriptive {
}
public Optional<String> getTab() {
return tab == null || tab.isEmpty() ? Optional.empty() : Optional.of(StringUtils.truncate(tab, 50));
return tab == null || tab.isEmpty()
? Optional.empty()
: Optional.of(StringUtils.truncate(tab, 50));
}
public Optional<String> getCondition() {
return condition == null || condition.value().isEmpty() ? Optional.empty() : Optional.of((condition.negated() ? "not_" : "") + StringUtils.truncate(condition.value(), 50));
if (condition == null || condition.value().isEmpty()) {
return Optional.empty();
} else if (condition.negated()) {
return Optional.of("not_" + getTruncatedConditionName());
} else {
return Optional.of(getTruncatedConditionName());
}
}
private String getTruncatedConditionName() {
return StringUtils.truncate(condition.value(), 50);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ProviderInformation)) return false;
if (!super.equals(o)) return false;
ProviderInformation that = (ProviderInformation) o;
return pluginName.equals(that.pluginName) &&
Objects.equals(tab, that.tab) &&
Objects.equals(condition, that.condition);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), pluginName, tab, condition);
}
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.extension.implementation.providers;
import com.djrapitops.plan.extension.implementation.MethodType;
import java.util.*;
import java.util.stream.Collectors;
/**
* Group class for handling multiple different types of {@link DataProvider}s.
@ -77,4 +78,24 @@ public class DataProviders {
}
return byReturnType;
}
public void removeProviderWithMethod(MethodWrapper method) {
MethodType methodType = method.getMethodType();
Map<Class, List<DataProvider>> byResultType = byMethodType.getOrDefault(methodType, Collections.emptyMap());
if (byResultType.isEmpty()) {
return;
}
Class resultType = method.getResultType();
List<DataProvider> providers = byResultType.getOrDefault(resultType, Collections.emptyList());
if (providers.isEmpty()) {
return;
}
byResultType.put(resultType, providers.stream()
.filter(provider -> provider.getMethod().equals(method))
.collect(Collectors.toList())
);
byMethodType.put(methodType, byResultType);
}
}

View File

@ -20,8 +20,10 @@ import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.Group;
import com.djrapitops.plan.extension.implementation.MethodType;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.UUID;
/**
@ -29,7 +31,7 @@ import java.util.UUID;
*
* @author Rsl1122
*/
public class MethodWrapper<T> {
public class MethodWrapper<T> implements Serializable {
private final Method method;
private final Class<T> resultType;
@ -41,39 +43,46 @@ public class MethodWrapper<T> {
methodType = MethodType.forMethod(this.method);
}
public T callMethod(DataExtension extension, UUID playerUUID, String playerName) throws InvocationTargetException, IllegalAccessException {
public T callMethod(DataExtension extension, UUID playerUUID, String playerName) {
if (methodType != MethodType.PLAYER_NAME && methodType != MethodType.PLAYER_UUID) {
throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not GROUP method.");
}
return callMethod(extension, playerUUID, playerName, null);
}
public T callMethod(DataExtension extension, Group group) throws InvocationTargetException, IllegalAccessException {
public T callMethod(DataExtension extension, Group group) {
if (methodType != MethodType.GROUP) {
throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not GROUP method.");
}
return callMethod(extension, null, null, group);
}
public T callMethod(DataExtension extension) throws InvocationTargetException, IllegalAccessException {
public T callMethod(DataExtension extension) {
if (methodType != MethodType.SERVER) {
throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not SERVER method.");
}
return callMethod(extension, null, null, null);
}
public T callMethod(DataExtension extension, UUID playerUUID, String playerName, Group group) throws InvocationTargetException, IllegalAccessException {
switch (methodType) {
case SERVER:
return resultType.cast(method.invoke(extension));
case PLAYER_UUID:
return resultType.cast(method.invoke(extension, playerUUID));
case PLAYER_NAME:
return resultType.cast(method.invoke(extension, playerName));
case GROUP:
return resultType.cast(method.invoke(extension, group));
default:
throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters.");
public T callMethod(DataExtension extension, UUID playerUUID, String playerName, Group group) {
try {
switch (methodType) {
case SERVER:
return resultType.cast(method.invoke(extension));
case PLAYER_UUID:
return resultType.cast(method.invoke(extension, playerUUID));
case PLAYER_NAME:
return resultType.cast(method.invoke(extension, playerName));
case GROUP:
return resultType.cast(method.invoke(extension, group));
default:
throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters.");
}
} catch (InvocationTargetException | IllegalAccessException e) {
Throwable cause = e.getCause();
boolean hasCause = cause != null;
throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters; caused " +
(hasCause ? cause.toString() : e.toString()));
}
}
@ -88,4 +97,19 @@ public class MethodWrapper<T> {
public Class<T> getResultType() {
return resultType;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MethodWrapper)) return false;
MethodWrapper<?> that = (MethodWrapper<?>) o;
return method.equals(that.method) &&
resultType.equals(that.resultType) &&
methodType == that.methodType;
}
@Override
public int hashCode() {
return Objects.hash(method, resultType, methodType);
}
}

View File

@ -0,0 +1,69 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.providers;
import com.djrapitops.plan.extension.annotation.Conditional;
import com.djrapitops.plan.extension.annotation.TableProvider;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.extension.table.Table;
import java.lang.reflect.Method;
/**
* Represents a DataExtension API method annotated with {@link com.djrapitops.plan.extension.annotation.TableProvider} annotation.
* <p>
* Used to obtain data to place in the database.
* <p>
* Please note that not all {@link ProviderInformation} is present.
*
* @author Rsl1122
*/
public class TableDataProvider extends DataProvider<Table> {
private final Color tableColor;
private TableDataProvider(ProviderInformation providerInformation, MethodWrapper<Table> methodWrapper, Color tableColor) {
super(providerInformation, methodWrapper);
this.tableColor = tableColor;
}
public static void placeToDataProviders(
DataProviders dataProviders, Method method, TableProvider annotation,
Conditional condition, String tab, String pluginName
) {
MethodWrapper<Table> methodWrapper = new MethodWrapper<>(method, Table.class);
ProviderInformation providerInformation = new ProviderInformation(
pluginName, method.getName(), null, null, null, 0, tab, condition
);
dataProviders.put(new TableDataProvider(providerInformation, methodWrapper, annotation.tableColor()));
}
public static Color getTableColor(DataProvider<Table> provider) {
if (provider instanceof TableDataProvider) {
return ((TableDataProvider) provider).getTableColor();
}
return Color.NONE;
}
public Color getTableColor() {
return tableColor;
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
@ -28,7 +29,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreBooleanProviderTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerBooleanResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerBooleanResultTransaction;
import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.*;
import java.util.concurrent.Callable;
@ -48,20 +48,17 @@ class BooleanProviderValueGatherer {
private final Database database;
private final DataProviders dataProviders;
private final PluginLogger logger;
BooleanProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
DataProviders dataProviders,
PluginLogger logger
DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
this.logger = logger;
}
Conditions gatherBooleanDataOfPlayer(UUID playerUUID, String playerName) {
@ -127,10 +124,7 @@ class BooleanProviderValueGatherer {
boolean hidden = BooleanDataProvider.isHidden(booleanProvider);
MethodWrapper<Boolean> method = booleanProvider.getMethod();
Boolean result = getMethodResult(
methodCaller.apply(method),
throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
);
Boolean result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
// Error during method call
satisfied.add(booleanProvider); // Prevents further attempts to call this provider for this player.
@ -155,13 +149,11 @@ class BooleanProviderValueGatherer {
return satisfied;
}
private <T> T getMethodResult(Callable<T> callable, Function<Throwable, String> errorMsg) {
private <T> T getMethodResult(Callable<T> callable, MethodWrapper<T> method) {
try {
return callable.call();
} catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
logger.warn(errorMsg.apply(e));
return null;
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
throw new DataExtensionMethodCallException(e, pluginName, method);
}
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
@ -30,7 +31,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.results
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerPercentageResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerDoubleResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerPercentageResultTransaction;
import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.Optional;
import java.util.UUID;
@ -51,20 +51,17 @@ class DoubleAndPercentageProviderValueGatherer {
private final Database database;
private final DataProviders dataProviders;
private final PluginLogger logger;
DoubleAndPercentageProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
DataProviders dataProviders,
PluginLogger logger
DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
this.logger = logger;
}
void gatherDoubleDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
@ -105,10 +102,7 @@ class DoubleAndPercentageProviderValueGatherer {
}
MethodWrapper<Double> method = doubleProvider.getMethod();
Double result = getMethodResult(
methodCaller.apply(method),
throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
);
Double result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
return;
}
@ -123,13 +117,11 @@ class DoubleAndPercentageProviderValueGatherer {
}
}
private <T> T getMethodResult(Callable<T> callable, Function<Throwable, String> errorMsg) {
private <T> T getMethodResult(Callable<T> callable, MethodWrapper<T> method) {
try {
return callable.call();
} catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
logger.warn(errorMsg.apply(e));
return null;
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
throw new DataExtensionMethodCallException(e, pluginName, method);
}
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
@ -29,7 +30,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreNumberProviderTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerNumberResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerNumberResultTransaction;
import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.Optional;
import java.util.UUID;
@ -50,20 +50,17 @@ class NumberProviderValueGatherer {
private final Database database;
private final DataProviders dataProviders;
private final PluginLogger logger;
NumberProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
DataProviders dataProviders,
PluginLogger logger
DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
this.logger = logger;
}
void gatherNumberDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
@ -100,10 +97,7 @@ class NumberProviderValueGatherer {
}
MethodWrapper<Long> method = numberProvider.getMethod();
Long result = getMethodResult(
methodCaller.apply(method),
throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
);
Long result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
return;
}
@ -115,12 +109,11 @@ class NumberProviderValueGatherer {
database.executeTransaction(storeTransactionCreator.apply(method, result));
}
private <T> T getMethodResult(Callable<T> callable, Function<Throwable, String> errorMsg) {
private <T> T getMethodResult(Callable<T> callable, MethodWrapper<T> method) {
try {
return callable.call();
} catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
logger.warn(errorMsg.apply(e));
return null;
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
throw new DataExtensionMethodCallException(e, pluginName, method);
}
}
}

View File

@ -22,13 +22,14 @@ import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.DataProviderExtractor;
import com.djrapitops.plan.extension.implementation.TabInformation;
import com.djrapitops.plan.extension.implementation.providers.DataProviders;
import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.StorePluginTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.StoreTabInformationTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveInvalidResultsTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.UUID;
@ -43,43 +44,49 @@ public class ProviderValueGatherer {
private final DataProviderExtractor extractor;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private DataProviders dataProviders;
private BooleanProviderValueGatherer booleanGatherer;
private NumberProviderValueGatherer numberGatherer;
private DoubleAndPercentageProviderValueGatherer doubleAndPercentageGatherer;
private StringProviderValueGatherer stringGatherer;
private TableProviderValueGatherer tableGatherer;
public ProviderValueGatherer(
DataExtension extension,
DataProviderExtractor extractor,
DBSystem dbSystem,
ServerInfo serverInfo,
PluginLogger logger
ServerInfo serverInfo
) {
this.callEvents = extension.callExtensionMethodsOn();
this.extractor = extractor;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
String pluginName = extractor.getPluginName();
UUID serverUUID = serverInfo.getServerUUID();
Database database = dbSystem.getDatabase();
dataProviders = extractor.getDataProviders();
booleanGatherer = new BooleanProviderValueGatherer(
extractor.getPluginName(), extension,
serverInfo.getServerUUID(), dbSystem.getDatabase(),
extractor.getDataProviders(), logger
pluginName, extension, serverUUID, database, dataProviders
);
numberGatherer = new NumberProviderValueGatherer(
extractor.getPluginName(), extension,
serverInfo.getServerUUID(), dbSystem.getDatabase(),
extractor.getDataProviders(), logger
pluginName, extension, serverUUID, database, dataProviders
);
doubleAndPercentageGatherer = new DoubleAndPercentageProviderValueGatherer(
extractor.getPluginName(), extension,
serverInfo.getServerUUID(), dbSystem.getDatabase(),
extractor.getDataProviders(), logger
pluginName, extension, serverUUID, database, dataProviders
);
stringGatherer = new StringProviderValueGatherer(
extractor.getPluginName(), extension,
serverInfo.getServerUUID(), dbSystem.getDatabase(),
extractor.getDataProviders(), logger
pluginName, extension, serverUUID, database, dataProviders
);
tableGatherer = new TableProviderValueGatherer(
pluginName, extension, serverUUID, database, dataProviders
);
}
public void disableMethodFromUse(MethodWrapper method) {
dataProviders.removeProviderWithMethod(method);
}
public boolean canCallEvent(CallEvents event) {
@ -121,6 +128,7 @@ public class ProviderValueGatherer {
numberGatherer.gatherNumberDataOfPlayer(playerUUID, playerName, conditions);
doubleAndPercentageGatherer.gatherDoubleDataOfPlayer(playerUUID, playerName, conditions);
stringGatherer.gatherStringDataOfPlayer(playerUUID, playerName, conditions);
tableGatherer.gatherTableDataOfPlayer(playerUUID, playerName, conditions);
}
public void updateValues() {
@ -128,5 +136,6 @@ public class ProviderValueGatherer {
numberGatherer.gatherNumberDataOfServer(conditions);
doubleAndPercentageGatherer.gatherDoubleDataOfServer(conditions);
stringGatherer.gatherStringDataOfServer(conditions);
tableGatherer.gatherTableDataOfServer(conditions);
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
@ -28,7 +29,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreStringProviderTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerStringResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerStringResultTransaction;
import com.djrapitops.plugin.logging.console.PluginLogger;
import org.apache.commons.lang3.StringUtils;
import java.util.Optional;
@ -50,20 +50,17 @@ class StringProviderValueGatherer {
private final Database database;
private final DataProviders dataProviders;
private final PluginLogger logger;
StringProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
DataProviders dataProviders,
PluginLogger logger
DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
this.logger = logger;
}
void gatherStringDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
@ -101,10 +98,7 @@ class StringProviderValueGatherer {
}
MethodWrapper<String> method = stringProvider.getMethod();
String result = getMethodResult(
methodCaller.apply(method),
throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
);
String result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
return;
}
@ -116,12 +110,11 @@ class StringProviderValueGatherer {
database.executeTransaction(storeTransactionCreator.apply(method, result));
}
private <T> T getMethodResult(Callable<T> callable, Function<Throwable, String> errorMsg) {
private <T> T getMethodResult(Callable<T> callable, MethodWrapper<String> method) {
try {
return callable.call();
} catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
logger.warn(errorMsg.apply(e));
return null;
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
throw new DataExtensionMethodCallException(e, pluginName, method);
}
}

View File

@ -0,0 +1,124 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.providers.gathering;
import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.extension.implementation.providers.DataProvider;
import com.djrapitops.plan.extension.implementation.providers.DataProviders;
import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
import com.djrapitops.plan.extension.implementation.providers.TableDataProvider;
import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreTableProviderTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerTableResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerTableResultTransaction;
import com.djrapitops.plan.extension.table.Table;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Gathers TableProvider method data.
*
* @author Rsl1122
*/
class TableProviderValueGatherer {
private final String pluginName;
private final DataExtension extension;
private final UUID serverUUID;
private final Database database;
private final DataProviders dataProviders;
TableProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
}
void gatherTableDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
// Method parameters abstracted away so that same method can be used for all parameter types
// Same with Method result store transaction creation
Function<MethodWrapper<Table>, Callable<Table>> methodCaller = method -> () -> method.callMethod(extension, playerUUID, playerName);
BiFunction<MethodWrapper<Table>, Table, Transaction> storeTransactionCreator = (method, result) -> new StorePlayerTableResultTransaction(pluginName, serverUUID, method.getMethodName(), playerUUID, result);
for (DataProvider<Table> tableProvider : dataProviders.getPlayerMethodsByType(Table.class)) {
gatherTableDataOfProvider(methodCaller, storeTransactionCreator, conditions, tableProvider);
}
}
void gatherTableDataOfServer(Conditions conditions) {
// Method parameters abstracted away so that same method can be used for all parameter types
// Same with Method result store transaction creation
Function<MethodWrapper<Table>, Callable<Table>> methodCaller = method -> () -> method.callMethod(extension);
BiFunction<MethodWrapper<Table>, Table, Transaction> storeTransactionCreator = (method, result) -> new StoreServerTableResultTransaction(pluginName, serverUUID, method.getMethodName(), result);
for (DataProvider<Table> tableProvider : dataProviders.getServerMethodsByType(Table.class)) {
gatherTableDataOfProvider(methodCaller, storeTransactionCreator, conditions, tableProvider);
}
}
private void gatherTableDataOfProvider(
Function<MethodWrapper<Table>, Callable<Table>> methodCaller,
BiFunction<MethodWrapper<Table>, Table, Transaction> storeTransactionCreator,
Conditions conditions,
DataProvider<Table> tableProvider
) {
ProviderInformation providerInformation = tableProvider.getProviderInformation();
Optional<String> condition = providerInformation.getCondition();
if (condition.isPresent() && conditions.isNotFulfilled(condition.get())) {
return;
}
MethodWrapper<Table> method = tableProvider.getMethod();
Table result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
return;
}
for (Icon icon : result.getIcons()) {
if (icon != null) {
database.executeTransaction(new StoreIconTransaction(icon));
}
}
database.executeTransaction(new StoreTableProviderTransaction(serverUUID, providerInformation, TableDataProvider.getTableColor(tableProvider), result));
database.executeTransaction(storeTransactionCreator.apply(method, result));
}
private <T> T getMethodResult(Callable<T> callable, MethodWrapper<T> method) {
try {
return callable.call();
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
throw new DataExtensionMethodCallException(e, pluginName, method);
}
}
}

View File

@ -40,4 +40,8 @@ public class ExtensionDoubleData implements ExtensionData {
public String getFormattedValue(Formatter<Double> formatter) {
return formatter.apply(value);
}
public double getRawValue() {
return value;
}
}

View File

@ -47,4 +47,8 @@ public class ExtensionNumberData implements ExtensionData {
public String getFormattedValue(Formatter<Long> formatter) {
return formatter.apply(value);
}
public long getRawValue() {
return value;
}
}

View File

@ -36,7 +36,10 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
private final Map<String, ExtensionNumberData> numberData;
private final Map<String, ExtensionStringData> stringData;
private final List<ExtensionTableData> tableData;
private List<String> order;
private List<ExtensionDescriptive> descriptives;
// Table and Graph data will be added later.
@ -48,6 +51,9 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
percentageData = new HashMap<>();
numberData = new HashMap<>();
stringData = new HashMap<>();
tableData = new ArrayList<>();
descriptives = new ArrayList<>();
}
public TabInformation getTabInformation() {
@ -78,6 +84,21 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
return Optional.ofNullable(stringData.get(providerName));
}
public List<ExtensionTableData> getTableData() {
return tableData;
}
/**
* Get all Descriptives for this tabs data.
* <p>
* Only available after the Tab has been built.
*
* @return List of {@link ExtensionDescriptive}s.
*/
public List<ExtensionDescriptive> getDescriptives() {
return descriptives;
}
@Override
public int compareTo(ExtensionTabData other) {
return Integer.compare(this.tabInformation.getTabPriority(), other.tabInformation.getTabPriority()); // Lower is first
@ -103,6 +124,23 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
this.percentageData.putAll(other.percentageData);
this.numberData.putAll(other.numberData);
this.stringData.putAll(other.stringData);
this.tableData.addAll(other.tableData);
createOrderingList();
}
private void createOrderingList() {
booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
doubleData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
percentageData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
numberData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
stringData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
order = descriptives.stream().sorted()
.map(ExtensionDescriptive::getName)
.distinct()// Method names are usually different, but in case someone had same method name with different parameters.
.collect(Collectors.toList());
}
public static class Factory {
@ -138,22 +176,14 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
return this;
}
private void createOrderingList() {
List<ExtensionDescriptive> descriptives = new ArrayList<>();
data.booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
data.doubleData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
data.percentageData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
data.numberData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
data.stringData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
data.order = descriptives.stream().sorted()
.map(ExtensionDescriptive::getName)
.distinct()// Method names are usually different, but in case someone had same method name with different parameters.
.collect(Collectors.toList());
public Factory putTableData(ExtensionTableData extensionTableData) {
data.tableData.add(extensionTableData);
return this;
}
public ExtensionTabData build() {
createOrderingList();
data.createOrderingList();
Collections.sort(data.tableData);
return data;
}
}

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 GNU 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
* GNU 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.extension.implementation.results;
import com.djrapitops.plan.data.element.TableContainer;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.table.Table;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Represents table data from a single TableProvider.
*
* @author Rsl1122
*/
public class ExtensionTableData implements Comparable<ExtensionTableData> {
private final String providerName;
private final Table table;
private final Color tableColor;
public ExtensionTableData(String providerName, Table table, Color tableColor) {
this.providerName = providerName;
this.table = table;
this.tableColor = tableColor;
}
public TableContainer getHtmlTable() {
String[] columns = table.getColumns();
Icon[] icons = table.getIcons();
List<Object[]> rows = table.getRows();
String[] header = buildHeader(columns, icons);
TableContainer htmlTable = new TableContainer(header);
if (rows.size() > 50) {
htmlTable.useJqueryDataTables(); // Use a jQuery data table since there are a lot of rows.
} else {
String colorName = com.djrapitops.plan.utilities.html.icon.Color.getByName(tableColor.name()).orElse(com.djrapitops.plan.utilities.html.icon.Color.NONE).getHtmlClass()
.replace("col-", ""); // TODO after PluginData deprecation, change this thing
htmlTable.setColor(colorName);
}
for (Object[] row : rows) {
htmlTable.addRow(Arrays.stream(row).map(value -> value != null ? value.toString() : null).toArray(Serializable[]::new));
}
return htmlTable;
}
private String[] buildHeader(String[] columns, Icon[] icons) {
ArrayList<String> header = new ArrayList<>();
for (int i = 0; i < columns.length; i++) {
String column = columns[i];
if (column == null) {
break;
}
header.add(com.djrapitops.plan.utilities.html.icon.Icon.fromExtensionIcon(icons[i]).toHtml() + ' ' + column);
}
return header.toArray(new String[0]);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ExtensionTableData)) return false;
ExtensionTableData that = (ExtensionTableData) o;
return providerName.equals(that.providerName) &&
tableColor == that.tableColor;
}
@Override
public int hashCode() {
return Objects.hash(providerName, tableColor);
}
@Override
public int compareTo(ExtensionTableData other) {
return String.CASE_INSENSITIVE_ORDER.compare(providerName, other.providerName);
}
public boolean isWideTable() {
return table.getMaxColumnSize() > 3;
}
}

View File

@ -19,10 +19,7 @@ package com.djrapitops.plan.extension.implementation.results.player;
import com.djrapitops.plan.extension.implementation.results.ExtensionInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.*;
/**
* Represents data of a single extension about a player.
@ -104,6 +101,31 @@ public class ExtensionPlayerData implements Comparable<ExtensionPlayerData> {
Collections.sort(data.tabs);
return data;
}
public Factory combine(Factory with) {
if (with != null) {
for (ExtensionTabData tab : with.build().getTabs()) {
Optional<ExtensionTabData> found = getTab(tab.getTabInformation().getTabName());
if (found.isPresent()) {
found.get().combine(tab);
} else {
addTab(tab);
}
}
}
return this;
}
public Optional<ExtensionTabData> getTab(String tabName) {
for (ExtensionTabData tab : data.tabs) {
if (tabName.equals(tab.getTabInformation().getTabName())) {
return Optional.of(tab);
}
}
return Optional.empty();
}
}
}

View File

@ -150,8 +150,8 @@ public class ExtensionAggregateNumbersQuery implements Query<Map<Integer, Extens
private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException {
FormatType formatType = FormatType.getByName(set.getString(ExtensionProviderTable.FORMAT_TYPE)).orElse(FormatType.NONE);
extensionTab.putNumberData(new ExtensionNumberData(modifiedDescriptive(descriptive, "_avg", "Average "), formatType, set.getLong("average")));
extensionTab.putNumberData(new ExtensionNumberData(modifiedDescriptive(descriptive, "_total", "Total "), formatType, set.getLong("total")));
extensionTab.putNumberData(new ExtensionNumberData(modifiedDescriptive(descriptive, "_avg", "Average "), formatType, (long) set.getDouble("average")));
extensionTab.putNumberData(new ExtensionNumberData(modifiedDescriptive(descriptive, "_total", "Total "), formatType, (long) set.getDouble("total")));
}
private ExtensionDescriptive modifiedDescriptive(ExtensionDescriptive descriptive, String appendToName, String appendToText) {

View File

@ -68,9 +68,29 @@ public class ExtensionPlayerDataQuery implements Query<Map<UUID, List<ExtensionP
Map<UUID, List<ExtensionInformation>> extensionsByServerUUID = db.query(ExtensionInformationQueries.allExtensions());
Map<Integer, ExtensionPlayerData.Factory> extensionDataByPluginID = db.query(fetchIncompletePlayerDataByPluginID());
Map<Integer, ExtensionPlayerData.Factory> tableDataByPluginID = db.query(new ExtensionPlayerTablesQuery(playerUUID));
combine(extensionDataByPluginID, tableDataByPluginID);
return flatMapByServerUUID(extensionsByServerUUID, extensionDataByPluginID);
}
private void combine(
Map<Integer, ExtensionPlayerData.Factory> extensionDataByPluginID,
Map<Integer, ExtensionPlayerData.Factory> aggregates
) {
for (Map.Entry<Integer, ExtensionPlayerData.Factory> entry : aggregates.entrySet()) {
Integer pluginID = entry.getKey();
ExtensionPlayerData.Factory data = entry.getValue();
ExtensionPlayerData.Factory found = extensionDataByPluginID.get(pluginID);
if (found == null) {
extensionDataByPluginID.put(pluginID, data);
} else {
found.combine(data);
}
}
}
private Map<UUID, List<ExtensionPlayerData>> flatMapByServerUUID(Map<UUID, List<ExtensionInformation>> extensionsByServerUUID, Map<Integer, ExtensionPlayerData.Factory> extensionDataByPluginID) {
Map<UUID, List<ExtensionPlayerData>> extensionDataByServerUUID = new HashMap<>();

View File

@ -0,0 +1,287 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.storage.queries;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Family;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.TabInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData;
import com.djrapitops.plan.extension.table.Table;
import com.djrapitops.plan.extension.table.TableAccessor;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Query player tables from tableprovider table.
* <p>
* Returns Map: PluginID - ExtensionPlayerData.Factory.
* <p>
* How it is done:
* - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory
* - Table values are queried and merged into the above multimap
* - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data
* - (Tab Name can be empty.)
* - Multi-map is sorted into ExtensionPlayerData objects by PluginID, one per ID
* <p>
* There are multiple data extraction methods to make extracting the value query easier.
*
* @author Rsl1122
*/
public class ExtensionPlayerTablesQuery implements Query<Map<Integer, ExtensionPlayerData.Factory>> {
private final UUID playerUUID;
public ExtensionPlayerTablesQuery(UUID playerUUID) {
this.playerUUID = playerUUID;
}
private static Map<Integer, ExtensionPlayerData.Factory> flatMapByPluginID(Map<Integer, Map<String, ExtensionTabData.Factory>> tabDataByPluginID) {
Map<Integer, ExtensionPlayerData.Factory> dataByPluginID = new HashMap<>();
for (Map.Entry<Integer, Map<String, ExtensionTabData.Factory>> entry : tabDataByPluginID.entrySet()) {
Integer pluginID = entry.getKey();
ExtensionPlayerData.Factory data = dataByPluginID.getOrDefault(pluginID, new ExtensionPlayerData.Factory(pluginID));
for (ExtensionTabData.Factory tabData : entry.getValue().values()) {
data.addTab(tabData.build());
}
dataByPluginID.put(pluginID, data);
}
return dataByPluginID;
}
@Override
public Map<Integer, ExtensionPlayerData.Factory> executeQuery(SQLDB db) {
Map<Integer, Map<Integer, Table.Factory>> tablesByPluginIDAndTableID = db.query(queryTableProviders());
Map<Integer, Map<Integer, Table.Factory>> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID));
Map<Integer, Map<String, ExtensionTabData.Factory>> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues);
return flatMapByPluginID(tabDataByPluginID);
}
/**
* @param tablesByPluginIDAndTableID {@code <Plugin ID - <Table ID - Table.Factory>>}
* @return {@code <Plugin ID - <Tab name - ExtensionTabData.Factory>}
*/
private Map<Integer, Map<String, ExtensionTabData.Factory>> mapToTabsByPluginID(Map<Integer, Map<Integer, Table.Factory>> tablesByPluginIDAndTableID) {
Map<Integer, Map<String, ExtensionTabData.Factory>> byPluginID = new HashMap<>();
for (Map.Entry<Integer, Map<Integer, Table.Factory>> entry : tablesByPluginIDAndTableID.entrySet()) {
Integer pluginID = entry.getKey();
Map<String, ExtensionTabData.Factory> byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>());
for (Table.Factory table : entry.getValue().values()) {
// Extra Table information
String tableName = TableAccessor.getTableName(table);
Color tableColor = TableAccessor.getColor(table);
// Extra tab information
String tabName = TableAccessor.getTabName(table);
int tabPriority = TableAccessor.getTabPriority(table);
ElementOrder[] tabOrder = TableAccessor.getTabOrder(table);
Icon tabIcon = TableAccessor.getTabIcon(table);
ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority)));
tab.putTableData(new ExtensionTableData(
tableName, table.build(), tableColor
));
byTabName.put(tabName, tab);
}
byPluginID.put(pluginID, byTabName);
}
return byPluginID;
}
// Map: <Plugin ID - <Table ID - Table.Factory>>
private Query<Map<Integer, Map<Integer, Table.Factory>>> queryTableValues(Map<Integer, Map<Integer, Table.Factory>> tables) {
String selectTableValues = SELECT +
ExtensionTableProviderTable.PLUGIN_ID + ',' +
ExtensionPlayerTableValueTable.TABLE_ID + ',' +
ExtensionPlayerTableValueTable.VALUE_1 + ',' +
ExtensionPlayerTableValueTable.VALUE_2 + ',' +
ExtensionPlayerTableValueTable.VALUE_3 + ',' +
ExtensionPlayerTableValueTable.VALUE_4 +
FROM + ExtensionPlayerTableValueTable.TABLE_NAME +
INNER_JOIN + ExtensionTableProviderTable.TABLE_NAME + " on " + ExtensionTableProviderTable.TABLE_NAME + '.' + ExtensionTableProviderTable.ID + '=' + ExtensionPlayerTableValueTable.TABLE_NAME + '.' + ExtensionPlayerTableValueTable.TABLE_ID +
WHERE + ExtensionPlayerTableValueTable.USER_UUID + "=?";
return new QueryStatement<Map<Integer, Map<Integer, Table.Factory>>>(selectTableValues, 10000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<Integer, Map<Integer, Table.Factory>> processResults(ResultSet set) throws SQLException {
while (set.next()) {
Table.Factory table = getTable(set);
if (table == null) {
continue;
}
Object[] row = extractTableRow(set);
if (row.length > 0) {
table.addRow(row);
}
}
return tables;
}
private Table.Factory getTable(ResultSet set) throws SQLException {
int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID);
Map<Integer, Table.Factory> byTableID = tables.get(pluginID);
if (byTableID == null) {
return null;
}
int tableID = set.getInt(ExtensionPlayerTableValueTable.TABLE_ID);
return byTableID.get(tableID);
}
};
}
private Object[] extractTableRow(ResultSet set) throws SQLException {
List<Object> row = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
String columnName = "col_" + i + "_value"; // See ExtensionPlayerTableValueTable.VALUE_1
String value = set.getString(columnName);
if (value == null) {
return row.toArray(new Object[0]);
}
row.add(value);
}
return row.toArray(new Object[0]);
}
// Map: <Plugin ID - <Table ID - Table.Factory>>
private Query<Map<Integer, Map<Integer, Table.Factory>>> queryTableProviders() {
String selectTables = SELECT +
"p1." + ExtensionTableProviderTable.ID + " as table_id," +
"p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," +
"p1." + ExtensionTableProviderTable.PROVIDER_NAME + " as table_name," +
"p1." + ExtensionTableProviderTable.COLOR + " as table_color," +
ExtensionTableProviderTable.COL_1 + ',' +
ExtensionTableProviderTable.COL_2 + ',' +
ExtensionTableProviderTable.COL_3 + ',' +
ExtensionTableProviderTable.COL_4 + ',' +
"t1." + ExtensionTabTable.TAB_NAME + " as tab_name," +
"t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," +
"t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," +
"i1." + ExtensionIconTable.ICON_NAME + " as i1_name," +
"i1." + ExtensionIconTable.FAMILY + " as i1_family," +
"i1." + ExtensionIconTable.COLOR + " as i1_color," +
"i2." + ExtensionIconTable.ICON_NAME + " as i2_name," +
"i2." + ExtensionIconTable.FAMILY + " as i2_family," +
"i2." + ExtensionIconTable.COLOR + " as i2_color," +
"i3." + ExtensionIconTable.ICON_NAME + " as i3_name," +
"i3." + ExtensionIconTable.FAMILY + " as i3_family," +
"i3." + ExtensionIconTable.COLOR + " as i3_color," +
"i4." + ExtensionIconTable.ICON_NAME + " as i4_name," +
"i4." + ExtensionIconTable.FAMILY + " as i4_family," +
"i4." + ExtensionIconTable.COLOR + " as i4_color," +
"i6." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," +
"i6." + ExtensionIconTable.FAMILY + " as tab_icon_family," +
"i6." + ExtensionIconTable.COLOR + " as tab_icon_color" +
FROM + ExtensionTableProviderTable.TABLE_NAME + " p1" +
INNER_JOIN + ExtensionPlayerTableValueTable.TABLE_NAME + " v1 on v1." + ExtensionPlayerTableValueTable.TABLE_ID + "=p1." + ExtensionTableProviderTable.ID +
LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionTableProviderTable.TAB_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_1_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_2_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i3 on i3." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_3_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i4 on i4." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_4_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i6 on i6." + ExtensionIconTable.ID + "=t1." + ExtensionTabTable.ICON_ID +
WHERE + "v1." + ExtensionPlayerTableValueTable.USER_UUID + "=?";
return new QueryStatement<Map<Integer, Map<Integer, Table.Factory>>>(selectTables, 100) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<Integer, Map<Integer, Table.Factory>> processResults(ResultSet set) throws SQLException {
Map<Integer, Map<Integer, Table.Factory>> byPluginID = new HashMap<>();
while (set.next()) {
int pluginID = set.getInt("plugin_id");
Map<Integer, Table.Factory> byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>());
int tableID = set.getInt("table_id");
Table.Factory table = Table.builder();
extractColumns(set, table);
TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE));
TableAccessor.setTableName(table, set.getString("table_name"));
TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse(""));
TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100));
TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values()));
TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon"));
byTableID.put(tableID, table);
byPluginID.put(pluginID, byTableID);
}
return byPluginID;
}
};
}
private void extractColumns(ResultSet set, Table.Factory table) throws SQLException {
String col1 = set.getString(ExtensionTableProviderTable.COL_1);
if (col1 != null) {
table.columnOne(col1, extractIcon(set, "i1"));
}
String col2 = set.getString(ExtensionTableProviderTable.COL_2);
if (col2 != null) {
table.columnTwo(col2, extractIcon(set, "i2"));
}
String col3 = set.getString(ExtensionTableProviderTable.COL_3);
if (col3 != null) {
table.columnThree(col3, extractIcon(set, "i3"));
}
String col4 = set.getString(ExtensionTableProviderTable.COL_4);
if (col4 != null) {
table.columnFour(col4, extractIcon(set, "i4"));
}
}
private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException {
String iconName = set.getString(iconColumnName + "_name");
if (iconName == null) {
return null;
}
return new Icon(
Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID),
iconName,
Color.getByName(set.getString(iconColumnName + "_color")).orElse(Color.NONE)
);
}
}

View File

@ -83,6 +83,7 @@ public class ExtensionServerDataQuery implements Query<List<ExtensionServerData>
combine(extensionDataByPluginID, db.query(new ExtensionAggregateDoublesQuery(serverUUID)));
combine(extensionDataByPluginID, db.query(new ExtensionAggregateNumbersQuery(serverUUID)));
combine(extensionDataByPluginID, db.query(new ExtensionAggregatePercentagesQuery(serverUUID)));
combine(extensionDataByPluginID, db.query(new ExtensionServerTablesQuery(serverUUID)));
return combineWithExtensionInfo(extensionsOfServer, extensionDataByPluginID);
}

View File

@ -0,0 +1,151 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.storage.queries;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.tables.*;
import com.djrapitops.plan.extension.FormatType;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Family;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.results.*;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Query Extension data of x most recent players on a server.
* <p>
* Returns Map: Player UUID - ExtensionTabData (container for provider based data)
*
* @author Rsl1122
*/
public class ExtensionServerPlayerDataTableQuery implements Query<Map<UUID, ExtensionTabData>> {
private final UUID serverUUID;
private final int xMostRecentPlayers;
public ExtensionServerPlayerDataTableQuery(UUID serverUUID, int xMostRecentPlayers) {
this.serverUUID = serverUUID;
this.xMostRecentPlayers = xMostRecentPlayers;
}
@Override
public Map<UUID, ExtensionTabData> executeQuery(SQLDB db) {
return db.query(fetchIncompletePlayerDataByPluginID());
}
private Query<Map<UUID, ExtensionTabData>> fetchIncompletePlayerDataByPluginID() {
String selectLimitedNumberOfPlayerUUIDsByLastSeenDate = SELECT +
SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID +
",MAX(" + SessionsTable.SESSION_END + ") as last_seen" +
FROM + SessionsTable.TABLE_NAME +
GROUP_BY + SessionsTable.USER_UUID +
ORDER_BY + SessionsTable.SESSION_END + " DESC LIMIT ?";
String sql = SELECT +
"v1." + ExtensionPlayerValueTable.USER_UUID + " as uuid," +
"v1." + ExtensionPlayerValueTable.DOUBLE_VALUE + " as double_value," +
"v1." + ExtensionPlayerValueTable.LONG_VALUE + " as long_value," +
"v1." + ExtensionPlayerValueTable.STRING_VALUE + " as string_value," +
"p1." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," +
"p1." + ExtensionProviderTable.TEXT + " as text," +
"p1." + ExtensionProviderTable.FORMAT_TYPE + " as format_type," +
"p1." + ExtensionProviderTable.IS_PLAYER_NAME + " as is_player_name," +
"i1." + ExtensionIconTable.ICON_NAME + " as provider_icon_name," +
"i1." + ExtensionIconTable.FAMILY + " as provider_icon_family" +
FROM + ExtensionPlayerValueTable.TABLE_NAME + " v1" +
INNER_JOIN + '(' + selectLimitedNumberOfPlayerUUIDsByLastSeenDate + ") as last_seen_q on last_seen_q.uuid=v1." + ExtensionPlayerValueTable.USER_UUID +
INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " p1 on p1." + ExtensionProviderTable.ID + "=v1." + ExtensionPlayerValueTable.PROVIDER_ID +
INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " e1 on e1." + ExtensionPluginTable.ID + "=p1." + ExtensionProviderTable.PLUGIN_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionProviderTable.ICON_ID +
WHERE + "e1." + ExtensionPluginTable.SERVER_UUID + "=?" +
AND + " v1." + ExtensionPlayerValueTable.BOOLEAN_VALUE + IS_NULL + // Don't select Boolean value rows
AND + " v1." + ExtensionPlayerValueTable.PERCENTAGE_VALUE + IS_NULL + // Don't select Percentage value rows
AND + " p1." + ExtensionProviderTable.IS_PLAYER_NAME + "=?";
return new QueryStatement<Map<UUID, ExtensionTabData>>(sql, 1000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, xMostRecentPlayers); // Limit to x most recently seen players
statement.setString(2, serverUUID.toString());
statement.setBoolean(3, false); // Don't select player_name String values
}
@Override
public Map<UUID, ExtensionTabData> processResults(ResultSet set) throws SQLException {
return extractDataByPlayer(set);
}
};
}
private Map<UUID, ExtensionTabData> extractDataByPlayer(ResultSet set) throws SQLException {
Map<UUID, ExtensionTabData.Factory> dataByPlayer = new HashMap<>();
while (set.next()) {
UUID playerUUID = UUID.fromString(set.getString("uuid"));
ExtensionTabData.Factory data = dataByPlayer.getOrDefault(playerUUID, new ExtensionTabData.Factory(null));
ExtensionDescriptive extensionDescriptive = extractDescriptive(set);
extractAndPutDataTo(data, extensionDescriptive, set);
dataByPlayer.put(playerUUID, data);
}
return dataByPlayer.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().build()));
}
private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException {
double doubleValue = set.getDouble(ExtensionPlayerValueTable.DOUBLE_VALUE);
if (!set.wasNull()) {
extensionTab.putDoubleData(new ExtensionDoubleData(descriptive, doubleValue));
return;
}
long numberValue = set.getLong(ExtensionPlayerValueTable.LONG_VALUE);
if (!set.wasNull()) {
FormatType formatType = FormatType.getByName(set.getString(ExtensionProviderTable.FORMAT_TYPE)).orElse(FormatType.NONE);
extensionTab.putNumberData(new ExtensionNumberData(descriptive, formatType, numberValue));
return;
}
String stringValue = set.getString(ExtensionPlayerValueTable.STRING_VALUE);
if (stringValue != null) {
boolean isPlayerName = false;
extensionTab.putStringData(new ExtensionStringData(descriptive, isPlayerName, stringValue));
}
}
private ExtensionDescriptive extractDescriptive(ResultSet set) throws SQLException {
String name = set.getString("provider_name");
String text = set.getString(ExtensionProviderTable.TEXT);
String iconName = set.getString("provider_icon_name");
Family family = Family.getByName(set.getString("provider_icon_family")).orElse(Family.SOLID);
Icon icon = new Icon(family, iconName, Color.NONE);
return new ExtensionDescriptive(name, text, null, icon, 0);
}
}

View File

@ -0,0 +1,284 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.storage.queries;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
import com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Family;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.TabInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData;
import com.djrapitops.plan.extension.table.Table;
import com.djrapitops.plan.extension.table.TableAccessor;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Query server tables from tableprovider table.
* <p>
* Returns Map: PluginID - ExtensionServerData.Factory.
* <p>
* How it is done:
* - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory
* - Table values are queried and merged into the above multimap
* - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data
* - (Tab Name can be empty.)
* - Multi-map is sorted into ExtensionServerData objects by PluginID, one per ID
* <p>
* There are multiple data extraction methods to make extracting the value query easier.
*
* @author Rsl1122
*/
public class ExtensionServerTablesQuery implements Query<Map<Integer, ExtensionServerData.Factory>> {
private final UUID serverUUID;
public ExtensionServerTablesQuery(UUID serverUUID) {
this.serverUUID = serverUUID;
}
@Override
public Map<Integer, ExtensionServerData.Factory> executeQuery(SQLDB db) {
Map<Integer, Map<Integer, Table.Factory>> tablesByPluginIDAndTableID = db.query(queryTableProviders());
Map<Integer, Map<Integer, Table.Factory>> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID));
Map<Integer, Map<String, ExtensionTabData.Factory>> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues);
return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID);
}
/**
* @param tablesByPluginIDAndTableID {@code <Plugin ID - <Table ID - Table.Factory>>}
* @return {@code <Plugin ID - <Tab name - ExtensionTabData.Factory>}
*/
private Map<Integer, Map<String, ExtensionTabData.Factory>> mapToTabsByPluginID(Map<Integer, Map<Integer, Table.Factory>> tablesByPluginIDAndTableID) {
Map<Integer, Map<String, ExtensionTabData.Factory>> byPluginID = new HashMap<>();
for (Map.Entry<Integer, Map<Integer, Table.Factory>> entry : tablesByPluginIDAndTableID.entrySet()) {
Integer pluginID = entry.getKey();
Map<String, ExtensionTabData.Factory> byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>());
for (Table.Factory table : entry.getValue().values()) {
// Extra Table information
String tableName = TableAccessor.getTableName(table);
Color tableColor = TableAccessor.getColor(table);
// Extra tab information
String tabName = TableAccessor.getTabName(table);
int tabPriority = TableAccessor.getTabPriority(table);
ElementOrder[] tabOrder = TableAccessor.getTabOrder(table);
Icon tabIcon = TableAccessor.getTabIcon(table);
ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority)));
tab.putTableData(new ExtensionTableData(
tableName, table.build(), tableColor
));
byTabName.put(tabName, tab);
}
byPluginID.put(pluginID, byTabName);
}
return byPluginID;
}
// Map: <Plugin ID - <Table ID - Table.Factory>>
private Query<Map<Integer, Map<Integer, Table.Factory>>> queryTableValues(Map<Integer, Map<Integer, Table.Factory>> tables) {
String selectTableValues = SELECT +
ExtensionTableProviderTable.PLUGIN_ID + ',' +
ExtensionServerTableValueTable.TABLE_ID + ',' +
ExtensionServerTableValueTable.VALUE_1 + ',' +
ExtensionServerTableValueTable.VALUE_2 + ',' +
ExtensionServerTableValueTable.VALUE_3 + ',' +
ExtensionServerTableValueTable.VALUE_4 + ',' +
ExtensionServerTableValueTable.VALUE_5 +
FROM + ExtensionServerTableValueTable.TABLE_NAME +
INNER_JOIN + ExtensionTableProviderTable.TABLE_NAME + " on " + ExtensionTableProviderTable.TABLE_NAME + '.' + ExtensionTableProviderTable.ID + '=' + ExtensionServerTableValueTable.TABLE_NAME + '.' + ExtensionServerTableValueTable.TABLE_ID +
WHERE + ExtensionServerTableValueTable.SERVER_UUID + "=?";
return new QueryStatement<Map<Integer, Map<Integer, Table.Factory>>>(selectTableValues, 10000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
}
@Override
public Map<Integer, Map<Integer, Table.Factory>> processResults(ResultSet set) throws SQLException {
while (set.next()) {
Table.Factory table = getTable(set);
if (table == null) {
continue;
}
Object[] row = extractTableRow(set);
if (row.length > 0) {
table.addRow(row);
}
}
return tables;
}
private Table.Factory getTable(ResultSet set) throws SQLException {
int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID);
Map<Integer, Table.Factory> byTableID = tables.get(pluginID);
if (byTableID == null) {
return null;
}
int tableID = set.getInt(ExtensionServerTableValueTable.TABLE_ID);
return byTableID.get(tableID);
}
};
}
private Object[] extractTableRow(ResultSet set) throws SQLException {
List<Object> row = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
String columnName = "col_" + i + "_value"; // See ExtensionServerTableValueTable.VALUE_1
String value = set.getString(columnName);
if (value == null) {
return row.toArray(new Object[0]);
}
row.add(value);
}
return row.toArray(new Object[0]);
}
// Map: <Plugin ID - <Table ID - Table.Factory>>
private Query<Map<Integer, Map<Integer, Table.Factory>>> queryTableProviders() {
String selectTables = SELECT +
"p1." + ExtensionTableProviderTable.ID + " as table_id," +
"p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," +
"p1." + ExtensionTableProviderTable.PROVIDER_NAME + " as table_name," +
"p1." + ExtensionTableProviderTable.COLOR + " as table_color," +
ExtensionTableProviderTable.COL_1 + ',' +
ExtensionTableProviderTable.COL_2 + ',' +
ExtensionTableProviderTable.COL_3 + ',' +
ExtensionTableProviderTable.COL_4 + ',' +
ExtensionTableProviderTable.COL_5 + ',' +
"t1." + ExtensionTabTable.TAB_NAME + " as tab_name," +
"t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," +
"t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," +
"i1." + ExtensionIconTable.ICON_NAME + " as i1_name," +
"i1." + ExtensionIconTable.FAMILY + " as i1_family," +
"i1." + ExtensionIconTable.COLOR + " as i1_color," +
"i2." + ExtensionIconTable.ICON_NAME + " as i2_name," +
"i2." + ExtensionIconTable.FAMILY + " as i2_family," +
"i2." + ExtensionIconTable.COLOR + " as i2_color," +
"i3." + ExtensionIconTable.ICON_NAME + " as i3_name," +
"i3." + ExtensionIconTable.FAMILY + " as i3_family," +
"i3." + ExtensionIconTable.COLOR + " as i3_color," +
"i4." + ExtensionIconTable.ICON_NAME + " as i4_name," +
"i4." + ExtensionIconTable.FAMILY + " as i4_family," +
"i4." + ExtensionIconTable.COLOR + " as i4_color," +
"i5." + ExtensionIconTable.ICON_NAME + " as i5_name," +
"i5." + ExtensionIconTable.FAMILY + " as i5_family," +
"i5." + ExtensionIconTable.COLOR + " as i5_color," +
"i6." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," +
"i6." + ExtensionIconTable.FAMILY + " as tab_icon_family," +
"i6." + ExtensionIconTable.COLOR + " as tab_icon_color" +
FROM + ExtensionTableProviderTable.TABLE_NAME + " p1" +
INNER_JOIN + ExtensionServerTableValueTable.TABLE_NAME + " v1 on v1." + ExtensionServerTableValueTable.TABLE_ID + "=p1." + ExtensionTableProviderTable.ID +
LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionTableProviderTable.TAB_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_1_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_2_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i3 on i3." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_3_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i4 on i4." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_4_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i5 on i5." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_5_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i6 on i6." + ExtensionIconTable.ID + "=t1." + ExtensionTabTable.ICON_ID +
WHERE + "v1." + ExtensionServerTableValueTable.SERVER_UUID + "=?";
return new QueryStatement<Map<Integer, Map<Integer, Table.Factory>>>(selectTables, 100) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
}
@Override
public Map<Integer, Map<Integer, Table.Factory>> processResults(ResultSet set) throws SQLException {
Map<Integer, Map<Integer, Table.Factory>> byPluginID = new HashMap<>();
while (set.next()) {
int pluginID = set.getInt("plugin_id");
Map<Integer, Table.Factory> byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>());
int tableID = set.getInt("table_id");
Table.Factory table = Table.builder();
extractColumns(set, table);
TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE));
TableAccessor.setTableName(table, set.getString("table_name"));
TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse(""));
TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100));
TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values()));
TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon"));
byTableID.put(tableID, table);
byPluginID.put(pluginID, byTableID);
}
return byPluginID;
}
};
}
private void extractColumns(ResultSet set, Table.Factory table) throws SQLException {
String col1 = set.getString(ExtensionTableProviderTable.COL_1);
if (col1 != null) {
table.columnOne(col1, extractIcon(set, "i1"));
}
String col2 = set.getString(ExtensionTableProviderTable.COL_2);
if (col2 != null) {
table.columnTwo(col2, extractIcon(set, "i2"));
}
String col3 = set.getString(ExtensionTableProviderTable.COL_3);
if (col3 != null) {
table.columnThree(col3, extractIcon(set, "i3"));
}
String col4 = set.getString(ExtensionTableProviderTable.COL_4);
if (col4 != null) {
table.columnFour(col4, extractIcon(set, "i4"));
}
String col5 = set.getString(ExtensionTableProviderTable.COL_5);
if (col5 != null) {
table.columnFive(col5, extractIcon(set, "i5"));
}
}
private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException {
String iconName = set.getString(iconColumnName + "_name");
if (iconName == null) {
return null;
}
return new Icon(
Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID),
iconName,
Color.getByName(set.getString(iconColumnName + "_color")).orElse(Color.NONE)
);
}
}

View File

@ -68,14 +68,14 @@ public class StoreTabInformationTransaction extends Transaction {
" SET " +
ExtensionTabTable.TAB_PRIORITY + "=?," +
ExtensionTabTable.ELEMENT_ORDER + "=?," +
ExtensionTabTable.ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," +
ExtensionTabTable.ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID +
WHERE + ExtensionTabTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
AND + ExtensionTabTable.TAB_NAME + "=?";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tabInformation.getTabPriority());
statement.setString(2, tabInformation.getTabElementOrder().map(ElementOrder::serialize).orElse(null));
statement.setString(2, ElementOrder.serialize(tabInformation.getTabElementOrder().orElse(ElementOrder.values())));
ExtensionIconTable.set3IconValuesToStatement(statement, 3, tabInformation.getTabIcon());
ExtensionPluginTable.set2PluginValuesToStatement(statement, 6, pluginName, serverUUID);
statement.setString(8, tabInformation.getTabName());
@ -88,14 +88,14 @@ public class StoreTabInformationTransaction extends Transaction {
ExtensionTabTable.TAB_NAME + "," +
ExtensionTabTable.ELEMENT_ORDER + "," +
ExtensionTabTable.TAB_PRIORITY + "," +
ExtensionTabTable.ICON_ID +
ExtensionTabTable.ICON_ID + "," +
ExtensionTabTable.PLUGIN_ID +
") VALUES (?,?,?," + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tabInformation.getTabName());
statement.setString(2, tabInformation.getTabElementOrder().map(ElementOrder::serialize).orElse(null));
statement.setString(2, ElementOrder.serialize(tabInformation.getTabElementOrder().orElse(ElementOrder.values())));
statement.setInt(3, tabInformation.getTabPriority());
ExtensionIconTable.set3IconValuesToStatement(statement, 4, tabInformation.getTabIcon());
ExtensionPluginTable.set2PluginValuesToStatement(statement, 7, pluginName, serverUUID);

View File

@ -0,0 +1,173 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.storage.transactions.providers;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.extension.table.Table;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.AND;
import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE;
import static com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable.*;
/**
* Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
*
* @author Rsl1122
*/
public class StoreTableProviderTransaction extends Transaction {
private final UUID serverUUID;
private final ProviderInformation providerInformation;
private final Color tableColor;
private final Table table;
public StoreTableProviderTransaction(UUID serverUUID, ProviderInformation providerInformation, Color tableColor, Table table) {
this.providerInformation = providerInformation;
this.tableColor = tableColor;
this.table = table;
this.serverUUID = serverUUID;
}
@Override
protected void performOperations() {
execute(storeProvider());
}
private Executable storeProvider() {
return connection -> {
if (!updateProvider().execute(connection)) {
return insertProvider().execute(connection);
}
return false;
};
}
private Executable updateProvider() {
String[] columns = table.getColumns();
Icon[] icons = table.getIcons();
String sql = "UPDATE " + TABLE_NAME + " SET " +
COLOR + "=?," +
COL_1 + "=?," +
COL_2 + "=?," +
COL_3 + "=?," +
COL_4 + "=?," +
COL_5 + "=?," +
CONDITION + "=?," +
TAB_ID + '=' + ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' +
ICON_1_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ICON_2_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ICON_3_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ICON_4_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ICON_5_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID +
WHERE + PROVIDER_NAME + "=?" +
AND + PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tableColor.name());
setStringOrNull(statement, 2, columns[0]);
setStringOrNull(statement, 3, columns[1]);
setStringOrNull(statement, 4, columns[2]);
setStringOrNull(statement, 5, columns[3]);
setStringOrNull(statement, 6, columns[4]);
setStringOrNull(statement, 7, providerInformation.getCondition().orElse(null));
ExtensionTabTable.set3TabValuesToStatement(statement, 8, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID);
ExtensionIconTable.set3IconValuesToStatement(statement, 11, icons[0]);
ExtensionIconTable.set3IconValuesToStatement(statement, 14, icons[1]);
ExtensionIconTable.set3IconValuesToStatement(statement, 17, icons[2]);
ExtensionIconTable.set3IconValuesToStatement(statement, 20, icons[3]);
ExtensionIconTable.set3IconValuesToStatement(statement, 23, icons[4]);
statement.setString(26, providerInformation.getName());
ExtensionPluginTable.set2PluginValuesToStatement(statement, 27, providerInformation.getPluginName(), serverUUID);
}
};
}
private Executable insertProvider() {
String[] columns = table.getColumns();
Icon[] icons = table.getIcons();
String sql = "INSERT INTO " + TABLE_NAME + '(' +
PROVIDER_NAME + ',' +
COLOR + ',' +
COL_1 + ',' +
COL_2 + ',' +
COL_3 + ',' +
COL_4 + ',' +
COL_5 + ',' +
CONDITION + ',' +
TAB_ID + ',' +
PLUGIN_ID + ',' +
ICON_1_ID + ',' +
ICON_2_ID + ',' +
ICON_3_ID + ',' +
ICON_4_ID + ',' +
ICON_5_ID +
") VALUES (?,?,?,?,?,?,?,?," +
ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' +
ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ')';
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, providerInformation.getName());
statement.setString(2, tableColor.name());
setStringOrNull(statement, 3, columns[0]);
setStringOrNull(statement, 4, columns[1]);
setStringOrNull(statement, 5, columns[2]);
setStringOrNull(statement, 6, columns[3]);
setStringOrNull(statement, 7, columns[4]);
setStringOrNull(statement, 8, providerInformation.getCondition().orElse(null));
ExtensionTabTable.set3TabValuesToStatement(statement, 9, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID);
ExtensionPluginTable.set2PluginValuesToStatement(statement, 12, providerInformation.getPluginName(), serverUUID);
ExtensionIconTable.set3IconValuesToStatement(statement, 14, icons[0]);
ExtensionIconTable.set3IconValuesToStatement(statement, 17, icons[1]);
ExtensionIconTable.set3IconValuesToStatement(statement, 20, icons[2]);
ExtensionIconTable.set3IconValuesToStatement(statement, 23, icons[3]);
ExtensionIconTable.set3IconValuesToStatement(statement, 26, icons[4]);
}
};
}
private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
if (value != null) {
statement.setString(index, value);
} else {
statement.setNull(index, Types.VARCHAR);
}
}
}

View File

@ -19,9 +19,7 @@ package com.djrapitops.plan.extension.implementation.storage.transactions.result
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable;
import com.djrapitops.plan.db.sql.tables.*;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@ -51,12 +49,17 @@ public class RemoveInvalidResultsTransaction extends Transaction {
@Override
protected void performOperations() {
for (String invalidatedMethod : invalidatedMethods) {
execute(deleteInvalidMethodResults(invalidatedMethod));
execute(deleteInvalidPlayerMethodResults(invalidatedMethod));
execute(deleteInvalidServerMethodResults(invalidatedMethod));
execute(deleteInvalidMethodProvider(invalidatedMethod));
execute(deleteInvalidPlayerTableResults(invalidatedMethod));
execute(deleteInvalidServerTableResults(invalidatedMethod));
execute(deleteInvalidTableProvider(invalidatedMethod));
}
}
private Executable deleteInvalidMethodResults(String invalidMethod) {
private Executable deleteInvalidPlayerMethodResults(String invalidMethod) {
String sql = "DELETE FROM " + ExtensionPlayerValueTable.TABLE_NAME +
WHERE + ExtensionPlayerValueTable.PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
return new ExecStatement(sql) {
@ -67,10 +70,56 @@ public class RemoveInvalidResultsTransaction extends Transaction {
};
}
private Executable deleteInvalidServerMethodResults(String invalidMethod) {
String sql = "DELETE FROM " + ExtensionServerValueTable.TABLE_NAME +
WHERE + ExtensionServerValueTable.PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID);
}
};
}
private Executable deleteInvalidPlayerTableResults(String invalidMethod) {
String sql = "DELETE FROM " + ExtensionPlayerTableValueTable.TABLE_NAME +
WHERE + ExtensionPlayerTableValueTable.TABLE_ID + "=" + ExtensionTableProviderTable.STATEMENT_SELECT_TABLE_ID;
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID);
}
};
}
private Executable deleteInvalidServerTableResults(String invalidMethod) {
String sql = "DELETE FROM " + ExtensionServerTableValueTable.TABLE_NAME +
WHERE + ExtensionServerTableValueTable.TABLE_ID + "=" + ExtensionTableProviderTable.STATEMENT_SELECT_TABLE_ID;
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID);
}
};
}
private Executable deleteInvalidMethodProvider(String invalidMethod) {
String sql = "DELETE FROM " + ExtensionProviderTable.TABLE_NAME +
WHERE + ExtensionProviderTable.PROVIDER_NAME + "=?" +
AND + ExtensionProviderTable.PLUGIN_ID + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
AND + ExtensionProviderTable.PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, invalidMethod);
ExtensionPluginTable.set2PluginValuesToStatement(statement, 2, pluginName, serverUUID);
}
};
}
private Executable deleteInvalidTableProvider(String invalidMethod) {
String sql = "DELETE FROM " + ExtensionTableProviderTable.TABLE_NAME +
WHERE + ExtensionTableProviderTable.TABLE_NAME + "=?" +
AND + ExtensionTableProviderTable.PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {

View File

@ -20,8 +20,10 @@ import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@ -39,37 +41,53 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*;
*
* @author Rsl1122
*/
public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction {
public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transaction {
private final String providerTable;
private final String playerValueTable;
private final String playerTableValueTable;
private final String tableTable;
public RemoveUnsatisfiedConditionalPlayerResultsTransaction() {
providerTable = ExtensionProviderTable.TABLE_NAME;
playerValueTable = ExtensionPlayerValueTable.TABLE_NAME;
tableTable = ExtensionTableProviderTable.TABLE_NAME;
playerTableValueTable = ExtensionPlayerTableValueTable.TABLE_NAME;
}
@Override
protected void performOperations() {
execute(deleteUnsatisfied());
String selectSatisfiedConditions = getSatisfiedConditionsSQL();
execute(deleteUnsatisfiedValues(selectSatisfiedConditions));
execute(deleteUnsatisfiedTableValues(selectSatisfiedConditions));
}
private Executable deleteUnsatisfied() {
private String getSatisfiedConditionsSQL() {
String reversedCondition = dbType == DBType.SQLITE ? "'not_' || " + ExtensionProviderTable.PROVIDED_CONDITION : "CONCAT('not_'," + ExtensionProviderTable.PROVIDED_CONDITION + ')';
String providerTable = ExtensionProviderTable.TABLE_NAME;
String playerValueTable = ExtensionPlayerValueTable.TABLE_NAME;
String selectSatisfiedPositiveConditions = SELECT +
ExtensionProviderTable.PROVIDED_CONDITION + ',' +
ExtensionPlayerValueTable.USER_UUID +
ExtensionProviderTable.PLUGIN_ID + ',' +
ExtensionPlayerTableValueTable.USER_UUID +
FROM + providerTable +
INNER_JOIN + playerValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionPlayerValueTable.PROVIDER_ID +
WHERE + ExtensionPlayerValueTable.BOOLEAN_VALUE + "=?" +
AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL;
String selectSatisfiedNegativeConditions = SELECT +
reversedCondition + " as " + ExtensionProviderTable.PROVIDED_CONDITION + ',' +
ExtensionPlayerValueTable.USER_UUID +
ExtensionProviderTable.PLUGIN_ID + ',' +
ExtensionPlayerTableValueTable.USER_UUID +
FROM + providerTable +
INNER_JOIN + playerValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionPlayerValueTable.PROVIDER_ID +
WHERE + ExtensionPlayerValueTable.BOOLEAN_VALUE + "=?" +
AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL;
// Query contents: Set of provided_conditions
String selectSatisfiedConditions = '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1";
return '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1";
}
private Executable deleteUnsatisfiedValues(String selectSatisfiedConditions) {
// Query contents:
// id | uuid | q1.uuid | condition | q1.provided_condition
// -- | ---- | ------- | --------- | ---------------------
@ -87,6 +105,8 @@ public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction
"=q1." + ExtensionPlayerValueTable.USER_UUID +
AND + ExtensionProviderTable.CONDITION +
"=q1." + ExtensionProviderTable.PROVIDED_CONDITION +
AND + providerTable + '.' + ExtensionProviderTable.PLUGIN_ID +
"=q1." + ExtensionProviderTable.PLUGIN_ID +
')' +
WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query
AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition
@ -105,4 +125,32 @@ public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction
}
};
}
private Executable deleteUnsatisfiedTableValues(String selectSatisfiedConditions) {
String selectUnsatisfiedValueIDs = SELECT + ExtensionTableProviderTable.ID +
FROM + tableTable +
LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled
" on (" +
tableTable + '.' + ExtensionTableProviderTable.CONDITION +
"=q1." + ExtensionProviderTable.PROVIDED_CONDITION +
AND + tableTable + '.' + ExtensionTableProviderTable.PLUGIN_ID +
"=q1." + ExtensionProviderTable.PLUGIN_ID +
')' +
WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query
AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition
// Nested query here is required because MySQL limits update statements with nested queries:
// The nested query creates a temporary table that bypasses the same table query-update limit.
// Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception.
String deleteValuesSQL = "DELETE FROM " + playerTableValueTable +
WHERE + ExtensionPlayerTableValueTable.TABLE_ID + " IN (" + SELECT + ExtensionTableProviderTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)";
return new ExecStatement(deleteValuesSQL) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, true); // Select provided conditions with 'true' value
statement.setBoolean(2, false); // Select negated conditions with 'false' value
}
};
}
}

View File

@ -0,0 +1,150 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable;
import com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Transaction to remove older results that violate an updated condition value.
* <p>
* How it works:
* - Select all fulfilled conditions for all servers (conditionName when true and not_conditionName when false)
* - Left join with server value & provider tables when plugin_ids match, and when condition matches a condition in the
* query above. (plugin_ids can be linked to servers)
* - Filter the join query for values where the condition did not match any provided condition in the join (Is null)
* - Delete all server values with IDs that are returned by the left join query after filtering
*
* @author Rsl1122
*/
public class RemoveUnsatisfiedConditionalServerResultsTransaction extends Transaction {
private final String providerTable;
private final String serverValueTable;
private final String serverTableValueTable;
private final String tableTable;
public RemoveUnsatisfiedConditionalServerResultsTransaction() {
providerTable = ExtensionProviderTable.TABLE_NAME;
serverValueTable = ExtensionServerValueTable.TABLE_NAME;
tableTable = ExtensionTableProviderTable.TABLE_NAME;
serverTableValueTable = ExtensionServerTableValueTable.TABLE_NAME;
}
@Override
protected void performOperations() {
String selectSatisfiedConditions = getSatisfiedConditionsSQL();
execute(deleteUnsatisfiedValues(selectSatisfiedConditions));
execute(deleteUnsatisfiedTableValues(selectSatisfiedConditions));
}
private Executable deleteUnsatisfiedValues(String selectSatisfiedConditions) {
// Query contents:
// id | provider_id | q1.provider_id | condition | q1.provided_condition
// -- | ----------- | -------------- | --------- | ---------------------
// 1 | ... | ... | A | A Satisfied condition
// 2 | ... | ... | not_B | not_B Satisfied condition
// 3 | ... | ... | NULL | NULL Satisfied condition
// 4 | ... | ... | B | NULL Unsatisfied condition, filtered to these in WHERE clause.
// 5 | ... | ... | not_C | NULL Unsatisfied condition
String selectUnsatisfiedValueIDs = SELECT + serverValueTable + '.' + ExtensionServerValueTable.ID +
FROM + providerTable +
INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID +
LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled
" on (" +
ExtensionProviderTable.CONDITION + "=q1." + ExtensionProviderTable.PROVIDED_CONDITION +
AND + providerTable + '.' + ExtensionProviderTable.PLUGIN_ID + "=q1." + ExtensionProviderTable.PLUGIN_ID +
')' +
WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query
AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition
// Nested query here is required because MySQL limits update statements with nested queries:
// The nested query creates a temporary table that bypasses the same table query-update limit.
// Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception.
String sql = "DELETE FROM " + serverValueTable +
WHERE + ExtensionServerValueTable.ID + " IN (" + SELECT + ExtensionServerValueTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, true); // Select provided conditions with 'true' value
statement.setBoolean(2, false); // Select negated conditions with 'false' value
}
};
}
private String getSatisfiedConditionsSQL() {
String reversedCondition = dbType == DBType.SQLITE ? "'not_' || " + ExtensionProviderTable.PROVIDED_CONDITION : "CONCAT('not_'," + ExtensionProviderTable.PROVIDED_CONDITION + ')';
String selectSatisfiedPositiveConditions = SELECT +
ExtensionProviderTable.PROVIDED_CONDITION + ',' +
ExtensionProviderTable.PLUGIN_ID +
FROM + providerTable +
INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID +
WHERE + ExtensionServerValueTable.BOOLEAN_VALUE + "=?" +
AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL;
String selectSatisfiedNegativeConditions = SELECT +
reversedCondition + " as " + ExtensionProviderTable.PROVIDED_CONDITION + ',' +
ExtensionProviderTable.PLUGIN_ID +
FROM + providerTable +
INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID +
WHERE + ExtensionServerValueTable.BOOLEAN_VALUE + "=?" +
AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL;
// Query contents: Set of provided_conditions
return '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1";
}
private Executable deleteUnsatisfiedTableValues(String selectSatisfiedConditions) {
String selectUnsatisfiedValueIDs = SELECT + ExtensionTableProviderTable.ID +
FROM + tableTable +
LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled
" on (" +
tableTable + '.' + ExtensionTableProviderTable.CONDITION +
"=q1." + ExtensionProviderTable.PROVIDED_CONDITION +
AND + tableTable + '.' + ExtensionTableProviderTable.PLUGIN_ID +
"=q1." + ExtensionProviderTable.PLUGIN_ID +
')' +
WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query
AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition
// Nested query here is required because MySQL limits update statements with nested queries:
// The nested query creates a temporary table that bypasses the same table query-update limit.
// Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception.
String deleteValuesSQL = "DELETE FROM " + serverTableValueTable +
WHERE + ExtensionServerTableValueTable.TABLE_ID + " IN (" + SELECT + ExtensionTableProviderTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)";
return new ExecStatement(deleteValuesSQL) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, true); // Select provided conditions with 'true' value
statement.setBoolean(2, false); // Select negated conditions with 'false' value
}
};
}
}

View File

@ -0,0 +1,155 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.access.*;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import com.djrapitops.plan.extension.table.Table;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable.*;
/**
* Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
*
* @author Rsl1122
*/
public class StorePlayerTableResultTransaction extends Transaction {
private final String pluginName;
private final UUID serverUUID;
private final String providerName;
private final UUID playerUUID;
private final Table table;
public StorePlayerTableResultTransaction(String pluginName, UUID serverUUID, String providerName, UUID playerUUID, Table table) {
this.pluginName = pluginName;
this.serverUUID = serverUUID;
this.providerName = providerName;
this.playerUUID = playerUUID;
this.table = table;
}
@Override
protected void performOperations() {
execute(storeValue());
}
private Executable storeValue() {
return connection -> {
int maxColumnSize = table.getMaxColumnSize();
if (maxColumnSize == 0) {
return false;
}
Integer tableID = query(tableID());
deleteOldValues(tableID).execute(connection);
insertNewValues(tableID).execute(connection);
return false;
};
}
private Executable deleteOldValues(int tableID) {
String sql = "DELETE FROM " + TABLE_NAME +
WHERE + TABLE_ID + "=?" +
AND + USER_UUID + "=?";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tableID);
statement.setString(2, playerUUID.toString());
}
};
}
private Executable insertNewValues(int tableID) {
String sql = "INSERT INTO " + TABLE_NAME + '(' +
TABLE_ID + ',' +
USER_UUID + ',' +
VALUE_1 + ',' +
VALUE_2 + ',' +
VALUE_3 + ',' +
VALUE_4 +
") VALUES (?,?,?,?,?,?)";
return new ExecBatchStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
int maxColumnSize = Math.min(table.getMaxColumnSize(), 4); // Limit to maximum 4 columns, or how many column names there are.
for (Object[] row : table.getRows()) {
statement.setInt(1, tableID);
statement.setString(2, playerUUID.toString());
for (int i = 0; i < maxColumnSize; i++) {
Object value = row[i];
setStringOrNull(statement, 3 + i, value != null ? value.toString() : null);
}
// Rest are set null if not 4 columns wide.
for (int i = maxColumnSize; i < 4; i++) {
statement.setNull(3 + i, Types.VARCHAR);
}
statement.addBatch();
}
}
};
}
private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
if (value != null) {
statement.setString(index, value);
} else {
statement.setNull(index, Types.VARCHAR);
}
}
private Query<Integer> tableID() {
String sql = SELECT + ExtensionTableProviderTable.ID +
FROM + ExtensionTableProviderTable.TABLE_NAME +
WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" +
AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
" LIMIT 1";
return new QueryStatement<Integer>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID);
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
if (set.next()) {
int id = set.getInt(ExtensionTableProviderTable.ID);
if (!set.wasNull()) {
return id;
}
}
throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName);
}
};
}
}

View File

@ -0,0 +1,154 @@
/*
* 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 GNU 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
* GNU 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.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.access.*;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import com.djrapitops.plan.extension.table.Table;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
import static com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable.*;
/**
* Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
*
* @author Rsl1122
*/
public class StoreServerTableResultTransaction extends Transaction {
private final String pluginName;
private final UUID serverUUID;
private final String providerName;
private final Table table;
public StoreServerTableResultTransaction(String pluginName, UUID serverUUID, String providerName, Table table) {
this.pluginName = pluginName;
this.serverUUID = serverUUID;
this.providerName = providerName;
this.table = table;
}
@Override
protected void performOperations() {
execute(storeValue());
}
private Executable storeValue() {
return connection -> {
int maxColumnSize = table.getMaxColumnSize();
if (maxColumnSize == 0) {
return false;
}
Integer tableID = query(tableID());
deleteOldValues(tableID).execute(connection);
insertNewValues(tableID).execute(connection);
return false;
};
}
private Executable deleteOldValues(int tableID) {
String sql = "DELETE FROM " + TABLE_NAME +
WHERE + TABLE_ID + "=?" +
AND + SERVER_UUID + "=?";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tableID);
statement.setString(2, serverUUID.toString());
}
};
}
private Executable insertNewValues(int tableID) {
String sql = "INSERT INTO " + TABLE_NAME + '(' +
TABLE_ID + ',' +
SERVER_UUID + ',' +
VALUE_1 + ',' +
VALUE_2 + ',' +
VALUE_3 + ',' +
VALUE_4 + ',' +
VALUE_5 +
") VALUES (?,?,?,?,?,?, ?)";
return new ExecBatchStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
int maxColumnSize = Math.min(table.getMaxColumnSize(), 5); // Limit to maximum 5 columns, or how many column names there are.
for (Object[] row : table.getRows()) {
statement.setInt(1, tableID);
statement.setString(2, serverUUID.toString());
for (int i = 0; i < maxColumnSize; i++) {
Object value = row[i];
setStringOrNull(statement, 3 + i, value != null ? value.toString() : null);
}
// Rest are set null if not 5 columns wide.
for (int i = maxColumnSize; i < 5; i++) {
statement.setNull(3 + i, Types.VARCHAR);
}
statement.addBatch();
}
}
};
}
private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
if (value != null) {
statement.setString(index, value);
} else {
statement.setNull(index, Types.VARCHAR);
}
}
private Query<Integer> tableID() {
String sql = SELECT + ExtensionTableProviderTable.ID +
FROM + ExtensionTableProviderTable.TABLE_NAME +
WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" +
AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
" LIMIT 1";
return new QueryStatement<Integer>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID);
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
if (set.next()) {
int id = set.getInt(ExtensionTableProviderTable.ID);
if (!set.wasNull()) {
return id;
}
}
throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName);
}
};
}
}

View File

@ -0,0 +1,81 @@
/*
* 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 GNU 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
* GNU 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.extension.table;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
/**
* Utility for accessing implementation variables inside Table.Factory object.
*
* @author Rsl1122
*/
public class TableAccessor {
private TableAccessor() {
/* Static method class */
}
public static Color getColor(Table.Factory factory) {
return factory.color;
}
public static void setColor(Table.Factory factory, Color color) {
factory.color = color;
}
public static String getTableName(Table.Factory factory) {
return factory.tableName;
}
public static void setTableName(Table.Factory factory, String tableName) {
factory.tableName = tableName;
}
public static String getTabName(Table.Factory factory) {
return factory.tabName;
}
public static void setTabName(Table.Factory factory, String tabName) {
factory.tabName = tabName;
}
public static int getTabPriority(Table.Factory factory) {
return factory.tabPriority;
}
public static void setTabPriority(Table.Factory factory, int tabPriority) {
factory.tabPriority = tabPriority;
}
public static ElementOrder[] getTabOrder(Table.Factory factory) {
return factory.tabOrder;
}
public static void setTabOrder(Table.Factory factory, ElementOrder[] tabOrder) {
factory.tabOrder = tabOrder;
}
public static Icon getTabIcon(Table.Factory factory) {
return factory.tabIcon;
}
public static void setTabIcon(Table.Factory factory, Icon tabIcon) {
factory.tabIcon = tabIcon;
}
}

View File

@ -126,11 +126,11 @@ public class PlanSystem implements SubSystem {
localeSystem,
versionCheckSystem,
databaseSystem,
importSystem,
exportSystem,
webServerSystem,
processing,
serverInfo,
importSystem,
exportSystem,
infoSystem,
cacheSystem,
listenerSystem,

View File

@ -18,10 +18,10 @@ package com.djrapitops.plan.system.export;
import com.djrapitops.plan.system.SubSystem;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.ExportSettings;
import com.djrapitops.plugin.api.Check;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -35,6 +35,7 @@ import javax.inject.Singleton;
public class ExportSystem implements SubSystem {
private final PlanConfig config;
private final ServerInfo serverInfo;
private final Processing processing;
private final HtmlExport htmlExport;
private final ConnectionSystem connectionSystem;
@ -42,11 +43,13 @@ public class ExportSystem implements SubSystem {
@Inject
public ExportSystem(
PlanConfig config,
ServerInfo serverInfo,
Processing processing,
HtmlExport htmlExport,
ConnectionSystem connectionSystem
) {
this.config = config;
this.serverInfo = serverInfo;
this.processing = processing;
this.htmlExport = htmlExport;
this.connectionSystem = connectionSystem;
@ -54,7 +57,7 @@ public class ExportSystem implements SubSystem {
@Override
public void enable() {
if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) {
if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) {
return;
}
if (config.isTrue(ExportSettings.JS_AND_CSS)) {
@ -69,7 +72,10 @@ public class ExportSystem implements SubSystem {
processing.submitNonCritical(htmlExport::exportAvailablePlayers);
}
if (config.isTrue(ExportSettings.SERVER_PAGE)) {
processing.submitNonCritical(htmlExport::exportAvailableServerPages);
processing.submitNonCritical(() -> {
htmlExport.cacheNetworkPage();
htmlExport.exportAvailableServerPages();
});
}
}

View File

@ -16,7 +16,6 @@
*/
package com.djrapitops.plan.system.export;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.ParseException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.container.BaseUser;
@ -32,9 +31,13 @@ import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.ExportSettings;
import com.djrapitops.plan.system.settings.theme.Theme;
import com.djrapitops.plan.system.settings.theme.ThemeVal;
import com.djrapitops.plan.system.webserver.cache.PageId;
import com.djrapitops.plan.system.webserver.cache.ResponseCache;
import com.djrapitops.plan.system.webserver.pages.json.JSONFactory;
import com.djrapitops.plan.system.webserver.response.pages.NetworkPageResponse;
import com.djrapitops.plan.utilities.html.pages.InspectPage;
import com.djrapitops.plan.utilities.html.pages.NetworkPage;
import com.djrapitops.plan.utilities.html.pages.PageFactory;
import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.utilities.Verify;
@ -55,7 +58,6 @@ import java.util.*;
@Singleton
public class HtmlExport extends SpecificExport {
private final PlanPlugin plugin;
private final PlanConfig config;
private final Theme theme;
private final PlanFiles files;
@ -66,18 +68,17 @@ public class HtmlExport extends SpecificExport {
@Inject
public HtmlExport(
PlanPlugin plugin,
PlanFiles files,
PlanConfig config,
Theme theme,
DBSystem dbSystem,
PageFactory pageFactory,
JSONFactory jsonFactory,
ServerInfo serverInfo,
ConnectionSystem connectionSystem,
ErrorHandler errorHandler
) {
super(files, serverInfo);
this.plugin = plugin;
super(files, jsonFactory, serverInfo);
this.config = config;
this.theme = theme;
this.files = files;
@ -93,7 +94,7 @@ public class HtmlExport extends SpecificExport {
}
public void exportServer(UUID serverUUID) {
if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) {
if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) {
return;
}
dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID))
@ -122,7 +123,7 @@ public class HtmlExport extends SpecificExport {
}
public void exportCachedPlayerPage(UUID playerUUID) {
if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) {
if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) {
return;
}
@ -166,6 +167,35 @@ public class HtmlExport extends SpecificExport {
}
}
public void cacheNetworkPage() {
if (serverInfo.getServer().isNotProxy()) {
return;
}
NetworkPage networkPage = pageFactory.networkPage();
ResponseCache.cacheResponse(PageId.SERVER.of(serverInfo.getServerUUID()), () -> {
try {
return new NetworkPageResponse(networkPage);
} catch (ParseException e) {
errorHandler.log(L.WARN, this.getClass(), e);
return null;
}
});
}
public void exportNetworkPage() {
if (serverInfo.getServer().isNotProxy()) {
return;
}
cacheNetworkPage();
try {
exportAvailableServerPage(serverInfo.getServerUUID(), serverInfo.getServer().getName());
} catch (IOException e) {
errorHandler.log(L.WARN, this.getClass(), e);
}
}
public void exportAvailableServerPages() {
try {
Map<UUID, String> serverNames = dbSystem.getDatabase().query(ServerQueries.fetchServerNames());
@ -231,15 +261,11 @@ public class HtmlExport extends SpecificExport {
public void exportPlugins() {
String[] resources = new String[]{
"web/plugins/bootstrap/css/bootstrap.css",
"web/plugins/node-waves/waves.css",
"web/plugins/node-waves/waves.js",
"web/plugins/animate-css/animate.css",
"web/plugins/jquery-slimscroll/jquery.slimscroll.js",
"web/plugins/jquery/jquery.min.js",
"web/plugins/bootstrap/js/bootstrap.js",
"web/plugins/jquery-datatable/skin/bootstrap/js/dataTables.bootstrap.js",
"web/plugins/jquery-datatable/jquery.dataTables.js",
"web/plugins/fullcalendar/fullcalendar.min.js",
"web/plugins/fullcalendar/fullcalendar.min.css",
"web/plugins/momentjs/moment.js",

View File

@ -24,6 +24,7 @@ import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.ExportSettings;
import com.djrapitops.plan.system.webserver.pages.json.JSONFactory;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
@ -55,10 +56,11 @@ public class JSONExport extends SpecificExport {
PlanConfig config,
DBSystem dbSystem,
ServerInfo serverInfo,
JSONFactory jsonFactory,
ResponseFactory responseFactory,
ErrorHandler errorHandler
) {
super(files, serverInfo);
super(files, jsonFactory, serverInfo);
this.config = config;
this.dbSystem = dbSystem;
this.responseFactory = responseFactory;

View File

@ -20,8 +20,8 @@ import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.cache.PageId;
import com.djrapitops.plan.system.webserver.cache.ResponseCache;
import com.djrapitops.plan.system.webserver.pages.json.JSONFactory;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plugin.api.Check;
import java.io.File;
import java.io.IOException;
@ -30,6 +30,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@ -41,17 +42,16 @@ import java.util.UUID;
public abstract class SpecificExport {
private final PlanFiles files;
private final ServerInfo serverInfo;
private final JSONFactory jsonFactory; // Hacky, TODO export needs a rework
protected final ServerInfo serverInfo;
private final boolean usingProxy;
protected SpecificExport(
SpecificExport(
PlanFiles files,
ServerInfo serverInfo
JSONFactory jsonFactory, ServerInfo serverInfo
) {
this.files = files;
this.jsonFactory = jsonFactory;
this.serverInfo = serverInfo;
usingProxy = Check.isBungeeAvailable() || Check.isVelocityAvailable();
}
protected File getFolder() {
@ -78,19 +78,19 @@ public abstract class SpecificExport {
Files.write(to.toPath(), lines, StandardCharsets.UTF_8);
}
protected File getServerFolder() {
File getServerFolder() {
File server = new File(getFolder(), "server");
server.mkdirs();
return server;
}
protected File getPlayerFolder() {
File getPlayerFolder() {
File player = new File(getFolder(), "player");
player.mkdirs();
return player;
}
protected void exportPlayerPage(String playerName, String html) throws IOException {
void exportPlayerPage(String playerName, String html) throws IOException {
List<String> lines = Arrays.asList(html.replace("../", "../../").split("\n"));
File htmlLocation = new File(getPlayerFolder(), URLEncoder.encode(playerName, "UTF-8").replace(".", "%2E"));
@ -100,7 +100,7 @@ public abstract class SpecificExport {
export(exportFile, lines);
}
protected void exportAvailablePlayerPage(UUID playerUUID, String name) throws IOException {
void exportAvailablePlayerPage(UUID playerUUID, String name) throws IOException {
Response response = ResponseCache.loadResponse(PageId.PLAYER.of(playerUUID));
if (response == null) {
return;
@ -110,7 +110,7 @@ public abstract class SpecificExport {
exportPlayerPage(name, html);
}
protected void exportAvailableServerPage(UUID serverUUID, String serverName) throws IOException {
void exportAvailableServerPage(UUID serverUUID, String serverName) throws IOException {
Response response = ResponseCache.loadResponse(PageId.SERVER.of(serverUUID));
if (response == null) {
@ -121,19 +121,23 @@ public abstract class SpecificExport {
.replace("href=\"plugins/", "href=\"../plugins/")
.replace("href=\"css/", "href=\"../css/")
.replace("src=\"plugins/", "src=\"../plugins/")
.replace("src=\"js/", "src=\"../js/");
.replace("src=\"js/", "src=\"../js/")
.replace("../json/players?serverName=" + serverName, "./players_table.json");
File htmlLocation;
if (usingProxy) {
if (serverInfo.getServer().isProxy()) {
if (serverUUID.equals(serverInfo.getServerUUID())) {
htmlLocation = new File(getFolder(), "network");
} else {
htmlLocation = new File(getServerFolder(), URLEncoder.encode(serverName, "UTF-8").replace(".", "%2E"));
html = html.replace("../", "../../");
exportPlayersTableJSON(htmlLocation, serverUUID);
}
} else {
htmlLocation = getServerFolder();
exportPlayersTableJSON(htmlLocation, serverUUID);
}
htmlLocation.mkdirs();
File exportFile = new File(htmlLocation, "index.html");
@ -141,4 +145,9 @@ public abstract class SpecificExport {
export(exportFile, lines);
}
private void exportPlayersTableJSON(File htmlLocation, UUID serverUUID) throws IOException {
File exportFile = new File(htmlLocation, "players_table.json");
export(exportFile, Collections.singletonList(jsonFactory.serverPlayersTableJSON(serverUUID)));
}
}

View File

@ -29,7 +29,6 @@ import com.djrapitops.plan.system.info.request.InfoRequestFactory;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.WebServer;
import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.logging.console.PluginLogger;
import dagger.Lazy;
@ -159,11 +158,8 @@ public abstract class InfoSystem implements SubSystem {
* @throws WebException If fails.
*/
public void requestSetUp(String addressToRequestServer) throws WebException {
if (Check.isBungeeAvailable()) {
throw new BadRequestException("Method not available on Bungee.");
}
if (Check.isVelocityAvailable()) {
throw new BadRequestException("Method not available on Velocity.");
if (serverInfo.getServer().isProxy()) {
throw new BadRequestException("Method not available on a Proxy server.");
}
Server bungee = new Server(-1, null, "Bungee", addressToRequestServer, -1);
String addressOfThisServer = webServer.get().getAccessAddress();

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.request.InfoRequest;
import com.djrapitops.plan.system.webserver.Request;
import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.pages.PageHandler;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
@ -30,7 +31,6 @@ import com.djrapitops.plugin.utilities.Verify;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
/**
* PageHandler for /info/requestclassname pages.
@ -62,7 +62,7 @@ public class InfoRequestPageHandler implements PageHandler {
}
@Override
public Response getResponse(Request request, List<String> target) throws WebException {
public Response getResponse(Request request, RequestTarget target) throws WebException {
int responseCode = 200;
try {
@ -85,7 +85,7 @@ public class InfoRequestPageHandler implements PageHandler {
responseCode = getResponseCodeFor(e);
throw e;
} finally {
connectionSystem.getConnectionLog().logConnectionFrom(request.getRemoteAddress(), request.getTarget(), responseCode);
connectionSystem.getConnectionLog().logConnectionFrom(request.getRemoteAddress(), request.getTargetString(), responseCode);
}
}

View File

@ -105,7 +105,10 @@ public class CacheAnalysisPageRequest extends InfoRequestWithVariables implement
}
if (config.get(ExportSettings.SERVER_PAGE)) {
processing.submitNonCritical(() -> htmlExport.exportServer(serverUUID));
processing.submitNonCritical(() -> {
htmlExport.exportNetworkPage();
htmlExport.exportServer(serverUUID);
});
}
if (config.get(ExportSettings.SERVER_JSON)) {
processing.submitNonCritical(() -> jsonExport.exportServerJSON(serverUUID));

View File

@ -22,9 +22,9 @@ import com.djrapitops.plan.api.exceptions.connection.GatewayException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.response.DefaultResponses;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.utilities.Verify;
import java.util.Map;
@ -37,9 +37,11 @@ import java.util.UUID;
*/
public class CheckConnectionRequest extends InfoRequestWithVariables {
private final ServerInfo serverInfo;
private final ConnectionSystem connectionSystem;
CheckConnectionRequest(String webServerAddress, ConnectionSystem connectionSystem) {
CheckConnectionRequest(String webServerAddress, ServerInfo serverInfo, ConnectionSystem connectionSystem) {
this.serverInfo = serverInfo;
this.connectionSystem = connectionSystem;
Verify.nullCheck(webServerAddress, () -> new IllegalArgumentException("webServerAddress can not be null."));
@ -47,7 +49,8 @@ public class CheckConnectionRequest extends InfoRequestWithVariables {
variables.put("continue", "yes");
}
CheckConnectionRequest(ConnectionSystem connectionSystem) {
CheckConnectionRequest(ServerInfo serverInfo, ConnectionSystem connectionSystem) {
this.serverInfo = serverInfo;
this.connectionSystem = connectionSystem;
}
@ -60,7 +63,7 @@ public class CheckConnectionRequest extends InfoRequestWithVariables {
public Response handleRequest(Map<String, String> variables) throws WebException {
// Available variables: sender, address
if (Check.isBungeeAvailable() || Check.isVelocityAvailable()) {
if (serverInfo.getServer().isProxy()) {
attemptConnection(variables);
}
@ -82,7 +85,7 @@ public class CheckConnectionRequest extends InfoRequestWithVariables {
Server bukkit = new Server(-1, serverUUID, "", address, -1);
try {
connectionSystem.sendInfoRequest(new CheckConnectionRequest(connectionSystem), bukkit);
connectionSystem.sendInfoRequest(new CheckConnectionRequest(serverInfo, connectionSystem), bukkit);
} catch (ConnectionFailException e) {
throw new GatewayException(e.getMessage());
}

View File

@ -123,15 +123,15 @@ public class InfoRequestFactory {
}
public SaveDBSettingsRequest saveDBSettingsRequest() {
return new SaveDBSettingsRequest(plugin.get(), config.get(), logger.get(), runnableFactory.get());
return new SaveDBSettingsRequest(plugin.get(), config.get(), serverInfo.get(), logger.get(), runnableFactory.get());
}
public SetupRequest sendDBSettingsRequest(String addressOfThisServer) {
return new SendDBSettingsRequest(addressOfThisServer, this, connectionSystem.get());
return new SendDBSettingsRequest(addressOfThisServer, serverInfo.get(), this, connectionSystem.get());
}
public CheckConnectionRequest checkConnectionRequest(String webAddress) {
return new CheckConnectionRequest(webAddress, connectionSystem.get());
return new CheckConnectionRequest(webAddress, serverInfo.get(), connectionSystem.get());
}
@Singleton
@ -169,7 +169,7 @@ public class InfoRequestFactory {
}
CheckConnectionRequest checkConnectionRequest() {
return new CheckConnectionRequest(factory.connectionSystem.get());
return new CheckConnectionRequest(factory.serverInfo.get(), factory.connectionSystem.get());
}
GenerateRequest generateAnalysisPageRequest() {
@ -204,6 +204,7 @@ public class InfoRequestFactory {
return new SaveDBSettingsRequest(
factory.plugin.get(),
factory.config.get(),
factory.serverInfo.get(),
factory.logger.get(),
factory.runnableFactory.get()
);
@ -211,6 +212,7 @@ public class InfoRequestFactory {
SetupRequest sendDBSettingsRequest() {
return new SendDBSettingsRequest(
factory.serverInfo.get(),
factory,
factory.connectionSystem.get()
);

View File

@ -20,13 +20,13 @@ import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.connection.BadRequestException;
import com.djrapitops.plan.api.exceptions.connection.InternalErrorException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
import com.djrapitops.plan.system.settings.paths.PluginSettings;
import com.djrapitops.plan.system.webserver.response.DefaultResponses;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse;
import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
@ -46,17 +46,19 @@ public class SaveDBSettingsRequest extends InfoRequestWithVariables implements S
private final PlanPlugin plugin;
private final PlanConfig config;
private final ServerInfo serverInfo;
private final PluginLogger logger;
private final RunnableFactory runnableFactory;
SaveDBSettingsRequest(
PlanPlugin plugin,
PlanConfig config,
PluginLogger logger,
ServerInfo serverInfo, PluginLogger logger,
RunnableFactory runnableFactory
) {
this.plugin = plugin;
this.config = config;
this.serverInfo = serverInfo;
this.logger = logger;
this.runnableFactory = runnableFactory;
@ -75,11 +77,8 @@ public class SaveDBSettingsRequest extends InfoRequestWithVariables implements S
@Override
public Response handleRequest(Map<String, String> variables) throws WebException {
if (Check.isBungeeAvailable()) {
return new BadRequestResponse("Not supposed to be called on a Bungee server");
}
if (Check.isVelocityAvailable()) {
return new BadRequestResponse("Not supposed to be called on a Velocity server");
if (serverInfo.getServer().isProxy()) {
return new BadRequestResponse("Not supposed to be called on a proxy server");
}
if (config.isFalse(PluginSettings.BUNGEE_COPY_CONFIG)) {
return new BadRequestResponse("Bungee config settings overridden on this server.");

View File

@ -22,10 +22,10 @@ import com.djrapitops.plan.api.exceptions.connection.GatewayException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.response.DefaultResponses;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse;
import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.utilities.Verify;
import java.net.SocketException;
@ -39,20 +39,23 @@ import java.util.UUID;
*/
public class SendDBSettingsRequest extends InfoRequestWithVariables implements SetupRequest {
private final ServerInfo serverInfo;
private final InfoRequestFactory infoRequestFactory;
private final ConnectionSystem connectionSystem;
SendDBSettingsRequest(
InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem
ServerInfo serverInfo, InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem
) {
this.serverInfo = serverInfo;
this.infoRequestFactory = infoRequestFactory;
this.connectionSystem = connectionSystem;
}
SendDBSettingsRequest(
String webServerAddress,
InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem
ServerInfo serverInfo, InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem
) {
this.serverInfo = serverInfo;
this.infoRequestFactory = infoRequestFactory;
this.connectionSystem = connectionSystem;
@ -68,11 +71,8 @@ public class SendDBSettingsRequest extends InfoRequestWithVariables implements S
@Override
public Response handleRequest(Map<String, String> variables) throws WebException {
// Available variables: sender, address
if (Check.isBukkitAvailable()) {
return new BadRequestResponse("Not supposed to be called on a Bukkit server");
}
if (Check.isSpongeAvailable()) {
return new BadRequestResponse("Not supposed to be called on a Sponge server");
if (serverInfo.getServer().isNotProxy()) {
return new BadRequestResponse("Not supposed to be called on a non proxy server");
}
String address = variables.get("address");

Some files were not shown because too many files have changed in this diff Show More