mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 07:07:43 +01:00
Prepared DownloadUtil task and updateAlert window
This commit is contained in:
parent
73de328cca
commit
fc01f797f0
6 changed files with 2067 additions and 3 deletions
3
.idea/copyright/profiles_settings.xml
generated
3
.idea/copyright/profiles_settings.xml
generated
|
@ -1,3 +0,0 @@
|
||||||
<component name="CopyrightManager">
|
|
||||||
<settings default="" />
|
|
||||||
</component>
|
|
110
common/src/main/java/io/bitsquare/common/util/DownloadUtil.java
Normal file
110
common/src/main/java/io/bitsquare/common/util/DownloadUtil.java
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
package io.bitsquare.common.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility that downloads a file from a URL.
|
||||||
|
* @author www.codejava.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.scene.control.ProgressIndicator;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
|
||||||
|
public class DownloadUtil extends Task<File> {
|
||||||
|
|
||||||
|
/* public static void main(String [] args) {
|
||||||
|
try {
|
||||||
|
downloadFile("https://github.com/bitsquare/bitsquare/releases/download/v0.4.9.9/Bitsquare-64bit-0.4.9.9.exe", "/home/bob/test");
|
||||||
|
} catch (IOException e) {e.printStackTrace();}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
private static final int BUFFER_SIZE = 4096;
|
||||||
|
private final String fileURL;
|
||||||
|
private final String saveDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads a file from a URL
|
||||||
|
* @param fileURL HTTP URL of the file to be downloaded
|
||||||
|
* @param saveDir path of the directory to save the file
|
||||||
|
*/
|
||||||
|
public DownloadUtil (final String fileURL, final String saveDir) {
|
||||||
|
this.fileURL = fileURL;
|
||||||
|
this.saveDir = saveDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads a file from a URL
|
||||||
|
* @param fileURL HTTP URL of the file to be downloaded
|
||||||
|
*/
|
||||||
|
public DownloadUtil (final String fileURL) {
|
||||||
|
this.fileURL = fileURL;
|
||||||
|
this.saveDir = System.getProperty("java.io.tmpdir");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected File call() throws Exception{
|
||||||
|
URL url = new URL(fileURL);
|
||||||
|
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
|
||||||
|
int responseCode = httpConn.getResponseCode();
|
||||||
|
|
||||||
|
// always check HTTP response code first
|
||||||
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
|
String fileName = "";
|
||||||
|
String disposition = httpConn.getHeaderField("Content-Disposition");
|
||||||
|
String contentType = httpConn.getContentType();
|
||||||
|
int contentLength = httpConn.getContentLength();
|
||||||
|
if (! contentLength > 0)
|
||||||
|
contentLength = -1;
|
||||||
|
|
||||||
|
if (disposition != null) {
|
||||||
|
// extracts file name from header field
|
||||||
|
int index = disposition.indexOf("filename=");
|
||||||
|
if (index > 0) {
|
||||||
|
fileName = disposition.substring(index + 9, disposition.length());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// extracts file name from URL
|
||||||
|
fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1, fileURL.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* System.out.println("Content-Type = " + contentType);
|
||||||
|
System.out.println("Content-Disposition = " + disposition);
|
||||||
|
System.out.println("Content-Length = " + contentLength);
|
||||||
|
System.out.println("fileName = " + fileName);
|
||||||
|
*/
|
||||||
|
// opens input stream from the HTTP connection
|
||||||
|
InputStream inputStream = httpConn.getInputStream();
|
||||||
|
String saveFilePath = saveDir + File.separator + fileName;
|
||||||
|
|
||||||
|
// opens an output stream to save into file
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(saveFilePath);
|
||||||
|
|
||||||
|
int bytesRead = -1;
|
||||||
|
int totalRead = 0;
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
totalRead += bytesRead;
|
||||||
|
updateProgress(totalRead, contentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
} finally {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("File downloaded");
|
||||||
|
return saveFilePath;
|
||||||
|
} else {
|
||||||
|
System.out.println("No file to download. Server replied HTTP code: " + responseCode);
|
||||||
|
}
|
||||||
|
httpConn.disconnect();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
483
common/src/main/java/io/bitsquare/common/util/Utilities.java
Normal file
483
common/src/main/java/io/bitsquare/common/util/Utilities.java
Normal file
|
@ -0,0 +1,483 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bitsquare.
|
||||||
|
*
|
||||||
|
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.bitsquare.common.util;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import com.google.gson.*;
|
||||||
|
import io.bitsquare.io.LookAheadObjectInputStream;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.scene.control.ProgressIndicator;
|
||||||
|
import javafx.scene.input.Clipboard;
|
||||||
|
import javafx.scene.input.ClipboardContent;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General utilities
|
||||||
|
*/
|
||||||
|
public class Utilities {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
|
||||||
|
private static long lastTimeStamp = System.currentTimeMillis();
|
||||||
|
public static final String LB = System.getProperty("line.separator");
|
||||||
|
public static final String LB2 = LB + LB;
|
||||||
|
|
||||||
|
public static String objectToJson(Object object) {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.setExclusionStrategies(new AnnotationExclusionStrategy())
|
||||||
|
/*.excludeFieldsWithModifiers(Modifier.TRANSIENT)*/
|
||||||
|
/* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)*/
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.create();
|
||||||
|
return gson.toJson(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ListeningExecutorService getListeningExecutorService(String name,
|
||||||
|
int corePoolSize,
|
||||||
|
int maximumPoolSize,
|
||||||
|
long keepAliveTimeInSec) {
|
||||||
|
return MoreExecutors.listeningDecorator(getThreadPoolExecutor(name, corePoolSize, maximumPoolSize, keepAliveTimeInSec));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ThreadPoolExecutor getThreadPoolExecutor(String name,
|
||||||
|
int corePoolSize,
|
||||||
|
int maximumPoolSize,
|
||||||
|
long keepAliveTimeInSec) {
|
||||||
|
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat(name)
|
||||||
|
.setDaemon(true)
|
||||||
|
.build();
|
||||||
|
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec,
|
||||||
|
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
|
||||||
|
executor.allowCoreThreadTimeOut(true);
|
||||||
|
executor.setRejectedExecutionHandler((r, e) -> {
|
||||||
|
log.debug("RejectedExecutionHandler called");
|
||||||
|
});
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor(String name,
|
||||||
|
int corePoolSize,
|
||||||
|
int maximumPoolSize,
|
||||||
|
long keepAliveTimeInSec) {
|
||||||
|
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat(name)
|
||||||
|
.setDaemon(true)
|
||||||
|
.setPriority(Thread.MIN_PRIORITY)
|
||||||
|
.build();
|
||||||
|
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
|
||||||
|
executor.setKeepAliveTime(keepAliveTimeInSec, TimeUnit.SECONDS);
|
||||||
|
executor.allowCoreThreadTimeOut(true);
|
||||||
|
executor.setMaximumPoolSize(maximumPoolSize);
|
||||||
|
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||||
|
executor.setRejectedExecutionHandler((r, e) -> {
|
||||||
|
log.debug("RejectedExecutionHandler called");
|
||||||
|
});
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isUnix() {
|
||||||
|
return isOSX() || isLinux() || getOSName().contains("freebsd");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isWindows() {
|
||||||
|
return getOSName().contains("win");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isOSX() {
|
||||||
|
return getOSName().contains("mac") || getOSName().contains("darwin");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLinux() {
|
||||||
|
return getOSName().contains("linux");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getOSName() {
|
||||||
|
return System.getProperty("os.name").toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getOSArchitecture() {
|
||||||
|
String osArch = System.getProperty("os.arch");
|
||||||
|
if (isWindows()) {
|
||||||
|
// See: Like always windows needs extra treatment
|
||||||
|
// https://stackoverflow.com/questions/20856694/how-to-find-the-os-bit-type
|
||||||
|
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
|
||||||
|
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
|
||||||
|
return arch.endsWith("64")
|
||||||
|
|| wow64Arch != null && wow64Arch.endsWith("64")
|
||||||
|
? "64" : "32";
|
||||||
|
} else if (osArch.contains("arm")) {
|
||||||
|
// armv8 is 64 bit, armv7l is 32 bit
|
||||||
|
return osArch.contains("64") || osArch.contains("v8") ? "64" : "32";
|
||||||
|
} else if (isLinux()) {
|
||||||
|
return osArch.startsWith("i") ? "32" : "64";
|
||||||
|
} else {
|
||||||
|
return osArch.contains("64") ? "64" : osArch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printSysInfo() {
|
||||||
|
log.info("os.name: " + System.getProperty("os.name"));
|
||||||
|
log.info("os.version: " + System.getProperty("os.version"));
|
||||||
|
log.info("os.arch: " + System.getProperty("os.arch"));
|
||||||
|
log.info("sun.arch.data.model: " + getJVMArchitecture());
|
||||||
|
log.info("JRE: " + System.getProperty("java.runtime.version", "-") + " (" + System.getProperty("java.vendor", "-") + ")");
|
||||||
|
log.info("JVM: " + System.getProperty("java.vm.version", "-") + " (" + System.getProperty("java.vm.name", "-") + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getJVMArchitecture() {
|
||||||
|
return System.getProperty("sun.arch.data.model");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCorrectOSArchitecture() {
|
||||||
|
boolean result = getOSArchitecture().endsWith(getJVMArchitecture());
|
||||||
|
if (!result) {
|
||||||
|
log.warn("System.getProperty(\"os.arch\") " + System.getProperty("os.arch"));
|
||||||
|
log.warn("System.getenv(\"ProgramFiles(x86)\") " + System.getenv("ProgramFiles(x86)"));
|
||||||
|
log.warn("System.getenv(\"PROCESSOR_ARCHITECTURE\")" + System.getenv("PROCESSOR_ARCHITECTURE"));
|
||||||
|
log.warn("System.getenv(\"PROCESSOR_ARCHITEW6432\") " + System.getenv("PROCESSOR_ARCHITEW6432"));
|
||||||
|
log.warn("System.getProperty(\"sun.arch.data.model\") " + System.getProperty("sun.arch.data.model"));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openURI(URI uri) throws IOException {
|
||||||
|
if (!isLinux()
|
||||||
|
&& Desktop.isDesktopSupported()
|
||||||
|
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
||||||
|
Desktop.getDesktop().browse(uri);
|
||||||
|
} else {
|
||||||
|
// Maybe Application.HostServices works in those cases?
|
||||||
|
// HostServices hostServices = getHostServices();
|
||||||
|
// hostServices.showDocument(uri.toString());
|
||||||
|
|
||||||
|
// On Linux Desktop is poorly implemented.
|
||||||
|
// See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform
|
||||||
|
if (!DesktopUtil.browse(uri))
|
||||||
|
throw new IOException("Failed to open URI: " + uri.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openDirectory(File directory) throws IOException {
|
||||||
|
if (!isLinux()
|
||||||
|
&& Desktop.isDesktopSupported()
|
||||||
|
&& Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
|
||||||
|
Desktop.getDesktop().open(directory);
|
||||||
|
} else {
|
||||||
|
// Maybe Application.HostServices works in those cases?
|
||||||
|
// HostServices hostServices = getHostServices();
|
||||||
|
// hostServices.showDocument(uri.toString());
|
||||||
|
|
||||||
|
// On Linux Desktop is poorly implemented.
|
||||||
|
// See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform
|
||||||
|
if (!DesktopUtil.open(directory))
|
||||||
|
throw new IOException("Failed to open directory: " + directory.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File downloadFile(String fileURL, String saveDir, ProgressIndicator indicator) throws IOException {
|
||||||
|
DownloadUtil task;
|
||||||
|
if (saveDir != null)
|
||||||
|
task = new DownloadUtil(fileURL, saveDir);
|
||||||
|
else
|
||||||
|
task = new DownloadUtil(fileURL); // Tries to use system temp directory
|
||||||
|
if (indicator != null) {
|
||||||
|
indicator.progressProperty().unbind();
|
||||||
|
indicator.progressProperty().bind(task.progressProperty());
|
||||||
|
}
|
||||||
|
Thread th = new Thread(task);
|
||||||
|
th.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printSystemLoad() {
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
long free = runtime.freeMemory() / 1024 / 1024;
|
||||||
|
long total = runtime.totalMemory() / 1024 / 1024;
|
||||||
|
long used = total - free;
|
||||||
|
log.info("System load (no. threads/used memory (MB)): " + Thread.activeCount() + "/" + used);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyToClipboard(String content) {
|
||||||
|
try {
|
||||||
|
if (content != null && content.length() > 0) {
|
||||||
|
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||||
|
ClipboardContent clipboardContent = new ClipboardContent();
|
||||||
|
clipboardContent.putString(content);
|
||||||
|
clipboard.setContent(clipboardContent);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.error("copyToClipboard failed " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] concatByteArrays(byte[]... arrays) {
|
||||||
|
int totalLength = 0;
|
||||||
|
for (byte[] array : arrays) {
|
||||||
|
totalLength += array.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] result = new byte[totalLength];
|
||||||
|
int currentIndex = 0;
|
||||||
|
for (byte[] array : arrays) {
|
||||||
|
System.arraycopy(array, 0, result, currentIndex, array.length);
|
||||||
|
currentIndex += array.length;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T jsonToObject(String jsonString, Class<T> classOfT) {
|
||||||
|
Gson gson =
|
||||||
|
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
|
||||||
|
return gson.fromJson(jsonString, classOfT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* public static Object deserializeHexStringToObject(String serializedHexString) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream byteInputStream =
|
||||||
|
new ByteArrayInputStream(org.bitcoinj.core.Utils.parseAsHexOrBase58(serializedHexString));
|
||||||
|
|
||||||
|
try (ObjectInputStream objectInputStream = new LookAheadObjectInputStream(byteInputStream)) {
|
||||||
|
result = objectInputStream.readObject();
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
byteInputStream.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException i) {
|
||||||
|
i.printStackTrace();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String serializeObjectToHexString(Serializable serializable) {
|
||||||
|
String result = null;
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
|
||||||
|
objectOutputStream.writeObject(serializable);
|
||||||
|
|
||||||
|
result = org.bitcoinj.core.Utils.HEX.encode(byteArrayOutputStream.toByteArray());
|
||||||
|
byteArrayOutputStream.close();
|
||||||
|
objectOutputStream.close();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public static <T extends Serializable> T deserialize(byte[] data) {
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
||||||
|
ObjectInput in = null;
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
in = new LookAheadObjectInputStream(bis, true);
|
||||||
|
result = in.readObject();
|
||||||
|
if (!(result instanceof Serializable))
|
||||||
|
throw new RuntimeException("Object not of type Serializable");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
bis.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// ignore close exception
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (in != null) {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// ignore close exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (T) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] serialize(Serializable object) {
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutput out = null;
|
||||||
|
byte[] result = null;
|
||||||
|
try {
|
||||||
|
out = new ObjectOutputStream(bos);
|
||||||
|
out.writeObject(object);
|
||||||
|
out.flush();
|
||||||
|
result = bos.toByteArray().clone();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (out != null) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
bos.close();
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printElapsedTime(String msg) {
|
||||||
|
if (!msg.isEmpty()) {
|
||||||
|
msg += " / ";
|
||||||
|
}
|
||||||
|
long timeStamp = System.currentTimeMillis();
|
||||||
|
log.debug(msg + "Elapsed: " + String.valueOf(timeStamp - lastTimeStamp));
|
||||||
|
lastTimeStamp = timeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printElapsedTime() {
|
||||||
|
printElapsedTime("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Object copy(Serializable orig) throws IOException, ClassNotFoundException {
|
||||||
|
try {
|
||||||
|
// Write the object out to a byte array
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream out = new ObjectOutputStream(bos);
|
||||||
|
out.writeObject(orig);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
// Make an input stream from the byte array and read
|
||||||
|
// a copy of the object back in.
|
||||||
|
ObjectInputStream in = new LookAheadObjectInputStream(new ByteArrayInputStream(bos.toByteArray()), true);
|
||||||
|
Object obj = in.readObject();
|
||||||
|
return obj;
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readTextFileFromServer(String url, String userAgent) throws IOException {
|
||||||
|
URLConnection connection = URI.create(url).toURL().openConnection();
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
connection.setUseCaches(false);
|
||||||
|
connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(10));
|
||||||
|
connection.addRequestProperty("User-Agent", userAgent);
|
||||||
|
connection.connect();
|
||||||
|
try (InputStream inputStream = connection.getInputStream()) {
|
||||||
|
return CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setThreadName(String name) {
|
||||||
|
Thread.currentThread().setName(name + "-" + new Random().nextInt(10000));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AnnotationExclusionStrategy implements ExclusionStrategy {
|
||||||
|
@Override
|
||||||
|
public boolean shouldSkipField(FieldAttributes f) {
|
||||||
|
return f.getAnnotation(JsonExclude.class) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldSkipClass(Class<?> clazz) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkCryptoPolicySetup() throws NoSuchAlgorithmException, LimitedKeyStrengthException {
|
||||||
|
if (Cipher.getMaxAllowedKeyLength("AES") > 128)
|
||||||
|
log.debug("Congratulations, you have unlimited key length support!");
|
||||||
|
else
|
||||||
|
throw new LimitedKeyStrengthException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toTruncatedString(Object message, int maxLenght) {
|
||||||
|
return StringUtils.abbreviate(message.toString(), maxLenght).replace("\n", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toTruncatedString(Object message) {
|
||||||
|
return toTruncatedString(message, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRandomPrefix(int minLength, int maxLength) {
|
||||||
|
int length = minLength + new Random().nextInt(maxLength - minLength + 1);
|
||||||
|
String result;
|
||||||
|
switch (new Random().nextInt(3)) {
|
||||||
|
case 0:
|
||||||
|
result = RandomStringUtils.randomAlphabetic(length);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
result = RandomStringUtils.randomNumeric(length);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
result = RandomStringUtils.randomAlphanumeric(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (new Random().nextInt(3)) {
|
||||||
|
case 0:
|
||||||
|
result = result.toUpperCase();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
result = result.toLowerCase();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShortId(String id) {
|
||||||
|
return getShortId(id, "-");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShortId(String id, String sep) {
|
||||||
|
String[] chunks = id.split(sep);
|
||||||
|
if (chunks.length > 0)
|
||||||
|
return chunks[0];
|
||||||
|
else
|
||||||
|
return id.substring(0, Math.min(8, id.length()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,299 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bitsquare.
|
||||||
|
*
|
||||||
|
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.bitsquare.arbitration;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import io.bitsquare.common.Timer;
|
||||||
|
import io.bitsquare.common.UserThread;
|
||||||
|
import io.bitsquare.common.crypto.KeyRing;
|
||||||
|
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||||
|
import io.bitsquare.common.handlers.ResultHandler;
|
||||||
|
import io.bitsquare.p2p.BootstrapListener;
|
||||||
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
|
import io.bitsquare.p2p.P2PService;
|
||||||
|
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
||||||
|
import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry;
|
||||||
|
import io.bitsquare.user.Preferences;
|
||||||
|
import io.bitsquare.user.User;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableMap;
|
||||||
|
import org.bitcoinj.core.ECKey;
|
||||||
|
import org.bitcoinj.core.Utils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.bitcoinj.core.Utils.HEX;
|
||||||
|
|
||||||
|
public class ArbitratorManager {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ArbitratorManager.class);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Static
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private static final long REPUBLISH_MILLIS = Arbitrator.TTL / 2;
|
||||||
|
private static final long RETRY_REPUBLISH_SEC = 5;
|
||||||
|
private static final long REPEATED_REPUBLISH_AT_STARTUP_SEC = 60;
|
||||||
|
|
||||||
|
// Keys for invited arbitrators in bootstrapping phase (before registration is open to anyone and security payment is implemented)
|
||||||
|
// For developers we add here 2 test keys so one can setup an arbitrator by adding that test pubKey
|
||||||
|
// to the publicKeys list and use the test PrivKey for arbitrator registration.
|
||||||
|
// PrivKey for dev testing: 6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a
|
||||||
|
// Matching pubKey for dev testing: 027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee
|
||||||
|
private static final List<String> publicKeys = new ArrayList<>(Arrays.asList(
|
||||||
|
"03697a499d24f497b3c46bf716318231e46c4e6a685a4e122d8e2a2b229fa1f4b8",
|
||||||
|
"0365c6af94681dbee69de1851f98d4684063bf5c2d64b1c73ed5d90434f375a054",
|
||||||
|
"031c502a60f9dbdb5ae5e438a79819e4e1f417211dd537ac12c9bc23246534c4bd",
|
||||||
|
"02c1e5a242387b6d5319ce27246cea6edaaf51c3550591b528d2578a4753c56c2c",
|
||||||
|
"025c319faf7067d9299590dd6c97fe7e56cd4dac61205ccee1cd1fc390142390a2",
|
||||||
|
"038f6e24c2bfe5d51d0a290f20a9a657c270b94ef2b9c12cd15ca3725fa798fc55",
|
||||||
|
"0255256ff7fb615278c4544a9bbd3f5298b903b8a011cd7889be19b6b1c45cbefe",
|
||||||
|
"024a3a37289f08c910fbd925ebc72b946f33feaeff451a4738ee82037b4cda2e95",
|
||||||
|
"02a88b75e9f0f8afba1467ab26799dcc38fd7a6468fb2795444b425eb43e2c10bd",
|
||||||
|
"02349a51512c1c04c67118386f4d27d768c5195a83247c150a4b722d161722ba81",
|
||||||
|
"03f718a2e0dc672c7cdec0113e72c3322efc70412bb95870750d25c32cd98de17d",
|
||||||
|
"028ff47ee2c56e66313928975c58fa4f1b19a0f81f3a96c4e9c9c3c6768075509e",
|
||||||
|
"02b517c0cbc3a49548f448ddf004ed695c5a1c52ec110be1bfd65fa0ca0761c94b",
|
||||||
|
"03df837a3a0f3d858e82f3356b71d1285327f101f7c10b404abed2abc1c94e7169",
|
||||||
|
"0203a90fb2ab698e524a5286f317a183a84327b8f8c3f7fa4a98fec9e1cefd6b72",
|
||||||
|
"023c99cc073b851c892d8c43329ca3beb5d2213ee87111af49884e3ce66cbd5ba5",
|
||||||
|
"027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee"
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Instance fields
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private final KeyRing keyRing;
|
||||||
|
private final ArbitratorService arbitratorService;
|
||||||
|
private final User user;
|
||||||
|
private Preferences preferences;
|
||||||
|
private final ObservableMap<NodeAddress, Arbitrator> arbitratorsObservableMap = FXCollections.observableHashMap();
|
||||||
|
private final List<Arbitrator> persistedAcceptedArbitrators;
|
||||||
|
private Timer republishArbitratorTimer, retryRepublishArbitratorTimer;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ArbitratorManager(KeyRing keyRing, ArbitratorService arbitratorService, User user, Preferences preferences) {
|
||||||
|
this.keyRing = keyRing;
|
||||||
|
this.arbitratorService = arbitratorService;
|
||||||
|
this.user = user;
|
||||||
|
this.preferences = preferences;
|
||||||
|
|
||||||
|
persistedAcceptedArbitrators = new ArrayList<>(user.getAcceptedArbitrators());
|
||||||
|
user.clearAcceptedArbitrators();
|
||||||
|
|
||||||
|
arbitratorService.addHashSetChangedListener(new HashMapChangedListener() {
|
||||||
|
@Override
|
||||||
|
public void onAdded(ProtectedStorageEntry data) {
|
||||||
|
if (data.getStoragePayload() instanceof Arbitrator)
|
||||||
|
updateArbitratorMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoved(ProtectedStorageEntry data) {
|
||||||
|
if (data.getStoragePayload() instanceof Arbitrator)
|
||||||
|
updateArbitratorMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutDown() {
|
||||||
|
stopRepublishArbitratorTimer();
|
||||||
|
stopRetryRepublishArbitratorTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void onAllServicesInitialized() {
|
||||||
|
if (user.getRegisteredArbitrator() != null) {
|
||||||
|
P2PService p2PService = arbitratorService.getP2PService();
|
||||||
|
if (p2PService.isBootstrapped())
|
||||||
|
isBootstrapped();
|
||||||
|
else
|
||||||
|
p2PService.addP2PServiceListener(new BootstrapListener() {
|
||||||
|
@Override
|
||||||
|
public void onBootstrapComplete() {
|
||||||
|
isBootstrapped();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateArbitratorMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void isBootstrapped() {
|
||||||
|
if (republishArbitratorTimer == null) {
|
||||||
|
republishArbitratorTimer = UserThread.runPeriodically(this::republishArbitrator, REPUBLISH_MILLIS, TimeUnit.MILLISECONDS);
|
||||||
|
UserThread.runAfter(this::republishArbitrator, REPEATED_REPUBLISH_AT_STARTUP_SEC);
|
||||||
|
republishArbitrator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateArbitratorMap() {
|
||||||
|
Map<NodeAddress, Arbitrator> map = arbitratorService.getArbitrators();
|
||||||
|
arbitratorsObservableMap.clear();
|
||||||
|
Map<NodeAddress, Arbitrator> filtered = map.values().stream()
|
||||||
|
.filter(e -> isPublicKeyInList(Utils.HEX.encode(e.getRegistrationPubKey()))
|
||||||
|
&& verifySignature(e.getPubKeyRing().getSignaturePubKey(), e.getRegistrationPubKey(), e.getRegistrationSignature()))
|
||||||
|
.collect(Collectors.toMap(Arbitrator::getArbitratorNodeAddress, Function.identity()));
|
||||||
|
|
||||||
|
arbitratorsObservableMap.putAll(filtered);
|
||||||
|
arbitratorsObservableMap.values().stream()
|
||||||
|
.filter(arbitrator -> persistedAcceptedArbitrators.contains(arbitrator))
|
||||||
|
.forEach(user::addAcceptedArbitrator);
|
||||||
|
|
||||||
|
if (preferences.getAutoSelectArbitrators()) {
|
||||||
|
arbitratorsObservableMap.values().stream()
|
||||||
|
.filter(user::hasMatchingLanguage)
|
||||||
|
.forEach(user::addAcceptedArbitrator);
|
||||||
|
} else {
|
||||||
|
// if we don't have any arbitrator we set all matching
|
||||||
|
// we use a delay as we might get our matching arbitrator a bit delayed (first we get one we did not selected
|
||||||
|
// then we get our selected one - we don't want to activate the first in that case)
|
||||||
|
UserThread.runAfter(() -> {
|
||||||
|
if (user.getAcceptedArbitrators().isEmpty()) {
|
||||||
|
arbitratorsObservableMap.values().stream()
|
||||||
|
.filter(user::hasMatchingLanguage)
|
||||||
|
.forEach(user::addAcceptedArbitrator);
|
||||||
|
}
|
||||||
|
}, 100, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
user.setRegisteredArbitrator(arbitrator);
|
||||||
|
arbitratorsObservableMap.put(arbitrator.getArbitratorNodeAddress(), arbitrator);
|
||||||
|
arbitratorService.addArbitrator(arbitrator,
|
||||||
|
() -> {
|
||||||
|
log.debug("Arbitrator successfully saved in P2P network");
|
||||||
|
resultHandler.handleResult();
|
||||||
|
|
||||||
|
if (arbitratorsObservableMap.size() > 0)
|
||||||
|
UserThread.runAfter(this::updateArbitratorMap, 100, TimeUnit.MILLISECONDS);
|
||||||
|
},
|
||||||
|
errorMessageHandler::handleErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeArbitrator(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
|
||||||
|
if (registeredArbitrator != null) {
|
||||||
|
user.setRegisteredArbitrator(null);
|
||||||
|
arbitratorsObservableMap.remove(registeredArbitrator.getArbitratorNodeAddress());
|
||||||
|
arbitratorService.removeArbitrator(registeredArbitrator,
|
||||||
|
() -> {
|
||||||
|
log.debug("Arbitrator successfully removed from P2P network");
|
||||||
|
resultHandler.handleResult();
|
||||||
|
},
|
||||||
|
errorMessageHandler::handleErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableMap<NodeAddress, Arbitrator> getArbitratorsObservableMap() {
|
||||||
|
return arbitratorsObservableMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A private key is handed over to selected arbitrators for registration.
|
||||||
|
// An invited arbitrator will sign at registration his storageSignaturePubKey with that private key and attach the signature and pubKey to his data.
|
||||||
|
// Other users will check the signature with the list of public keys hardcoded in the app.
|
||||||
|
public String signStorageSignaturePubKey(ECKey key) {
|
||||||
|
String keyToSignAsHex = Utils.HEX.encode(keyRing.getPubKeyRing().getSignaturePubKey().getEncoded());
|
||||||
|
return key.signMessage(keyToSignAsHex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ECKey getRegistrationKey(String privKeyBigIntString) {
|
||||||
|
try {
|
||||||
|
return ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyBigIntString)));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPublicKeyInList(String pubKeyAsHex) {
|
||||||
|
return publicKeys.contains(pubKeyAsHex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Private
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void republishArbitrator() {
|
||||||
|
Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
|
||||||
|
if (registeredArbitrator != null) {
|
||||||
|
addArbitrator(registeredArbitrator,
|
||||||
|
this::updateArbitratorMap,
|
||||||
|
errorMessage -> {
|
||||||
|
if (retryRepublishArbitratorTimer == null)
|
||||||
|
retryRepublishArbitratorTimer = UserThread.runPeriodically(() -> {
|
||||||
|
stopRetryRepublishArbitratorTimer();
|
||||||
|
republishArbitrator();
|
||||||
|
}, RETRY_REPUBLISH_SEC);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifySignature(PublicKey storageSignaturePubKey, byte[] registrationPubKey, String signature) {
|
||||||
|
String keyToSignAsHex = Utils.HEX.encode(storageSignaturePubKey.getEncoded());
|
||||||
|
try {
|
||||||
|
ECKey key = ECKey.fromPublicOnly(registrationPubKey);
|
||||||
|
key.verifyMessage(keyToSignAsHex, signature);
|
||||||
|
return true;
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
log.warn("verifySignature failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void stopRetryRepublishArbitratorTimer() {
|
||||||
|
if (retryRepublishArbitratorTimer != null) {
|
||||||
|
retryRepublishArbitratorTimer.stop();
|
||||||
|
retryRepublishArbitratorTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopRepublishArbitratorTimer() {
|
||||||
|
if (republishArbitratorTimer != null) {
|
||||||
|
republishArbitratorTimer.stop();
|
||||||
|
republishArbitratorTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1044
gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java
Normal file
1044
gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bitsquare.
|
||||||
|
*
|
||||||
|
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.bitsquare.gui.main.overlays.windows;
|
||||||
|
|
||||||
|
import io.bitsquare.alert.Alert;
|
||||||
|
import io.bitsquare.common.util.Utilities;
|
||||||
|
import io.bitsquare.gui.components.HyperlinkWithIcon;
|
||||||
|
import io.bitsquare.gui.main.overlays.Overlay;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ProgressIndicator;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static io.bitsquare.gui.util.FormBuilder.addButton;
|
||||||
|
import static io.bitsquare.gui.util.FormBuilder.addLabelHyperlinkWithIcon;
|
||||||
|
import static io.bitsquare.gui.util.FormBuilder.addMultilineLabel;
|
||||||
|
|
||||||
|
public class DisplayUpdateDownloadWindow extends Overlay<DisplayUpdateDownloadWindow> {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(DisplayUpdateDownloadWindow.class);
|
||||||
|
private Alert alert;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Public API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public DisplayUpdateDownloadWindow() {
|
||||||
|
type = Type.Attention;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
width = 700;
|
||||||
|
// need to set headLine, otherwise the fields will not be created in addHeadLine
|
||||||
|
headLine = "Update available!";
|
||||||
|
createGridPane();
|
||||||
|
addHeadLine();
|
||||||
|
addSeparator();
|
||||||
|
addContent();
|
||||||
|
applyStyles();
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayUpdateDownloadWindow alertMessage(Alert alert) {
|
||||||
|
this.alert = alert;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Protected
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void addContent() {
|
||||||
|
checkNotNull(alert, "alertMessage must not be null");
|
||||||
|
Label messageLabel = addMultilineLabel(gridPane, ++rowIndex, alert.message, 10);
|
||||||
|
headLine = "Important update information!";
|
||||||
|
headLineLabel.setStyle("-fx-text-fill: -fx-accent; -fx-font-weight: bold; -fx-font-size: 22;");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch (Utilities.get)
|
||||||
|
ProgressIndicator indicator = new ProgressIndicator(0L);
|
||||||
|
indicator.setVisible(false);
|
||||||
|
GridPane.setRowIndex(indicator, ++rowIndex);
|
||||||
|
GridPane.setColumnIndex(indicator, 1);
|
||||||
|
gridPane.getChildren().add(indicator);
|
||||||
|
|
||||||
|
Button downloadButton = addButton(gridPane, ++rowIndex, "Download now");
|
||||||
|
|
||||||
|
// TODO How do we get the right URL for the download?
|
||||||
|
String url = "https://bitsquare.io/downloads" + File.separator + alert.version + File.separator;
|
||||||
|
String fileName;
|
||||||
|
if (Utilities.isOSX())
|
||||||
|
fileName = "Bitsquare-" + alert.version + ".dmg";
|
||||||
|
else if (Utilities.isWindows())
|
||||||
|
fileName = "Bitsquare-" + Utilities.getOSArchitecture() + "bit-" + alert.version + ".exe";
|
||||||
|
else if (Utilities.isLinux())
|
||||||
|
fileName = "Bitsquare-" + Utilities.getOSArchitecture() + "bit-" + alert.version + ".deb";
|
||||||
|
else {
|
||||||
|
downloadButton.setDisable(true);
|
||||||
|
messageLabel.setText("Unable to determine the correct installer. Pleaase manually download and verify " +
|
||||||
|
"the correct file from https://bitsquare.io/downloads");
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadButton.setOnAction(e -> {
|
||||||
|
indicator.setVisible(true);
|
||||||
|
try {
|
||||||
|
Utilities.downloadFile(url, null, indicator);
|
||||||
|
} catch (IOException e) {
|
||||||
|
messageLabel.setText("Unable to download files.\n" +
|
||||||
|
"Please manually download and verify the file from https://bitsquare.io/downloads");
|
||||||
|
downloadButton.setDisable(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
closeButton = new Button("Close");
|
||||||
|
closeButton.setOnAction(e -> {
|
||||||
|
hide();
|
||||||
|
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
|
||||||
|
});
|
||||||
|
|
||||||
|
GridPane.setRowIndex(closeButton, ++rowIndex);
|
||||||
|
GridPane.setColumnIndex(closeButton, 1);
|
||||||
|
gridPane.getChildren().add(closeButton);
|
||||||
|
GridPane.setMargin(closeButton, new Insets(10, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue