mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-12-19 23:28:59 +01:00
* #1497 Throw dedicated exception for invalid YAML files and handle it on startup - Wrap SnakeYAML exceptions when loading config.yml and commands.yml on startup into own exception type - Handle exception type on startup with specific error message * #1497 Fix inaccurate JavaDoc comment
This commit is contained in:
parent
7864bb06ac
commit
329657bd5f
@ -26,11 +26,13 @@ import fr.xephi.authme.service.BackupService;
|
|||||||
import fr.xephi.authme.service.BukkitService;
|
import fr.xephi.authme.service.BukkitService;
|
||||||
import fr.xephi.authme.service.MigrationService;
|
import fr.xephi.authme.service.MigrationService;
|
||||||
import fr.xephi.authme.service.bungeecord.BungeeReceiver;
|
import fr.xephi.authme.service.bungeecord.BungeeReceiver;
|
||||||
|
import fr.xephi.authme.service.yaml.YamlParseException;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import fr.xephi.authme.settings.SettingsWarner;
|
import fr.xephi.authme.settings.SettingsWarner;
|
||||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||||
import fr.xephi.authme.task.CleanupTask;
|
import fr.xephi.authme.task.CleanupTask;
|
||||||
import fr.xephi.authme.task.purge.PurgeService;
|
import fr.xephi.authme.task.purge.PurgeService;
|
||||||
|
import fr.xephi.authme.util.ExceptionUtils;
|
||||||
import org.apache.commons.lang.SystemUtils;
|
import org.apache.commons.lang.SystemUtils;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
@ -133,7 +135,13 @@ public class AuthMe extends JavaPlugin {
|
|||||||
try {
|
try {
|
||||||
initialize();
|
initialize();
|
||||||
} catch (Throwable th) {
|
} catch (Throwable th) {
|
||||||
|
YamlParseException yamlParseException = ExceptionUtils.findThrowableInCause(YamlParseException.class, th);
|
||||||
|
if (yamlParseException == null) {
|
||||||
ConsoleLogger.logException("Aborting initialization of AuthMe:", th);
|
ConsoleLogger.logException("Aborting initialization of AuthMe:", th);
|
||||||
|
} else {
|
||||||
|
ConsoleLogger.logException("File '" + yamlParseException.getFile() + "' contains invalid YAML. "
|
||||||
|
+ "Please run its contents through http://yamllint.com", yamlParseException);
|
||||||
|
}
|
||||||
stopOrUnload();
|
stopOrUnload();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package fr.xephi.authme.initialization;
|
|||||||
|
|
||||||
import ch.jalu.configme.configurationdata.ConfigurationData;
|
import ch.jalu.configme.configurationdata.ConfigurationData;
|
||||||
import ch.jalu.configme.resource.PropertyResource;
|
import ch.jalu.configme.resource.PropertyResource;
|
||||||
import ch.jalu.configme.resource.YamlFileResource;
|
import fr.xephi.authme.service.yaml.YamlFileResourceProvider;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import fr.xephi.authme.settings.SettingsMigrationService;
|
import fr.xephi.authme.settings.SettingsMigrationService;
|
||||||
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
|
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
|
||||||
@ -37,7 +37,7 @@ public class SettingsProvider implements Provider<Settings> {
|
|||||||
if (!configFile.exists()) {
|
if (!configFile.exists()) {
|
||||||
FileUtils.create(configFile);
|
FileUtils.create(configFile);
|
||||||
}
|
}
|
||||||
PropertyResource resource = new YamlFileResource(configFile);
|
PropertyResource resource = YamlFileResourceProvider.loadFromFile(configFile);
|
||||||
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
|
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
|
||||||
return new Settings(dataFolder, resource, migrationService, configurationData);
|
return new Settings(dataFolder, resource, migrationService, configurationData);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package fr.xephi.authme.service.yaml;
|
||||||
|
|
||||||
|
import ch.jalu.configme.resource.YamlFileResource;
|
||||||
|
import org.yaml.snakeyaml.parser.ParserException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates {@link YamlFileResource} objects.
|
||||||
|
*/
|
||||||
|
public final class YamlFileResourceProvider {
|
||||||
|
|
||||||
|
private YamlFileResourceProvider() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link YamlFileResource} instance for the given file. Wraps SnakeYAML's parse exception
|
||||||
|
* into an AuthMe exception.
|
||||||
|
*
|
||||||
|
* @param file the file to load
|
||||||
|
* @return the generated resource
|
||||||
|
*/
|
||||||
|
public static YamlFileResource loadFromFile(File file) {
|
||||||
|
try {
|
||||||
|
return new YamlFileResource(file);
|
||||||
|
} catch (ParserException e) {
|
||||||
|
throw new YamlParseException(file.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package fr.xephi.authme.service.yaml;
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.parser.ParserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception when a YAML file could not be parsed.
|
||||||
|
*/
|
||||||
|
public class YamlParseException extends RuntimeException {
|
||||||
|
|
||||||
|
private final String file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param file the file a parsing exception occurred with
|
||||||
|
* @param snakeYamlException the caught exception from SnakeYAML
|
||||||
|
*/
|
||||||
|
public YamlParseException(String file, ParserException snakeYamlException) {
|
||||||
|
super(snakeYamlException);
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
package fr.xephi.authme.settings.commandconfig;
|
package fr.xephi.authme.settings.commandconfig;
|
||||||
|
|
||||||
import ch.jalu.configme.SettingsManager;
|
import ch.jalu.configme.SettingsManager;
|
||||||
import ch.jalu.configme.resource.YamlFileResource;
|
|
||||||
import fr.xephi.authme.initialization.DataFolder;
|
import fr.xephi.authme.initialization.DataFolder;
|
||||||
import fr.xephi.authme.initialization.Reloadable;
|
import fr.xephi.authme.initialization.Reloadable;
|
||||||
import fr.xephi.authme.service.BukkitService;
|
import fr.xephi.authme.service.BukkitService;
|
||||||
import fr.xephi.authme.service.GeoIpService;
|
import fr.xephi.authme.service.GeoIpService;
|
||||||
|
import fr.xephi.authme.service.yaml.YamlFileResourceProvider;
|
||||||
import fr.xephi.authme.util.FileUtils;
|
import fr.xephi.authme.util.FileUtils;
|
||||||
import fr.xephi.authme.util.PlayerUtils;
|
import fr.xephi.authme.util.PlayerUtils;
|
||||||
import fr.xephi.authme.util.lazytags.Tag;
|
import fr.xephi.authme.util.lazytags.Tag;
|
||||||
@ -149,7 +149,7 @@ public class CommandManager implements Reloadable {
|
|||||||
FileUtils.copyFileFromResource(file, "commands.yml");
|
FileUtils.copyFileFromResource(file, "commands.yml");
|
||||||
|
|
||||||
SettingsManager settingsManager = new SettingsManager(
|
SettingsManager settingsManager = new SettingsManager(
|
||||||
new YamlFileResource(file), commandMigrationService, CommandSettingsHolder.class);
|
YamlFileResourceProvider.loadFromFile(file), commandMigrationService, CommandSettingsHolder.class);
|
||||||
CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS);
|
CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS);
|
||||||
onJoinCommands = newReplacer(commandConfig.getOnJoin());
|
onJoinCommands = newReplacer(commandConfig.getOnJoin());
|
||||||
onLoginCommands = newOnLoginCmdReplacer(commandConfig.getOnLogin());
|
onLoginCommands = newOnLoginCmdReplacer(commandConfig.getOnLogin());
|
||||||
|
36
src/main/java/fr/xephi/authme/util/ExceptionUtils.java
Normal file
36
src/main/java/fr/xephi/authme/util/ExceptionUtils.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package fr.xephi.authme.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for exceptions.
|
||||||
|
*/
|
||||||
|
public final class ExceptionUtils {
|
||||||
|
|
||||||
|
private ExceptionUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first throwable of the given {@code wantedThrowableType} by visiting the provided
|
||||||
|
* throwable and its causes recursively.
|
||||||
|
*
|
||||||
|
* @param wantedThrowableType the throwable type to find
|
||||||
|
* @param throwable the throwable to start with
|
||||||
|
* @param <T> the desired throwable subtype
|
||||||
|
* @return the first throwable found of the given type, or null if none found
|
||||||
|
*/
|
||||||
|
public static <T extends Throwable> T findThrowableInCause(Class<T> wantedThrowableType, Throwable throwable) {
|
||||||
|
Set<Throwable> visitedObjects = Sets.newIdentityHashSet();
|
||||||
|
Throwable currentThrowable = throwable;
|
||||||
|
while (currentThrowable != null && !visitedObjects.contains(currentThrowable)) {
|
||||||
|
if (wantedThrowableType.isInstance(currentThrowable)) {
|
||||||
|
return wantedThrowableType.cast(currentThrowable);
|
||||||
|
}
|
||||||
|
visitedObjects.add(currentThrowable);
|
||||||
|
currentThrowable = currentThrowable.getCause();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package fr.xephi.authme.service.yaml;
|
||||||
|
|
||||||
|
import ch.jalu.configme.resource.YamlFileResource;
|
||||||
|
import fr.xephi.authme.TestHelper;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.yaml.snakeyaml.parser.ParserException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link YamlFileResourceProvider}.
|
||||||
|
*/
|
||||||
|
public class YamlFileResourceProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldLoadValidFile() {
|
||||||
|
// given
|
||||||
|
File yamlFile = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "service/yaml/validYaml.yml");
|
||||||
|
|
||||||
|
// when
|
||||||
|
YamlFileResource resource = YamlFileResourceProvider.loadFromFile(yamlFile);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(resource.getString("test.jkl"), equalTo("Test test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldThrowForInvalidFile() {
|
||||||
|
// given
|
||||||
|
File yamlFile = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "service/yaml/invalidYaml.yml");
|
||||||
|
|
||||||
|
// when
|
||||||
|
try {
|
||||||
|
YamlFileResourceProvider.loadFromFile(yamlFile);
|
||||||
|
|
||||||
|
// then
|
||||||
|
fail("Expected exception to be thrown");
|
||||||
|
} catch (YamlParseException e) {
|
||||||
|
assertThat(e.getFile(), equalTo(yamlFile.getPath()));
|
||||||
|
assertThat(e.getCause(), instanceOf(ParserException.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java
Normal file
54
src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package fr.xephi.authme.util;
|
||||||
|
|
||||||
|
import fr.xephi.authme.ReflectionTestUtils;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ConcurrentModificationException;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.hamcrest.Matchers.sameInstance;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link ExceptionUtils}.
|
||||||
|
*/
|
||||||
|
public class ExceptionUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldFindWantedThrowable() {
|
||||||
|
// given
|
||||||
|
ConcurrentModificationException initialCme = new ConcurrentModificationException();
|
||||||
|
Throwable th = new Throwable(initialCme);
|
||||||
|
ConcurrentModificationException cme = new ConcurrentModificationException(th);
|
||||||
|
IllegalStateException ise = new IllegalStateException(cme);
|
||||||
|
UnsupportedOperationException uoe = new UnsupportedOperationException(ise);
|
||||||
|
ReflectiveOperationException roe = new ReflectiveOperationException(uoe);
|
||||||
|
|
||||||
|
// when
|
||||||
|
IllegalStateException resultIse = ExceptionUtils.findThrowableInCause(IllegalStateException.class, roe);
|
||||||
|
ConcurrentModificationException resultCme = ExceptionUtils.findThrowableInCause(ConcurrentModificationException.class, cme);
|
||||||
|
StackOverflowError resultSoe = ExceptionUtils.findThrowableInCause(StackOverflowError.class, cme);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(resultIse, sameInstance(ise));
|
||||||
|
assertThat(resultCme, sameInstance(cme));
|
||||||
|
assertThat(resultSoe, nullValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldHandleCircularCausesGracefully() {
|
||||||
|
// given
|
||||||
|
IllegalStateException ise = new IllegalStateException();
|
||||||
|
UnsupportedOperationException uoe = new UnsupportedOperationException(ise);
|
||||||
|
ReflectiveOperationException roe = new ReflectiveOperationException(uoe);
|
||||||
|
ReflectionTestUtils.setField(Throwable.class, ise, "cause", roe);
|
||||||
|
|
||||||
|
// when
|
||||||
|
NullPointerException resultNpe = ExceptionUtils.findThrowableInCause(NullPointerException.class, uoe);
|
||||||
|
UnsupportedOperationException resultUoe = ExceptionUtils.findThrowableInCause(UnsupportedOperationException.class, uoe);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(resultNpe, nullValue());
|
||||||
|
assertThat(resultUoe, sameInstance(uoe));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
# File with invalid YAML
|
||||||
|
test:
|
||||||
|
abc: 'test'
|
||||||
|
def: 'Going to forget a quote here
|
||||||
|
jkl: 'Test test'
|
@ -0,0 +1,4 @@
|
|||||||
|
test:
|
||||||
|
abc: 'test'
|
||||||
|
def: 'All quotes are good here'
|
||||||
|
jkl: 'Test test'
|
Loading…
Reference in New Issue
Block a user