diff --git a/.gitignore b/.gitignore index 064cdbf0b877fcaa6cd1c833b345298c993bffe4..02efb3d8bd47247c48a1e52ba49b6ba691e81bb1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .idea/ *.iml .DS_Store +snapshots \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1a37142e64dcde75bec6b23cb852f4fcb0089deb..b32c12551b51c005f987f598af3a81f9e4c60ea5 100644 --- a/pom.xml +++ b/pom.xml @@ -56,5 +56,12 @@ <artifactId>reflections</artifactId> <version>0.9.10</version> </dependency> + <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.7</version> + </dependency> + </dependencies> </project> \ No newline at end of file diff --git a/src/main/java/fucoin/AbstractNode.java b/src/main/java/fucoin/AbstractNode.java index 9777980aa3f1880b274b162f251bf001d5cb61ef..fb3d50ee5f91b7ebeeeb4d0482d68b61293a4635 100644 --- a/src/main/java/fucoin/AbstractNode.java +++ b/src/main/java/fucoin/AbstractNode.java @@ -3,10 +3,18 @@ package fucoin; import akka.actor.ActorRef; import akka.actor.Address; import akka.actor.UntypedActor; +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import fucoin.actions.transaction.ActionGetAmount; import fucoin.wallet.AbstractWallet; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; import java.io.Serializable; +import java.time.LocalTime; import java.util.HashMap; public abstract class AbstractNode extends UntypedActor implements Serializable { @@ -68,4 +76,28 @@ public abstract class AbstractNode extends UntypedActor implements Serializable public HashMap<String, ActorRef> getKnownNeighbors() { return knownNeighbors; } -} \ No newline at end of file + + public void createDump(String name, String content, LocalTime time) { + String filename = "snapshots/Dump_" + time.getNano() + "_" + name + ".json"; + + try { + File theDir = new File("snapshots"); + + // if the directory does not exist, create it + if (!theDir.exists()) { + theDir.mkdir(); + } + + File myFile = new File(filename); + myFile.createNewFile(); + FileOutputStream fOut = new FileOutputStream(myFile); + OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut); + myOutWriter.append(content); + myOutWriter.close(); + fOut.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/fucoin/actions/transaction/SuperVisorAction.java b/src/main/java/fucoin/actions/SuperVisorAction.java similarity index 80% rename from src/main/java/fucoin/actions/transaction/SuperVisorAction.java rename to src/main/java/fucoin/actions/SuperVisorAction.java index 19e93a885d76155f28378790bfd358a52c7a281c..1cae3ead4d7d146ee5913e0e7e2960dc52da2aed 100644 --- a/src/main/java/fucoin/actions/transaction/SuperVisorAction.java +++ b/src/main/java/fucoin/actions/SuperVisorAction.java @@ -1,4 +1,4 @@ -package fucoin.actions.transaction; +package fucoin.actions; import fucoin.actions.Action; import fucoin.supervisor.SuperVisorImpl; diff --git a/src/main/java/fucoin/actions/control/ActionAnnounceWalletCreation.java b/src/main/java/fucoin/actions/control/ActionAnnounceWalletCreation.java index ee897f78bf0f3926ad283762364c360fb3096010..205424d29ff48760ec2a3096b7ca8f173518754a 100644 --- a/src/main/java/fucoin/actions/control/ActionAnnounceWalletCreation.java +++ b/src/main/java/fucoin/actions/control/ActionAnnounceWalletCreation.java @@ -2,7 +2,7 @@ package fucoin.actions.control; import akka.actor.ActorRef; import akka.actor.UntypedActorContext; -import fucoin.actions.transaction.SuperVisorAction; +import fucoin.actions.SuperVisorAction; import fucoin.supervisor.SuperVisorImpl; /** diff --git a/src/main/java/fucoin/actions/control/ActionCreateSuperVisorSnapshot.java b/src/main/java/fucoin/actions/control/ActionCreateSuperVisorSnapshot.java new file mode 100644 index 0000000000000000000000000000000000000000..7bfa9d159ab8a987152961ad9701ea59b9f88adb --- /dev/null +++ b/src/main/java/fucoin/actions/control/ActionCreateSuperVisorSnapshot.java @@ -0,0 +1,26 @@ +package fucoin.actions.control; + +import akka.actor.ActorRef; +import akka.actor.UntypedActorContext; +import fucoin.actions.SuperVisorAction; +import fucoin.supervisor.SuperVisorImpl; + +import java.time.LocalTime; + +public class ActionCreateSuperVisorSnapshot extends SuperVisorAction { + /** + * Timestamp for the file name + */ + private final LocalTime time; + + + public ActionCreateSuperVisorSnapshot(LocalTime time) { + this.time = time; + } + + + @Override + protected void onAction(ActorRef sender, ActorRef self, UntypedActorContext context, SuperVisorImpl abstractNode) { + abstractNode.createDump(time); + } +} diff --git a/src/main/java/fucoin/actions/control/ActionCreateWalletSnapshot.java b/src/main/java/fucoin/actions/control/ActionCreateWalletSnapshot.java new file mode 100644 index 0000000000000000000000000000000000000000..f730419483b8709a32e036160f6e97fd5807b07d --- /dev/null +++ b/src/main/java/fucoin/actions/control/ActionCreateWalletSnapshot.java @@ -0,0 +1,29 @@ +package fucoin.actions.control; + +import akka.actor.ActorRef; +import akka.actor.UntypedActorContext; +import com.google.gson.Gson; +import fucoin.actions.ClientAction; +import fucoin.wallet.AbstractWallet; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.time.LocalTime; + +public class ActionCreateWalletSnapshot extends ClientAction { + /** + * Timestamp for the file name + */ + private final LocalTime time; + + + public ActionCreateWalletSnapshot(LocalTime time) { + this.time = time; + } + + @Override + protected void onAction(ActorRef sender, ActorRef self, UntypedActorContext context, AbstractWallet abstractNode) { + abstractNode.createDump(time); + } +} diff --git a/src/main/java/fucoin/actions/join/ServerActionJoin.java b/src/main/java/fucoin/actions/join/ServerActionJoin.java index 18a1cf66174035d224dbacb0744f4fd851218034..f203170c85447bbe1f411042f85f4cb853ba655c 100644 --- a/src/main/java/fucoin/actions/join/ServerActionJoin.java +++ b/src/main/java/fucoin/actions/join/ServerActionJoin.java @@ -3,7 +3,7 @@ package fucoin.actions.join; import akka.actor.ActorRef; import akka.actor.UntypedActorContext; import fucoin.actions.transaction.ActionInvokeDistributedCommittedTransfer; -import fucoin.actions.transaction.SuperVisorAction; +import fucoin.actions.SuperVisorAction; import fucoin.supervisor.SuperVisorImpl; /** diff --git a/src/main/java/fucoin/actions/statistics/ActionInterchangeState.java b/src/main/java/fucoin/actions/statistics/ActionInterchangeState.java new file mode 100644 index 0000000000000000000000000000000000000000..8d06ab53f8eb3186723c1acf32a451f49b5f4b2a --- /dev/null +++ b/src/main/java/fucoin/actions/statistics/ActionInterchangeState.java @@ -0,0 +1,26 @@ +package fucoin.actions.statistics; + +import akka.actor.ActorRef; +import akka.actor.UntypedActorContext; +import fucoin.actions.ClientAction; +import fucoin.wallet.AbstractWallet; +import fucoin.wallet.WalletStatistics; + +/** + * Wallet A sends a Wallet B current Amount and statistic's. + * Wallet B updates his statistic's. + * @see WalletStatistics + */ +public class ActionInterchangeState extends ClientAction { + + private final int currentAmount; + + public ActionInterchangeState(int currentAmount) { + this.currentAmount = currentAmount; + } + + @Override + protected void onAction(ActorRef sender, ActorRef self, UntypedActorContext context, AbstractWallet abstractNode) { + abstractNode.getStatistics().update(currentAmount); + } +} diff --git a/src/main/java/fucoin/actions/transaction/CoordinatorTransaction.java b/src/main/java/fucoin/actions/transaction/CoordinatorTransaction.java index b28dc56f2174166cb81abda28ba6f11540bb3c87..d9844e08bb3e603904c1434316c95e2fe20faa05 100644 --- a/src/main/java/fucoin/actions/transaction/CoordinatorTransaction.java +++ b/src/main/java/fucoin/actions/transaction/CoordinatorTransaction.java @@ -2,6 +2,7 @@ package fucoin.actions.transaction; import akka.actor.ActorRef; import akka.actor.UntypedActorContext; +import fucoin.actions.SuperVisorAction; import fucoin.supervisor.SuperVisorImpl; public abstract class CoordinatorTransaction extends SuperVisorAction { diff --git a/src/main/java/fucoin/configurations/AbstractConfiguration.java b/src/main/java/fucoin/configurations/AbstractConfiguration.java index 8493704af98937d47688ad0b57323902394e0d8d..8fb57e72a990c2e8d60b109e78b24b7d2d729f0b 100644 --- a/src/main/java/fucoin/configurations/AbstractConfiguration.java +++ b/src/main/java/fucoin/configurations/AbstractConfiguration.java @@ -28,11 +28,11 @@ import java.util.concurrent.ThreadLocalRandom; */ public abstract class AbstractConfiguration extends AbstractNode { - private ActorRef superVisor; + protected ActorRef superVisor; - private final List<ActorRef> activeActors = new ArrayList<>(); + protected final List<ActorRef> activeActors = new ArrayList<>(); - private Timeout timeout = new Timeout(Duration.create(10, "seconds")); + protected Timeout timeout = new Timeout(Duration.create(10, "seconds")); private int remainingTransactions; diff --git a/src/main/java/fucoin/configurations/MassWalletConfiguration.java b/src/main/java/fucoin/configurations/MassWalletConfiguration.java index fcaaadf3cea173e7b0cf3fa6fc038fbee5dcecae..b0f222fa293ed4111217b79bcd1c57eb617d490d 100644 --- a/src/main/java/fucoin/configurations/MassWalletConfiguration.java +++ b/src/main/java/fucoin/configurations/MassWalletConfiguration.java @@ -11,7 +11,7 @@ public class MassWalletConfiguration extends AbstractConfiguration { public void run() { initSupervisor(); try { - spawnWallets(200, false); + spawnWallets(20, false); System.out.println("Wallet spawning done!"); } catch (Exception e) { System.out.println("Wallet spawning timed out!"); diff --git a/src/main/java/fucoin/configurations/StatisticsWalletConfiguration.java b/src/main/java/fucoin/configurations/StatisticsWalletConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..5a5fc7cbe15c199502176c254a473a27ed9f4928 --- /dev/null +++ b/src/main/java/fucoin/configurations/StatisticsWalletConfiguration.java @@ -0,0 +1,102 @@ +package fucoin.configurations; + +import akka.actor.ActorRef; +import akka.pattern.Patterns; +import fucoin.actions.control.ActionCreateSuperVisorSnapshot; +import fucoin.actions.control.ActionCreateWalletSnapshot; +import fucoin.actions.statistics.ActionInterchangeState; +import fucoin.actions.transaction.ActionGetAmount; +import fucoin.actions.transaction.ActionGetAmountAnswer; +import fucoin.configurations.internal.ConfigurationName; +import scala.concurrent.Await; +import scala.concurrent.Future; + +import java.time.LocalTime; +import java.util.Collections; +import java.util.List; + +/** + * This configuration spawns 200 wallets to demonstrate the spawning of many headless wallets + */ +@ConfigurationName("Statistics") +public class StatisticsWalletConfiguration extends AbstractConfiguration { + /** + * Spawn the number of wallets. + */ + private final static int sNumberOfWallets = 10; + + private int numberOfInterchangeStatistic = 20; + + @Override + public void run() { + initSupervisor(); + try { + spawnWallets(sNumberOfWallets, false); + System.out.println("Wallet spawning done!"); + } catch (Exception e) { + System.out.println("Wallet spawning timed out!"); + } + + randomTransactions(10, 10); + + try { + //Busy wait + while (activeActors.size() < sNumberOfWallets) { + + } + try { + Thread.sleep(4000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("=========== Stats runs"); + interchangeStatistic(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void interchangeStatistic() throws Exception { + if (!(numberOfInterchangeStatistic > 0)) { + //stop interchange statistics + return; + } + + List<ActorRef> wallets = wallets(); + Collections.shuffle(wallets); + + ActorRef sender = wallets.get(0); + ActorRef recipient = wallets.get(1); + + Future<Object> future = Patterns.ask(sender, new ActionGetAmount(), timeout); + ActionGetAmountAnswer answer = (ActionGetAmountAnswer) Await.result(future, timeout.duration()); + + recipient.tell(new ActionInterchangeState(answer.amount), sender); + + numberOfInterchangeStatistic--; + if (numberOfInterchangeStatistic == 0) { + createDump(); + } else { + interchangeStatistic(); + } + } + + /** + * Create a Dump of the SuperVisor and all active Wallets. + */ + private void createDump() { + System.out.println("Create Dumps"); + LocalTime time = LocalTime.now(); + superVisor.tell(new ActionCreateSuperVisorSnapshot(time), superVisor); + for (ActorRef wallet: activeActors) { + wallet.tell(new ActionCreateWalletSnapshot(time), wallet); + } + } + + @Override + public void onReceive(Object message) { + super.onReceive(message); + } + + +} diff --git a/src/main/java/fucoin/gui/SuperVisorGuiControl.java b/src/main/java/fucoin/gui/SuperVisorGuiControl.java index b8b29e555f906ae8de36d8b1edd625fe7acc824d..9c2fbefc259308aeb401dfabba21389732d7eb36 100644 --- a/src/main/java/fucoin/gui/SuperVisorGuiControl.java +++ b/src/main/java/fucoin/gui/SuperVisorGuiControl.java @@ -1,5 +1,7 @@ package fucoin.gui; +import fucoin.supervisor.AmountTableModel; + public interface SuperVisorGuiControl extends TransactionLogger { /** @@ -7,6 +9,8 @@ public interface SuperVisorGuiControl extends TransactionLogger { */ void onLeave(); - public void updateTable(String address, String name, int amount); + void updateTable(String address, String name, int amount); + + AmountTableModel getTable(); } diff --git a/src/main/java/fucoin/gui/SuperVisorGuiControlImpl.java b/src/main/java/fucoin/gui/SuperVisorGuiControlImpl.java index b0c9a125e68a2cadce66c418126fa563e9aec6bc..f5d75e4911692e610dbe688e5cbf4d46fcf287b8 100644 --- a/src/main/java/fucoin/gui/SuperVisorGuiControlImpl.java +++ b/src/main/java/fucoin/gui/SuperVisorGuiControlImpl.java @@ -48,6 +48,11 @@ public class SuperVisorGuiControlImpl implements SuperVisorGuiControl { this.amountTableModel.updateTable(address, name, amount); } + @Override + public AmountTableModel getTable() { + return this.amountTableModel; + } + private void log(LogMessage logMessage) { if (logActive) { threadGUI.log(logMessage); diff --git a/src/main/java/fucoin/supervisor/ActionUpdateQueue.java b/src/main/java/fucoin/supervisor/ActionUpdateQueue.java index e15ce4b8c757d5e7c7882e4149a5923b56fbc609..81ac1a5f49b9147080f2b859419a24651f3fec43 100644 --- a/src/main/java/fucoin/supervisor/ActionUpdateQueue.java +++ b/src/main/java/fucoin/supervisor/ActionUpdateQueue.java @@ -3,7 +3,7 @@ package fucoin.supervisor; import akka.actor.ActorRef; import akka.actor.UntypedActorContext; import fucoin.actions.transaction.ActionCommitDistributedCommittedTransfer; -import fucoin.actions.transaction.SuperVisorAction; +import fucoin.actions.SuperVisorAction; import java.util.List; diff --git a/src/main/java/fucoin/supervisor/AmountTableModel.java b/src/main/java/fucoin/supervisor/AmountTableModel.java index 80378e916040dac5fc3fb816250206cefb424ff5..8ed4172e51e994d030b6cb90087f16f537c37c59 100644 --- a/src/main/java/fucoin/supervisor/AmountTableModel.java +++ b/src/main/java/fucoin/supervisor/AmountTableModel.java @@ -1,10 +1,10 @@ package fucoin.supervisor; -import javax.swing.*; import javax.swing.table.DefaultTableModel; +import java.io.Serializable; import java.util.Vector; -public class AmountTableModel extends DefaultTableModel { +public class AmountTableModel extends DefaultTableModel implements Serializable { public AmountTableModel() { super(new Object[]{"Address", "Name", "Amount"}, 0); diff --git a/src/main/java/fucoin/supervisor/SuperVisorImpl.java b/src/main/java/fucoin/supervisor/SuperVisorImpl.java index 8b4ec173daab141c1172927ecaf34da83d5376c0..9b9778b8e3d462eba7cb257db18e4dbe8f94c662 100644 --- a/src/main/java/fucoin/supervisor/SuperVisorImpl.java +++ b/src/main/java/fucoin/supervisor/SuperVisorImpl.java @@ -2,17 +2,19 @@ package fucoin.supervisor; import akka.actor.ActorRef; import akka.actor.Props; +import com.google.gson.Gson; import fucoin.actions.Action; import fucoin.actions.control.ActionWalletCreationDone; import fucoin.actions.persist.ActionInvokeUpdate; import fucoin.actions.transaction.ActionGetAmountAnswer; import fucoin.actions.transaction.ActionNotifyObserver; -import fucoin.actions.transaction.SuperVisorAction; +import fucoin.actions.SuperVisorAction; import fucoin.gui.SuperVisorGuiControl; import fucoin.AbstractNode; import fucoin.gui.TransactionLogger; import javax.swing.*; +import java.time.LocalTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -23,12 +25,12 @@ public class SuperVisorImpl extends AbstractNode implements TransactionLogger { //private AmountTableModel amountTableModel; - private Map<Long, DistributedCommittedTransferRequest> requestQueue; + private transient Map<Long, DistributedCommittedTransferRequest> requestQueue; - private SuperVisorGuiControl gui; + private transient SuperVisorGuiControl gui; private int pendingBankCommits = 0; - private ActorRef bankCommitObserver = null; + private transient ActorRef bankCommitObserver = null; public SuperVisorImpl() { @@ -167,4 +169,9 @@ public class SuperVisorImpl extends AbstractNode implements TransactionLogger { public void setBankCommitObserver(ActorRef bankCommitObserver) { this.bankCommitObserver = bankCommitObserver; } + + public void createDump(LocalTime time) { + String str = new Gson().toJson(this.gui.getTable()); + super.createDump("SuperVisor", str, time); + } } diff --git a/src/main/java/fucoin/wallet/AbstractWallet.java b/src/main/java/fucoin/wallet/AbstractWallet.java index 882c2a547b9060a576589281eaaea8c9d3e078ba..ec1e0aee186574cef980f97a4850dbe6b474d949 100644 --- a/src/main/java/fucoin/wallet/AbstractWallet.java +++ b/src/main/java/fucoin/wallet/AbstractWallet.java @@ -6,12 +6,17 @@ import fucoin.gui.TransactionLogger; import scala.concurrent.Future; import java.io.Serializable; +import java.time.LocalTime; /** * */ public abstract class AbstractWallet extends AbstractNode implements Serializable, TransactionLogger { + /** + * + */ + protected WalletStatistics statistics = new WalletStatistics(); /** * Currently amount of this wallet */ @@ -77,6 +82,14 @@ public abstract class AbstractWallet extends AbstractNode implements Serializabl */ public abstract void setActive(boolean isActive); + public WalletStatistics getStatistics() { + return statistics; + } + + public void setStatistics(WalletStatistics statistics) { + this.statistics = statistics; + } + /** * Returns the * @@ -114,4 +127,6 @@ public abstract class AbstractWallet extends AbstractNode implements Serializabl * @param observer */ public abstract void send(String address, int amount, ActorRef observer); + + public abstract void createDump(LocalTime time); } diff --git a/src/main/java/fucoin/wallet/WalletImpl.java b/src/main/java/fucoin/wallet/WalletImpl.java index 951a1519f91fa609e143fce37a554279eebcecbb..f25e0a0e5fc5caf76ede09f93b7e03adf4e5ddd9 100644 --- a/src/main/java/fucoin/wallet/WalletImpl.java +++ b/src/main/java/fucoin/wallet/WalletImpl.java @@ -2,6 +2,10 @@ package fucoin.wallet; import akka.actor.ActorRef; import akka.actor.Props; +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import fucoin.actions.ClientAction; import fucoin.actions.join.ActionJoin; import fucoin.actions.join.ActionJoinAnswer; @@ -16,16 +20,17 @@ import scala.concurrent.Future; import static akka.dispatch.Futures.future; +import java.time.LocalTime; import java.util.concurrent.ConcurrentLinkedQueue; public class WalletImpl extends AbstractWallet { - private ActorRef preKnownNeighbour; - private ActorRef remoteSuperVisorActor; + private transient ActorRef preKnownNeighbour; + private transient ActorRef remoteSuperVisorActor; private transient WalletGuiControl gui; private String preKnownNeighbourName; private boolean isActive; - private ConcurrentLinkedQueue<ActorRef> deferedSupervisorReceivers = new ConcurrentLinkedQueue<>(); + private transient ConcurrentLinkedQueue<ActorRef> deferedSupervisorReceivers = new ConcurrentLinkedQueue<>(); public WalletImpl(String name) { super(name); @@ -245,4 +250,32 @@ public class WalletImpl extends AbstractWallet { System.out.println(message); } } + + public void createDump(LocalTime time){ + Gson gson = new GsonBuilder() + .setExclusionStrategies(new ExcludeAttributes()) + //.serializeNulls() <-- uncomment to serialize NULL fields as well + .create(); + String s = gson.toJson(this); + super.createDump(getName(), s, time); + } + + class ExcludeAttributes implements ExclusionStrategy { + + public boolean shouldSkipClass(Class<?> arg0) { + return false; + } + + public boolean shouldSkipField(FieldAttributes f) { + + return !(f.getName().equals("statistics") || + f.getName().equals("min") || + f.getName().equals("max") || + f.getName().equals("sum") || + f.getName().equals("count") || + f.getName().equals("amount") || + f.getName().equals("name")); + } + + } } diff --git a/src/main/java/fucoin/wallet/WalletStatistics.java b/src/main/java/fucoin/wallet/WalletStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..833fa210f451f6a33489dfde6cbe760d5b600836 --- /dev/null +++ b/src/main/java/fucoin/wallet/WalletStatistics.java @@ -0,0 +1,65 @@ +package fucoin.wallet; + +import java.io.Serializable; + +/** + * + */ +public class WalletStatistics implements Serializable { + private int min; + private int max; + private int sum; + private int count; + + public WalletStatistics() { + this.min = Integer.MAX_VALUE; + this.max = Integer.MIN_VALUE; + this.sum = 0; + this.count = 0; + } + + public WalletStatistics(int min, int max, int sum, int count) { + this.min = min; + this.max = max; + this.sum = sum; + this.count = count; + } + + public void update(int currentWalletAmount) { + setMin(currentWalletAmount); + setMax(currentWalletAmount); + setSum(currentWalletAmount); + } + + public int getMin() { + return min; + } + + public void setMin(int min) { + this.min = Math.min(this.min, min); + } + + public int getMax() { + return max; + } + + public void setMax(int max) { + this.max = Math.max(this.max, max); + } + + public int getSum() { + return sum; + } + + public void setSum(int sum) { + this.sum = (this.sum + sum) / 2; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = (this.count + count) / 2; + } +}