Use delayed write on background thread for persistence

This commit is contained in:
Manfred Karrer 2015-03-24 21:10:35 +01:00
parent b807ee17a6
commit b96b133c31
35 changed files with 635 additions and 777 deletions

View File

@ -26,7 +26,6 @@ import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.debug.DebugView;
import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.util.Utilities;
import com.google.common.base.Throwables;
@ -74,20 +73,13 @@ public class BitsquareApp extends Application {
bitsquareAppModule = new BitsquareAppModule(env, primaryStage);
injector = Guice.createInjector(bitsquareAppModule);
injector.getInstance(InjectorViewFactory.class).setInjector(injector);
// route uncaught exceptions to a user-facing dialog
Thread.currentThread().setUncaughtExceptionHandler((thread, 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
log.trace("viewLoader.load(MainView.class)");
CachingViewLoader viewLoader = injector.getInstance(CachingViewLoader.class);
View view = viewLoader.load(MainView.class);

View File

@ -23,12 +23,11 @@ import io.bitsquare.arbitration.tomp2p.TomP2PArbitratorModule;
import io.bitsquare.btc.BitcoinModule;
import io.bitsquare.crypto.CryptoModule;
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.tomp2p.TomP2POfferModule;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.persistence.Storage;
import io.bitsquare.p2p.P2PModule;
import io.bitsquare.p2p.tomp2p.TomP2PModule;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.TradeModule;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.Preferences;
@ -63,14 +62,9 @@ class BitsquareAppModule extends BitsquareModule {
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
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(UpdateProcess.class).in(Singleton.class);
install(networkModule());
install(bitcoinModule());
install(cryptoModule());

View File

@ -21,8 +21,7 @@ import io.bitsquare.BitsquareException;
import io.bitsquare.btc.UserAgent;
import io.bitsquare.btc.WalletService;
import io.bitsquare.gui.main.MainView;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.persistence.Storage;
import io.bitsquare.storage.Storage;
import io.bitsquare.util.Utilities;
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(Persistence.DIR_KEY, appDataDir);
setProperty(Persistence.PREFIX_KEY, appName + "_pref");
setProperty(MainView.TITLE_KEY, appName);
}});
}

View File

@ -18,7 +18,7 @@
package io.bitsquare.arbitration;
import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.persistence.Storage;
import io.bitsquare.storage.Storage;
import io.bitsquare.user.User;
import org.bitcoinj.core.Coin;
@ -95,7 +95,7 @@ public class Arbitrator implements Serializable {
public Arbitrator(Storage<Arbitrator> storage, User user) {
this.storage = storage;
Arbitrator persisted = storage.getPersisted(this);
Arbitrator persisted = storage.initAndGetPersisted(this);
if (persisted != null) {
//TODO for mock arbitrator
id = persisted.getName();

View File

@ -17,7 +17,7 @@
package io.bitsquare.btc;
import io.bitsquare.persistence.Storage;
import io.bitsquare.storage.Storage;
import org.bitcoinj.core.Wallet;
import org.bitcoinj.crypto.DeterministicKey;
@ -48,7 +48,7 @@ public class AddressEntryList extends ArrayList<AddressEntry> implements Seriali
public void init(Wallet wallet) {
this.wallet = wallet;
AddressEntryList persisted = storage.getPersisted(this);
AddressEntryList persisted = storage.initAndGetPersisted(this);
if (persisted != null) {
for (AddressEntry addressEntry : persisted) {
addressEntry.setDeterministicKey((DeterministicKey) wallet.findKeyFromPubHash(addressEntry.getPubKeyHash()));

View File

@ -21,7 +21,6 @@ import io.bitsquare.btc.listeners.AddressConfidenceListener;
import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.listeners.TxConfidenceListener;
import io.bitsquare.crypto.SignatureService;
import io.bitsquare.persistence.Persistence;
import org.bitcoinj.core.AbstractWalletEventListener;
import org.bitcoinj.core.Address;
@ -91,11 +90,9 @@ public class WalletService {
private final NetworkParameters params;
private final FeePolicy feePolicy;
private final SignatureService signatureService;
private final Persistence persistence;
private final File walletDir;
private final String walletPrefix;
private final UserAgent userAgent;
private final BitcoinNetwork bitcoinNetwork;
private WalletAppKit walletAppKit;
private Wallet wallet;
@ -111,14 +108,12 @@ public class WalletService {
@Inject
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) {
this.bitcoinNetwork = bitcoinNetwork;
this.addressEntryList = addressEntryList;
this.params = bitcoinNetwork.getParameters();
this.feePolicy = feePolicy;
this.signatureService = signatureService;
this.persistence = persistence;
this.walletDir = walletDir;
this.walletPrefix = walletPrefix;
this.userAgent = userAgent;

View File

@ -21,7 +21,7 @@ import io.bitsquare.common.viewfx.view.View;
import io.bitsquare.common.viewfx.view.ViewPath;
import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.trade.BuyView;
import io.bitsquare.persistence.Storage;
import io.bitsquare.storage.Storage;
import com.google.inject.Inject;
@ -62,7 +62,7 @@ public class Navigation implements Serializable {
public Navigation(Storage<Navigation> storage) {
this.storage = storage;
Navigation persisted = storage.getPersisted(this);
Navigation persisted = storage.initAndGetPersisted(this);
if (persisted != null) {
previousPath = persisted.getPreviousPath();
}

View File

@ -68,9 +68,8 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
private final String title;
@Inject
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, OverlayManager
overlayManager,
Transitions transitions, @Named(MainView.TITLE_KEY) String title) {
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, OverlayManager overlayManager, Transitions transitions,
@Named(MainView.TITLE_KEY) String title) {
super(model);
this.viewLoader = viewLoader;
this.navigation = navigation;

View File

@ -18,7 +18,6 @@
package io.bitsquare.gui.main;
import io.bitsquare.app.UpdateProcess;
import io.bitsquare.arbitration.ArbitratorService;
import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.WalletService;
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.BootstrapState;
import io.bitsquare.p2p.ClientNode;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.User;
import com.google.inject.Inject;
@ -91,27 +88,20 @@ class MainViewModel implements ViewModel {
private final User user;
private final WalletService walletService;
private final ClientNode clientNode;
private ArbitratorService arbitratorService;
private final TradeManager tradeManager;
private UpdateProcess updateProcess;
private final BSFormatter formatter;
private Persistence persistence;
private AccountSettings accountSettings;
@Inject
public MainViewModel(User user, WalletService walletService, ClientNode clientNode,
ArbitratorService arbitratorService,
TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
BSFormatter formatter, Persistence persistence, AccountSettings accountSettings) {
BSFormatter formatter) {
this.user = user;
this.walletService = walletService;
this.clientNode = clientNode;
this.arbitratorService = arbitratorService;
this.tradeManager = tradeManager;
this.updateProcess = updateProcess;
this.formatter = formatter;
this.persistence = persistence;
this.accountSettings = accountSettings;
bitcoinNetworkAsString = bitcoinNetwork.toString();

View File

@ -27,7 +27,6 @@ import io.bitsquare.common.viewfx.view.View;
import io.bitsquare.common.viewfx.view.ViewLoader;
import io.bitsquare.gui.main.account.arbitrator.profile.ArbitratorProfileView;
import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.user.AccountSettings;
import java.util.ArrayList;
@ -54,15 +53,13 @@ public class ArbitratorBrowserView extends ActivatableView<Pane, Void> implement
private final ViewLoader viewLoader;
private final AccountSettings accountSettings;
private final Persistence persistence;
private final ArbitratorService messageService;
@Inject
public ArbitratorBrowserView(CachingViewLoader viewLoader, AccountSettings accountSettings, Persistence persistence,
public ArbitratorBrowserView(CachingViewLoader viewLoader, AccountSettings accountSettings,
ArbitratorService messageService) {
this.viewLoader = viewLoader;
this.accountSettings = accountSettings;
this.persistence = persistence;
this.messageService = messageService;
}

View File

@ -25,7 +25,6 @@ import io.bitsquare.locale.Country;
import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.Region;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.User;
@ -46,7 +45,6 @@ class FiatAccountDataModel implements Activatable, DataModel {
private final User user;
private final AccountSettings accountSettings;
private final Persistence persistence;
final StringProperty title = new SimpleStringProperty();
final StringProperty holderName = new SimpleStringProperty();
@ -68,8 +66,7 @@ class FiatAccountDataModel implements Activatable, DataModel {
@Inject
public FiatAccountDataModel(User user, Persistence persistence, AccountSettings accountSettings) {
this.persistence = persistence;
public FiatAccountDataModel(User user, AccountSettings accountSettings) {
this.user = user;
this.accountSettings = accountSettings;
}

View File

@ -17,15 +17,12 @@
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.DataModel;
import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.fiat.FiatAccountType;
import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.User;
import com.google.inject.Inject;
@ -42,9 +39,6 @@ import javafx.collections.ObservableList;
class IrcAccountDataModel implements Activatable, DataModel {
private final User user;
private final AccountSettings accountSettings;
private final ArbitratorService messageService;
private final Persistence persistence;
final StringProperty nickName = new SimpleStringProperty();
final ObjectProperty<FiatAccountType> type = new SimpleObjectProperty<>();
@ -57,12 +51,8 @@ class IrcAccountDataModel implements Activatable, DataModel {
@Inject
public IrcAccountDataModel(User user, Persistence persistence, AccountSettings accountSettings,
ArbitratorService messageService) {
this.persistence = persistence;
public IrcAccountDataModel(User user) {
this.user = user;
this.accountSettings = accountSettings;
this.messageService = messageService;
}
@Override

View File

@ -22,7 +22,6 @@ import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.common.viewfx.model.DataModel;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.user.User;
import org.bitcoinj.core.Coin;
@ -50,7 +49,6 @@ class RegistrationDataModel implements DataModel {
private final WalletService walletService;
private final User user;
private final Persistence persistence;
private String transactionId;
private AddressEntry addressEntry;
@ -61,11 +59,10 @@ class RegistrationDataModel implements DataModel {
@Inject
public RegistrationDataModel(WalletService walletService, User user, Persistence persistence) {
public RegistrationDataModel(WalletService walletService, User user) {
this.walletService = walletService;
this.user = user;
this.persistence = persistence;
if (walletService != null && walletService.getWallet() != null) {
addressEntry = walletService.getRegistrationAddressEntry();

View File

@ -18,16 +18,13 @@
package io.bitsquare.gui.main.account.content.restrictions;
import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.arbitration.ArbitratorService;
import io.bitsquare.common.viewfx.model.Activatable;
import io.bitsquare.common.viewfx.model.DataModel;
import io.bitsquare.locale.Country;
import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.locale.Region;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.User;
import com.google.inject.Inject;
@ -38,10 +35,7 @@ import javafx.collections.ObservableList;
class RestrictionsDataModel implements Activatable, DataModel {
private final User user;
private final AccountSettings accountSettings;
private final Persistence persistence;
private final ArbitratorService messageService;
final ObservableList<Locale> languageList = FXCollections.observableArrayList();
final ObservableList<Country> countryList = FXCollections.observableArrayList();
@ -52,12 +46,8 @@ class RestrictionsDataModel implements Activatable, DataModel {
@Inject
public RestrictionsDataModel(User user, AccountSettings accountSettings, Persistence persistence,
ArbitratorService messageService) {
this.user = user;
public RestrictionsDataModel(AccountSettings accountSettings) {
this.accountSettings = accountSettings;
this.persistence = persistence;
this.messageService = messageService;
}
@Override

View File

@ -28,7 +28,6 @@ import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.Country;
import io.bitsquare.offer.Direction;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.user.AccountSettings;
import io.bitsquare.user.Preferences;
@ -72,8 +71,6 @@ class CreateOfferDataModel implements Activatable, DataModel {
private final WalletService walletService;
private final AccountSettings accountSettings;
private Preferences preferences;
private final User user;
private final Persistence persistence;
private final BSFormatter formatter;
private final String offerId;
@ -110,14 +107,12 @@ class CreateOfferDataModel implements Activatable, DataModel {
// non private for testing
@Inject
public CreateOfferDataModel(TradeManager tradeManager, WalletService walletService, AccountSettings accountSettings,
Preferences preferences, User user, Persistence persistence,
Preferences preferences, User user,
BSFormatter formatter) {
this.tradeManager = tradeManager;
this.walletService = walletService;
this.accountSettings = accountSettings;
this.preferences = preferences;
this.user = user;
this.persistence = persistence;
this.formatter = formatter;
this.offerId = UUID.randomUUID().toString();
@ -231,7 +226,7 @@ class CreateOfferDataModel implements Activatable, DataModel {
}
void securityDepositInfoDisplayed() {
persistence.write("displaySecurityDepositInfo", false);
preferences.setDisplaySecurityDepositInfo(false);
}
@ -255,15 +250,6 @@ class CreateOfferDataModel implements Activatable, DataModel {
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) {
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
}
@ -281,4 +267,8 @@ class CreateOfferDataModel implements Activatable, DataModel {
fiatCode.set(fiatAccount.getCurrency().getCurrencyCode());
}
}
public Boolean getDisplaySecurityDepositInfo() {
return preferences.getDisplaySecurityDepositInfo();
}
}

View File

@ -147,7 +147,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
@FXML
void onShowPayFundsScreen() {
// TODO deactivate for testing the moment
/* if (model.displaySecurityDepositInfo()) {
/* if (model.getDisplaySecurityDepositInfo()) {
overlayManager.blurContent();
List<Action> actions = new ArrayList<>();
actions.add(new AbstractAction(BSResources.get("shared.close")) {

View File

@ -250,8 +250,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
return formatter;
}
Boolean displaySecurityDepositInfo() {
return dataModel.displaySecurityDepositInfo();
Boolean getDisplaySecurityDepositInfo() {
return dataModel.getDisplaySecurityDepositInfo();
}
private void setupListeners() {

View File

@ -24,7 +24,6 @@ import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.common.viewfx.model.Activatable;
import io.bitsquare.common.viewfx.model.DataModel;
import io.bitsquare.offer.Offer;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.handlers.TradeResultHandler;
import io.bitsquare.user.Preferences;
@ -58,7 +57,6 @@ class TakeOfferDataModel implements Activatable, DataModel {
private final TradeManager tradeManager;
private final WalletService walletService;
private final Preferences preferences;
private final Persistence persistence;
private Offer offer;
private AddressEntry addressEntry;
@ -78,12 +76,10 @@ class TakeOfferDataModel implements Activatable, DataModel {
@Inject
public TakeOfferDataModel(TradeManager tradeManager,
WalletService walletService,
Preferences preferences,
Persistence persistence) {
Preferences preferences) {
this.tradeManager = tradeManager;
this.walletService = walletService;
this.preferences = preferences;
this.persistence = persistence;
offerFeeAsCoin.set(FeePolicy.CREATE_OFFER_FEE);
networkFeeAsCoin.set(FeePolicy.TX_FEE);
@ -174,16 +170,12 @@ class TakeOfferDataModel implements Activatable, DataModel {
return true;
}
Boolean displaySecurityDepositInfo() {
Object securityDepositInfoDisplayedObject = persistence.read("displaySecurityDepositInfo");
if (securityDepositInfoDisplayedObject instanceof Boolean)
return (Boolean) securityDepositInfoDisplayedObject;
else
return true;
Boolean getDisplaySecurityDepositInfo() {
return preferences.getDisplaySecurityDepositInfo();
}
void securityDepositInfoDisplayed() {
persistence.write("displaySecurityDepositInfo", false);
preferences.setDisplaySecurityDepositInfo(false);
}

View File

@ -319,7 +319,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private void setupPaymentScreen() {
// TODO deactivate for testing the moment
/* if (model.displaySecurityDepositInfo()) {
/* if (model.getDisplaySecurityDepositInfo()) {
overlayManager.blurContent();
List<Action> actions = new ArrayList<>();
actions.add(new AbstractAction(BSResources.get("shared.close")) {

View File

@ -359,8 +359,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
return paymentLabel;
}
Boolean displaySecurityDepositInfo() {
return dataModel.displaySecurityDepositInfo();
Boolean getDisplaySecurityDepositInfo() {
return dataModel.getDisplaySecurityDepositInfo();
}

View File

@ -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);
}
}
}

View 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;
}
}

View 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();
}
}
}

View 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;
}
}

View File

@ -33,7 +33,6 @@ import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.handlers.TradeResultHandler;
import io.bitsquare.trade.handlers.TransactionResultHandler;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityModel;
@ -51,17 +50,16 @@ import io.bitsquare.user.User;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
import java.io.Serializable;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
@ -72,7 +70,6 @@ public class TradeManager {
private final User user;
private final AccountSettings accountSettings;
private final Persistence persistence;
private final MessageService messageService;
private MailboxService mailboxService;
private final AddressService addressService;
@ -81,14 +78,15 @@ public class TradeManager {
private final SignatureService signatureService;
private EncryptionService<MailboxMessage> encryptionService;
private final OfferBookService offerBookService;
private File storageDir;
private final Map<String, TakerAsSellerProtocol> takerAsSellerProtocolMap = new HashMap<>();
private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocolMap = new HashMap<>();
private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>();
private final ObservableList<Trade> openOfferTrades = FXCollections.observableArrayList();
private final ObservableList<Trade> pendingTrades = FXCollections.observableArrayList();
private final ObservableList<Trade> closedTrades = FXCollections.observableArrayList();
private final TradesList openOfferTrades;
private final TradesList pendingTrades;
private final TradesList closedTrades;
private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>();
@ -97,13 +95,15 @@ public class TradeManager {
///////////////////////////////////////////////////////////////////////////////////////////
@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,
WalletService walletService, SignatureService signatureService, EncryptionService<MailboxMessage> encryptionService,
OfferBookService offerBookService) {
OfferBookService offerBookService, @Named("storage.dir") File storageDir) {
this.user = user;
this.accountSettings = accountSettings;
this.persistence = persistence;
this.openOfferTrades = openOfferTrades;
this.pendingTrades = pendingTrades;
this.closedTrades = closedTrades;
this.messageService = messageService;
this.mailboxService = mailboxService;
this.addressService = addressService;
@ -112,19 +112,7 @@ public class TradeManager {
this.signatureService = signatureService;
this.encryptionService = encryptionService;
this.offerBookService = offerBookService;
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);
}
this.storageDir = storageDir;
}
@ -198,7 +186,6 @@ public class TradeManager {
Trade trade = new Trade(offer);
trade.setLifeCycleState(Trade.LifeCycleState.OPEN_OFFER);
openOfferTrades.add(trade);
persistOpenOfferTrades();
createOffererAsBuyerProtocol(trade);
resultHandler.handleResult(transaction);
@ -265,7 +252,7 @@ public class TradeManager {
// TODO remove if check when persistence is impl.
if (offererAsBuyerProtocolMap.containsKey(tradeId)) {
offererAsBuyerProtocolMap.get(tradeId).onFiatPaymentStarted();
persistPendingTrades();
// persistPendingTrades();
}
}
@ -276,9 +263,7 @@ public class TradeManager {
public void onWithdrawAtTradeCompleted(Trade trade) {
trade.setLifeCycleState(Trade.LifeCycleState.COMPLETED);
pendingTrades.remove(trade);
persistPendingTrades();
closedTrades.add(trade);
persistClosedTrades();
removeFromProtocolMap(trade);
}
@ -297,15 +282,15 @@ public class TradeManager {
///////////////////////////////////////////////////////////////////////////////////////////
public ObservableList<Trade> getOpenOfferTrades() {
return openOfferTrades;
return openOfferTrades.getObservableList();
}
public ObservableList<Trade> getPendingTrades() {
return pendingTrades;
return pendingTrades.getObservableList();
}
public ObservableList<Trade> getClosedTrades() {
return closedTrades;
return closedTrades.getObservableList();
}
@ -329,12 +314,10 @@ public class TradeManager {
if (result.isPresent()) {
Trade trade = result.get();
openOfferTrades.remove(trade);
persistOpenOfferTrades();
if (isCancelRequest) {
trade.setLifeCycleState(Trade.LifeCycleState.CANCELED);
closedTrades.add(trade);
persistClosedTrades();
}
}
@ -375,7 +358,6 @@ public class TradeManager {
trade.setTradingPeer(peer);
trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
pendingTrades.add(trade);
persistPendingTrades();
TakerAsSellerProtocol sellerTakesOfferProtocol = createTakerAsSellerProtocol(trade);
//trade.setProtocol(sellerTakesOfferProtocol);
@ -397,7 +379,7 @@ public class TradeManager {
case FIAT_PAYMENT_STARTED:
case FIAT_PAYMENT_RECEIVED:
case PAYOUT_PUBLISHED:
persistPendingTrades();
// persistPendingTrades();
break;
case MESSAGE_SENDING_FAILED:
case FAULT:
@ -418,7 +400,7 @@ public class TradeManager {
blockChainService,
signatureService,
user,
persistence);
storageDir);
TakerAsSellerProtocol protocol = new TakerAsSellerProtocol(model);
takerAsSellerProtocolMap.put(trade.getId(), protocol);
@ -440,7 +422,7 @@ public class TradeManager {
blockChainService,
signatureService,
user,
persistence);
storageDir);
// TODO check, remove listener
@ -450,7 +432,7 @@ public class TradeManager {
case INIT:
break;
case TAKE_OFFER_FEE_TX_CREATED:
persistPendingTrades();
// persistPendingTrades();
break;
case DEPOSIT_PUBLISHED:
removeOpenOffer(trade.getOffer(),
@ -459,13 +441,12 @@ public class TradeManager {
false);
model.trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
pendingTrades.add(trade);
persistPendingTrades();
break;
case DEPOSIT_CONFIRMED:
case FIAT_PAYMENT_STARTED:
case FIAT_PAYMENT_RECEIVED:
case PAYOUT_PUBLISHED:
persistPendingTrades();
// persistPendingTrades();
break;
case TAKE_OFFER_FEE_PUBLISH_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));
}
}

View 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;
}
}

View File

@ -26,7 +26,6 @@ import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import java.io.Serializable;
@ -45,7 +44,6 @@ public class SharedTradeModel extends SharedTaskModel implements Serializable {
transient public final WalletService walletService;
transient public final BlockChainService blockChainService;
transient public final SignatureService signatureService;
transient protected final Persistence persistence;
transient public MailboxMessage mailboxMessage;
@ -62,15 +60,13 @@ public class SharedTradeModel extends SharedTaskModel implements Serializable {
MailboxService mailboxService,
WalletService walletService,
BlockChainService blockChainService,
SignatureService signatureService,
Persistence persistence) {
SignatureService signatureService) {
this.offer = offer;
this.messageService = messageService;
this.mailboxService = mailboxService;
this.walletService = walletService;
this.blockChainService = blockChainService;
this.signatureService = signatureService;
this.persistence = persistence;
id = offer.getId();
tradeWalletService = walletService.getTradeWalletService();

View File

@ -22,11 +22,12 @@ import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.SignatureService;
import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.SharedTradeModel;
import io.bitsquare.user.User;
import java.io.File;
import java.io.Serializable;
import org.slf4j.Logger;
@ -34,9 +35,11 @@ import org.slf4j.LoggerFactory;
public class OffererAsBuyerModel extends SharedTradeModel implements Serializable {
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;
public final Taker taker;
public final Offerer offerer;
@ -50,26 +53,25 @@ public class OffererAsBuyerModel extends SharedTradeModel implements Serializabl
BlockChainService blockChainService,
SignatureService signatureService,
User user,
Persistence persistence) {
File storageDir) {
super(trade.getOffer(),
messageService,
mailboxService,
walletService,
blockChainService,
signatureService,
persistence);
signatureService);
this.trade = trade;
Serializable serializable = persistence.read(this, "BuyerAsOffererModel_" + id);
if (serializable instanceof OffererAsBuyerModel) {
OffererAsBuyerModel persistedModel = (OffererAsBuyerModel) serializable;
this.storage = new Storage<>(storageDir);
OffererAsBuyerModel persisted = storage.initAndGetPersisted(this, getClass().getSimpleName() + id);
if (persisted != null) {
log.debug("Model reconstructed form persisted model.");
setTakeOfferFeeTxId(persistedModel.takeOfferFeeTxId);
setTakeOfferFeeTxId(persisted.takeOfferFeeTxId);
taker = persistedModel.taker;
offerer = persistedModel.offerer;
taker = persisted.taker;
offerer = persisted.offerer;
}
else {
taker = new Taker();
@ -90,13 +92,13 @@ public class OffererAsBuyerModel extends SharedTradeModel implements Serializabl
// Get called form taskRunner after each completed task
@Override
public void persist() {
persistence.write(this, "BuyerAsOffererModel_" + id, this);
storage.save();
}
@Override
public void onComplete() {
// Just in case of successful completion we delete our persisted object
persistence.remove(this, "BuyerAsOffererModel_" + id);
storage.remove();
}
public String getTakeOfferFeeTxId() {

View File

@ -22,13 +22,14 @@ import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.SignatureService;
import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.SharedTradeModel;
import io.bitsquare.user.User;
import org.bitcoinj.core.Transaction;
import java.io.File;
import java.io.Serializable;
import org.slf4j.Logger;
@ -36,9 +37,11 @@ import org.slf4j.LoggerFactory;
public class TakerAsSellerModel extends SharedTradeModel implements Serializable {
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 Offerer offerer;
@ -52,28 +55,27 @@ public class TakerAsSellerModel extends SharedTradeModel implements Serializable
WalletService walletService,
BlockChainService blockChainService,
SignatureService signatureService,
User user,
Persistence persistence) {
User user,
File storageDir) {
super(trade.getOffer(),
messageService,
mailboxService,
walletService,
blockChainService,
signatureService,
persistence);
signatureService);
this.trade = trade;
this.storage = new Storage<>(storageDir);
Serializable serializable = persistence.read(this, "SellerAsTakerModel_" + id);
if (serializable instanceof TakerAsSellerModel) {
TakerAsSellerModel persistedModel = (TakerAsSellerModel) serializable;
log.debug("Model reconstructed form persisted model.");
TakerAsSellerModel persisted = storage.initAndGetPersisted(this, getClass().getSimpleName() + id);
if (persisted != null) {
log.debug("Model reconstructed from persisted model.");
setTakeOfferFeeTx(persistedModel.getTakeOfferFeeTx());
setPayoutTx(persistedModel.payoutTx);
setTakeOfferFeeTx(persisted.getTakeOfferFeeTx());
setPayoutTx(persisted.payoutTx);
taker = persistedModel.taker;
offerer = persistedModel.offerer;
taker = persisted.taker;
offerer = persisted.offerer;
}
else {
taker = new Taker();
@ -93,13 +95,13 @@ public class TakerAsSellerModel extends SharedTradeModel implements Serializable
// Get called form taskRunner after each completed task
@Override
public void persist() {
persistence.write(this, "SellerAsTakerModel_" + id, this);
storage.save();
}
@Override
public void onComplete() {
// Just in case of successful completion we delete our persisted object
persistence.remove(this, "SellerAsTakerModel_" + id);
storage.remove();
}

View File

@ -21,7 +21,7 @@ import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.locale.Country;
import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.persistence.Storage;
import io.bitsquare.storage.Storage;
import org.bitcoinj.core.Coin;
@ -44,6 +44,7 @@ public class AccountSettings implements Serializable {
private List<Locale> acceptedLanguageLocales = new ArrayList<>();
private List<Country> acceptedCountryLocales = new ArrayList<>();
private List<Arbitrator> acceptedArbitrators = new ArrayList<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -53,7 +54,7 @@ public class AccountSettings implements Serializable {
public AccountSettings(Storage<AccountSettings> storage, Arbitrator defaultArbitrator) {
this.storage = storage;
AccountSettings persisted = storage.getPersisted(this);
AccountSettings persisted = storage.initAndGetPersisted(this);
if (persisted != null) {
acceptedLanguageLocales = persisted.getAcceptedLanguageLocales();
acceptedCountryLocales = persisted.getAcceptedCountries();
@ -106,7 +107,7 @@ public class AccountSettings implements Serializable {
storage.save();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -17,7 +17,7 @@
package io.bitsquare.user;
import io.bitsquare.persistence.Storage;
import io.bitsquare.storage.Storage;
import org.bitcoinj.utils.MonetaryFormat;
@ -53,13 +53,13 @@ public class Preferences implements Serializable {
private String _btcDenomination = MonetaryFormat.CODE_BTC;
private Boolean _useAnimations = true;
private Boolean _useEffects = true;
private Boolean displaySecurityDepositInfo = true;
// Observable wrappers
transient private final StringProperty btcDenomination = new SimpleStringProperty(_btcDenomination);
transient private final BooleanProperty useAnimations = new SimpleBooleanProperty(_useAnimations);
transient private final BooleanProperty useEffects = new SimpleBooleanProperty(_useEffects);
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@ -68,11 +68,12 @@ public class Preferences implements Serializable {
public Preferences(Storage<Preferences> storage) {
this.storage = storage;
Preferences persisted = storage.getPersisted(this);
Preferences persisted = storage.initAndGetPersisted(this);
if (persisted != null) {
setBtcDenomination(persisted._btcDenomination);
setUseAnimations(persisted._useAnimations);
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
@ -107,6 +108,11 @@ public class Preferences implements Serializable {
this.useEffects.set(useEffects);
}
public void setDisplaySecurityDepositInfo(Boolean displaySecurityDepositInfo) {
this.displaySecurityDepositInfo = displaySecurityDepositInfo;
storage.save();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
@ -124,6 +130,10 @@ public class Preferences implements Serializable {
return useAnimations.get();
}
public Boolean getDisplaySecurityDepositInfo() {
return displaySecurityDepositInfo;
}
public StringProperty btcDenominationProperty() {
return btcDenomination;
}
@ -135,4 +145,6 @@ public class Preferences implements Serializable {
public BooleanProperty useEffectsProperty() {
return useEffects;
}
}

View File

@ -20,7 +20,7 @@ package io.bitsquare.user;
import io.bitsquare.crypto.EncryptionService;
import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.gui.components.Popups;
import io.bitsquare.persistence.Storage;
import io.bitsquare.storage.Storage;
import java.io.Serializable;
@ -72,7 +72,7 @@ public class User implements Serializable {
this.storage = storage;
this.encryptionService = encryptionService;
User persisted = storage.getPersisted(this);
User persisted = storage.initAndGetPersisted(this);
if (persisted != null) {
p2pSigKeyPair = persisted.getP2pSigKeyPair();
p2pEncryptKeyPair = persisted.getP2pEncryptKeyPair();

View File

@ -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));
}
}

View File

@ -44,7 +44,7 @@ public class CreateOfferViewModelTest {
BSFormatter formatter = new BSFormatter(new User());
formatter.setLocale(Locale.US);
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);
}

View File

@ -17,38 +17,21 @@
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.offer.OfferBookService;
import io.bitsquare.offer.tomp2p.TomP2POfferBookService;
import io.bitsquare.p2p.BootstrapState;
import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Node;
import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
import io.bitsquare.p2p.tomp2p.TomP2PMessageService;
import io.bitsquare.p2p.tomp2p.TomP2PNode;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.user.User;
import org.bitcoinj.core.Address;
import org.bitcoinj.utils.Threading;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
/**
* That test is ignored for automated testing as it needs custom setup.
* <p/>
@ -72,22 +55,19 @@ public class PlaceOfferProtocolTest {
private TomP2PNode tomP2PNode;
private BootstrappedPeerBuilder bootstrappedPeerBuilder;
@Before
/* @Before
public void setup() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
dir.mkdirs();
Persistence persistence = new Persistence(dir, "prefs");
persistence.init();
// messageService
Node bootstrapNode = Node.at("localhost", "127.0.0.1");
User user = new User();
/* try {
*//* try {
user.initPersistedObject();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}*/
}*//*
bootstrappedPeerBuilder = new BootstrappedPeerBuilder(Node.DEFAULT_PORT, false, bootstrapNode, "<unspecified>");
tomP2PNode = new TomP2PNode(bootstrappedPeerBuilder);
messageService = new TomP2PMessageService(tomP2PNode, null, null, null);
@ -153,7 +133,7 @@ public class PlaceOfferProtocolTest {
public void shutDown() throws IOException, InterruptedException {
walletService.shutDown();
bootstrappedPeerBuilder.shutDown();
}
}*/
/* @Test
public void validateOfferTest() throws InterruptedException {