Merge branch 'wip-cbeams'

These changes eliminate the use of "appName" throughout the codebase in
favor of explicitly named, and therefore individually configurable
alternatives specific to each component. For example, instead of passing
the application name through the WalletFacade boundary and then using a
utility like AppDirectory to put construct the wallet directory on the
fly, the path to wallet directory is now passed explicitly as a
@Named(WALLET_DIR) parameter to the WalletFacade constructor.

The result is not only better configurability (e.g. the WALLET_DIR
property can be overridden without affecting any other parts of the
system), but also better understandability. See
BitsquareEnvironment#defaultProperties to see how it all comes together.
Note how the value of appName is mapped to each of these new properties,
all in one place where it's easy to get an overview etc.

Also, as of this commit, there is only one place in the codebase where
the word "Bitsquare" is hard-coded (in `i.b.app.BitsquareEnvironment`):

    $ git grep -h '"Bitsquare"' src
        public static final String DEFAULT_APP_NAME = "Bitsquare";

To keep things clean, further hard-coding should be avoided from this
point forward.

See extended comments for each commit for details.

* wip-cbeams:
  Eliminate remaining uses of @Named("appName")
  Introduce explicit title param in ViewCB
  Introduce io.bitsquare.btc.UserAgent
  Introduce explicit dir and prefix params in Persistence
  Introduce explicit wallet and useragent params in WalletFacade
  Rename environment "app properties"=>"default properties"
This commit is contained in:
Chris Beams 2014-11-11 14:59:59 +01:00
commit d3f1ec252e
No known key found for this signature in database
GPG Key ID: 3D214F8F5BC5ED73
16 changed files with 226 additions and 208 deletions

View File

@ -18,15 +18,20 @@
package io.bitsquare.app;
import io.bitsquare.BitsquareException;
import io.bitsquare.btc.UserAgent;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.ViewCB;
import io.bitsquare.persistence.Persistence;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Properties;
import joptsimple.OptionSet;
import lighthouse.files.AppDirectory;
import org.springframework.core.env.JOptCommandLinePropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
@ -39,10 +44,16 @@ import org.springframework.core.io.support.ResourcePropertySource;
public class BitsquareEnvironment extends StandardEnvironment {
public static final String APP_NAME_KEY = "appName";
public static final String APP_NAME_KEY = "app.name";
public static final String DEFAULT_APP_NAME = "Bitsquare";
private static final String BITSQUARE_APP_PROPERTY_SOURCE_NAME = "bitsquareAppProperties";
public static final String USER_DATA_DIR_KEY = "user.data.dir";
public static final String DEFAULT_USER_DATA_DIR = defaultUserDataDir();
public static final String APP_DATA_DIR_KEY = "app.data.dir";
public static final String DEFAULT_APP_DATA_DIR = appDataDir(DEFAULT_USER_DATA_DIR, DEFAULT_APP_NAME);
private static final String BITSQUARE_DEFAULT_PROPERTY_SOURCE_NAME = "bitsquareDefaultProperties";
private static final String BITSQUARE_CLASSPATH_PROPERTY_SOURCE_NAME = "bitsquareClasspathProperties";
private static final String BITSQUARE_FILESYSTEM_PROPERTY_SOURCE_NAME = "bitsquareFilesystemProperties";
private static final String BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME = "bitsquareCommandLineProperties";
@ -56,19 +67,40 @@ public class BitsquareEnvironment extends StandardEnvironment {
new JOptCommandLinePropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME, options);
String appName = commandLineProperties.containsProperty(APP_NAME_KEY) ?
DEFAULT_APP_NAME + "-" + commandLineProperties.getProperty(APP_NAME_KEY) :
(String) commandLineProperties.getProperty(APP_NAME_KEY) :
DEFAULT_APP_NAME;
String userDataDir = commandLineProperties.containsProperty(USER_DATA_DIR_KEY) ?
(String) commandLineProperties.getProperty(USER_DATA_DIR_KEY) :
DEFAULT_USER_DATA_DIR;
String appDataDir = commandLineProperties.containsProperty(APP_DATA_DIR_KEY) ?
(String) commandLineProperties.getProperty(APP_DATA_DIR_KEY) :
appDataDir(userDataDir, appName);
MutablePropertySources propertySources = this.getPropertySources();
propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, appProperties(appName));
propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, defaultProperties(appDataDir, appName));
propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, classpathProperties());
propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, filesystemProperties(appName));
propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, filesystemProperties(appDataDir));
propertySources.addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, commandLineProperties);
}
private PropertySource<?> appProperties(String appName) {
return new PropertiesPropertySource(BITSQUARE_APP_PROPERTY_SOURCE_NAME, new Properties() {{
private PropertySource<?> defaultProperties(String appDataDir, String appName) {
return new PropertiesPropertySource(BITSQUARE_DEFAULT_PROPERTY_SOURCE_NAME, new Properties() {{
setProperty(APP_DATA_DIR_KEY, appDataDir);
setProperty(APP_NAME_KEY, appName);
setProperty(UserAgent.NAME_KEY, appName);
setProperty(UserAgent.VERSION_KEY, "0.1");
setProperty(WalletFacade.DIR_KEY, appDataDir);
setProperty(WalletFacade.PREFIX_KEY, appName);
setProperty(Persistence.DIR_KEY, appDataDir);
setProperty(Persistence.PREFIX_KEY, appName + "_pref");
setProperty(ViewCB.TITLE_KEY, appName);
}});
}
@ -81,8 +113,8 @@ public class BitsquareEnvironment extends StandardEnvironment {
}
}
private PropertySource<?> filesystemProperties(String appName) {
String location = String.format("file:%s/bitsquare.conf", AppDirectory.dir(appName));
private PropertySource<?> filesystemProperties(String appDir) {
String location = String.format("file:%s/bitsquare.conf", appDir);
Resource resource = resourceLoader.getResource(location);
if (!resource.exists()) {
@ -95,4 +127,22 @@ public class BitsquareEnvironment extends StandardEnvironment {
throw new BitsquareException(ex);
}
}
private static String defaultUserDataDir() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win"))
return System.getenv("APPDATA");
if (os.contains("mac"))
return Paths.get(System.getProperty("user.home"), "Library", "Application Support").toString();
// *nix
return Paths.get(System.getProperty("user.home"), ".local", "share").toString();
}
private static String appDataDir(String userDataDir, String appName) {
return Paths.get(userDataDir, appName).toString();
}
}

View File

@ -17,7 +17,7 @@
package io.bitsquare.app.gui;
import io.bitsquare.app.BitsquareEnvironment;
import io.bitsquare.BitsquareException;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.SystemTray;
import io.bitsquare.gui.ViewLoader;
@ -35,21 +35,21 @@ import com.google.inject.Injector;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lighthouse.files.AppDirectory;
import org.springframework.core.env.Environment;
public class BitsquareApp extends Application {
private static final Logger log = LoggerFactory.getLogger(BitsquareApp.class);
import static io.bitsquare.app.BitsquareEnvironment.*;
public class BitsquareApp extends Application {
private static Environment env;
private BitsquareAppModule bitsquareAppModule;
@ -63,8 +63,6 @@ public class BitsquareApp extends Application {
public void start(Stage primaryStage) throws IOException {
Preconditions.checkArgument(env != null, "Environment must not be null");
String appName = env.getRequiredProperty(BitsquareEnvironment.APP_NAME_KEY);
bitsquareAppModule = new BitsquareAppModule(env, primaryStage);
injector = Guice.createInjector(bitsquareAppModule);
@ -75,13 +73,9 @@ public class BitsquareApp extends Application {
Popups.handleUncaughtExceptions(Throwables.getRootCause(throwable)));
// configure the Bitsquare application data directory
// initialize the application data directory (if necessary)
try {
AppDirectory.initAppDir(appName);
} catch (IOException e) {
log.error(e.getMessage());
}
initAppDir(env.getRequiredProperty(APP_DATA_DIR_KEY));
// load and apply any stored settings
@ -125,7 +119,7 @@ public class BitsquareApp extends Application {
// configure the primary stage
primaryStage.setTitle(appName);
primaryStage.setTitle(env.getRequiredProperty(APP_NAME_KEY));
primaryStage.setScene(scene);
primaryStage.setMinWidth(75);
primaryStage.setMinHeight(50);
@ -143,4 +137,20 @@ public class BitsquareApp extends Application {
bitsquareAppModule.close(injector);
System.exit(0);
}
private void initAppDir(String appDir) {
Path dir = Paths.get(appDir);
if (Files.exists(dir)) {
if (!Files.isWritable(dir)) {
throw new BitsquareException("Application data directory '%s' is not writeable", dir);
}
return;
}
try {
Files.createDirectory(dir);
} catch (IOException ex) {
throw new BitsquareException(ex, "Application data directory '%s' could not be created", dir);
}
}
}

View File

@ -26,7 +26,7 @@ import io.bitsquare.network.Node;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY;
import static io.bitsquare.app.BitsquareEnvironment.*;
import static io.bitsquare.btc.BitcoinModule.BITCOIN_NETWORK_KEY;
import static io.bitsquare.msg.tomp2p.TomP2PMessageModule.*;
import static io.bitsquare.network.Node.*;
@ -39,8 +39,10 @@ public class BitsquareAppMain extends BitsquareExecutable {
@Override
protected void customizeOptionParsing(OptionParser parser) {
parser.accepts(APP_NAME_KEY, "Qualified application name").withRequiredArg();
parser.accepts(NAME_KEY, "Name of this node").withRequiredArg();
parser.accepts(APP_NAME_KEY, "Application name").withRequiredArg().defaultsTo(DEFAULT_APP_NAME);
parser.accepts(USER_DATA_DIR_KEY, "User data directory").withRequiredArg().defaultsTo(DEFAULT_USER_DATA_DIR);
parser.accepts(APP_DATA_DIR_KEY, "Application data directory").withRequiredArg().defaultsTo(DEFAULT_APP_DATA_DIR);
parser.accepts(NAME_KEY, "Network name").withRequiredArg();
parser.accepts(PORT_KEY, "Port to listen on").withRequiredArg().defaultsTo(String.valueOf(Node.DEFAULT_PORT));
parser.accepts(BITCOIN_NETWORK_KEY).withRequiredArg().defaultsTo(BitcoinModule.DEFAULT_BITCOIN_NETWORK);
parser.accepts(BOOTSTRAP_NODE_NAME_KEY).withRequiredArg().defaultsTo(BootstrapNodes.DEFAULT.getName());

View File

@ -18,7 +18,6 @@
package io.bitsquare.app.gui;
import io.bitsquare.BitsquareModule;
import io.bitsquare.app.BitsquareEnvironment;
import io.bitsquare.btc.BitcoinModule;
import io.bitsquare.crypto.CryptoModule;
import io.bitsquare.gui.GuiModule;
@ -32,12 +31,15 @@ import io.bitsquare.trade.TradeModule;
import io.bitsquare.user.User;
import com.google.inject.Injector;
import com.google.inject.name.Names;
import java.io.File;
import javafx.stage.Stage;
import org.springframework.core.env.Environment;
import static com.google.inject.name.Names.named;
class BitsquareAppModule extends BitsquareModule {
private final Stage primaryStage;
@ -50,19 +52,19 @@ class BitsquareAppModule extends BitsquareModule {
@Override
protected void configure() {
bind(User.class).asEagerSingleton();
bind(Persistence.class).asEagerSingleton();
bind(Settings.class).asEagerSingleton();
File persistenceDir = new File(env.getRequiredProperty(Persistence.DIR_KEY));
bind(File.class).annotatedWith(named(Persistence.DIR_KEY)).toInstance(persistenceDir);
bindConstant().annotatedWith(named(Persistence.PREFIX_KEY)).to(env.getRequiredProperty(Persistence.PREFIX_KEY));
bind(Persistence.class).asEagerSingleton();
install(messageModule());
install(bitcoinModule());
install(cryptoModule());
install(tradeModule());
install(offerModule());
install(guiModule());
String appName = env.getRequiredProperty(BitsquareEnvironment.APP_NAME_KEY);
bindConstant().annotatedWith(Names.named("appName")).to(appName);
}
protected MessageModule messageModule() {

View File

@ -26,8 +26,12 @@ import org.bitcoinj.params.TestNet3Params;
import com.google.inject.Injector;
import java.io.File;
import org.springframework.core.env.Environment;
import static com.google.inject.name.Names.named;
public class BitcoinModule extends BitsquareModule {
public static final String BITCOIN_NETWORK_KEY = "bitcoin.network";
@ -39,10 +43,19 @@ public class BitcoinModule extends BitsquareModule {
@Override
protected void configure() {
bind(WalletFacade.class).asEagerSingleton();
bind(FeePolicy.class).asEagerSingleton();
bind(BlockChainFacade.class).asEagerSingleton();
bind(NetworkParameters.class).toInstance(network());
bind(FeePolicy.class).asEagerSingleton();
bindConstant().annotatedWith(named(UserAgent.NAME_KEY)).to(env.getRequiredProperty(UserAgent.NAME_KEY));
bindConstant().annotatedWith(named(UserAgent.VERSION_KEY)).to(env.getRequiredProperty(UserAgent.VERSION_KEY));
bind(UserAgent.class).asEagerSingleton();
File walletDir = new File(env.getRequiredProperty(WalletFacade.DIR_KEY));
bind(File.class).annotatedWith(named(WalletFacade.DIR_KEY)).toInstance(walletDir);
bindConstant().annotatedWith(named(WalletFacade.PREFIX_KEY)).to(env.getRequiredProperty(WalletFacade.PREFIX_KEY));
bind(WalletFacade.class).asEagerSingleton();
bind(BlockChainFacade.class).asEagerSingleton();
}
@Override

View File

@ -0,0 +1,44 @@
/*
* 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.btc;
import javax.inject.Inject;
import javax.inject.Named;
public class UserAgent {
public static final String NAME_KEY = "useragent.name";
public static final String VERSION_KEY = "useragent.version";
private final String name;
private final String version;
@Inject
public UserAgent(@Named(NAME_KEY) String name, @Named(VERSION_KEY) String version) {
this.name = name;
this.version = version;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
}

View File

@ -57,6 +57,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Service;
import java.io.File;
import java.io.Serializable;
import java.math.BigInteger;
@ -84,8 +85,6 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lighthouse.files.AppDirectory;
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
/**
@ -94,24 +93,30 @@ import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
*/
public class WalletFacade {
private static final Logger log = LoggerFactory.getLogger(WalletFacade.class);
private static final String LOCK_NAME = "lock";
public static final String DIR_KEY = "wallet.dir";
public static final String PREFIX_KEY = "wallet.prefix";
private final ReentrantLock lock = Threading.lock("lock");
private final NetworkParameters params;
private WalletAppKit walletAppKit;
private final FeePolicy feePolicy;
private final CryptoFacade cryptoFacade;
private final Persistence persistence;
private final String appName;
private final List<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArrayList<>();
private final List<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArrayList<>();
private final List<BalanceListener> balanceListeners = new CopyOnWriteArrayList<>();
private final ReentrantLock lock = Threading.lock(LOCK_NAME);
private final NetworkParameters params;
private final FeePolicy feePolicy;
private final CryptoFacade cryptoFacade;
private final Persistence persistence;
private final File walletDir;
private final String walletPrefix;
private final UserAgent userAgent;
private WalletAppKit walletAppKit;
private Wallet wallet;
private WalletEventListener walletEventListener;
private AddressEntry registrationAddressEntry;
private AddressEntry arbitratorDepositAddressEntry;
@GuardedBy("lock")
private List<AddressEntry> addressEntryList = new ArrayList<>();
private @GuardedBy(LOCK_NAME) List<AddressEntry> addressEntryList = new ArrayList<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -119,12 +124,15 @@ public class WalletFacade {
@Inject
public WalletFacade(NetworkParameters params, FeePolicy feePolicy, CryptoFacade cryptoFacade,
Persistence persistence, @Named("appName") String appName) {
Persistence persistence, UserAgent userAgent,
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix) {
this.params = params;
this.feePolicy = feePolicy;
this.cryptoFacade = cryptoFacade;
this.persistence = persistence;
this.appName = appName;
this.walletDir = walletDir;
this.walletPrefix = walletPrefix;
this.userAgent = userAgent;
}
@ -141,7 +149,7 @@ public class WalletFacade {
Threading.USER_THREAD = executor;
// If seed is non-null it means we are restoring from backup.
walletAppKit = new WalletAppKit(params, AppDirectory.dir(appName).toFile(), appName) {
walletAppKit = new WalletAppKit(params, walletDir, walletPrefix) {
@Override
protected void onSetupCompleted() {
// Don't make the user wait for confirmations for now, as the intention is they're sending it
@ -195,7 +203,7 @@ public class WalletFacade {
walletAppKit.setDownloadListener(downloadListener)
.setBlockingStartup(false)
.setUserAgent(appName, "0.1");
.setUserAgent(userAgent.getName(), userAgent.getVersion());
/*
// TODO restore from DeterministicSeed

View File

@ -28,6 +28,8 @@ import io.bitsquare.gui.util.validation.FiatValidator;
import io.bitsquare.gui.util.validation.InputValidator;
import io.bitsquare.gui.util.validation.PasswordValidator;
import com.google.inject.name.Names;
import javafx.stage.Stage;
import org.springframework.core.env.Environment;
@ -57,5 +59,7 @@ public class GuiModule extends BitsquareModule {
bind(Stage.class).toInstance(primaryStage);
Popups.primaryStage = primaryStage;
Help.primaryStage = primaryStage;
bindConstant().annotatedWith(Names.named(ViewCB.TITLE_KEY)).to(env.getRequiredProperty(ViewCB.TITLE_KEY));
}
}

View File

@ -31,7 +31,6 @@ import org.slf4j.LoggerFactory;
public class Navigation {
private static final Logger log = LoggerFactory.getLogger(Navigation.class);
// New listeners can be added during iteration so we use CopyOnWriteArrayList to prevent invalid array
// modification
private final List<Listener> listeners = new CopyOnWriteArrayList<>();

View File

@ -36,6 +36,8 @@ import org.slf4j.LoggerFactory;
public class ViewCB<T extends PresentationModel> implements Initializable {
private static final Logger log = LoggerFactory.getLogger(ViewCB.class);
public static final String TITLE_KEY = "view.title";
protected T presentationModel;
//TODO Initializable has to be changed to CodeBehind<? extends PresentationModel> when all UIs are updated
protected Initializable childController;

View File

@ -31,11 +31,11 @@ public class SystemNotification {
private static final Logger log = LoggerFactory.getLogger(SystemNotification.class);
private static final Notification.Notifier notifier = NotifierBuilder.create().build();
public static void openInfoNotification(String headline, String message) {
public static void openInfoNotification(String title, String message) {
// On windows it causes problems with the hidden stage used in the hansolo Notification implementation
// Lets deactivate it for the moment and fix that with a more native-like or real native solution later.
String os = System.getProperty("os.name").toLowerCase();
if (!os.contains("win"))
notifier.notify(NotificationBuilder.create().title(headline).message(message).build());
notifier.notify(NotificationBuilder.create().title(title).message(message).build());
}
}

View File

@ -59,7 +59,7 @@ public class MainViewCB extends ViewCB<MainPM> {
private final OverlayManager overlayManager;
private final ToggleGroup navButtonsGroup = new ToggleGroup();
private final Settings settings;
private final String appName;
private final String title;
private BorderPane baseApplicationContainer;
private VBox splashScreen;
@ -77,13 +77,13 @@ public class MainViewCB extends ViewCB<MainPM> {
@Inject
private MainViewCB(MainPM presentationModel, Navigation navigation, OverlayManager overlayManager,
TradeManager tradeManager, Settings settings, @Named("appName") String appName) {
TradeManager tradeManager, Settings settings, @Named(TITLE_KEY) String title) {
super(presentationModel);
this.navigation = navigation;
this.overlayManager = overlayManager;
this.settings = settings;
this.appName = appName;
this.title = title;
tradeManager.featureNotImplementedWarningProperty().addListener((ov, oldValue, newValue) -> {
if (oldValue == null && newValue != null) {
@ -207,8 +207,8 @@ public class MainViewCB extends ViewCB<MainPM> {
numPendingTradesLabel.setText(String.valueOf(numPendingTrades));
}
log.trace("openInfoNotification " + appName);
SystemNotification.openInfoNotification(appName, "You got a new trade message.");
log.trace("openInfoNotification " + title);
SystemNotification.openInfoNotification(title, "You got a new trade message.");
}
else {
if (portfolioButtonButtonPane.getChildren().size() > 1)

View File

@ -17,8 +17,7 @@
package io.bitsquare.persistence;
import io.bitsquare.util.FileUtil;
import org.bitcoinj.core.Utils;
import org.bitcoinj.utils.Threading;
import java.io.File;
@ -52,22 +51,27 @@ public class Persistence {
private static final Logger log = LoggerFactory.getLogger(Persistence.class);
private static final ReentrantLock lock = Threading.lock("Storage");
public static final String DIR_KEY = "persistence.dir";
public static final String PREFIX_KEY = "persistence.prefix";
@GuardedBy("lock")
private Map<String, Serializable> rootMap = new HashMap<>();
private final File dir;
private final String prefix;
private final File storageFile;
private String appName;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public Persistence(@Named("appName") String appName) {
this.appName = appName;
this.prefix = appName + "_pref";
this.storageFile = FileUtil.getFile(appName, prefix, "ser");
public Persistence(
@Named(DIR_KEY) File dir,
@Named(PREFIX_KEY) String prefix) {
this.dir = dir;
this.prefix = prefix;
this.storageFile = new File(dir, prefix + ".ser");
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -220,7 +224,7 @@ public class Persistence {
FileOutputStream fileOutputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
tempFile = FileUtil.getTempFile(appName, prefix);
tempFile = File.createTempFile("temp_" + prefix, null, dir);
// Don't use auto closeable resources in try() as we would need too many try/catch clauses (for tempFile)
// and we need to close it
@ -240,7 +244,7 @@ public class Persistence {
fileOutputStream.close();
objectOutputStream.close();
FileUtil.writeTempFileToFile(tempFile, storageFile);
writeTempFileToFile(tempFile, storageFile);
} catch (IOException e) {
e.printStackTrace();
log.error("save object to file failed." + e);
@ -269,4 +273,20 @@ public class Persistence {
lock.unlock();
}
}
public void writeTempFileToFile(File tempFile, File file) throws IOException {
if (Utils.isWindows()) {
// Work around an issue on Windows whereby you can't rename over existing files.
final File canonical = file.getCanonicalFile();
if (canonical.exists() && !canonical.delete()) {
throw new IOException("Failed to delete canonical file for replacement with save");
}
if (!tempFile.renameTo(canonical)) {
throw new IOException("Failed to rename " + tempFile + " to " + canonical);
}
}
else if (!tempFile.renameTo(file)) {
throw new IOException("Failed to rename " + tempFile + " to " + file);
}
}
}

View File

@ -1,57 +0,0 @@
/*
* 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.util;
import org.bitcoinj.core.Utils;
import java.io.File;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lighthouse.files.AppDirectory;
public class FileUtil {
private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
public static File getFile(String appName, String name, String suffix) {
return new File(AppDirectory.dir(appName).toFile(), name + "." + suffix);
}
public static File getTempFile(String appName, String prefix) throws IOException {
return File.createTempFile("temp_" + prefix, null, AppDirectory.dir(appName).toFile());
}
public static void writeTempFileToFile(File tempFile, File file) throws IOException {
if (Utils.isWindows()) {
// Work around an issue on Windows whereby you can't rename over existing files.
final File canonical = file.getCanonicalFile();
if (canonical.exists() && !canonical.delete()) {
throw new IOException("Failed to delete canonical file for replacement with save");
}
if (!tempFile.renameTo(canonical)) {
throw new IOException("Failed to rename " + tempFile + " to " + canonical);
}
}
else if (!tempFile.renameTo(file)) {
throw new IOException("Failed to rename " + tempFile + " to " + file);
}
}
}

View File

@ -1,73 +0,0 @@
/*
* 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 lighthouse.files;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static com.google.common.base.Preconditions.checkNotNull;
// TODO update to open source file when its released, check licence issues
/**
* Manages the directory where the app stores all its files.
*/
public class AppDirectory {
public static Path getUserDataDir() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
return Paths.get(System.getenv("APPDATA"));
}
else if (os.contains("mac")) {
return Paths.get(System.getProperty("user.home"), "Library", "Application Support");
}
else {
// Linux and other similar systems, we hope (not Android).
return Paths.get(System.getProperty("user.home"), ".local", "share");
}
}
public static Path getUserDataDir(String appName) {
return getUserDataDir().resolve(appName);
}
public static Path initAppDir(String appName) throws IOException {
Path dir = dir(appName);
if (!Files.exists(dir))
Files.createDirectory(dir);
else if (!Files.isWritable(dir))
throw new IOException("App directory is not writeable");
return dir;
}
private static Path dir;
public static Path dir(String appName) {
if (dir == null)
return getUserDataDir(appName);
else
return dir;
}
public static void overrideAppDir(Path newDir) {
dir = checkNotNull(newDir);
}
}

View File

@ -25,8 +25,6 @@ import io.bitsquare.gui.ViewLoader;
import com.google.inject.Guice;
import com.google.inject.Injector;
import java.util.Properties;
import javafx.application.Application;
import javafx.stage.Stage;
@ -36,7 +34,6 @@ import org.junit.BeforeClass;
import org.junit.Test;
import joptsimple.OptionParser;
import org.springframework.core.env.PropertiesPropertySource;
public class ViewLoaderTests {
@ -65,11 +62,8 @@ public class ViewLoaderTests {
@Before
public void setUp() {
Properties properties = new Properties();
properties.setProperty(BitsquareEnvironment.APP_NAME_KEY, "testApp");
OptionParser parser = new OptionParser();
BitsquareEnvironment env = new BitsquareEnvironment(parser.parse(new String[] {}));
env.getPropertySources().addLast(new PropertiesPropertySource("testProperties", properties));
Injector injector = Guice.createInjector(new BitsquareAppModule(env, TestApp.primaryStage));
ViewLoader.setInjector(injector);
}