Save current progress
This commit is contained in:
parent
669cd9e789
commit
d4740d71e7
13 changed files with 631 additions and 31 deletions
|
@ -68,7 +68,12 @@ repositories {
|
|||
|
||||
dependencies {
|
||||
provided "dev.zontreck:EventsBus:${Bus_API}.${Bus_Patch}"
|
||||
|
||||
provided "dev.zontreck:LibAC:${LibAC_API}.${LibAC_Patch}"
|
||||
|
||||
provided "org.mariadb.jdbc:mariadb-java-client:${MariaDB_JDBC_Version}"
|
||||
provided "org.slf4j:log4j-over-slf4j:2.0.7"
|
||||
provided "org.slf4j:slf4j-simple:2.0.7"
|
||||
}
|
||||
|
||||
def MAVEN_PASSWORD_PROPERTY = "AriasCreationsMavenPassword"
|
||||
|
@ -117,7 +122,8 @@ task jarjar(type: Jar) {
|
|||
manifest {
|
||||
attributes (
|
||||
'Main-Class': application.mainClass,
|
||||
'Multi-Release': 'true'
|
||||
'Multi-Release': 'true',
|
||||
'Implementation-Version': version
|
||||
)
|
||||
}
|
||||
archiveClassifier = "AIO"
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
apiVer=1.0
|
||||
Bus_API=1.0
|
||||
Bus_Patch=33
|
||||
Bus_Patch=45
|
||||
|
||||
|
||||
LibAC_API=1.4
|
||||
LibAC_Patch=35
|
||||
LibAC_Patch=46
|
||||
|
||||
MariaDB_JDBC_Version=3.3.2
|
||||
|
||||
org.gradle.daemon=false
|
|
@ -11,9 +11,14 @@ import dev.zontreck.ariaslib.terminal.Banners;
|
|||
import dev.zontreck.ariaslib.util.FileIO;
|
||||
import dev.zontreck.ariaslib.util.Hashing;
|
||||
import dev.zontreck.eventsbus.Bus;
|
||||
import dev.zontreck.eventsbus.EventDispatcher;
|
||||
import dev.zontreck.playsync.data.DataAccountant;
|
||||
import dev.zontreck.playsync.data.DataFragment;
|
||||
import dev.zontreck.playsync.data.Manifest;
|
||||
import dev.zontreck.playsync.data.database.DatabaseConnection;
|
||||
import dev.zontreck.playsync.data.database.Migrations;
|
||||
import dev.zontreck.playsync.data.database.migrations.ChunksTable;
|
||||
import dev.zontreck.playsync.data.database.migrations.ManifestsTable;
|
||||
import dev.zontreck.playsync.events.EventHandlers;
|
||||
import dev.zontreck.playsync.exceptions.UnsupportedAlgorithmException;
|
||||
import dev.zontreck.playsync.exceptions.UnsupportedIDException;
|
||||
|
@ -22,6 +27,8 @@ import dev.zontreck.playsync.games.nintendo.ID6;
|
|||
import dev.zontreck.playsync.server.HTTPServer;
|
||||
|
||||
import java.io.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -36,12 +43,26 @@ public class PlaySyncServer {
|
|||
DEFAULT_ARGS.setArg(new IntegerArgument("port", 1588));
|
||||
DEFAULT_ARGS.setArg(new StringArgument("dataDir", "data"));
|
||||
DEFAULT_ARGS.setArg(new StringArgument("hash", "MD5"));
|
||||
DEFAULT_ARGS.setArg(new IntegerArgument("db_port", 3306));
|
||||
DEFAULT_ARGS.setArg(new StringArgument("db_host", "localhost"));
|
||||
DEFAULT_ARGS.setArg(new StringArgument("db_name", "PlaySync"));
|
||||
DEFAULT_ARGS.setArg(new StringArgument("db_user", "NSET\r"));
|
||||
DEFAULT_ARGS.setArg(new StringArgument("db_pass", "NSET\r"));
|
||||
|
||||
}
|
||||
|
||||
Settings.PORT_NUMBER = (int) DEFAULT_ARGS.getArg("port").getValue();
|
||||
Settings.DATA_DIRECTORY = (String)DEFAULT_ARGS.getArg("dataDir").getValue();
|
||||
Settings.HASH_ALGORITHM = (String)DEFAULT_ARGS.getArg("hash").getValue();
|
||||
|
||||
public static void printUsage()
|
||||
{
|
||||
log("Usage: java -jar PlaySyncServer.jar --db_user=<value> --db_pass=<value> [options]");
|
||||
log("");
|
||||
log("--port=<value>\t\t\tDefault: 1588\n",
|
||||
"--dataDir=<value>\t\tDefault: data\n",
|
||||
"--hash=<value>\t\t\tDefault: MD5, (valid: MD5, SHA256)\n",
|
||||
"--db_port=<value>\t\tDefault: 3306\n",
|
||||
"--db_host=<value>\t\tDefault: localhost\n",
|
||||
"--db_user=<value>\t\t(REQUIRED)\n",
|
||||
"--db_name=<value>\t\tDefault: PlaySync\n",
|
||||
"--db_pass=<value>\t\t(REQUIRED)\n");
|
||||
}
|
||||
|
||||
private static void log(String... args)
|
||||
|
@ -74,19 +95,26 @@ public class PlaySyncServer {
|
|||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
log(Banners.generateBanner("PlaySync Server"));
|
||||
log(Banners.generateBanner("Harbinger CDN Server"));
|
||||
log("Version: "+PlaySyncServer.class.getPackage().getImplementationVersion() + "\n");
|
||||
|
||||
Arguments active = ArgumentsParser.parseArguments(args, DEFAULT_ARGS);
|
||||
|
||||
IntegerArgument port = (IntegerArgument) active.getArg("port");
|
||||
StringArgument dataDir = (StringArgument) active.getArg("dataDir");
|
||||
StringArgument hashing = (StringArgument) active.getArg("hash");
|
||||
|
||||
log("Registering EventHandlers");
|
||||
Bus.Register(EventHandlers.class, new EventHandlers());
|
||||
log("EventHandlers have been registered");
|
||||
IntegerArgument dbPort = (IntegerArgument) active.getArg("db_port");
|
||||
StringArgument dbHost = (StringArgument) active.getArg("db_host");
|
||||
StringArgument dbName = (StringArgument) active.getArg("db_name");
|
||||
StringArgument dbUser = (StringArgument) active.getArg("db_user");
|
||||
StringArgument dbPass = (StringArgument) active.getArg("db_pass");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
log("Parsing command line arguments...");
|
||||
|
||||
if(active.hasArg("port"))
|
||||
Settings.PORT_NUMBER = port.getValue();
|
||||
|
||||
|
@ -96,10 +124,72 @@ public class PlaySyncServer {
|
|||
if(active.hasArg("hash"))
|
||||
Settings.HASH_ALGORITHM = hashing.getValue();
|
||||
|
||||
if(active.hasArg("db_port"))
|
||||
Settings.DB_PORT = dbPort.getValue();
|
||||
|
||||
if(active.hasArg("db_host"))
|
||||
Settings.DB_HOST = dbHost.getValue();
|
||||
|
||||
if(active.hasArg("db_name"))
|
||||
Settings.DB_NAME = dbName.getValue();
|
||||
|
||||
if(active.hasArg("db_user"))
|
||||
{
|
||||
if(dbUser.getValue().equalsIgnoreCase("NSET\r"))
|
||||
{
|
||||
printUsage();
|
||||
|
||||
log("Argument: --db_user is required");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
Settings.DB_USERNAME = dbUser.getValue();
|
||||
}
|
||||
|
||||
if(active.hasArg("db_pass"))
|
||||
{
|
||||
if(dbPass.getValue().equalsIgnoreCase("NSET\r"))
|
||||
{
|
||||
log("Argument: --db_pass is required if the password is not blank");
|
||||
Settings.DB_PASSWORD = "";
|
||||
}else
|
||||
Settings.DB_PASSWORD = dbPass.getValue();
|
||||
}
|
||||
|
||||
log("Finished parsing command line options");
|
||||
|
||||
|
||||
log("Starting the EventDispatcher");
|
||||
EventDispatcher.Reset();
|
||||
EventDispatcher.Register(EventHandlers.class);
|
||||
log("EventDispatcher has been started.");
|
||||
|
||||
|
||||
log("Port number set to: ", String.valueOf(Settings.PORT_NUMBER));
|
||||
log("Data will be loaded/stored in: ", Settings.DATA_DIRECTORY);
|
||||
log("Hashing algorithm set to: ", Settings.HASH_ALGORITHM);
|
||||
|
||||
log("Connecting to database...");
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = DatabaseConnection.getConnection();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
log("\n\n");
|
||||
log(Banners.generateBanner("Invalid Credentials or DB Settings"));
|
||||
System.exit(1);
|
||||
}
|
||||
log("Connection established");
|
||||
|
||||
log("Performing database migrations...");
|
||||
try {
|
||||
Migrations.doMigration(conn);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
log(Banners.generateBanner("Critical Failure when creating migrations table"));
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
File data = Settings.getDataFolder();
|
||||
if(!data.exists())
|
||||
{
|
||||
|
@ -125,6 +215,7 @@ public class PlaySyncServer {
|
|||
*/
|
||||
mf.FileHash = Hashing.md5(fullFile);
|
||||
mf.GamePlatform = Platform.Nintendo;
|
||||
mf.GameName = "Animal Crossing";
|
||||
|
||||
DataAccountant.DataTotal = fullFile.length;
|
||||
|
||||
|
@ -145,7 +236,7 @@ public class PlaySyncServer {
|
|||
{
|
||||
try {
|
||||
DataFragment frag = DataFragment.Create(dis);
|
||||
if(frag.getSize()<1024)
|
||||
if(frag.getSize()<512)
|
||||
{
|
||||
hasData=false;
|
||||
}
|
||||
|
@ -182,7 +273,11 @@ public class PlaySyncServer {
|
|||
|
||||
for(DataFragment frag : fragments)
|
||||
{
|
||||
frag.Save();
|
||||
ChunksTable chunksTable = (ChunksTable)Migrations.Tables.get(ChunksTable.class);
|
||||
|
||||
chunksTable.setChunk(frag);
|
||||
|
||||
|
||||
DataAccountant.ChunksWritten++;
|
||||
DataAccountant.ChunksRemain--;
|
||||
DataAccountant.updateCur(DataAccountant.ChunksWritten);
|
||||
|
|
|
@ -9,6 +9,14 @@ public class Settings
|
|||
public static String DATA_DIRECTORY;
|
||||
public static String HASH_ALGORITHM;
|
||||
|
||||
public static int DB_PORT;
|
||||
public static String DB_HOST;
|
||||
public static String DB_USERNAME;
|
||||
public static String DB_PASSWORD;
|
||||
public static String DB_NAME;
|
||||
|
||||
|
||||
|
||||
|
||||
public static final boolean DEBUG_MODE = true;
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ package dev.zontreck.playsync.data;
|
|||
|
||||
import dev.zontreck.ariaslib.util.Hashing;
|
||||
import dev.zontreck.playsync.Settings;
|
||||
import dev.zontreck.playsync.data.database.Migrations;
|
||||
import dev.zontreck.playsync.data.database.migrations.ChunksTable;
|
||||
import dev.zontreck.playsync.exceptions.UnsupportedAlgorithmException;
|
||||
|
||||
import javax.xml.crypto.Data;
|
||||
|
@ -13,7 +15,7 @@ import java.nio.file.Path;
|
|||
*/
|
||||
public class DataFragment
|
||||
{
|
||||
private byte[] data = new byte[1024];
|
||||
private byte[] data = new byte[512];
|
||||
|
||||
/**
|
||||
* Takes either 1024, or the remaining number of bytes, whichever is less
|
||||
|
@ -22,7 +24,7 @@ public class DataFragment
|
|||
*/
|
||||
public static DataFragment Create(DataInputStream dis) throws IOException {
|
||||
DataFragment frag = new DataFragment();
|
||||
frag.data = dis.readNBytes(1024);
|
||||
frag.data = dis.readNBytes(512);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
@ -30,32 +32,50 @@ public class DataFragment
|
|||
/**
|
||||
* Save the data fragment to its hash ID
|
||||
*/
|
||||
public void Save()
|
||||
public byte[] Save()
|
||||
{
|
||||
Path chunks = Settings.getDataChunksFolder();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
|
||||
try {
|
||||
Path chunk = chunks.resolve(getHash() + ".bin");
|
||||
if(!chunks.toFile().exists()) chunks.toFile().mkdirs();
|
||||
|
||||
//DataAccountant.DataWritten += data.length;
|
||||
if(chunk.toFile().exists()) {
|
||||
if(((ChunksTable)Migrations.Tables.get(ChunksTable.class)).hasChunk(getHash()))
|
||||
{
|
||||
DataAccountant.DataSkipped += data.length;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DataOutputStream dos = new DataOutputStream(new FileOutputStream(chunk.toFile()));
|
||||
|
||||
dos.writeInt(getSize());
|
||||
dos.write(data);
|
||||
dos.close();
|
||||
} catch (UnsupportedAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read from a byte array
|
||||
* @param array Array to load as a data fragment
|
||||
* @return The fragment of data
|
||||
*/
|
||||
public static DataFragment Read(byte[] array)
|
||||
{
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(array);
|
||||
DataInputStream dis = new DataInputStream(bais);
|
||||
|
||||
DataFragment fragment = new DataFragment();
|
||||
|
||||
try {
|
||||
int count = dis.readInt();
|
||||
fragment.data = dis.readNBytes(count);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@ public class Manifest
|
|||
public List<String> Fragments = new ArrayList<>();
|
||||
public String FileHash = "";
|
||||
public ID6 GameID;
|
||||
public String GameName;
|
||||
public Platform GamePlatform = Platform.Unknown;
|
||||
private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
|
||||
|
||||
|
@ -56,6 +57,7 @@ public class Manifest
|
|||
DataOutputStream dos = new DataOutputStream(new FileOutputStream(manifest.toFile()));
|
||||
dos.write(GameID.asBytes());
|
||||
dos.writeByte(GamePlatform.ordinal());
|
||||
dos.writeUTF(GameName);
|
||||
|
||||
dos.writeInt(Fragments.size());
|
||||
|
||||
|
@ -91,6 +93,7 @@ public class Manifest
|
|||
|
||||
ret.GameID = ID6.fromBytes(dis.readNBytes(6));
|
||||
ret.GamePlatform = Platform.fromByte(dis.readByte());
|
||||
ret.GameName = dis.readUTF();
|
||||
ret.FileHash = hash;
|
||||
int count = dis.readInt();
|
||||
for(int i=0;i<count;i++)
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package dev.zontreck.playsync.data.database;
|
||||
|
||||
import dev.zontreck.playsync.Settings;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class DatabaseConnection {
|
||||
private static Connection connection = null;
|
||||
|
||||
public static Connection getConnection() throws SQLException {
|
||||
if (connection == null || connection.isClosed()) {
|
||||
try {
|
||||
// Load MariaDB JDBC driver
|
||||
Class.forName("org.mariadb.jdbc.Driver");
|
||||
|
||||
// Establish connection
|
||||
connection = DriverManager.getConnection(
|
||||
"jdbc:mariadb://" + Settings.DB_HOST + ":" + Settings.DB_PORT + "/" + Settings.DB_NAME,
|
||||
Settings.DB_USERNAME, Settings.DB_PASSWORD
|
||||
);
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
e.printStackTrace();
|
||||
throw new SQLException("Failed to connect to database.");
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
public static void closeConnection() throws SQLException {
|
||||
if (connection != null && !connection.isClosed()) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
package dev.zontreck.playsync.data.database;
|
||||
|
||||
|
||||
|
||||
import dev.zontreck.eventsbus.Bus;
|
||||
import dev.zontreck.eventsbus.EventDispatcher;
|
||||
import dev.zontreck.playsync.server.events.MigrationEvent;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Migrations {
|
||||
private static final String MIGRATIONS_TABLE_NAME = "migrations";
|
||||
public static Map<Class<?>,Migration> Tables = new HashMap<>();
|
||||
|
||||
|
||||
public static void createMigrationsTable(Connection connection) throws SQLException {
|
||||
String createTableQuery = "CREATE TABLE IF NOT EXISTS " + MIGRATIONS_TABLE_NAME + " ("
|
||||
+ "table_name VARCHAR(255) PRIMARY KEY,"
|
||||
+ "version INT NOT NULL"
|
||||
+ ")";
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(createTableQuery)) {
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getTableVersion(Connection connection, String tableName) throws SQLException {
|
||||
String selectQuery = "SELECT version FROM " + MIGRATIONS_TABLE_NAME + " WHERE table_name = ?";
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(selectQuery)) {
|
||||
statement.setString(1, tableName);
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
return resultSet.getInt("version");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Default version if not found
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table version in the migrations table
|
||||
* @param connection The DB connection to use
|
||||
* @param tableName The table name to update
|
||||
* @param version The new version to store
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static void setTableVersion(Connection connection, String tableName, int version) throws SQLException {
|
||||
String updateQuery = "INSERT INTO " + MIGRATIONS_TABLE_NAME + " (table_name, version) VALUES (?, ?)"
|
||||
+ " ON DUPLICATE KEY UPDATE version = ?";
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(updateQuery)) {
|
||||
statement.setString(1, tableName);
|
||||
statement.setInt(2, version);
|
||||
statement.setInt(3, version);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public static void doMigration(Connection connection) throws SQLException {
|
||||
int ver = 0;
|
||||
try {
|
||||
ver = getTableVersion(connection, MIGRATIONS_TABLE_NAME);
|
||||
|
||||
}catch(SQLException e)
|
||||
{
|
||||
createMigrationsTable(connection);
|
||||
setTableVersion(connection, MIGRATIONS_TABLE_NAME, 1);
|
||||
|
||||
ver = getTableVersion(connection, MIGRATIONS_TABLE_NAME);
|
||||
}
|
||||
if(ver == 0)
|
||||
{
|
||||
createMigrationsTable(connection);
|
||||
|
||||
setTableVersion(connection, MIGRATIONS_TABLE_NAME, 1);
|
||||
}
|
||||
|
||||
switch(ver)
|
||||
{
|
||||
default:
|
||||
{
|
||||
break; // nothing to do here yet
|
||||
}
|
||||
}
|
||||
|
||||
EventDispatcher.Post(new MigrationEvent());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abstract class defining the functions that will need to be used by any table implementing migrations.
|
||||
*/
|
||||
public static abstract class Migration {
|
||||
|
||||
/**
|
||||
* Gets the name of the table associated with this migration.
|
||||
*
|
||||
* @return The name of the table.
|
||||
*/
|
||||
public abstract String getTableName();
|
||||
|
||||
/**
|
||||
* Creates the table associated with this migration if it doesn't exist.
|
||||
*
|
||||
* @param connection The database connection.
|
||||
* @throws SQLException if a database access error occurs or this method is called on a closed connection.
|
||||
*/
|
||||
public abstract void createTable(Connection connection) throws SQLException;
|
||||
|
||||
/**
|
||||
* Gets the current version of the table from the migrations table.
|
||||
*
|
||||
* @return The current version of the table.
|
||||
* @throws SQLException if a database access error occurs or this method is called on a closed connection.
|
||||
*/
|
||||
public int getVersion() throws SQLException {
|
||||
return Migrations.getTableVersion(DatabaseConnection.getConnection(), getTableName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the version of the table in the migrations table.
|
||||
*
|
||||
* @param version The version to set.
|
||||
* @throws SQLException if a database access error occurs or this method is called on a closed connection.
|
||||
*/
|
||||
public void setVersion(int version) throws SQLException {
|
||||
Migrations.setTableVersion(DatabaseConnection.getConnection(), getTableName(), version);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used for the internal doMigrations function. It should return the current highest migration version for the table
|
||||
* @return Current highest version
|
||||
*/
|
||||
public abstract int getCurrentTableVersion();
|
||||
|
||||
/**
|
||||
* Used internally.
|
||||
* <br/><br/>
|
||||
* This function executes migrations to bring the table to the current version.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void doMigration() throws SQLException
|
||||
{
|
||||
int ver = getVersion();
|
||||
Connection conn = DatabaseConnection.getConnection();
|
||||
|
||||
if(ver==0) createTable(conn);
|
||||
|
||||
while(ver < getCurrentTableVersion())
|
||||
{
|
||||
migrate(ver+1);
|
||||
|
||||
System.out.println("Migrate Table [" + getTableName() +"] from version " + ver + " to " + (ver+1));
|
||||
ver = getVersion();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a migration specific to the table itself
|
||||
* @param version
|
||||
* @throws SQLException
|
||||
*/
|
||||
public abstract void migrate(int version) throws SQLException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package dev.zontreck.playsync.data.database.migrations;
|
||||
|
||||
import dev.zontreck.playsync.data.DataFragment;
|
||||
import dev.zontreck.playsync.data.database.DatabaseConnection;
|
||||
import dev.zontreck.playsync.data.database.Migrations;
|
||||
import dev.zontreck.playsync.exceptions.UnsupportedAlgorithmException;
|
||||
|
||||
import javax.sql.RowSet;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class ChunksTable extends Migrations.Migration {
|
||||
@Override
|
||||
public String getTableName() {
|
||||
return "chunks";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTable(Connection connection) throws SQLException {
|
||||
String sql = "CREATE TABLE `chunks` (" +
|
||||
" `Hash` varchar(255) NOT NULL," +
|
||||
" `Data` blob NOT NULL," +
|
||||
" PRIMARY KEY (`Hash`)," +
|
||||
" UNIQUE KEY `Hash` (`Hash`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci";
|
||||
|
||||
try (PreparedStatement pstat = connection.prepareStatement(sql))
|
||||
{
|
||||
pstat.executeUpdate();
|
||||
}
|
||||
|
||||
setVersion(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentTableVersion() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(int version) throws SQLException {
|
||||
Connection conn = DatabaseConnection.getConnection();
|
||||
|
||||
switch(version)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the database to check if the Chunk exists or not
|
||||
* @param hash The chunk's hash value
|
||||
* @return True if the chunk is present in the database
|
||||
*/
|
||||
public boolean hasChunk(String hash)
|
||||
{
|
||||
String sql = "SELECT * FROM `?`" +
|
||||
" WHERE Hash='?';";
|
||||
|
||||
try(PreparedStatement pstat = DatabaseConnection.getConnection().prepareStatement(sql))
|
||||
{
|
||||
pstat.setString(1, getTableName());
|
||||
pstat.setString(2, hash);
|
||||
|
||||
ResultSet result = pstat.executeQuery();
|
||||
if(result.getRow() == 0 && !result.next())
|
||||
{
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the chunk if it exists.
|
||||
* <br/>
|
||||
* If the chunk does not exist, returns null, otherwise, never null.
|
||||
* @param hash The hash to check for
|
||||
* @see ChunksTable#hasChunk(String)
|
||||
* @return The deserialied DataFragment
|
||||
*/
|
||||
public DataFragment getChunk(String hash)
|
||||
{
|
||||
if(!hasChunk(hash)) return null;
|
||||
String sql = "SELECT Data from `?`" +
|
||||
" WHERE Hash='?';";
|
||||
|
||||
try (PreparedStatement pstat = DatabaseConnection.getConnection().prepareStatement(sql))
|
||||
{
|
||||
pstat.setString(1, getTableName());
|
||||
pstat.setString(2, hash);
|
||||
|
||||
ResultSet result = pstat.executeQuery();
|
||||
if(result.getRow()==0) result.next();
|
||||
byte[] blob = result.getBytes("Data");
|
||||
|
||||
return DataFragment.Read(blob);
|
||||
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the data
|
||||
* @param fragment The fragment to insert
|
||||
*/
|
||||
public void setChunk(DataFragment fragment)
|
||||
{
|
||||
try {
|
||||
String hash = fragment.getHash();
|
||||
byte[] array = fragment.Save();
|
||||
|
||||
String sql = "INSERT INTO `?` (Hash, Data) VALUES (" +
|
||||
"'?', ?" +
|
||||
");";
|
||||
|
||||
try(PreparedStatement pstat = DatabaseConnection.getConnection().prepareStatement(sql))
|
||||
{
|
||||
pstat.setString(1, getTableName());
|
||||
pstat.setString(2, hash);
|
||||
pstat.setBytes(3, array);
|
||||
|
||||
pstat.executeUpdate();
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
} catch (UnsupportedAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package dev.zontreck.playsync.data.database.migrations;
|
||||
|
||||
import dev.zontreck.playsync.data.database.DatabaseConnection;
|
||||
import dev.zontreck.playsync.data.database.Migrations;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class ManifestsTable extends Migrations.Migration
|
||||
{
|
||||
@Override
|
||||
public String getTableName() {
|
||||
return "manifests";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTable(Connection connection) throws SQLException {
|
||||
String sql = "CREATE TABLE `" + getTableName() + "` (" +
|
||||
" `Name` varchar(255) NOT NULL," +
|
||||
" `Data` blob NOT NULL," +
|
||||
" PRIMARY KEY (`Name`)," +
|
||||
" UNIQUE KEY `Name` (`Name`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;";
|
||||
|
||||
try (PreparedStatement pstat = connection.prepareStatement(sql))
|
||||
{
|
||||
|
||||
pstat.executeUpdate();
|
||||
}
|
||||
|
||||
setVersion(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentTableVersion() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(int version) throws SQLException {
|
||||
Connection conn = DatabaseConnection.getConnection();
|
||||
switch(version)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
String sql = "ALTER TABLE `" + getTableName() + "` " +
|
||||
"CHANGE `Name` `Hash` VARCHAR(255) " +
|
||||
"CHARACTER SET utf8mb4 " +
|
||||
"COLLATE utf8mb4_general_ci NOT NULL;";
|
||||
|
||||
try(PreparedStatement pstat = conn.prepareStatement(sql))
|
||||
{
|
||||
pstat.executeUpdate();
|
||||
}
|
||||
setVersion(2);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
String sql = "ALTER TABLE `manifests` ADD `GameTitle` " +
|
||||
"VARCHAR(255) NOT NULL AFTER `Hash`; ";
|
||||
|
||||
try(PreparedStatement pstat = conn.prepareStatement(sql))
|
||||
{
|
||||
pstat.executeUpdate();
|
||||
}
|
||||
setVersion(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,34 @@
|
|||
package dev.zontreck.playsync.events;
|
||||
|
||||
import dev.zontreck.ariaslib.util.Maps;
|
||||
import dev.zontreck.eventsbus.annotations.EventSubscriber;
|
||||
import dev.zontreck.eventsbus.annotations.SingleshotEvent;
|
||||
import dev.zontreck.eventsbus.annotations.Subscribe;
|
||||
import dev.zontreck.playsync.data.database.Migrations;
|
||||
import dev.zontreck.playsync.data.database.migrations.ChunksTable;
|
||||
import dev.zontreck.playsync.data.database.migrations.ManifestsTable;
|
||||
import dev.zontreck.playsync.server.events.MigrationEvent;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
@EventSubscriber
|
||||
public class EventHandlers
|
||||
{
|
||||
@Subscribe(allowCancelled = false)
|
||||
@SingleshotEvent
|
||||
public static void onMigrations(MigrationEvent event)
|
||||
{
|
||||
System.out.println("Performing table migrations...");
|
||||
ManifestsTable manifests = new ManifestsTable();
|
||||
ChunksTable chunks = new ChunksTable();
|
||||
try {
|
||||
manifests.doMigration();
|
||||
chunks.doMigration();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Migrations.Tables = Maps.of(new Maps.Entry<>(ManifestsTable.class, manifests), new Maps.Entry<>(ChunksTable.class, chunks));
|
||||
System.out.println("Table migrations completed...");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package dev.zontreck.playsync.server.events;
|
||||
|
||||
import dev.zontreck.ariaslib.util.Lists;
|
||||
import dev.zontreck.eventsbus.Cancellable;
|
||||
import dev.zontreck.eventsbus.Event;
|
||||
import dev.zontreck.eventsbus.annotations.Cancellable;
|
||||
import dev.zontreck.playsync.exceptions.HTTPResponseLockedException;
|
||||
import dev.zontreck.playsync.server.HTTPResponseData;
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package dev.zontreck.playsync.server.events;
|
||||
|
||||
import dev.zontreck.eventsbus.Event;
|
||||
|
||||
public class MigrationEvent extends Event
|
||||
{
|
||||
}
|
Loading…
Reference in a new issue