mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-09-29 23:17:28 +02:00
Full System no longer required for database tests
- Extracted ErrorLogger interface - Created a new Component for Database tests - Added TestAPFModule and TestSystemObjectBindingModule - Database tests no longer enable the full system Affects issues: - Close #1401
This commit is contained in:
parent
c8f1585ee1
commit
6effdbec39
@ -37,7 +37,11 @@ public class BukkitCommand implements CommandExecutor, TabCompleter {
|
|||||||
private final ErrorLogger errorLogger;
|
private final ErrorLogger errorLogger;
|
||||||
private final Subcommand command;
|
private final Subcommand command;
|
||||||
|
|
||||||
public BukkitCommand(RunnableFactory runnableFactory, ErrorLogger errorLogger, Subcommand command) {
|
public BukkitCommand(
|
||||||
|
RunnableFactory runnableFactory,
|
||||||
|
ErrorLogger errorLogger,
|
||||||
|
Subcommand command
|
||||||
|
) {
|
||||||
this.runnableFactory = runnableFactory;
|
this.runnableFactory = runnableFactory;
|
||||||
this.errorLogger = errorLogger;
|
this.errorLogger = errorLogger;
|
||||||
this.command = command;
|
this.command = command;
|
||||||
|
@ -41,8 +41,8 @@ public class BungeeCommand extends Command implements TabExecutor {
|
|||||||
|
|
||||||
public BungeeCommand(
|
public BungeeCommand(
|
||||||
RunnableFactory runnableFactory,
|
RunnableFactory runnableFactory,
|
||||||
ErrorLogger errorLogger, Subcommand command,
|
ErrorLogger errorLogger,
|
||||||
String name
|
Subcommand command, String name
|
||||||
) {
|
) {
|
||||||
super(name);
|
super(name);
|
||||||
this.runnableFactory = runnableFactory;
|
this.runnableFactory = runnableFactory;
|
||||||
|
@ -293,6 +293,13 @@ public class PlanSystem implements SubSystem {
|
|||||||
return extensionService;
|
return extensionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Originally visible for testing purposes.
|
||||||
|
*
|
||||||
|
* @return the error logger of the system
|
||||||
|
* @deprecated A smell, dagger should be used to construct things instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public ErrorLogger getErrorLogger() {
|
public ErrorLogger getErrorLogger() {
|
||||||
return errorLogger;
|
return errorLogger;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ import com.djrapitops.plan.settings.config.PlanConfig;
|
|||||||
import com.djrapitops.plan.settings.locale.Locale;
|
import com.djrapitops.plan.settings.locale.Locale;
|
||||||
import com.djrapitops.plan.settings.locale.LocaleSystem;
|
import com.djrapitops.plan.settings.locale.LocaleSystem;
|
||||||
import com.djrapitops.plan.storage.file.JarResource;
|
import com.djrapitops.plan.storage.file.JarResource;
|
||||||
|
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||||
|
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
@ -70,4 +72,10 @@ public class SystemObjectProvidingModule {
|
|||||||
return plugin.getDataFolder();
|
return plugin.getDataFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ErrorLogger provideErrorLogger(PluginErrorLogger errorLogger) {
|
||||||
|
return errorLogger;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -81,4 +81,12 @@ public class ErrorContext implements Serializable {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ErrorContext{" +
|
||||||
|
"related=" + related +
|
||||||
|
", whatToDo='" + whatToDo + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,284 +16,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.utilities.logging;
|
package com.djrapitops.plan.utilities.logging;
|
||||||
|
|
||||||
import com.djrapitops.plan.PlanPlugin;
|
|
||||||
import com.djrapitops.plan.delivery.formatting.Formatters;
|
|
||||||
import com.djrapitops.plan.exceptions.ExceptionWithContext;
|
import com.djrapitops.plan.exceptions.ExceptionWithContext;
|
||||||
import com.djrapitops.plan.identification.properties.ServerProperties;
|
|
||||||
import com.djrapitops.plan.storage.file.PlanFiles;
|
|
||||||
import com.djrapitops.plan.utilities.java.Lists;
|
|
||||||
import com.djrapitops.plan.version.VersionChecker;
|
|
||||||
import com.djrapitops.plugin.logging.L;
|
import com.djrapitops.plugin.logging.L;
|
||||||
import com.djrapitops.plugin.logging.console.PluginLogger;
|
|
||||||
import com.djrapitops.plugin.logging.error.ErrorHandler;
|
import com.djrapitops.plugin.logging.error.ErrorHandler;
|
||||||
import dagger.Lazy;
|
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
public interface ErrorLogger extends ErrorHandler {
|
||||||
import javax.inject.Singleton;
|
default <T extends ExceptionWithContext> void log(L level, T throwable) {
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* New logger that logs errors to specific files.
|
|
||||||
*
|
|
||||||
* @author Rsl1122
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class ErrorLogger implements ErrorHandler {
|
|
||||||
|
|
||||||
private final PlanPlugin plugin;
|
|
||||||
private final PluginLogger logger;
|
|
||||||
private final PlanFiles files;
|
|
||||||
private final Lazy<ServerProperties> serverProperties;
|
|
||||||
private final Lazy<VersionChecker> versionChecker;
|
|
||||||
private final Lazy<Formatters> formatters;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ErrorLogger(
|
|
||||||
PlanPlugin plugin,
|
|
||||||
PluginLogger logger,
|
|
||||||
PlanFiles files,
|
|
||||||
Lazy<ServerProperties> serverProperties,
|
|
||||||
Lazy<VersionChecker> versionChecker,
|
|
||||||
Lazy<Formatters> formatters
|
|
||||||
) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.logger = logger;
|
|
||||||
this.files = files;
|
|
||||||
this.serverProperties = serverProperties;
|
|
||||||
this.versionChecker = versionChecker;
|
|
||||||
this.formatters = formatters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends ExceptionWithContext> void log(L level, T throwable) {
|
|
||||||
log(level, (Throwable) throwable, throwable.getContext().orElse(ErrorContext.builder().related("Missing Context").build()));
|
log(level, (Throwable) throwable, throwable.getContext().orElse(ErrorContext.builder().related("Missing Context").build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void log(L level, Throwable throwable, ErrorContext context) {
|
void log(L level, Throwable throwable, ErrorContext context);
|
||||||
String errorName = throwable.getClass().getSimpleName();
|
|
||||||
String hash = hash(throwable);
|
|
||||||
Path logsDir = files.getLogsDirectory();
|
|
||||||
Path errorLog = logsDir.resolve(errorName + "-" + hash + ".txt");
|
|
||||||
mergeAdditionalContext(throwable, context);
|
|
||||||
if (Files.exists(errorLog)) {
|
|
||||||
logExisting(errorLog, throwable, context, hash);
|
|
||||||
} else {
|
|
||||||
logNew(errorLog, throwable, context, hash);
|
|
||||||
}
|
|
||||||
logToConsole(level, errorLog, throwable, context);
|
|
||||||
if (L.CRITICAL == level) {
|
|
||||||
plugin.getPluginLogger().error("CRITICAL error triggered a plugin shutdown.");
|
|
||||||
plugin.onDisable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mergeAdditionalContext(Throwable throwable, ErrorContext context) {
|
|
||||||
Throwable cause = throwable.getCause();
|
|
||||||
while (cause != null) {
|
|
||||||
if (cause instanceof ExceptionWithContext) {
|
|
||||||
((ExceptionWithContext) cause).getContext().ifPresent(context::merge);
|
|
||||||
}
|
|
||||||
cause = cause.getCause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logExisting(Path errorLog, Throwable throwable, ErrorContext context, String hash) {
|
|
||||||
// Read existing
|
|
||||||
List<String> lines;
|
|
||||||
try (Stream<String> read = Files.lines(errorLog)) {
|
|
||||||
lines = read.collect(Collectors.toList());
|
|
||||||
} catch (IOException e) {
|
|
||||||
logAfterReadError(errorLog, throwable, context, hash);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int occurrences = getOccurrences(lines) + 1;
|
|
||||||
List<String> newLines = buildNewLines(context, lines, occurrences, hash);
|
|
||||||
overwrite(errorLog, throwable, newLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void overwrite(Path errorLog, Throwable throwable, List<String> newLines) {
|
|
||||||
try {
|
|
||||||
Files.write(errorLog, newLines, StandardOpenOption.TRUNCATE_EXISTING);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throwable.addSuppressed(e);
|
|
||||||
Logger.getGlobal().log(Level.SEVERE, "Failed to log Plan error, see suppressed.", throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> buildNewLines(ErrorContext context, List<String> lines, int occurrences, String hash) {
|
|
||||||
Lists.Builder<String> builder = Lists.builder(String.class)
|
|
||||||
.add(hash + " - Last occurred: " + getTimeStamp() + " Occurrences: " + occurrences);
|
|
||||||
// 5 contexts are enough.
|
|
||||||
if (occurrences <= 5) {
|
|
||||||
builder = buildContext(context, occurrences, builder);
|
|
||||||
}
|
|
||||||
int lineCount = lines.size();
|
|
||||||
int firstContextLineIndex = findFirstContextLine(lines, lineCount);
|
|
||||||
return builder.addAll(lines.subList(firstContextLineIndex, lineCount))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getTimeStamp() {
|
|
||||||
return formatters.get().iso8601NoClockLong().apply(System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Lists.Builder<String> buildContext(ErrorContext context, int occurrences, Lists.Builder<String> builder) {
|
|
||||||
return builder.add("---- Context " + occurrences + " ----")
|
|
||||||
.add("Plan v" + versionChecker.get().getCurrentVersion())
|
|
||||||
.add(serverProperties.get().getName() + " " + serverProperties.get().getVersion())
|
|
||||||
.add("Server v" + serverProperties.get().getImplVersion())
|
|
||||||
.add("")
|
|
||||||
.addAll(context.toLines())
|
|
||||||
.add("");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logAfterReadError(Path errorLog, Throwable throwable, ErrorContext context, String hash) {
|
|
||||||
logger.error("Failed to read " + errorLog + " deleting file");
|
|
||||||
try {
|
|
||||||
Files.deleteIfExists(errorLog);
|
|
||||||
} catch (IOException ioException) {
|
|
||||||
logger.error("Failed to delete " + errorLog);
|
|
||||||
}
|
|
||||||
logNew(errorLog, throwable, context, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getOccurrences(List<String> lines) {
|
|
||||||
String occurLine = lines.get(0);
|
|
||||||
return Integer.parseInt(StringUtils.splitByWholeSeparator(occurLine, ": ")[2].trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
private int findFirstContextLine(List<String> lines, int lineCount) {
|
|
||||||
int firstContextLineIndex = 0;
|
|
||||||
for (int i = 0; i < lineCount; i++) {
|
|
||||||
if (lines.get(i).contains("---- Context")) {
|
|
||||||
firstContextLineIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firstContextLineIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logToConsole(L level, Path errorLog, Throwable throwable, ErrorContext context) {
|
|
||||||
String errorName = throwable.getClass().getSimpleName();
|
|
||||||
String errorMsg = throwable.getMessage();
|
|
||||||
String errorLocation = errorLog.toString();
|
|
||||||
logger.log(level,
|
|
||||||
"Ran into " + errorName + " - logged to " + errorLocation,
|
|
||||||
"(INCLUDE CONTENTS OF THE FILE IN ANY REPORTS)",
|
|
||||||
context.getWhatToDo().map(td -> "What to do: " + td).orElse("Error msg: \"" + errorMsg + "\"")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logNew(Path errorLog, Throwable throwable, ErrorContext context, String hash) {
|
|
||||||
List<String> stacktrace = buildReadableStacktrace(new ArrayList<>(), throwable);
|
|
||||||
List<String> lines = Lists.builder(String.class)
|
|
||||||
.add(hash + " - Last occurred: " + getTimeStamp() + " Occurrences: 1")
|
|
||||||
.apply(builder -> this.buildContext(context, 1, builder))
|
|
||||||
.add("---- Stacktrace ----")
|
|
||||||
.addAll(stacktrace)
|
|
||||||
.build();
|
|
||||||
writeNew(errorLog, throwable, lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeNew(Path errorLog, Throwable throwable, List<String> lines) {
|
|
||||||
try {
|
|
||||||
Files.createDirectories(errorLog.getParent());
|
|
||||||
Files.write(errorLog, lines, StandardOpenOption.CREATE_NEW, StandardOpenOption.APPEND);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throwable.addSuppressed(e);
|
|
||||||
Logger.getGlobal().log(Level.SEVERE, "Failed to log Plan error, see suppressed.", throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void log(L level, Class caughtBy, Throwable throwable) {
|
default void log(L level, Class caughtBy, Throwable throwable) {
|
||||||
log(level, throwable, ErrorContext.builder()
|
log(level, throwable, ErrorContext.builder()
|
||||||
.related("Caught by " + caughtBy.getName())
|
.related("Caught by " + caughtBy.getName())
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String hash(Throwable e) {
|
|
||||||
int seed = 0;
|
|
||||||
Throwable cause = e;
|
|
||||||
String previousLine = null;
|
|
||||||
while (cause != null) {
|
|
||||||
for (StackTraceElement element : cause.getStackTrace()) {
|
|
||||||
String asLine = element.toString();
|
|
||||||
if (asLine.equals(previousLine)) continue;
|
|
||||||
if (seed == 0) {
|
|
||||||
seed = asLine.hashCode();
|
|
||||||
} else {
|
|
||||||
seed *= asLine.hashCode();
|
|
||||||
}
|
|
||||||
previousLine = asLine;
|
|
||||||
}
|
|
||||||
cause = cause.getCause();
|
|
||||||
}
|
|
||||||
return DigestUtils.sha256Hex(Integer.toString(seed)).substring(0, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> buildReadableStacktrace(List<String> trace, Throwable e) {
|
|
||||||
trace.add(e.toString());
|
|
||||||
Deduplicator deduplicator = new Deduplicator();
|
|
||||||
for (StackTraceElement element : e.getStackTrace()) {
|
|
||||||
String line = element.toString();
|
|
||||||
deduplicator.addLines(trace, line);
|
|
||||||
}
|
|
||||||
deduplicator.addLeftoverDuplicateCountLine(trace);
|
|
||||||
Throwable[] suppressed = e.getSuppressed();
|
|
||||||
if (suppressed.length > 0) {
|
|
||||||
for (Throwable suppressedThrowable : suppressed) {
|
|
||||||
trace.add(" Suppressed:");
|
|
||||||
for (String line : buildReadableStacktrace(new ArrayList<>(), suppressedThrowable)) {
|
|
||||||
trace.add(" " + line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Throwable cause = e.getCause();
|
|
||||||
if (cause != null) {
|
|
||||||
trace.add("Caused by:");
|
|
||||||
buildReadableStacktrace(trace, cause);
|
|
||||||
}
|
|
||||||
return trace;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Deduplicator {
|
|
||||||
private String previousLine = null;
|
|
||||||
private String lastDuplicate = null;
|
|
||||||
private int duplicateCount = 0;
|
|
||||||
|
|
||||||
public void addLines(List<String> trace, String line) {
|
|
||||||
if (duplicateCount > 0 && !line.equals(lastDuplicate)) {
|
|
||||||
String returnLine = " x " + duplicateCount;
|
|
||||||
duplicateCount = 1;
|
|
||||||
trace.add(returnLine);
|
|
||||||
trace.add(" " + line);
|
|
||||||
} else if (line.equals(lastDuplicate)) {
|
|
||||||
duplicateCount++;
|
|
||||||
} else if (line.equals(previousLine)) {
|
|
||||||
lastDuplicate = line;
|
|
||||||
duplicateCount = 2;
|
|
||||||
} else {
|
|
||||||
previousLine = line;
|
|
||||||
trace.add(" " + line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addLeftoverDuplicateCountLine(List<String> trace) {
|
|
||||||
if (duplicateCount > 0) {
|
|
||||||
trace.add(" x " + duplicateCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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.utilities.logging;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.PlanPlugin;
|
||||||
|
import com.djrapitops.plan.delivery.formatting.Formatters;
|
||||||
|
import com.djrapitops.plan.exceptions.ExceptionWithContext;
|
||||||
|
import com.djrapitops.plan.identification.properties.ServerProperties;
|
||||||
|
import com.djrapitops.plan.storage.file.PlanFiles;
|
||||||
|
import com.djrapitops.plan.utilities.java.Lists;
|
||||||
|
import com.djrapitops.plan.version.VersionChecker;
|
||||||
|
import com.djrapitops.plugin.logging.L;
|
||||||
|
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||||
|
import dagger.Lazy;
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New logger that logs errors to specific files.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class PluginErrorLogger implements ErrorLogger {
|
||||||
|
|
||||||
|
private final PlanPlugin plugin;
|
||||||
|
private final PluginLogger logger;
|
||||||
|
private final PlanFiles files;
|
||||||
|
private final Lazy<ServerProperties> serverProperties;
|
||||||
|
private final Lazy<VersionChecker> versionChecker;
|
||||||
|
private final Lazy<Formatters> formatters;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public PluginErrorLogger(
|
||||||
|
PlanPlugin plugin,
|
||||||
|
PluginLogger logger,
|
||||||
|
PlanFiles files,
|
||||||
|
Lazy<ServerProperties> serverProperties,
|
||||||
|
Lazy<VersionChecker> versionChecker,
|
||||||
|
Lazy<Formatters> formatters
|
||||||
|
) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.logger = logger;
|
||||||
|
this.files = files;
|
||||||
|
this.serverProperties = serverProperties;
|
||||||
|
this.versionChecker = versionChecker;
|
||||||
|
this.formatters = formatters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void log(L level, Throwable throwable, ErrorContext context) {
|
||||||
|
String errorName = throwable.getClass().getSimpleName();
|
||||||
|
String hash = hash(throwable);
|
||||||
|
Path logsDir = files.getLogsDirectory();
|
||||||
|
Path errorLog = logsDir.resolve(errorName + "-" + hash + ".txt");
|
||||||
|
mergeAdditionalContext(throwable, context);
|
||||||
|
if (Files.exists(errorLog)) {
|
||||||
|
logExisting(errorLog, throwable, context, hash);
|
||||||
|
} else {
|
||||||
|
logNew(errorLog, throwable, context, hash);
|
||||||
|
}
|
||||||
|
logToConsole(level, errorLog, throwable, context);
|
||||||
|
if (L.CRITICAL == level) {
|
||||||
|
plugin.getPluginLogger().error("CRITICAL error triggered a plugin shutdown.");
|
||||||
|
plugin.onDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeAdditionalContext(Throwable throwable, ErrorContext context) {
|
||||||
|
Throwable cause = throwable.getCause();
|
||||||
|
while (cause != null) {
|
||||||
|
if (cause instanceof ExceptionWithContext) {
|
||||||
|
((ExceptionWithContext) cause).getContext().ifPresent(context::merge);
|
||||||
|
}
|
||||||
|
cause = cause.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logExisting(Path errorLog, Throwable throwable, ErrorContext context, String hash) {
|
||||||
|
// Read existing
|
||||||
|
List<String> lines;
|
||||||
|
try (Stream<String> read = Files.lines(errorLog)) {
|
||||||
|
lines = read.collect(Collectors.toList());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logAfterReadError(errorLog, throwable, context, hash);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int occurrences = getOccurrences(lines) + 1;
|
||||||
|
List<String> newLines = buildNewLines(context, lines, occurrences, hash);
|
||||||
|
overwrite(errorLog, throwable, newLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void overwrite(Path errorLog, Throwable throwable, List<String> newLines) {
|
||||||
|
try {
|
||||||
|
Files.write(errorLog, newLines, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throwable.addSuppressed(e);
|
||||||
|
Logger.getGlobal().log(Level.SEVERE, "Failed to log Plan error, see suppressed.", throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> buildNewLines(ErrorContext context, List<String> lines, int occurrences, String hash) {
|
||||||
|
Lists.Builder<String> builder = Lists.builder(String.class)
|
||||||
|
.add(hash + " - Last occurred: " + getTimeStamp() + " Occurrences: " + occurrences);
|
||||||
|
// 5 contexts are enough.
|
||||||
|
if (occurrences <= 5) {
|
||||||
|
builder = buildContext(context, occurrences, builder);
|
||||||
|
}
|
||||||
|
int lineCount = lines.size();
|
||||||
|
int firstContextLineIndex = findFirstContextLine(lines, lineCount);
|
||||||
|
return builder.addAll(lines.subList(firstContextLineIndex, lineCount))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTimeStamp() {
|
||||||
|
return formatters.get().iso8601NoClockLong().apply(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Lists.Builder<String> buildContext(ErrorContext context, int occurrences, Lists.Builder<String> builder) {
|
||||||
|
return builder.add("---- Context " + occurrences + " ----")
|
||||||
|
.add("Plan v" + versionChecker.get().getCurrentVersion())
|
||||||
|
.add(serverProperties.get().getName() + " " + serverProperties.get().getVersion())
|
||||||
|
.add("Server v" + serverProperties.get().getImplVersion())
|
||||||
|
.add("")
|
||||||
|
.addAll(context.toLines())
|
||||||
|
.add("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logAfterReadError(Path errorLog, Throwable throwable, ErrorContext context, String hash) {
|
||||||
|
logger.error("Failed to read " + errorLog + " deleting file");
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(errorLog);
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
logger.error("Failed to delete " + errorLog);
|
||||||
|
}
|
||||||
|
logNew(errorLog, throwable, context, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getOccurrences(List<String> lines) {
|
||||||
|
String occurLine = lines.get(0);
|
||||||
|
return Integer.parseInt(StringUtils.splitByWholeSeparator(occurLine, ": ")[2].trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findFirstContextLine(List<String> lines, int lineCount) {
|
||||||
|
int firstContextLineIndex = 0;
|
||||||
|
for (int i = 0; i < lineCount; i++) {
|
||||||
|
if (lines.get(i).contains("---- Context")) {
|
||||||
|
firstContextLineIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstContextLineIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logToConsole(L level, Path errorLog, Throwable throwable, ErrorContext context) {
|
||||||
|
String errorName = throwable.getClass().getSimpleName();
|
||||||
|
String errorMsg = throwable.getMessage();
|
||||||
|
String errorLocation = errorLog.toString();
|
||||||
|
logger.log(level,
|
||||||
|
"Ran into " + errorName + " - logged to " + errorLocation,
|
||||||
|
"(INCLUDE CONTENTS OF THE FILE IN ANY REPORTS)",
|
||||||
|
context.getWhatToDo().map(td -> "What to do: " + td).orElse("Error msg: \"" + errorMsg + "\"")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logNew(Path errorLog, Throwable throwable, ErrorContext context, String hash) {
|
||||||
|
List<String> stacktrace = buildReadableStacktrace(new ArrayList<>(), throwable);
|
||||||
|
List<String> lines = Lists.builder(String.class)
|
||||||
|
.add(hash + " - Last occurred: " + getTimeStamp() + " Occurrences: 1")
|
||||||
|
.apply(builder -> this.buildContext(context, 1, builder))
|
||||||
|
.add("---- Stacktrace ----")
|
||||||
|
.addAll(stacktrace)
|
||||||
|
.build();
|
||||||
|
writeNew(errorLog, throwable, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeNew(Path errorLog, Throwable throwable, List<String> lines) {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(errorLog.getParent());
|
||||||
|
Files.write(errorLog, lines, StandardOpenOption.CREATE_NEW, StandardOpenOption.APPEND);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throwable.addSuppressed(e);
|
||||||
|
Logger.getGlobal().log(Level.SEVERE, "Failed to log Plan error, see suppressed.", throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String hash(Throwable e) {
|
||||||
|
int seed = 0;
|
||||||
|
Throwable cause = e;
|
||||||
|
String previousLine = null;
|
||||||
|
while (cause != null) {
|
||||||
|
for (StackTraceElement element : cause.getStackTrace()) {
|
||||||
|
String asLine = element.toString();
|
||||||
|
if (asLine.equals(previousLine)) continue;
|
||||||
|
if (seed == 0) {
|
||||||
|
seed = asLine.hashCode();
|
||||||
|
} else {
|
||||||
|
seed *= asLine.hashCode();
|
||||||
|
}
|
||||||
|
previousLine = asLine;
|
||||||
|
}
|
||||||
|
cause = cause.getCause();
|
||||||
|
}
|
||||||
|
return DigestUtils.sha256Hex(Integer.toString(seed)).substring(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> buildReadableStacktrace(List<String> trace, Throwable e) {
|
||||||
|
trace.add(e.toString());
|
||||||
|
Deduplicator deduplicator = new Deduplicator();
|
||||||
|
for (StackTraceElement element : e.getStackTrace()) {
|
||||||
|
String line = element.toString();
|
||||||
|
deduplicator.addLines(trace, line);
|
||||||
|
}
|
||||||
|
deduplicator.addLeftoverDuplicateCountLine(trace);
|
||||||
|
Throwable[] suppressed = e.getSuppressed();
|
||||||
|
if (suppressed.length > 0) {
|
||||||
|
for (Throwable suppressedThrowable : suppressed) {
|
||||||
|
trace.add(" Suppressed:");
|
||||||
|
for (String line : buildReadableStacktrace(new ArrayList<>(), suppressedThrowable)) {
|
||||||
|
trace.add(" " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause != null) {
|
||||||
|
trace.add("Caused by:");
|
||||||
|
buildReadableStacktrace(trace, cause);
|
||||||
|
}
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Deduplicator {
|
||||||
|
private String previousLine = null;
|
||||||
|
private String lastDuplicate = null;
|
||||||
|
private int duplicateCount = 0;
|
||||||
|
|
||||||
|
public void addLines(List<String> trace, String line) {
|
||||||
|
if (duplicateCount > 0 && !line.equals(lastDuplicate)) {
|
||||||
|
String returnLine = " x " + duplicateCount;
|
||||||
|
duplicateCount = 1;
|
||||||
|
trace.add(returnLine);
|
||||||
|
trace.add(" " + line);
|
||||||
|
} else if (line.equals(lastDuplicate)) {
|
||||||
|
duplicateCount++;
|
||||||
|
} else if (line.equals(previousLine)) {
|
||||||
|
lastDuplicate = line;
|
||||||
|
duplicateCount = 2;
|
||||||
|
} else {
|
||||||
|
previousLine = line;
|
||||||
|
trace.add(" " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLeftoverDuplicateCountLine(List<String> trace) {
|
||||||
|
if (duplicateCount > 0) {
|
||||||
|
trace.add(" x " + duplicateCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.version;
|
package com.djrapitops.plan.version;
|
||||||
|
|
||||||
import com.djrapitops.plan.PlanPlugin;
|
|
||||||
import com.djrapitops.plan.SubSystem;
|
import com.djrapitops.plan.SubSystem;
|
||||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
import com.djrapitops.plan.settings.config.paths.PluginSettings;
|
import com.djrapitops.plan.settings.config.paths.PluginSettings;
|
||||||
@ -29,6 +28,7 @@ import com.djrapitops.plugin.api.utility.Version;
|
|||||||
import com.djrapitops.plugin.logging.L;
|
import com.djrapitops.plugin.logging.L;
|
||||||
import com.djrapitops.plugin.logging.console.PluginLogger;
|
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||||
import com.djrapitops.plugin.task.AbsRunnable;
|
import com.djrapitops.plugin.task.AbsRunnable;
|
||||||
|
import com.djrapitops.plugin.task.RunnableFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
@ -49,8 +49,8 @@ public class VersionChecker implements SubSystem {
|
|||||||
private final Locale locale;
|
private final Locale locale;
|
||||||
private final PlanConfig config;
|
private final PlanConfig config;
|
||||||
private final PluginLogger logger;
|
private final PluginLogger logger;
|
||||||
|
private final RunnableFactory runnableFactory;
|
||||||
private final ErrorLogger errorLogger;
|
private final ErrorLogger errorLogger;
|
||||||
private final PlanPlugin plugin;
|
|
||||||
|
|
||||||
private VersionInfo newVersionAvailable;
|
private VersionInfo newVersionAvailable;
|
||||||
|
|
||||||
@ -60,15 +60,15 @@ public class VersionChecker implements SubSystem {
|
|||||||
Locale locale,
|
Locale locale,
|
||||||
PlanConfig config,
|
PlanConfig config,
|
||||||
PluginLogger logger,
|
PluginLogger logger,
|
||||||
ErrorLogger errorLogger,
|
RunnableFactory runnableFactory,
|
||||||
PlanPlugin plugin
|
ErrorLogger errorLogger
|
||||||
) {
|
) {
|
||||||
this.currentVersion = currentVersion;
|
this.currentVersion = currentVersion;
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
this.runnableFactory = runnableFactory;
|
||||||
this.errorLogger = errorLogger;
|
this.errorLogger = errorLogger;
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNewVersionAvailable() {
|
public boolean isNewVersionAvailable() {
|
||||||
@ -108,7 +108,7 @@ public class VersionChecker implements SubSystem {
|
|||||||
if (config.isFalse(PluginSettings.CHECK_FOR_UPDATES)) {
|
if (config.isFalse(PluginSettings.CHECK_FOR_UPDATES)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
plugin.getRunnableFactory().create("VersionChecker", new AbsRunnable() {
|
runnableFactory.create("VersionChecker", new AbsRunnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
checkForUpdates();
|
checkForUpdates();
|
||||||
|
@ -66,12 +66,14 @@ class ShutdownSaveTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setupShutdownSaveObject(@TempDir Path temporaryFolder) throws Exception {
|
void setupShutdownSaveObject(@TempDir Path temporaryFolder) throws Exception {
|
||||||
PlanPluginComponent pluginComponent = DaggerPlanPluginComponent.builder().plan(
|
PlanPluginComponent pluginComponent = DaggerPlanPluginComponent.builder()
|
||||||
PlanPluginMocker.setUp()
|
.bindTemporaryDirectory(temporaryFolder)
|
||||||
.withDataFolder(temporaryFolder.resolve("ShutdownSaveTest").toFile())
|
.plan(
|
||||||
.withLogging()
|
PlanPluginMocker.setUp()
|
||||||
.getPlanMock()
|
.withDataFolder(temporaryFolder.resolve("ShutdownSaveTest").toFile())
|
||||||
).build();
|
.withLogging()
|
||||||
|
.getPlanMock()
|
||||||
|
).build();
|
||||||
PlanSystem system = pluginComponent.system();
|
PlanSystem system = pluginComponent.system();
|
||||||
|
|
||||||
database = system.getDatabaseSystem().getSqLiteFactory().usingFileCalled("test");
|
database = system.getDatabaseSystem().getSqLiteFactory().usingFileCalled("test");
|
||||||
|
@ -131,11 +131,11 @@ public interface DatabaseTest extends DatabaseTestPreparer {
|
|||||||
|
|
||||||
TestPluginLogger logger = new TestPluginLogger();
|
TestPluginLogger logger = new TestPluginLogger();
|
||||||
new DBCleanTask(
|
new DBCleanTask(
|
||||||
system().getConfigSystem().getConfig(),
|
config(),
|
||||||
new Locale(),
|
new Locale(),
|
||||||
system().getDatabaseSystem(),
|
dbSystem(),
|
||||||
new QuerySvc(system().getDatabaseSystem(), system().getServerInfo(), null),
|
new QuerySvc(dbSystem(), serverInfo(), null),
|
||||||
system().getServerInfo(),
|
serverInfo(),
|
||||||
logger,
|
logger,
|
||||||
null
|
null
|
||||||
).cleanOldPlayers(db());
|
).cleanOldPlayers(db());
|
||||||
@ -173,7 +173,7 @@ public interface DatabaseTest extends DatabaseTestPreparer {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
default void configIsStoredInTheDatabase() {
|
default void configIsStoredInTheDatabase() {
|
||||||
PlanConfig config = system().getConfigSystem().getConfig();
|
PlanConfig config = config();
|
||||||
|
|
||||||
db().executeTransaction(new StoreConfigTransaction(serverUUID(), config, System.currentTimeMillis()));
|
db().executeTransaction(new StoreConfigTransaction(serverUUID(), config, System.currentTimeMillis()));
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ public interface DatabaseTest extends DatabaseTestPreparer {
|
|||||||
configIsStoredInTheDatabase();
|
configIsStoredInTheDatabase();
|
||||||
long savedMs = System.currentTimeMillis();
|
long savedMs = System.currentTimeMillis();
|
||||||
|
|
||||||
PlanConfig config = system().getConfigSystem().getConfig();
|
PlanConfig config = config();
|
||||||
|
|
||||||
db().executeTransaction(new StoreConfigTransaction(serverUUID(), config, System.currentTimeMillis()));
|
db().executeTransaction(new StoreConfigTransaction(serverUUID(), config, System.currentTimeMillis()));
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ public interface DatabaseTest extends DatabaseTestPreparer {
|
|||||||
String testSQL = SELECT + sql.dateToDayStamp(sql.epochSecondToDate(Long.toString((time + offset) / 1000))) + " as date";
|
String testSQL = SELECT + sql.dateToDayStamp(sql.epochSecondToDate(Long.toString((time + offset) / 1000))) + " as date";
|
||||||
|
|
||||||
System.out.println(testSQL);
|
System.out.println(testSQL);
|
||||||
String expected = system().getDeliveryUtilities().getFormatters().iso8601NoClockLong().apply(time);
|
String expected = deliveryUtilities().getFormatters().iso8601NoClockLong().apply(time);
|
||||||
String result = db.query(new QueryAllStatement<String>(testSQL) {
|
String result = db.query(new QueryAllStatement<String>(testSQL) {
|
||||||
@Override
|
@Override
|
||||||
public String processResults(ResultSet set) throws SQLException {
|
public String processResults(ResultSet set) throws SQLException {
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage.database;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.delivery.DeliveryUtilities;
|
||||||
|
import com.djrapitops.plan.identification.ServerInfo;
|
||||||
|
import com.djrapitops.plan.settings.ConfigSystem;
|
||||||
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
|
import com.djrapitops.plan.storage.file.PlanFiles;
|
||||||
|
import dagger.BindsInstance;
|
||||||
|
import dagger.Component;
|
||||||
|
import utilities.DBPreparer;
|
||||||
|
import utilities.dagger.*;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Component(modules = {
|
||||||
|
DBSystemModule.class,
|
||||||
|
TestSystemObjectProvidingModule.class,
|
||||||
|
|
||||||
|
TestAPFModule.class,
|
||||||
|
PlanPluginModule.class,
|
||||||
|
PluginServerPropertiesModule.class,
|
||||||
|
PluginSuperClassBindingModule.class
|
||||||
|
})
|
||||||
|
public interface DatabaseTestComponent extends DBPreparer.Dependencies {
|
||||||
|
|
||||||
|
default void enable() {
|
||||||
|
configSystem().enable();
|
||||||
|
dbSystem().enable();
|
||||||
|
serverInfo().enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
default void disable() {
|
||||||
|
serverInfo().disable();
|
||||||
|
dbSystem().disable();
|
||||||
|
configSystem().disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlanConfig config();
|
||||||
|
|
||||||
|
ConfigSystem configSystem();
|
||||||
|
|
||||||
|
DBSystem dbSystem();
|
||||||
|
|
||||||
|
ServerInfo serverInfo();
|
||||||
|
|
||||||
|
DeliveryUtilities deliveryUtilities();
|
||||||
|
|
||||||
|
PlanFiles files();
|
||||||
|
|
||||||
|
@Component.Builder
|
||||||
|
interface Builder {
|
||||||
|
@BindsInstance
|
||||||
|
Builder bindTemporaryDirectory(@Named("tempDir") Path tempDir);
|
||||||
|
|
||||||
|
DatabaseTestComponent build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,6 +17,9 @@
|
|||||||
package com.djrapitops.plan.storage.database;
|
package com.djrapitops.plan.storage.database;
|
||||||
|
|
||||||
import com.djrapitops.plan.PlanSystem;
|
import com.djrapitops.plan.PlanSystem;
|
||||||
|
import com.djrapitops.plan.delivery.DeliveryUtilities;
|
||||||
|
import com.djrapitops.plan.identification.ServerInfo;
|
||||||
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
import com.djrapitops.plan.storage.database.transactions.Executable;
|
import com.djrapitops.plan.storage.database.transactions.Executable;
|
||||||
import com.djrapitops.plan.storage.database.transactions.Transaction;
|
import com.djrapitops.plan.storage.database.transactions.Transaction;
|
||||||
import utilities.TestConstants;
|
import utilities.TestConstants;
|
||||||
@ -33,8 +36,25 @@ public interface DatabaseTestPreparer {
|
|||||||
|
|
||||||
UUID serverUUID();
|
UUID serverUUID();
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
PlanSystem system();
|
PlanSystem system();
|
||||||
|
|
||||||
|
default PlanConfig config() {
|
||||||
|
return system().getConfigSystem().getConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
default DBSystem dbSystem() {
|
||||||
|
return system().getDatabaseSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
default ServerInfo serverInfo() {
|
||||||
|
return system().getServerInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
default DeliveryUtilities deliveryUtilities() {
|
||||||
|
return system().getDeliveryUtilities();
|
||||||
|
}
|
||||||
|
|
||||||
default void execute(Executable executable) {
|
default void execute(Executable executable) {
|
||||||
db().executeTransaction(new Transaction() {
|
db().executeTransaction(new Transaction() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
package com.djrapitops.plan.storage.database;
|
package com.djrapitops.plan.storage.database;
|
||||||
|
|
||||||
import com.djrapitops.plan.PlanSystem;
|
import com.djrapitops.plan.PlanSystem;
|
||||||
|
import com.djrapitops.plan.delivery.DeliveryUtilities;
|
||||||
import com.djrapitops.plan.identification.Server;
|
import com.djrapitops.plan.identification.Server;
|
||||||
|
import com.djrapitops.plan.identification.ServerInfo;
|
||||||
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
import com.djrapitops.plan.storage.database.queries.*;
|
import com.djrapitops.plan.storage.database.queries.*;
|
||||||
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
|
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
|
||||||
import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction;
|
import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction;
|
||||||
@ -28,15 +31,16 @@ import org.junit.jupiter.api.BeforeAll;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import utilities.DBPreparer;
|
import utilities.DBPreparer;
|
||||||
import utilities.RandomData;
|
import utilities.RandomData;
|
||||||
import utilities.mocks.PluginMockComponent;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for the H2 database.
|
* Tests for the H2 database.
|
||||||
@ -61,13 +65,16 @@ public class H2Test implements DatabaseTest,
|
|||||||
|
|
||||||
private static final int TEST_PORT_NUMBER = RandomData.randomInt(9005, 9500);
|
private static final int TEST_PORT_NUMBER = RandomData.randomInt(9005, 9500);
|
||||||
|
|
||||||
private static PlanSystem system;
|
|
||||||
private static Database database;
|
private static Database database;
|
||||||
|
private static DatabaseTestComponent component;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void setupDatabase(@TempDir Path temp) throws Exception {
|
static void setupDatabase(@TempDir Path temp) throws Exception {
|
||||||
system = new PluginMockComponent(temp).getPlanSystem();
|
component = DaggerDatabaseTestComponent.builder()
|
||||||
database = new DBPreparer(system, TEST_PORT_NUMBER).prepareH2()
|
.bindTemporaryDirectory(temp)
|
||||||
|
.build();
|
||||||
|
component.enable();
|
||||||
|
database = new DBPreparer(component, TEST_PORT_NUMBER).prepareH2()
|
||||||
.orElseThrow(IllegalStateException::new);
|
.orElseThrow(IllegalStateException::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +108,7 @@ public class H2Test implements DatabaseTest,
|
|||||||
database.close();
|
database.close();
|
||||||
System.out.println("Database state after close: " + database.getState().name());
|
System.out.println("Database state after close: " + database.getState().name());
|
||||||
}
|
}
|
||||||
system.disable();
|
component.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -111,11 +118,33 @@ public class H2Test implements DatabaseTest,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID serverUUID() {
|
public UUID serverUUID() {
|
||||||
return system.getServerInfo().getServerUUID();
|
return component.serverInfo().getServerUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlanConfig config() {
|
||||||
|
return component.config();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DBSystem dbSystem() {
|
||||||
|
return component.dbSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerInfo serverInfo() {
|
||||||
|
return component.serverInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeliveryUtilities deliveryUtilities() {
|
||||||
|
return component.deliveryUtilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlanSystem system() {
|
public PlanSystem system() {
|
||||||
return system;
|
PlanSystem mockSystem = Mockito.mock(PlanSystem.class);
|
||||||
|
when(mockSystem.getPlanFiles()).thenReturn(component.files());
|
||||||
|
return mockSystem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
package com.djrapitops.plan.storage.database;
|
package com.djrapitops.plan.storage.database;
|
||||||
|
|
||||||
import com.djrapitops.plan.PlanSystem;
|
import com.djrapitops.plan.PlanSystem;
|
||||||
|
import com.djrapitops.plan.delivery.DeliveryUtilities;
|
||||||
import com.djrapitops.plan.identification.Server;
|
import com.djrapitops.plan.identification.Server;
|
||||||
|
import com.djrapitops.plan.identification.ServerInfo;
|
||||||
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
import com.djrapitops.plan.storage.database.queries.*;
|
import com.djrapitops.plan.storage.database.queries.*;
|
||||||
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
|
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
|
||||||
import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction;
|
import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction;
|
||||||
@ -29,16 +32,17 @@ import org.junit.jupiter.api.BeforeAll;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import utilities.DBPreparer;
|
import utilities.DBPreparer;
|
||||||
import utilities.RandomData;
|
import utilities.RandomData;
|
||||||
import utilities.mocks.PluginMockComponent;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for MySQL database.
|
* Tests for MySQL database.
|
||||||
@ -67,13 +71,16 @@ class MySQLTest implements DatabaseTest,
|
|||||||
|
|
||||||
private static final int TEST_PORT_NUMBER = RandomData.randomInt(9005, 9500);
|
private static final int TEST_PORT_NUMBER = RandomData.randomInt(9005, 9500);
|
||||||
|
|
||||||
private static PlanSystem system;
|
|
||||||
private static Database database;
|
private static Database database;
|
||||||
|
private static DatabaseTestComponent component;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void setupDatabase(@TempDir Path temp) throws Exception {
|
static void setupDatabase(@TempDir Path temp) throws Exception {
|
||||||
system = new PluginMockComponent(temp).getPlanSystem();
|
component = DaggerDatabaseTestComponent.builder()
|
||||||
Optional<Database> mysql = new DBPreparer(system, TEST_PORT_NUMBER).prepareMySQL();
|
.bindTemporaryDirectory(temp)
|
||||||
|
.build();
|
||||||
|
component.enable();
|
||||||
|
Optional<Database> mysql = new DBPreparer(component, TEST_PORT_NUMBER).prepareMySQL();
|
||||||
Assumptions.assumeTrue(mysql.isPresent());
|
Assumptions.assumeTrue(mysql.isPresent());
|
||||||
database = mysql.get();
|
database = mysql.get();
|
||||||
}
|
}
|
||||||
@ -104,7 +111,7 @@ class MySQLTest implements DatabaseTest,
|
|||||||
@AfterAll
|
@AfterAll
|
||||||
static void disableSystem() {
|
static void disableSystem() {
|
||||||
if (database != null) database.close();
|
if (database != null) database.close();
|
||||||
system.disable();
|
component.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -114,11 +121,33 @@ class MySQLTest implements DatabaseTest,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID serverUUID() {
|
public UUID serverUUID() {
|
||||||
return system.getServerInfo().getServerUUID();
|
return component.serverInfo().getServerUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlanConfig config() {
|
||||||
|
return component.config();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DBSystem dbSystem() {
|
||||||
|
return component.dbSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerInfo serverInfo() {
|
||||||
|
return component.serverInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeliveryUtilities deliveryUtilities() {
|
||||||
|
return component.deliveryUtilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlanSystem system() {
|
public PlanSystem system() {
|
||||||
return system;
|
PlanSystem mockSystem = Mockito.mock(PlanSystem.class);
|
||||||
|
when(mockSystem.getPlanFiles()).thenReturn(component.files());
|
||||||
|
return mockSystem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
package com.djrapitops.plan.storage.database;
|
package com.djrapitops.plan.storage.database;
|
||||||
|
|
||||||
import com.djrapitops.plan.PlanSystem;
|
import com.djrapitops.plan.PlanSystem;
|
||||||
|
import com.djrapitops.plan.delivery.DeliveryUtilities;
|
||||||
import com.djrapitops.plan.identification.Server;
|
import com.djrapitops.plan.identification.Server;
|
||||||
|
import com.djrapitops.plan.identification.ServerInfo;
|
||||||
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
import com.djrapitops.plan.storage.database.queries.*;
|
import com.djrapitops.plan.storage.database.queries.*;
|
||||||
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
|
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
|
||||||
import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction;
|
import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction;
|
||||||
@ -28,15 +31,16 @@ import org.junit.jupiter.api.BeforeAll;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import utilities.DBPreparer;
|
import utilities.DBPreparer;
|
||||||
import utilities.RandomData;
|
import utilities.RandomData;
|
||||||
import utilities.mocks.PluginMockComponent;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for SQLite Database.
|
* Tests for SQLite Database.
|
||||||
@ -61,13 +65,16 @@ public class SQLiteTest implements DatabaseTest,
|
|||||||
|
|
||||||
private static final int TEST_PORT_NUMBER = RandomData.randomInt(9005, 9500);
|
private static final int TEST_PORT_NUMBER = RandomData.randomInt(9005, 9500);
|
||||||
|
|
||||||
private static PlanSystem system;
|
|
||||||
private static Database database;
|
private static Database database;
|
||||||
|
private static DatabaseTestComponent component;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void setupDatabase(@TempDir Path temp) throws Exception {
|
static void setupDatabase(@TempDir Path temp) throws Exception {
|
||||||
system = new PluginMockComponent(temp).getPlanSystem();
|
component = DaggerDatabaseTestComponent.builder()
|
||||||
database = new DBPreparer(system, TEST_PORT_NUMBER).prepareSQLite()
|
.bindTemporaryDirectory(temp)
|
||||||
|
.build();
|
||||||
|
component.enable();
|
||||||
|
database = new DBPreparer(component, TEST_PORT_NUMBER).prepareSQLite()
|
||||||
.orElseThrow(IllegalStateException::new);
|
.orElseThrow(IllegalStateException::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +105,7 @@ public class SQLiteTest implements DatabaseTest,
|
|||||||
@AfterAll
|
@AfterAll
|
||||||
static void disableSystem() {
|
static void disableSystem() {
|
||||||
if (database != null) database.close();
|
if (database != null) database.close();
|
||||||
system.disable();
|
component.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -108,11 +115,33 @@ public class SQLiteTest implements DatabaseTest,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID serverUUID() {
|
public UUID serverUUID() {
|
||||||
return system.getServerInfo().getServerUUID();
|
return component.serverInfo().getServerUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlanConfig config() {
|
||||||
|
return component.config();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DBSystem dbSystem() {
|
||||||
|
return component.dbSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerInfo serverInfo() {
|
||||||
|
return component.serverInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeliveryUtilities deliveryUtilities() {
|
||||||
|
return component.deliveryUtilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlanSystem system() {
|
public PlanSystem system() {
|
||||||
return system;
|
PlanSystem mockSystem = Mockito.mock(PlanSystem.class);
|
||||||
|
when(mockSystem.getPlanFiles()).thenReturn(component.files());
|
||||||
|
return mockSystem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ public interface DatabaseBackupTest extends DatabaseTestPreparer {
|
|||||||
default void testBackupAndRestoreSQLite() throws Exception {
|
default void testBackupAndRestoreSQLite() throws Exception {
|
||||||
File tempFile = Files.createTempFile(system().getPlanFiles().getDataFolder().toPath(), "backup-", ".db").toFile();
|
File tempFile = Files.createTempFile(system().getPlanFiles().getDataFolder().toPath(), "backup-", ".db").toFile();
|
||||||
tempFile.deleteOnExit();
|
tempFile.deleteOnExit();
|
||||||
SQLiteDB backup = system().getDatabaseSystem().getSqLiteFactory().usingFile(tempFile);
|
SQLiteDB backup = dbSystem().getSqLiteFactory().usingFile(tempFile);
|
||||||
backup.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
backup.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||||
try {
|
try {
|
||||||
backup.init();
|
backup.init();
|
||||||
@ -103,7 +103,7 @@ public interface DatabaseBackupTest extends DatabaseTestPreparer {
|
|||||||
default void testBackupAndRestoreH2() throws Exception {
|
default void testBackupAndRestoreH2() throws Exception {
|
||||||
File tempFile = Files.createTempFile(system().getPlanFiles().getDataFolder().toPath(), "backup-", ".db").toFile();
|
File tempFile = Files.createTempFile(system().getPlanFiles().getDataFolder().toPath(), "backup-", ".db").toFile();
|
||||||
tempFile.deleteOnExit();
|
tempFile.deleteOnExit();
|
||||||
H2DB backup = system().getDatabaseSystem().getH2Factory().usingFile(tempFile);
|
H2DB backup = dbSystem().getH2Factory().usingFile(tempFile);
|
||||||
backup.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
backup.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||||
try {
|
try {
|
||||||
backup.init();
|
backup.init();
|
||||||
|
@ -49,7 +49,7 @@ class ErrorLoggerTest {
|
|||||||
PluginMockComponent component = new PluginMockComponent(dir);
|
PluginMockComponent component = new PluginMockComponent(dir);
|
||||||
SYSTEM = component.getPlanSystem();
|
SYSTEM = component.getPlanSystem();
|
||||||
SYSTEM.enable();
|
SYSTEM.enable();
|
||||||
UNDER_TEST = SYSTEM.getErrorLogger();
|
UNDER_TEST = component.getPluginErrorLogger();
|
||||||
LOGS_DIR = SYSTEM.getPlanFiles().getLogsDirectory();
|
LOGS_DIR = SYSTEM.getPlanFiles().getLogsDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,11 +34,15 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public class DBPreparer {
|
public class DBPreparer {
|
||||||
|
|
||||||
private final PlanSystem system;
|
private final Dependencies dependencies;
|
||||||
private final int testPortNumber;
|
private final int testPortNumber;
|
||||||
|
|
||||||
public DBPreparer(PlanSystem system, int testPortNumber) {
|
public DBPreparer(PlanSystem system, int testPortNumber) {
|
||||||
this.system = system;
|
this(new PlanSystemAsDependencies(system), testPortNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBPreparer(Dependencies dependencies, int testPortNumber) {
|
||||||
|
this.dependencies = dependencies;
|
||||||
this.testPortNumber = testPortNumber;
|
this.testPortNumber = testPortNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,12 +57,12 @@ public class DBPreparer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SQLDB prepareDBByName(String dbName) throws EnableException {
|
private SQLDB prepareDBByName(String dbName) throws EnableException {
|
||||||
PlanConfig config = system.getConfigSystem().getConfig();
|
PlanConfig config = dependencies.config();
|
||||||
config.set(WebserverSettings.PORT, testPortNumber);
|
config.set(WebserverSettings.PORT, testPortNumber);
|
||||||
config.set(DatabaseSettings.TYPE, dbName);
|
config.set(DatabaseSettings.TYPE, dbName);
|
||||||
system.enable();
|
|
||||||
|
|
||||||
DBSystem dbSystem = system.getDatabaseSystem();
|
DBSystem dbSystem = dependencies.dbSystem();
|
||||||
|
dbSystem.enable();
|
||||||
SQLDB db = (SQLDB) dbSystem.getActiveDatabaseByName(dbName);
|
SQLDB db = (SQLDB) dbSystem.getActiveDatabaseByName(dbName);
|
||||||
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||||
db.init();
|
db.init();
|
||||||
@ -91,7 +95,7 @@ public class DBPreparer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Database> prepareMySQL() throws EnableException {
|
public Optional<Database> prepareMySQL() throws EnableException {
|
||||||
PlanConfig config = system.getConfigSystem().getConfig();
|
PlanConfig config = dependencies.config();
|
||||||
Optional<String> formattedDB = setUpMySQLSettings(config);
|
Optional<String> formattedDB = setUpMySQLSettings(config);
|
||||||
if (formattedDB.isPresent()) {
|
if (formattedDB.isPresent()) {
|
||||||
String formattedDatabase = formattedDB.get();
|
String formattedDatabase = formattedDB.get();
|
||||||
@ -108,4 +112,29 @@ public class DBPreparer {
|
|||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface Dependencies {
|
||||||
|
PlanConfig config();
|
||||||
|
|
||||||
|
DBSystem dbSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
static class PlanSystemAsDependencies implements Dependencies {
|
||||||
|
private final PlanSystem system;
|
||||||
|
|
||||||
|
PlanSystemAsDependencies(PlanSystem system) {
|
||||||
|
this.system = system;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlanConfig config() {
|
||||||
|
return system.getConfigSystem().getConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DBSystem dbSystem() {
|
||||||
|
return system.getDatabaseSystem();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 utilities.dagger;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.exceptions.EnableException;
|
||||||
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
|
import com.djrapitops.plan.settings.config.paths.DatabaseSettings;
|
||||||
|
import com.djrapitops.plan.settings.locale.Locale;
|
||||||
|
import com.djrapitops.plan.storage.database.DBSystem;
|
||||||
|
import com.djrapitops.plan.storage.database.H2DB;
|
||||||
|
import com.djrapitops.plan.storage.database.MySQLDB;
|
||||||
|
import com.djrapitops.plan.storage.database.SQLiteDB;
|
||||||
|
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class DBSystemModule {
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
DBSystem provideDatabaseSystem(
|
||||||
|
PlanConfig config,
|
||||||
|
Locale locale,
|
||||||
|
SQLiteDB.Factory sqLiteDB,
|
||||||
|
H2DB.Factory h2Factory,
|
||||||
|
MySQLDB mySQLDB,
|
||||||
|
PluginLogger logger
|
||||||
|
) {
|
||||||
|
return new DBSystem(locale, sqLiteDB, h2Factory, logger) {
|
||||||
|
@Override
|
||||||
|
public void enable() throws EnableException {
|
||||||
|
databases.add(sqLiteDB.usingDefaultFile());
|
||||||
|
databases.add(h2Factory.usingDefaultFile());
|
||||||
|
databases.add(mySQLDB);
|
||||||
|
String dbType = config.get(DatabaseSettings.TYPE).toLowerCase().trim();
|
||||||
|
db = getActiveDatabaseByName(dbType);
|
||||||
|
super.enable();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -19,13 +19,14 @@ package utilities.dagger;
|
|||||||
import com.djrapitops.plan.PlanPlugin;
|
import com.djrapitops.plan.PlanPlugin;
|
||||||
import com.djrapitops.plan.PlanSystem;
|
import com.djrapitops.plan.PlanSystem;
|
||||||
import com.djrapitops.plan.commands.PlanCommand;
|
import com.djrapitops.plan.commands.PlanCommand;
|
||||||
import com.djrapitops.plan.modules.APFModule;
|
|
||||||
import com.djrapitops.plan.modules.PlaceholderModule;
|
import com.djrapitops.plan.modules.PlaceholderModule;
|
||||||
import com.djrapitops.plan.modules.SystemObjectProvidingModule;
|
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
|
||||||
import dagger.BindsInstance;
|
import dagger.BindsInstance;
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dagger component for {@link com.djrapitops.plan.PlanPlugin} based Plan system.
|
* Dagger component for {@link com.djrapitops.plan.PlanPlugin} based Plan system.
|
||||||
@ -35,20 +36,25 @@ import javax.inject.Singleton;
|
|||||||
@Singleton
|
@Singleton
|
||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
PlanPluginModule.class,
|
PlanPluginModule.class,
|
||||||
SystemObjectProvidingModule.class,
|
TestSystemObjectProvidingModule.class,
|
||||||
APFModule.class,
|
TestAPFModule.class,
|
||||||
PlaceholderModule.class,
|
PlaceholderModule.class,
|
||||||
|
|
||||||
PluginServerPropertiesModule.class,
|
PluginServerPropertiesModule.class,
|
||||||
PluginSuperClassBindingModule.class
|
PluginSuperClassBindingModule.class,
|
||||||
|
DBSystemModule.class
|
||||||
})
|
})
|
||||||
public interface PlanPluginComponent {
|
public interface PlanPluginComponent {
|
||||||
PlanCommand planCommand();
|
PlanCommand planCommand();
|
||||||
|
|
||||||
PlanSystem system();
|
PlanSystem system();
|
||||||
|
|
||||||
|
PluginErrorLogger pluginErrorLogger();
|
||||||
|
|
||||||
@Component.Builder
|
@Component.Builder
|
||||||
interface Builder {
|
interface Builder {
|
||||||
|
@BindsInstance
|
||||||
|
Builder bindTemporaryDirectory(@Named("tempDir") Path tempDir);
|
||||||
|
|
||||||
@BindsInstance
|
@BindsInstance
|
||||||
Builder plan(PlanPlugin plan);
|
Builder plan(PlanPlugin plan);
|
||||||
|
@ -18,18 +18,9 @@ package utilities.dagger;
|
|||||||
|
|
||||||
import com.djrapitops.plan.PlanPlugin;
|
import com.djrapitops.plan.PlanPlugin;
|
||||||
import com.djrapitops.plan.TaskSystem;
|
import com.djrapitops.plan.TaskSystem;
|
||||||
import com.djrapitops.plan.exceptions.EnableException;
|
|
||||||
import com.djrapitops.plan.gathering.ServerSensor;
|
import com.djrapitops.plan.gathering.ServerSensor;
|
||||||
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
|
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
|
||||||
import com.djrapitops.plan.processing.Processing;
|
import com.djrapitops.plan.processing.Processing;
|
||||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
|
||||||
import com.djrapitops.plan.settings.config.paths.DatabaseSettings;
|
|
||||||
import com.djrapitops.plan.settings.locale.Locale;
|
|
||||||
import com.djrapitops.plan.storage.database.DBSystem;
|
|
||||||
import com.djrapitops.plan.storage.database.H2DB;
|
|
||||||
import com.djrapitops.plan.storage.database.MySQLDB;
|
|
||||||
import com.djrapitops.plan.storage.database.SQLiteDB;
|
|
||||||
import com.djrapitops.plugin.logging.console.PluginLogger;
|
|
||||||
import com.djrapitops.plugin.task.RunnableFactory;
|
import com.djrapitops.plugin.task.RunnableFactory;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@ -49,29 +40,6 @@ import static org.mockito.Mockito.when;
|
|||||||
@Module
|
@Module
|
||||||
public class PluginSuperClassBindingModule {
|
public class PluginSuperClassBindingModule {
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
DBSystem provideDatabaseSystem(
|
|
||||||
PlanConfig config,
|
|
||||||
Locale locale,
|
|
||||||
SQLiteDB.Factory sqLiteDB,
|
|
||||||
H2DB.Factory h2Factory,
|
|
||||||
MySQLDB mySQLDB,
|
|
||||||
PluginLogger logger
|
|
||||||
) {
|
|
||||||
return new DBSystem(locale, sqLiteDB, h2Factory, logger) {
|
|
||||||
@Override
|
|
||||||
public void enable() throws EnableException {
|
|
||||||
databases.add(sqLiteDB.usingDefaultFile());
|
|
||||||
databases.add(h2Factory.usingDefaultFile());
|
|
||||||
databases.add(mySQLDB);
|
|
||||||
String dbType = config.get(DatabaseSettings.TYPE).toLowerCase().trim();
|
|
||||||
db = getActiveDatabaseByName(dbType);
|
|
||||||
super.enable();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
TaskSystem provideTaskSystem(RunnableFactory runnableFactory) {
|
TaskSystem provideTaskSystem(RunnableFactory runnableFactory) {
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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 utilities.dagger;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||||
|
import com.djrapitops.plugin.benchmarking.Timings;
|
||||||
|
import com.djrapitops.plugin.command.ColorScheme;
|
||||||
|
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||||
|
import com.djrapitops.plugin.logging.console.TestPluginLogger;
|
||||||
|
import com.djrapitops.plugin.logging.debug.ConsoleDebugLogger;
|
||||||
|
import com.djrapitops.plugin.logging.debug.DebugLogger;
|
||||||
|
import com.djrapitops.plugin.logging.error.ErrorHandler;
|
||||||
|
import com.djrapitops.plugin.task.RunnableFactory;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import utilities.mocks.objects.TestRunnableFactory;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class TestAPFModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("currentVersion")
|
||||||
|
@Singleton
|
||||||
|
String provideCurrentVersion() {
|
||||||
|
return "0.0.1";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ColorScheme provideColorScheme() {
|
||||||
|
return new ColorScheme(
|
||||||
|
"", "", ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
DebugLogger provideDebugLogger(PluginLogger logger) {
|
||||||
|
return new ConsoleDebugLogger(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
PluginLogger providePluginLogger() {
|
||||||
|
return new TestPluginLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ErrorHandler provideErrorHandler(ErrorLogger errorLogger) {
|
||||||
|
return errorLogger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
Timings provideTimings(DebugLogger debugLogger) {
|
||||||
|
return new Timings(debugLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
RunnableFactory provideRunnableFactory() {
|
||||||
|
return new TestRunnableFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 utilities.dagger;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.settings.config.ExtensionSettings;
|
||||||
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
|
import com.djrapitops.plan.settings.locale.Locale;
|
||||||
|
import com.djrapitops.plan.settings.locale.LocaleSystem;
|
||||||
|
import com.djrapitops.plan.storage.file.JarResource;
|
||||||
|
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import utilities.TestResources;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class TestSystemObjectProvidingModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
Locale provideLocale(LocaleSystem localeSystem) {
|
||||||
|
return localeSystem.getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ExtensionSettings providePluginsConfigSection(PlanConfig config) {
|
||||||
|
return config.getExtensionSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("isExtensionEnabled")
|
||||||
|
Predicate<String> provideExtensionEnabledConfigCheck(PlanConfig config) {
|
||||||
|
return config.getExtensionSettings()::isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
JarResource.StreamFunction provideJarStreamFunction(@Named("tempDir") Path tempDir) {
|
||||||
|
return resource -> {
|
||||||
|
File copyTo = tempDir.resolve(resource).toFile();
|
||||||
|
TestResources.copyResourceIntoFile(copyTo, "/" + resource);
|
||||||
|
return new FileInputStream(copyTo);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("dataFolder")
|
||||||
|
File provideDataFolder(@Named("tempDir") Path tempDir) {
|
||||||
|
return tempDir.toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ErrorLogger provideErrorLogger() {
|
||||||
|
return (level, throwable, context) -> {
|
||||||
|
throw new AssertionError("Test had an Exception: " + level.name() + " " + throwable.toString() + " " + context, throwable);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ package utilities.mocks;
|
|||||||
|
|
||||||
import com.djrapitops.plan.PlanPlugin;
|
import com.djrapitops.plan.PlanPlugin;
|
||||||
import com.djrapitops.plan.PlanSystem;
|
import com.djrapitops.plan.PlanSystem;
|
||||||
|
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
|
||||||
import utilities.dagger.DaggerPlanPluginComponent;
|
import utilities.dagger.DaggerPlanPluginComponent;
|
||||||
import utilities.dagger.PlanPluginComponent;
|
import utilities.dagger.PlanPluginComponent;
|
||||||
|
|
||||||
@ -50,9 +51,20 @@ public class PluginMockComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PlanSystem getPlanSystem() throws Exception {
|
public PlanSystem getPlanSystem() throws Exception {
|
||||||
if (component == null) {
|
initComponent();
|
||||||
component = DaggerPlanPluginComponent.builder().plan(getPlanMock()).build();
|
|
||||||
}
|
|
||||||
return component.system();
|
return component.system();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initComponent() throws Exception {
|
||||||
|
if (component == null) {
|
||||||
|
component = DaggerPlanPluginComponent.builder()
|
||||||
|
.bindTemporaryDirectory(tempDir)
|
||||||
|
.plan(getPlanMock()).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PluginErrorLogger getPluginErrorLogger() throws Exception {
|
||||||
|
initComponent();
|
||||||
|
return component.pluginErrorLogger();
|
||||||
|
}
|
||||||
}
|
}
|
@ -50,7 +50,10 @@ public class NukkitAFKListener implements Listener {
|
|||||||
private final ErrorLogger errorLogger;
|
private final ErrorLogger errorLogger;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public NukkitAFKListener(PlanConfig config, ErrorLogger errorLogger) {
|
public NukkitAFKListener(
|
||||||
|
PlanConfig config,
|
||||||
|
ErrorLogger errorLogger
|
||||||
|
) {
|
||||||
this.errorLogger = errorLogger;
|
this.errorLogger = errorLogger;
|
||||||
this.ignorePermissionInfo = new HashMap<>();
|
this.ignorePermissionInfo = new HashMap<>();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user