#457 Improve ForceFlatToSqlite conversion

- Change ForceFlatToSqlite converter to use a generic datasource destination (i.e. can be used for Flat2MySQL later)
- Add tests, including for FlatFile
- Check that user is not present in destination datasource before adding
- Persist last location from flatfile as well
This commit is contained in:
ljacqu
2016-02-27 11:24:47 +01:00
parent 27b1fa770f
commit 1b818bd833
8 changed files with 259 additions and 70 deletions
@@ -3,39 +3,51 @@ package fr.xephi.authme.converter;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.datasource.SQLite;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import java.sql.SQLException;
import fr.xephi.authme.datasource.FlatFile;
import fr.xephi.authme.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Mandatory migration from the deprecated flat file datasource to SQLite.
*/
public class ForceFlatToSqlite {
public class ForceFlatToSqlite implements Converter {
private final DataSource database;
private final NewSetting settings;
private final DataSource source;
private final DataSource destination;
public ForceFlatToSqlite(DataSource database, NewSetting settings) {
this.database = database;
this.settings = settings;
/**
* Constructor.
*
* @param source The datasource to convert (flatfile)
* @param destination The datasource to copy the data to (sqlite)
*/
public ForceFlatToSqlite(FlatFile source, DataSource destination) {
this.source = source;
this.destination = destination;
}
public DataSource run() {
try {
DataSource sqlite = new SQLite(settings);
for (PlayerAuth auth : database.getAllAuths()) {
auth.setRealName("Player");
sqlite.saveAuth(auth);
/**
* Perform the conversion.
*/
@Override
public void run() {
List<String> skippedPlayers = new ArrayList<>();
for (PlayerAuth auth : source.getAllAuths()) {
if (destination.isAuthAvailable(auth.getNickname())) {
skippedPlayers.add(auth.getNickname());
} else {
destination.saveAuth(auth);
destination.updateQuitLoc(auth);
}
settings.setProperty(DatabaseSettings.BACKEND, DataSourceType.SQLITE);
settings.save();
ConsoleLogger.info("Database successfully converted to sqlite!");
return sqlite;
} catch (SQLException | ClassNotFoundException e) {
ConsoleLogger.logException("Could not convert from Flatfile to SQLite:", e);
}
return null;
if (!skippedPlayers.isEmpty()) {
ConsoleLogger.showError("Warning: skipped conversion for players which were already in SQLite: "
+ StringUtils.join(", ", skippedPlayers));
}
ConsoleLogger.info("Database successfully converted from " + source.getClass().getSimpleName()
+ " to " + destination.getClass().getSimpleName());
}
}
@@ -1,5 +1,6 @@
package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
@@ -42,7 +43,7 @@ public class FlatFile implements DataSource {
try {
source.createNewFile();
} catch (IOException e) {
ConsoleLogger.showError(e.getMessage());
ConsoleLogger.logException("Cannot open flatfile", e);
if (Settings.isStopEnabled) {
ConsoleLogger.showError("Can't use FLAT FILE... SHUTDOWN...");
instance.getServer().shutdown();
@@ -50,10 +51,14 @@ public class FlatFile implements DataSource {
if (!Settings.isStopEnabled) {
instance.getServer().getPluginManager().disablePlugin(instance);
}
e.printStackTrace();
}
}
@VisibleForTesting
public FlatFile(File source) {
this.source = source;
}
@Override
public synchronized boolean isAuthAvailable(String user) {
BufferedReader br = null;
@@ -601,7 +606,7 @@ public class FlatFile implements DataSource {
@Override
public boolean updateRealName(String user, String realName) {
return false;
throw new UnsupportedOperationException("Flat file no longer supported");
}
@Override
@@ -618,33 +623,25 @@ public class FlatFile implements DataSource {
String line;
while ((line = br.readLine()) != null) {
String[] args = line.split(":");
switch (args.length) {
case 2:
auths.add(new PlayerAuth(args[0], args[1], "192.168.0.1", 0, "your@email.com", args[0]));
break;
case 3:
auths.add(new PlayerAuth(args[0], args[1], args[2], 0, "your@email.com", args[0]));
break;
case 4:
auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), "your@email.com", args[0]));
break;
case 7:
auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "unavailableworld", "your@email.com", args[0]));
break;
case 8:
auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]));
break;
case 9:
auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]));
break;
// We expect to encounter 2, 3, 4, 7, 8 or 9 fields. Ignore the line otherwise
if (args.length >= 2 && args.length != 5 && args.length != 6 && args.length <= 9) {
PlayerAuth.Builder builder = PlayerAuth.builder()
.name(args[0]).realName(args[0])
.password(args[1], null);
if (args.length >= 3) builder.ip(args[2]);
if (args.length >= 4) builder.lastLogin(Long.parseLong(args[3]));
if (args.length >= 7) {
builder.locX(Double.parseDouble(args[4]))
.locY(Double.parseDouble(args[5]))
.locZ(Double.parseDouble(args[6]));
}
if (args.length >= 8) builder.locWorld(args[7]);
if (args.length >= 9) builder.email(args[8]);
auths.add(builder.build());
}
}
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return auths;
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
return auths;
ConsoleLogger.logException("Error while getting auths from flatfile:", ex);
} finally {
if (br != null) {
try {
@@ -658,7 +655,7 @@ public class FlatFile implements DataSource {
@Override
public List<PlayerAuth> getLoggedPlayers() {
return new ArrayList<>();
throw new UnsupportedOperationException("Flat file no longer supported");
}
@Override
@@ -5,6 +5,8 @@ import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.converter.ForceFlatToSqlite;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.datasource.FlatFile;
import fr.xephi.authme.datasource.SQLite;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.SHA256;
@@ -58,12 +60,17 @@ public final class MigrationService {
if (DataSourceType.FILE == settings.getProperty(DatabaseSettings.BACKEND)) {
ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated; it will be changed "
+ "to SQLite... Connection will be impossible until conversion is done!");
ForceFlatToSqlite converter = new ForceFlatToSqlite(dataSource, settings);
DataSource result = converter.run();
if (result == null) {
throw new IllegalStateException("Error during conversion from flatfile to SQLite");
} else {
return result;
FlatFile flatFile = (FlatFile) dataSource;
try {
SQLite sqlite = new SQLite(settings);
ForceFlatToSqlite converter = new ForceFlatToSqlite(flatFile, sqlite);
converter.run();
settings.setProperty(DatabaseSettings.BACKEND, DataSourceType.SQLITE);
settings.save();
return sqlite;
} catch (Exception e) {
ConsoleLogger.logException("Error during conversion from Flatfile to SQLite", e);
throw new IllegalStateException(e);
}
}
return null;