jpms related changes for v1.4.2-beta

This commit is contained in:
Craig Raw 2021-06-15 16:50:05 +02:00
parent 1208baf00e
commit 7f178b5f67
8 changed files with 132 additions and 26 deletions

View file

@ -5,7 +5,7 @@ plugins {
id 'org.beryx.jlink' version '2.22.0'
}
def sparrowVersion = '1.4.1'
def sparrowVersion = '1.4.2'
def os = org.gradle.internal.os.OperatingSystem.current()
def osName = os.getFamilyName()
if(os.macOsX) {
@ -50,7 +50,7 @@ dependencies {
exclude group: 'org.slf4j'
}
implementation('org.jdbi:jdbi3-sqlobject:3.20.0')
implementation('org.flywaydb:flyway-core:7.10.1-SNAPSHOT')
implementation('org.flywaydb:flyway-core:7.10.5-SNAPSHOT')
implementation('org.fxmisc.richtext:richtextfx:0.10.4')
implementation('no.tornado:tornadofx-controls:1.0.4')
implementation('com.google.zxing:javase:3.4.0')
@ -138,6 +138,8 @@ jlink {
requires 'com.fasterxml.jackson.databind'
requires 'jdk.crypto.cryptoki'
requires 'java.management'
uses 'org.flywaydb.core.extensibility.FlywayExtension'
uses 'org.flywaydb.core.internal.database.DatabaseType'
}
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--ignore-signing-information', '--exclude-files', '**.png']
@ -161,7 +163,8 @@ jlink {
"--add-opens=javafx.graphics/com.sun.javafx.application=com.sparrowwallet.sparrow",
"--add-opens=java.base/java.net=com.sparrowwallet.sparrow",
"--add-reads=com.sparrowwallet.merged.module=java.desktop",
"--add-reads=com.sparrowwallet.merged.module=java.sql"]
"--add-reads=com.sparrowwallet.merged.module=java.sql",
"--add-reads=com.sparrowwallet.merged.module=com.sparrowwallet.sparrow"]
if(os.macOsX) {
jvmArgs += "--add-opens=javafx.graphics/com.sun.glass.ui.mac=com.sparrowwallet.merged.module"

View file

@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.4.1</string>
<string>1.4.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<!-- See https://developer.apple.com/app-store/categories/ for list of AppStore categories -->

View file

@ -12,7 +12,7 @@ public class AboutController {
private Label title;
public void initializeView() {
title.setText(MainApp.APP_NAME + " " + MainApp.APP_VERSION);
title.setText(MainApp.APP_NAME + " " + MainApp.APP_VERSION + MainApp.APP_VERSION_SUFFIX);
}
public void setStage(Stage stage) {

View file

@ -31,7 +31,8 @@ import java.util.stream.Collectors;
public class MainApp extends Application {
public static final String APP_ID = "com.sparrowwallet.sparrow";
public static final String APP_NAME = "Sparrow";
public static final String APP_VERSION = "1.4.1";
public static final String APP_VERSION = "1.4.2";
public static final String APP_VERSION_SUFFIX = "-beta";
public static final String APP_HOME_PROPERTY = "sparrow.home";
public static final String NETWORK_ENV_PROPERTY = "SPARROW_NETWORK";

View file

@ -230,7 +230,7 @@ public class Hwi {
String hwiPath = hwiExecutable.getAbsolutePath();
if(command.isTestFirst() && (hwiPath.contains(tmpDir) || hwiPath.startsWith(homeDir.getAbsolutePath())) && (!hwiPath.contains(HWI_VERSION_DIR) || !testHwi(hwiExecutable))) {
if(Platform.getCurrent() == Platform.OSX) {
deleteDirectory(hwiExecutable.getParentFile());
IOUtils.deleteDirectory(hwiExecutable.getParentFile());
} else {
hwiExecutable.delete();
}
@ -339,17 +339,6 @@ public class Hwi {
}
}
private boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}
public static File newDirectory(File destinationDir, ZipEntry zipEntry, Set<PosixFilePermission> setFilePermissions) throws IOException {
String destDirPath = destinationDir.getCanonicalPath();

View file

@ -1,8 +1,17 @@
package com.sparrowwallet.sparrow.io;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class IOUtils {
public static FileType getFileType(File file) {
@ -38,4 +47,75 @@ public class IOUtils {
return FileType.UNKNOWN;
}
/**
* List directory contents for a resource folder. Not recursive.
* This is basically a brute-force implementation.
* Works for regular files, JARs and Java modules.
*
* @param clazz Any java class that lives in the same place as the resources you want.
* @param path Should end with "/", but not start with one.
* @return Just the name of each member item, not the full paths.
* @throws URISyntaxException
* @throws IOException
*/
public static String[] getResourceListing(Class clazz, String path) throws URISyntaxException, IOException {
URL dirURL = clazz.getClassLoader().getResource(path);
if(dirURL != null && dirURL.getProtocol().equals("file")) {
/* A file path: easy enough */
return new File(dirURL.toURI()).list();
}
if(dirURL == null) {
/*
* In case of a jar file, we can't actually find a directory.
* Have to assume the same jar as clazz.
*/
String me = clazz.getName().replace(".", "/")+".class";
dirURL = clazz.getClassLoader().getResource(me);
}
if(dirURL.getProtocol().equals("jar")) {
/* A JAR path */
String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); //strip out only the JAR file
JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
Set<String> result = new HashSet<String>(); //avoid duplicates in case it is a subdirectory
while(entries.hasMoreElements()) {
String name = entries.nextElement().getName();
if(name.startsWith(path)) { //filter according to the path
String entry = name.substring(path.length());
int checkSubdir = entry.indexOf("/");
if (checkSubdir >= 0) {
// if it is a subdirectory, we just return the directory name
entry = entry.substring(0, checkSubdir);
}
result.add(entry);
}
}
return result.toArray(new String[result.size()]);
}
if(dirURL.getProtocol().equals("jrt")) {
java.nio.file.FileSystem jrtFs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
Path resourcePath = jrtFs.getPath("modules/com.sparrowwallet.sparrow", path);
return Files.list(resourcePath).map(filePath -> filePath.getFileName().toString()).toArray(String[]::new);
}
throw new UnsupportedOperationException("Cannot list files for URL " + dirURL);
}
public static boolean deleteDirectory(File directory) {
try {
Files.walk(directory.toPath())
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch(IOException e) {
return false;
}
return true;
}
}

View file

@ -1,6 +1,7 @@
package com.sparrowwallet.sparrow.io.db;
import com.google.common.eventbus.Subscribe;
import com.google.common.io.Files;
import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.crypto.Argon2KeyDeriver;
import com.sparrowwallet.drongo.crypto.AsymmetricKeyDeriver;
@ -29,6 +30,7 @@ import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.util.*;
import java.util.stream.Collectors;
@ -46,6 +48,7 @@ public class DbPersistence implements Persistence {
public static final byte[] HEADER_MAGIC_1 = "SPRW1\n".getBytes(StandardCharsets.UTF_8);
private static final String H2_USER = "sa";
private static final String H2_PASSWORD = "";
public static final String MIGRATION_RESOURCES_DIR = "com/sparrowwallet/sparrow/sql/";
private HikariDataSource dataSource;
private AsymmetricKeyDeriver keyDeriver;
@ -299,8 +302,9 @@ public class DbPersistence implements Persistence {
}
private void migrate(Storage storage, String schema, ECKey encryptionKey) throws StorageException {
File migrationDir = getMigrationDir();
try {
Flyway flyway = getFlyway(storage, schema, getFilePassword(encryptionKey));
Flyway flyway = getFlyway(storage, schema, getFilePassword(encryptionKey), migrationDir);
flyway.migrate();
} catch(FlywayValidateException e) {
log.error("Failed to open wallet file. Validation error during schema migration.", e);
@ -308,20 +312,22 @@ public class DbPersistence implements Persistence {
} catch(FlywayException e) {
log.error("Failed to open wallet file. ", e);
throw new StorageException("Failed to open wallet file.\n" + e.getMessage(), e);
} finally {
IOUtils.deleteDirectory(migrationDir);
}
}
private void cleanAndMigrate(Storage storage, String schema, String password) throws StorageException {
File migrationDir = getMigrationDir();
try {
boolean existing = (dataSource == null);
Flyway flyway = getFlyway(storage, schema, password);
if(existing) {
Flyway flyway = getFlyway(storage, schema, password, migrationDir);
flyway.clean();
}
flyway.migrate();
} catch(FlywayException e) {
log.error("Failed to save wallet file.", e);
throw new StorageException("Failed to save wallet file.\n" + e.getMessage(), e);
} finally {
IOUtils.deleteDirectory(migrationDir);
}
}
@ -504,8 +510,30 @@ public class DbPersistence implements Persistence {
return jdbi;
}
private Flyway getFlyway(Storage storage, String schema, String password) throws StorageException {
return Flyway.configure().dataSource(getDataSource(storage, password)).locations("com/sparrowwallet/sparrow/sql").schemas(schema).load();
private Flyway getFlyway(Storage storage, String schema, String password, File resourcesDir) throws StorageException {
return Flyway.configure().dataSource(getDataSource(storage, password)).locations("filesystem:" + resourcesDir.getAbsolutePath()).schemas(schema).failOnMissingLocations(true).load();
}
//Flyway does not support JPMS yet, so the migration files are extracted to a temp dir in order to avoid classloader encapsulation issues
private File getMigrationDir() {
File migrationDir = Files.createTempDir();
try {
String[] files = IOUtils.getResourceListing(DbPersistence.class, MIGRATION_RESOURCES_DIR);
for(String name : files) {
File targetFile = new File(migrationDir, name);
try(InputStream inputStream = DbPersistence.class.getResourceAsStream("/" + MIGRATION_RESOURCES_DIR + name)) {
if(inputStream != null) {
java.nio.file.Files.copy(inputStream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} else {
log.error("Could not load resource at /" + MIGRATION_RESOURCES_DIR + name);
}
}
}
} catch(Exception e) {
log.error("Could not extract migration resources", e);
}
return migrationDir;
}
private HikariDataSource getDataSource(Storage storage, String password) throws StorageException {
@ -518,11 +546,15 @@ public class DbPersistence implements Persistence {
private HikariDataSource createDataSource(File walletFile, String password) throws StorageException {
try {
Class.forName("org.h2.Driver");
HikariConfig config = new HikariConfig();
config.setJdbcUrl(getUrl(walletFile, password));
config.setUsername(H2_USER);
config.setPassword(password == null ? H2_PASSWORD : password + " " + H2_PASSWORD);
return new HikariDataSource(config);
} catch(ClassNotFoundException e) {
log.error("Cannot find H2 driver", e);
throw new StorageException("Cannot find H2 driver", e);
} catch(HikariPool.PoolInitializationException e) {
if(e.getMessage() != null && e.getMessage().contains("Database may be already in use")) {
log.error("Wallet file may already be in use. Make sure the application is not running elsewhere.", e);

View file

@ -9,6 +9,7 @@
<logger name="sun.net.www.protocol.http.HttpURLConnection" level="INFO" />
<logger name="h2database" level="ERROR" />
<logger name="com.zaxxer.hikari.HikariDataSource" level="WARN" />
<logger name="com.zaxxer.hikari.pool.HikariPool" level="ERROR" />
<logger name="org.flywaydb.core.internal.command.DbValidate" level="WARN" />
<logger name="org.flywaydb.core.internal.command.DbMigrate" level="WARN" />
<logger name="org.flywaydb.core.internal.command.DbClean" level="ERROR" />