From 8e6e2256bc3c7e89fc0eb7b790a26071d0135e3a Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Thu, 10 Jul 2014 21:44:38 +0200 Subject: [PATCH] WalletAppKit/Template: Cleaner way to check if the app is already running. Backport from Lighthouse. --- .../com/google/bitcoin/kits/WalletAppKit.java | 29 +++++++++++++++--- .../src/main/java/wallettemplate/Main.java | 30 ++++++++----------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/com/google/bitcoin/kits/WalletAppKit.java b/core/src/main/java/com/google/bitcoin/kits/WalletAppKit.java index 83eed650..084ea841 100644 --- a/core/src/main/java/com/google/bitcoin/kits/WalletAppKit.java +++ b/core/src/main/java/com/google/bitcoin/kits/WalletAppKit.java @@ -30,12 +30,10 @@ import com.google.common.util.concurrent.Service; import com.subgraph.orchid.TorClient; import org.bitcoinj.wallet.Protos; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.net.InetAddress; import java.net.UnknownHostException; +import java.nio.channels.FileLock; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -188,6 +186,29 @@ public class WalletAppKit extends AbstractIdleService { */ protected void onSetupCompleted() { } + /** + * Tests to see if the spvchain file has an operating system file lock on it. Useful for checking if your app + * is already running. If another copy of your app is running and you start the appkit anyway, an exception will + * be thrown during the startup process. Returns false if the chain file does not exist. + */ + public boolean isChainFileLocked() throws IOException { + RandomAccessFile file2 = null; + try { + File file = new File(directory, filePrefix + ".spvchain"); + if (!file.exists()) + return false; + file2 = new RandomAccessFile(file, "rw"); + FileLock lock = file2.getChannel().tryLock(); + if (lock == null) + return true; + lock.close(); + return false; + } finally { + if (file2 != null) + file2.close(); + } + } + @Override protected void startUp() throws Exception { // Runs in a separate thread. diff --git a/wallettemplate/src/main/java/wallettemplate/Main.java b/wallettemplate/src/main/java/wallettemplate/Main.java index fd7fc04e..c3183193 100644 --- a/wallettemplate/src/main/java/wallettemplate/Main.java +++ b/wallettemplate/src/main/java/wallettemplate/Main.java @@ -5,10 +5,8 @@ import com.google.bitcoin.core.NetworkParameters; import com.google.bitcoin.kits.WalletAppKit; import com.google.bitcoin.params.MainNetParams; import com.google.bitcoin.params.RegTestParams; -import com.google.bitcoin.store.BlockStoreException; import com.google.bitcoin.utils.BriefLogFormatter; import com.google.bitcoin.utils.Threading; -import com.google.common.base.Throwables; import javafx.application.Application; import javafx.application.Platform; import javafx.fxml.FXMLLoader; @@ -41,22 +39,12 @@ public class Main extends Application { instance = this; // Show the crash dialog for any exceptions that we don't handle and that hit the main loop. GuiUtils.handleCrashesOnThisThread(); - try { - init(mainWindow); - } catch (Throwable t) { - // Nicer message for the case where the block store file is locked. - if (Throwables.getRootCause(t) instanceof BlockStoreException) { - GuiUtils.informationalAlert("Already running", "This application is already running and cannot be started twice."); - } else { - throw t; - } - } - } - private void init(Stage mainWindow) throws IOException { + // Match Aqua UI style. if (System.getProperty("os.name").toLowerCase().contains("mac")) { AquaFx.style(); } + // Load the GUI. The Controller class will be automagically created and wired up. URL location = getClass().getResource("main.fxml"); FXMLLoader loader = new FXMLLoader(location); @@ -78,6 +66,16 @@ public class Main extends Application { Threading.USER_THREAD = Platform::runLater; // Create the app kit. It won't do any heavyweight initialization until after we start it. bitcoin = new WalletAppKit(params, new File("."), APP_NAME); + if (bitcoin.isChainFileLocked()) { + informationalAlert("Already running", "This application is already running and cannot be started twice."); + Platform.exit(); + return; + } + + mainWindow.show(); + + // Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen + // or progress widget to keep the user engaged whilst we initialise, but we don't. if (params == RegTestParams.get()) { bitcoin.connectToLocalHost(); // You should run a regtest mode bitcoind locally. } else if (params == MainNetParams.get()) { @@ -89,9 +87,6 @@ public class Main extends Application { // As an example! // bitcoin.useTor(); } - - // Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen - // or progress widget to keep the user engaged whilst we initialise, but we don't. bitcoin.setDownloadListener(controller.progressBarUpdater()) .setBlockingStartup(false) .setUserAgent(APP_NAME, "1.0"); @@ -102,7 +97,6 @@ public class Main extends Application { bitcoin.peerGroup().setMaxConnections(11); System.out.println(bitcoin.wallet()); controller.onBitcoinSetup(); - mainWindow.show(); } public class OverlayUI {