mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-06-20 21:04:28 -04:00
Use delayed write on background thread for persistence
This commit is contained in:
parent
b807ee17a6
commit
b96b133c31
35 changed files with 635 additions and 777 deletions
|
@ -26,7 +26,6 @@ import io.bitsquare.gui.components.Popups;
|
||||||
import io.bitsquare.gui.main.MainView;
|
import io.bitsquare.gui.main.MainView;
|
||||||
import io.bitsquare.gui.main.debug.DebugView;
|
import io.bitsquare.gui.main.debug.DebugView;
|
||||||
import io.bitsquare.gui.util.ImageUtil;
|
import io.bitsquare.gui.util.ImageUtil;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.util.Utilities;
|
import io.bitsquare.util.Utilities;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
|
@ -76,18 +75,11 @@ public class BitsquareApp extends Application {
|
||||||
injector.getInstance(InjectorViewFactory.class).setInjector(injector);
|
injector.getInstance(InjectorViewFactory.class).setInjector(injector);
|
||||||
|
|
||||||
// route uncaught exceptions to a user-facing dialog
|
// route uncaught exceptions to a user-facing dialog
|
||||||
|
|
||||||
Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) ->
|
Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) ->
|
||||||
Popups.handleUncaughtExceptions(Throwables.getRootCause(throwable)));
|
Popups.handleUncaughtExceptions(Throwables.getRootCause(throwable)));
|
||||||
|
|
||||||
// load and apply any stored settings
|
|
||||||
|
|
||||||
|
|
||||||
Persistence persistence = injector.getInstance(Persistence.class);
|
|
||||||
persistence.init();
|
|
||||||
|
|
||||||
// load the main view and create the main scene
|
// load the main view and create the main scene
|
||||||
|
|
||||||
log.trace("viewLoader.load(MainView.class)");
|
log.trace("viewLoader.load(MainView.class)");
|
||||||
CachingViewLoader viewLoader = injector.getInstance(CachingViewLoader.class);
|
CachingViewLoader viewLoader = injector.getInstance(CachingViewLoader.class);
|
||||||
View view = viewLoader.load(MainView.class);
|
View view = viewLoader.load(MainView.class);
|
||||||
|
|
|
@ -23,12 +23,11 @@ import io.bitsquare.arbitration.tomp2p.TomP2PArbitratorModule;
|
||||||
import io.bitsquare.btc.BitcoinModule;
|
import io.bitsquare.btc.BitcoinModule;
|
||||||
import io.bitsquare.crypto.CryptoModule;
|
import io.bitsquare.crypto.CryptoModule;
|
||||||
import io.bitsquare.gui.GuiModule;
|
import io.bitsquare.gui.GuiModule;
|
||||||
import io.bitsquare.p2p.P2PModule;
|
|
||||||
import io.bitsquare.p2p.tomp2p.TomP2PModule;
|
|
||||||
import io.bitsquare.offer.OfferModule;
|
import io.bitsquare.offer.OfferModule;
|
||||||
import io.bitsquare.offer.tomp2p.TomP2POfferModule;
|
import io.bitsquare.offer.tomp2p.TomP2POfferModule;
|
||||||
import io.bitsquare.persistence.Persistence;
|
import io.bitsquare.p2p.P2PModule;
|
||||||
import io.bitsquare.persistence.Storage;
|
import io.bitsquare.p2p.tomp2p.TomP2PModule;
|
||||||
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.TradeModule;
|
import io.bitsquare.trade.TradeModule;
|
||||||
import io.bitsquare.user.AccountSettings;
|
import io.bitsquare.user.AccountSettings;
|
||||||
import io.bitsquare.user.Preferences;
|
import io.bitsquare.user.Preferences;
|
||||||
|
@ -63,11 +62,6 @@ class BitsquareAppModule extends BitsquareModule {
|
||||||
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
|
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
|
||||||
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir);
|
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir);
|
||||||
|
|
||||||
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).in(Singleton.class);
|
|
||||||
|
|
||||||
bind(Environment.class).toInstance(env);
|
bind(Environment.class).toInstance(env);
|
||||||
bind(UpdateProcess.class).in(Singleton.class);
|
bind(UpdateProcess.class).in(Singleton.class);
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,7 @@ import io.bitsquare.BitsquareException;
|
||||||
import io.bitsquare.btc.UserAgent;
|
import io.bitsquare.btc.UserAgent;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.gui.main.MainView;
|
import io.bitsquare.gui.main.MainView;
|
||||||
import io.bitsquare.persistence.Persistence;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.persistence.Storage;
|
|
||||||
import io.bitsquare.util.Utilities;
|
import io.bitsquare.util.Utilities;
|
||||||
import io.bitsquare.util.spring.JOptCommandLinePropertySource;
|
import io.bitsquare.util.spring.JOptCommandLinePropertySource;
|
||||||
|
|
||||||
|
@ -137,9 +136,6 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
||||||
|
|
||||||
setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString());
|
setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString());
|
||||||
|
|
||||||
setProperty(Persistence.DIR_KEY, appDataDir);
|
|
||||||
setProperty(Persistence.PREFIX_KEY, appName + "_pref");
|
|
||||||
|
|
||||||
setProperty(MainView.TITLE_KEY, appName);
|
setProperty(MainView.TITLE_KEY, appName);
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
package io.bitsquare.arbitration;
|
package io.bitsquare.arbitration;
|
||||||
|
|
||||||
import io.bitsquare.locale.LanguageUtil;
|
import io.bitsquare.locale.LanguageUtil;
|
||||||
import io.bitsquare.persistence.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -95,7 +95,7 @@ public class Arbitrator implements Serializable {
|
||||||
public Arbitrator(Storage<Arbitrator> storage, User user) {
|
public Arbitrator(Storage<Arbitrator> storage, User user) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
|
|
||||||
Arbitrator persisted = storage.getPersisted(this);
|
Arbitrator persisted = storage.initAndGetPersisted(this);
|
||||||
if (persisted != null) {
|
if (persisted != null) {
|
||||||
//TODO for mock arbitrator
|
//TODO for mock arbitrator
|
||||||
id = persisted.getName();
|
id = persisted.getName();
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
package io.bitsquare.btc;
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
import io.bitsquare.persistence.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
|
|
||||||
import org.bitcoinj.core.Wallet;
|
import org.bitcoinj.core.Wallet;
|
||||||
import org.bitcoinj.crypto.DeterministicKey;
|
import org.bitcoinj.crypto.DeterministicKey;
|
||||||
|
@ -48,7 +48,7 @@ public class AddressEntryList extends ArrayList<AddressEntry> implements Seriali
|
||||||
public void init(Wallet wallet) {
|
public void init(Wallet wallet) {
|
||||||
this.wallet = wallet;
|
this.wallet = wallet;
|
||||||
|
|
||||||
AddressEntryList persisted = storage.getPersisted(this);
|
AddressEntryList persisted = storage.initAndGetPersisted(this);
|
||||||
if (persisted != null) {
|
if (persisted != null) {
|
||||||
for (AddressEntry addressEntry : persisted) {
|
for (AddressEntry addressEntry : persisted) {
|
||||||
addressEntry.setDeterministicKey((DeterministicKey) wallet.findKeyFromPubHash(addressEntry.getPubKeyHash()));
|
addressEntry.setDeterministicKey((DeterministicKey) wallet.findKeyFromPubHash(addressEntry.getPubKeyHash()));
|
||||||
|
|
|
@ -21,7 +21,6 @@ import io.bitsquare.btc.listeners.AddressConfidenceListener;
|
||||||
import io.bitsquare.btc.listeners.BalanceListener;
|
import io.bitsquare.btc.listeners.BalanceListener;
|
||||||
import io.bitsquare.btc.listeners.TxConfidenceListener;
|
import io.bitsquare.btc.listeners.TxConfidenceListener;
|
||||||
import io.bitsquare.crypto.SignatureService;
|
import io.bitsquare.crypto.SignatureService;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.AbstractWalletEventListener;
|
import org.bitcoinj.core.AbstractWalletEventListener;
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.core.Address;
|
||||||
|
@ -91,11 +90,9 @@ public class WalletService {
|
||||||
private final NetworkParameters params;
|
private final NetworkParameters params;
|
||||||
private final FeePolicy feePolicy;
|
private final FeePolicy feePolicy;
|
||||||
private final SignatureService signatureService;
|
private final SignatureService signatureService;
|
||||||
private final Persistence persistence;
|
|
||||||
private final File walletDir;
|
private final File walletDir;
|
||||||
private final String walletPrefix;
|
private final String walletPrefix;
|
||||||
private final UserAgent userAgent;
|
private final UserAgent userAgent;
|
||||||
private final BitcoinNetwork bitcoinNetwork;
|
|
||||||
|
|
||||||
private WalletAppKit walletAppKit;
|
private WalletAppKit walletAppKit;
|
||||||
private Wallet wallet;
|
private Wallet wallet;
|
||||||
|
@ -111,14 +108,12 @@ public class WalletService {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public WalletService(BitcoinNetwork bitcoinNetwork, FeePolicy feePolicy, SignatureService signatureService,
|
public WalletService(BitcoinNetwork bitcoinNetwork, FeePolicy feePolicy, SignatureService signatureService,
|
||||||
Persistence persistence, AddressEntryList addressEntryList, UserAgent userAgent,
|
AddressEntryList addressEntryList, UserAgent userAgent,
|
||||||
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix) {
|
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix) {
|
||||||
this.bitcoinNetwork = bitcoinNetwork;
|
|
||||||
this.addressEntryList = addressEntryList;
|
this.addressEntryList = addressEntryList;
|
||||||
this.params = bitcoinNetwork.getParameters();
|
this.params = bitcoinNetwork.getParameters();
|
||||||
this.feePolicy = feePolicy;
|
this.feePolicy = feePolicy;
|
||||||
this.signatureService = signatureService;
|
this.signatureService = signatureService;
|
||||||
this.persistence = persistence;
|
|
||||||
this.walletDir = walletDir;
|
this.walletDir = walletDir;
|
||||||
this.walletPrefix = walletPrefix;
|
this.walletPrefix = walletPrefix;
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
|
|
|
@ -21,7 +21,7 @@ import io.bitsquare.common.viewfx.view.View;
|
||||||
import io.bitsquare.common.viewfx.view.ViewPath;
|
import io.bitsquare.common.viewfx.view.ViewPath;
|
||||||
import io.bitsquare.gui.main.MainView;
|
import io.bitsquare.gui.main.MainView;
|
||||||
import io.bitsquare.gui.main.trade.BuyView;
|
import io.bitsquare.gui.main.trade.BuyView;
|
||||||
import io.bitsquare.persistence.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class Navigation implements Serializable {
|
||||||
public Navigation(Storage<Navigation> storage) {
|
public Navigation(Storage<Navigation> storage) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
|
|
||||||
Navigation persisted = storage.getPersisted(this);
|
Navigation persisted = storage.initAndGetPersisted(this);
|
||||||
if (persisted != null) {
|
if (persisted != null) {
|
||||||
previousPath = persisted.getPreviousPath();
|
previousPath = persisted.getPreviousPath();
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,9 +68,8 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
||||||
private final String title;
|
private final String title;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, OverlayManager
|
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, OverlayManager overlayManager, Transitions transitions,
|
||||||
overlayManager,
|
@Named(MainView.TITLE_KEY) String title) {
|
||||||
Transitions transitions, @Named(MainView.TITLE_KEY) String title) {
|
|
||||||
super(model);
|
super(model);
|
||||||
this.viewLoader = viewLoader;
|
this.viewLoader = viewLoader;
|
||||||
this.navigation = navigation;
|
this.navigation = navigation;
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
package io.bitsquare.gui.main;
|
package io.bitsquare.gui.main;
|
||||||
|
|
||||||
import io.bitsquare.app.UpdateProcess;
|
import io.bitsquare.app.UpdateProcess;
|
||||||
import io.bitsquare.arbitration.ArbitratorService;
|
|
||||||
import io.bitsquare.btc.BitcoinNetwork;
|
import io.bitsquare.btc.BitcoinNetwork;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.common.viewfx.model.ViewModel;
|
import io.bitsquare.common.viewfx.model.ViewModel;
|
||||||
|
@ -29,10 +28,8 @@ import io.bitsquare.locale.CountryUtil;
|
||||||
import io.bitsquare.p2p.BaseP2PService;
|
import io.bitsquare.p2p.BaseP2PService;
|
||||||
import io.bitsquare.p2p.BootstrapState;
|
import io.bitsquare.p2p.BootstrapState;
|
||||||
import io.bitsquare.p2p.ClientNode;
|
import io.bitsquare.p2p.ClientNode;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.user.AccountSettings;
|
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
@ -91,27 +88,20 @@ class MainViewModel implements ViewModel {
|
||||||
private final User user;
|
private final User user;
|
||||||
private final WalletService walletService;
|
private final WalletService walletService;
|
||||||
private final ClientNode clientNode;
|
private final ClientNode clientNode;
|
||||||
private ArbitratorService arbitratorService;
|
|
||||||
private final TradeManager tradeManager;
|
private final TradeManager tradeManager;
|
||||||
private UpdateProcess updateProcess;
|
private UpdateProcess updateProcess;
|
||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
private Persistence persistence;
|
|
||||||
private AccountSettings accountSettings;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MainViewModel(User user, WalletService walletService, ClientNode clientNode,
|
public MainViewModel(User user, WalletService walletService, ClientNode clientNode,
|
||||||
ArbitratorService arbitratorService,
|
|
||||||
TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
|
TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
|
||||||
BSFormatter formatter, Persistence persistence, AccountSettings accountSettings) {
|
BSFormatter formatter) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.clientNode = clientNode;
|
this.clientNode = clientNode;
|
||||||
this.arbitratorService = arbitratorService;
|
|
||||||
this.tradeManager = tradeManager;
|
this.tradeManager = tradeManager;
|
||||||
this.updateProcess = updateProcess;
|
this.updateProcess = updateProcess;
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
this.persistence = persistence;
|
|
||||||
this.accountSettings = accountSettings;
|
|
||||||
|
|
||||||
bitcoinNetworkAsString = bitcoinNetwork.toString();
|
bitcoinNetworkAsString = bitcoinNetwork.toString();
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import io.bitsquare.common.viewfx.view.View;
|
||||||
import io.bitsquare.common.viewfx.view.ViewLoader;
|
import io.bitsquare.common.viewfx.view.ViewLoader;
|
||||||
import io.bitsquare.gui.main.account.arbitrator.profile.ArbitratorProfileView;
|
import io.bitsquare.gui.main.account.arbitrator.profile.ArbitratorProfileView;
|
||||||
import io.bitsquare.locale.LanguageUtil;
|
import io.bitsquare.locale.LanguageUtil;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.user.AccountSettings;
|
import io.bitsquare.user.AccountSettings;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -54,15 +53,13 @@ public class ArbitratorBrowserView extends ActivatableView<Pane, Void> implement
|
||||||
|
|
||||||
private final ViewLoader viewLoader;
|
private final ViewLoader viewLoader;
|
||||||
private final AccountSettings accountSettings;
|
private final AccountSettings accountSettings;
|
||||||
private final Persistence persistence;
|
|
||||||
private final ArbitratorService messageService;
|
private final ArbitratorService messageService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ArbitratorBrowserView(CachingViewLoader viewLoader, AccountSettings accountSettings, Persistence persistence,
|
public ArbitratorBrowserView(CachingViewLoader viewLoader, AccountSettings accountSettings,
|
||||||
ArbitratorService messageService) {
|
ArbitratorService messageService) {
|
||||||
this.viewLoader = viewLoader;
|
this.viewLoader = viewLoader;
|
||||||
this.accountSettings = accountSettings;
|
this.accountSettings = accountSettings;
|
||||||
this.persistence = persistence;
|
|
||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import io.bitsquare.locale.Country;
|
||||||
import io.bitsquare.locale.CountryUtil;
|
import io.bitsquare.locale.CountryUtil;
|
||||||
import io.bitsquare.locale.CurrencyUtil;
|
import io.bitsquare.locale.CurrencyUtil;
|
||||||
import io.bitsquare.locale.Region;
|
import io.bitsquare.locale.Region;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.user.AccountSettings;
|
import io.bitsquare.user.AccountSettings;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
|
@ -46,7 +45,6 @@ class FiatAccountDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
private final User user;
|
private final User user;
|
||||||
private final AccountSettings accountSettings;
|
private final AccountSettings accountSettings;
|
||||||
private final Persistence persistence;
|
|
||||||
|
|
||||||
final StringProperty title = new SimpleStringProperty();
|
final StringProperty title = new SimpleStringProperty();
|
||||||
final StringProperty holderName = new SimpleStringProperty();
|
final StringProperty holderName = new SimpleStringProperty();
|
||||||
|
@ -68,8 +66,7 @@ class FiatAccountDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FiatAccountDataModel(User user, Persistence persistence, AccountSettings accountSettings) {
|
public FiatAccountDataModel(User user, AccountSettings accountSettings) {
|
||||||
this.persistence = persistence;
|
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.accountSettings = accountSettings;
|
this.accountSettings = accountSettings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,12 @@
|
||||||
|
|
||||||
package io.bitsquare.gui.main.account.content.irc;
|
package io.bitsquare.gui.main.account.content.irc;
|
||||||
|
|
||||||
import io.bitsquare.arbitration.ArbitratorService;
|
|
||||||
import io.bitsquare.common.viewfx.model.Activatable;
|
import io.bitsquare.common.viewfx.model.Activatable;
|
||||||
import io.bitsquare.common.viewfx.model.DataModel;
|
import io.bitsquare.common.viewfx.model.DataModel;
|
||||||
import io.bitsquare.fiat.FiatAccount;
|
import io.bitsquare.fiat.FiatAccount;
|
||||||
import io.bitsquare.fiat.FiatAccountType;
|
import io.bitsquare.fiat.FiatAccountType;
|
||||||
import io.bitsquare.locale.CountryUtil;
|
import io.bitsquare.locale.CountryUtil;
|
||||||
import io.bitsquare.locale.CurrencyUtil;
|
import io.bitsquare.locale.CurrencyUtil;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.user.AccountSettings;
|
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
@ -42,9 +39,6 @@ import javafx.collections.ObservableList;
|
||||||
class IrcAccountDataModel implements Activatable, DataModel {
|
class IrcAccountDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
private final User user;
|
private final User user;
|
||||||
private final AccountSettings accountSettings;
|
|
||||||
private final ArbitratorService messageService;
|
|
||||||
private final Persistence persistence;
|
|
||||||
|
|
||||||
final StringProperty nickName = new SimpleStringProperty();
|
final StringProperty nickName = new SimpleStringProperty();
|
||||||
final ObjectProperty<FiatAccountType> type = new SimpleObjectProperty<>();
|
final ObjectProperty<FiatAccountType> type = new SimpleObjectProperty<>();
|
||||||
|
@ -57,12 +51,8 @@ class IrcAccountDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public IrcAccountDataModel(User user, Persistence persistence, AccountSettings accountSettings,
|
public IrcAccountDataModel(User user) {
|
||||||
ArbitratorService messageService) {
|
|
||||||
this.persistence = persistence;
|
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.accountSettings = accountSettings;
|
|
||||||
this.messageService = messageService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -22,7 +22,6 @@ import io.bitsquare.btc.FeePolicy;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.btc.listeners.BalanceListener;
|
import io.bitsquare.btc.listeners.BalanceListener;
|
||||||
import io.bitsquare.common.viewfx.model.DataModel;
|
import io.bitsquare.common.viewfx.model.DataModel;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -50,7 +49,6 @@ class RegistrationDataModel implements DataModel {
|
||||||
|
|
||||||
private final WalletService walletService;
|
private final WalletService walletService;
|
||||||
private final User user;
|
private final User user;
|
||||||
private final Persistence persistence;
|
|
||||||
|
|
||||||
private String transactionId;
|
private String transactionId;
|
||||||
private AddressEntry addressEntry;
|
private AddressEntry addressEntry;
|
||||||
|
@ -61,11 +59,10 @@ class RegistrationDataModel implements DataModel {
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public RegistrationDataModel(WalletService walletService, User user, Persistence persistence) {
|
public RegistrationDataModel(WalletService walletService, User user) {
|
||||||
|
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.persistence = persistence;
|
|
||||||
|
|
||||||
if (walletService != null && walletService.getWallet() != null) {
|
if (walletService != null && walletService.getWallet() != null) {
|
||||||
addressEntry = walletService.getRegistrationAddressEntry();
|
addressEntry = walletService.getRegistrationAddressEntry();
|
||||||
|
|
|
@ -18,16 +18,13 @@
|
||||||
package io.bitsquare.gui.main.account.content.restrictions;
|
package io.bitsquare.gui.main.account.content.restrictions;
|
||||||
|
|
||||||
import io.bitsquare.arbitration.Arbitrator;
|
import io.bitsquare.arbitration.Arbitrator;
|
||||||
import io.bitsquare.arbitration.ArbitratorService;
|
|
||||||
import io.bitsquare.common.viewfx.model.Activatable;
|
import io.bitsquare.common.viewfx.model.Activatable;
|
||||||
import io.bitsquare.common.viewfx.model.DataModel;
|
import io.bitsquare.common.viewfx.model.DataModel;
|
||||||
import io.bitsquare.locale.Country;
|
import io.bitsquare.locale.Country;
|
||||||
import io.bitsquare.locale.CountryUtil;
|
import io.bitsquare.locale.CountryUtil;
|
||||||
import io.bitsquare.locale.LanguageUtil;
|
import io.bitsquare.locale.LanguageUtil;
|
||||||
import io.bitsquare.locale.Region;
|
import io.bitsquare.locale.Region;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.user.AccountSettings;
|
import io.bitsquare.user.AccountSettings;
|
||||||
import io.bitsquare.user.User;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
@ -38,10 +35,7 @@ import javafx.collections.ObservableList;
|
||||||
|
|
||||||
class RestrictionsDataModel implements Activatable, DataModel {
|
class RestrictionsDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
private final User user;
|
|
||||||
private final AccountSettings accountSettings;
|
private final AccountSettings accountSettings;
|
||||||
private final Persistence persistence;
|
|
||||||
private final ArbitratorService messageService;
|
|
||||||
|
|
||||||
final ObservableList<Locale> languageList = FXCollections.observableArrayList();
|
final ObservableList<Locale> languageList = FXCollections.observableArrayList();
|
||||||
final ObservableList<Country> countryList = FXCollections.observableArrayList();
|
final ObservableList<Country> countryList = FXCollections.observableArrayList();
|
||||||
|
@ -52,12 +46,8 @@ class RestrictionsDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public RestrictionsDataModel(User user, AccountSettings accountSettings, Persistence persistence,
|
public RestrictionsDataModel(AccountSettings accountSettings) {
|
||||||
ArbitratorService messageService) {
|
|
||||||
this.user = user;
|
|
||||||
this.accountSettings = accountSettings;
|
this.accountSettings = accountSettings;
|
||||||
this.persistence = persistence;
|
|
||||||
this.messageService = messageService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,7 +28,6 @@ import io.bitsquare.fiat.FiatAccount;
|
||||||
import io.bitsquare.gui.util.BSFormatter;
|
import io.bitsquare.gui.util.BSFormatter;
|
||||||
import io.bitsquare.locale.Country;
|
import io.bitsquare.locale.Country;
|
||||||
import io.bitsquare.offer.Direction;
|
import io.bitsquare.offer.Direction;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.user.AccountSettings;
|
import io.bitsquare.user.AccountSettings;
|
||||||
import io.bitsquare.user.Preferences;
|
import io.bitsquare.user.Preferences;
|
||||||
|
@ -72,8 +71,6 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
||||||
private final WalletService walletService;
|
private final WalletService walletService;
|
||||||
private final AccountSettings accountSettings;
|
private final AccountSettings accountSettings;
|
||||||
private Preferences preferences;
|
private Preferences preferences;
|
||||||
private final User user;
|
|
||||||
private final Persistence persistence;
|
|
||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
|
|
||||||
private final String offerId;
|
private final String offerId;
|
||||||
|
@ -110,14 +107,12 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
||||||
// non private for testing
|
// non private for testing
|
||||||
@Inject
|
@Inject
|
||||||
public CreateOfferDataModel(TradeManager tradeManager, WalletService walletService, AccountSettings accountSettings,
|
public CreateOfferDataModel(TradeManager tradeManager, WalletService walletService, AccountSettings accountSettings,
|
||||||
Preferences preferences, User user, Persistence persistence,
|
Preferences preferences, User user,
|
||||||
BSFormatter formatter) {
|
BSFormatter formatter) {
|
||||||
this.tradeManager = tradeManager;
|
this.tradeManager = tradeManager;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.accountSettings = accountSettings;
|
this.accountSettings = accountSettings;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
this.user = user;
|
|
||||||
this.persistence = persistence;
|
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
this.offerId = UUID.randomUUID().toString();
|
this.offerId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
@ -231,7 +226,7 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void securityDepositInfoDisplayed() {
|
void securityDepositInfoDisplayed() {
|
||||||
persistence.write("displaySecurityDepositInfo", false);
|
preferences.setDisplaySecurityDepositInfo(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -255,15 +250,6 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
||||||
return offerId;
|
return offerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean displaySecurityDepositInfo() {
|
|
||||||
Object securityDepositInfoDisplayedObject = persistence.read("displaySecurityDepositInfo");
|
|
||||||
if (securityDepositInfoDisplayedObject instanceof Boolean)
|
|
||||||
return (Boolean) securityDepositInfoDisplayedObject;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void updateBalance(@NotNull Coin balance) {
|
private void updateBalance(@NotNull Coin balance) {
|
||||||
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
||||||
}
|
}
|
||||||
|
@ -281,4 +267,8 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
||||||
fiatCode.set(fiatAccount.getCurrency().getCurrencyCode());
|
fiatCode.set(fiatAccount.getCurrency().getCurrencyCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getDisplaySecurityDepositInfo() {
|
||||||
|
return preferences.getDisplaySecurityDepositInfo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
@FXML
|
@FXML
|
||||||
void onShowPayFundsScreen() {
|
void onShowPayFundsScreen() {
|
||||||
// TODO deactivate for testing the moment
|
// TODO deactivate for testing the moment
|
||||||
/* if (model.displaySecurityDepositInfo()) {
|
/* if (model.getDisplaySecurityDepositInfo()) {
|
||||||
overlayManager.blurContent();
|
overlayManager.blurContent();
|
||||||
List<Action> actions = new ArrayList<>();
|
List<Action> actions = new ArrayList<>();
|
||||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||||
|
|
|
@ -250,8 +250,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
return formatter;
|
return formatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean displaySecurityDepositInfo() {
|
Boolean getDisplaySecurityDepositInfo() {
|
||||||
return dataModel.displaySecurityDepositInfo();
|
return dataModel.getDisplaySecurityDepositInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupListeners() {
|
private void setupListeners() {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import io.bitsquare.btc.listeners.BalanceListener;
|
||||||
import io.bitsquare.common.viewfx.model.Activatable;
|
import io.bitsquare.common.viewfx.model.Activatable;
|
||||||
import io.bitsquare.common.viewfx.model.DataModel;
|
import io.bitsquare.common.viewfx.model.DataModel;
|
||||||
import io.bitsquare.offer.Offer;
|
import io.bitsquare.offer.Offer;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.trade.handlers.TradeResultHandler;
|
import io.bitsquare.trade.handlers.TradeResultHandler;
|
||||||
import io.bitsquare.user.Preferences;
|
import io.bitsquare.user.Preferences;
|
||||||
|
@ -58,7 +57,6 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
||||||
private final TradeManager tradeManager;
|
private final TradeManager tradeManager;
|
||||||
private final WalletService walletService;
|
private final WalletService walletService;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
private final Persistence persistence;
|
|
||||||
|
|
||||||
private Offer offer;
|
private Offer offer;
|
||||||
private AddressEntry addressEntry;
|
private AddressEntry addressEntry;
|
||||||
|
@ -78,12 +76,10 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
||||||
@Inject
|
@Inject
|
||||||
public TakeOfferDataModel(TradeManager tradeManager,
|
public TakeOfferDataModel(TradeManager tradeManager,
|
||||||
WalletService walletService,
|
WalletService walletService,
|
||||||
Preferences preferences,
|
Preferences preferences) {
|
||||||
Persistence persistence) {
|
|
||||||
this.tradeManager = tradeManager;
|
this.tradeManager = tradeManager;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
this.persistence = persistence;
|
|
||||||
|
|
||||||
offerFeeAsCoin.set(FeePolicy.CREATE_OFFER_FEE);
|
offerFeeAsCoin.set(FeePolicy.CREATE_OFFER_FEE);
|
||||||
networkFeeAsCoin.set(FeePolicy.TX_FEE);
|
networkFeeAsCoin.set(FeePolicy.TX_FEE);
|
||||||
|
@ -174,16 +170,12 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean displaySecurityDepositInfo() {
|
Boolean getDisplaySecurityDepositInfo() {
|
||||||
Object securityDepositInfoDisplayedObject = persistence.read("displaySecurityDepositInfo");
|
return preferences.getDisplaySecurityDepositInfo();
|
||||||
if (securityDepositInfoDisplayedObject instanceof Boolean)
|
|
||||||
return (Boolean) securityDepositInfoDisplayedObject;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void securityDepositInfoDisplayed() {
|
void securityDepositInfoDisplayed() {
|
||||||
persistence.write("displaySecurityDepositInfo", false);
|
preferences.setDisplaySecurityDepositInfo(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -319,7 +319,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
|
|
||||||
private void setupPaymentScreen() {
|
private void setupPaymentScreen() {
|
||||||
// TODO deactivate for testing the moment
|
// TODO deactivate for testing the moment
|
||||||
/* if (model.displaySecurityDepositInfo()) {
|
/* if (model.getDisplaySecurityDepositInfo()) {
|
||||||
overlayManager.blurContent();
|
overlayManager.blurContent();
|
||||||
List<Action> actions = new ArrayList<>();
|
List<Action> actions = new ArrayList<>();
|
||||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||||
|
|
|
@ -359,8 +359,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
return paymentLabel;
|
return paymentLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean displaySecurityDepositInfo() {
|
Boolean getDisplaySecurityDepositInfo() {
|
||||||
return dataModel.displaySecurityDepositInfo();
|
return dataModel.getDisplaySecurityDepositInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,299 +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.persistence;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Utils;
|
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple storage solution for serialized data
|
|
||||||
* TODO: Should be improved with a more robust solution or maybe a lightweight database.
|
|
||||||
* TODO: Should run in a dedicated thread.
|
|
||||||
*/
|
|
||||||
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";
|
|
||||||
private static final long MIN_INTERVAL_BETWEEN_WRITE_OPERATIONS = 1000;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private Map<String, Serializable> rootMap = new HashMap<>();
|
|
||||||
private Map<String, Long> timestampMap = new HashMap<>();
|
|
||||||
|
|
||||||
private final File dir;
|
|
||||||
private final String prefix;
|
|
||||||
private final File storageFile;
|
|
||||||
private int resetCounter = 0;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public API
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void init() {
|
|
||||||
try {
|
|
||||||
lock.lock();
|
|
||||||
final Map<String, Serializable> map = readRootMap();
|
|
||||||
|
|
||||||
if (map == null)
|
|
||||||
saveObjectToFile((Serializable) rootMap);
|
|
||||||
else
|
|
||||||
rootMap = map;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map
|
|
||||||
public void write(Object classInstance, String propertyKey, List<? extends Serializable> value) {
|
|
||||||
write(classInstance.getClass().getName() + "." + propertyKey, value);
|
|
||||||
}
|
|
||||||
public void write(Object classInstance, String propertyKey, Serializable value) {
|
|
||||||
write(classInstance.getClass().getName() + "." + propertyKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// not used outside
|
|
||||||
public void write(String key, Map<String, ? extends Serializable> value) {
|
|
||||||
write(key, (Serializable) value);
|
|
||||||
}
|
|
||||||
public void write(String key, List<? extends Serializable> value) {
|
|
||||||
write(key, (Serializable) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Serializable
|
|
||||||
public void remove(Object classInstance, String propertyKey) {
|
|
||||||
try {
|
|
||||||
lock.lock();
|
|
||||||
rootMap.remove(classInstance.getClass().getName() + "." + propertyKey);
|
|
||||||
saveObjectToFile((Serializable) rootMap);
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void write(Serializable classInstance) {
|
|
||||||
write(classInstance.getClass().getName(), classInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(String key, Serializable value) {
|
|
||||||
//log.trace("Write object with key = " + key + " / value = " + value);
|
|
||||||
// TODO add throttle to limit write operations
|
|
||||||
|
|
||||||
try {
|
|
||||||
lock.lock();
|
|
||||||
rootMap.put(key, value);
|
|
||||||
saveObjectToFile((Serializable) rootMap);
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Serializable read(Object classInstance) {
|
|
||||||
return read(classInstance.getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Serializable read(Object classInstance, String propertyKey) {
|
|
||||||
return read(classInstance.getClass().getName() + "." + propertyKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// read from local rootMap, just if not found read from disc
|
|
||||||
public Serializable read(String key) {
|
|
||||||
try {
|
|
||||||
lock.lock();
|
|
||||||
if (rootMap.containsKey(key)) {
|
|
||||||
// log.trace("Read object with key = " + key + " / value = " + rootMap.get(key));
|
|
||||||
return rootMap.get(key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final Map<String, Serializable> map = readRootMap();
|
|
||||||
if (map != null) {
|
|
||||||
rootMap = map;
|
|
||||||
}
|
|
||||||
if (rootMap.containsKey(key)) {
|
|
||||||
// log.trace("Read object with key = " + key + " / value = " + rootMap.get(key));
|
|
||||||
return rootMap.get(key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.info("Object with key = " + key + " not found.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
private Map<String, Serializable> readRootMap() {
|
|
||||||
try {
|
|
||||||
final Object object = readObjectFromFile(storageFile);
|
|
||||||
if (object == null) {
|
|
||||||
log.error("readRootMap returned null object.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (object instanceof Map) {
|
|
||||||
return (Map<String, Serializable>) object;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.error("Object is not type of Map<String, Serializable>");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
|
|
||||||
log.trace("File not found is ok for the first execute.");
|
|
||||||
return null;
|
|
||||||
} catch (ClassNotFoundException | IOException e2) {
|
|
||||||
log.warn("Could not read rootMap. " + e2);
|
|
||||||
|
|
||||||
// If there are problems with incompatible versions, we reset the persisted data
|
|
||||||
// TODO We need a clean solution when we use another persistence solution
|
|
||||||
rootMap = new HashMap<>();
|
|
||||||
saveObjectToFile((Serializable) rootMap);
|
|
||||||
resetCounter++;
|
|
||||||
|
|
||||||
// We only try that once, if it fails repeatedly we
|
|
||||||
if (resetCounter == 1) {
|
|
||||||
log.warn("We reset the persisted data and try again to read the root map.");
|
|
||||||
return readRootMap();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
e2.printStackTrace();
|
|
||||||
log.error("We tried already to reset the persisted data, but we get an error again, so we give up.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveObjectToFile(Serializable serializable) {
|
|
||||||
File tempFile = null;
|
|
||||||
FileOutputStream fileOutputStream = null;
|
|
||||||
ObjectOutputStream objectOutputStream = null;
|
|
||||||
try {
|
|
||||||
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
|
|
||||||
// manually before replacing file with temp file
|
|
||||||
fileOutputStream = new FileOutputStream(tempFile);
|
|
||||||
objectOutputStream = new ObjectOutputStream(fileOutputStream);
|
|
||||||
|
|
||||||
objectOutputStream.writeObject(serializable);
|
|
||||||
|
|
||||||
// Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
|
|
||||||
// to not write through to physical media for at least a few seconds, but this is the best we can do.
|
|
||||||
fileOutputStream.flush();
|
|
||||||
fileOutputStream.getFD().sync();
|
|
||||||
|
|
||||||
// Close resources before replacing file with temp file because otherwise it causes problems on windows
|
|
||||||
// when rename temp file
|
|
||||||
fileOutputStream.close();
|
|
||||||
objectOutputStream.close();
|
|
||||||
|
|
||||||
writeTempFileToFile(tempFile, storageFile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error("save object to file failed." + e);
|
|
||||||
} finally {
|
|
||||||
if (tempFile != null && tempFile.exists()) {
|
|
||||||
log.warn("Temp file still exists after failed save.");
|
|
||||||
if (!tempFile.delete()) log.error("Cannot delete temp file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (objectOutputStream != null) objectOutputStream.close();
|
|
||||||
if (fileOutputStream != null) fileOutputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error("Cannot close resources.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object readObjectFromFile(File file) throws IOException, ClassNotFoundException {
|
|
||||||
lock.lock();
|
|
||||||
try (final FileInputStream fileInputStream = new FileInputStream(file);
|
|
||||||
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
|
|
||||||
return objectInputStream.readObject();
|
|
||||||
} finally {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,103 +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.persistence;
|
|
||||||
|
|
||||||
import io.bitsquare.gui.components.Popups;
|
|
||||||
import io.bitsquare.util.FileUtil;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InvalidClassException;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class Storage<T extends Serializable> {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(Storage.class);
|
|
||||||
|
|
||||||
public static final String DIR_KEY = "storage.dir";
|
|
||||||
private final File dir;
|
|
||||||
private File storageFile;
|
|
||||||
private T serializable;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public Storage(@Named(DIR_KEY) File dir) {
|
|
||||||
this.dir = dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() {
|
|
||||||
if (storageFile == null)
|
|
||||||
throw new RuntimeException("storageFile = null. Call setupFileStorage before using read/write.");
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileUtil.write(serializable, dir, storageFile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error(e.getMessage());
|
|
||||||
Popups.openErrorPopup("An exception occurred at writing data to disc.", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getPersisted(T serializable) {
|
|
||||||
this.serializable = serializable;
|
|
||||||
storageFile = new File(dir, serializable.getClass().getSimpleName() + ".ser");
|
|
||||||
|
|
||||||
if (storageFile == null)
|
|
||||||
throw new RuntimeException("storageFile = null. Call init before using read/write.");
|
|
||||||
|
|
||||||
try {
|
|
||||||
T persistedObject = (T) FileUtil.read(storageFile);
|
|
||||||
|
|
||||||
// If we did not get any exception we can be sure the data are consistent so we make a backup
|
|
||||||
FileUtil.backupFile(storageFile, new File(Paths.get(dir.getAbsolutePath(), "backups").toString()),
|
|
||||||
serializable.getClass().getSimpleName() + ".ser");
|
|
||||||
return persistedObject;
|
|
||||||
} catch (InvalidClassException e) {
|
|
||||||
log.error("Version of persisted class has changed. We cannot read the persisted data anymore. We make a backup and remove the inconsistent file.");
|
|
||||||
try {
|
|
||||||
// In case the persisted data have been critical (keys) we keep a backup which might be used for recovery
|
|
||||||
FileUtil.removeAndBackupFile(storageFile, new File(Paths.get(dir.getAbsolutePath(), "inconsistent").toString()),
|
|
||||||
serializable.getClass().getSimpleName() + ".ser");
|
|
||||||
} catch (IOException e1) {
|
|
||||||
e1.printStackTrace();
|
|
||||||
log.error(e1.getMessage());
|
|
||||||
// We swallow Exception if backup fails
|
|
||||||
}
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
log.info("File not available. That is OK for the first run.");
|
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error(e.getMessage());
|
|
||||||
Popups.openErrorPopup("An exception occurred at reading data from disc.", e.getMessage());
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
302
core/src/main/java/io/bitsquare/storage/FileManager.java
Normal file
302
core/src/main/java/io/bitsquare/storage/FileManager.java
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2013 Google Inc.
|
||||||
|
* Copyright 2014 Andreas Schildbach
|
||||||
|
* <p/>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p/>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p/>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.bitsquare.storage;
|
||||||
|
|
||||||
|
import io.bitsquare.gui.components.Popups;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Utils;
|
||||||
|
import org.bitcoinj.utils.Threading;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.io.Files;
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Borrowed from BitcoinJ WalletFiles
|
||||||
|
* A class that handles atomic and optionally delayed writing of a file to disk.
|
||||||
|
* It can be useful to delay writing of a file to disk on slow devices.
|
||||||
|
* By coalescing writes and doing serialization
|
||||||
|
* and disk IO on a background thread performance can be improved.
|
||||||
|
*/
|
||||||
|
public class FileManager<T> {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(FileManager.class);
|
||||||
|
private static final ReentrantLock lock = Threading.lock("FileUtil");
|
||||||
|
|
||||||
|
private final File dir;
|
||||||
|
private final File storageFile;
|
||||||
|
private final ScheduledThreadPoolExecutor executor;
|
||||||
|
private final AtomicBoolean savePending;
|
||||||
|
private final long delay;
|
||||||
|
private final TimeUnit delayTimeUnit;
|
||||||
|
private final Callable<Void> saver;
|
||||||
|
private T serializable;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public FileManager(File dir, File storageFile, long delay, TimeUnit delayTimeUnit) {
|
||||||
|
this.dir = dir;
|
||||||
|
this.storageFile = storageFile;
|
||||||
|
|
||||||
|
final ThreadFactoryBuilder builder = new ThreadFactoryBuilder()
|
||||||
|
.setDaemon(true)
|
||||||
|
.setNameFormat("FileManager thread")
|
||||||
|
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread.
|
||||||
|
|
||||||
|
builder.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread t, Throwable throwable) {
|
||||||
|
Platform.runLater(() -> Popups.handleUncaughtExceptions(Throwables.getRootCause(throwable)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// An executor that starts up threads when needed and shuts them down later.
|
||||||
|
this.executor = new ScheduledThreadPoolExecutor(1, builder.build());
|
||||||
|
this.executor.setKeepAliveTime(5, TimeUnit.SECONDS);
|
||||||
|
this.executor.allowCoreThreadTimeOut(true);
|
||||||
|
this.executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||||
|
|
||||||
|
// File must only be accessed from the auto-save executor from now on, to avoid simultaneous access.
|
||||||
|
this.savePending = new AtomicBoolean();
|
||||||
|
this.delay = delay;
|
||||||
|
this.delayTimeUnit = checkNotNull(delayTimeUnit);
|
||||||
|
|
||||||
|
this.saver = new Callable<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void call() throws Exception {
|
||||||
|
// Runs in an auto save thread.
|
||||||
|
if (!savePending.getAndSet(false)) {
|
||||||
|
// Some other scheduled request already beat us to it.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
saveNowInternal(serializable);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
FileManager.this.shutdown();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually write the wallet file to disk, using an atomic rename when possible. Runs on the current thread.
|
||||||
|
*/
|
||||||
|
public void saveNow(T serializable) throws IOException {
|
||||||
|
saveNowInternal(serializable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues up a save in the background. Useful for not very important wallet changes.
|
||||||
|
*/
|
||||||
|
public void saveLater(T serializable) {
|
||||||
|
this.serializable = serializable;
|
||||||
|
|
||||||
|
if (savePending.getAndSet(true))
|
||||||
|
return; // Already pending.
|
||||||
|
executor.schedule(saver, delay, delayTimeUnit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object read(File file) throws IOException, ClassNotFoundException {
|
||||||
|
lock.lock();
|
||||||
|
try (final FileInputStream fileInputStream = new FileInputStream(file);
|
||||||
|
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
|
||||||
|
return objectInputStream.readObject();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeFile(T serializable) {
|
||||||
|
storageFile.delete();
|
||||||
|
|
||||||
|
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
||||||
|
if (backupDir.exists()) {
|
||||||
|
File backupFile = new File(backupDir, serializable.getClass().getSimpleName());
|
||||||
|
if (backupFile.exists())
|
||||||
|
backupFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shut down auto-saving.
|
||||||
|
*/
|
||||||
|
public void shutdown() {
|
||||||
|
if (serializable != null)
|
||||||
|
log.debug("shutDown " + serializable.getClass().getSimpleName());
|
||||||
|
else
|
||||||
|
log.debug("shutDown");
|
||||||
|
|
||||||
|
executor.shutdown();
|
||||||
|
try {
|
||||||
|
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // forever
|
||||||
|
} catch (InterruptedException x) {
|
||||||
|
throw new RuntimeException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAndBackupFile(File storageFile, File dir, String name) throws IOException {
|
||||||
|
File corruptedBackupDir = new File(Paths.get(dir.getAbsolutePath(), "corrupted").toString());
|
||||||
|
if (!corruptedBackupDir.exists())
|
||||||
|
corruptedBackupDir.mkdir();
|
||||||
|
|
||||||
|
renameTempFileToFile(storageFile, new File(corruptedBackupDir, serializable.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void backupFile(T serializable) throws IOException {
|
||||||
|
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
||||||
|
if (!backupDir.exists())
|
||||||
|
backupDir.mkdir();
|
||||||
|
|
||||||
|
Files.copy(storageFile, new File(backupDir, serializable.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Private
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void saveNowInternal(T serializable) throws IOException {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
saveToFile(serializable, dir, storageFile);
|
||||||
|
log.info("Save completed in {}msec", System.currentTimeMillis() - now);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveToFile(T serializable, File dir, File storageFile) throws IOException {
|
||||||
|
lock.lock();
|
||||||
|
File tempFile = null;
|
||||||
|
FileOutputStream fileOutputStream = null;
|
||||||
|
ObjectOutputStream objectOutputStream = null;
|
||||||
|
try {
|
||||||
|
if (!dir.exists())
|
||||||
|
dir.mkdir();
|
||||||
|
|
||||||
|
tempFile = File.createTempFile("temp", 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
|
||||||
|
// manually before replacing file with temp file
|
||||||
|
fileOutputStream = new FileOutputStream(tempFile);
|
||||||
|
objectOutputStream = new ObjectOutputStream(fileOutputStream);
|
||||||
|
|
||||||
|
objectOutputStream.writeObject(serializable);
|
||||||
|
|
||||||
|
// Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
|
||||||
|
// to not write through to physical media for at least a few seconds, but this is the best we can do.
|
||||||
|
fileOutputStream.flush();
|
||||||
|
fileOutputStream.getFD().sync();
|
||||||
|
|
||||||
|
// Close resources before replacing file with temp file because otherwise it causes problems on windows
|
||||||
|
// when rename temp file
|
||||||
|
fileOutputStream.close();
|
||||||
|
objectOutputStream.close();
|
||||||
|
|
||||||
|
renameTempFileToFile(tempFile, storageFile);
|
||||||
|
} finally {
|
||||||
|
if (tempFile != null && tempFile.exists()) {
|
||||||
|
log.warn("Temp file still exists after failed save.");
|
||||||
|
if (!tempFile.delete()) log.error("Cannot delete temp file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (objectOutputStream != null)
|
||||||
|
objectOutputStream.close();
|
||||||
|
if (fileOutputStream != null)
|
||||||
|
fileOutputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// We swallow that
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error("Cannot close resources.");
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renameTempFileToFile(File tempFile, File file) throws IOException {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
core/src/main/java/io/bitsquare/storage/Storage.java
Normal file
141
core/src/main/java/io/bitsquare/storage/Storage.java
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* 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.storage;
|
||||||
|
|
||||||
|
import io.bitsquare.gui.components.Popups;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InvalidClassException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* That class handles the storage of a particular object to disk using Java serialisation.
|
||||||
|
* To support evolving versions of the serialised data we need to take care that we don't break the object structure.
|
||||||
|
* Java serialisation is tolerant with added fields, but removing or changing existing fields will break the backwards compatibility.
|
||||||
|
* Alternative frameworks for serialisation like Kyro or mapDB have shown problems with version migration, so we stuck with plain Java
|
||||||
|
* serialisation.
|
||||||
|
* <p/>
|
||||||
|
* For every data object we write a separate file to minimize the risk of corrupted files in case of inconsistency from newer versions.
|
||||||
|
* In case of a corrupted file we backup the old file to a separate directory, so if it holds critical data it might be helpful for recovery.
|
||||||
|
* <p/>
|
||||||
|
* We also backup at first read the file, so we have a valid file form the latest version in case a write operation corrupted the file.
|
||||||
|
* <p/>
|
||||||
|
* The read operation is triggered just at object creation (startup) and is at the moment not executed on a background thread to avoid asynchronous behaviour.
|
||||||
|
* As the data are small and it is just one read access the performance penalty is small and might be even worse to create and setup a thread for it.
|
||||||
|
* <p/>
|
||||||
|
* The write operation used a background thread and supports a delayed write to avoid too many repeated write operations.
|
||||||
|
*/
|
||||||
|
public class Storage<T extends Serializable> {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Storage.class);
|
||||||
|
public static final String DIR_KEY = "storage.dir";
|
||||||
|
|
||||||
|
private final File dir;
|
||||||
|
private FileManager<T> fileManager;
|
||||||
|
private File storageFile;
|
||||||
|
private T serializable;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public Storage(@Named(DIR_KEY) File dir) {
|
||||||
|
this.dir = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T initAndGetPersisted(T serializable) {
|
||||||
|
return initAndGetPersisted(serializable, serializable.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public T initAndGetPersisted(T serializable, String fileName) {
|
||||||
|
this.serializable = serializable;
|
||||||
|
storageFile = new File(dir, fileName);
|
||||||
|
fileManager = new FileManager<>(dir, storageFile, 500, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
return getPersisted(serializable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save delayed and on a background thread
|
||||||
|
public void save() {
|
||||||
|
if (storageFile == null)
|
||||||
|
throw new RuntimeException("storageFile = null. Call setupFileStorage before using read/write.");
|
||||||
|
|
||||||
|
fileManager.saveLater(serializable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
fileManager.removeFile(serializable);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Private
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// We do the file read on the UI thread to avoid problems from multi threading.
|
||||||
|
// Data are small and read is done only at startup, so it is no performance issue.
|
||||||
|
private T getPersisted(T serializable) {
|
||||||
|
if (storageFile == null)
|
||||||
|
throw new RuntimeException("storageFile = null. Call init before using read/write.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
T persistedObject = (T) fileManager.read(storageFile);
|
||||||
|
log.info("Read {} completed in {}msec", serializable.getClass().getSimpleName(), System.currentTimeMillis() - now);
|
||||||
|
|
||||||
|
// If we did not get any exception we can be sure the data are consistent so we make a backup
|
||||||
|
now = System.currentTimeMillis();
|
||||||
|
fileManager.backupFile(serializable);
|
||||||
|
log.info("Backup {} completed in {}msec", serializable.getClass().getSimpleName(), System.currentTimeMillis() - now);
|
||||||
|
|
||||||
|
return persistedObject;
|
||||||
|
} catch (InvalidClassException e) {
|
||||||
|
log.error("Version of persisted class has changed. We cannot read the persisted data anymore. We make a backup and remove the inconsistent file.");
|
||||||
|
try {
|
||||||
|
// In case the persisted data have been critical (keys) we keep a backup which might be used for recovery
|
||||||
|
fileManager.removeAndBackupFile(storageFile, new File(Paths.get(dir.getAbsolutePath(), "inconsistent").toString()),
|
||||||
|
serializable.getClass().getSimpleName());
|
||||||
|
} catch (IOException e1) {
|
||||||
|
e1.printStackTrace();
|
||||||
|
log.error(e1.getMessage());
|
||||||
|
// We swallow Exception if backup fails
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
log.info("File not available. That is OK for the first run.");
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error(e.getMessage());
|
||||||
|
Popups.openErrorPopup("An exception occurred at reading data from disc.", e.getMessage());
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,6 @@ import io.bitsquare.p2p.MailboxMessage;
|
||||||
import io.bitsquare.p2p.MailboxService;
|
import io.bitsquare.p2p.MailboxService;
|
||||||
import io.bitsquare.p2p.MessageService;
|
import io.bitsquare.p2p.MessageService;
|
||||||
import io.bitsquare.p2p.Peer;
|
import io.bitsquare.p2p.Peer;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.trade.handlers.TradeResultHandler;
|
import io.bitsquare.trade.handlers.TradeResultHandler;
|
||||||
import io.bitsquare.trade.handlers.TransactionResultHandler;
|
import io.bitsquare.trade.handlers.TransactionResultHandler;
|
||||||
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel;
|
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel;
|
||||||
|
@ -51,17 +50,16 @@ import io.bitsquare.user.User;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.utils.Fiat;
|
import org.bitcoinj.utils.Fiat;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.File;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -72,7 +70,6 @@ public class TradeManager {
|
||||||
|
|
||||||
private final User user;
|
private final User user;
|
||||||
private final AccountSettings accountSettings;
|
private final AccountSettings accountSettings;
|
||||||
private final Persistence persistence;
|
|
||||||
private final MessageService messageService;
|
private final MessageService messageService;
|
||||||
private MailboxService mailboxService;
|
private MailboxService mailboxService;
|
||||||
private final AddressService addressService;
|
private final AddressService addressService;
|
||||||
|
@ -81,14 +78,15 @@ public class TradeManager {
|
||||||
private final SignatureService signatureService;
|
private final SignatureService signatureService;
|
||||||
private EncryptionService<MailboxMessage> encryptionService;
|
private EncryptionService<MailboxMessage> encryptionService;
|
||||||
private final OfferBookService offerBookService;
|
private final OfferBookService offerBookService;
|
||||||
|
private File storageDir;
|
||||||
|
|
||||||
private final Map<String, TakerAsSellerProtocol> takerAsSellerProtocolMap = new HashMap<>();
|
private final Map<String, TakerAsSellerProtocol> takerAsSellerProtocolMap = new HashMap<>();
|
||||||
private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocolMap = new HashMap<>();
|
private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocolMap = new HashMap<>();
|
||||||
private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>();
|
private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>();
|
||||||
|
|
||||||
private final ObservableList<Trade> openOfferTrades = FXCollections.observableArrayList();
|
private final TradesList openOfferTrades;
|
||||||
private final ObservableList<Trade> pendingTrades = FXCollections.observableArrayList();
|
private final TradesList pendingTrades;
|
||||||
private final ObservableList<Trade> closedTrades = FXCollections.observableArrayList();
|
private final TradesList closedTrades;
|
||||||
private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>();
|
private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,13 +95,15 @@ public class TradeManager {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TradeManager(User user, AccountSettings accountSettings, Persistence persistence,
|
public TradeManager(User user, AccountSettings accountSettings, TradesList openOfferTrades, TradesList pendingTrades, TradesList closedTrades,
|
||||||
MessageService messageService, MailboxService mailboxService, AddressService addressService, BlockChainService blockChainService,
|
MessageService messageService, MailboxService mailboxService, AddressService addressService, BlockChainService blockChainService,
|
||||||
WalletService walletService, SignatureService signatureService, EncryptionService<MailboxMessage> encryptionService,
|
WalletService walletService, SignatureService signatureService, EncryptionService<MailboxMessage> encryptionService,
|
||||||
OfferBookService offerBookService) {
|
OfferBookService offerBookService, @Named("storage.dir") File storageDir) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.accountSettings = accountSettings;
|
this.accountSettings = accountSettings;
|
||||||
this.persistence = persistence;
|
this.openOfferTrades = openOfferTrades;
|
||||||
|
this.pendingTrades = pendingTrades;
|
||||||
|
this.closedTrades = closedTrades;
|
||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
this.mailboxService = mailboxService;
|
this.mailboxService = mailboxService;
|
||||||
this.addressService = addressService;
|
this.addressService = addressService;
|
||||||
|
@ -112,19 +112,7 @@ public class TradeManager {
|
||||||
this.signatureService = signatureService;
|
this.signatureService = signatureService;
|
||||||
this.encryptionService = encryptionService;
|
this.encryptionService = encryptionService;
|
||||||
this.offerBookService = offerBookService;
|
this.offerBookService = offerBookService;
|
||||||
|
this.storageDir = storageDir;
|
||||||
Serializable openOffersObject = persistence.read(this, "openOffers");
|
|
||||||
if (openOffersObject instanceof List<?>) {
|
|
||||||
openOfferTrades.addAll((List<Trade>) openOffersObject);
|
|
||||||
}
|
|
||||||
Serializable pendingTradesObject = persistence.read(this, "pendingTrades");
|
|
||||||
if (pendingTradesObject instanceof List<?>) {
|
|
||||||
pendingTrades.addAll((List<Trade>) pendingTradesObject);
|
|
||||||
}
|
|
||||||
Serializable closedTradesObject = persistence.read(this, "closedTrades");
|
|
||||||
if (closedTradesObject instanceof List<?>) {
|
|
||||||
closedTrades.addAll((List<Trade>) closedTradesObject);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -198,7 +186,6 @@ public class TradeManager {
|
||||||
Trade trade = new Trade(offer);
|
Trade trade = new Trade(offer);
|
||||||
trade.setLifeCycleState(Trade.LifeCycleState.OPEN_OFFER);
|
trade.setLifeCycleState(Trade.LifeCycleState.OPEN_OFFER);
|
||||||
openOfferTrades.add(trade);
|
openOfferTrades.add(trade);
|
||||||
persistOpenOfferTrades();
|
|
||||||
|
|
||||||
createOffererAsBuyerProtocol(trade);
|
createOffererAsBuyerProtocol(trade);
|
||||||
resultHandler.handleResult(transaction);
|
resultHandler.handleResult(transaction);
|
||||||
|
@ -265,7 +252,7 @@ public class TradeManager {
|
||||||
// TODO remove if check when persistence is impl.
|
// TODO remove if check when persistence is impl.
|
||||||
if (offererAsBuyerProtocolMap.containsKey(tradeId)) {
|
if (offererAsBuyerProtocolMap.containsKey(tradeId)) {
|
||||||
offererAsBuyerProtocolMap.get(tradeId).onFiatPaymentStarted();
|
offererAsBuyerProtocolMap.get(tradeId).onFiatPaymentStarted();
|
||||||
persistPendingTrades();
|
// persistPendingTrades();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,9 +263,7 @@ public class TradeManager {
|
||||||
public void onWithdrawAtTradeCompleted(Trade trade) {
|
public void onWithdrawAtTradeCompleted(Trade trade) {
|
||||||
trade.setLifeCycleState(Trade.LifeCycleState.COMPLETED);
|
trade.setLifeCycleState(Trade.LifeCycleState.COMPLETED);
|
||||||
pendingTrades.remove(trade);
|
pendingTrades.remove(trade);
|
||||||
persistPendingTrades();
|
|
||||||
closedTrades.add(trade);
|
closedTrades.add(trade);
|
||||||
persistClosedTrades();
|
|
||||||
removeFromProtocolMap(trade);
|
removeFromProtocolMap(trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,15 +282,15 @@ public class TradeManager {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public ObservableList<Trade> getOpenOfferTrades() {
|
public ObservableList<Trade> getOpenOfferTrades() {
|
||||||
return openOfferTrades;
|
return openOfferTrades.getObservableList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableList<Trade> getPendingTrades() {
|
public ObservableList<Trade> getPendingTrades() {
|
||||||
return pendingTrades;
|
return pendingTrades.getObservableList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableList<Trade> getClosedTrades() {
|
public ObservableList<Trade> getClosedTrades() {
|
||||||
return closedTrades;
|
return closedTrades.getObservableList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -329,12 +314,10 @@ public class TradeManager {
|
||||||
if (result.isPresent()) {
|
if (result.isPresent()) {
|
||||||
Trade trade = result.get();
|
Trade trade = result.get();
|
||||||
openOfferTrades.remove(trade);
|
openOfferTrades.remove(trade);
|
||||||
persistOpenOfferTrades();
|
|
||||||
|
|
||||||
if (isCancelRequest) {
|
if (isCancelRequest) {
|
||||||
trade.setLifeCycleState(Trade.LifeCycleState.CANCELED);
|
trade.setLifeCycleState(Trade.LifeCycleState.CANCELED);
|
||||||
closedTrades.add(trade);
|
closedTrades.add(trade);
|
||||||
persistClosedTrades();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +358,6 @@ public class TradeManager {
|
||||||
trade.setTradingPeer(peer);
|
trade.setTradingPeer(peer);
|
||||||
trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
|
trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
|
||||||
pendingTrades.add(trade);
|
pendingTrades.add(trade);
|
||||||
persistPendingTrades();
|
|
||||||
|
|
||||||
TakerAsSellerProtocol sellerTakesOfferProtocol = createTakerAsSellerProtocol(trade);
|
TakerAsSellerProtocol sellerTakesOfferProtocol = createTakerAsSellerProtocol(trade);
|
||||||
//trade.setProtocol(sellerTakesOfferProtocol);
|
//trade.setProtocol(sellerTakesOfferProtocol);
|
||||||
|
@ -397,7 +379,7 @@ public class TradeManager {
|
||||||
case FIAT_PAYMENT_STARTED:
|
case FIAT_PAYMENT_STARTED:
|
||||||
case FIAT_PAYMENT_RECEIVED:
|
case FIAT_PAYMENT_RECEIVED:
|
||||||
case PAYOUT_PUBLISHED:
|
case PAYOUT_PUBLISHED:
|
||||||
persistPendingTrades();
|
// persistPendingTrades();
|
||||||
break;
|
break;
|
||||||
case MESSAGE_SENDING_FAILED:
|
case MESSAGE_SENDING_FAILED:
|
||||||
case FAULT:
|
case FAULT:
|
||||||
|
@ -418,7 +400,7 @@ public class TradeManager {
|
||||||
blockChainService,
|
blockChainService,
|
||||||
signatureService,
|
signatureService,
|
||||||
user,
|
user,
|
||||||
persistence);
|
storageDir);
|
||||||
|
|
||||||
TakerAsSellerProtocol protocol = new TakerAsSellerProtocol(model);
|
TakerAsSellerProtocol protocol = new TakerAsSellerProtocol(model);
|
||||||
takerAsSellerProtocolMap.put(trade.getId(), protocol);
|
takerAsSellerProtocolMap.put(trade.getId(), protocol);
|
||||||
|
@ -440,7 +422,7 @@ public class TradeManager {
|
||||||
blockChainService,
|
blockChainService,
|
||||||
signatureService,
|
signatureService,
|
||||||
user,
|
user,
|
||||||
persistence);
|
storageDir);
|
||||||
|
|
||||||
|
|
||||||
// TODO check, remove listener
|
// TODO check, remove listener
|
||||||
|
@ -450,7 +432,7 @@ public class TradeManager {
|
||||||
case INIT:
|
case INIT:
|
||||||
break;
|
break;
|
||||||
case TAKE_OFFER_FEE_TX_CREATED:
|
case TAKE_OFFER_FEE_TX_CREATED:
|
||||||
persistPendingTrades();
|
// persistPendingTrades();
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_PUBLISHED:
|
case DEPOSIT_PUBLISHED:
|
||||||
removeOpenOffer(trade.getOffer(),
|
removeOpenOffer(trade.getOffer(),
|
||||||
|
@ -459,13 +441,12 @@ public class TradeManager {
|
||||||
false);
|
false);
|
||||||
model.trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
|
model.trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
|
||||||
pendingTrades.add(trade);
|
pendingTrades.add(trade);
|
||||||
persistPendingTrades();
|
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_CONFIRMED:
|
case DEPOSIT_CONFIRMED:
|
||||||
case FIAT_PAYMENT_STARTED:
|
case FIAT_PAYMENT_STARTED:
|
||||||
case FIAT_PAYMENT_RECEIVED:
|
case FIAT_PAYMENT_RECEIVED:
|
||||||
case PAYOUT_PUBLISHED:
|
case PAYOUT_PUBLISHED:
|
||||||
persistPendingTrades();
|
// persistPendingTrades();
|
||||||
break;
|
break;
|
||||||
case TAKE_OFFER_FEE_PUBLISH_FAILED:
|
case TAKE_OFFER_FEE_PUBLISH_FAILED:
|
||||||
case MESSAGE_SENDING_FAILED:
|
case MESSAGE_SENDING_FAILED:
|
||||||
|
@ -541,16 +522,4 @@ public class TradeManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void persistOpenOfferTrades() {
|
|
||||||
persistence.write(this, "openOfferTrades", (List<Trade>) new ArrayList<>(openOfferTrades));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void persistPendingTrades() {
|
|
||||||
persistence.write(this, "pendingTrades", (List<Trade>) new ArrayList<>(pendingTrades));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void persistClosedTrades() {
|
|
||||||
persistence.write(this, "closedTrades", (List<Trade>) new ArrayList<>(closedTrades));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
72
core/src/main/java/io/bitsquare/trade/TradesList.java
Normal file
72
core/src/main/java/io/bitsquare/trade/TradesList.java
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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.trade;
|
||||||
|
|
||||||
|
import io.bitsquare.storage.Storage;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class TradesList extends ArrayList<Trade> implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(TradesList.class);
|
||||||
|
|
||||||
|
transient final private Storage<TradesList> storage;
|
||||||
|
transient private ObservableList<Trade> observableList;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TradesList(Storage<TradesList> storage) {
|
||||||
|
this.storage = storage;
|
||||||
|
|
||||||
|
TradesList persisted = storage.initAndGetPersisted(this);
|
||||||
|
if (persisted != null) {
|
||||||
|
addAll(persisted);
|
||||||
|
observableList = FXCollections.observableArrayList(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
observableList = FXCollections.observableArrayList(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(Trade trade) {
|
||||||
|
boolean result = super.add(trade);
|
||||||
|
storage.save();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object trade) {
|
||||||
|
boolean result = super.remove(trade);
|
||||||
|
storage.save();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<Trade> getObservableList() {
|
||||||
|
return observableList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,6 @@ import io.bitsquare.offer.Offer;
|
||||||
import io.bitsquare.p2p.MailboxMessage;
|
import io.bitsquare.p2p.MailboxMessage;
|
||||||
import io.bitsquare.p2p.MailboxService;
|
import io.bitsquare.p2p.MailboxService;
|
||||||
import io.bitsquare.p2p.MessageService;
|
import io.bitsquare.p2p.MessageService;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -45,7 +44,6 @@ public class SharedTradeModel extends SharedTaskModel implements Serializable {
|
||||||
transient public final WalletService walletService;
|
transient public final WalletService walletService;
|
||||||
transient public final BlockChainService blockChainService;
|
transient public final BlockChainService blockChainService;
|
||||||
transient public final SignatureService signatureService;
|
transient public final SignatureService signatureService;
|
||||||
transient protected final Persistence persistence;
|
|
||||||
|
|
||||||
transient public MailboxMessage mailboxMessage;
|
transient public MailboxMessage mailboxMessage;
|
||||||
|
|
||||||
|
@ -62,15 +60,13 @@ public class SharedTradeModel extends SharedTaskModel implements Serializable {
|
||||||
MailboxService mailboxService,
|
MailboxService mailboxService,
|
||||||
WalletService walletService,
|
WalletService walletService,
|
||||||
BlockChainService blockChainService,
|
BlockChainService blockChainService,
|
||||||
SignatureService signatureService,
|
SignatureService signatureService) {
|
||||||
Persistence persistence) {
|
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
this.mailboxService = mailboxService;
|
this.mailboxService = mailboxService;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.blockChainService = blockChainService;
|
this.blockChainService = blockChainService;
|
||||||
this.signatureService = signatureService;
|
this.signatureService = signatureService;
|
||||||
this.persistence = persistence;
|
|
||||||
|
|
||||||
id = offer.getId();
|
id = offer.getId();
|
||||||
tradeWalletService = walletService.getTradeWalletService();
|
tradeWalletService = walletService.getTradeWalletService();
|
||||||
|
|
|
@ -22,11 +22,12 @@ import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.crypto.SignatureService;
|
import io.bitsquare.crypto.SignatureService;
|
||||||
import io.bitsquare.p2p.MailboxService;
|
import io.bitsquare.p2p.MailboxService;
|
||||||
import io.bitsquare.p2p.MessageService;
|
import io.bitsquare.p2p.MessageService;
|
||||||
import io.bitsquare.persistence.Persistence;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.protocol.trade.SharedTradeModel;
|
import io.bitsquare.trade.protocol.trade.SharedTradeModel;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -34,9 +35,11 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class OffererAsBuyerModel extends SharedTradeModel implements Serializable {
|
public class OffererAsBuyerModel extends SharedTradeModel implements Serializable {
|
||||||
private static final long serialVersionUID = 5000457153390911569L;
|
private static final long serialVersionUID = 5000457153390911569L;
|
||||||
private static final Logger log = LoggerFactory.getLogger(OffererAsBuyerModel.class);
|
transient private static final Logger log = LoggerFactory.getLogger(OffererAsBuyerModel.class);
|
||||||
|
|
||||||
|
transient private Storage<OffererAsBuyerModel> storage;
|
||||||
transient public final Trade trade;
|
transient public final Trade trade;
|
||||||
|
|
||||||
public final Taker taker;
|
public final Taker taker;
|
||||||
public final Offerer offerer;
|
public final Offerer offerer;
|
||||||
|
|
||||||
|
@ -50,26 +53,25 @@ public class OffererAsBuyerModel extends SharedTradeModel implements Serializabl
|
||||||
BlockChainService blockChainService,
|
BlockChainService blockChainService,
|
||||||
SignatureService signatureService,
|
SignatureService signatureService,
|
||||||
User user,
|
User user,
|
||||||
Persistence persistence) {
|
File storageDir) {
|
||||||
super(trade.getOffer(),
|
super(trade.getOffer(),
|
||||||
messageService,
|
messageService,
|
||||||
mailboxService,
|
mailboxService,
|
||||||
walletService,
|
walletService,
|
||||||
blockChainService,
|
blockChainService,
|
||||||
signatureService,
|
signatureService);
|
||||||
persistence);
|
|
||||||
|
|
||||||
this.trade = trade;
|
this.trade = trade;
|
||||||
|
this.storage = new Storage<>(storageDir);
|
||||||
|
|
||||||
Serializable serializable = persistence.read(this, "BuyerAsOffererModel_" + id);
|
OffererAsBuyerModel persisted = storage.initAndGetPersisted(this, getClass().getSimpleName() + id);
|
||||||
if (serializable instanceof OffererAsBuyerModel) {
|
if (persisted != null) {
|
||||||
OffererAsBuyerModel persistedModel = (OffererAsBuyerModel) serializable;
|
|
||||||
log.debug("Model reconstructed form persisted model.");
|
log.debug("Model reconstructed form persisted model.");
|
||||||
|
|
||||||
setTakeOfferFeeTxId(persistedModel.takeOfferFeeTxId);
|
setTakeOfferFeeTxId(persisted.takeOfferFeeTxId);
|
||||||
|
|
||||||
taker = persistedModel.taker;
|
taker = persisted.taker;
|
||||||
offerer = persistedModel.offerer;
|
offerer = persisted.offerer;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
taker = new Taker();
|
taker = new Taker();
|
||||||
|
@ -90,13 +92,13 @@ public class OffererAsBuyerModel extends SharedTradeModel implements Serializabl
|
||||||
// Get called form taskRunner after each completed task
|
// Get called form taskRunner after each completed task
|
||||||
@Override
|
@Override
|
||||||
public void persist() {
|
public void persist() {
|
||||||
persistence.write(this, "BuyerAsOffererModel_" + id, this);
|
storage.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
// Just in case of successful completion we delete our persisted object
|
// Just in case of successful completion we delete our persisted object
|
||||||
persistence.remove(this, "BuyerAsOffererModel_" + id);
|
storage.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTakeOfferFeeTxId() {
|
public String getTakeOfferFeeTxId() {
|
||||||
|
|
|
@ -22,13 +22,14 @@ import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.crypto.SignatureService;
|
import io.bitsquare.crypto.SignatureService;
|
||||||
import io.bitsquare.p2p.MailboxService;
|
import io.bitsquare.p2p.MailboxService;
|
||||||
import io.bitsquare.p2p.MessageService;
|
import io.bitsquare.p2p.MessageService;
|
||||||
import io.bitsquare.persistence.Persistence;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.protocol.trade.SharedTradeModel;
|
import io.bitsquare.trade.protocol.trade.SharedTradeModel;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -36,9 +37,11 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class TakerAsSellerModel extends SharedTradeModel implements Serializable {
|
public class TakerAsSellerModel extends SharedTradeModel implements Serializable {
|
||||||
private static final long serialVersionUID = -963501132927618376L;
|
private static final long serialVersionUID = -963501132927618376L;
|
||||||
private static final Logger log = LoggerFactory.getLogger(TakerAsSellerModel.class);
|
transient private static final Logger log = LoggerFactory.getLogger(TakerAsSellerModel.class);
|
||||||
|
|
||||||
|
transient private Storage<TakerAsSellerModel> storage;
|
||||||
|
transient public final Trade trade;
|
||||||
|
|
||||||
public final Trade trade;
|
|
||||||
public final Taker taker;
|
public final Taker taker;
|
||||||
public final Offerer offerer;
|
public final Offerer offerer;
|
||||||
|
|
||||||
|
@ -53,27 +56,26 @@ public class TakerAsSellerModel extends SharedTradeModel implements Serializable
|
||||||
BlockChainService blockChainService,
|
BlockChainService blockChainService,
|
||||||
SignatureService signatureService,
|
SignatureService signatureService,
|
||||||
User user,
|
User user,
|
||||||
Persistence persistence) {
|
File storageDir) {
|
||||||
super(trade.getOffer(),
|
super(trade.getOffer(),
|
||||||
messageService,
|
messageService,
|
||||||
mailboxService,
|
mailboxService,
|
||||||
walletService,
|
walletService,
|
||||||
blockChainService,
|
blockChainService,
|
||||||
signatureService,
|
signatureService);
|
||||||
persistence);
|
|
||||||
|
|
||||||
this.trade = trade;
|
this.trade = trade;
|
||||||
|
this.storage = new Storage<>(storageDir);
|
||||||
|
|
||||||
Serializable serializable = persistence.read(this, "SellerAsTakerModel_" + id);
|
TakerAsSellerModel persisted = storage.initAndGetPersisted(this, getClass().getSimpleName() + id);
|
||||||
if (serializable instanceof TakerAsSellerModel) {
|
if (persisted != null) {
|
||||||
TakerAsSellerModel persistedModel = (TakerAsSellerModel) serializable;
|
log.debug("Model reconstructed from persisted model.");
|
||||||
log.debug("Model reconstructed form persisted model.");
|
|
||||||
|
|
||||||
setTakeOfferFeeTx(persistedModel.getTakeOfferFeeTx());
|
setTakeOfferFeeTx(persisted.getTakeOfferFeeTx());
|
||||||
setPayoutTx(persistedModel.payoutTx);
|
setPayoutTx(persisted.payoutTx);
|
||||||
|
|
||||||
taker = persistedModel.taker;
|
taker = persisted.taker;
|
||||||
offerer = persistedModel.offerer;
|
offerer = persisted.offerer;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
taker = new Taker();
|
taker = new Taker();
|
||||||
|
@ -93,13 +95,13 @@ public class TakerAsSellerModel extends SharedTradeModel implements Serializable
|
||||||
// Get called form taskRunner after each completed task
|
// Get called form taskRunner after each completed task
|
||||||
@Override
|
@Override
|
||||||
public void persist() {
|
public void persist() {
|
||||||
persistence.write(this, "SellerAsTakerModel_" + id, this);
|
storage.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
// Just in case of successful completion we delete our persisted object
|
// Just in case of successful completion we delete our persisted object
|
||||||
persistence.remove(this, "SellerAsTakerModel_" + id);
|
storage.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import io.bitsquare.arbitration.Arbitrator;
|
||||||
import io.bitsquare.locale.Country;
|
import io.bitsquare.locale.Country;
|
||||||
import io.bitsquare.locale.CountryUtil;
|
import io.bitsquare.locale.CountryUtil;
|
||||||
import io.bitsquare.locale.LanguageUtil;
|
import io.bitsquare.locale.LanguageUtil;
|
||||||
import io.bitsquare.persistence.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ public class AccountSettings implements Serializable {
|
||||||
private List<Country> acceptedCountryLocales = new ArrayList<>();
|
private List<Country> acceptedCountryLocales = new ArrayList<>();
|
||||||
private List<Arbitrator> acceptedArbitrators = new ArrayList<>();
|
private List<Arbitrator> acceptedArbitrators = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -53,7 +54,7 @@ public class AccountSettings implements Serializable {
|
||||||
public AccountSettings(Storage<AccountSettings> storage, Arbitrator defaultArbitrator) {
|
public AccountSettings(Storage<AccountSettings> storage, Arbitrator defaultArbitrator) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
|
|
||||||
AccountSettings persisted = storage.getPersisted(this);
|
AccountSettings persisted = storage.initAndGetPersisted(this);
|
||||||
if (persisted != null) {
|
if (persisted != null) {
|
||||||
acceptedLanguageLocales = persisted.getAcceptedLanguageLocales();
|
acceptedLanguageLocales = persisted.getAcceptedLanguageLocales();
|
||||||
acceptedCountryLocales = persisted.getAcceptedCountries();
|
acceptedCountryLocales = persisted.getAcceptedCountries();
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
package io.bitsquare.user;
|
package io.bitsquare.user;
|
||||||
|
|
||||||
import io.bitsquare.persistence.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
|
|
||||||
import org.bitcoinj.utils.MonetaryFormat;
|
import org.bitcoinj.utils.MonetaryFormat;
|
||||||
|
|
||||||
|
@ -53,13 +53,13 @@ public class Preferences implements Serializable {
|
||||||
private String _btcDenomination = MonetaryFormat.CODE_BTC;
|
private String _btcDenomination = MonetaryFormat.CODE_BTC;
|
||||||
private Boolean _useAnimations = true;
|
private Boolean _useAnimations = true;
|
||||||
private Boolean _useEffects = true;
|
private Boolean _useEffects = true;
|
||||||
|
private Boolean displaySecurityDepositInfo = true;
|
||||||
|
|
||||||
// Observable wrappers
|
// Observable wrappers
|
||||||
transient private final StringProperty btcDenomination = new SimpleStringProperty(_btcDenomination);
|
transient private final StringProperty btcDenomination = new SimpleStringProperty(_btcDenomination);
|
||||||
transient private final BooleanProperty useAnimations = new SimpleBooleanProperty(_useAnimations);
|
transient private final BooleanProperty useAnimations = new SimpleBooleanProperty(_useAnimations);
|
||||||
transient private final BooleanProperty useEffects = new SimpleBooleanProperty(_useEffects);
|
transient private final BooleanProperty useEffects = new SimpleBooleanProperty(_useEffects);
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -68,11 +68,12 @@ public class Preferences implements Serializable {
|
||||||
public Preferences(Storage<Preferences> storage) {
|
public Preferences(Storage<Preferences> storage) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
|
|
||||||
Preferences persisted = storage.getPersisted(this);
|
Preferences persisted = storage.initAndGetPersisted(this);
|
||||||
if (persisted != null) {
|
if (persisted != null) {
|
||||||
setBtcDenomination(persisted._btcDenomination);
|
setBtcDenomination(persisted._btcDenomination);
|
||||||
setUseAnimations(persisted._useAnimations);
|
setUseAnimations(persisted._useAnimations);
|
||||||
setUseEffects(persisted._useEffects);
|
setUseEffects(persisted._useEffects);
|
||||||
|
displaySecurityDepositInfo = persisted.getDisplaySecurityDepositInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use that to guarantee update of the serializable field and to make a storage update in case of a change
|
// Use that to guarantee update of the serializable field and to make a storage update in case of a change
|
||||||
|
@ -107,6 +108,11 @@ public class Preferences implements Serializable {
|
||||||
this.useEffects.set(useEffects);
|
this.useEffects.set(useEffects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDisplaySecurityDepositInfo(Boolean displaySecurityDepositInfo) {
|
||||||
|
this.displaySecurityDepositInfo = displaySecurityDepositInfo;
|
||||||
|
storage.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Getter
|
// Getter
|
||||||
|
@ -124,6 +130,10 @@ public class Preferences implements Serializable {
|
||||||
return useAnimations.get();
|
return useAnimations.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getDisplaySecurityDepositInfo() {
|
||||||
|
return displaySecurityDepositInfo;
|
||||||
|
}
|
||||||
|
|
||||||
public StringProperty btcDenominationProperty() {
|
public StringProperty btcDenominationProperty() {
|
||||||
return btcDenomination;
|
return btcDenomination;
|
||||||
}
|
}
|
||||||
|
@ -135,4 +145,6 @@ public class Preferences implements Serializable {
|
||||||
public BooleanProperty useEffectsProperty() {
|
public BooleanProperty useEffectsProperty() {
|
||||||
return useEffects;
|
return useEffects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ package io.bitsquare.user;
|
||||||
import io.bitsquare.crypto.EncryptionService;
|
import io.bitsquare.crypto.EncryptionService;
|
||||||
import io.bitsquare.fiat.FiatAccount;
|
import io.bitsquare.fiat.FiatAccount;
|
||||||
import io.bitsquare.gui.components.Popups;
|
import io.bitsquare.gui.components.Popups;
|
||||||
import io.bitsquare.persistence.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ public class User implements Serializable {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.encryptionService = encryptionService;
|
this.encryptionService = encryptionService;
|
||||||
|
|
||||||
User persisted = storage.getPersisted(this);
|
User persisted = storage.initAndGetPersisted(this);
|
||||||
if (persisted != null) {
|
if (persisted != null) {
|
||||||
p2pSigKeyPair = persisted.getP2pSigKeyPair();
|
p2pSigKeyPair = persisted.getP2pSigKeyPair();
|
||||||
p2pEncryptKeyPair = persisted.getP2pEncryptKeyPair();
|
p2pEncryptKeyPair = persisted.getP2pEncryptKeyPair();
|
||||||
|
|
|
@ -1,136 +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 org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class FileUtil {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
|
|
||||||
private static final ReentrantLock lock = Threading.lock("FileUtil");
|
|
||||||
|
|
||||||
public static void write(Serializable serializable, File dir, File storageFile) throws IOException {
|
|
||||||
lock.lock();
|
|
||||||
File tempFile = null;
|
|
||||||
FileOutputStream fileOutputStream = null;
|
|
||||||
ObjectOutputStream objectOutputStream = null;
|
|
||||||
try {
|
|
||||||
if (!dir.exists())
|
|
||||||
dir.mkdir();
|
|
||||||
|
|
||||||
tempFile = File.createTempFile("temp", 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
|
|
||||||
// manually before replacing file with temp file
|
|
||||||
fileOutputStream = new FileOutputStream(tempFile);
|
|
||||||
objectOutputStream = new ObjectOutputStream(fileOutputStream);
|
|
||||||
|
|
||||||
objectOutputStream.writeObject(serializable);
|
|
||||||
|
|
||||||
// Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
|
|
||||||
// to not write through to physical media for at least a few seconds, but this is the best we can do.
|
|
||||||
fileOutputStream.flush();
|
|
||||||
fileOutputStream.getFD().sync();
|
|
||||||
|
|
||||||
// Close resources before replacing file with temp file because otherwise it causes problems on windows
|
|
||||||
// when rename temp file
|
|
||||||
fileOutputStream.close();
|
|
||||||
objectOutputStream.close();
|
|
||||||
|
|
||||||
writeTempFileToFile(tempFile, storageFile);
|
|
||||||
} finally {
|
|
||||||
if (tempFile != null && tempFile.exists()) {
|
|
||||||
log.warn("Temp file still exists after failed save.");
|
|
||||||
if (!tempFile.delete()) log.error("Cannot delete temp file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (objectOutputStream != null)
|
|
||||||
objectOutputStream.close();
|
|
||||||
if (fileOutputStream != null)
|
|
||||||
fileOutputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// We swallow that
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error("Cannot close resources.");
|
|
||||||
}
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object read(File file) throws IOException, ClassNotFoundException {
|
|
||||||
lock.lock();
|
|
||||||
try (final FileInputStream fileInputStream = new FileInputStream(file);
|
|
||||||
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
|
|
||||||
return objectInputStream.readObject();
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeTempFileToFile(File tempFile, File file) throws IOException {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeAndBackupFile(File storageFile, File dir, String name) throws IOException {
|
|
||||||
if (!dir.exists())
|
|
||||||
dir.mkdir();
|
|
||||||
|
|
||||||
writeTempFileToFile(storageFile, new File(dir, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backupFile(File storageFile, File dir, String name) throws IOException {
|
|
||||||
if (!dir.exists())
|
|
||||||
dir.mkdir();
|
|
||||||
|
|
||||||
Files.copy(storageFile, new File(dir, name));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -44,7 +44,7 @@ public class CreateOfferViewModelTest {
|
||||||
BSFormatter formatter = new BSFormatter(new User());
|
BSFormatter formatter = new BSFormatter(new User());
|
||||||
formatter.setLocale(Locale.US);
|
formatter.setLocale(Locale.US);
|
||||||
formatter.setFiatCurrencyCode("USD");
|
formatter.setFiatCurrencyCode("USD");
|
||||||
model = new CreateOfferDataModel(null, null, null, null, null, null, formatter);
|
model = new CreateOfferDataModel(null, null, null, null, null, formatter);
|
||||||
|
|
||||||
presenter = new CreateOfferViewModel(model, new FiatValidator(null), new BtcValidator(), formatter);
|
presenter = new CreateOfferViewModel(model, new FiatValidator(null), new BtcValidator(), formatter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,38 +17,21 @@
|
||||||
|
|
||||||
package io.bitsquare.trade.protocol.placeoffer;
|
package io.bitsquare.trade.protocol.placeoffer;
|
||||||
|
|
||||||
import io.bitsquare.btc.BitcoinNetwork;
|
|
||||||
import io.bitsquare.btc.FeePolicy;
|
|
||||||
import io.bitsquare.btc.UserAgent;
|
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.offer.OfferBookService;
|
import io.bitsquare.offer.OfferBookService;
|
||||||
import io.bitsquare.offer.tomp2p.TomP2POfferBookService;
|
|
||||||
import io.bitsquare.p2p.BootstrapState;
|
|
||||||
import io.bitsquare.p2p.MessageService;
|
import io.bitsquare.p2p.MessageService;
|
||||||
import io.bitsquare.p2p.Node;
|
|
||||||
import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
|
import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
|
||||||
import io.bitsquare.p2p.tomp2p.TomP2PMessageService;
|
|
||||||
import io.bitsquare.p2p.tomp2p.TomP2PNode;
|
import io.bitsquare.p2p.tomp2p.TomP2PNode;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.user.User;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.core.Address;
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import rx.Observable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* That test is ignored for automated testing as it needs custom setup.
|
* That test is ignored for automated testing as it needs custom setup.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -72,22 +55,19 @@ public class PlaceOfferProtocolTest {
|
||||||
private TomP2PNode tomP2PNode;
|
private TomP2PNode tomP2PNode;
|
||||||
private BootstrappedPeerBuilder bootstrappedPeerBuilder;
|
private BootstrappedPeerBuilder bootstrappedPeerBuilder;
|
||||||
|
|
||||||
@Before
|
/* @Before
|
||||||
public void setup() throws InterruptedException {
|
public void setup() throws InterruptedException {
|
||||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
dir.mkdirs();
|
dir.mkdirs();
|
||||||
|
|
||||||
Persistence persistence = new Persistence(dir, "prefs");
|
|
||||||
persistence.init();
|
|
||||||
|
|
||||||
// messageService
|
// messageService
|
||||||
Node bootstrapNode = Node.at("localhost", "127.0.0.1");
|
Node bootstrapNode = Node.at("localhost", "127.0.0.1");
|
||||||
User user = new User();
|
User user = new User();
|
||||||
/* try {
|
*//* try {
|
||||||
user.initPersistedObject();
|
user.initPersistedObject();
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}*/
|
}*//*
|
||||||
bootstrappedPeerBuilder = new BootstrappedPeerBuilder(Node.DEFAULT_PORT, false, bootstrapNode, "<unspecified>");
|
bootstrappedPeerBuilder = new BootstrappedPeerBuilder(Node.DEFAULT_PORT, false, bootstrapNode, "<unspecified>");
|
||||||
tomP2PNode = new TomP2PNode(bootstrappedPeerBuilder);
|
tomP2PNode = new TomP2PNode(bootstrappedPeerBuilder);
|
||||||
messageService = new TomP2PMessageService(tomP2PNode, null, null, null);
|
messageService = new TomP2PMessageService(tomP2PNode, null, null, null);
|
||||||
|
@ -153,7 +133,7 @@ public class PlaceOfferProtocolTest {
|
||||||
public void shutDown() throws IOException, InterruptedException {
|
public void shutDown() throws IOException, InterruptedException {
|
||||||
walletService.shutDown();
|
walletService.shutDown();
|
||||||
bootstrappedPeerBuilder.shutDown();
|
bootstrappedPeerBuilder.shutDown();
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/* @Test
|
/* @Test
|
||||||
public void validateOfferTest() throws InterruptedException {
|
public void validateOfferTest() throws InterruptedException {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue