diff --git a/data-migration/pom.xml b/data-migration/pom.xml
index 81492fb20a..902d8dd8ed 100644
--- a/data-migration/pom.xml
+++ b/data-migration/pom.xml
@@ -9,6 +9,17 @@
data-migration
jar
+
+
+
+
+ com.mockrunner
+ mockrunner-jdbc
+ 2.0.1
+ test
+
+
+
@@ -31,7 +42,7 @@
org.xerial
sqlite-jdbc
- compile
+ runtime
@@ -44,7 +55,7 @@
com.h2database
h2
- compile
+ runtime
@@ -58,6 +69,13 @@
system-rules
test
+
+
+
+ com.mockrunner
+ mockrunner-jdbc
+ test
+
diff --git a/data-migration/src/main/java/com/quorum/tessera/data/migration/CmdLineExecutor.java b/data-migration/src/main/java/com/quorum/tessera/data/migration/CmdLineExecutor.java
index 7a77d49046..c937bc58ce 100644
--- a/data-migration/src/main/java/com/quorum/tessera/data/migration/CmdLineExecutor.java
+++ b/data-migration/src/main/java/com/quorum/tessera/data/migration/CmdLineExecutor.java
@@ -1,27 +1,29 @@
-
package com.quorum.tessera.data.migration;
+import java.io.InputStream;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
+import java.util.Properties;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-
public class CmdLineExecutor {
private CmdLineExecutor() {
throw new UnsupportedOperationException("");
}
-
- protected static int execute(String... args) throws Exception {
+ protected static int execute(String... args) throws Exception {
Options options = new Options();
@@ -47,6 +49,8 @@ protected static int execute(String... args) throws Exception {
.required()
.build());
+
+
options.addOption(
Option.builder()
.longOpt("exporttype")
@@ -57,6 +61,16 @@ protected static int execute(String... args) throws Exception {
.argName("TYPE")
.required()
.build());
+
+ options.addOption(
+ Option.builder()
+ .longOpt("dbconfig")
+ .desc("Properties file with create table, insert row and jdbc url")
+ .hasArg(true)
+ .optionalArg(false)
+ .numberOfArgs(1)
+ .argName("PATH")
+ .build());
options.addOption(
Option.builder()
@@ -71,25 +85,27 @@ protected static int execute(String... args) throws Exception {
options.addOption(
Option.builder()
- .longOpt("dbuser")
- .desc("Database username to use")
- .hasArg(true)
- .optionalArg(true)
- .numberOfArgs(1)
- .argName("PATH")
- .required()
- .build());
+ .longOpt("dbuser")
+ .desc("Database username to use")
+ .hasArg(true)
+ .optionalArg(true)
+ .numberOfArgs(1)
+ .argName("USER")
+ .required()
+ .build());
options.addOption(
Option.builder()
- .longOpt("dbpass")
- .desc("Database password to use")
- .hasArg(true)
- .optionalArg(true)
- .numberOfArgs(1)
- .argName("PATH")
- .required()
- .build());
+ .longOpt("dbpass")
+ .desc("Database password to use")
+ .hasArg(true)
+ .optionalArg(true)
+ .numberOfArgs(1)
+ .argName("PASS")
+ .required()
+ .build());
+
+
if (Arrays.asList(args).contains("help")) {
HelpFormatter formatter = new HelpFormatter();
@@ -110,10 +126,41 @@ protected static int execute(String... args) throws Exception {
final String password = line.getOptionValue("dbpass");
final String exportTypeStr = line.getOptionValue("exporttype");
- final ExportType exportType = ExportType.valueOf(exportTypeStr.toUpperCase());
+
+ final ExportType exportType = Optional.ofNullable(exportTypeStr)
+ .map(String::toUpperCase)
+ .map(ExportType::valueOf).get();
final Path outputFile = Paths.get(line.getOptionValue("outputfile")).toAbsolutePath();
- final DataExporter dataExporter = DataExporterFactory.create(exportType);
+
+ final DataExporter dataExporter;
+ if (exportType == ExportType.JDBC) {
+ if (!line.hasOption("dbconfig")) {
+ throw new MissingOptionException("dbconfig file path is required when no export type is defined.");
+ }
+
+ String dbconfig = line.getOptionValue("dbconfig");
+
+ Properties properties = new Properties();
+ try (InputStream inStream = Files.newInputStream(Paths.get(dbconfig))) {
+ properties.load(inStream);
+ }
+
+ String insertRow = Objects.requireNonNull(properties.getProperty("insertRow",null),
+ "No insertRow value defined in config file. ");
+
+ String createTable = Objects.requireNonNull(properties.getProperty("createTable",null),
+ "No createTable value defined in config file. ");
+
+ String jdbcUrl = Objects.requireNonNull(properties.getProperty("jdbcUrl",null),
+ "No jdbcUrl value defined in config file. ");
+
+ dataExporter = new JdbcDataExporter(jdbcUrl, insertRow, createTable);
+
+ } else {
+ dataExporter = DataExporterFactory.create(exportType);
+ }
+
dataExporter.export(data, outputFile, username, password);
System.out.printf("Exported data to %s", Objects.toString(outputFile));
diff --git a/data-migration/src/main/java/com/quorum/tessera/data/migration/ExportType.java b/data-migration/src/main/java/com/quorum/tessera/data/migration/ExportType.java
index d25b1cfa38..9d16e7a188 100644
--- a/data-migration/src/main/java/com/quorum/tessera/data/migration/ExportType.java
+++ b/data-migration/src/main/java/com/quorum/tessera/data/migration/ExportType.java
@@ -1,19 +1,11 @@
package com.quorum.tessera.data.migration;
-import java.sql.Driver;
-
public enum ExportType {
- H2(org.h2.Driver.class),
- SQLITE(org.sqlite.JDBC.class);
-
- final Class extends Driver> driver;
+ H2,
+ SQLITE,
+ JDBC;
- ExportType(Class extends Driver> driver) {
- this.driver = driver;
- }
-
-
}
diff --git a/data-migration/src/main/java/com/quorum/tessera/data/migration/JdbcDataExporter.java b/data-migration/src/main/java/com/quorum/tessera/data/migration/JdbcDataExporter.java
new file mode 100644
index 0000000000..fe9547f3f9
--- /dev/null
+++ b/data-migration/src/main/java/com/quorum/tessera/data/migration/JdbcDataExporter.java
@@ -0,0 +1,46 @@
+package com.quorum.tessera.data.migration;
+
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class JdbcDataExporter implements DataExporter {
+
+ private final String jdbcUrl;
+
+ private final String insertRow;
+
+ private final String createTable;
+
+ public JdbcDataExporter(String jdbcUrl, String insertRow, String createTable) {
+ this.jdbcUrl = jdbcUrl;
+ this.insertRow = insertRow;
+ this.createTable = createTable;
+ }
+
+ @Override
+ public void export(Map data, Path output, String username, String password) throws SQLException {
+
+ try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {
+
+ try (Statement stmt = conn.createStatement()) {
+ stmt.executeUpdate(createTable);
+ }
+
+ try (PreparedStatement insertStatement = conn.prepareStatement(insertRow)) {
+ for (Entry values : data.entrySet()) {
+ insertStatement.setBytes(1, values.getKey());
+ insertStatement.setBytes(2, values.getValue());
+ insertStatement.execute();
+ }
+ }
+
+ }
+ }
+
+}
diff --git a/data-migration/src/test/java/com/quorum/tessera/data/migration/CmdLineExecutorTest.java b/data-migration/src/test/java/com/quorum/tessera/data/migration/CmdLineExecutorTest.java
index acccf3c250..dd0f3cd37e 100644
--- a/data-migration/src/test/java/com/quorum/tessera/data/migration/CmdLineExecutorTest.java
+++ b/data-migration/src/test/java/com/quorum/tessera/data/migration/CmdLineExecutorTest.java
@@ -1,5 +1,6 @@
package com.quorum.tessera.data.migration;
+import com.mockrunner.mock.jdbc.JDBCMockObjectFactory;
import org.apache.commons.cli.MissingOptionException;
import org.junit.After;
import org.junit.Before;
@@ -36,9 +37,9 @@ public void onSetup() throws Exception {
public void onTearDown() throws IOException {
if (Files.exists(outputPath)) {
Files.walk(outputPath)
- .sorted(Comparator.reverseOrder())
- .map(Path::toFile)
- .forEach(File::delete);
+ .sorted(Comparator.reverseOrder())
+ .map(Path::toFile)
+ .forEach(File::delete);
}
}
@@ -60,7 +61,7 @@ public void noOptions() {
assertThat(throwable).isInstanceOf(MissingOptionException.class);
assertThat(((MissingOptionException) throwable).getMissingOptions())
- .containsExactlyInAnyOrder("storetype", "inputpath", "exporttype", "outputfile", "dbpass", "dbuser");
+ .containsExactlyInAnyOrder("storetype", "inputpath", "exporttype", "outputfile", "dbpass", "dbuser");
}
@@ -150,8 +151,53 @@ public void cannotBeConstructed() throws Exception {
final Throwable throwable = catchThrowable(constructor::newInstance);
assertThat(throwable)
- .isInstanceOf(InvocationTargetException.class)
- .hasCauseExactlyInstanceOf(UnsupportedOperationException.class);
+ .isInstanceOf(InvocationTargetException.class)
+ .hasCauseExactlyInstanceOf(UnsupportedOperationException.class);
}
+ @Test(expected = org.apache.commons.cli.MissingOptionException.class)
+ public void exportTypeJdbcNoDbConfigProvided() throws Exception {
+
+ final Path inputFile = Paths.get(getClass().getResource("/dir/").toURI());
+
+ final String[] args = new String[]{
+ "-storetype", "dir",
+ "-inputpath", inputFile.toString(),
+ "-outputfile", outputPath.toString(),
+ "-exporttype", "jdbc",
+ "-dbpass", "-dbuser"
+ };
+
+ CmdLineExecutor.execute(args);
+
+ }
+
+ @Test
+ public void exportTypeJdbc() throws Exception {
+
+ JDBCMockObjectFactory mockObjectFactory = new JDBCMockObjectFactory();
+
+ try {
+ mockObjectFactory.registerMockDriver();
+
+ String dbConfigPath = getClass().getResource("/dbconfig.properties").getFile();
+
+ final Path inputFile = Paths.get(getClass().getResource("/dir/").toURI());
+
+ final String[] args = new String[]{
+ "-storetype", "dir",
+ "-inputpath", inputFile.toString(),
+ "-outputfile", outputPath.toString(),
+ "-exporttype", "jdbc",
+ "-dbconfig", dbConfigPath,
+ "-dbpass", "-dbuser"
+ };
+
+ CmdLineExecutor.execute(args);
+
+ assertThat(outputPath).isNotNull();
+ } finally {
+ mockObjectFactory.restoreDrivers();
+ }
+ }
}
diff --git a/data-migration/src/test/java/com/quorum/tessera/data/migration/JdbcDataExporterTest.java b/data-migration/src/test/java/com/quorum/tessera/data/migration/JdbcDataExporterTest.java
new file mode 100644
index 0000000000..befc12c60e
--- /dev/null
+++ b/data-migration/src/test/java/com/quorum/tessera/data/migration/JdbcDataExporterTest.java
@@ -0,0 +1,51 @@
+
+package com.quorum.tessera.data.migration;
+
+import com.mockrunner.jdbc.BasicJDBCTestCaseAdapter;
+import com.mockrunner.mock.jdbc.JDBCMockObjectFactory;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.After;
+import org.junit.Test;
+import static org.mockito.Mockito.mock;
+
+
+public class JdbcDataExporterTest extends BasicJDBCTestCaseAdapter{
+
+
+ private final JDBCMockObjectFactory mockObjectFactory = new JDBCMockObjectFactory();
+
+ @Test
+ public void onSetUp() {
+ mockObjectFactory.registerMockDriver();
+ }
+
+ @After
+ public void onTearDown(){
+ mockObjectFactory.restoreDrivers();
+ }
+
+ @Test
+ public void doStuff() throws Exception {
+
+
+ JdbcDataExporter exporter = new JdbcDataExporter("jdbc:bogus","insert stuff","create stuff");
+
+ Map data = new HashMap() {{
+ put("ONE".getBytes(),"TWO".getBytes());
+ }};
+
+
+
+ Path output = mock(Path.class);
+
+ exporter.export(data, output, "someone", "pw");
+
+ verifyAllStatementsClosed();
+ verifyAllStatementsClosed();
+
+
+ }
+
+}
diff --git a/data-migration/src/test/resources/dbconfig.properties b/data-migration/src/test/resources/dbconfig.properties
new file mode 100644
index 0000000000..85b91273e1
--- /dev/null
+++ b/data-migration/src/test/resources/dbconfig.properties
@@ -0,0 +1,3 @@
+insertRow=insert stuff
+createTable=create stuff
+jdbcUrl=jdbc:bogus