--- /dev/null
+Mon Jan 26 16:00 CEST 2015
+This is version 1.0 of PeerSimGrid
+
+1) An overview
+2) Preliminaries
+3) Compile & Run
+
+--------------------------------------------------------------------------------------------
+1) An overview:
+PeerSimGrid (PSG) is an interface developed in Java and allows users to simulate
+and execute their code under PeerSim or Simgrid simulator, using PeerSim implementation policy.
+
+This archive is composed of:
+* the src/ directory containing the simulator source and examples
+* the configs/ directory containing example of configuration files
+* psg.jar, a java archive containing all libraries
+
+
+2) Preliminaries:
+ Before using psg simulator, you need to make some changes in your configuration file:
+ * Replace the "UniformRandomTransport" transport protocol by "psgsim.PSGTransport".
+ * Replace the "simulation.endtime" by "simulation.duration"
+ * you can define your platform on the configuration file as:
+ platform path/to/your/file.xml
+
+
+3) Compile & Run:
+ In short, the way to compile and execute your code is:
+ Compile it:
+ $> make compile
+
+ Test it (optional):
+ $> make test
+ This test execute two examples, (chord and edaggregation found in the example folder), under the two simulators
+ and compare their outputs.
+
+ Run it:
+ $> ./run.sh path/to/your/configuration_file
+ For example:
+ $>./run.sh configs/chordPSG.txt
+
+ For the documentation
+ $> make doc
+
+ For the help command
+ $> make help
+
+Note: For more informations please contact khaled.baati@gmail.com
\ No newline at end of file
--- /dev/null
+#Config file for BitTorrent extension
+
+random.seed 1234567890
+simulation.endtime 1800000#6^8
+simulation.logtime 10^3
+OutputName bittorrent
+simulation.experiments 1
+
+network.size 30
+network.node peersim.core.GeneralNode
+
+protocol.urt UniformRandomTransport
+protocol.urt.mindelay 10
+protocol.urt.maxdelay 400
+
+#BE AWARE: the value "max_swarm_size" must be greater than
+#the value "peerset_size", since I have to be sure
+#that the space for the neighbor nodes is enough.
+
+protocol.bittorrent example.bittorrent.BitTorrent
+protocol.bittorrent.file_size 100
+protocol.bittorrent.max_swarm_size 80
+protocol.bittorrent.peerset_size 50
+protocol.bittorrent.duplicated_requests 1
+protocol.bittorrent.transport urt
+protocol.bittorrent.max_growth 20
+
+init.net example.bittorrent.NetworkInitializer
+init.net.protocol bittorrent
+init.net.transport urt
+init.net.newer_distr 80
+init.net.seeder_distr 15
+
+control.observer example.bittorrent.BTObserver
+control.observer.protocol bittorrent
+control.observer.step 10000
+
+control.dynamics example.bittorrent.NetworkDynamics
+control.dynamics.protocol bittorrent
+control.dynamics.newer_distr 60
+control.dynamics.minsize 20
+control.dynamics.tracker_can_die 1
+control.dynamics.step 100000
+control.dynamics.transport urt
+control.dynamics.add 0#5
+control.dynamics.remove 0#5
\ No newline at end of file
--- /dev/null
+#Config file for BitTorrent extension
+OutputName bittorrent
+platform platforms/psg.xml
+unit ms
+random.seed 1234567890
+simulation.duration 1800000
+simulation.logtime 10^3
+
+simulation.experiments 1
+
+network.size 40
+network.node peersim.core.GeneralNode
+
+protocol.urt psgsim.PSGTransport
+protocol.urt.mindelay 0#10
+protocol.urt.maxdelay 0#400
+
+#BE AWARE: the value "max_swarm_size" must be greater than
+#the value "peerset_size", since I have to be sure
+#that the space for the neighbor nodes is enough.
+
+protocol.bittorrent example.bittorrent.BitTorrent
+protocol.bittorrent.file_size 100
+protocol.bittorrent.max_swarm_size 80
+protocol.bittorrent.peerset_size 50
+protocol.bittorrent.duplicated_requests 1
+protocol.bittorrent.transport urt
+protocol.bittorrent.max_growth 20
+
+init.net example.bittorrent.NetworkInitializer
+init.net.protocol bittorrent
+init.net.transport urt
+init.net.newer_distr 80
+init.net.seeder_distr 15
+
+control.observer example.bittorrent.BTObserver
+control.observer.protocol bittorrent
+control.observer.step 10000
+
+control.dynamics example.bittorrent.NetworkDynamics
+control.dynamics.protocol bittorrent
+control.dynamics.newer_distr 60
+control.dynamics.minsize 20
+control.dynamics.tracker_can_die 1
+control.dynamics.step 100000
+control.dynamics.transport urt
+control.dynamics.add 0#5
+control.dynamics.remove 0#5
\ No newline at end of file
--- /dev/null
+# PEERSIM CHORD\r
+\r
+random.seed 1234567890\r
+simulation.endtime 10^4\r
+simulation.logtime 10^6\r
+OutputName chord\r
+simulation.experiments 1\r
+\r
+network.size 40\r
+protocol.tr UniformRandomTransport\r
+{\r
+ mindelay 0\r
+ maxdelay 0\r
+}\r
+\r
+protocol.chord example.chord.ChordProtocol\r
+{\r
+ transport tr\r
+}\r
+\r
+control.traffic example.chord.TrafficGenerator\r
+{\r
+ protocol chord\r
+ step 100\r
+}\r
+\r
+init.create example.chord.CreateNw \r
+{\r
+ protocol chord\r
+ idLength 128\r
+ succListSize 12\r
+}\r
+\r
+control.observer example.chord.MessageCounterObserver\r
+{\r
+ protocol chord\r
+ step 90000\r
+}\r
+\r
+#control.dnet DynamicNetwork\r
+#{\r
+# #add 2\r
+# add -2\r
+# minsize 18#3000\r
+# maxsize 60#7000\r
+# step 100000\r
+# init.0 example.chord.ChordInitializer\r
+# { \r
+# protocol chord\r
+# }\r
+#}
\ No newline at end of file
--- /dev/null
+# PEERSIM CHORD\r
+\r
+random.seed 1234567890\r
+simulation.duration 10^4\r
+simulation.logtime 10^6\r
+unit sec\r
+OutputName chord\r
+platform platforms/psg.xml\r
+simulation.experiments 1\r
+\r
+network.size 40\r
+protocol.tr psgsim.PSGTransport\r
+{\r
+ mindelay 0\r
+ maxdelay 0\r
+}\r
+\r
+protocol.chord example.chord.ChordProtocol\r
+{\r
+ transport tr\r
+}\r
+\r
+control.traffic example.chord.TrafficGenerator\r
+{\r
+ protocol chord\r
+ step 100\r
+}\r
+\r
+init.create example.chord.CreateNw \r
+{\r
+ protocol chord\r
+ idLength 128\r
+ succListSize 12\r
+}\r
+\r
+control.observer example.chord.MessageCounterObserver\r
+{\r
+ protocol chord\r
+ step 90000\r
+}\r
+\r
+\r
+#control.dnet DynamicNetwork\r
+#{\r
+# #add 2\r
+# add -2\r
+# minsize 18#3000\r
+# maxsize 60#7000\r
+# step 100000\r
+# init.0 example.chord.ChordInitializer\r
+# { \r
+# protocol chord\r
+# }\r
+#}\r
--- /dev/null
+# network size
+SIZE 50
+OutputName edaggregation
+
+# parameters of periodic execution
+CYCLES 100
+CYCLE SIZE*100
+
+# parameters of message transfer
+# delay values here are relative to cycle length, in percentage,
+# eg 50 means half the cycle length, 200 twice the cycle length, etc.
+MINDELAY 0
+MAXDELAY 0
+# drop is a probability, 0<=DROP<=1
+DROP 0
+
+random.seed 1234567890
+network.size SIZE
+simulation.endtime CYCLE*CYCLES
+simulation.logtime CYCLE
+
+################### protocols ===========================
+
+protocol.link peersim.core.IdleProtocol
+
+protocol.avg example.edaggregation.AverageED
+protocol.avg.linkable link
+protocol.avg.step CYCLE
+protocol.avg.transport tr
+
+protocol.tr UnreliableTransport
+protocol.tr.transport urt
+protocol.tr.drop DROP
+
+protocol.urt UniformRandomTransport
+protocol.urt.mindelay (CYCLE*MINDELAY)/100
+protocol.urt.maxdelay (CYCLE*MAXDELAY)/100
+################### initialization ======================
+
+init.rndlink WireKOut
+init.rndlink.k 20
+init.rndlink.protocol link
+
+init.vals LinearDistribution
+init.vals.protocol avg
+init.vals.max SIZE
+init.vals.min 1
+
+init.sch CDScheduler
+init.sch.protocol avg
+init.sch.randstart
+
+################ control ==============================
+
+control.0 SingleValueObserver
+control.0.protocol avg
+control.0.step CYCLE
--- /dev/null
+# network size
+SIZE 50
+OutputName edaggregation
+platform platforms/psg.xml
+unit sec
+# parameters of periodic execution
+CYCLES 100
+CYCLE SIZE*100
+# parameters of message transfer
+# delay values here are relative to cycle length, in percentage,
+# eg 50 means half the cycle length, 200 twice the cycle length, etc.
+MINDELAY 0
+MAXDELAY 0
+# drop is a probability, 0<=DROP<=1
+DROP 0
+
+random.seed 1234567890
+network.size SIZE
+simulation.duration CYCLE*CYCLES
+simulation.logtime CYCLE
+
+################### protocols ===========================
+
+protocol.link peersim.core.IdleProtocol
+
+protocol.avg example.edaggregation.AverageED
+protocol.avg.linkable link
+protocol.avg.step CYCLE
+protocol.avg.transport tr
+
+protocol.tr UnreliableTransport
+protocol.tr.transport urt
+protocol.tr.drop DROP
+
+protocol.urt psgsim.PSGTransport
+protocol.urt.mindelay (CYCLE*MINDELAY)/100
+protocol.urt.maxdelay (CYCLE*MAXDELAY)/100
+################### initialization ======================
+
+init.rndlink WireKOut
+init.rndlink.k 20
+init.rndlink.protocol link
+
+init.vals LinearDistribution
+init.vals.protocol avg
+init.vals.max SIZE
+init.vals.min 1
+
+init.sch CDScheduler
+init.sch.protocol avg
+init.sch.randstart
+
+################ control ==============================
+
+control.0 SingleValueObserver
+control.0.protocol avg
+control.0.step CYCLE
--- /dev/null
+# ::::::::::::::::::::::::::::::::::::::::::::::::::::::\r
+# :: Symphony Default Configuration\r
+# ::::::::::::::::::::::::::::::::::::::::::::::::::::::\r
+\r
+# network size\r
+SIZE 50\r
+\r
+# parameters of periodic execution\r
+CYCLES 100\r
+CYCLE SIZE/2\r
+OutputName symphony\r
+\r
+# parameters of message transfer\r
+# delay values here are relative to cycle length, in percentage,\r
+# eg 50 means half the cycle length, 200 twice the cycle length, etc.\r
+MINDELAY 0\r
+MAXDELAY 0\r
+\r
+random.seed 1234567890\r
+network.size SIZE\r
+simulation.experiments 1\r
+simulation.endtime 2000#CYCLE*CYCLES\r
+simulation.logtime CYCLE\r
+\r
+################### transports ===========================\r
+\r
+protocol.tr UniformRandomTransport \r
+{\r
+ mindelay (CYCLE*MINDELAY)/100\r
+ maxdelay (CYCLE*MAXDELAY)/100\r
+}\r
+\r
+################### protocols ===========================\r
+\r
+order.protocol link networkestimator symphony symphonynetworkmanager\r
+\r
+protocol.link peersim.core.IdleProtocol\r
+\r
+protocol.symphony example.symphony.SymphonyProtocol\r
+{\r
+ linkable link\r
+ transport tr\r
+ shortlink 4\r
+ # if commented means: longlink log(n)\r
+ #longlink 4\r
+ routing unidirectional\r
+ lookahead off\r
+}\r
+\r
+#protocol.networkestimator example.symphony.SimpleNetworkSizeEstimatorProtocol\r
+\r
+protocol.networkestimator example.symphony.SymphonyEstimationProtocol\r
+{\r
+ symphony symphony\r
+ # if commented means: s log(n)\r
+ #s 3\r
+}\r
+\r
+protocol.symphonynetworkmanager example.symphony.SymphonyNetworkManager\r
+{\r
+ symphony symphony\r
+ transport tr\r
+ networkestimator networkestimator\r
+ attempts 3\r
+ nTimeout 5\r
+ relinking on\r
+ relinkingLowerBound 0.5\r
+ relinkingUpperBound 2.0\r
+ step 4*CYCLE #useless\r
+}\r
+\r
+################### initialization ======================\r
+\r
+order.init netbuild checknet\r
+\r
+init.netbuild example.symphony.SymphonyNetworkBuilder\r
+{\r
+ symphony symphony\r
+ createLongLinks true\r
+ attempts 5\r
+}\r
+\r
+init.checknet example.symphony.SymphonyNetworkChecker\r
+{\r
+ symphony symphony\r
+ networkestimator networkestimator\r
+}\r
+\r
+################ control ==============================\r
+\r
+order.control sch checknet randomroutetest ringroutetest leavetest dnet estimationtest statistics\r
+\r
+control.randomroutetest example.symphony.RandomRouteTest\r
+{\r
+ symphony symphony\r
+ step CYCLE\r
+}\r
+\r
+control.ringroutetest example.symphony.RingRouteTest\r
+{\r
+ symphony symphony\r
+ startnode 0\r
+ step CYCLE\r
+}\r
+\r
+control.sch CDScheduler\r
+{\r
+ protocol symphonynetworkmanager\r
+ step CYCLE*2\r
+ randstart\r
+}\r
+\r
+control.checknet example.symphony.SymphonyNetworkChecker\r
+{\r
+ symphony symphony\r
+ networkestimator networkestimator\r
+ step CYCLE\r
+}\r
+\r
+control.dnet peersim.dynamics.DynamicNetwork\r
+{\r
+ add 0\r
+ maxsize 50\r
+ minsize SIZE/2\r
+ step CYCLE*2\r
+ init.0 example.symphony.SymphonyNodeInizializer\r
+ {\r
+ symphonynetworkmanager symphonynetworkmanager\r
+ symphony symphony\r
+ bootstrapnode 0\r
+ }\r
+}\r
+\r
+control.leavetest example.symphony.LeaveTest\r
+{\r
+ symphonynetworkmanager symphonynetworkmanager\r
+ n 1\r
+ minsizeOnline SIZE-1\r
+ waitTargetSizeToStart 2*SIZE\r
+ step CYCLE*2\r
+}\r
+\r
+control.statistics example.symphony.SymphonyStatistics\r
+{\r
+ symphony symphony\r
+ step (CYCLE*CYCLES)-1\r
+}\r
+\r
+control.estimationtest example.symphony.test.NetworkEstimationTest\r
+{\r
+ symphony symphony\r
+ symphonynetworkmanager symphonynetworkmanager\r
+ step CYCLE*4\r
+}
\ No newline at end of file
--- /dev/null
+# ::::::::::::::::::::::::::::::::::::::::::::::::::::::\r
+# :: Symphony Default Configuration\r
+# ::::::::::::::::::::::::::::::::::::::::::::::::::::::\r
+\r
+# network size\r
+SIZE 50\r
+unit sec\r
+# parameters of periodic execution\r
+CYCLES 100\r
+CYCLE SIZE/2\r
+OutputName symphony\r
+platform platforms/psg.xml\r
+\r
+# parameters of message transfer\r
+# delay values here are relative to cycle length, in percentage,\r
+# eg 50 means half the cycle length, 200 twice the cycle length, etc.\r
+MINDELAY 0\r
+MAXDELAY 0\r
+\r
+random.seed 1234567890\r
+network.size SIZE\r
+simulation.experiments 1\r
+simulation.duration 2000#CYCLE*CYCLES\r
+simulation.logtime CYCLE\r
+\r
+################### transports ===========================\r
+\r
+protocol.tr psgsim.PSGTransport \r
+{\r
+ mindelay (CYCLE*MINDELAY)/100\r
+ maxdelay (CYCLE*MAXDELAY)/100\r
+}\r
+\r
+################### protocols ===========================\r
+\r
+order.protocol link networkestimator symphony symphonynetworkmanager\r
+\r
+protocol.link peersim.core.IdleProtocol\r
+\r
+protocol.symphony example.symphony.SymphonyProtocol\r
+{\r
+ linkable link\r
+ transport tr\r
+ shortlink 4\r
+ # if commented means: longlink log(n)\r
+ #longlink 4\r
+ routing unidirectional\r
+ lookahead off\r
+}\r
+\r
+#protocol.networkestimator example.symphony.SimpleNetworkSizeEstimatorProtocol\r
+\r
+protocol.networkestimator example.symphony.SymphonyEstimationProtocol\r
+{\r
+ symphony symphony\r
+ # if commented means: s log(n)\r
+ #s 3\r
+}\r
+\r
+protocol.symphonynetworkmanager example.symphony.SymphonyNetworkManager\r
+{\r
+ symphony symphony\r
+ transport tr\r
+ networkestimator networkestimator\r
+ attempts 3\r
+ nTimeout 5\r
+ relinking on\r
+ relinkingLowerBound 0.5\r
+ relinkingUpperBound 2.0\r
+ step 4*CYCLE #useless\r
+}\r
+\r
+################### initialization ======================\r
+\r
+order.init netbuild checknet\r
+\r
+init.netbuild example.symphony.SymphonyNetworkBuilder\r
+{\r
+ symphony symphony\r
+ createLongLinks true\r
+ attempts 5\r
+}\r
+\r
+init.checknet example.symphony.SymphonyNetworkChecker\r
+{\r
+ symphony symphony\r
+ networkestimator networkestimator\r
+}\r
+\r
+################ control ==============================\r
+\r
+order.control sch checknet randomroutetest ringroutetest leavetest dnet estimationtest statistics\r
+\r
+control.randomroutetest example.symphony.RandomRouteTest\r
+{\r
+ symphony symphony\r
+ step CYCLE\r
+}\r
+\r
+control.ringroutetest example.symphony.RingRouteTest\r
+{\r
+ symphony symphony\r
+ startnode 0\r
+ step CYCLE\r
+}\r
+\r
+control.sch CDScheduler\r
+{\r
+ protocol symphonynetworkmanager\r
+ step CYCLE*2\r
+ randstart\r
+}\r
+\r
+control.checknet example.symphony.SymphonyNetworkChecker\r
+{\r
+ symphony symphony\r
+ networkestimator networkestimator\r
+ step CYCLE\r
+}\r
+\r
+control.dnet peersim.dynamics.DynamicNetwork\r
+{\r
+ add 0\r
+ maxsize 50\r
+ minsize SIZE/2\r
+ step CYCLE*2\r
+ init.0 example.symphony.SymphonyNodeInizializer\r
+ {\r
+ symphonynetworkmanager symphonynetworkmanager\r
+ symphony symphony\r
+ bootstrapnode 0\r
+ }\r
+}\r
+\r
+control.leavetest example.symphony.LeaveTest\r
+{\r
+ symphonynetworkmanager symphonynetworkmanager\r
+ n 1\r
+ minsizeOnline SIZE-1\r
+ waitTargetSizeToStart 2*SIZE\r
+ step CYCLE*2\r
+}\r
+\r
+control.statistics example.symphony.SymphonyStatistics\r
+{\r
+ symphony symphony\r
+ step (CYCLE*CYCLES)-1\r
+}\r
+\r
+control.estimationtest example.symphony.test.NetworkEstimationTest\r
+{\r
+ symphony symphony\r
+ symphonynetworkmanager symphonynetworkmanager\r
+ step CYCLE*4\r
+}\r
--- /dev/null
+<?xml version='1.0'?>
+<!DOCTYPE platform SYSTEM "http://simgrid.gforge.inria.fr/simgrid.dtd">
+<!-- _________
+ | |
+ | router |
+ ____________|__________|_____________ backbone
+ | | | | | |
+ l0| l1| l2| l97| l96 | | l99
+ | | | ........ | | |
+ | |
+ c-0.me c-99.me
+
+-->
+<platform version="3">
+<config>
+<prop id="network/latency_factor" value="1.0"/>
+</config>
+<AS id="AS0" routing="Full">
+ <cluster id="my_cluster_1" prefix="" suffix=""
+ radical="0-50000" power="1Gf" bw="200Mbps" lat="0ms"
+ bb_bw="200Mbps" bb_lat="0ms"/>
+</AS>
+</platform>
--- /dev/null
+#!/bin/bash
+
+if [ $(uname -m) = "i686" ]; then
+ eval ulimit -s 64
+else
+ eval ulimit -s 128
+fi
+echo '------------- Start execution..';
+java -Xmx1024m -cp lib.jar:classes peersim.Simulator $1
+echo '------------- done -------------';
+exit 0
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.util.*;
+
+/**
+ * This {@link Control} provides a way to keep track of some
+ * parameters of the BitTorrent network.
+ */
+ public class BTObserver implements Control {
+
+ /**
+ * The protocol to operate on.
+ * @config
+ */
+ private static final String PAR_PROT="protocol";
+
+ /**
+ * Protocol identifier, obtained from config property
+ */
+ private final int pid;
+
+ /**
+ * The basic constructor that reads the configuration file.
+ * @param prefix the configuration prefix for this class
+ */
+ public BTObserver(String prefix) {
+ pid = Configuration.getPid(prefix + "." + PAR_PROT);
+ }
+
+ /**
+ * Prints information about the BitTorrent network
+ * and the number of leechers and seeders.
+ * Please refer to the code comments for more details.
+ * @return always false
+ */
+ public boolean execute() {
+ IncrementalFreq nodeStatusStats = new IncrementalFreq();
+ IncrementalStats neighborStats = new IncrementalStats();
+
+ int numberOfNodes = Network.size();
+ int numberOfCompletedPieces = 0;
+
+ // cycles from 1, since the node 0 is the tracker
+ for (int i=1; i<numberOfNodes; ++i) {
+
+ // stats on number of leechers and seeders in the network
+ // and consequently also on number of completed files in the network
+ nodeStatusStats.add(((BitTorrent)(Network.get(i).getProtocol(pid))).getPeerStatus());
+
+ // stats on number of neighbors per peer
+ neighborStats.add(((BitTorrent)(Network.get(i).getProtocol(pid))).getNNodes());
+ }
+
+ // number of the pieces of the file, equal for every node, here 1 is chosen,
+ // since 1 is the first "normal" node (0 is the tracker)
+ int numberOfPieces = ((BitTorrent)(Network.get(1).getProtocol(pid))).nPieces;
+
+ for (int i=1; i<numberOfNodes; ++i) {
+ numberOfCompletedPieces = 0;
+
+ // discovers the status of the current peer (leecher or seeder)
+ int ps = ((BitTorrent)(Network.get(i).getProtocol(pid))).getPeerStatus();
+ String peerStatus;
+ if (ps==0) {
+ peerStatus = "L"; //leecher
+ }
+ else {
+ peerStatus = "S"; //seeder
+ }
+
+
+ if (Network.get(i)!=null) {
+
+ // counts the number of completed pieces for the i-th node
+ for (int j=0; j<numberOfPieces; j++) {
+ if ( ((BitTorrent)(Network.get(i).getProtocol(pid))).getFileStatus()[j] == 16) {
+ numberOfCompletedPieces++;
+ }
+ }
+
+ /*
+ * Put here the output lines of the Observer. An example is provided with
+ * basic information and stats.
+ * CommonState.getTime() is used to print out time references
+ * (useful for graph plotting).
+ */
+
+ System.out.println("OBS: node " + ((BitTorrent)(Network.get(i).getProtocol(pid))).getThisNodeID() + "(" + peerStatus + ")" + "\t pieces completed: " + numberOfCompletedPieces + "\t \t down: " + ((BitTorrent)(Network.get(i).getProtocol(pid))).nPiecesDown + "\t up: " + ((BitTorrent)(Network.get(i).getProtocol(pid))).nPiecesUp + " time: " + CommonState.getTime());
+ //System.out.println("[OBS] t " + CommonState.getTime() + "\t pc " + numberOfCompletedPieces + "\t n " + ((BitTorrent)(Network.get(i).getProtocol(pid))).getThisNodeID());
+ //System.out.println( CommonState.getTime() + "\t" + numberOfCompletedPieces + "\t" + ((BitTorrent)(Network.get(i).getProtocol(pid))).getThisNodeID());
+
+ }
+ else {
+ //System.out.println("[OBS] t " + CommonState.getTime() + "\t pc " + "0" + "\t n " + "0");
+ }
+
+ }
+
+ // prints the frequency of 0 (leechers) and 1 (seeders)
+ nodeStatusStats.printAll(System.out);
+
+ // prints the average number of neighbors per peer
+ System.out.println("Avg number of neighbors per peer: " + neighborStats.getAverage());
+
+ return false;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+import peersim.core.*;
+import peersim.config.*;
+import peersim.edsim.*;
+import peersim.transport.*;
+
+/**
+ * This is the class that implements the BitTorrent module for Peersim
+ */
+public class BitTorrent implements EDProtocol {
+ /**
+ * The size in Megabytes of the file being shared.
+ * @config
+ */
+ private static final String PAR_SIZE="file_size";
+ /**
+ * The Transport used by the the protocol.
+ * @config
+ */
+ private static final String PAR_TRANSPORT="transport";
+ /**
+ * The maximum number of neighbor that a node can have.
+ * @config
+ */
+ private static final String PAR_SWARM="max_swarm_size";
+ /**
+ * The maximum number of peers returned by the tracker when a new
+ * set of peers is requested through a <tt>TRACKER</tt> message.
+ * @config
+ */
+ private static final String PAR_PEERSET_SIZE="peerset_size";
+ /**
+ * Defines how much the network can grow with respect to the <tt>network.size</tt>
+ * when {@link NetworkDynamics} is used.
+ * @config
+ */
+ private static final String PAR_MAX_GROWTH="max_growth";
+ /**
+ * Is the number of requests of the same block sent to different peers.
+ * @config
+ */
+ private static final String PAR_DUP_REQ = "duplicated_requests";
+
+ /**
+ * KEEP_ALIVE message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int KEEP_ALIVE = 1;
+
+ /**
+ * CHOKE message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int CHOKE = 2;
+
+ /**
+ * UNCHOKE message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int UNCHOKE = 3;
+
+ /**
+ * INTERESTED message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int INTERESTED = 4;
+
+ /**
+ * NOT_INTERESTED message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int NOT_INTERESTED = 5;
+
+ /**
+ * HAVE message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int HAVE = 6;
+
+ /**
+ * BITFIELD message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int BITFIELD = 7;
+
+ /**
+ * REQUEST message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int REQUEST = 8;
+
+ /**
+ * PIECE message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int PIECE = 9;
+
+ /**
+ * CANCEL message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int CANCEL = 10;
+
+ /**
+ * TRACKER message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int TRACKER = 11;
+
+ /**
+ * PEERSET message.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int PEERSET = 12;
+
+ /**
+ * CHOKE_TIME event.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int CHOKE_TIME = 13;
+
+ /**
+ * OPTUNCHK_TIME event.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int OPTUNCHK_TIME = 14;
+
+ /**
+ * ANTISNUB_TIME event.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int ANTISNUB_TIME = 15;
+
+ /**
+ * CHECKALIVE_TIME event.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int CHECKALIVE_TIME = 16;
+
+ /**
+ * TRACKERALIVE_TIME event.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int TRACKERALIVE_TIME = 17;
+
+ /**
+ * DOWNLOAD_COMPLETED event.
+ * @see SimpleEvent#type "Event types"
+ */
+ private static final int DOWNLOAD_COMPLETED = 18;
+
+ /**
+ * The maxium connection speed of the local node.
+ */
+ int maxBandwidth;
+
+ /**
+ * Stores the neighbors ordered by ID.
+ * @see Element
+ */
+ private example.bittorrent.Element byPeer[];
+
+ /**
+ * Contains the neighbors ordered by bandwidth as needed by the unchocking
+ * algorithm.
+ */
+ private example.bittorrent.Element byBandwidth[];
+
+ /**
+ * The Neighbors list.
+ */
+ private Neighbor cache[];
+
+ /**
+ * Reference to the neighbors that unchocked the local node.
+ */
+ private boolean unchokedBy[];
+
+ /**
+ * Number of neighbors in the cache. When it decreases under 20, a new peerset
+ * is requested to the tracker.
+ */
+ private int nNodes = 0;
+
+ /**
+ * Maximum number of nodes in the network.
+ */
+ private int nMaxNodes;
+
+ /**
+ * The status of the local peer. 0 means that the current peer is a leecher, 1 a seeder.
+ */
+ private int peerStatus;
+
+ /**
+ * Defines how much the network can grow with respect to the <tt>network.size</tt>
+ * when {@link NetworkDynamics} is used.
+ */
+ public int maxGrowth;
+
+ /**
+ * File status of the local node. Contains the blocks owned by the local node.
+ */
+ private int status[];
+
+ /**
+ * Current number of Bitfield request sent. It must be taken into account
+ * before sending another one.
+ */
+ private int nBitfieldSent = 0;
+
+ /**
+ * Current number of pieces in upload from the local peer.
+ */
+ public int nPiecesUp = 0;
+ /**
+ * Current number of pieces in download to the local peer.
+ */
+ public int nPiecesDown = 0;
+
+ /**
+ * Current number of piece completed.
+ */
+ private int nPieceCompleted = 0;
+
+ /**
+ * Current downloading piece ID, the previous lastInterested piece.
+ */
+ int currentPiece = -1;
+
+ /**
+ * Used to compute the average download rates in choking algorithm. Stores the
+ * number of <tt>CHOKE</tt> events.
+ */
+ int n_choke_time = 0;
+
+ /**
+ * Used to send the <tt>TRACKER</tt> message when the local node has 20 neighbors
+ * for the first time.
+ */
+ boolean lock = false;
+
+ /**
+ * Number of peers interested to my pieces.
+ */
+ int numInterestedPeers = 0;
+
+ /**
+ * Last piece for which the local node sent an <tt>INTERESTED</tt> message.
+ */
+ int lastInterested = -1;
+
+ /**
+ * The status of the current piece in download. Length 16, every time the local node
+ * receives a PIECE message, it updates the corrisponding block's cell. The cell
+ * contains the ID for that block of that piece. If an already owned
+ * block is received this is discarded.
+ */
+ private int pieceStatus[];
+
+ /**
+ * Length of the file. Stored as number of pieces (256KB each one).
+ */
+ int nPieces;
+
+ /**
+ * Contains the neighbors's status of the file. Every row represents a
+ * node and every a cell has value O if the neighbor doesn't
+ * have the piece, 1 otherwise. It has {@link #swarmSize} rows and {@link #nPieces}
+ * columns.
+ */
+ int [][]swarm;
+
+ /**
+ * The summation of the swarm's rows. Calculated every time a {@link #BITFIELD} message
+ * is received and updated every time HAVE message is received.
+ */
+ int rarestPieceSet[];
+
+ /**
+ * The five pending block requests.
+ */
+ int pendingRequest[];
+
+ /**
+ * The maximum swarm size (default is 80)
+ */
+ int swarmSize;
+
+ /**
+ * The size of the peerset. This is the number of "friends" nodes
+ * sent from the tracker to each new node (default: 50)
+ */
+ int peersetSize;
+
+ /**
+ * The ID of the current node
+ */
+ private long thisNodeID;
+
+ /**
+ * Number of duplicated requests as specified in the configuration file.
+ * @see BitTorrent#PAR_DUP_REQ
+ */
+ private int numberOfDuplicatedRequests;
+
+ /**
+ * The queue where the requests to serve are stored.
+ * The default dimension of the queue is 20.
+ */
+ Queue requestToServe = null;
+
+ /**
+ * The queue where the out of sequence incoming pieces are stored
+ * waiting for the right moment to be processed.
+ * The default dimension of the queue is 100.
+ */
+ Queue incomingPieces = null;
+
+ /**
+ * The Transport ID.
+ * @see BitTorrent#PAR_TRANSPORT
+ */
+ int tid;
+
+ /**
+ * The reference to the tracker node. If equals to <tt>null</tt>, the local
+ * node is the tracker.
+ */
+ private Node tracker = null;
+
+ /**
+ * The default constructor. Reads the configuration file and initializes the
+ * configuration parameters.
+ * @param prefix the component prefix declared in the configuration file
+ */
+ public BitTorrent(String prefix){ // Used for the tracker's protocol
+ tid = Configuration.getPid(prefix+"."+PAR_TRANSPORT);
+ nPieces = (int)((Configuration.getInt(prefix+"."+PAR_SIZE))*1000000/256000);
+ swarmSize = (int)Configuration.getInt(prefix+"."+PAR_SWARM);
+ peersetSize = (int)Configuration.getInt(prefix+"."+PAR_PEERSET_SIZE);
+ numberOfDuplicatedRequests = (int)Configuration.getInt(prefix+"."+PAR_DUP_REQ);
+ maxGrowth = (int)Configuration.getInt(prefix+"."+PAR_MAX_GROWTH);
+ nMaxNodes = Network.getCapacity()-1;
+ }
+
+ /**
+ * Gets the reference to the tracker node.
+ * @return the reference to the tracker
+ */
+ public Node getTracker(){
+ return tracker;
+ }
+
+ /**
+ * Gets the number of neighbors currently stored in the cache of the local node.
+ * @return the number of neighbors in the cache
+ */
+ public int getNNodes(){
+ return this.nNodes;
+ }
+
+ /**
+ * Sets the reference to the tracker node.
+ * @param t the tracker node
+ */
+ public void setTracker(Node t){
+ tracker = t;
+ }
+
+ /**
+ * Sets the ID of the local node.
+ * @param id the ID of the node
+ */
+ public void setThisNodeID(long id) {
+ this.thisNodeID = id;
+ }
+
+ /**
+ * Gets the ID of the local node.
+ * @return the ID of the local node
+ */
+ public long getThisNodeID(){
+ return this.thisNodeID;
+ }
+
+ /**
+ * Gets the file status of the local node.
+ * @return the file status of the local node
+ */
+ public int[] getFileStatus(){
+ return this.status;
+ }
+
+ /**
+ * Initializes the tracker node. This method
+ * only performs the initialization of the tracker's cache.
+ */
+ public void initializeTracker() {
+ cache = new Neighbor[nMaxNodes+maxGrowth];
+ for(int i=0; i<nMaxNodes+maxGrowth; i++){
+ cache[i]= new Neighbor();
+ }
+ }
+
+ /**
+ * <p>Checks the number of neighbors and if it is equal to 20
+ * sends a TRACKER messages to the tracker, asking for a new
+ * peer set.</p>
+ *
+ * <p>This method *must* be called after every call of {@link #removeNeighbor}
+ * in {@link #processEvent}.
+ * </p>
+ */
+ private void processNeighborListSize(Node node, int pid) {
+ if (nNodes==20) {
+ Object ev;
+ long latency;
+ ev = new SimpleMsg(TRACKER, node);
+ Node tracker = ((BitTorrent)node.getProtocol(pid)).tracker;
+ if(tracker != null){
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node, tracker);
+// EDSimulator.add(latency,ev,tracker,pid);
+ ((Transport) node.getProtocol(tid)).send(node, tracker, ev, pid);
+ }
+ }
+ }
+
+ /**
+ * The standard method that processes incoming events.
+ * @param node reference to the local node for which the event is going to be processed
+ * @param pid BitTorrent's protocol id
+ * @param event the event to process
+ */
+ public void processEvent(Node node, int pid, Object event){
+
+ Object ev;
+ long latency;
+ switch(((SimpleEvent)event).getType()){
+
+ case KEEP_ALIVE: // 1
+ {
+ Node sender = ((IntMsg)event).getSender();
+ int isResponse = ((IntMsg)event).getInt();
+ //System.out.println("process, keep_alive: sender is "+sender.getID()+", local is "+node.getID());
+ Element e = search(sender.getID());
+ if(e!= null){ //if I know the sender
+ cache[e.peer].isAlive();
+ if(isResponse==0 && alive(sender)){
+ Object msg = new IntMsg(KEEP_ALIVE,node,1,0);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node, sender);
+// EDSimulator.add(latency,msg,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, msg, pid);
+ cache[e.peer].justSent();
+ }
+ }
+ else{
+ System.err.println("despite it should never happen, it happened");
+ ev = new BitfieldMsg(BITFIELD, true, false, node, status, nPieces);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ nBitfieldSent++;
+ }
+
+ };break;
+
+ case CHOKE: // 2, CHOKE message.
+ {
+ Node sender = ((SimpleMsg)event).getSender();
+ //System.out.println("process, choke: sender is "+sender.getID()+", local is "+node.getID());
+ Element e = search(sender.getID());
+ if(e!= null){ //if I know the sender
+ cache[e.peer].isAlive();
+ unchokedBy[e.peer]= false; // I'm choked by it
+ }
+ else{
+ System.err.println("despite it should never happen, it happened");
+ ev = new BitfieldMsg(BITFIELD, true, false, node, status, nPieces);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ nBitfieldSent++;
+ }
+ };break;
+
+ case UNCHOKE: // 3, UNCHOKE message.
+ {
+ Node sender = ((SimpleMsg)event).getSender();
+ //System.out.println("process, unchoke: sender is "+sender.getID()+", local is "+node.getID());
+ Element e = search(sender.getID());
+ if(e != null){ // If I know the sender
+ int senderIndex = e.peer;
+ cache[senderIndex].isAlive();
+ /* I send to it some of the pending requests not yet satisfied. */
+ int t = numberOfDuplicatedRequests;
+ for(int i=4;i>=0 && t>0;i--){
+ if(pendingRequest[i]==-1)
+ break;
+ if(alive(cache[senderIndex].node) && swarm[senderIndex][decode(pendingRequest[i],0)]==1){ //If the sender has that piece
+ ev = new IntMsg(REQUEST, node,pendingRequest[i],0);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev, sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ cache[senderIndex].justSent();
+ }
+ if(!alive(cache[senderIndex].node)){
+ System.out.println("unchoke1 rm neigh "+ cache[i].node.getID() );
+ removeNeighbor(cache[senderIndex].node);
+ processNeighborListSize(node,pid);
+ return;
+ }
+ t--;
+ }
+ // I request missing blocks to fill the queue
+ int block = getBlock();
+ int piece;
+ while(block != -2){ //while still available request to send
+ if(block < 0){ // No more block to request for the current piece
+ piece = getPiece();
+ if(piece == -1){ // no more piece to request
+ break;
+ }
+ for(int j=0; j<swarmSize; j++){// send the interested message to those
+ // nodes which have that piece
+ lastInterested = piece;
+ if(alive(cache[j].node) && swarm[j][piece]==1){
+ ev = new IntMsg(INTERESTED, node, lastInterested,0);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,cache[j].node);
+// EDSimulator.add(latency,ev,cache[j].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node, cache[j].node, ev, pid);
+ cache[j].justSent();
+ }
+
+ if(!alive(cache[j].node)){
+ //System.out.println("unchoke2 rm neigh "+ cache[j].node.getID() );
+ removeNeighbor(cache[j].node);
+ processNeighborListSize(node,pid);
+ }
+ }
+ block = getBlock();
+ }
+ else{ // block value referred to a real block
+ if(alive(cache[senderIndex].node) && swarm[senderIndex][decode(block,0)]==1 && addRequest(block)){ // The sender has that block
+ ev = new IntMsg(REQUEST, node, block,0);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+
+ cache[senderIndex].justSent();
+ }
+ else{
+ if(!alive(cache[senderIndex].node)){
+ System.out.println("unchoke3 rm neigh "+ cache[senderIndex].node.getID() );
+ removeNeighbor(cache[senderIndex].node);
+ processNeighborListSize(node,pid);
+ }
+ return;
+ }
+ block = getBlock();
+ }
+ }
+ unchokedBy[senderIndex] = true; // I add the sender to the list
+ }
+ else // It should never happen.
+ {
+ System.err.println("despite it should never happen, it happened");
+ for(int i=0; i<swarmSize; i++)
+ if(cache[i].node !=null)
+ System.err.println(cache[i].node.getID());
+ ev = new BitfieldMsg(BITFIELD, true, false, node, status, nPieces);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ nBitfieldSent++;
+ }
+ };break;
+
+ case INTERESTED: // 4, INTERESTED message.
+ {
+ numInterestedPeers++;
+ Node sender = ((IntMsg)event).getSender();
+ //System.out.println("process, interested: sender is "+sender.getID()+", local is "+node.getID());
+ int value = ((IntMsg)event).getInt();
+ Element e = search(sender.getID());
+ if(e!=null){
+ cache[e.peer].isAlive();
+ cache[e.peer].interested = value;
+ }
+ else{
+ System.err.println("despite it should never happen, it happened");
+ ev = new BitfieldMsg(BITFIELD, true, false, node, status, nPieces);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ nBitfieldSent++;
+ }
+
+ }; break;
+
+ case NOT_INTERESTED: // 5, NOT_INTERESTED message.
+ {
+ numInterestedPeers--;
+ Node sender = ((IntMsg)event).getSender();
+ //System.out.println("process, not_interested: sender is "+sender.getID()+", local is "+node.getID());
+ int value = ((IntMsg)event).getInt();
+ Element e = search(sender.getID());
+ if(e!=null){
+ cache[e.peer].isAlive();
+ if(cache[e.peer].interested == value)
+ cache[e.peer].interested = -1; // not interested
+ }
+ }; break;
+
+ case HAVE: // 6, HAVE message.
+ {
+ Node sender = ((IntMsg)event).getSender();
+ //System.out.println("process, have: sender is "+sender.getID()+", local is "+node.getID());
+ int piece = ((IntMsg)event).getInt();
+ Element e = search(sender.getID());
+ if(e!=null){
+ cache[e.peer].isAlive();
+ swarm[e.peer][piece]=1;
+ rarestPieceSet[piece]++;
+ boolean isSeeder = true;
+ for(int i=0; i<nPieces; i++){
+ isSeeder = isSeeder && (swarm[e.peer][i]==1);
+ }
+ e.isSeeder = isSeeder;
+ }
+ else{
+ System.err.println("despite it should never happen, it happened");
+ ev = new BitfieldMsg(BITFIELD, true, false, node, status, nPieces);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ nBitfieldSent++;
+ }
+ }; break;
+
+ case BITFIELD: // 7, BITFIELD message
+ {
+ Node sender = ((BitfieldMsg)event).getSender();
+ int []fileStatus = ((BitfieldMsg)event).getArray();
+ /*Response with NACK*/
+ if(!((BitfieldMsg)event).isRequest && !((BitfieldMsg)event).ack){
+ Element e = search(sender.getID());
+ if(e == null) // if is a response with nack that follows a request
+ nBitfieldSent--;
+ // otherwise is a response with ack that follows a duplicate
+ // insertion attempt
+ //System.out.println("process, bitfield_resp_nack: sender is "+sender.getID()+", local is "+node.getID());
+ return;
+ }
+ /*Request with NACK*/
+ if(((BitfieldMsg)event).isRequest && !((BitfieldMsg)event).ack){
+ //System.out.println("process, bitfield_req_nack: sender is "+sender.getID()+", local is "+node.getID());
+ if(alive(sender)){
+ Element e = search(sender.getID());
+ ev = new BitfieldMsg(BITFIELD, false, true, node, status, nPieces); //response with ack
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ cache[e.peer].justSent();
+ }
+ }
+ /*Response with ACK*/
+ if(!((BitfieldMsg)event).isRequest && ((BitfieldMsg)event).ack){
+ nBitfieldSent--;
+ //System.out.println("process, bitfield_resp_ack: sender is "+sender.getID()+", local is "+node.getID());
+ if(alive(sender)){
+ if(addNeighbor(sender)){
+ Element e = search(sender.getID());
+ cache[e.peer].isAlive();
+ swarm[e.peer] = fileStatus;
+ boolean isSeeder = true;
+ for(int i=0; i<nPieces; i++){
+ rarestPieceSet[i]+= fileStatus[i];
+ isSeeder = isSeeder && (fileStatus[i]==1);
+ }
+ e.isSeeder = isSeeder;
+
+ if(nNodes==10 && !lock){ // I begin to request pieces
+ lock = true;
+ int piece = getPiece();
+ if(piece == -1)
+ return;
+ lastInterested = piece;
+ currentPiece = lastInterested;
+ ev = new IntMsg(INTERESTED, node, lastInterested,0);
+ for(int i=0; i<swarmSize; i++){// send the interested message to those
+ // nodes which have that piece
+ if(alive(cache[i].node) && swarm[i][piece]==1){
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,cache[i].node);
+// EDSimulator.add(latency,ev,cache[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node, cache[i].node, ev, pid);
+ cache[i].justSent();
+ }
+ }
+
+ }
+
+ }
+ }
+ else
+ System.out.println("Sender "+sender.getID()+" not alive");
+ }
+ /*Request with ACK*/
+ if(((BitfieldMsg)event).isRequest && ((BitfieldMsg)event).ack){
+ //System.out.println("process, bitfield_req_ack: sender is "+sender.getID()+", local is "+node.getID());
+ if(alive(sender)){
+ if(addNeighbor(sender)){
+ Element e = search(sender.getID());
+ cache[e.peer].isAlive();
+ swarm[e.peer] = fileStatus;
+ boolean isSeeder = true;
+ for(int i=0; i<nPieces; i++){
+ rarestPieceSet[i]+= fileStatus[i]; // I update the rarestPieceSet with the pieces of the new node
+ isSeeder = isSeeder && (fileStatus[i]==1); // I check if the new node is a seeder
+ }
+ e.isSeeder = isSeeder;
+ ev = new BitfieldMsg(BITFIELD, false, true, node, status, nPieces); //response with ack
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ cache[e.peer].justSent();
+ if(nNodes==10 && !lock){ // I begin to request pieces
+ int piece = getPiece();
+ if(piece == -1)
+ return;
+ lastInterested = piece;
+ currentPiece = lastInterested;
+ ev = new IntMsg(INTERESTED, node, lastInterested,0);
+ for(int i=0; i<swarmSize; i++){// send the interested message to those
+ // nodes which have that piece
+ if(alive(cache[i].node) && swarm[i][piece]==1){
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,cache[i].node);
+// EDSimulator.add(latency,ev,cache[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node, cache[i].node, ev, pid);
+ cache[i].justSent();
+ }
+ }
+
+ }
+ }
+ else {
+ Element e;
+ if((e = search(sender.getID()))!=null){ // The sender was already in the cache
+ cache[e.peer].isAlive();
+ ev = new BitfieldMsg(BITFIELD, false, true, node, status, nPieces); //response with ack
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ cache[e.peer].justSent();
+ }
+ else{ // Was not be possible add the sender (nBitfield+nNodes > swarmSize)
+ ev = new BitfieldMsg(BITFIELD, false, false, node, status, nPieces); //response with nack
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node, sender, ev, pid);
+ }
+ }
+
+ }
+ else
+ System.out.println("Sender "+sender.getID()+" not alive");
+ }
+ };break;
+
+ case REQUEST: // 8, REQUEST message.
+ {
+ Object evnt;
+ Node sender = ((IntMsg)event).getSender();
+ int value = ((IntMsg)event).getInt();
+ Element e;
+ BitTorrent senderP;
+ int remoteRate;
+ int localRate;
+ int bandwidth;
+ int downloadTime;
+
+ e = search(sender.getID());
+ if (e==null)
+ return;
+ cache[e.peer].isAlive();
+
+ requestToServe.enqueue(value, sender);
+
+ /*I serve the enqueued requests until 10 uploding pieces or an empty queue*/
+ while(!requestToServe.empty() && nPiecesUp <10){
+ Request req = requestToServe.dequeue();
+ e = search(req.sender.getID());
+ if(e!=null && alive(req.sender)){
+// ev = new IntMsg(PIECE, node, req.id);
+ nPiecesUp++;
+ e.valueUP++;
+ senderP = ((BitTorrent)req.sender.getProtocol(pid));
+ senderP.nPiecesDown++;
+ remoteRate = senderP.maxBandwidth/(senderP.nPiecesUp + senderP.nPiecesDown);
+ localRate = maxBandwidth/(nPiecesUp + nPiecesDown);
+ bandwidth = Math.min(remoteRate, localRate);
+ downloadTime = ((16*8)/(bandwidth))*1000; // in milliseconds
+
+ ev = new IntMsg(PIECE, node, req.id, 16*8 * 1024);
+ ((Transport) node.getProtocol(tid)).send(node, req.sender, ev, pid);
+
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,req.sender);
+// EDSimulator.add(latency+downloadTime,ev,req.sender,pid);
+ cache[e.peer].justSent();
+
+ /*I send to me an event to indicate that the download is completed.
+ This prevent that, when the receiver death occurres, my value nPiecesUp
+ doesn't decrease.*/
+ evnt = new SimpleMsg(DOWNLOAD_COMPLETED, req.sender);
+// EDSimulator.add(latency+downloadTime,evnt,node,pid);
+ ((Transport) node.getProtocol(tid)).send(node, node, evnt, pid);
+ }
+ }
+ }; break;
+
+ case PIECE: // 9, PIECE message.
+ {
+ Node sender = ((IntMsg)event).getSender();
+ /* Set the correct value for the local uploading and remote
+ downloading number of pieces */
+ nPiecesDown--;
+
+ if(peerStatus == 1)// To save CPU cycles
+ return;
+ //System.out.println("process, piece: sender is "+sender.getID()+", local is "+node.getID());
+ Element e = search(sender.getID());
+
+ if(e==null){ //I can't accept a piece not wait
+ return;
+ }
+ e.valueDOWN++;
+
+ cache[e.peer].isAlive();
+
+ int value = ((IntMsg)event).getInt();
+ int piece = decode(value,0);
+ int block = decode(value,1);
+ /* If the block has not been already downloaded and it belongs to
+ the current downloading piece.*/
+ if(piece == currentPiece && decode(pieceStatus[block],0)!= piece){
+ pieceStatus[block] = value;
+ status[piece]++;
+ removeRequest(value);
+ requestNextBlocks(node, pid, e.peer);
+
+ }else{ // Either a future piece or an owned piece
+ if(piece!=currentPiece && status[piece]!=16){ // Piece not owned, will be considered later
+ incomingPieces.enqueue(value, sender);
+ }
+
+ }
+ ev = new IntMsg(CANCEL, node, value,0);
+ /* I send a CANCEL to all nodes to which I previously requested the block*/
+ for(int i=0; i<swarmSize; i++){
+ if(alive(cache[i].node) && unchokedBy[i]==true && swarm[i][decode(block,0)]==1 && cache[i].node != sender){
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,cache[i].node);
+// EDSimulator.add(latency,ev,cache[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node, cache[i].node, ev, pid);
+ cache[i].justSent();
+ }
+ }
+
+ if(status[currentPiece]==16){ // if piece completed, I change the currentPiece to the next wanted
+ nPieceCompleted++;
+ ev = new IntMsg(HAVE, node, currentPiece,0);
+ for(int i=0; i<swarmSize; i++){ // I send the HAVE for the piece
+ if(alive(cache[i].node)){
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,cache[i].node);
+// EDSimulator.add(latency,ev,cache[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node, cache[i].node, ev, pid);
+ cache[i].justSent();
+ }
+ if(!alive(cache[i].node)){
+ //System.out.println("piece3 rm neigh "+ cache[i].node.getID() );
+ removeNeighbor(cache[i].node);
+ processNeighborListSize(node,pid);
+ }
+ }
+ ev = new IntMsg(NOT_INTERESTED, node, currentPiece,0);
+ for(int i=0; i<swarmSize; i++){ // I send the NOT_INTERESTED to which peer I sent an INTERESTED
+ if(swarm[i][piece]==1 && alive(cache[i].node)){
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,cache[i].node);
+// EDSimulator.add(latency,ev,cache[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node, cache[i].node, ev, pid);
+ cache[i].justSent();
+ }
+ if(!alive(cache[i].node)){
+ //System.out.println("piece4 rm neigh "+ cache[i].node.getID() );
+ removeNeighbor(cache[i].node);
+ processNeighborListSize(node,pid);
+ }
+ }
+ if(nPieceCompleted == nPieces){
+ System.out.println("FILE COMPLETED for peer "+node.getID());
+ this.peerStatus = 1;
+ }
+
+ /* I set the currentPiece to the lastInterested. Then I extract
+ the queued received blocks
+ */
+
+ currentPiece = lastInterested;
+ int m = incomingPieces.dim;
+ while(m > 0){ // I process the queue
+ m--;
+ Request temp = incomingPieces.dequeue();
+ int p = decode(temp.id,0); // piece id
+ int b = decode(temp.id,1); // block id
+ Element s = search(temp.sender.getID());
+ if(s==null) // if the node that sent the block in the queue is dead
+ continue;
+ if(p==currentPiece && decode(pieceStatus[b],0)!= p){
+ pieceStatus[b] = temp.id;
+ status[p]++;
+ removeRequest(temp.id);
+ requestNextBlocks(node, pid, s.peer);
+ }
+ else{ // The piece not currently desired will be moved to the tail
+ if(p!= currentPiece) // If not a duplicate block but belongs to another piece
+ incomingPieces.enqueue(temp.id,temp.sender);
+ else // duplicate block
+ requestNextBlocks(node, pid, s.peer);
+ }
+ }
+ }
+ }; break;
+
+ case CANCEL:
+ {
+ Node sender = ((IntMsg)event).getSender();
+ int value = ((IntMsg)event).getInt();
+ requestToServe.remove(sender, value);
+ };break;
+
+ case PEERSET: // PEERSET message
+ {
+ Node sender = ((PeerSetMsg)event).getSender();
+ //System.out.println("process, peerset: sender is "+sender.getID()+", local is "+node.getID());
+ Neighbor n[] = ((PeerSetMsg)event).getPeerSet();
+
+ for(int i=0; i<peersetSize; i++){
+ if( n[i]!=null && alive(n[i].node) && search(n[i].node.getID())==null && nNodes+nBitfieldSent <swarmSize-2) {
+ ev = new BitfieldMsg(BITFIELD, true, true, node, status, nPieces);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node,n[i].node);
+// EDSimulator.add(latency,ev,n[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node, n[i].node, ev, pid);
+
+ nBitfieldSent++;
+ // Here I should call the Neighbor.justSent(), but here
+ // the node is not yet in the cache.
+ }
+ }
+ }; break;
+
+ case TRACKER: // TRACKER message
+ {
+
+ int j=0;
+ Node sender = ((SimpleMsg)event).getSender();
+ //System.out.println("process, tracker: sender is "+sender.getID()+", local is "+node.getID());
+ if(!alive(sender))
+ return;
+ Neighbor tmp[] = new Neighbor[peersetSize];
+ int k=0;
+ if(nNodes <= peersetSize){
+ for(int i=0; i< nMaxNodes+maxGrowth; i++){
+ if(cache[i].node != null && cache[i].node.getID()!= sender.getID()){
+ tmp[k]=cache[i];
+ k++;
+ }
+ }
+ ev = new PeerSetMsg(PEERSET, tmp, node);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node, sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node,sender, ev, pid);
+ return;
+ }
+
+ while(j < peersetSize){
+ int i = CommonState.r.nextInt(nMaxNodes+maxGrowth);
+ for (int z=0; z<j; z++){
+ if(cache[i].node==null || tmp[z].node.getID() == cache[i].node.getID() || cache[i].node.getID() == sender.getID()){
+ z=0;
+ i= CommonState.r.nextInt(nMaxNodes+maxGrowth);
+ }
+ }
+ if(cache[i].node != null){
+ tmp[j] = cache[i];
+ j++;
+ }
+ }
+ ev = new PeerSetMsg(PEERSET, tmp, node);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node, sender);
+// EDSimulator.add(latency,ev,sender,pid);
+ ((Transport) node.getProtocol(tid)).send(node,sender, ev, pid);
+ }; break;
+
+ case CHOKE_TIME: //Every 10 secs.
+ {
+ n_choke_time++;
+
+ ev = new SimpleEvent(CHOKE_TIME);
+ EDSimulator.add(10000,ev,node,pid);
+ int j=0;
+ /*I copy the interested nodes in the byBandwidth array*/
+ for(int i=0;i< swarmSize && byPeer[i].peer != -1; i++){
+ if(cache[byPeer[i].peer].interested > 0){
+ byBandwidth[j]=byPeer[i]; //shallow copy
+ j++;
+ }
+ }
+
+ /*It ensures that in the next 20sec, if there are less nodes interested
+ than now, those in surplus will not be ordered. */
+ for(;j<swarmSize;j++){
+ byBandwidth[j]=null;
+ }
+ sortByBandwidth();
+ int optimistic = 3;
+ int luckies[] = new int[3];
+ try{ // It takes the first three neighbors
+ luckies[0] = byBandwidth[0].peer;
+ optimistic--;
+ luckies[1] = byBandwidth[1].peer;
+ optimistic--;
+ luckies[2] = byBandwidth[2].peer;
+ }
+ catch(NullPointerException e){ // If not enough peer in byBandwidth it chooses the other romdomly
+ for(int z = optimistic; z>0;z--){
+ int lucky = CommonState.r.nextInt(nNodes);
+ while(cache[byPeer[lucky].peer].status ==1 && alive(cache[byPeer[lucky].peer].node) &&
+ cache[byPeer[lucky].peer].interested == 0)// until the lucky peer is already unchoked or not interested
+ lucky = CommonState.r.nextInt(nNodes);
+ luckies[3-z]= byPeer[lucky].peer;
+ }
+ }
+ for(int i=0; i<swarmSize; i++){ // I perform the chokes and the unchokes
+ if((i==luckies[0] || i==luckies[1] || i==luckies[2]) && alive(cache[i].node) && cache[i].status != 2){ //the unchokes
+ cache[i].status = 1;
+ ev = new SimpleMsg(UNCHOKE, node);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node, cache[i].node);
+// EDSimulator.add(latency,ev,cache[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node,cache[i].node, ev, pid);
+ cache[i].justSent();
+ //System.out.println("average time, unchoked: "+cache[i].node.getID());
+ }
+ else{ // the chokes
+ if(alive(cache[i].node) && (cache[i].status == 1 || cache[i].status == 2)){
+ cache[i].status = 0;
+ ev = new SimpleMsg(CHOKE, node);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node, cache[i].node);
+// EDSimulator.add(latency,ev,cache[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node,cache[i].node, ev, pid);
+ cache[i].justSent();
+ }
+ }
+ }
+
+ if(n_choke_time%2==0){ //every 20 secs. Used in computing the average download rates
+ for(int i=0; i<nNodes; i++){
+ if(this.peerStatus == 0){ // I'm a leeacher
+ byPeer[i].head20 = byPeer[i].valueDOWN;
+ }
+ else{
+ byPeer[i].head20 = byPeer[i].valueUP;
+ }
+ }
+ }
+ }; break;
+
+ case OPTUNCHK_TIME:
+ {
+
+ //System.out.println("process, optunchk_time");
+
+ ev = new SimpleEvent(OPTUNCHK_TIME);
+ EDSimulator.add(30000,ev,node,pid);
+ int lucky = CommonState.r.nextInt(nNodes);
+ while(cache[byPeer[lucky].peer].status ==1)// until the lucky peer is already unchoked
+ lucky = CommonState.r.nextInt(nNodes);
+ if(!alive(cache[byPeer[lucky].peer].node))
+ return;
+ cache[byPeer[lucky].peer].status = 1;
+ Object msg = new SimpleMsg(UNCHOKE,node);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node, cache[byPeer[lucky].peer].node);
+// EDSimulator.add(latency,msg,cache[byPeer[lucky].peer].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node,cache[byPeer[lucky].peer].node, msg, pid);
+ cache[byPeer[lucky].peer].justSent();
+ }; break;
+
+ case ANTISNUB_TIME:
+ {
+ if(this.peerStatus == 1) // I'm a seeder, I don't update the event
+ return;
+ //System.out.println("process, antisnub_time");
+ for(int i=0; i<nNodes; i++){
+ if(byPeer[i].valueDOWN >0 && (byPeer[i].valueDOWN - byPeer[i].head60)==0){// No blocks downloaded in 1 min
+ cache[byPeer[i].peer].status = 2; // I'm snubbed by it
+ }
+ byPeer[i].head60 = byPeer[i].valueDOWN;
+ }
+ ev = new SimpleEvent(ANTISNUB_TIME);
+ EDSimulator.add(60000,ev,node,pid);
+ long time = CommonState.getTime();
+ }; break;
+
+ case CHECKALIVE_TIME:
+ {
+
+ //System.out.println("process, checkalive_time");
+
+ long now = CommonState.getTime();
+ for(int i=0; i<swarmSize; i++){
+ /*If are at least 2 minutes (plus 1 sec of tolerance) that
+ I don't send anything to it.*/
+ if(alive(cache[i].node) && (cache[i].lastSent < (now-121000))){
+ Object msg = new IntMsg(KEEP_ALIVE,node,0,0);
+// latency = ((Transport)node.getProtocol(tid)).getLatency(node, cache[i].node);
+// EDSimulator.add(latency,msg,cache[i].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node,cache[i].node, msg, pid);
+ cache[i].justSent();
+ }
+ /*If are at least 2 minutes (plus 1 sec of tolerance) that I don't
+ receive anything from it though I sent a keepalive 2 minutes ago*/
+ else{
+ if(cache[i].lastSeen <(now-121000) && cache[i].node != null && cache[i].lastSent < (now-121000)){
+ System.out.println("process, checkalive_time, rm neigh " + cache[i].node.getID());
+ if(cache[i].node.getIndex() != -1){
+ System.out.println("This should never happen: I remove a node that is not effectively died");
+ }
+ removeNeighbor(cache[i].node);
+ processNeighborListSize(node,pid);
+ }
+ }
+ }
+ ev = new SimpleEvent(CHECKALIVE_TIME);
+ EDSimulator.add(120000,ev,node,pid);
+ }; break;
+
+ case TRACKERALIVE_TIME:
+ {
+ //System.out.println("process, trackeralive_time");
+ if(alive(tracker)){
+ ev = new SimpleEvent(TRACKERALIVE_TIME);
+ EDSimulator.add(1800000,ev,node,pid);
+ }
+ else
+ tracker=null;
+
+ }; break;
+
+ case DOWNLOAD_COMPLETED:
+ {
+ nPiecesUp--;
+ }; break;
+
+ }
+ }
+
+ /**
+ * Given a piece index and a block index it encodes them in an unique integer value.
+ * @param piece the index of the piece to encode.
+ * @param block the index of the block to encode.
+ * @return the encoding of the piece and the block indexes.
+ */
+ private int encode(int piece, int block){
+ return (piece*100)+block;
+
+ }
+ /**
+ * Returns either the piece or the block that contained in the <tt>value</tt> depending
+ * on <tt>part</tt>: 0 means the piece value, 1 the block value.
+ * @param value the ID of the block to decode.
+ * @param part the information to extract from <tt>value</tt>. 0 means the piece index, 1 the block index.
+ * @return the piece or the block index depending about the value of <tt>part</tt>
+ */
+ private int decode(int value, int part){
+ if (value==-1) // Not a true value to decode
+ return -1;
+ if(part == 0) // I'm interested to the piece
+ return value/100;
+ else // I'm interested to the block
+ return value%100;
+ }
+
+ /**
+ * Used by {@link NodeInitializer#choosePieces(int, BitTorrent) NodeInitializer} to set
+ * the number of piece completed from the beginning in according with
+ * the distribution in the configuration file.
+ * @param number the number of piece completed
+ */
+ public void setCompleted(int number){
+ this.nPieceCompleted = number;
+ }
+
+ /**
+ * Sets the status (the set of blocks) of the file for the current node.
+ * Note that a piece is considered <i>completed</i> if the number
+ * of downloaded blocks is 16.
+ * @param index The index of the piece
+ * @param value Number of blocks downloaded for the piece index.
+ */
+ public void setStatus(int index, int value){
+ status[index]=value;
+ }
+
+ /**
+ * Sets the status of the local node.
+ * @param status The status of the node: 1 means seeder, 0 leecher
+ */
+ public void setPeerStatus(int status){
+ this.peerStatus = status;
+ }
+
+ /**
+ * Gets the status of the local node.
+ * @return The status of the local node: 1 means seeder, 0 leecher
+ */
+ public int getPeerStatus(){
+ return peerStatus;
+ }
+
+ /**
+ * Gets the number of blocks for a given piece owned by the local node.
+ * @param index The index of the piece
+ * @return Number of blocks downloaded for the piece index
+ */
+ public int getStatus(int index){
+ return status[index];
+ }
+
+ /**
+ * Sets the maximum bandwdith for the local node.
+ * @param value The value of bandwidth in Kbps
+ */
+ public void setBandwidth(int value){
+ maxBandwidth = value;
+ }
+
+ /**
+ * Checks if a node is still alive in the simulated network.
+ * @param node The node to check
+ * @return true if the node <tt>node</tt> is up, false otherwise
+ * @see peersim.core.GeneralNode#isUp
+ */
+ public boolean alive(Node node){
+ if(node == null)
+ return false;
+ else
+ return node.isUp();
+ }
+
+ /**
+ * Adds a neighbor to the cache of the local node.
+ * The new neighbor is put in the first null position.
+ * @param neighbor The neighbor node to add
+ * @return <tt>false</tt> if the neighbor is already present in the cache (this can happen when the peer requests a
+ * new peer set to the tracker an there is still this neighbor within) or no place is available.
+ * Otherwise, returns true if the node is correctly added to the cache.
+ */
+ public boolean addNeighbor(Node neighbor){
+ if(search(neighbor.getID()) !=null){// if already exists
+ // System.err.println("Node "+neighbor.getID() + " not added, already exist.");
+ return false;
+ }
+ if(this.tracker == null){ // I'm in the tracker's BitTorrent protocol
+ for(int i=0; i< nMaxNodes+maxGrowth; i++){
+ if(cache[i].node == null){
+ cache[i].node = neighbor;
+ cache[i].status = 0; //chocked
+ cache[i].interested = -1; //not interested
+ this.nNodes++;
+
+ //System.err.println("i: " + i +" nMaxNodes: " + nMaxNodes);
+ return true;
+ }
+ }
+ }
+ else{
+ if((nNodes+nBitfieldSent) < swarmSize){
+ //System.out.println("I'm the node " + this.thisNodeID + ", trying to add node "+neighbor.getID());
+ for(int i=0; i<swarmSize; i++){
+ if(cache[i].node == null){
+ cache[i].node = neighbor;
+ cache[i].status = 0; //choked
+ cache[i].interested = -1; // not interested
+ byPeer[nNodes].peer = i;
+ byPeer[nNodes].ID = neighbor.getID();
+ sortByPeer();
+ this.nNodes++;
+ //System.out.println(neighbor.getID()+" added!");
+ return true;
+ }
+ }
+ System.out.println("Node not added, no places available");
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes a neighbor from the cache of the local node.
+ * @param neighbor The node to remove
+ * @return true if the node is correctly removed, false otherwise.
+ */
+ public boolean removeNeighbor(Node neighbor) {
+
+ if (neighbor == null)
+ return true;
+
+ // this is the tracker's bittorrent protocol
+ if (this.tracker == null) {
+ for (int i=0; i< (nMaxNodes+maxGrowth); i++) {
+
+ // check the feasibility of the removal
+ if ( (cache[i] != null) && (cache[i].node != null) &&
+ (cache[i].node.getID() == neighbor.getID()) ) {
+ cache[i].node = null;
+ this.nNodes--;
+ return true;
+ }
+ }
+ return false;
+ }
+ // this is the bittorrent protocol of a peer
+ else {
+
+ Element e = search(neighbor.getID());
+
+ if (e != null) {
+ for (int i=0; i<nPieces; i++) {
+ rarestPieceSet[i] -= swarm[e.peer][i];
+ swarm[e.peer][i] = 0;
+ }
+
+ cache[e.peer].node = null;
+ cache[e.peer].status = 0;
+ cache[e.peer].interested = -1;
+ unchokedBy[e.peer] = false;
+ this.nNodes--;
+ e.peer = -1;
+ e.ID = Integer.MAX_VALUE;
+ e.valueUP = 0;
+ e.valueDOWN = 0;
+ e.head20 = 0;
+ e.head60 = 0;
+ sortByPeer();
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds a request to the pendingRequest queue.
+ * @param block The requested block
+ * @return true if the request has been successfully added to the queue, false otherwise
+ */
+ private boolean addRequest(int block){
+ int i=4;
+ while(i>=0 && pendingRequest[i]!=-1){
+ i--;
+ }
+ if(i>=0){
+ pendingRequest[i] = block;
+ return true;
+ }
+ else { // It should never happen
+ //System.err.println("pendingRequest queue full");
+ return false;
+ }
+ }
+
+ /**
+ * Removes the block with the given <tt>id</tt> from the {@link #pendingRequest} queue
+ * and sorts the queue leaving the empty cell at the left.
+ * @param id the id of the requested block
+ */
+ private void removeRequest(int id){
+ int i = 4;
+ for(; i>=0; i--){
+ if(pendingRequest[i]==id)
+ break;
+ }
+ for(; i>=0; i--){
+ if(i==0)
+ pendingRequest[i] = -1;
+ else
+ pendingRequest[i] = pendingRequest[i-1];
+ }
+ }
+
+ /**
+ * Requests new block until the {@link #pendingRequest} is full to the sender of the just received piece.
+ * It calls {@link #getNewBlock(Node, int)} to implement the <i>strict priority</i> strategy.
+ * @param node the local node
+ * @param pid the BitTorrent protocol id
+ * @param sender the sender of the just received received piece.
+ */
+ private void requestNextBlocks(Node node, int pid, int sender){
+ int block = getNewBlock(node, pid);
+ while(block != -2){
+ if(unchokedBy[sender]==true && alive(cache[sender].node) && addRequest(block)){
+ Object ev = new IntMsg(REQUEST, node, block,0);
+
+// long latency = ((Transport)node.getProtocol(tid)).getLatency(node,cache[sender].node);
+// EDSimulator.add(latency,ev,cache[sender].node,pid);
+
+ ((Transport) node.getProtocol(tid)).send(node,cache[sender].node, ev, pid);
+ cache[sender].justSent();
+ }
+ else{ // I cannot send request
+ if(!alive(cache[sender].node) && cache[sender].node!=null){
+ System.out.println("piece2 rm neigh "+ cache[sender].node.getID() );
+ removeNeighbor(cache[sender].node);
+ processNeighborListSize(node,pid);
+ }
+ return;
+ }
+ block = getNewBlock(node, pid);
+ }
+ }
+
+ /**
+ * It returns the id of the next block to request. Sends <tt>INTERESTED</tt> if the new
+ * block belongs to a new piece.
+ * It uses {@link #getBlock()} to get the next block of a piece and calls {@link #getPiece()}
+ * when all the blocks for the {@link #currentPiece} have been requested.
+ * @param node the local node
+ * @param pid the BitTorrent protocol id
+ * @return -2 if no more places available in the <tt>pendingRequest</tt> queue;<br/>
+ * the value of the next block to request otherwise</p>
+ */
+ private int getNewBlock(Node node, int pid){
+ int block = getBlock();
+ if(block < 0){ // No more block to request for the current piece
+
+ if(block ==-2) // Pending request queue full
+ return -2;
+
+ int newPiece = getPiece();
+ if(newPiece == -1){ // no more piece to request
+ return -2;
+ }
+
+ lastInterested = newPiece;
+ Object ev = new IntMsg(INTERESTED, node, lastInterested,0);
+
+ for(int j=0; j<swarmSize; j++){// send the interested message to those
+ // nodes which have that piece
+ if(alive(cache[j].node) && swarm[j][newPiece]==1){
+// long latency = ((Transport)node.getProtocol(tid)).getLatency(node,cache[j].node);
+// EDSimulator.add(latency,ev,cache[j].node,pid);
+ ((Transport) node.getProtocol(tid)).send(node,cache[j].node, ev, pid);
+ cache[j].justSent();
+ }
+ if(!alive(cache[j].node)){
+ //System.out.println("piece1 rm neigh "+ cache[j].node.getID() );
+
+ removeNeighbor(cache[j].node);
+ processNeighborListSize(node,pid);
+ }
+ }
+ block = getBlock();
+ return block;
+ }
+ else{
+ // block value referred to a real block
+ return block;
+ }
+ }
+
+ /**
+ * Returns the next block to request for the {@link #currentPiece}.
+ * @return an index of a block of the <tt>currentPiece</tt> if there are still
+ * available places in the {@link #pendingRequest} queue;<br/>
+ * -2 if the <tt>pendingRequest</tt> queue is full;<br/>
+ * -1 if no more blocks to request for the current piece.
+ */
+ private int getBlock(){
+ int i=4;
+ while(i>=0 && pendingRequest[i]!=-1){ // i is the first empty position from the head
+ i--;
+ }
+ if(i==-1){// No places in the pendingRequest available
+ //System.out.println("Pendig request queue full!");
+ return -2;
+ }
+ int j;
+ //The queue is not empty & last requested block belongs to lastInterested piece
+ if(i!=4 && decode(pendingRequest[i+1],0)==lastInterested)
+ j=decode(pendingRequest[i+1],1)+1; // the block following the last requested
+ else // I don't know which is the next block, so I search it.
+ j=0;
+ /* I search another block until the current has been already received.
+ * If in pieceStatus at position j there is a block that belongs to
+ * lastInterested piece, means that the block j has been already
+ * received, otherwise I can request it.
+ */
+ while(j<16 && decode(pieceStatus[j],0)==lastInterested){
+ j++;
+ }
+ if(j==16) // No more block to request for lastInterested piece
+ return -1;
+ return encode(lastInterested,j);
+ }
+
+ /**
+ * Returns the next correct piece to download. It choose the piece by using the
+ * <i>random first</i> and <i>rarest first</i> policy. For the beginning 4 pieces
+ * of a file the first one is used then the pieces are chosen using <i>rarest first</i>.
+ * @see "Documentation about the BitTorrent module"
+ * @return the next piece to download. If the whole file has been requested
+ * -1 is returned.
+ */
+ private int getPiece(){
+ int piece = -1;
+ if(nPieceCompleted < 4){ //Uses random first piece
+ piece = CommonState.r.nextInt(nPieces);
+ while(status[piece]==16 || piece == currentPiece) // until the piece is owned
+ piece = CommonState.r.nextInt(nPieces);
+ return piece;
+ }
+ else{ //Uses rarest piece first
+ int j=0;
+ for(; j<nPieces; j++){ // I find the first not owned piece
+ if(status[j]==0){
+ piece = j;
+ if(piece != lastInterested) // teoretically it works because
+ // there should be only one interested
+ // piece not yet downloaded
+ break;
+ }
+ }
+ if(piece==-1){ // Never entered in the previous 'if' statement; for all
+ // pieces an has been sent
+ return -1;
+ }
+
+ int rarestPieces[] = new int[nPieces-j]; // the pieces with the less number of occurences\
+ rarestPieces[0] = j;
+ int nValues = 1; // number of pieces less distributed in the network
+ for(int i=j+1; i<nPieces; i++){ // Finds the rarest piece not owned
+ if(rarestPieceSet[i]< rarestPieceSet[rarestPieces[0]] && status[i]==0){ // if strictly less than the current one
+ rarestPieces[0] = i;
+ nValues = 1;
+ }
+ if(rarestPieceSet[i]==rarestPieceSet[rarestPieces[0]] && status[i]==0){ // if equal
+ rarestPieces[nValues] = i;
+ nValues++;
+ }
+ }
+
+ piece = CommonState.r.nextInt(nValues); // one of the less owned pieces
+ return rarestPieces[piece];
+ }
+ }
+
+ /**
+ * Returns the file's size as number of pieces of 256KB.
+ * @return number of pieces that compose the file.
+ */
+ public int getNPieces(){
+ return nPieces;
+ }
+ /**
+ * Clone method of the class. Returns a deep copy of the BitTorrent class. Used
+ * by the simulation to initialize the {@link peersim.core.Network}
+ * @return the deep copy of the BitTorrent class.
+ */
+ public Object clone(){
+ Object prot = null;
+ try{
+ prot = (BitTorrent)super.clone();
+ }
+ catch(CloneNotSupportedException e){};
+
+ ((BitTorrent)prot).cache = new Neighbor[swarmSize];
+ for(int i=0; i<swarmSize; i++){
+ ((BitTorrent)prot).cache[i] = new Neighbor();
+ }
+
+ ((BitTorrent)prot).byPeer = new Element[swarmSize];
+ for(int i=0; i<swarmSize; i++){
+ ((BitTorrent)prot).byPeer[i] = new Element();
+ }
+
+ ((BitTorrent)prot).unchokedBy = new boolean[swarmSize];
+
+ ((BitTorrent)prot).byBandwidth = new Element[swarmSize];
+ ((BitTorrent)prot).status = new int[nPieces];
+ ((BitTorrent)prot).pieceStatus = new int[16];
+ for(int i=0; i<16;i++)
+ ((BitTorrent)prot).pieceStatus[i] = -1;
+ ((BitTorrent)prot).pendingRequest = new int[5];
+ for(int i=0; i<5;i++)
+ ((BitTorrent)prot).pendingRequest[i] = -1;
+ ((BitTorrent)prot).rarestPieceSet = new int[nPieces];
+ for(int i=0; i<nPieces;i++)
+ ((BitTorrent)prot).rarestPieceSet[i] = 0;
+ ((BitTorrent)prot).swarm = new int[swarmSize][nPieces];
+ ((BitTorrent)prot).requestToServe = new Queue(20);
+ ((BitTorrent)prot).incomingPieces = new Queue(100);
+ return prot;
+ }
+
+ /**
+ * Sorts {@link #byPeer} array by peer's ID. It implements the <i>InsertionSort</i>
+ * algorithm.
+ */
+ public void sortByPeer(){
+ int i;
+
+ for(int j=1; j<swarmSize; j++) // out is dividing line
+ {
+ Element key = new Element();
+ byPeer[j].copyTo(key) ; // remove marked item
+ i = j-1; // start shifts at out
+ while(i>=0 && (byPeer[i].ID > key.ID)) // until one is smaller,
+ {
+ byPeer[i].copyTo(byPeer[i+1]); // shift item right,
+ i--; // go left one position
+ }
+ key.copyTo(byPeer[i+1]); // insert marked item
+ }
+
+ }
+
+ /**
+ * Sorts the array {@link #byBandwidth} using <i>QuickSort</i> algorithm.
+ * <tt>null</tt> elements and seeders are moved to the end of the array.
+ */
+ public void sortByBandwidth() {
+ quicksort(0, swarmSize-1);
+ }
+
+ /**
+ * Used by {@link #sortByBandwidth()}. It's the implementation of the
+ * <i>QuickSort</i> algorithm.
+ * @param left the leftmost index of the array to sort.
+ * @param right the rightmost index of the array to sort.
+ */
+ private void quicksort(int left, int right) {
+ if (right <= left) return;
+ int i = partition(left, right);
+ quicksort(left, i-1);
+ quicksort(i+1, right);
+ }
+
+ /**
+ * Used by {@link #quicksort(int, int)}, partitions the subarray to sort returning
+ * the splitting point as stated by the <i>QuickSort</i> algorithm.
+ * @see "The <i>QuickSort</i> algorithm".
+ */
+ private int partition(int left, int right) {
+ int i = left - 1;
+ int j = right;
+ while (true) {
+ while (greater(byBandwidth[++i], byBandwidth[right])) // find item on left to swap
+ ; // a[right] acts as sentinel
+ while (greater(byBandwidth[right], byBandwidth[--j])) { // find item on right to swap
+ if (j == left) break; // don't go out-of-bounds
+ }
+ if (i >= j) break; // check if pointers cross
+ swap(i, j); // swap two elements into place
+ }
+ swap(i, right); // swap with partition element
+ return i;
+ }
+
+ /**
+ * Aswers to the question "is x > y?". Compares the {@link Element}s given as
+ * parameters. <tt>Element x</tt> is greater than <tt>y</tt> if isn't <tt>null</tt>
+ * and in the last 20 seconds the local node has downloaded ("uploaded" if the local node is a
+ * seeder) more blocks than from <tt>y</tt>.
+ * @param x the first <tt>Element</tt> to compare.
+ * @param y the second <tt>Element</tt> to compare
+ * @return <tt>true</tt> if x > y;<br/>
+ * <tt>false</tt> otherwise.
+ */
+ private boolean greater(Element x, Element y) {
+ /*
+ * Null elements and seeders are shifted at the end
+ * of the array
+ */
+ if (x==null) return false;
+ if (y==null) return true;
+ if (x.isSeeder) return false;
+ if (y.isSeeder) return true;
+
+ // if the local node is a leecher
+ if (peerStatus==0) {
+ if ((x.valueDOWN - x.head20) >
+ (y.valueDOWN -y.head20))
+ return true;
+ else return false;
+ }
+
+ // if peerStatus==1 (the local node is a seeder)
+ else {
+ if ((x.valueUP - x.head20) >
+ (y.valueUP -y.head20))
+ return true;
+ else return false;
+ }
+ }
+
+ /**
+ * Swaps {@link Element} <tt>i</tt> with <tt>j</tt> in the {@link #byBandwidth}.<br/>
+ * Used by {@link #partition(int, int)}
+ * @param i index of the first element to swap
+ * @param j index of the second element to swap
+ */
+ private void swap(int i, int j) {
+ Element swap = byBandwidth[i];
+ byBandwidth[i] = byBandwidth[j];
+ byBandwidth[j] = swap;
+ }
+
+ /** Searches the node with the given ID. It does a dychotomic
+ * search.
+ * @param ID ID of the node to search.
+ * @return the {@link Element} in {@link #byPeer} which represents the node with the
+ * given ID.
+ */
+ public Element search(long ID){
+ int low = 0;
+ int high = swarmSize-1;
+ int p = low+((high-low)/2); //Initial probe position
+ while ( low <= high) {
+ if ( byPeer[p] == null || byPeer[p].ID > ID)
+ high = p - 1;
+ else {
+ if( byPeer[p].ID < ID ) //Wasteful second comparison forced by syntax limitations.
+ low = p + 1;
+ else
+ return byPeer[p];
+ }
+ p = low+((high-low)/2); //Next probe position.
+ }
+ return null;
+ }
+}
+
+/**
+ * This class is used to store the main informations about a neighbors regarding
+ * the calculation of the Downloading/Uploading rates. Is the class of items in
+ * {@link example.bittorrent.BitTorrent#byPeer} and {@link example.bittorrent.BitTorrent#byBandwidth}.
+ */
+class Element{
+ /**
+ * ID of the represented node.
+ */
+ public long ID = Integer.MAX_VALUE;
+ /**
+ * Index position of the node in the {@link example.bittorrent.BitTorrent#cache} array.
+ */
+ public int peer = -1;
+ /**
+ * Number of blocks uploaded to anyone since the beginning.
+ */
+ public int valueUP = 0;
+ /**
+ * Number of blocks downloaded from anyone since the beginning.
+ */
+ public int valueDOWN = 0;
+ /**
+ * Value of either {@link #valueUP} or {@link #valueDOWN} (depending by
+ * {@link example.bittorrent.BitTorrent#peerStatus}) 20 seconds before.
+ */
+ public int head20 = 0;
+ /**
+ * Value of either {@link #valueUP} or {@link #valueDOWN} (depending by
+ * {@link example.bittorrent.BitTorrent#peerStatus}) 60 seconds before.
+ */
+ public int head60 = 0;
+ /**
+ * <tt>true</tt> if the node is a seeder, <tt>false</tt> otherwise.
+ */
+ public boolean isSeeder = false;
+ /**
+ * Makes a deep copy of the Element to <tt>destination</tt>
+ * @param destination Element instance where to make the copy
+ */
+ public void copyTo(Element destination){
+ destination.ID = this.ID;
+ destination.peer = this.peer;
+ destination.valueUP = this.valueUP;
+ destination.valueDOWN = this.valueDOWN;
+ destination.head20 = this.head20;
+ destination.head60 = this.head60;
+ }
+}
+
+/**
+ * This class stores information about the neighbors regarding their status. It is
+ * the type of the items in the {@link example.bittorrent.BitTorrent#cache}.
+ */
+class Neighbor{
+ /**
+ * Reference to the node in the {@link peersim.core.Network}.
+ */
+ public Node node = null;
+ /**
+ * -1 means not interested<br/>
+ * Other values means the last piece number for which the node is interested.
+ */
+ public int interested;
+ /**
+ * 0 means CHOKED<br/>
+ * 1 means UNCHOKED<br/>
+ * 2 means SNUBBED_BY. If this value is set and the node is to be unchocked,
+ * value 2 has the priority.
+ */
+ public int status;
+ /**
+ * Last time a message from the node represented has been received.
+ */
+ public long lastSeen = 0;
+ /**
+ * Last time a message to the node represented has been sent.
+ */
+ public long lastSent = 0;
+
+ /**
+ * Sets the last time the neighbor was seen.
+ */
+ public void isAlive(){
+ long now = CommonState.getTime();
+ this.lastSeen = now;
+ }
+
+ /*
+ * Sets the last time the local peer sent something to the neighbor.
+ */
+ public void justSent(){
+ long now = CommonState.getTime();
+ this.lastSent = now;
+ }
+
+}
+
+/**
+ * Class type of the queues's items in {@link example.bittorrent.BitTorrent#incomingPieces}
+ * and {@link example.bittorrent.BitTorrent#requestToServe}.
+ */
+class Queue{
+ int maxSize;
+ int head = 0;
+ int tail = 0;
+ int dim = 0;
+ Request queue[];
+
+ /**
+ * Public constructor. Creates a queue of size <tt>size</tt>.
+ */
+ public Queue(int size){
+ maxSize = size;
+ queue = new Request[size];
+ for(int i=0; i< size; i++)
+ queue[i]= new Request();
+ }
+
+ /**
+ * Enqueues the request of the block <tt>id</tt> and its <tt>sender</tt>
+ * @param id the id of the block in the request
+ * @param sender a reference to the sender of the request
+ * @return <tt>true</tt> if the request has been correctly added, <tt>false</tt>
+ * otherwise.
+ */
+ public boolean enqueue(int id, Node sender){
+ if(dim < maxSize){
+ queue[tail%maxSize].id = id;
+ queue[tail%maxSize].sender = sender;
+ tail++;
+ dim++;
+ return true;
+ }
+ else return false;
+ }
+
+ /**
+ * Returns the {@link Request} in the head of the queue.
+ * @return the element in the head.<br/>
+ * <tt>null</tt> if the queue is empty.
+ */
+ public Request dequeue(){
+ Request value;
+ if(dim > 0){
+ value = queue[head%maxSize];
+ head++;
+ dim--;
+ return value;
+ }
+ else return null; //empty queue
+ }
+
+ /**
+ * Returns the status of the queue.
+ * @return <tt>true</tt> if the queue is empty, <tt>false</tt>
+ * otherwise.
+ */
+ public boolean empty(){
+ return (dim == 0);
+ }
+
+ /**
+ * Returns <tt>true</tt> if block given as parameter is in.
+ * @param value the id of the block to search.
+ * @return <tt>true</tt> if the block <tt>value</tt> is in the queue, <tt>false</tt>
+ * otherwise.
+ */
+ public boolean contains(int value){
+ if(empty())
+ return false;
+ for(int i=head; i<head+dim; i++){
+ if(queue[i%maxSize].id == value)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes a request from the queue.
+ * @param sender the sender of the request.
+ * @param value the id of the block requested.
+ * @return <tt>true</tt> if the request has been correctly removed, <tt>false</tt>
+ * otherwise.
+ */
+ public boolean remove(Node sender, int value){
+ if(empty())
+ return false;
+ for(int i=head; i<head+dim; i++){
+ if(queue[i%maxSize].id == value && queue[i%maxSize].sender == sender){
+ for(int j=i; j>head; j--){ // Shifts the elements for the removal
+ queue[j%maxSize]= queue[(j-1)%maxSize];
+ }
+ head++;
+ dim--;
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+/**
+ * This class represent an enqueued request of a block.
+ */
+class Request{
+ /**
+ * The id of the block.
+ */
+ public int id;
+ /**
+ * The sender of the request.
+ */
+ public Node sender;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+import peersim.core.*;
+
+/**
+ * This class is a {@link SimpleMsg} and represents the <tt>bitfield</tt>
+ * message.
+ */
+public class BitfieldMsg extends SimpleMsg{
+ /**
+ * The status of the file to transmit to neighbors nodes
+ */
+ int[] array;
+
+ /**
+ * Defines the type of the Bitfield message. If <tt>isRequest</tt> is true, then
+ * the message is a request of subscription; otherwise the message is a response.
+ */
+ boolean isRequest;
+
+ /**
+ * <p>The ACK value used to implement <i>ack</i> and <i>nack</i> messages.</p>
+ * <p>It has value <tt>true</tt> if the message is a reponse and the sender has inserted
+ * the receiver in its own cache of neighbors.<br/>
+ * If for some reason (for instance the cache had already 80 peer inside at the moment of the
+ * request) it was not possible to insert the peer, the value is <tt>false</tt>.<br/>
+ * It has value <tt>false</tt> also if the message is a request and is sent when occours
+ * an unespected message.
+ * </p>
+ * @see "The documentation to understand the 4 different types of Bitfield messages"
+ */
+ boolean ack;
+
+ /**
+ * The basic constructor of the Bitfield message.
+ * @param type The type of the message, according to {@link SimpleMsg}
+ * @param isRequest Defines if the message is a request or not
+ * @param ack Defines if the message type is an <i>ack</i> or a <i>nack</i>
+ * @param sender The sender node
+ * @param source The array containing the status of the file
+ * @param size The size of the array
+ */
+ public BitfieldMsg(int type, boolean isRequest, boolean ack, Node sender, int source[], int size){
+ super.type = type;
+ super.sender = sender;
+ this.isRequest = isRequest;
+ this.ack = ack;
+ this.array = new int[size];
+ for(int i=0; i<size;i++){ // it sends a copy
+ if(source[i]==16)
+ this.array[i] = 1;
+ else
+ this.array[i] = 0;
+ }
+ }
+
+ /**
+ * Gets the array containing the status of the file.
+ * @return The status of the file
+ */
+ public int[] getArray(){
+ return this.array;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+import peersim.core.*;
+import psgsim.Sizable;
+
+/**
+ * This class is a {@link SimpleMsg} and acts as a container for a message that
+ * uses only an integer value.
+ */
+public class IntMsg extends SimpleMsg implements Sizable {
+
+ /**
+ * The data value (an integer) contained in the message.
+ */
+ private int integer;
+ private double size;
+
+ /**
+ * The basic constructor of the message.
+ *
+ * @param type
+ * the type of the message
+ * @param sender
+ * The sender node
+ * @param value
+ * The data value of the message
+ */
+ public IntMsg(int type, Node sender, int value, double size) {
+ super.type = type;
+ super.sender = sender;
+ this.integer = value;
+ this.size = size;
+ }
+
+ /**
+ * Gets the value contained in the message.
+ *
+ * @return the integer value contained in the message
+ */
+ public int getInt() {
+ return this.integer;
+ }
+
+ @Override
+ public double getSize() {
+ // TODO Auto-generated method stub
+ return size;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.transport.*;
+import peersim.edsim.*;
+
+/**
+ * This {@link Control} can change the size of networks by adding and removing
+ * nodes. This class supports only permanent removal of nodes and the addition
+ * of brand new nodes. That is, temporary downtime is not supported by this
+ * class.
+ */
+public class NetworkDynamics implements Control {
+ private static final int TRACKER = 11;
+ private static final int CHOKE_TIME = 13;
+ private static final int OPTUNCHK_TIME = 14;
+ private static final int ANTISNUB_TIME = 15;
+ private static final int CHECKALIVE_TIME = 16;
+ private static final int TRACKERALIVE_TIME = 17;
+
+ /**
+ * The protocol to operate on.
+ *
+ * @config
+ */
+ private static final String PAR_PROT = "protocol";
+
+ /**
+ * Nodes are removed until the size specified by this parameter is reached.
+ * The network will never go below this size as a result of this class.
+ * Defaults to 0.
+ *
+ * @config
+ */
+ private static final String PAR_MIN = "minsize";
+
+ /**
+ * Specifies if the tracker can disappear from the network. 0 means no, 1
+ * means yes
+ *
+ * @config
+ */
+ private static final String PAR_TRACKER_DIE = "tracker_can_die";
+
+ /**
+ * The Transport used by the the control.
+ *
+ * @config
+ */
+ private static final String PAR_TRANSPORT = "transport";
+
+ /**
+ * Specifies how many nodes will be added to the network.
+ *
+ * @config
+ */
+ private static final String PAR_ADD = "add";
+
+ /**
+ * Specifies how many nodes will be removed from the network.
+ *
+ * @config
+ */
+ private static final String PAR_REMOVE = "remove";
+
+ /*
+ * The following are local variables, obtained from config property.
+ */
+ private final int pid;
+ private final int tid;
+ private final int maxSize;
+ private final int minsize;
+ private boolean trackerCanDie = false; // false (value 0) by default
+ private final int add; // number of nodes to be added
+ private final int remove; // number of nodes to be removed
+
+ private final NodeInitializer init;
+ private Node tracker;
+
+ /**
+ * Standard constructor that reads the configuration parameters. Invoked by
+ * the simulation engine.
+ *
+ * @param prefix
+ * the configuration prefix for this class
+ */
+ public NetworkDynamics(String prefix) {
+ pid = Configuration.getPid(prefix + "." + PAR_PROT);
+ minsize = Configuration.getInt(prefix + "." + PAR_MIN, 0);
+ tid = Configuration.getPid(prefix + "." + PAR_TRANSPORT);
+ add = Configuration.getInt(prefix + "." + PAR_ADD);
+ remove = Configuration.getInt(prefix + "." + PAR_REMOVE);
+
+ /*
+ * By default, the tracker can not disappear. If
+ * control.dynamics.tracker_can_die is set to 1, the tracker can die.
+ */
+ if (Configuration.getInt(prefix + "." + PAR_TRACKER_DIE) == 1) {
+ trackerCanDie = true;
+ }
+
+ init = new NodeInitializer("init.net");
+ tracker = Network.get(0);
+
+ maxSize = (Network.size() - 1)
+ + ((BitTorrent) tracker.getProtocol(pid)).maxGrowth;
+ }
+
+ /**
+ * Adds n nodes to the network. New nodes can be added only if the tracker
+ * is up.
+ *
+ * @param n
+ * the number of nodes to add, must be non-negative.
+ */
+ protected void add(int n) {
+ if (n == 0)
+ return;
+ // tracker is up
+ if (tracker.isUp()) {
+ for (int i = 0; i < n; ++i) {
+ // create a new node
+ Node nodeToBeAdded = (Node) Network.prototype.clone();
+
+ // add the new node to the network
+ Network.add(nodeToBeAdded); // questo nodo sara' in posizione
+ // Network.len -1
+
+ /*
+ * Initialize the new node using the NodeInitializer class; this
+ * it the same as
+ * init.initialize(Network.get(Network.size()-1));
+ */
+ init.initialize(nodeToBeAdded);
+
+ /*
+ * The new node sends a TRACKER message to the tracker, asking
+ * for a list of peers. The tracker will respond with a PEERSET
+ * message. All the related events are also attached to the new
+ * node.
+ */
+ long latency =((Transport)nodeToBeAdded.getProtocol(tid)).getLatency(nodeToBeAdded,tracker);
+ Object ev = new SimpleMsg(TRACKER, nodeToBeAdded);
+ EDSimulator.add(latency,ev,tracker,pid);
+// ((Transport) nodeToBeAdded.getProtocol(tid)).send(
+// nodeToBeAdded, tracker, ev, pid);
+
+ ev = new SimpleEvent(CHOKE_TIME);
+ EDSimulator.add(10000, ev, nodeToBeAdded, pid);
+ ev = new SimpleEvent(OPTUNCHK_TIME);
+ EDSimulator.add(30000, ev, nodeToBeAdded, pid);
+ ev = new SimpleEvent(ANTISNUB_TIME);
+ EDSimulator.add(60000, ev, nodeToBeAdded, pid);
+ ev = new SimpleEvent(CHECKALIVE_TIME);
+ EDSimulator.add(120000, ev, nodeToBeAdded, pid);
+ ev = new SimpleEvent(TRACKERALIVE_TIME);
+ EDSimulator.add(1800000, ev, nodeToBeAdded, pid);
+
+ // add the new node to the tracker's cache
+ if (((BitTorrent) tracker.getProtocol(pid))
+ .addNeighbor(nodeToBeAdded))
+ System.out
+ .println("DYN: A new node has been added to the network.");
+ }
+ }
+ /*
+ * Otherwise, the tracker is down and no new nodes can be added to the
+ * network.
+ */
+ else
+ System.out.println("DYN: Tracker is down. No new nodes added.");
+ }
+
+ /**
+ * Removes n nodes from the network. A node can be removed either if the
+ * tracker is up or down; if the tracker is up, the node to be removed will
+ * be removed also from the tracker's cache.
+ *
+ * @param n
+ * the number of nodes to remove.
+ */
+ protected void remove(int n) {
+ // the index of the node to be removed
+ int nodeIndex = 0;
+
+ for (int i = 0; i < n; ++i) {
+ nodeIndex = CommonState.r.nextInt(Network.size());
+ // if the tracker can not disappear from the network
+ if (!trackerCanDie) {
+ /*
+ * Choose an index for the node to be removed. The value 0 will
+ * be discarded, since the tracker cannot disappear. Non
+ * existing nodes cannot be removed: if the returned index
+ * corresponds to a non-existing node, a new index will be
+ * generated.
+ */
+ while (nodeIndex == 0) {
+ nodeIndex = CommonState.r.nextInt(Network.size());
+ }
+ }
+ // otherwise, also the tracker can disappear
+ else {
+ nodeIndex = CommonState.r.nextInt(Network.size());
+ }
+
+ // a warning message
+ // if (nodeIndex==0)
+ // System.out.println("DYN: The tracker is going to disapper.");
+
+ // remove the node with the given index from the network
+ Node nodeToBeRemoved = Network.remove(nodeIndex);
+
+ // then remove it from the tracker's cache, if it is possible (= the
+ // tracker is up);
+ if (tracker.isUp()) {
+ if (((BitTorrent) tracker.getProtocol(pid))
+ .removeNeighbor(nodeToBeRemoved))
+ System.err
+ .println("DYN: A node has been removed from the network.");
+ } else { // the tracker is down
+ System.err.println("DYN: The tracker is DOWN!");
+ }
+ }
+ }
+
+ /**
+ * Calls {@link #add(int)} or {@link #remove} with the parameters defined by
+ * the configuration.
+ *
+ * @return always false
+ */
+ public boolean execute() {
+ int choice = (CommonState.r.nextInt(2)); // 0 or 1
+ // adding new nodes
+ if (choice == 0) {
+ /*
+ * If the specified number of nodes cannot be added, it tries to add
+ * a less number of nodes without going out of bounds. Otherwise,
+ * all specified nodes will be added.
+ */
+ if (Network.size() + this.add > maxSize) {
+ System.err.println("DYN: " + (maxSize - Network.size())
+ + " nodes will be added.");
+ add(maxSize - Network.size());
+ } else {
+ System.err
+ .println("DYN: " + this.add + " nodes will be added.");
+ add(this.add);
+ }
+ }
+ // removing existing nodes
+ else {
+ if (Network.size() - this.remove < minsize) {
+ System.err.println("DYN: " + (Network.size() - minsize)
+ + " nodes will be removed.");
+ remove(Network.size() - minsize);
+ } else {
+ System.err.println("DYN: " + this.remove
+ + " nodes will be removed.");
+ remove(this.remove);
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+import peersim.core.*;
+import peersim.config.Configuration;
+import peersim.edsim.EDSimulator;
+import peersim.transport.Transport;
+import java.util.Random;
+
+/**
+ * This {@link Control} ...
+ */
+public class NetworkInitializer implements Control {
+ /**
+ * The protocol to operate on.
+ *
+ * @config
+ */
+ private static final String PAR_PROT="protocol";
+
+ private static final String PAR_TRANSPORT="transport";
+
+ private static final int TRACKER = 11;
+
+ private static final int CHOKE_TIME = 13;
+
+ private static final int OPTUNCHK_TIME = 14;
+
+ private static final int ANTISNUB_TIME = 15;
+
+ private static final int CHECKALIVE_TIME = 16;
+
+ private static final int TRACKERALIVE_TIME = 17;
+
+ /** Protocol identifier, obtained from config property */
+ private final int pid;
+ private final int tid;
+ private NodeInitializer init;
+
+ private Random rnd;
+
+ public NetworkInitializer(String prefix) {
+ pid = Configuration.getPid(prefix+"."+PAR_PROT);
+ tid = Configuration.getPid(prefix+"."+PAR_TRANSPORT);
+ init = new NodeInitializer(prefix);
+ }
+
+ public boolean execute() {
+ int completed;
+ Node tracker = Network.get(0);
+
+ // manca l'inizializzazione del tracker;
+
+ ((BitTorrent)Network.get(0).getProtocol(pid)).initializeTracker();
+
+ for(int i=1; i<Network.size(); i++){
+ System.err.println("chiamate ad addNeighbor " + i);
+ ((BitTorrent)Network.get(0).getProtocol(pid)).addNeighbor(Network.get(i));
+ init.initialize(Network.get(i));
+ }
+ for(int i=1; i< Network.size(); i++){
+ Node n = Network.get(i);
+
+ Object ev = new SimpleMsg(TRACKER, n);
+ long latency = ((Transport)n.getProtocol(tid)).getLatency(n,tracker);
+ EDSimulator.add(latency,ev,tracker,pid);
+// ((Transport) n.getProtocol(tid)).send(n,tracker, ev, pid);
+
+ ev = new SimpleEvent(CHOKE_TIME);
+ EDSimulator.add(10000,ev,n,pid);
+ ev = new SimpleEvent(OPTUNCHK_TIME);
+ EDSimulator.add(30000,ev,n,pid);
+ ev = new SimpleEvent(ANTISNUB_TIME);
+ EDSimulator.add(60000,ev,n,pid);
+ ev = new SimpleEvent(CHECKALIVE_TIME);
+ EDSimulator.add(120000,ev,n,pid);
+ ev = new SimpleEvent(TRACKERALIVE_TIME);
+ EDSimulator.add(1800000,ev,n,pid);
+ }
+ return true;
+ }
+
+ }
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+import peersim.core.*;
+import peersim.config.Configuration;
+
+/**
+ * This class provides a way to initialize a single node of the network.
+ * The initialization is performed by choosing the bandwidth of the node
+ * and choosing how much the shared file has been downloaded.
+ */
+public class NodeInitializer{
+
+ /**
+ * The protocol to operate on.
+ * @config
+ */
+ private static final String PAR_PROT="protocol";
+
+ /**
+ * The percentage of nodes with no downloaded pieces.
+ * @config
+ * @see "The documentation for an example on how to properly set this parameter."
+ */
+ private static final String PAR_NEWER_DISTR="newer_distr";
+
+ /**
+ * The percentage of seeders in the network.
+ * @config
+ */
+ private static final String PAR_SEEDER_DISTR="seeder_distr";
+
+ /**
+ * The percentage of nodes with no downloaded pieces,
+ * as defined in {@see #PAR_NEWER_DISTR}.
+ */
+ private int newerDistr;
+
+ /**
+ * The percentage of seeder nodes,
+ * as defined in {@see #PAR_SEEDER_DISTR}.
+ */
+ private int seederDistr;
+
+ /**
+ * The BitTorrent protocol ID.
+ */
+ private final int pid;
+
+ /**
+ * The basic constructor of the class, which reads the parameters
+ * from the configuration file.
+ * @param prefix the configuration prefix for this class
+ */
+ public NodeInitializer(String prefix){
+ pid = Configuration.getPid(prefix+"."+PAR_PROT);
+ newerDistr = Configuration.getInt(prefix+"."+PAR_NEWER_DISTR);
+ seederDistr = Configuration.getInt(prefix+"."+PAR_SEEDER_DISTR);
+ }
+
+ /**
+ * Initializes the node <tt>n</tt> associating it
+ * with the BitTorrent protocol and setting the reference to the tracker,
+ * the status of the file and the bandwidth.
+ * @param n The node to initialize
+ */
+ public void initialize(Node n){
+ Node tracker = Network.get(0);
+ BitTorrent p;
+ p = (BitTorrent)n.getProtocol(pid);
+ p.setTracker(tracker);
+ p.setThisNodeID(n.getID());
+ setFileStatus(p);
+ setBandwidth(p);
+ }
+
+ /**
+ * Sets the status of the shared file according to the
+ * probability value given by {@link #getProbability()}.
+ * @param p The BitTorrent protocol
+ */
+ private void setFileStatus(BitTorrent p){
+ int percentage = getProbability();
+ choosePieces(percentage, p);
+ }
+
+ /**
+ * Set the maximum bandwidth for the node, choosing
+ * uniformly at random among 4 values.
+ * <p>
+ * The allowed bandwidth speed are 640 Kbps, 1 Mbps, 2 Mbps and 4 Mbps.
+ * </p>
+ * @param p The BitTorrent protocol
+ */
+ private void setBandwidth(BitTorrent p){
+ int value = CommonState.r.nextInt(4);
+ switch(value){
+ case 0: p.setBandwidth(640);break; //640Kbps
+ case 1: p.setBandwidth(1024);break;// 1Mbps
+ case 2: p.setBandwidth(2048);break;// 2Mbps
+ case 3: p.setBandwidth(4096);break; //4Mbps
+ }
+ }
+
+ /**
+ * Sets the completed pieces for the given protocol <tt>p</tt>.
+ * @parm percentage The percentage of the downloaded pieces, according to {@link #getProbability()}
+ * @param p the BitTorrent protocol
+ */
+ private void choosePieces(int percentage, BitTorrent p){
+ double temp = ((double)p.nPieces/100.0)*percentage; // We use a double to avoid the loss of precision
+ // during the division operation
+ int completed = (int)temp; //integer number of piece to set as completed
+ //0 if the peer is a newer
+ p.setCompleted(completed);
+ if(percentage == 100)
+ p.setPeerStatus(1);
+ int tmp;
+ while(completed!=0){
+ tmp = CommonState.r.nextInt(p.nPieces);
+ if(p.getStatus(tmp)!=16){
+ p.setStatus(tmp, 16);
+ completed--;
+ }
+ }
+ }
+
+ /**
+ * Gets a probability according with the parameter <tt>newer_distr</tt>
+ * defined in the configuration file.
+ * @return the probabilty value, where 0 means that the peer is new and no pieces has been downloaded,
+ * 100 means that the peer is a seeder; other values defines a random probability.
+ * @see #PAR_NEWER_DISTR
+ */
+ private int getProbability(){
+ int value = CommonState.r.nextInt(100);
+ if((value+1)<=seederDistr)
+ return 100;
+ value = CommonState.r.nextInt(100);
+ if((value+1)<=newerDistr){
+ return 0; // A newer peer, with probability newer_distr
+ }
+ else{
+ value = CommonState.r.nextInt(9);
+ return (value+1)*10;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+package example.bittorrent;
+
+import peersim.core.*;
+
+/**
+ * This class is a {@link SimpleMsg} and represents the <tt>peerset</tt>
+ * message used by the tracker to send to the peers a list of neighbors.
+ */
+public class PeerSetMsg extends SimpleMsg{
+
+ /**
+ * The set of "friends" peers sent by the tracker to each node.
+ */
+ private Neighbor[] peerSet;
+
+ /**
+ * Initializes a new <tt>peerset</tt> message.
+ * @param type is the type of the message (it should be 12)
+ * @param array is the array containing the references to the neighbor nodes
+ * @param sender the sender node
+ * @see SimpleEvent
+ */
+ public PeerSetMsg(int type, Neighbor []array, Node sender){
+ super.type = type;
+ peerSet = array; // references to the effective nodes
+ super.sender = sender;
+ }
+
+ /**
+ * Gets the peer set.
+ * @return the peer set, namely the set of neighbor nodes.
+ */
+ public Neighbor[] getPeerSet(){
+ return this.peerSet;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+/**
+ * This class defines a simple event. A simple event is characterized only
+ * by its type.
+ */
+public class SimpleEvent {
+
+ /**
+ * The identifier of the type of the event.
+ * <p>
+ * The available identifiers for event type are:<br/>
+ * <ul>
+ * <li>1 is KEEP_ALIVE message</li>
+ * <li>2 is CHOKE message</li>
+ * <li>3 is UNCHOKE message</li>
+ * <li>4 is INTERESTED message</li>
+ * <li>5 is NOT_INTERESTED message</li>
+ * <li>6 is HAVE message</li>
+ * <li>7 is BITFIELD message</li>
+ * <li>8 is REQUEST message</li>
+ * <li>9 is PIECE message</li>
+ * <li>10 is CANCEL message</li>
+ * <li>11 is TRACKER message</li>
+ * <li>12 is PEERSET message</li>
+ * <li>13 is CHOKE_TIME event</li>
+ * <li>14 is OPTUNCHK_TIME event</li>
+ * <li>15 is ANTISNUB_TIME event</li>
+ * <li>16 is CHECKALIVE_TIME event</li>
+ * <li>17 is TRACKERALIVE_TIME event</li>
+ * <li>18 is DOWNLOAD_COMPLETED event</li>
+ *</ul></p>
+ */
+ protected int type;
+
+ public SimpleEvent(){
+ }
+
+ /**
+ * Initializes the type of the event.
+ * @param type The identifier of the type of the event
+ */
+ public SimpleEvent(int type){
+ this.type = type;
+ }
+
+ /**
+ * Gets the type of the event.
+ * @return The type of the current event.
+ */
+ public int getType(){
+ return this.type;
+ }
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2008 Fabrizio Frioli, Michele Pedrolli
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Please send your questions/suggestions to:
+ * {fabrizio.frioli, michele.pedrolli} at studenti dot unitn dot it
+ *
+ */
+
+package example.bittorrent;
+
+import peersim.core.*;
+
+/**
+ * This class defines a simple message.
+ * A simple message has its type and the reference of the sender node.
+ * @see SimpleEvent
+ */
+public class SimpleMsg extends SimpleEvent {
+
+ /**
+ * The sender of the message.
+ */
+ protected Node sender;
+
+ public SimpleMsg(){
+ }
+
+ /**
+ * Initializes the simple message with its type and sender.
+ * @param type The identifier of the type of the message
+ * @param sender The sender of the message
+ */
+ public SimpleMsg(int type, Node sender){
+ super.type = type;
+ this.sender = sender;
+ }
+
+ /**
+ * Gets the sender of the message.
+ * @return The sender of the message.
+ */
+ public Node getSender(){
+ return this.sender;
+ }
+}
--- /dev/null
+package example.chord;\r
+\r
+import java.math.BigInteger;\r
+import java.util.Random;\r
+import peersim.config.Configuration;\r
+import peersim.core.CommonState;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+import peersim.dynamics.NodeInitializer;\r
+\r
+public class ChordInitializer implements NodeInitializer {\r
+\r
+ private static final String PAR_PROT = "protocol";\r
+\r
+ private int pid = 0;\r
+\r
+ private ChordProtocol cp;\r
+\r
+ public ChordInitializer(String prefix) {\r
+ pid = Configuration.getPid(prefix + "." + PAR_PROT);\r
+ }\r
+\r
+ public void initialize(Node n) {\r
+ cp = (ChordProtocol) n.getProtocol(pid);\r
+ join(n);\r
+ }\r
+\r
+ public void join(Node myNode) {\r
+ Random generator = new Random();\r
+ //Random generator = new Random(1234567890);\r
+ cp.predecessor = null;\r
+ // search a node to join\r
+ Node n;\r
+ do {\r
+ n = Network.get(generator.nextInt(Network.size()));\r
+ } while (n == null || n.isUp() == false);\r
+ cp.m = ((ChordProtocol) n.getProtocol(pid)).m;\r
+ cp.chordId = new BigInteger(cp.m, CommonState.r);\r
+ ChordProtocol cpRemote = (ChordProtocol) n.getProtocol(pid);\r
+\r
+ Node successor = cpRemote.find_successor(cp.chordId);\r
+ cp.fails = 0;\r
+ cp.stabilizations = 0;\r
+ cp.varSuccList = cpRemote.varSuccList;\r
+ cp.varSuccList = 0;\r
+ cp.succLSize = cpRemote.succLSize;\r
+ cp.successorList = new Node[cp.succLSize];\r
+ cp.successorList[0] = successor;\r
+ cp.fingerTable = new Node[cp.m];\r
+ long succId = 0;\r
+ BigInteger lastId = ((ChordProtocol) Network.get(Network.size() - 1)\r
+ .getProtocol(pid)).chordId;\r
+ do {\r
+ cp.stabilizations++;\r
+ succId = cp.successorList[0].getID();\r
+ cp.stabilize(myNode);\r
+ if (((ChordProtocol) cp.successorList[0].getProtocol(pid)).chordId\r
+ .compareTo(cp.chordId) < 0) {\r
+ cp.successorList[0] = ((ChordProtocol) cp.successorList[0]\r
+ .getProtocol(pid)).find_successor(cp.chordId);\r
+ }\r
+ // controllo di non essere l'ultimo elemento della rete\r
+ if (cp.chordId.compareTo(lastId) > 0) {\r
+ cp.successorList[0] = Network.get(0);\r
+ break;\r
+ }\r
+ } while (cp.successorList[0].getID() != succId\r
+ || ((ChordProtocol) cp.successorList[0].getProtocol(pid)).chordId\r
+ .compareTo(cp.chordId) < 0);\r
+ cp.fixFingers();\r
+ }\r
+}\r
--- /dev/null
+/**\r
+ * \r
+ */\r
+package example.chord;\r
+\r
+/**\r
+ * @author Andrea\r
+ * \r
+ */\r
+public interface ChordMessage {\r
+\r
+}\r
--- /dev/null
+/**\r
+ * \r
+ */\r
+package example.chord;\r
+\r
+import peersim.config.Configuration;\r
+import peersim.core.CommonState;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+import peersim.edsim.EDProtocol;\r
+import peersim.transport.Transport;\r
+\r
+import java.math.*;\r
+\r
+import org.simgrid.msg.Host;\r
+import org.simgrid.msg.Msg;\r
+\r
+/**\r
+ * @author Andrea\r
+ * \r
+ */\r
+public class ChordProtocol implements EDProtocol {\r
+\r
+ private static final String PAR_TRANSPORT = "transport";\r
+\r
+ private Parameters p;\r
+\r
+ private int[] lookupMessage;\r
+\r
+ public int index = 0;\r
+\r
+ public Node predecessor;\r
+\r
+ public Node[] fingerTable;\r
+\r
+ public Node[] successorList;\r
+\r
+ public BigInteger chordId;\r
+\r
+ public int m;\r
+\r
+ public int succLSize;\r
+\r
+ public String prefix;\r
+\r
+ private int next = 0;\r
+\r
+ // campo x debug\r
+ private int currentNode = 0;\r
+\r
+ public int varSuccList = 0;\r
+\r
+ public int stabilizations = 0;\r
+\r
+ public int fails = 0;\r
+\r
+ /**\r
+ * \r
+ */\r
+ public ChordProtocol(String prefix) {\r
+ this.prefix = prefix;\r
+ lookupMessage = new int[1];\r
+ lookupMessage[0] = 0;\r
+ p = new Parameters();\r
+ p.tid = Configuration.getPid(prefix + "." + PAR_TRANSPORT);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see peersim.edsim.EDProtocol#processEvent(peersim.core.Node, int,\r
+ * java.lang.Object)\r
+ */\r
+ public void processEvent(Node node, int pid, Object event) {\r
+ // processare le richieste a seconda della routing table del nodo\r
+ p.pid = pid;\r
+ // currentNode = node.getIndex();\r
+ currentNode = (int) node.getID();\r
+ if (event.getClass() == LookUpMessage.class) {\r
+ LookUpMessage message = (LookUpMessage) event;\r
+ message.increaseHopCounter();\r
+ BigInteger target = message.getTarget();\r
+ Transport t = (Transport) node.getProtocol(p.tid);\r
+ Node n = message.getSender();\r
+ System.out.println("R process " + "at time="\r
+ + CommonState.getTime() + " to dest:" + currentNode\r
+ + " from src:" + n.getID() + " message: ("\r
+ + message.getSender().getID() + ";" + message.getTarget()\r
+ + ")");\r
+ if (target == ((ChordProtocol) node.getProtocol(pid)).chordId) {\r
+ // mandare mess di tipo final\r
+ Object msg = new FinalMessage(message.getHopCounter());\r
+ System.out.println("S Final Message " + "at time="\r
+ + CommonState.getTime() + " from src:" + node.getID()\r
+ + " to dest:" + n.getID() + " message: "\r
+ + message.getHopCounter() + " HopCounter");\r
+ t.send(node, n, msg, pid);\r
+\r
+ }\r
+ if (target != ((ChordProtocol) node.getProtocol(pid)).chordId) {\r
+ // funzione lookup sulla fingertabable\r
+ Node dest = find_successor(target);\r
+ if (dest.isUp() == false) {\r
+ do {\r
+ varSuccList = 0;\r
+ stabilize(node);\r
+ stabilizations++;\r
+ fixFingers();\r
+ dest = find_successor(target);\r
+ } while (dest.isUp() == false);\r
+ }\r
+ if (dest.getID() == successorList[0].getID()\r
+ && (target.compareTo(((ChordProtocol) dest\r
+ .getProtocol(p.pid)).chordId) < 0)) {\r
+ fails++;\r
+ } else {\r
+ System.out.println("S process " + "at time="\r
+ + CommonState.getTime() + " from src:"\r
+ + node.getID() + " to dest:" + dest.getID()\r
+ + " message: (" + message.getSender().getID() + ";"\r
+ + message.getTarget() + ")");\r
+ // t.send(message.getSender(), dest, message, pid);\r
+ t.send(node, dest, message, pid);\r
+\r
+ }\r
+ }\r
+ }\r
+ if (event.getClass() == FinalMessage.class) {\r
+ FinalMessage message = (FinalMessage) event;\r
+ System.out.println("R Final Message " + "at time="\r
+ + CommonState.getTime() + " to dest:" + node.getID()+"\n");\r
+ lookupMessage = new int[index + 1];\r
+ lookupMessage[index] = message.getHopCounter();\r
+ index++;\r
+ }\r
+ }\r
+\r
+ public Object clone() {\r
+ ChordProtocol cp = new ChordProtocol(prefix);\r
+ String val = BigInteger.ZERO.toString();\r
+ cp.chordId = new BigInteger(val);\r
+ cp.fingerTable = new Node[m];\r
+ cp.successorList = new Node[succLSize];\r
+ cp.currentNode = 0;\r
+ return cp;\r
+ }\r
+\r
+ public int[] getLookupMessage() {\r
+ return lookupMessage;\r
+ }\r
+\r
+ public void stabilize(Node myNode) {\r
+ try {\r
+ Node node = ((ChordProtocol) successorList[0].getProtocol(p.pid)).predecessor;\r
+ if (node != null) {\r
+ if (this.chordId == ((ChordProtocol) node.getProtocol(p.pid)).chordId)\r
+ return;\r
+ BigInteger remoteID = ((ChordProtocol) node.getProtocol(p.pid)).chordId;\r
+ if (idInab(\r
+ remoteID,\r
+ chordId,\r
+ ((ChordProtocol) successorList[0].getProtocol(p.pid)).chordId))\r
+ successorList[0] = node;\r
+ ((ChordProtocol) successorList[0].getProtocol(p.pid))\r
+ .notify(myNode);\r
+ }\r
+ updateSuccessorList();\r
+ } catch (Exception e1) {\r
+ e1.printStackTrace();\r
+ updateSuccessor();\r
+ }\r
+ }\r
+\r
+ private void updateSuccessorList() throws Exception {\r
+ try {\r
+ while (successorList[0] == null || successorList[0].isUp() == false) {\r
+ updateSuccessor();\r
+ }\r
+ System.arraycopy(\r
+ ((ChordProtocol) successorList[0].getProtocol(p.pid)).successorList,\r
+ 0, successorList, 1, succLSize - 2);\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ public void notify(Node node) throws Exception {\r
+ BigInteger nodeId = ((ChordProtocol) node.getProtocol(p.pid)).chordId;\r
+ if ((predecessor == null)\r
+ || (idInab(\r
+ nodeId,\r
+ ((ChordProtocol) predecessor.getProtocol(p.pid)).chordId,\r
+ this.chordId))) {\r
+ predecessor = node;\r
+ }\r
+ }\r
+\r
+ private void updateSuccessor() {\r
+ boolean searching = true;\r
+ while (searching) {\r
+ try {\r
+ Node node = successorList[varSuccList];\r
+ varSuccList++;\r
+ successorList[0] = node;\r
+ if (successorList[0] == null\r
+ || successorList[0].isUp() == false) {\r
+ if (varSuccList >= succLSize - 1) {\r
+ searching = false;\r
+ varSuccList = 0;\r
+ } else\r
+ updateSuccessor();\r
+ }\r
+ updateSuccessorList();\r
+ searching = false;\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+\r
+ private boolean idInab(BigInteger id, BigInteger a, BigInteger b) {\r
+ if ((a.compareTo(id) == -1) && (id.compareTo(b) == -1)) {\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ public Node find_successor(BigInteger id) {\r
+ try {\r
+ if (successorList[0] == null || successorList[0].isUp() == false) {\r
+ updateSuccessor();\r
+ }\r
+ if (idInab(\r
+ id,\r
+ this.chordId,\r
+ ((ChordProtocol) successorList[0].getProtocol(p.pid)).chordId)) {\r
+ return successorList[0];\r
+ } else {\r
+ Node tmp = closest_preceding_node(id);\r
+ return tmp;\r
+ }\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ return successorList[0];\r
+ }\r
+\r
+ private Node closest_preceding_node(BigInteger id) {\r
+ for (int i = m; i > 0; i--) {\r
+ try {\r
+ if (fingerTable[i - 1] == null\r
+ || fingerTable[i - 1].isUp() == false) {\r
+ continue;\r
+ }\r
+ BigInteger fingerId = ((ChordProtocol) (fingerTable[i - 1]\r
+ .getProtocol(p.pid))).chordId;\r
+ if ((idInab(fingerId, this.chordId, id))\r
+ || (id.compareTo(fingerId) == 0)) {\r
+ return fingerTable[i - 1];\r
+ }\r
+ if (fingerId.compareTo(this.chordId) == -1) {\r
+ // sono nel caso in cui ho fatto un giro della rete\r
+ // circolare\r
+ if (idInab(id, fingerId, this.chordId)) {\r
+ return fingerTable[i - 1];\r
+ }\r
+ }\r
+ if ((id.compareTo(fingerId) == -1)\r
+ && (id.compareTo(this.chordId) == -1)) {\r
+ if (i == 1)\r
+ return successorList[0];\r
+ BigInteger lowId = ((ChordProtocol) fingerTable[i - 2]\r
+ .getProtocol(p.pid)).chordId;\r
+ if (idInab(id, lowId, fingerId))\r
+ return fingerTable[i - 2];\r
+ else if (fingerId.compareTo(this.chordId) == -1)\r
+ continue;\r
+ else if (fingerId.compareTo(this.chordId) == 1)\r
+ return fingerTable[i - 1];\r
+ }\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ if (fingerTable[m - 1] == null)\r
+ return successorList[0];\r
+ return successorList[0];\r
+ }\r
+\r
+ // debug function\r
+ private void printFingers() {\r
+ for (int i = fingerTable.length - 1; i > 0; i--) {\r
+ if (fingerTable[i] == null) {\r
+ System.out.println("Finger " + i + " is null");\r
+ continue;\r
+ }\r
+ if ((((ChordProtocol) fingerTable[i].getProtocol(p.pid)).chordId)\r
+ .compareTo(this.chordId) == 0)\r
+ break;\r
+ System.out\r
+ .println("Finger["\r
+ + i\r
+ + "] = "\r
+ + fingerTable[i].getIndex()\r
+ + " chordId "\r
+ + ((ChordProtocol) fingerTable[i]\r
+ .getProtocol(p.pid)).chordId);\r
+ }\r
+ }\r
+\r
+ public void fixFingers() {\r
+ if (next >= m - 1)\r
+ next = 0;\r
+ if (fingerTable[next] != null && fingerTable[next].isUp()) {\r
+ next++;\r
+ return;\r
+ }\r
+ BigInteger base;\r
+ if (next == 0)\r
+ base = BigInteger.ONE;\r
+ else {\r
+ base = BigInteger.valueOf(2);\r
+ for (int exp = 1; exp < next; exp++) {\r
+ base = base.multiply(BigInteger.valueOf(2));\r
+ }\r
+ }\r
+ BigInteger pot = this.chordId.add(base);\r
+ BigInteger idFirst = ((ChordProtocol) Network.get(0).getProtocol(p.pid)).chordId;\r
+ BigInteger idLast = ((ChordProtocol) Network.get(Network.size() - 1)\r
+ .getProtocol(p.pid)).chordId;\r
+ if (pot.compareTo(idLast) == 1) {\r
+ pot = (pot.mod(idLast));\r
+ if (pot.compareTo(this.chordId) != -1) {\r
+ next++;\r
+ return;\r
+ }\r
+ if (pot.compareTo(idFirst) == -1) {\r
+ this.fingerTable[next] = Network.get(Network.size() - 1);\r
+ next++;\r
+ return;\r
+ }\r
+ }\r
+ do {\r
+ fingerTable[next] = ((ChordProtocol) successorList[0]\r
+ .getProtocol(p.pid)).find_successor(pot);\r
+ pot = pot.subtract(BigInteger.ONE);\r
+ ((ChordProtocol) successorList[0].getProtocol(p.pid)).fixFingers();\r
+ } while (fingerTable[next] == null || fingerTable[next].isUp() == false);\r
+ next++;\r
+ }\r
+\r
+ /**\r
+ */\r
+ public void emptyLookupMessage() {\r
+ index = 0;\r
+ this.lookupMessage = new int[0];\r
+ }\r
+}\r
--- /dev/null
+/**\r
+ * \r
+ */\r
+package example.chord;\r
+\r
+import peersim.core.*;\r
+import peersim.config.Configuration;\r
+import java.math.*;\r
+\r
+/**\r
+ * @author Andrea\r
+ * \r
+ */\r
+public class CreateNw implements Control {\r
+\r
+ private int pid = 0;\r
+\r
+ private static final String PAR_IDLENGTH = "idLength";\r
+\r
+ private static final String PAR_PROT = "protocol";\r
+\r
+ private static final String PAR_SUCCSIZE = "succListSize";\r
+\r
+ int idLength = 0;\r
+\r
+ int successorLsize = 0;\r
+\r
+ int fingSize = 0;\r
+ //campo x debug\r
+ boolean verbose = false;\r
+\r
+ /**\r
+ * \r
+ */\r
+ public CreateNw(String prefix) {\r
+ pid = Configuration.getPid(prefix + "." + PAR_PROT);\r
+ idLength = Configuration.getInt(prefix + "." + PAR_IDLENGTH); \r
+ successorLsize = Configuration.getInt(prefix + "." + PAR_SUCCSIZE); \r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see peersim.core.Control#execute()\r
+ */\r
+\r
+ public boolean execute() {\r
+ for (int i = 0; i < Network.size(); i++) {\r
+ Node node = (Node) Network.get(i);\r
+ ChordProtocol cp = (ChordProtocol) node.getProtocol(pid);\r
+ cp.m = idLength;\r
+ cp.succLSize = successorLsize;\r
+ cp.varSuccList = 0;\r
+ cp.chordId = new BigInteger(idLength, CommonState.r);\r
+ cp.fingerTable = new Node[idLength];\r
+ cp.successorList = new Node[successorLsize];\r
+ }\r
+ NodeComparator nc = new NodeComparator(pid);\r
+ Network.sort(nc);\r
+ createFingerTable();\r
+ return false;\r
+ }\r
+\r
+ public Node findId(BigInteger id, int nodeOne, int nodeTwo) {\r
+ if (nodeOne >= (nodeTwo - 1)) \r
+ return Network.get(nodeOne);\r
+ int middle = (nodeOne + nodeTwo) / 2;\r
+ if (((middle) >= Network.size() - 1))\r
+ System.out.print("ERROR: Middle is bigger than Network.size");\r
+ if (((middle) <= 0))\r
+ return Network.get(0);\r
+ try {\r
+ BigInteger newId = ((ChordProtocol) ((Node) Network.get(middle))\r
+ .getProtocol(pid)).chordId;\r
+ BigInteger lowId;\r
+ if (middle > 0)\r
+ lowId = ((ChordProtocol) ((Node) Network.get(middle - 1))\r
+ .getProtocol(pid)).chordId;\r
+ else\r
+ lowId = newId;\r
+ BigInteger highId = ((ChordProtocol) ((Node) Network\r
+ .get(middle + 1)).getProtocol(pid)).chordId;\r
+ if (id.compareTo(newId) == 0\r
+ || ((id.compareTo(newId) == 1) && (id.compareTo(highId) == -1))) {\r
+ return Network.get(middle);\r
+ }\r
+ if ((id.compareTo(newId) == -1) && (id.compareTo(lowId) == 1)) {\r
+ if (middle > 0)\r
+ return Network.get(middle - 1);\r
+ else\r
+ return Network.get(0);\r
+ }\r
+ if (id.compareTo(newId) == -1) {\r
+ return findId(id, nodeOne, middle);\r
+ } else if (id.compareTo(newId) == 1) {\r
+ return findId(id, middle, nodeTwo);\r
+ }\r
+ return null;\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ return null;\r
+ }\r
+ }\r
+\r
+ public void createFingerTable() {\r
+ BigInteger idFirst = ((ChordProtocol) Network.get(0).getProtocol(pid)).chordId;\r
+ BigInteger idLast = ((ChordProtocol) Network.get(Network.size() - 1)\r
+ .getProtocol(pid)).chordId;\r
+ for (int i = 0; i < Network.size(); i++) {\r
+ Node node = (Node) Network.get(i);\r
+ ChordProtocol cp = (ChordProtocol) node.getProtocol(pid);\r
+ for (int a = 0; a < successorLsize; a++) {\r
+ if (a + i < (Network.size() - 1))\r
+ cp.successorList[a] = Network.get(a + i + 1);\r
+ else\r
+ cp.successorList[a] = Network.get(a);\r
+ }\r
+ if (i > 0)\r
+ cp.predecessor = (Node) Network.get(i - 1);\r
+ else\r
+ cp.predecessor = (Node) Network.get(Network.size() - 1);\r
+ int j = 0;\r
+ for (j = 0; j < idLength; j++) {\r
+ BigInteger base;\r
+ if (j == 0)\r
+ base = BigInteger.ONE;\r
+ else {\r
+ base = BigInteger.valueOf(2);\r
+ for (int exp = 1; exp < j; exp++) {\r
+ base = base.multiply(BigInteger.valueOf(2));\r
+ }\r
+ }\r
+ BigInteger pot = cp.chordId.add(base);\r
+ if (pot.compareTo(idLast) == 1) {\r
+ pot = (pot.mod(idLast));\r
+ if (pot.compareTo(cp.chordId) != -1) {\r
+ break;\r
+ }\r
+ if (pot.compareTo(idFirst) == -1) {\r
+ cp.fingerTable[j] = Network.get(Network.size() - 1);\r
+ continue;\r
+ }\r
+ }\r
+ cp.fingerTable[j] = findId(pot, 0, Network.size() - 1);\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+package example.chord;\r
+\r
+public class FinalMessage implements ChordMessage {\r
+\r
+ private int hopCounter = 0;\r
+\r
+ public FinalMessage(int hopCounter) {\r
+ this.hopCounter = hopCounter;\r
+ }\r
+\r
+ public int getHopCounter() {\r
+ return hopCounter;\r
+ }\r
+}\r
--- /dev/null
+package example.chord;\r
+\r
+import java.math.*;\r
+import peersim.core.*;\r
+\r
+public class LookUpMessage implements ChordMessage {\r
+\r
+ private Node sender;\r
+\r
+ private BigInteger targetId;\r
+\r
+ private int hopCounter = -1;\r
+\r
+ public LookUpMessage(Node sender, BigInteger targetId) {\r
+ this.sender = sender;\r
+ this.targetId = targetId;\r
+ }\r
+\r
+ public void increaseHopCounter() {\r
+ hopCounter++;\r
+ }\r
+\r
+ /**\r
+ * @return the senderId\r
+ */\r
+ public Node getSender() {\r
+ return sender;\r
+ }\r
+\r
+ /**\r
+ * @return the target\r
+ */\r
+ public BigInteger getTarget() {\r
+ return targetId;\r
+ }\r
+\r
+ /**\r
+ * @return the hopCounter\r
+ */\r
+ public int getHopCounter() {\r
+ return hopCounter;\r
+ }\r
+\r
+}\r
--- /dev/null
+/**\r
+ * \r
+ */\r
+package example.chord;\r
+\r
+import java.util.ArrayList;\r
+import peersim.core.Control;\r
+import peersim.core.Network;\r
+import peersim.config.Configuration;\r
+\r
+/**\r
+ * @author Andrea\r
+ * \r
+ */\r
+public class MessageCounterObserver implements Control {\r
+\r
+ private static final String PAR_PROT = "protocol";\r
+\r
+ private final String prefix;\r
+\r
+ private final int pid;\r
+\r
+ /**\r
+ * \r
+ */\r
+ public MessageCounterObserver(String prefix) {\r
+ this.prefix = prefix;\r
+ this.pid = Configuration.getPid(prefix + "." + PAR_PROT);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see peersim.core.Control#execute()\r
+ */\r
+ public boolean execute() {\r
+ int size = Network.size();\r
+ int totalStab = 0;\r
+ int totFails = 0;\r
+ ArrayList hopCounters = new ArrayList(); // struttura dati che\r
+ // memorizza gli hop di\r
+ // tutti i mess mandati\r
+ hopCounters.clear();\r
+ int max = 0;\r
+ int min = Integer.MAX_VALUE;\r
+ for (int i = 0; i < size; i++) {\r
+ ChordProtocol cp = (ChordProtocol) Network.get(i).getProtocol(pid);\r
+ // trovare tutti gli hopCOunter dei messaggi lookup mandati\r
+ int[] counters = new int[cp.getLookupMessage().length];\r
+ System.arraycopy(cp.getLookupMessage(), 0, counters, 0, cp\r
+ .getLookupMessage().length);\r
+ totalStab = totalStab + cp.stabilizations;\r
+ totFails = totFails + cp.fails;\r
+ cp.stabilizations = 0;\r
+ cp.fails = 0;\r
+ int maxNew = maxArray(counters, cp.index);\r
+ if (maxNew > max)\r
+ max = maxNew;\r
+ if (cp.index != 0) {\r
+ for (int j = 0; j < cp.index; j++)\r
+ hopCounters.add(counters[j]);\r
+ int minNew = minArray(counters, cp.index);\r
+ if (minNew < min)\r
+ min = minNew;\r
+ }\r
+ cp.emptyLookupMessage();\r
+ }\r
+ double media = meanCalculator(hopCounters);\r
+ if (media > 0)\r
+ System.out.println("Mean: " + media + " Max Value: " + max\r
+ + " Min Value: " + min + " # Observations: "\r
+ + hopCounters.size());\r
+ System.out.println(" # Stabilizations: " + totalStab + " # Failures: "\r
+ + totFails);\r
+ System.out.println("");\r
+ return false;\r
+ }\r
+\r
+ private double meanCalculator(ArrayList list) {\r
+ int lenght = list.size();\r
+ if (lenght == 0)\r
+ return 0;\r
+ int sum = 0;\r
+ for (int i = 0; i < lenght; i++) {\r
+ sum = sum + ((Integer) list.get(i)).intValue();\r
+ }\r
+ double mean = sum / lenght;\r
+ return mean;\r
+ }\r
+\r
+ private int maxArray(int[] array, int dim) {\r
+ int max = 0;\r
+ for (int j = 0; j < dim; j++) {\r
+ if (array[j] > max)\r
+ max = array[j];\r
+ }\r
+ return max;\r
+ }\r
+\r
+ private int minArray(int[] array, int dim) {\r
+ int min = 0;\r
+ for (int j = 0; j < dim; j++) {\r
+ if (array[j] < min)\r
+ min = array[j];\r
+ }\r
+ return min;\r
+ }\r
+}\r
--- /dev/null
+package example.chord;\r
+\r
+import java.util.Comparator;\r
+import java.math.*;\r
+import peersim.core.*;\r
+\r
+public class NodeComparator implements Comparator {\r
+\r
+ public int pid = 0;\r
+\r
+ public NodeComparator(int pid) {\r
+ this.pid = pid;\r
+ }\r
+\r
+ public int compare(Object arg0, Object arg1) {\r
+ BigInteger one = ((ChordProtocol) ((Node) arg0).getProtocol(pid)).chordId;\r
+ BigInteger two = ((ChordProtocol) ((Node) arg1).getProtocol(pid)).chordId;\r
+ return one.compareTo(two);\r
+ }\r
+\r
+}\r
--- /dev/null
+package example.chord;\r
+\r
+public class Parameters {\r
+ int pid;\r
+\r
+ int tid;\r
+}\r
--- /dev/null
+/**\r
+ * \r
+ */\r
+package example.chord;\r
+\r
+import org.simgrid.msg.Host;\r
+\r
+import peersim.core.*;\r
+import peersim.config.Configuration;\r
+import peersim.edsim.EDSimulator;\r
+import psgsim.PSGSimulator;\r
+\r
+/**\r
+ * @author Andrea\r
+ * \r
+ */\r
+public class TrafficGenerator implements Control {\r
+\r
+ private static final String PAR_PROT = "protocol";\r
+\r
+ private final int pid;\r
+\r
+ /**\r
+ * \r
+ */\r
+ public TrafficGenerator(String prefix) {\r
+ pid = Configuration.getPid(prefix + "." + PAR_PROT);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see peersim.core.Control#execute()\r
+ */\r
+ public boolean execute() {\r
+ int size = Network.size();\r
+ Node sender, target;\r
+ int i = 0;\r
+ do {\r
+ i++;\r
+ sender = Network.get(CommonState.r.nextInt(size));\r
+ target = Network.get(CommonState.r.nextInt(size));\r
+ } while (sender == null || sender.isUp() == false || target == null\r
+ || target.isUp() == false);\r
+ LookUpMessage message = new LookUpMessage(sender,\r
+ ((ChordProtocol) target.getProtocol(pid)).chordId);\r
+ System.out.println("TrafficGenerator at time "+CommonState.getTime()+" Node:"\r
+ + message.getSender().getID() +" target "+target.getID() + " pid:"\r
+ + pid);\r
+ EDSimulator.add(10, message, sender, pid);\r
+ return false;\r
+ }\r
+\r
+}\r
--- /dev/null
+/*
+ * Copyright (c) 2003 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package example.edaggregation;
+
+import peersim.vector.SingleValueHolder;
+import peersim.config.*;
+import peersim.core.*;
+import peersim.transport.Transport;
+import peersim.cdsim.CDProtocol;
+import peersim.edsim.EDProtocol;
+
+/**
+ * Event driven version of epidemic averaging.
+ */
+public class AverageED extends SingleValueHolder implements CDProtocol,
+ EDProtocol {
+
+ // --------------------------------------------------------------------------
+ // Initialization
+ // --------------------------------------------------------------------------
+
+ /**
+ * @param prefix
+ * string prefix for config properties
+ */
+ public AverageED(String prefix) {
+ super(prefix);
+ }
+
+ // --------------------------------------------------------------------------
+ // methods
+ // --------------------------------------------------------------------------
+
+ /**
+ * This is the standard method the define periodic activity. The frequency
+ * of execution of this method is defined by a
+ * {@link peersim.edsim.CDScheduler} component in the configuration.
+ */
+ public void nextCycle(Node node, int pid) {
+ Linkable linkable = (Linkable) node.getProtocol(FastConfig
+ .getLinkable(pid));
+ if (linkable.degree() > 0) {
+ int degree=linkable.degree();
+ int i=CommonState.r.nextInt(degree);
+ Node peern = linkable.getNeighbor(i);
+ System.out.println("Pid of the protocol: "+pid);
+ System.out.println("Time="+CommonState.getTime()+" degree="+degree+" i="+i+" peernID="+peern.getID()+" peernIndex="+peern.getIndex());
+ if (!peern.isUp())
+ return;
+ AverageMessage ob=new AverageMessage(value, node);
+ System.out.println("NextCycle\t"+"\t Time: " + CommonState.getTime()+ "\t src: " + node.getID() + "\t dst: " + peern.getID()+"\t msg:"+ob.value);
+ ((Transport) node.getProtocol(FastConfig.getTransport(pid))).send(
+ node, peern, ob, pid);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+
+ /**
+ * This is the standard method to define to process incoming messages.
+ */
+ public void processEvent(Node node, int pid, Object event) {
+
+ AverageMessage aem = (AverageMessage) event;
+
+ AverageMessage ob=null;
+ if (aem.sender != null){
+ System.out.println("ProcessEventR\t"+"\t Time: " + CommonState.getTime() + "\t src: " + aem.sender.getID() + "\t dst: " + node.getID()+"\t msg:"+aem.value);
+ ob=new AverageMessage(value, null);
+ System.out.println("ProcessEventS\t"+"\t Time: " + CommonState.getTime()+ "\t src: " + node.getID() + "\t dst: " + aem.sender.getID()+"\t msg:"+ob.value);
+ ((Transport) node.getProtocol(FastConfig.getTransport(pid))).send(
+ node, aem.sender, ob, pid);
+ } else {
+ System.out.println("ProcessEventR\t"+"\t Time: " +CommonState.getTime() + "\t src: " + "NULL" + "\t dst: " + node.getID()+"\t msg:"+aem.value);
+ }
+ value = (value + aem.value) / 2;
+ }
+
+}
+
+// --------------------------------------------------------------------------
+// --------------------------------------------------------------------------
+
+/**
+ * The type of a message. It contains a value of type double and the sender node
+ * of type {@link peersim.core.Node}.
+ */
+class AverageMessage {
+
+ final double value;
+ /**
+ * If not null, this has to be answered, otherwise this is the answer.
+ */
+ final Node sender;
+
+ public AverageMessage(double value, Node sender) {
+ this.value = value;
+ this.sender = sender;
+ }
+}
\ No newline at end of file
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.Iterator;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ * Adapter Class absolutely UNSAFE, just to be able to iterate peersim.core.Network\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class AdapterIterableNetwork implements Iterable<Node>, Iterator<Node> {\r
+\r
+ private int i = 0;\r
+\r
+ public Iterator<Node> iterator() {\r
+ return this;\r
+ }\r
+\r
+ public boolean hasNext() {\r
+ return i < Network.size();\r
+ }\r
+\r
+ public Node next() {\r
+ return Network.get(i++);\r
+ }\r
+\r
+ public void remove() {\r
+ throw new UnsupportedOperationException("Not supported yet.");\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.Comparator;\r
+\r
+import example.symphony.SymphonyProtocol.BootstrapStatus;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ * Object-Adapter\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class AdapterSymphonyNodeComparator implements Comparator<Tuple<Node, BootstrapStatus>> {\r
+\r
+ private SymphonyNodeComparator comparator;\r
+\r
+ public AdapterSymphonyNodeComparator(SymphonyNodeComparator comparator) {\r
+ this.comparator = comparator;\r
+ }\r
+\r
+ public int compare(Tuple<Node, BootstrapStatus> o1, Tuple<Node, BootstrapStatus> o2) {\r
+\r
+ Node node1 = o1.x;\r
+ Node node2 = o2.x;\r
+\r
+ return comparator.compare(node1, node2);\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public interface Handler {\r
+\r
+ /**\r
+ * Handler associable to a routing request\r
+ *\r
+ * @param src Symphony Protocol that has sent the routing request\r
+ * @param evt Tuple that contains: Node that manages the identifier, Identifier that the routing\r
+ * has done on\r
+ */\r
+ void handle(SymphonyProtocol src, Tuple<Node, Double> evt);\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import peersim.config.Configuration;\r
+import peersim.core.CommonState;\r
+import peersim.core.Control;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class LeaveTest implements Control {\r
+\r
+ private static final String PAR_NETMANAGER = "symphonynetworkmanager";\r
+ private static final String PAR_NUMBER_LEAVES = "n";\r
+ private static final String PAR_MIN_SIZE = "minsizeOnline";\r
+ private static final String PAR_WAIT_TARGET_SIZE = "waitTargetSizeToStart";\r
+ private final int networkManagerID;\r
+ private final double n;\r
+ private final int minSizeNetwork;\r
+ private int targetSize;\r
+\r
+ public LeaveTest(String prefix) {\r
+ networkManagerID = Configuration.getPid(prefix + "." + PAR_NETMANAGER);\r
+ double nAppo = Configuration.getDouble(prefix + "." + PAR_NUMBER_LEAVES);\r
+ if (!(nAppo > 0.0 && nAppo < 1.0)) {\r
+ n = (int) Math.round(nAppo);\r
+ } else {\r
+ n = nAppo;\r
+ }\r
+\r
+ minSizeNetwork = Configuration.getInt(prefix + "." + PAR_MIN_SIZE, -1);\r
+ targetSize = Configuration.getInt(prefix + "." + PAR_WAIT_TARGET_SIZE, -1);\r
+ }\r
+\r
+ public boolean execute() {\r
+\r
+ if (minSizeNetwork > 0) {\r
+\r
+ int onlineNode = 0;\r
+ AdapterIterableNetwork it = new AdapterIterableNetwork();\r
+ for (Node node : it) {\r
+ if (node.isUp()) {\r
+ onlineNode++;\r
+ }\r
+ }\r
+\r
+ if (targetSize <= 0 || targetSize <= onlineNode) {\r
+ targetSize = -1;\r
+\r
+ // verify if i have to remove an exact number of nodes or a percentage of them\r
+ int actualN = (int) (n < 1.0 ? Math.ceil(Network.size() * n) : n);\r
+\r
+ for (int i = 0; i < actualN && Network.size() > 0; i++) {\r
+ if (onlineNode > minSizeNetwork) {\r
+ Node leaveNode = Network.get(Math.abs(CommonState.r.nextInt()) % Network.size());\r
+\r
+ while (!leaveNode.isUp()) {\r
+ leaveNode = Network.get(Math.abs(CommonState.r.nextInt()) % Network.size());\r
+ }\r
+\r
+ SymphonyNetworkManager networkManager = (SymphonyNetworkManager) leaveNode.getProtocol(networkManagerID);\r
+\r
+ networkManager.leave(leaveNode);\r
+\r
+ onlineNode--;\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return false;\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class Message implements Cloneable {\r
+\r
+ public enum MessageType {\r
+\r
+ ROUTE, ROUTE_RESPONSE, ROUTE_FAIL,\r
+ JOIN, JOIN_RESPONSE,\r
+ UPDATE_NEIGHBOURS, UPDATE_NEIGHBOURS_RESPONSE,\r
+ REQUEST_LONG_RANGE_LINK, ACCEPTED_LONG_RANGE_LINK, REJECT_LONG_RANGE_LINK, DISCONNECT_LONG_RANGE_LINK, UNAVAILABLE_LONG_RANGE_LINK,\r
+ UPDATE_STATUS, UPDATE_STATUS_RESPONSE,\r
+ LEAVE,\r
+ KEEP_ALIVE, KEEP_ALIVE_RESPONSE\r
+ }\r
+ private long hopCounter;\r
+ private MessageType type;\r
+ private Node src;\r
+ private Node currentHop;\r
+ private Object body;\r
+ private static long globalID = 0;\r
+ private final long id;\r
+\r
+ public Message(Object body, Node src, MessageType type) {\r
+ this.type = type;\r
+ this.src = src;\r
+ this.body = body;\r
+ hopCounter = 0;\r
+ id = globalID++;\r
+ currentHop = src;\r
+ }\r
+\r
+ public long getID() {\r
+ return id;\r
+ }\r
+\r
+ public Object getBody() {\r
+ return body;\r
+ }\r
+\r
+ public void incrementHop() {\r
+ hopCounter++;\r
+ }\r
+\r
+ public long getHop() {\r
+ return hopCounter;\r
+ }\r
+\r
+ public MessageType getType() {\r
+ return type;\r
+ }\r
+\r
+ public Node getSourceNode() {\r
+ return src;\r
+ }\r
+\r
+ public Node getCurrentHop() {\r
+ return currentHop;\r
+ }\r
+\r
+ public void setCurrentHop(Node currentHop) {\r
+ this.currentHop = currentHop;\r
+ }\r
+\r
+ @Override\r
+ public Object clone() throws CloneNotSupportedException {\r
+ return super.clone();\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+\r
+ StringBuilder builder = new StringBuilder();\r
+ builder.append("Message@").append(this.hashCode()).append("[\n");\r
+\r
+ builder.append("\tID : ").append(id).append(",\n");\r
+ builder.append("\tSource ID: ").append(src.getID()).append(",\n");\r
+ builder.append("\tType : ").append(type).append(",\n");\r
+ builder.append("\tBody : ").append(body).append(",\n");\r
+ builder.append("\tCurrent Hop ID: ").append(currentHop.getID()).append(",\n");\r
+ builder.append("\tHop Counter : ").append(hopCounter).append("\n]\n");\r
+\r
+ return builder.toString();\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import peersim.core.Node;\r
+import peersim.core.Protocol;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public interface NetworkSizeEstimatorProtocolInterface extends Protocol {\r
+\r
+ public int getNetworkSize(Node node);\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+import peersim.config.Configuration;\r
+import peersim.core.CommonState;\r
+import peersim.core.Control;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class RandomRouteTest implements Control, Handler {\r
+\r
+ private static final String PAR_SYMPHONY = "symphony";\r
+ private final int symphonyID;\r
+\r
+ public RandomRouteTest(String prefix) {\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY);\r
+ }\r
+\r
+ public boolean execute() {\r
+\r
+ Node src = Network.get(Math.abs(CommonState.r.nextInt()) % Network.size());\r
+\r
+ SymphonyProtocol symphony = (SymphonyProtocol) src.getProtocol(symphonyID);\r
+ try {\r
+ symphony.route(src, CommonState.r.nextDouble(), this);\r
+ } catch (RoutingException ex) {\r
+ Logger.getLogger(RandomRouteTest.class.getName()).log(Level.SEVERE, ex.getMessage());\r
+ }\r
+\r
+ return false;\r
+\r
+ }\r
+\r
+ public void handle(SymphonyProtocol symphony, Tuple<Node, Double> tuple) {\r
+\r
+ if (tuple == null) {\r
+ Logger.getLogger(RandomRouteTest.class.getName()).log(Level.SEVERE, "FAIL ROUTE RANDOMTEST");\r
+ return;\r
+ }\r
+\r
+ Logger.getLogger(RandomRouteTest.class.getName()).log(Level.FINE, symphony.getIdentifier() + " source find the manager of " + tuple.y + " and it is " + ((SymphonyProtocol) tuple.x.getProtocol(symphonyID)).getIdentifier());\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.LinkedList;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import example.symphony.SymphonyProtocol.BootstrapStatus;\r
+import peersim.config.Configuration;\r
+import peersim.core.Control;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class RingRouteTest implements Control, Handler {\r
+\r
+ private static final String PAR_SYMPHONY = "symphony";\r
+ private static final String PAR_STARTNODE = "startnode";\r
+ private final int symphonyID;\r
+ private final int indexStartNode;\r
+ private Node start;\r
+ private boolean finished;\r
+ private boolean flagTimeout;\r
+ private HashSet<Node> antiLoopSet;\r
+\r
+ public RingRouteTest(String prefix) {\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY);\r
+ indexStartNode = Configuration.getInt(prefix + "." + PAR_STARTNODE, 0);\r
+\r
+ finished = true;\r
+ flagTimeout = false;\r
+ antiLoopSet = new HashSet<Node>();\r
+ }\r
+\r
+ public boolean execute() {\r
+\r
+ if (!finished && flagTimeout) {\r
+\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.WARNING, "Sent msg but no aswer. Timeout. Ring Route Test terminated for Timeout.");\r
+\r
+ finished = true;\r
+ flagTimeout = false;\r
+ }\r
+\r
+ if (finished) {\r
+\r
+ flagTimeout = true;\r
+ antiLoopSet.clear();\r
+\r
+ int indexRealStartNode = indexStartNode;\r
+ Node realStartNode = Network.get(indexStartNode);\r
+ SymphonyProtocol symphony = (SymphonyProtocol) realStartNode.getProtocol(symphonyID);\r
+\r
+ while (!symphony.isBootstrapped() || !realStartNode.isUp()) {\r
+ indexRealStartNode = (indexRealStartNode + 1) % Network.size();\r
+ realStartNode = Network.get(indexRealStartNode);\r
+ symphony = (SymphonyProtocol) realStartNode.getProtocol(symphonyID);\r
+\r
+ if (indexRealStartNode == indexStartNode) {\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.WARNING, "No ONLINE nodes. The ring is vanished. Ring Route Terminated.");\r
+ finished = true;\r
+ flagTimeout = false;\r
+ return false;\r
+ }\r
+ }\r
+\r
+ start = realStartNode;\r
+ finished = false;\r
+\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.INFO, "RingRoute started.");\r
+\r
+ doRoute(start, true);\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ public void handle(SymphonyProtocol symphony, Tuple<Node, Double> tuple) {\r
+\r
+ if (tuple == null) {\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.SEVERE, "FAIL RING ROUTING");\r
+ finished = true;\r
+ return;\r
+ }\r
+\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.FINER, symphony.getIdentifier() + " source find the manager of " + tuple.y + " and it is " + ((SymphonyProtocol) tuple.x.getProtocol(symphonyID)).getIdentifier());\r
+\r
+ doRoute(tuple.x, false);\r
+ }\r
+\r
+ private void doRoute(Node node, boolean firstTime) {\r
+\r
+ SymphonyProtocol symphonyStartNode = (SymphonyProtocol) start.getProtocol(symphonyID);\r
+\r
+ if (!symphonyStartNode.isBootstrapped()) {\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.INFO, "The node i started from left. Ring Route Terminated.");\r
+ finished = true;\r
+ return;\r
+ }\r
+\r
+ if (!firstTime && node.equals(start)) {\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.INFO, "RingRoute Terminated");\r
+ finished = true;\r
+ return;\r
+ }\r
+\r
+ if (antiLoopSet.contains(node)) {\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.INFO, "Not able to reach the node that i started from. Ring Route Terminated.");\r
+ finished = true;\r
+ return;\r
+ } else {\r
+ antiLoopSet.add(node);\r
+ }\r
+\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ AdapterSymphonyNodeComparator adapterSymphonyNodeComparator = new AdapterSymphonyNodeComparator(new SymphonyNodeComparator(symphonyID, node));\r
+\r
+ Collection<Tuple<Node, BootstrapStatus>> collection = (Collection<Tuple<Node, BootstrapStatus>>) symphony.leftShortRangeLinks.clone();\r
+ LinkedList<Tuple<Node, BootstrapStatus>> list = new LinkedList<Tuple<Node, BootstrapStatus>>(collection);\r
+ Collections.sort(list, adapterSymphonyNodeComparator);\r
+\r
+ Node targetNode = null;\r
+ for (Tuple<Node, BootstrapStatus> tuple : list) {\r
+ if (tuple.y == BootstrapStatus.ONLINE) {\r
+ targetNode = tuple.x;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (targetNode == null || !targetNode.isUp()) {\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.WARNING, "Terminated Ring Route but not done completely");\r
+ finished = true;\r
+ return;\r
+ }\r
+\r
+ SymphonyProtocol symphonyTarget = (SymphonyProtocol) targetNode.getProtocol(symphonyID);\r
+ try {\r
+ symphony.route(node, symphonyTarget.getIdentifier(), this);\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.FINEST, "Ring from: " + symphony.getIdentifier() + " to " + symphonyTarget.getIdentifier());\r
+ } catch (RoutingException ex) {\r
+ Logger.getLogger(RingRouteTest.class.getName()).log(Level.WARNING, "Finito AnelloRoute MA NON FATTO TUTTO");\r
+ finished = true;\r
+ }\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class RoutingException extends Exception {\r
+\r
+ public RoutingException() {\r
+ }\r
+\r
+ public RoutingException(String msg) {\r
+ super(msg);\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SimpleNetworkSizeEstimatorProtocol implements NetworkSizeEstimatorProtocolInterface {\r
+\r
+ public SimpleNetworkSizeEstimatorProtocol(String prefix) {\r
+ }\r
+\r
+ public int getNetworkSize(Node node) {\r
+ return Network.size();\r
+ }\r
+\r
+ @Override\r
+ public Object clone() {\r
+ return this;\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.*;\r
+import peersim.config.Configuration;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SymphonyEstimationProtocol implements NetworkSizeEstimatorProtocolInterface {\r
+\r
+ private static final String PAR_SYMPHONY = "symphony";\r
+ private static final String PAR_S = "s";\r
+ private final int symphonyID;\r
+ private final int s;\r
+\r
+ public SymphonyEstimationProtocol(String prefix) {\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY);\r
+ s = Configuration.getInt(prefix + "." + PAR_S, -1);\r
+ }\r
+\r
+ /**\r
+ * Implementation of the estimated network size as a variant of the paper one. It use anyway the\r
+ * idea to calculate the size from the length segments but without exchanging the information\r
+ * with the neighbours instead using only the local information.\r
+ */\r
+ public int getNetworkSize(Node node) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+\r
+ // If the node is not yet inside the ring i return the minimum size (2 nodes)\r
+ if (!symphony.isBootstrapped()) {\r
+ return 2;\r
+ }\r
+\r
+ /*\r
+ * I clone the short range links views (wrapped into an ArrayList because the returned list\r
+ * 'Arrays.asList doesn't support the "removeAll" method or better its size is fixed)\r
+ */\r
+ ArrayList<Tuple<Node, SymphonyProtocol.BootstrapStatus>> leftList = new ArrayList<Tuple<Node, SymphonyProtocol.BootstrapStatus>>(Arrays.asList((Tuple<Node, SymphonyProtocol.BootstrapStatus>[]) symphony.leftShortRangeLinks.toArray(new Tuple[0])));\r
+ ArrayList<Tuple<Node, SymphonyProtocol.BootstrapStatus>> rightList = new ArrayList<Tuple<Node, SymphonyProtocol.BootstrapStatus>>(Arrays.asList((Tuple<Node, SymphonyProtocol.BootstrapStatus>[]) symphony.rightShortRangeLinks.toArray(new Tuple[0])));\r
+\r
+ // Remove the neighbours that are offline\r
+ LinkedList<Tuple<Node, SymphonyProtocol.BootstrapStatus>> offlineNeighbors = new LinkedList<Tuple<Node, SymphonyProtocol.BootstrapStatus>>();\r
+ for (Tuple<Node, SymphonyProtocol.BootstrapStatus> tuple : leftList) {\r
+ if (tuple.y == SymphonyProtocol.BootstrapStatus.OFFLINE) {\r
+ offlineNeighbors.add(tuple);\r
+ }\r
+ }\r
+ leftList.removeAll(offlineNeighbors);\r
+ offlineNeighbors.clear();\r
+ for (Tuple<Node, SymphonyProtocol.BootstrapStatus> tuple : rightList) {\r
+ if (tuple.y == SymphonyProtocol.BootstrapStatus.OFFLINE) {\r
+ offlineNeighbors.add(tuple);\r
+ }\r
+ }\r
+ rightList.removeAll(offlineNeighbors);\r
+\r
+ // Sort the neighbours based on the distance from me\r
+ Comparator<Tuple<Node, SymphonyProtocol.BootstrapStatus>> comparator = new AdapterSymphonyNodeComparator(new SymphonyNodeComparator(symphonyID, symphony.getIdentifier()));\r
+ Collections.sort(leftList, comparator);\r
+ Collections.sort(rightList, comparator);\r
+\r
+ // Calculate the variables to estimated the network size\r
+ double Xs = 0;\r
+ int countS = 0;\r
+\r
+ List<Tuple<Node, SymphonyProtocol.BootstrapStatus>> appoList[] = new List[2];\r
+ appoList[0] = leftList;\r
+ appoList[1] = rightList;\r
+\r
+ double[] appoPrecIdentifier = new double[]{symphony.getIdentifier(), symphony.getIdentifier()};\r
+ int[] appoCurrentIndex = new int[]{0, 0};\r
+\r
+ int realS = (int) (s <= 0 ? Math.log(Network.size() / Math.log(2)) : s);\r
+\r
+ for (int i = 0; i < realS; i++) {\r
+ double precIdentifier = appoPrecIdentifier[i % 2];\r
+ int currentIndex = appoCurrentIndex[i % 2];\r
+ List<Tuple<Node, SymphonyProtocol.BootstrapStatus>> currentList = appoList[i % 2];\r
+\r
+ try {\r
+ double currentIdentifier = ((SymphonyProtocol) currentList.get(currentIndex).x.getProtocol(symphonyID)).getIdentifier();\r
+\r
+ appoPrecIdentifier[i % 2] = currentIdentifier;\r
+ appoCurrentIndex[i % 2] = appoCurrentIndex[i % 2] + 1;\r
+\r
+ double distance = Math.abs(currentIdentifier - precIdentifier);\r
+ Xs += Math.min(distance, 1.0 - distance);\r
+ countS++;\r
+ } catch (IndexOutOfBoundsException ex) {\r
+ // Simply i skip the counting\r
+ }\r
+ }\r
+\r
+ int ret = Xs == 0 ? 0 : (int) Math.round(countS / Xs);\r
+\r
+ return ret;\r
+ }\r
+\r
+ @Override\r
+ public Object clone() {\r
+ return this;\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.Collection;\r
+import java.util.Comparator;\r
+import java.util.LinkedList;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import example.symphony.SymphonyProtocol.BootstrapStatus;\r
+import peersim.config.Configuration;\r
+import peersim.core.CommonState;\r
+import peersim.core.Control;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ * Inizializer that create the initial ring\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SymphonyNetworkBuilder implements Control {\r
+\r
+ private static final String PAR_SYMHONY = "symphony";\r
+ private static final String PAR_LONG_LINK = "createLongLinks";\r
+ private static final String PAR_MAX_ATTEMPTS = "attempts";\r
+ private final int symphonyID;\r
+ private final boolean createLongRangeLinks;\r
+ private final int MAX_ATTEMPTS;\r
+\r
+ public SymphonyNetworkBuilder(String prefix) {\r
+\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMHONY);\r
+ createLongRangeLinks = Configuration.getBoolean(prefix + "." + PAR_LONG_LINK, true);\r
+ MAX_ATTEMPTS = Configuration.getInt(prefix + "." + PAR_MAX_ATTEMPTS, 5);\r
+ }\r
+\r
+ public boolean execute() {\r
+\r
+ // Sort the network for convenience (from 0.0 to 1.0)\r
+ Network.sort(new Comparator<Node>() {\r
+\r
+ public int compare(Node o1, Node o2) {\r
+\r
+ SymphonyProtocol symphony1 = (SymphonyProtocol) o1.getProtocol(symphonyID);\r
+ SymphonyProtocol symphony2 = (SymphonyProtocol) o2.getProtocol(symphonyID);\r
+\r
+ Double identifier1 = symphony1.getIdentifier();\r
+ Double identifier2 = symphony2.getIdentifier();\r
+\r
+ return identifier1.compareTo(identifier2);\r
+ }\r
+ });\r
+\r
+ int numShortLinksPerSide = ((SymphonyProtocol) Network.get(0).getProtocol(symphonyID)).numberShortRangeLinksPerSide;\r
+\r
+ for (int i = 0; i < Network.size(); i++) {\r
+\r
+ Node node = Network.get(i);\r
+ SymphonyProtocol symphonyNode = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+\r
+ // Create the short links\r
+ for (int j = 1; j <= numShortLinksPerSide; j++) {\r
+\r
+ int pos = i - j;\r
+ pos = pos < 0 ? Network.size() + pos : pos;\r
+ symphonyNode.rightShortRangeLinks.add(new Tuple<Node, BootstrapStatus>(Network.get(pos), BootstrapStatus.ONLINE));\r
+\r
+ pos = (i + j) % Network.size();\r
+ symphonyNode.leftShortRangeLinks.add(new Tuple<Node, BootstrapStatus>(Network.get(pos), BootstrapStatus.ONLINE));\r
+ }\r
+\r
+ symphonyNode.loggedIntoNetwork = SymphonyProtocol.BootstrapStatus.ONLINE;\r
+ }\r
+\r
+ /*\r
+ * UPDATE: Putted a flag to decide if perform this part of code or not at configuration\r
+ * time. At default i create the long range links.\r
+ *\r
+ * The Long Range Links could be left to the networkmanager but the tests that we'll do have\r
+ * to put into account that in an initial phase will be some message exchanging to create\r
+ * the long range links and so the latency is faked... for that reason the long range links\r
+ * are created manually here such a way to have a complete symphony network from the\r
+ * beginning.\r
+ */\r
+ if (createLongRangeLinks) {\r
+ for (Node node : new AdapterIterableNetwork()) {\r
+\r
+ SymphonyProtocol symphonyNode = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+\r
+ // Create the long links\r
+ int k = (int) Math.ceil(Math.log(Network.size()) / Math.log(2));\r
+\r
+ if (symphonyNode.fixedLongRangeLinks) {\r
+ k = symphonyNode.numberFixedLongRangeLinks;\r
+ }\r
+\r
+ Collection<Node> allShortLinks = new LinkedList<Node>();\r
+ for (Tuple<Node, BootstrapStatus> shortTuple : symphonyNode.leftShortRangeLinks) {\r
+ allShortLinks.add(shortTuple.x);\r
+ }\r
+ for (Tuple<Node, BootstrapStatus> shortTuple : symphonyNode.rightShortRangeLinks) {\r
+ allShortLinks.add(shortTuple.x);\r
+ }\r
+\r
+ int j = 0;\r
+ int attempts = MAX_ATTEMPTS;\r
+ while (j <= k) {\r
+\r
+ double distance = Math.exp(k * (CommonState.r.nextDouble() - 1.0));\r
+ double targetIdentifier = (symphonyNode.getIdentifier() + distance) % 1;\r
+\r
+ Node targetNode;\r
+ try {\r
+\r
+ // use the unidirectional routing because i want to catch the manager\r
+ targetNode = symphonyNode.findClosestNode(targetIdentifier, new AdapterIterableNetwork(), true);\r
+ SymphonyProtocol symphonyTargetNode = (SymphonyProtocol) targetNode.getProtocol(symphonyID);\r
+ if (!targetNode.equals(node)\r
+ && !symphonyNode.longRangeLinksOutgoing.contains(targetNode)\r
+ && symphonyTargetNode.longRangeLinksIncoming.size() < (2 * k)\r
+ && !allShortLinks.contains(targetNode)) {\r
+\r
+ boolean fresh = symphonyTargetNode.longRangeLinksIncoming.add(node);\r
+\r
+ if (fresh) {\r
+ j++;\r
+ attempts = MAX_ATTEMPTS;\r
+ symphonyNode.longRangeLinksOutgoing.add(targetNode);\r
+ } else {\r
+ attempts--;\r
+ if (attempts <= 0) { // Because i don't want to loop i try a finite number of times\r
+ attempts = MAX_ATTEMPTS;\r
+ j++;\r
+ }\r
+\r
+ }\r
+ } else {\r
+ attempts--;\r
+ if (attempts <= 0) { // Because i don't want to loop i try a finite number of times\r
+ attempts = MAX_ATTEMPTS;\r
+ j++;\r
+ }\r
+\r
+ }\r
+ } catch (RoutingException ex) {\r
+ Logger.getLogger(SymphonyNetworkBuilder.class.getName()).log(Level.SEVERE, null, ex);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // Shuffle\r
+ Network.shuffle();\r
+\r
+ return false;\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import example.symphony.SymphonyProtocol.BootstrapStatus;\r
+import peersim.config.Configuration;\r
+import peersim.core.Control;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SymphonyNetworkChecker implements Control {\r
+\r
+ private static final String PAR_SYMHONY = "symphony";\r
+ private static final String PAR_NETSIZE = "networkestimator";\r
+ private final int symphonyID;\r
+ private final int networkEstimatorID;\r
+\r
+ public SymphonyNetworkChecker(String prefix) {\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMHONY);\r
+ networkEstimatorID = Configuration.getPid(prefix + "." + PAR_NETSIZE);\r
+ }\r
+\r
+ public boolean execute() {\r
+\r
+ boolean isNotOK = false;\r
+\r
+ Set<Double> idSet = new HashSet<Double>();\r
+ Iterable<Node> coll = new AdapterIterableNetwork();\r
+\r
+ int countOnline = 0;\r
+ int count = 0;\r
+ int notBootstrapped = 0;\r
+ int countKO = 0;\r
+ int disconnected = 0;\r
+\r
+ for (Node node : coll) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+\r
+ if (!node.isUp()) {\r
+ disconnected++;\r
+ } else {\r
+ count++;\r
+ }\r
+\r
+ if (symphony.loggedIntoNetwork == SymphonyProtocol.BootstrapStatus.ONLINE) {\r
+\r
+ countOnline++;\r
+\r
+ NetworkSizeEstimatorProtocolInterface networkEstimator = (NetworkSizeEstimatorProtocolInterface) node.getProtocol(networkEstimatorID);\r
+ int k = (int) Math.ceil(Math.log(networkEstimator.getNetworkSize(node)) / Math.log(2));\r
+\r
+ boolean checkLeftShortRangeLinks = symphony.leftShortRangeLinks.size() > 0 && symphony.leftShortRangeLinks.size() <= symphony.numberShortRangeLinksPerSide;\r
+ boolean checkRightShortRangeLinks = symphony.rightShortRangeLinks.size() > 0 && symphony.rightShortRangeLinks.size() <= symphony.numberShortRangeLinksPerSide;\r
+\r
+ boolean oneNeighborOnline = false;\r
+ for (Tuple<Node, BootstrapStatus> leftTuple : symphony.leftShortRangeLinks) {\r
+ if (leftTuple.y != BootstrapStatus.ONLINE && leftTuple.y != BootstrapStatus.OFFLINE) {\r
+ notBootstrapped++;\r
+ } else {\r
+ oneNeighborOnline = true;\r
+ checkLeftShortRangeLinks = checkLeftShortRangeLinks && SymphonyProtocol.isLeftNeighbour(node, leftTuple.x);\r
+ }\r
+ }\r
+ checkLeftShortRangeLinks = checkLeftShortRangeLinks && oneNeighborOnline;\r
+\r
+ oneNeighborOnline = false;\r
+ for (Tuple<Node, BootstrapStatus> rightTuple : symphony.rightShortRangeLinks) {\r
+ if (rightTuple.y != BootstrapStatus.ONLINE && rightTuple.y != BootstrapStatus.OFFLINE) {\r
+ notBootstrapped++;\r
+ } else {\r
+ oneNeighborOnline = true;\r
+ checkRightShortRangeLinks = checkRightShortRangeLinks && !SymphonyProtocol.isLeftNeighbour(node, rightTuple.x);\r
+ }\r
+ }\r
+ checkRightShortRangeLinks = checkRightShortRangeLinks && oneNeighborOnline;\r
+\r
+ // Check if the node is in its neighbours\r
+ if (checkLeftShortRangeLinks) {\r
+ AdapterSymphonyNodeComparator comparator = new AdapterSymphonyNodeComparator(new SymphonyNodeComparator(symphonyID, node));\r
+ checkLeftShortRangeLinks = checkLeftShortRangeLinks && !Collections.min(symphony.leftShortRangeLinks, comparator).x.equals(node);\r
+ }\r
+\r
+ if (checkRightShortRangeLinks) {\r
+ AdapterSymphonyNodeComparator comparator = new AdapterSymphonyNodeComparator(new SymphonyNodeComparator(symphonyID, node));\r
+ checkRightShortRangeLinks = checkRightShortRangeLinks && !Collections.min(symphony.rightShortRangeLinks, comparator).x.equals(node);\r
+ }\r
+\r
+ boolean checkLongRangeLinksOutgoing = !symphony.longRangeLinksOutgoing.contains(node);\r
+ boolean checkLongRangeLinksIncoming = /*\r
+ * symphony.longRangeLinksIncoming.size() <= (2 * k) &&\r
+ */ !symphony.longRangeLinksIncoming.contains(node);\r
+\r
+ boolean checkUniqueID = !idSet.contains(symphony.getIdentifier());\r
+ idSet.add(symphony.getIdentifier());\r
+\r
+ boolean nextIsNotOK = !(checkUniqueID && checkLeftShortRangeLinks && checkRightShortRangeLinks && checkLongRangeLinksOutgoing && checkLongRangeLinksIncoming);\r
+\r
+ if (nextIsNotOK) {\r
+ countKO++;\r
+ Logger.getLogger(SymphonyNetworkChecker.class.getName()).log(Level.SEVERE, "OPS");\r
+ }\r
+\r
+ isNotOK = isNotOK || nextIsNotOK;\r
+ }\r
+ }\r
+\r
+ System.out.println("Error: " + countKO);\r
+ System.out.println("Online: " + countOnline + "/" + count);\r
+ System.out.println("Not Bootstrapped: " + notBootstrapped);\r
+ System.out.println("Disconnected: " + disconnected);\r
+ System.out.println("Network Size: " + Network.size());\r
+\r
+ return isNotOK;\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.*;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import example.symphony.Message.MessageType;\r
+import example.symphony.SymphonyProtocol.BootstrapStatus;\r
+import peersim.cdsim.CDProtocol;\r
+import peersim.config.Configuration;\r
+import peersim.core.CommonState;\r
+import peersim.core.Fallible;\r
+import peersim.core.Node;\r
+import peersim.edsim.EDProtocol;\r
+import peersim.transport.Transport;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SymphonyNetworkManager implements EDProtocol, CDProtocol {\r
+\r
+ private static final String PAR_SYMPHONY = "symphony";\r
+ private static final String PAR_TRANSP = "transport";\r
+ private static final String PAR_ATTEMPTS = "attempts";\r
+ private static final String PAR_NETSIZE = "networkestimator";\r
+ private static final String PAR_NUM_TIMEOUT = "nTimeout";\r
+ private static final String PAR_RELINKING = "relinking";\r
+ private static final String PAR_RELINKING_LOWER_BOUND = "relinkingLowerBound";\r
+ private static final String PAR_RELINKING_UPPER_BOUND = "relinkingUpperBound";\r
+ private static final int DEFAULT_K = 1;\r
+ private static final int DEFAULT_N = 2;\r
+ private static final double DEFAULT_RELINKING_LOWER_BOUND = 0.5;\r
+ private static final double DEFAULT_RELINKING_UPPER_BOUND = 2.0;\r
+ private final String prefix;\r
+ private final int symphonyID;\r
+ private final int transportID;\r
+ private final int networkEstimatorID;\r
+ private final int attempts;\r
+ private final int pid;\r
+ private final int nTimeout;\r
+ private final HashMap<Node, Integer> keepAliveMap;\r
+ private final boolean relinkingProtocolActivated;\r
+ private final double relinkingUpperBound;\r
+ private final double relinkingLowerBound;\r
+ private int k = DEFAULT_K; // Number of Long Range Link\r
+ private int n = DEFAULT_N; // Estimation Network size\r
+ private static boolean firstPrintConfig = true;\r
+ /*\r
+ * Estimation Network size at which last long distance link was established, at the beginning -1\r
+ * to indicate that we never had Long Range Links\r
+ */\r
+ private int nLink = -1;\r
+ private int currentAttempts;\r
+\r
+ public SymphonyNetworkManager(String prefix) {\r
+\r
+ this.prefix = prefix;\r
+ pid = Configuration.lookupPid(prefix.replaceAll("protocol.", ""));\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY);\r
+ transportID = Configuration.getPid(prefix + "." + PAR_TRANSP);\r
+ networkEstimatorID = Configuration.getPid(prefix + "." + PAR_NETSIZE);\r
+ attempts = Configuration.getInt(prefix + "." + PAR_ATTEMPTS);\r
+ nTimeout = Configuration.getInt(prefix + "." + PAR_NUM_TIMEOUT, 10);\r
+ relinkingProtocolActivated = !Configuration.getString(prefix + "." + PAR_RELINKING, "on").toLowerCase().equals("off");\r
+ double relinkingLowerBoundAppo = Configuration.getDouble(prefix + "." + PAR_RELINKING_LOWER_BOUND, DEFAULT_RELINKING_LOWER_BOUND);\r
+ double relinkingUpperBoundAppo = Configuration.getDouble(prefix + "." + PAR_RELINKING_UPPER_BOUND, DEFAULT_RELINKING_UPPER_BOUND);\r
+ if (relinkingLowerBoundAppo > relinkingUpperBoundAppo) {\r
+ relinkingLowerBound = DEFAULT_RELINKING_LOWER_BOUND;\r
+ relinkingUpperBound = DEFAULT_RELINKING_UPPER_BOUND;\r
+ } else {\r
+ relinkingLowerBound = relinkingLowerBoundAppo;\r
+ relinkingUpperBound = relinkingUpperBoundAppo;\r
+ }\r
+\r
+ keepAliveMap = new HashMap<Node, Integer>();\r
+\r
+ printConfig();\r
+ }\r
+\r
+ private void printConfig() {\r
+\r
+ if (firstPrintConfig) {\r
+ firstPrintConfig = false;\r
+ System.out.println(SymphonyNetworkManager.class.getSimpleName() + " Configuration:");\r
+ System.out.println("- Attempts per LongRangeLinks: " + attempts);\r
+ System.out.println("- Number of Timeout before a node is considered OFFLINE (through Keep-alive):" + nTimeout);\r
+ System.out.println("- Relinking: " + (relinkingProtocolActivated ? "ON" : "OFF"));\r
+ System.out.println("- Relinking Range: [" + relinkingLowerBound + ", " + relinkingUpperBound + "]");\r
+ System.out.println("-------------------------------\n");\r
+ }\r
+ }\r
+\r
+ public void join(final Node node, final Node bootstrapNode) throws RoutingException {\r
+ final SymphonyProtocol bootstrapSymphony = (SymphonyProtocol) bootstrapNode.getProtocol(symphonyID);\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+\r
+ /*\r
+ * Search (through the bootstrap node) and contact the Manager Node of myself such a way to\r
+ * be able to insert myself into the ring and create the short links\r
+ *\r
+ */\r
+ bootstrapSymphony.route(bootstrapNode, symphony.getIdentifier(), new Handler() {\r
+\r
+ public void handle(SymphonyProtocol src, Tuple<Node, Double> tuple) {\r
+ if (tuple == null) {\r
+ Logger.getLogger(SymphonyNetworkManager.class.getName()).log(Level.SEVERE, "FAIL ROUTE JOIN");\r
+ node.setFailState(Fallible.DEAD);\r
+ return;\r
+ }\r
+\r
+ Node managerNode = tuple.x;\r
+\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+ Message msg = new Message(node, node, MessageType.JOIN);\r
+ transport.send(node, managerNode, msg, pid);\r
+ }\r
+ });\r
+\r
+ // The Long Range Links are added after that i joined the ring (before i can't because i haven't got the nodes to do the routing)\r
+ }\r
+\r
+ /**\r
+ * Conservative Re-Linking (i reuse the ones already created: not all fresh)\r
+ *\r
+ * @param node\r
+ */\r
+ public void updateLongRangeLinks(Node node) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+\r
+ // if too much links i delete the farest ones\r
+ while (symphony.longRangeLinksOutgoing.size() > k) {\r
+ Node distantNode = Collections.max(symphony.longRangeLinksOutgoing, new SymphonyNodeComparator(symphonyID, node));\r
+ symphony.longRangeLinksOutgoing.remove(distantNode);\r
+\r
+ // Communicate to the outgoing node that it ins't anymore one of my long range links\r
+ Message disconnectMsg = new Message(null, node, MessageType.DISCONNECT_LONG_RANGE_LINK);\r
+ transport.send(node, distantNode, disconnectMsg, pid);\r
+ }\r
+\r
+ // I can search Long Range Links only if i'm into the ring and i'm able to do routing\r
+ if (symphony.isBootstrapped()) {\r
+ // if only few i try again, untill attempts times, to add new ones\r
+ int difference = k - symphony.longRangeLinksOutgoing.size();\r
+ currentAttempts = attempts;\r
+ for (int i = 0; i < difference; i++) {\r
+ sendLongRangeLinkRequest(symphony, node);\r
+ }\r
+ }\r
+ }\r
+ private static final int MAX_ANTILOOP_COUNT_MANAGER_MYSELF = 5;\r
+ private int antiLoopManagerMySelf = 0;\r
+\r
+ private void sendLongRangeLinkRequest(final SymphonyProtocol symphony, final Node node) {\r
+ boolean routingOk;\r
+ do {\r
+ double distance = Math.exp((Math.log(n) / Math.log(2)) * (CommonState.r.nextDouble() - 1.0)); // Harmonic Distribution\r
+ double targetIdentifier = (symphony.getIdentifier() + distance) % 1;\r
+ try {\r
+\r
+ symphony.route(node, targetIdentifier, new Handler() {\r
+\r
+ public void handle(SymphonyProtocol src, Tuple<Node, Double> tuple) {\r
+\r
+ if (tuple == null) {\r
+ Logger.getLogger(SymphonyNetworkManager.class.getName()).log(Level.SEVERE, "FAIL ROUTE SENDLONGRANGELINKREQUEST");\r
+ return;\r
+ }\r
+\r
+ Collection<Node> allShortLinks = new LinkedList<Node>();\r
+ for (Tuple<Node, BootstrapStatus> shortTuple : symphony.leftShortRangeLinks) {\r
+ allShortLinks.add(shortTuple.x);\r
+ }\r
+ for (Tuple<Node, BootstrapStatus> shortTuple : symphony.rightShortRangeLinks) {\r
+ allShortLinks.add(shortTuple.x);\r
+ }\r
+\r
+ /*\r
+ *\r
+ * I'm myself one of my short links, special case... i try again without\r
+ * reduce the attempts for a maximum of MAX_ANTILOOP_COUNT_MANAGER_MYSELF\r
+ * times after that i start again to reduce the attempts\r
+ */\r
+ if (tuple.x.equals(node) || allShortLinks.contains(tuple.x)) {\r
+\r
+ if (antiLoopManagerMySelf < MAX_ANTILOOP_COUNT_MANAGER_MYSELF) {\r
+\r
+ antiLoopManagerMySelf++;\r
+ sendLongRangeLinkRequest(symphony, node);\r
+ } else {\r
+ antiLoopManagerMySelf = 0;\r
+ currentAttempts--;\r
+ }\r
+ } else {\r
+\r
+ boolean alreadyAdded = symphony.longRangeLinksOutgoing.contains(tuple.x);\r
+ /*\r
+ *\r
+ * OPINABLE: DESCREASE ATTEMPTS ONLY FOR REJECT? If yes i have to manage\r
+ * the possible loop (nodi exhaurited so already all added)\r
+ */\r
+ if (alreadyAdded && currentAttempts > 0) {\r
+ currentAttempts--;\r
+ sendLongRangeLinkRequest(symphony, node);\r
+ } else if (!alreadyAdded) {\r
+ Message msg = new Message(null, node, MessageType.REQUEST_LONG_RANGE_LINK);\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+ transport.send(node, tuple.x, msg, pid);\r
+ }\r
+ }\r
+ }\r
+ });\r
+ routingOk = true;\r
+ } catch (RoutingException ex) {\r
+ routingOk = false;\r
+ }\r
+ } while (!routingOk);\r
+ }\r
+\r
+ public void leave(Node node) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+\r
+ if (symphony.loggedIntoNetwork != BootstrapStatus.OFFLINE) {\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+\r
+ symphony.loggedIntoNetwork = BootstrapStatus.OFFLINE;\r
+\r
+ // Communicate that i'm leaving to the outgoing (that i point to) nodes\r
+ for (Node outgoingNode : symphony.longRangeLinksOutgoing) {\r
+ Message disconnectMsg = new Message(null, node, MessageType.DISCONNECT_LONG_RANGE_LINK);\r
+ transport.send(node, outgoingNode, disconnectMsg, pid);\r
+ }\r
+\r
+ // Communicate that i'm leaving to the incoming (that they point to me) nodes\r
+ for (Node incomingNode : symphony.longRangeLinksIncoming) {\r
+ Message unavailableMsg = new Message(null, node, MessageType.UNAVAILABLE_LONG_RANGE_LINK);\r
+ transport.send(node, incomingNode, unavailableMsg, pid);\r
+ }\r
+\r
+ // Communicate to my neighbours (short range links) that i'm leaving and i send to them the near neighbours\r
+ for (Tuple<Node, BootstrapStatus> leftTuple : symphony.leftShortRangeLinks) {\r
+ Message leaveMsg = new Message(symphony.rightShortRangeLinks.clone(), node, MessageType.LEAVE);\r
+ transport.send(node, leftTuple.x, leaveMsg, pid);\r
+ }\r
+\r
+ for (Tuple<Node, BootstrapStatus> rightTuple : symphony.rightShortRangeLinks) {\r
+ Message leaveMsg = new Message(symphony.leftShortRangeLinks.clone(), node, MessageType.LEAVE);\r
+ transport.send(node, rightTuple.x, leaveMsg, pid);\r
+ }\r
+\r
+ node.setFailState(Fallible.DEAD);\r
+ }\r
+ }\r
+\r
+ public void processEvent(Node node, int pid, Object event) {\r
+\r
+ Message msg = (Message) event;\r
+\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+\r
+ Collection<Tuple<Node, BootstrapStatus>> collection = null;\r
+ switch (msg.getType()) {\r
+ case JOIN:\r
+ // I send my current neighbours to the entering node\r
+ collection = (Collection<Tuple<Node, BootstrapStatus>>) symphony.leftShortRangeLinks.clone();\r
+ collection.addAll((Collection<Tuple<Node, BootstrapStatus>>) symphony.rightShortRangeLinks.clone());\r
+ Message responseMsg = new Message(collection, node, MessageType.JOIN_RESPONSE);\r
+ transport.send(node, msg.getSourceNode(), responseMsg, pid);\r
+\r
+ /*\r
+ * Update my neighbours list, adding the new one (for sure it is entering in the\r
+ * left side)\r
+ *\r
+ * Put to "ONLINE_AND_ALL_NEIGHBOURS_OFFLINE" because maybe the bootstrap phase is\r
+ * not terminated yet (ashyncronous communication)\r
+ */\r
+ symphony.leftShortRangeLinks.add(new Tuple<Node, BootstrapStatus>(msg.getSourceNode(), BootstrapStatus.ONLINE_AND_ALL_NEIGHBOURS_OFFLINE));\r
+\r
+\r
+ fixNeighbours(node, symphony.leftShortRangeLinks);\r
+ break;\r
+ case JOIN_RESPONSE:\r
+\r
+ Collection<Tuple<Node, BootstrapStatus>> tupleCollection = (Collection<Tuple<Node, BootstrapStatus>>) msg.getBody();\r
+\r
+ /*\r
+ *\r
+ * My manager is a right neighbour. The manager is already inside the ring, boostrap\r
+ * obliviously ok\r
+ */\r
+ symphony.rightShortRangeLinks.add(new Tuple<Node, BootstrapStatus>(msg.getSourceNode(), BootstrapStatus.ONLINE));\r
+\r
+ // Set my neighbours in the correct position\r
+ for (Tuple<Node, BootstrapStatus> tuple : tupleCollection) {\r
+ if (SymphonyProtocol.isLeftNeighbour(node, tuple.x)) {\r
+ symphony.leftShortRangeLinks.add(tuple);\r
+ } else {\r
+ symphony.rightShortRangeLinks.add(tuple);\r
+ }\r
+ }\r
+\r
+ fixNeighbours(node, symphony.leftShortRangeLinks);\r
+ fixNeighbours(node, symphony.rightShortRangeLinks);\r
+\r
+ // Update bootstrap status\r
+ checkBootstrapStatus(node);\r
+\r
+ // I send the refresh command such a way to exchange the views\r
+ refreshNeighbours(node);\r
+\r
+ // Update Long Range Links, because it's at the beginning is the same as adding k\r
+ updateLongRangeLinks(node);\r
+ break;\r
+ case UPDATE_NEIGHBOURS:\r
+\r
+ Collection<Tuple<Node, BootstrapStatus>> collectionCloned = ((Collection<Tuple<Node, BootstrapStatus>>) symphony.leftShortRangeLinks.clone());\r
+ collectionCloned.addAll(((Collection<Tuple<Node, BootstrapStatus>>) symphony.rightShortRangeLinks.clone()));\r
+\r
+ // Send my neighbours such a way it can also update itself\r
+ Message responseUpdateMsg = new Message(collectionCloned, node, MessageType.UPDATE_NEIGHBOURS_RESPONSE);\r
+ transport.send(node, msg.getSourceNode(), responseUpdateMsg, pid);\r
+\r
+ // Update my view with the new node\r
+ Tuple<Node, BootstrapStatus> neighbourTuple = new Tuple<Node, BootstrapStatus>(msg.getSourceNode(), (BootstrapStatus) msg.getBody());\r
+ if (SymphonyProtocol.isLeftNeighbour(node, msg.getSourceNode())) {\r
+ collection = symphony.leftShortRangeLinks;\r
+ } else {\r
+ collection = symphony.rightShortRangeLinks;\r
+ }\r
+ collection.add(neighbourTuple);\r
+\r
+ fixNeighbours(node, collection);\r
+ fixLookAheadMap(node);\r
+ break;\r
+ case UPDATE_NEIGHBOURS_RESPONSE:\r
+\r
+ Collection<Tuple<Node, BootstrapStatus>> responseCollection = (Collection<Tuple<Node, BootstrapStatus>>) msg.getBody();\r
+\r
+ for (Tuple<Node, BootstrapStatus> neighbourResponseTuple : responseCollection) {\r
+ if (SymphonyProtocol.isLeftNeighbour(node, neighbourResponseTuple.x)) {\r
+ collection = symphony.leftShortRangeLinks;\r
+ } else {\r
+ collection = symphony.rightShortRangeLinks;\r
+ }\r
+ collection.add(neighbourResponseTuple);\r
+ }\r
+\r
+ // Fix the neighbours number to the maximum allow and maybe remove myself from the list\r
+ fixNeighbours(node, symphony.leftShortRangeLinks);\r
+ fixNeighbours(node, symphony.rightShortRangeLinks);\r
+ fixLookAheadMap(node);\r
+ break;\r
+ case UPDATE_STATUS:\r
+ case UPDATE_STATUS_RESPONSE:\r
+\r
+ Node updNode = msg.getSourceNode();\r
+ BootstrapStatus updStatus = (BootstrapStatus) msg.getBody();\r
+\r
+ // I search the neighbour and i update its status\r
+ boolean founded = false;\r
+\r
+ // Try to see if it is on the left\r
+ for (Tuple<Node, BootstrapStatus> leftTuple : symphony.leftShortRangeLinks) {\r
+ if (leftTuple.x.equals(updNode)) {\r
+ symphony.leftShortRangeLinks.remove(leftTuple);\r
+ symphony.leftShortRangeLinks.add(new Tuple<Node, BootstrapStatus>(updNode, updStatus));\r
+\r
+ founded = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ // If it isn't on the left i try with the neighbours on the right\r
+ if (!founded) {\r
+ for (Tuple<Node, BootstrapStatus> rightTuple : symphony.rightShortRangeLinks) {\r
+ if (rightTuple.x.equals(updNode)) {\r
+ symphony.rightShortRangeLinks.remove(rightTuple);\r
+ symphony.rightShortRangeLinks.add(new Tuple<Node, BootstrapStatus>(updNode, updStatus));\r
+\r
+ break;\r
+ }\r
+ }\r
+\r
+ fixNeighbours(node, symphony.rightShortRangeLinks);\r
+ } else {\r
+ fixNeighbours(node, symphony.leftShortRangeLinks);\r
+ }\r
+\r
+ checkBootstrapStatusAndAlert(node);\r
+\r
+ if (msg.getType() == MessageType.UPDATE_STATUS) {\r
+ Message responseUpdStatus = new Message(symphony.loggedIntoNetwork, node, MessageType.UPDATE_STATUS_RESPONSE);\r
+ transport.send(node, updNode, responseUpdStatus, pid);\r
+ }\r
+\r
+ break;\r
+ case REQUEST_LONG_RANGE_LINK:\r
+ MessageType responseType = MessageType.REJECT_LONG_RANGE_LINK;\r
+ if (symphony.longRangeLinksIncoming.size() < (2 * k)) {\r
+ boolean added = symphony.longRangeLinksIncoming.add(msg.getSourceNode());\r
+ if (added) {\r
+ responseType = MessageType.ACCEPTED_LONG_RANGE_LINK;\r
+ }\r
+ }\r
+ Message responseLongLinkMsg = new Message(null, node, responseType);\r
+ transport.send(node, msg.getSourceNode(), responseLongLinkMsg, pid);\r
+ break;\r
+ case ACCEPTED_LONG_RANGE_LINK:\r
+ nLink = n;\r
+ symphony.longRangeLinksOutgoing.add(msg.getSourceNode());\r
+ break;\r
+ case REJECT_LONG_RANGE_LINK:\r
+ if (currentAttempts > 0) {\r
+ currentAttempts--;\r
+ sendLongRangeLinkRequest(symphony, node);\r
+ }\r
+ break;\r
+ case DISCONNECT_LONG_RANGE_LINK:\r
+ symphony.longRangeLinksIncoming.remove(msg.getSourceNode());\r
+ symphony.lookAheadMap.put(msg.getSourceNode(), null);\r
+ break;\r
+ case UNAVAILABLE_LONG_RANGE_LINK:\r
+ symphony.longRangeLinksOutgoing.remove(msg.getSourceNode());\r
+ symphony.lookAheadMap.put(msg.getSourceNode(), null);\r
+ break;\r
+ case LEAVE:\r
+ Tuple<Node, BootstrapStatus> foundedTuple = null;\r
+\r
+ // Verify if the node that is leaving is a left neighbour\r
+ for (Tuple<Node, BootstrapStatus> leftTuple : symphony.leftShortRangeLinks) {\r
+ if (leftTuple.x.equals(msg.getSourceNode())) {\r
+ collection = symphony.leftShortRangeLinks;\r
+ foundedTuple = leftTuple;\r
+ break;\r
+ }\r
+ }\r
+\r
+ // Verify if the node that is leaving is a right neighbour\r
+ if (collection == null) {\r
+ for (Tuple<Node, BootstrapStatus> rightTuple : symphony.rightShortRangeLinks) {\r
+ if (rightTuple.x.equals(msg.getSourceNode())) {\r
+ collection = symphony.rightShortRangeLinks;\r
+ foundedTuple = rightTuple;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // if i've found the neighbour i remove it and i add to myself its neighbours\r
+ if (collection != null) {\r
+ collection.addAll((Collection<Tuple<Node, BootstrapStatus>>) msg.getBody());\r
+ collection.remove(foundedTuple);\r
+ fixNeighbours(node, collection);\r
+\r
+ // Update status and ready to send an alert in case i'm out of the ring\r
+ checkBootstrapStatusAndAlert(node);\r
+ }\r
+ break;\r
+ case KEEP_ALIVE:\r
+ Set<Double>[] lookAheadSetArray = new LinkedHashSet[2];\r
+\r
+ /*\r
+ * Check if the contacting node is doing lookAhead and in case of affirmative answer\r
+ * i provide to it the long range link identifiers (according to my routing mode)\r
+ */\r
+ if ((Boolean) msg.getBody()) {\r
+ int i = 0;\r
+ Iterable[] iterableArray;\r
+ if (symphony.bidirectionalRouting) {\r
+ iterableArray = new Iterable[]{symphony.longRangeLinksOutgoing, symphony.longRangeLinksIncoming};\r
+ } else {\r
+ iterableArray = new Iterable[]{symphony.longRangeLinksOutgoing};\r
+ }\r
+\r
+ for (Iterable<Node> iterable : iterableArray) {\r
+ lookAheadSetArray[i] = new LinkedHashSet<Double>();\r
+ Set<Double> lookAheadSet = lookAheadSetArray[i];\r
+ Iterator<Node> it = iterable.iterator();\r
+ while (it.hasNext()) {\r
+ Node longLinkNode = it.next();\r
+ lookAheadSet.add(((SymphonyProtocol) longLinkNode.getProtocol(symphonyID)).getIdentifier());\r
+ }\r
+ i++;\r
+ }\r
+ }\r
+\r
+ transport.send(node, msg.getSourceNode(), new Message(lookAheadSetArray, node, MessageType.KEEP_ALIVE_RESPONSE), pid);\r
+ break;\r
+ case KEEP_ALIVE_RESPONSE:\r
+ // Reset the counter to 0\r
+ keepAliveMap.put(msg.getSourceNode(), 0);\r
+\r
+ if (symphony.lookAhead) {\r
+ symphony.lookAheadMap.put(msg.getSourceNode(), (Set<Double>[]) msg.getBody());\r
+ }\r
+\r
+ break;\r
+ }\r
+ }\r
+\r
+ /**\r
+ *\r
+ * Update the status and communicate immediately to the neighbours if the node is gone out from\r
+ * the ring (and before it was inside)\r
+ *\r
+ * @param node\r
+ */\r
+ private void checkBootstrapStatusAndAlert(Node node) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ BootstrapStatus beforeStatus = symphony.loggedIntoNetwork;\r
+\r
+ checkBootstrapStatus(node);\r
+\r
+ // Instead of waiting that the update happens periodically i do it now because i'm out of the ring and before i wasn't\r
+ if (symphony.loggedIntoNetwork != beforeStatus && !symphony.isBootstrapped()) {\r
+ updateBootstrapStatusNeighbours(node, true);\r
+ }\r
+ }\r
+\r
+ private void fixNeighbours(Node node, Collection<Tuple<Node, BootstrapStatus>> neighbours) {\r
+\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+\r
+ // Remove duplicates, remove that ones that are in an obsolete status\r
+ Collection<Tuple<Node, BootstrapStatus>> removedNeighbours = new LinkedHashSet<Tuple<Node, BootstrapStatus>>();\r
+ for (Tuple<Node, BootstrapStatus> tuple : neighbours) {\r
+\r
+ // Remove myself from the neighbours list\r
+ if (tuple.x.equals(node)) {\r
+ removedNeighbours.add(tuple);\r
+ continue;\r
+ }\r
+\r
+ EnumSet<BootstrapStatus> status = EnumSet.allOf(BootstrapStatus.class);\r
+ status.remove(tuple.y);\r
+\r
+ for (BootstrapStatus opposite : status) {\r
+ Tuple<Node, BootstrapStatus> oppositeNeighbour = new Tuple<Node, BootstrapStatus>(tuple.x, opposite);\r
+ if (neighbours.contains(oppositeNeighbour)) {\r
+ if (tuple.y != BootstrapStatus.ONLINE) {\r
+ removedNeighbours.add(new Tuple<Node, BootstrapStatus>(tuple.x, BootstrapStatus.OFFLINE));\r
+ if (opposite == BootstrapStatus.ONLINE) {\r
+ removedNeighbours.add(new Tuple<Node, BootstrapStatus>(tuple.x, BootstrapStatus.ONLINE_AND_ALL_NEIGHBOURS_OFFLINE));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ neighbours.removeAll(removedNeighbours);\r
+\r
+ /*\r
+ *\r
+ * I count the neighbours that are in the ONLINE status but before i remove the ones that\r
+ * are gone in timeout during the keep-alive procedure because can be someone that is old\r
+ * but not remove from the exchanging views (UPDATE_NEIGHBOURS) procedure and are not\r
+ * effectively online. To do anyway the possibility to the node to join again i decrease its\r
+ * timeout value. This only if the node is ONLINE and so i'm really interested that it is ok\r
+ * for the routing.\r
+ *\r
+ */\r
+ int onlineNeighbours = 0;\r
+ for (Tuple<Node, BootstrapStatus> tuple : neighbours) {\r
+\r
+ Integer value = keepAliveMap.get(tuple.x);\r
+ if (value != null && value >= nTimeout && tuple.y == BootstrapStatus.ONLINE) {\r
+ keepAliveMap.put(tuple.x, value - 1);\r
+ removedNeighbours.add(tuple);\r
+ } else {\r
+\r
+ if (tuple.y == BootstrapStatus.ONLINE) {\r
+ onlineNeighbours++;\r
+ }\r
+ }\r
+ }\r
+ neighbours.removeAll(removedNeighbours);\r
+\r
+ // Fix the neighbours number to the maximum allowed\r
+ SymphonyNodeComparator comparator = new SymphonyNodeComparator(symphonyID, node);\r
+ AdapterSymphonyNodeComparator adapterComparator = new AdapterSymphonyNodeComparator(comparator);\r
+ while (neighbours.size() > symphony.numberShortRangeLinksPerSide) {\r
+ Tuple<Node, BootstrapStatus> distantTuple = Collections.max(neighbours, adapterComparator);\r
+\r
+ // Mantain the link with the ring\r
+ if (distantTuple.y == BootstrapStatus.ONLINE) {\r
+ if (onlineNeighbours > 1) {\r
+ neighbours.remove(distantTuple);\r
+ onlineNeighbours--;\r
+ } else {\r
+ /*\r
+ * If will be only one neighbour that is online i save it and i'm going to\r
+ * eliminate another one (for sure it'll be not online)\r
+ *\r
+ */\r
+ Tuple<Node, BootstrapStatus> backupOnlineNeighbour = distantTuple;\r
+ neighbours.remove(backupOnlineNeighbour);\r
+ distantTuple = Collections.max(neighbours, adapterComparator);\r
+ neighbours.add(backupOnlineNeighbour);\r
+ neighbours.remove(distantTuple);\r
+ }\r
+\r
+ } else {\r
+ neighbours.remove(distantTuple);\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Object clone() {\r
+ SymphonyNetworkManager dolly = new SymphonyNetworkManager(prefix);\r
+ return dolly;\r
+ }\r
+\r
+ public void nextCycle(Node node, int protocolID) {\r
+\r
+ if (node.isUp()) {\r
+\r
+ // Update the estimated network size\r
+ updateN(node);\r
+\r
+ // Update the estimated K\r
+ updateK(node);\r
+\r
+ // Update the bootstrap status of my neighbours that were joining the ring\r
+ updateBootstrapStatusNeighbours(node, false);\r
+\r
+ // Refresh the neighbours views\r
+ refreshNeighbours(node);\r
+\r
+ // I send and check the connection status of the neighbours\r
+ keepAlive(node);\r
+\r
+ // Update the bootstrap status\r
+ checkBootstrapStatus(node);\r
+\r
+ // If it's active i check the Relinking criteria\r
+ if (relinkingProtocolActivated) {\r
+ reLinkingProtocol(node);\r
+ }\r
+\r
+ // Update the long range links (conservative)\r
+ updateLongRangeLinks(node);\r
+ }\r
+ }\r
+\r
+ /**\r
+ *\r
+ * @param allNeighbours true, communicate/receive the status update from all the neighbours.\r
+ * false, communicate/receive the status update only from the neighbours that are NOT ONLINE\r
+ *\r
+ */\r
+ private void updateBootstrapStatusNeighbours(Node node, boolean allNeighbours) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+\r
+ Collection<Tuple<Node, BootstrapStatus>> collection = new LinkedHashSet<Tuple<Node, BootstrapStatus>>();\r
+ collection.addAll(symphony.leftShortRangeLinks);\r
+ collection.addAll(symphony.rightShortRangeLinks);\r
+\r
+ for (Tuple<Node, BootstrapStatus> neighbourTuple : collection) {\r
+ if (allNeighbours || neighbourTuple.y != BootstrapStatus.ONLINE) {\r
+ Message msg = new Message(symphony.loggedIntoNetwork, node, MessageType.UPDATE_STATUS);\r
+ transport.send(node, neighbourTuple.x, msg, pid);\r
+ }\r
+ }\r
+ }\r
+\r
+ private void updateN(Node node) {\r
+ NetworkSizeEstimatorProtocolInterface networkEstimator = (NetworkSizeEstimatorProtocolInterface) node.getProtocol(networkEstimatorID);\r
+ n = networkEstimator.getNetworkSize(node);\r
+ if (n <= 0) {\r
+ n = DEFAULT_N;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Update the K value with the current expectation of the network size\r
+ */\r
+ private void updateK(Node node) {\r
+\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ if (!symphony.fixedLongRangeLinks) {\r
+ k = (int) Math.ceil(Math.log(n) / Math.log(2));\r
+\r
+ if (k <= 0) {\r
+ k = DEFAULT_K;\r
+ }\r
+ } else {\r
+ k = symphony.numberFixedLongRangeLinks;\r
+ }\r
+ }\r
+\r
+ private void refreshNeighbours(Node node) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+\r
+ for (Tuple<Node, BootstrapStatus> leftTuple : symphony.leftShortRangeLinks) {\r
+ Node leftNode = leftTuple.x;\r
+ Message updateNeighbourMsg = new Message(symphony.loggedIntoNetwork, node, MessageType.UPDATE_NEIGHBOURS);\r
+ transport.send(node, leftNode, updateNeighbourMsg, pid);\r
+ }\r
+\r
+ for (Tuple<Node, BootstrapStatus> rightTuple : symphony.rightShortRangeLinks) {\r
+ Node rightNode = rightTuple.x;\r
+ Message updateNeighbourMsg = new Message(symphony.loggedIntoNetwork, node, MessageType.UPDATE_NEIGHBOURS);\r
+ transport.send(node, rightNode, updateNeighbourMsg, pid);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Method to update the (connection) status of the node. Perform the update to the "up" so:\r
+ * OFFLINE -> ONLINE_AND_ALL_NEIGHBOURS_OFFLINE -> ONLINE\r
+ *\r
+ * and to the "down" only: ONLINE -> ONLINE_AND_ALL_NEIGHBOURS_OFFLINE\r
+ *\r
+ * @param node\r
+ */\r
+ private void checkBootstrapStatus(Node node) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+\r
+ if (symphony.loggedIntoNetwork != BootstrapStatus.OFFLINE) {\r
+\r
+ symphony.loggedIntoNetwork = BootstrapStatus.ONLINE_AND_ALL_NEIGHBOURS_OFFLINE;\r
+\r
+ // Check if i'm inside the ring and i'm able to do routing\r
+ if (!symphony.leftShortRangeLinks.isEmpty() && !symphony.rightShortRangeLinks.isEmpty()) {\r
+\r
+ boolean leftOk = false;\r
+ for (Tuple<Node, BootstrapStatus> leftTuple : symphony.leftShortRangeLinks) {\r
+ if (leftTuple.y == BootstrapStatus.ONLINE) {\r
+ leftOk = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (leftOk) {\r
+ for (Tuple<Node, BootstrapStatus> rightTuple : symphony.rightShortRangeLinks) {\r
+ if (rightTuple.y == BootstrapStatus.ONLINE) {\r
+ symphony.loggedIntoNetwork = BootstrapStatus.ONLINE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Remove the possible wrong entries from the lookAhead table\r
+ */\r
+ private void fixLookAheadMap(Node node) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ for (Tuple<Node, BootstrapStatus> tuple : symphony.leftShortRangeLinks) {\r
+ symphony.lookAheadMap.put(tuple.x, null);\r
+ }\r
+ for (Tuple<Node, BootstrapStatus> tuple : symphony.rightShortRangeLinks) {\r
+ symphony.lookAheadMap.put(tuple.x, null);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Sent keep-alive messages to verify if the links still online\r
+ *\r
+ * if enable the lookAhead mode i require the neighbours list from my neighbours (1-lookAhead).\r
+ *\r
+ * Note: I don't reuse the UPDATE_STATUS messages because i want to mantain separate the\r
+ * semantic and have more clear source code\r
+ */\r
+ private void keepAlive(Node node) {\r
+\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+\r
+ // Send and check for the long range links (both incoming and outgoing)\r
+ for (Iterable<Node> iterable : new Iterable[]{symphony.longRangeLinksOutgoing, symphony.longRangeLinksIncoming}) {\r
+ Iterator<Node> longLinksIterator = iterable.iterator();\r
+ while (longLinksIterator.hasNext()) {\r
+ Node longLinkNode = longLinksIterator.next();\r
+ Integer value = keepAliveMap.get(longLinkNode);\r
+ if (value == null) {\r
+ value = 0;\r
+ }\r
+\r
+ /*\r
+ * Verify if i reached the sufficient time of sending and not receiving an answer\r
+ * and so i can consider that node as disconnected\r
+ */\r
+ if (value >= nTimeout) {\r
+ symphony.lookAheadMap.put(longLinkNode, null); // Do it anyway if it's enabled the lookAhead or not\r
+ longLinksIterator.remove();\r
+ } else {\r
+ keepAliveMap.put(longLinkNode, value + 1);\r
+\r
+ Message keepAliveMsg = new Message(symphony.lookAhead, node, MessageType.KEEP_ALIVE);\r
+ transport.send(node, longLinkNode, keepAliveMsg, pid);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Send and check for the short links\r
+ for (Iterable<Tuple<Node, BootstrapStatus>> iterable : new Iterable[]{symphony.rightShortRangeLinks, symphony.leftShortRangeLinks}) {\r
+ Iterator<Tuple<Node, BootstrapStatus>> shortLinksIterator = iterable.iterator();\r
+ while (shortLinksIterator.hasNext()) {\r
+ Node shortLinkNode = shortLinksIterator.next().x;\r
+ Integer value = keepAliveMap.get(shortLinkNode);\r
+ if (value == null) {\r
+ value = 0;\r
+ }\r
+\r
+ // the same as above\r
+ if (value >= nTimeout) {\r
+ shortLinksIterator.remove();\r
+ } else {\r
+ keepAliveMap.put(shortLinkNode, value + 1);\r
+\r
+ // LookAhead is not to be performed to the short links!\r
+ Message keepAliveMsg = new Message(false, node, MessageType.KEEP_ALIVE);\r
+ transport.send(node, shortLinkNode, keepAliveMsg, pid);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Implement the Re-Linking criteria of the Long Range Links. It does the complete refresh. The\r
+ * repopulation is done through the 'updateLongRangeLinks' method.\r
+ */\r
+ private void reLinkingProtocol(Node node) {\r
+ // I do the check only if i succeed at least one time to create a long range link\r
+ if (nLink > 0) {\r
+ double criterionValue = n / nLink;\r
+\r
+ if (!(criterionValue >= relinkingLowerBound && criterionValue <= relinkingUpperBound)) {\r
+\r
+ /*\r
+ * Not explicitly precised in the paper: if i haven't created a new link i update\r
+ * anyway nLink because can happen a special case that i will not be able to create\r
+ * links because the reLinkingProtocol procedure is "faster".\r
+ */\r
+ nLink = n;\r
+\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ Transport transport = (Transport) node.getProtocol(transportID);\r
+\r
+ // Communicate to the all Outgoing Long Range Links that they aren't anymore\r
+ for (Node longRangeLinkOutgoingNode : symphony.longRangeLinksOutgoing) {\r
+ Message disconnectMsg = new Message(null, node, MessageType.DISCONNECT_LONG_RANGE_LINK);\r
+ transport.send(node, longRangeLinkOutgoingNode, disconnectMsg, pid);\r
+ }\r
+\r
+ symphony.longRangeLinksOutgoing.clear();\r
+ }\r
+ }\r
+ }\r
+\r
+ public int getK() {\r
+ return k;\r
+ }\r
+\r
+ public int getN() {\r
+ return n;\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.Comparator;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ * Comparator that measure the relative distance from a target node\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SymphonyNodeComparator implements Comparator<Node> {\r
+\r
+ private final int symphonyID;\r
+ private double target;\r
+\r
+ public SymphonyNodeComparator(int symphonyID) {\r
+ this.symphonyID = symphonyID;\r
+ }\r
+\r
+ public SymphonyNodeComparator(int symphonyID, double target) {\r
+ this(symphonyID);\r
+ this.target = target;\r
+ }\r
+\r
+ public SymphonyNodeComparator(int symphonyID, Node targetNode) {\r
+ this(symphonyID);\r
+ SymphonyProtocol symphony = (SymphonyProtocol) targetNode.getProtocol(symphonyID);\r
+ this.target = symphony.getIdentifier();\r
+ }\r
+\r
+ public int compare(Node o1, Node o2) {\r
+\r
+ SymphonyProtocol symphony1 = (SymphonyProtocol) o1.getProtocol(symphonyID);\r
+ SymphonyProtocol symphony2 = (SymphonyProtocol) o2.getProtocol(symphonyID);\r
+\r
+ Double identifier1 = symphony1.getIdentifier();\r
+ Double identifier2 = symphony2.getIdentifier();\r
+\r
+ Double distance1 = Math.abs(target - identifier1) % 1;\r
+ Double distance2 = Math.abs(target - identifier2) % 1;\r
+\r
+ identifier1 = Math.min(1.0 - distance1, distance1);\r
+ identifier2 = Math.min(1.0 - distance2, distance2);\r
+\r
+ Logger.getLogger(SymphonyNodeComparator.class.getName()).log(Level.FINEST, "id1= " + symphony1.getIdentifier() + " target= " + target + " id2= " + symphony2.getIdentifier() + " res= " + identifier1.compareTo(identifier2));\r
+\r
+ return identifier1.compareTo(identifier2);\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+import peersim.config.Configuration;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+import peersim.dynamics.NodeInitializer;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SymphonyNodeInizializer implements NodeInitializer {\r
+\r
+ private static final String PAR_NETMANAGER = "symphonynetworkmanager";\r
+ private static final String PAR_SYMPHONY = "symphony";\r
+ private static final String PAR_BOOTNODE = "bootstrapnode";\r
+ private final int networkManagerID;\r
+ private final int symphonyID;\r
+ private final int indexBootstrapNode;\r
+\r
+ public SymphonyNodeInizializer(String prefix) {\r
+\r
+ networkManagerID = Configuration.getPid(prefix + "." + PAR_NETMANAGER);\r
+ indexBootstrapNode = Configuration.getInt(prefix + "." + PAR_BOOTNODE, 0);\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY);\r
+ }\r
+\r
+ @Override\r
+ public void initialize(Node node) {\r
+ int indexRealBootstrapNode = indexBootstrapNode;\r
+ Node realBootstrapNode = Network.get(indexBootstrapNode);\r
+ SymphonyNetworkManager symphonyNetworkManager = (SymphonyNetworkManager) node.getProtocol(networkManagerID);\r
+ SymphonyProtocol symphony = (SymphonyProtocol) realBootstrapNode.getProtocol(symphonyID);\r
+\r
+ boolean joinSent;\r
+ do {\r
+ try {\r
+ while (!symphony.isBootstrapped() || !realBootstrapNode.isUp()) {\r
+ indexRealBootstrapNode = (indexRealBootstrapNode + 1) % Network.size();\r
+ realBootstrapNode = Network.get(indexRealBootstrapNode);\r
+ symphony = (SymphonyProtocol) realBootstrapNode.getProtocol(symphonyID);\r
+\r
+ if (indexRealBootstrapNode == indexBootstrapNode) {\r
+ Logger.getLogger(SymphonyNodeInizializer.class.getName()).log(Level.WARNING, "No node ONLINE. Impossible to do the network bootstrap.");\r
+ return;\r
+ }\r
+ }\r
+\r
+ symphonyNetworkManager.join(node, realBootstrapNode);\r
+ joinSent = true;\r
+ } catch (RoutingException ex) {\r
+ Logger.getLogger(SymphonyNodeInizializer.class.getName()).log(Level.SEVERE, "Join Issue");\r
+ joinSent = false;\r
+ }\r
+ } while (!joinSent);\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import java.lang.ref.SoftReference;\r
+import java.util.*;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import example.symphony.Message.MessageType;\r
+import peersim.config.Configuration;\r
+import peersim.core.CommonState;\r
+import peersim.core.Node;\r
+import peersim.edsim.EDProtocol;\r
+import peersim.transport.Transport;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SymphonyProtocol implements EDProtocol {\r
+\r
+ private static final String PAR_SHORT_LINK = "shortlink";\r
+ private static final String PAR_LONG_LINK = "longlink";\r
+ private static final String PAR_TRANSP = "transport";\r
+ private static final String PAR_ROUTING = "routing";\r
+ private static final String PAR_LOOKAHEAD = "lookahead";\r
+ private static Set<Double> allIdentifier = new HashSet<Double>();\r
+ private final String prefix;\r
+ private static int pid;\r
+ private final int transportID;\r
+ private final double identifier;\r
+ public final int sequentialIdentifier;\r
+ private static int sequentialCounter = 0;\r
+ public final int numberShortRangeLinksPerSide;\r
+ public final boolean bidirectionalRouting;\r
+ public final boolean lookAhead;\r
+ public final boolean fixedLongRangeLinks;\r
+ public final int numberFixedLongRangeLinks;\r
+ public LinkedHashSet<Node> longRangeLinksOutgoing;\r
+ public LinkedHashSet<Node> longRangeLinksIncoming;\r
+ public LinkedHashSet<Tuple<Node, BootstrapStatus>> rightShortRangeLinks;\r
+ public LinkedHashSet<Tuple<Node, BootstrapStatus>> leftShortRangeLinks;\r
+ /**\r
+ * Array Contract: at position 0 -> OutgoingLongRangeLinks, 1 -> IncomingLongRangeLinks\r
+ */\r
+ public final LinkedHashMap<Node, Set<Double>[]> lookAheadMap;\r
+ private HashMap<Double, Handler> mapHandler;\r
+ /**\r
+ * IDs Set to verify if there are cycles\r
+ */\r
+ private Set<Long> messageHistoryID;\r
+ /**\r
+ *\r
+ * Tuple chronology that contains: <received message, the possible answer message>\r
+ *\r
+ * I use SoftReference as a trade off between memory usage and accurancy\r
+ */\r
+ private Set<SoftReference<Tuple<Message, Message>>> messageHistory;\r
+ private static boolean firstPrintConfig = true;\r
+\r
+ public enum BootstrapStatus {\r
+\r
+ NEW, OFFLINE, ONLINE_AND_ALL_NEIGHBOURS_OFFLINE, ONLINE\r
+ }\r
+ public BootstrapStatus loggedIntoNetwork;\r
+\r
+ public SymphonyProtocol(String prefix) {\r
+\r
+ this.prefix = prefix;\r
+ pid = Configuration.lookupPid(prefix.replaceAll("protocol.", ""));\r
+ transportID = Configuration.getPid(prefix + "." + PAR_TRANSP);\r
+ numberShortRangeLinksPerSide = Configuration.getInt(prefix + "." + PAR_SHORT_LINK, 2) / 2;\r
+ bidirectionalRouting = !Configuration.getString(prefix + "." + PAR_ROUTING, "bidirectional").toLowerCase().equals("unidirectional");\r
+ lookAhead = !Configuration.getString(prefix + "." + PAR_LOOKAHEAD, "on").toLowerCase().equals("off");\r
+ numberFixedLongRangeLinks = Configuration.getInt(prefix + "." + PAR_LONG_LINK, -1);\r
+ fixedLongRangeLinks = numberFixedLongRangeLinks >= 0;\r
+\r
+ longRangeLinksOutgoing = new LinkedHashSet<Node>();\r
+ longRangeLinksIncoming = new LinkedHashSet<Node>();\r
+ rightShortRangeLinks = new LinkedHashSet<Tuple<Node, BootstrapStatus>>();\r
+ leftShortRangeLinks = new LinkedHashSet<Tuple<Node, BootstrapStatus>>();\r
+ lookAheadMap = new LinkedHashMap<Node, Set<Double>[]>();\r
+\r
+ identifier = generateUniqueIdentifier();\r
+ sequentialIdentifier = sequentialCounter++;\r
+\r
+ mapHandler = new HashMap<Double, Handler>();\r
+\r
+ messageHistoryID = new HashSet<Long>();\r
+ messageHistory = new LinkedHashSet<SoftReference<Tuple<Message, Message>>>();\r
+ loggedIntoNetwork = BootstrapStatus.NEW;\r
+\r
+ printConfig();\r
+ }\r
+\r
+ private void printConfig() {\r
+\r
+ if (firstPrintConfig) {\r
+ firstPrintConfig = false;\r
+ System.out.println(SymphonyProtocol.class.getSimpleName() + " Configuration:");\r
+ System.out.println("- Number of short range links per side: " + numberShortRangeLinksPerSide);\r
+ System.out.println("- Number of long range links: " + (fixedLongRangeLinks ? numberFixedLongRangeLinks : "log(n)"));\r
+ System.out.println("- Routing mode: " + (bidirectionalRouting ? "Bidirectional" : "Unidirectional"));\r
+ System.out.println("- LookAhead status: " + (lookAhead ? "ON" : "OFF"));\r
+ System.out.println("-------------------------------\n");\r
+ }\r
+ }\r
+\r
+ /**\r
+ *\r
+ * Method to identify the next node that has to be contacted. It's going to be used the mode\r
+ * that is described into the configuration file\r
+ */\r
+ public Node getCandidateForRouting(double identifierToRoute) throws RoutingException {\r
+ if (bidirectionalRouting) {\r
+ return getCandidateForBidirectionalRoute(identifierToRoute);\r
+ } else {\r
+ return getCandidateForUnidirectionalRoute(identifierToRoute);\r
+ }\r
+ }\r
+\r
+ /**\r
+ *\r
+ * Method to individuate the next node that as to be contacted through Unidirectional Routing\r
+ * mode\r
+ */\r
+ public Node getCandidateForUnidirectionalRoute(double identifierToRoute) throws RoutingException {\r
+\r
+ LinkedHashSet<Node> allLinks = new LinkedHashSet<Node>();\r
+ Node manager = putShortRangeLinksIntoContainerForRouting(allLinks, identifierToRoute);\r
+\r
+ if (manager != null) {\r
+ return manager;\r
+ }\r
+\r
+ allLinks.addAll(longRangeLinksOutgoing);\r
+\r
+ return findClosestNode(identifierToRoute, allLinks, true);\r
+ }\r
+\r
+ /**\r
+ * Method to individuate the next node that as to be contacted through Bidirectional Routing\r
+ * mode\r
+ */\r
+ public Node getCandidateForBidirectionalRoute(double identifierToRoute) throws RoutingException {\r
+\r
+ LinkedHashSet<Node> allLinks = new LinkedHashSet<Node>();\r
+ Node manager = putShortRangeLinksIntoContainerForRouting(allLinks, identifierToRoute);\r
+\r
+ if (manager != null) {\r
+ return manager;\r
+ }\r
+\r
+ allLinks.addAll(longRangeLinksOutgoing);\r
+ allLinks.addAll(longRangeLinksIncoming);\r
+\r
+ return findClosestNode(identifierToRoute, allLinks, false);\r
+ }\r
+\r
+ /**\r
+ * @return Null if it is NOT found the manager. Node if it is found.\r
+ */\r
+ private Node putShortRangeLinksIntoContainerForRouting(Set<Node> container, double identifierToRoute) {\r
+ for (Tuple<Node, BootstrapStatus> rightTuple : rightShortRangeLinks) {\r
+ if (rightTuple.y == BootstrapStatus.ONLINE) {\r
+ container.add(rightTuple.x);\r
+ }\r
+ }\r
+\r
+ if (!container.isEmpty()) {\r
+\r
+ // Special case: i verify if the neighbour at my right (ONLINE) is the manager\r
+ SymphonyNodeComparator comparator = new SymphonyNodeComparator(pid, identifier);\r
+ Node nearRightNeighbour = Collections.min(container, comparator);\r
+ if (nearRightNeighbour != null) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) nearRightNeighbour.getProtocol(pid);\r
+ if (!isLeftNeighbour(identifier, identifierToRoute) && isLeftNeighbour(symphony.getIdentifier(), identifierToRoute)) {\r
+ return nearRightNeighbour;\r
+ }\r
+ }\r
+ }\r
+\r
+ for (Tuple<Node, BootstrapStatus> leftTuple : leftShortRangeLinks) {\r
+ if (leftTuple.y == BootstrapStatus.ONLINE) {\r
+ container.add(leftTuple.x);\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ *\r
+ * Individuates effectively the next candidate for the routing. Checks if the lookahead is\r
+ * activated and in case of affirmative answer it's going to use that information.\r
+ *\r
+ * @param identifierToRoute Identifier to reach\r
+ * @param container Candidate Nodes Container\r
+ * @param clockwise true, does unidirectional routing. false, does bidirectional routing.\r
+ * @return The nearest node to reach identifierToRoute\r
+ * @throws RoutingException Throw in case no candidate is found\r
+ */\r
+ public Node findClosestNode(final double identifierToRoute, final Iterable<Node> container, final boolean clockwise) throws RoutingException {\r
+ Node ret = null;\r
+ double min = Double.MAX_VALUE;\r
+\r
+ for (Node node : container) {\r
+ SymphonyProtocol symphonyNodeContainer = (SymphonyProtocol) node.getProtocol(pid);\r
+ double realCandidateIdentifier = symphonyNodeContainer.getIdentifier();\r
+\r
+ Set<Double> candidateIdentifierSet = new LinkedHashSet<Double>();\r
+ candidateIdentifierSet.add(realCandidateIdentifier);\r
+\r
+ boolean lookAheadClockwise = true;\r
+\r
+ /*\r
+ *\r
+ * If lookahead is activated add all the reachable identifiers. No checks are performed\r
+ * on the node type (short/long) because at maximum the map return null.\r
+ */\r
+ if (lookAhead) {\r
+ Set<Double>[] lookAheadIdentifierSetArray = lookAheadMap.get(node);\r
+\r
+ if (lookAheadIdentifierSetArray != null) {\r
+ Set<Double> lookAheadIdentifierSet = lookAheadIdentifierSetArray[0];\r
+\r
+ if (lookAheadIdentifierSet == null) {\r
+ lookAheadIdentifierSet = new LinkedHashSet<Double>();\r
+ }\r
+\r
+ /*\r
+ *\r
+ * If bidirectional routing is going to be performed so i put into account also\r
+ * the Incoming Long Range Links of the current neighbour\r
+ */\r
+ if (bidirectionalRouting && lookAheadIdentifierSetArray[1] != null) {\r
+ lookAheadIdentifierSet.addAll(lookAheadIdentifierSetArray[1]);\r
+ lookAheadClockwise = false;\r
+ }\r
+\r
+ if (!lookAheadIdentifierSet.isEmpty()) {\r
+ candidateIdentifierSet.addAll(lookAheadIdentifierSet);\r
+ }\r
+ }\r
+ }\r
+\r
+ for (Double candidateIdentifier : candidateIdentifierSet) {\r
+ // if it is a my neighbour i use my routing mode instead if it is a looAhead one i use its routing mode\r
+ boolean currentClockwise = candidateIdentifier.equals(realCandidateIdentifier) ? clockwise : lookAheadClockwise;\r
+\r
+ double distance = Math.abs(candidateIdentifier - identifierToRoute);\r
+ distance = Math.min(distance, 1.0 - distance);\r
+\r
+ // if clockwise i have to exclude the case: candidateIdentifier - indentifierToRoute - identifier\r
+ if (currentClockwise) {\r
+ if (isLeftNeighbour(candidateIdentifier, identifierToRoute)) {\r
+\r
+ // Special case (0.9 - 0.1) the normal order is not more meanful to decide the side\r
+ if (identifierToRoute >= candidateIdentifier) {\r
+ distance = identifierToRoute - candidateIdentifier;\r
+ } else {\r
+ distance = (1.0 - candidateIdentifier) + identifierToRoute;\r
+ }\r
+ } else {\r
+ distance = (1.0 - (candidateIdentifier - identifierToRoute)) % 1;\r
+ }\r
+ }\r
+\r
+ /*\r
+ *\r
+ * Priority to the node that i'm directly connected and only after i use the\r
+ * lookAhead information\r
+ */\r
+ if (min >= Math.abs(distance)\r
+ && (candidateIdentifier.equals(realCandidateIdentifier)\r
+ || ret == null\r
+ || min > Math.abs(distance))) {\r
+ ret = node;\r
+ min = Math.abs(distance);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (ret == null) {\r
+ throw new RoutingException("Impossible do routing. [Hit: Neighbour links (maybe) not yet online.");\r
+ }\r
+\r
+ return ret;\r
+ }\r
+\r
+ /**\r
+ *\r
+ * @param neighbourNode Neighbour Node\r
+ * @return true if the node is a left neighbour (or itself), false if it is a right one\r
+ */\r
+ public static boolean isLeftNeighbour(Node rootNode, Node neighbourNode) {\r
+ SymphonyProtocol rootSymphony = (SymphonyProtocol) rootNode.getProtocol(pid);\r
+ SymphonyProtocol neighbourSymphony = (SymphonyProtocol) neighbourNode.getProtocol(pid);\r
+\r
+ return isLeftNeighbour(rootSymphony.getIdentifier(), neighbourSymphony.getIdentifier());\r
+ }\r
+\r
+ public static boolean isLeftNeighbour(double rootIdentifier, double neighbourIdentifier) {\r
+\r
+ // I calculate putting the hypotesis that i have to translate/"normalize", after i'll check if it was useless\r
+ double traslateRootIdentifier = (rootIdentifier + 0.5) % 1;\r
+ double traslateNeighbourIdentifier = (neighbourIdentifier + 0.5) % 1;\r
+ double distance = traslateNeighbourIdentifier - traslateRootIdentifier;\r
+\r
+ // I verify if the neighbourIdentifier is over half ring, if yes i don't need to do the translation/"normalization"\r
+ if ((neighbourIdentifier + 0.5) != traslateNeighbourIdentifier) {\r
+ distance = neighbourIdentifier - rootIdentifier;\r
+ }\r
+\r
+ return distance >= 0 && distance <= 0.5;\r
+ }\r
+\r
+ public void route(Node src, double key, Handler handler) throws RoutingException {\r
+\r
+ mapHandler.put(key, handler);\r
+\r
+ Message msg = new Message(key, src, MessageType.ROUTE);\r
+\r
+ Node targetNode = src;\r
+\r
+ if (!isManagerOf(key)) {\r
+ targetNode = getCandidateForRouting(key);\r
+ Transport transport = (Transport) src.getProtocol(transportID);\r
+ transport.send(src, targetNode, msg, pid);\r
+ }\r
+\r
+ // Insert the message into the chronology\r
+ Tuple<Message, Message> historyTuple = new Tuple<Message, Message>();\r
+ try {\r
+ historyTuple.x = msg;\r
+ historyTuple.y = (Message) msg.clone();\r
+ historyTuple.y.setCurrentHop(targetNode);\r
+ } catch (CloneNotSupportedException ex) {\r
+ Logger.getLogger(SymphonyProtocol.class.getName()).log(Level.SEVERE, "Impossible to clonate the message!");\r
+ historyTuple.x = null;\r
+ historyTuple.y = msg;\r
+ msg.setCurrentHop(targetNode);\r
+ }\r
+ messageHistory.add(new SoftReference<Tuple<Message, Message>>(historyTuple));\r
+ messageHistoryID.add(msg.getID());\r
+\r
+ /*\r
+ *\r
+ * If i am the manager (brutally through the reference), i don't do the loopback routing but\r
+ * i soddisfy immediately the request\r
+ */\r
+ if (targetNode == src) {\r
+\r
+ // Uppdate the chronology\r
+ historyTuple.y = new Message(key, targetNode, MessageType.ROUTE_RESPONSE);\r
+\r
+ Tuple<Node, Double> tuple = new Tuple<Node, Double>(src, key);\r
+ mapHandler.remove(key);\r
+ handler.handle(this, tuple);\r
+ }\r
+ }\r
+\r
+ public void processEvent(Node node, int pid, Object event) {\r
+ Message msg = (Message) event;\r
+ msg.incrementHop(); // I increment the message Hop\r
+\r
+ Tuple<Message, Message> historyTuple = new Tuple<Message, Message>();\r
+ try {\r
+ // I clone the message such a way to store into the chronology the hop sender's information\r
+ historyTuple.x = (Message) msg.clone();\r
+ } catch (CloneNotSupportedException ex) {\r
+ Logger.getLogger(SymphonyProtocol.class.getName()).log(Level.SEVERE, "Impossible to clonate the message!");\r
+ historyTuple.x = msg;\r
+ }\r
+\r
+ messageHistory.add(new SoftReference<Tuple<Message, Message>>(historyTuple));\r
+\r
+ Double key;\r
+ Transport transport;\r
+ Handler handler;\r
+\r
+ // Individuate cycles\r
+ if (messageHistoryID.contains(msg.getID())) {\r
+ Message responseMsg = new Message(msg, node, MessageType.ROUTE_FAIL);\r
+\r
+ historyTuple.y = responseMsg;\r
+\r
+ transport = (Transport) node.getProtocol(transportID);\r
+ transport.send(node, msg.getSourceNode(), responseMsg, pid);\r
+ return;\r
+ }\r
+\r
+ /*\r
+ * If i'm arrived till here means that i'm not into a cycle --> i insert the message ID into\r
+ * the chronology\r
+ */\r
+ messageHistoryID.add(msg.getID());\r
+\r
+ switch (msg.getType()) {\r
+ case ROUTE:\r
+ key = (Double) msg.getBody();\r
+ Logger.getLogger(SymphonyProtocol.class.getName()).log(Level.FINEST, key + " " + identifier);\r
+ if (isManagerOf(key)) {\r
+ transport = (Transport) msg.getSourceNode().getProtocol(transportID);\r
+ Message responseMsg = new Message(new Tuple<Node, Double>(node, key), node, MessageType.ROUTE_RESPONSE);\r
+ historyTuple.y = responseMsg;\r
+ transport.send(node, msg.getSourceNode(), responseMsg, pid);\r
+ } else {\r
+ try {\r
+ Node targetNode = getCandidateForRouting(key);\r
+\r
+ try {\r
+ // I clone the message such a way to store the info (into the chronology) of the hop receiver\r
+ historyTuple.y = (Message) msg.clone();\r
+ historyTuple.y.setCurrentHop(targetNode);\r
+ } catch (CloneNotSupportedException ex) {\r
+ Logger.getLogger(SymphonyProtocol.class.getName()).log(Level.SEVERE, "Impossible to clonate the message!");\r
+ historyTuple.y = msg;\r
+ msg.setCurrentHop(targetNode);\r
+ }\r
+\r
+ transport = (Transport) node.getProtocol(transportID);\r
+ transport.send(node, targetNode, msg, pid);\r
+ } catch (RoutingException ex) {\r
+ /*\r
+ *\r
+ * I send the same message to myself (it is going to queue into the event\r
+ * queue and in this way i "earn" time (postpone) and i hope that the\r
+ * network will be ok in the meanwhile)\r
+ */\r
+ historyTuple.y = msg;\r
+ msg.setCurrentHop(node);\r
+ transport = (Transport) node.getProtocol(transportID);\r
+ transport.send(node, node, msg, pid);\r
+ }\r
+ }\r
+ break;\r
+ case ROUTE_RESPONSE:\r
+ Tuple<Node, Double> tuple = (Tuple<Node, Double>) msg.getBody();\r
+ key = tuple.y;\r
+ handler = mapHandler.get(key);\r
+ mapHandler.remove(key);\r
+ handler.handle(this, tuple);\r
+ break;\r
+ case ROUTE_FAIL:\r
+ Message requestMsg = (Message) msg.getBody();\r
+ key = (Double) requestMsg.getBody();\r
+ handler = mapHandler.get(key);\r
+ mapHandler.remove(key);\r
+ handler.handle(this, null);\r
+ break;\r
+ }\r
+ }\r
+\r
+ public boolean isManagerOf(double key) {\r
+\r
+ if (key == identifier) {\r
+ return true;\r
+ }\r
+\r
+ SymphonyNodeComparator comparator = new SymphonyNodeComparator(pid, identifier);\r
+ AdapterSymphonyNodeComparator adapterComparator = new AdapterSymphonyNodeComparator(comparator);\r
+\r
+ Collection<Tuple<Node, BootstrapStatus>> leftShortRangeLinksCloned = (Collection<Tuple<Node, BootstrapStatus>>) leftShortRangeLinks.clone();\r
+ Node targetNode = null;\r
+\r
+ while (targetNode == null && !leftShortRangeLinksCloned.isEmpty()) {\r
+ Tuple<Node, BootstrapStatus> nearTuple = Collections.min(leftShortRangeLinksCloned, adapterComparator);\r
+ if (nearTuple.y == BootstrapStatus.ONLINE) {\r
+ targetNode = nearTuple.x;\r
+ } else {\r
+ leftShortRangeLinksCloned.remove(nearTuple);\r
+ }\r
+ }\r
+\r
+ // SPECIAL CASE: NO LEFT NEIGHBOURS. I became the Manager.\r
+ if (targetNode == null) {\r
+ return true;\r
+ }\r
+\r
+ SymphonyProtocol symphony = (SymphonyProtocol) targetNode.getProtocol(pid);\r
+ // Check if it's the situation: right neighbour - key - me. So if i'm the manager or not.\r
+ boolean ret = isLeftNeighbour(identifier, key) && (!isLeftNeighbour(symphony.getIdentifier(), key) && symphony.getIdentifier() != key);\r
+\r
+ return ret;\r
+ }\r
+\r
+ public double getIdentifier() {\r
+ return identifier;\r
+ }\r
+\r
+ public Tuple<Message, Message>[] getHistoryMessage() {\r
+ SoftReference<Tuple<Message, Message>>[] array = messageHistory.toArray(new SoftReference[0]);\r
+ LinkedList<Tuple<Message, Message>> list = new LinkedList<Tuple<Message, Message>>();\r
+ for (SoftReference<Tuple<Message, Message>> reference : array) {\r
+ Tuple<Message, Message> tuple = reference.get();\r
+ if (tuple != null) {\r
+ list.add(tuple);\r
+ }\r
+ }\r
+ return list.toArray(new Tuple[0]);\r
+ }\r
+\r
+ public void clearHistoryMessage() {\r
+ messageHistory.clear();\r
+ }\r
+\r
+ private double generateUniqueIdentifier() {\r
+ boolean duplicated = true;\r
+ Double id = null;\r
+\r
+ while (duplicated) {\r
+ id = CommonState.r.nextDouble();\r
+ duplicated = allIdentifier.contains(id);\r
+ }\r
+\r
+ allIdentifier.add(id);\r
+\r
+ return id;\r
+ }\r
+\r
+ @Override\r
+ public Object clone() {\r
+ SymphonyProtocol dolly = new SymphonyProtocol(prefix);\r
+ return dolly;\r
+ }\r
+\r
+ public boolean isBootstrapped() {\r
+ return loggedIntoNetwork == BootstrapStatus.ONLINE;\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+import peersim.config.Configuration;\r
+import peersim.core.Control;\r
+import peersim.core.Network;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class SymphonyStatistics implements Control {\r
+\r
+ private static final String PAR_SYMPHONY = "symphony";\r
+ private final int symphonyID;\r
+ private long totalMsg = 0;\r
+ private long numRouteResposeMsg = 0;\r
+ private long numRouteMsg = 0;\r
+ private long numRouteFailMsg = 0;\r
+ private long numRouteFoundManagerMsg = 0;\r
+ private double mediaHopRouteResponseMsg = 0.0;\r
+\r
+ public SymphonyStatistics(String prefix) {\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY);\r
+ }\r
+\r
+ public boolean execute() {\r
+\r
+ AdapterIterableNetwork itNetwork = new AdapterIterableNetwork();\r
+ for (Node node : itNetwork) {\r
+ SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID);\r
+ Tuple<Message, Message>[] tupleMessages = symphony.getHistoryMessage();\r
+ totalMsg += tupleMessages.length;\r
+\r
+ for (Tuple<Message, Message> tupleMessage : tupleMessages) {\r
+\r
+ Message message = tupleMessage.x;\r
+\r
+ if (message != null) {\r
+ switch (message.getType()) {\r
+ case ROUTE:\r
+ numRouteMsg++;\r
+ if (tupleMessage.y != null && tupleMessage.y.getType() == Message.MessageType.ROUTE_RESPONSE) {\r
+ numRouteFoundManagerMsg++;\r
+ mediaHopRouteResponseMsg = ((mediaHopRouteResponseMsg * (numRouteFoundManagerMsg - 1)) + message.getHop()) / (double) numRouteFoundManagerMsg;\r
+ }\r
+ break;\r
+ case ROUTE_FAIL:\r
+ numRouteFailMsg++;\r
+ break;\r
+ case ROUTE_RESPONSE:\r
+ numRouteResposeMsg++;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ symphony.clearHistoryMessage();\r
+ }\r
+\r
+ printStatistics();\r
+\r
+ return false;\r
+ }\r
+\r
+ private void printStatistics() {\r
+ System.out.println("### Statistics ###");\r
+ System.out.println("- Total Messages: " + totalMsg);\r
+ System.out.println("- Total Route Messages: " + numRouteMsg);\r
+ System.out.println("- Found Manager Route Message: " + numRouteFoundManagerMsg);\r
+ System.out.println("- Response Message: " + numRouteResposeMsg);\r
+ System.out.println("- Fail Message: " + numRouteFailMsg);\r
+ System.out.println();\r
+ System.out.println("Average Hop:" + mediaHopRouteResponseMsg + " Expected value (k = log n): " + (Math.log(Network.size()) / Math.log(2)));\r
+ System.out.println("### END ###\n");\r
+ }\r
+}\r
--- /dev/null
+package example.symphony;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class Tuple<X, Y> {\r
+\r
+ public X x;\r
+ public Y y;\r
+\r
+ public Tuple() {\r
+ }\r
+\r
+ public Tuple(X x, Y y) {\r
+ this.x = x;\r
+ this.y = y;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+\r
+ if (obj instanceof Tuple) {\r
+ Tuple tuple = (Tuple) obj;\r
+\r
+ // (x != null && tuple.x != null) ==> (x==tuple.x || x.equals(tuple.x))\r
+ // x == null <==> tuple.x == null\r
+\r
+ boolean equalsX = (x == null && tuple.x == null) || ((x != null && tuple.x != null) && (x == tuple.x || x.equals(tuple.x)));\r
+ boolean equalsY = (y == null && tuple.y == null) || ((y != null && tuple.y != null) && (y == tuple.y || y.equals(tuple.y)));\r
+\r
+ return equalsX && equalsY;\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ int hash = 5;\r
+ hash = 89 * hash + (this.x != null ? this.x.hashCode() : 0);\r
+ hash = 89 * hash + (this.y != null ? this.y.hashCode() : 0);\r
+ return hash;\r
+ }\r
+}\r
--- /dev/null
+package example.symphony.test;\r
+\r
+import example.symphony.AdapterIterableNetwork;\r
+import example.symphony.SymphonyNetworkManager;\r
+import example.symphony.SymphonyProtocol;\r
+import peersim.config.Configuration;\r
+import peersim.core.Control;\r
+import peersim.core.Node;\r
+\r
+/**\r
+ *\r
+ * @author Andrea Esposito <and1989@gmail.com>\r
+ */\r
+public class NetworkEstimationTest implements Control {\r
+\r
+ private static final String PAR_NETMANAGER = "symphonynetworkmanager";\r
+ private static final String PAR_SYMPHONY = "symphony";\r
+ private final int symphonyID;\r
+ private final int networkManagerID;\r
+\r
+ public NetworkEstimationTest(String prefix) {\r
+\r
+ networkManagerID = Configuration.getPid(prefix + "." + PAR_NETMANAGER);\r
+ symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY);\r
+ }\r
+\r
+ public boolean execute() {\r
+\r
+ AdapterIterableNetwork it = new AdapterIterableNetwork();\r
+ int max = Integer.MIN_VALUE;\r
+ int min = Integer.MAX_VALUE;\r
+ int sum = 0;\r
+ int total = 0;\r
+ for (Node node : it) {\r
+ if (node.isUp() && ((SymphonyProtocol) node.getProtocol(symphonyID)).isBootstrapped()) {\r
+ SymphonyNetworkManager networkManager = (SymphonyNetworkManager) node.getProtocol(networkManagerID);\r
+ int n = networkManager.getN();\r
+ min = n < min ? n : min;\r
+ max = n > max ? n : max;\r
+ sum += n;\r
+ total++;\r
+ }\r
+ }\r
+\r
+ System.out.println("Real Dimension: " + (Math.log(total) / Math.log(2)));\r
+ System.out.println("Average Estimated Dimension: " + (total == 0 ? "No Node online" : (Math.log((sum / total)) / Math.log(2))));\r
+ System.out.println("MAX: " + Math.log(max) / Math.log(2));\r
+ System.out.println("MIN: " + Math.log(min) / Math.log(2));\r
+\r
+ return false;\r
+ }\r
+}\r
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim;
+
+import psgsim.PSGSimulator;
+
+import java.io.*;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.simgrid.msg.HostNotFoundException;
+import org.simgrid.msg.NativeException;
+
+import peersim.cdsim.*;
+import peersim.config.*;
+import peersim.core.*;
+import peersim.edsim.*;
+
+/**
+ * This is the main entry point to peersim. This class loads configuration and
+ * detects the simulation type. According to this, it invokes the appropriate
+ * simulator. The known simulators at this moment, along with the way to detect
+ * them are the following:
+ * <ul>
+ * <li>{@link CDSimulator}: if {@link CDSimulator#isConfigurationCycleDriven}
+ * returns true</li>
+ * <li>{@link EDSimulator}: if {@link EDSimulator#isConfigurationEventDriven}
+ * returns true</li>
+ * </ul>
+ * This list represents the order in which these alternatives are checked. That
+ * is, if more than one return true, then the first will be taken. Note that
+ * this class checks only for these clues and does not check if the
+ * configuration is consistent or valid.
+ *
+ * @see #main
+ */
+public class Simulator {
+
+ // ========================== static constants ==========================
+ // ======================================================================
+ public static int nbreR = 0;
+ /** {@link CDSimulator} */
+ public static final int CDSIM = 0;
+
+ /** {@link EDSimulator} */
+ public static final int EDSIM = 1;
+
+ /** {@link psgsim.PSGSimulator} */
+ public static final int PSGSIM = 2;
+
+ /** Unknown simulator */
+ public static final int UNKNOWN = -1;
+
+ /** the class names of simulators used */
+ protected static final String[] simName = { "peersim.cdsim.CDSimulator",
+ "peersim.edsim.EDSimulator", "psgsim.PSGSimulator" };
+
+ /**
+ * Parameter representing the number of times the experiment is run.
+ * Defaults to 1.
+ *
+ * @config
+ */
+ public static final String PAR_EXPS = "simulation.experiments";
+
+ /**
+ * If present, this parameter activates the redirection of the standard
+ * output to a given PrintStream. This comes useful for processing the
+ * output of the simulation from within the simulator.
+ *
+ * @config
+ */
+ public static final String PAR_REDIRECT = "simulation.stdout";
+
+ // ==================== static fields ===================================
+ // ======================================================================
+
+ /** */
+ private static int simID = UNKNOWN;
+
+ // ========================== methods ===================================
+ // ======================================================================
+
+ /**
+ * Returns the numeric id of the simulator to invoke. At the moment this can
+ * be {@link #CDSIM}, {@link #EDSIM} or {@link #UNKNOWN}.
+ */
+ public static int getSimID() {
+
+ if (simID == UNKNOWN) {
+ if (CDSimulator.isConfigurationCycleDriven()) {
+ simID = CDSIM;
+ } else if (EDSimulator.isConfigurationEventDriven()) {
+ simID = EDSIM;
+ } else
+ simID = PSGSIM;
+
+ }
+ return simID;
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * Loads the configuration and executes the experiments. The number of
+ * independent experiments is given by config parameter {@value #PAR_EXPS}.
+ * In all experiments the configuration is the same, only the random seed is
+ * not re-initialized between experiments.
+ * <p>
+ * Loading the configuration is currently done with the help of constructing
+ * an instance of {@link ParsedProperties} using the constructor
+ * {@link ParsedProperties#ParsedProperties(String[])}. The parameter
+ * <code>args</code> is simply passed to this class. This class is then used
+ * to initialize the configuration.
+ * <p>
+ * After loading the configuration, the experiments are run by invoking the
+ * appropriate engine, which is identified as follows:
+ * <ul>
+ * <li>{@link CDSimulator}: if
+ * {@link CDSimulator#isConfigurationCycleDriven} returns true</li>
+ * <li>{@link EDSimulator}: if
+ * {@link EDSimulator#isConfigurationEventDriven} returns true</li>
+ * </ul>
+ * <p>
+ * This list represents the order in which these alternatives are checked.
+ * That is, if more than one return true, then the first will be taken. Note
+ * that this class checks only for these clues and does not check if the
+ * configuration is consistent or valid.
+ *
+ * @param args
+ * passed on to
+ * {@link ParsedProperties#ParsedProperties(String[])}
+ * @throws InterruptedException
+ * @throws HostNotFoundException
+ * @see ParsedProperties
+ * @see Configuration
+ * @see CDSimulator
+ * @see EDSimulator
+ */
+ public static void main(String[] args) throws InterruptedException,
+ HostNotFoundException {
+ long time = System.currentTimeMillis();
+ long start;
+ System.err.println("Simulator: loading configuration");
+ Configuration.setConfig(new ParsedProperties(args));
+ PrintStream newout = (PrintStream) Configuration.getInstance(
+ PAR_REDIRECT, System.out);
+ if (newout != System.out)
+ System.setOut(newout);
+
+ int exps = Configuration.getInt(PAR_EXPS, 1);
+ final int SIMID = getSimID();
+ if (SIMID == UNKNOWN) {
+ System.err
+ .println("Simulator: unable to determine simulation engine type");
+ return;
+ }
+
+ try {
+
+ for (int k = 0; k < exps; ++k) {
+ if (k > 0) {
+ long seed = CommonState.r.nextLong();
+ CommonState.initializeRandom(seed);
+ }
+ System.err.print("Simulator: starting experiment " + k);
+ System.err.println(" invoking " + simName[SIMID]);
+ System.err.println("Random seed: "
+ + CommonState.r.getLastSeed());
+ // XXX could be done through reflection, but
+ // this is easier to read.
+
+ switch (SIMID) {
+ case CDSIM:
+ CDSimulator.nextExperiment();
+ break;
+ case EDSIM:
+ log("ps");
+ start = System.currentTimeMillis();
+ EDSimulator.nextExperiment();
+ System.err.print("Duration of Simulation in ps:"
+ + (System.currentTimeMillis() - start) + " ms\n");
+ break;
+ case PSGSIM:
+ try {
+ log("psg");
+ start = System.currentTimeMillis();
+ PSGSimulator.main();
+ System.err.print("Duration of Simulation in psg:"
+ + (System.currentTimeMillis() - start)
+ + " ms\n");
+ } catch (NativeException e) {
+ System.err
+ .println("***********Native exception***************");
+ e.printStackTrace();
+ }
+ break;
+ }
+ }
+
+ } catch (MissingParameterException e) {
+ System.err.println(e + "");
+ System.exit(1);
+ } catch (IllegalParameterException e) {
+ System.err.println(e + "");
+ System.exit(1);
+ }
+
+ // undocumented testing capabilities
+ if (Configuration.contains("__t"))
+ System.out.println(System.currentTimeMillis() - time);
+ if (Configuration.contains("__x"))
+ Network.test();
+
+ }
+
+ /**
+ *
+ * @param sim
+ */
+ public static void log(String sim) {
+ String propName = "OutputName";
+
+ /** le nom de l'OS */
+ final String OS_NAME = System.getProperty("os.name");
+ File file = null;
+ String prot = Configuration.getString(propName, "null");
+ if (prot.contentEquals("null")) {
+ System.err.println("OutputName parameter not defined");
+ } else {
+ if ("Linux".equals(OS_NAME) || "Mac".equals(OS_NAME)) {
+ if (!new File("outputs" + prot).exists()) {
+ new File("outputs/" + prot).mkdirs();
+ }
+ String path = "outputs/" + prot + "/";
+ file = new File(path + sim + ".txt");
+ } else {
+ if (!new File("outputs" + prot).exists())
+ new File("outputs\\" + prot).mkdirs();
+ String path = "outputs\\" + prot + "\\";
+ file = new File(path + sim + ".txt");
+ }
+ try {
+ PrintStream printStream = new PrintStream(file);
+ System.setOut(printStream);
+ // System.setErr(printStream);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.cdsim;
+
+import peersim.core.Protocol;
+import peersim.core.Node;
+
+/**
+* Defines cycle driven protocols, that is, protocols that have a periodic
+* activity in regular time intervals.
+*/
+public interface CDProtocol extends Protocol
+{
+
+/**
+ * A protocol which is defined by performing an algorithm in more or less
+ * regular periodic intervals.
+ * This method is called by the simulator engine once in each cycle with
+ * the appropriate parameters.
+ *
+ * @param node
+ * the node on which this component is run
+ * @param protocolID
+ * the id of this protocol in the protocol array
+ */
+public void nextCycle(Node node, int protocolID);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.cdsim;
+
+import java.util.*;
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * This is the cycle driven simulation engine. It is a fully static
+ * singleton class. For a cycle driven simulation the configuration can
+ * describe a set of {@link Protocol}s, and their ordering, a set of
+ * {@link Control}s and their ordering and a set of initializers and their
+ * ordering. See parameters {@value #PAR_INIT}, {@value #PAR_CTRL}. Out
+ * of the set of protocols, this engine only executes the ones that
+ * implement the {@link CDProtocol} interface.
+ * <p>
+ * One experiment run by {@link #nextExperiment} works as follows. First
+ * the initializers are run in the specified order, then the following is
+ * iterated {@value #PAR_CYCLES} times: If {@value #PAR_NOMAIN} is
+ * specified, then simply the controls specified in the configuration are
+ * run in the specified order. If {@value #PAR_NOMAIN} is not specified,
+ * then the controls in the configuration are run in the specified order,
+ * followed by the execution of {@link FullNextCycle}.
+ * <p>
+ * All components (controls and protocols) can have configuration
+ * parameters that control their scheduling (see {@link Scheduler}). This
+ * way they can skip cycles, start from a specified cycle, etc. As a
+ * special case, components can be scheduled to run after the last cycle.
+ * That is, each experiment is finished by running the controls that are
+ * scheduled after the last cycle.
+ * <p>
+ * Finally, any control can interrupt an experiment at any time it is
+ * executed by returning true in method {@link Control#execute}. However,
+ * the controls scheduled to run after the last cycle are still executed
+ * completely, irrespective of their return value and even if the
+ * experiment was interrupted.
+ * @see Configuration
+ */
+public class CDSimulator
+{
+
+// ============== fields ===============================================
+// =====================================================================
+
+/**
+ * Parameter representing the maximum number of cycles to be performed
+ * @config
+ */
+public static final String PAR_CYCLES = "simulation.cycles";
+
+/**
+ * This option is only for experts. It switches off the main cycle that
+ * calls the cycle driven protocols. When you switch this off, you need to
+ * control the execution of the protocols by configuring controls that do
+ * the job (e.g., {@link FullNextCycle}, {@link NextCycle}). It's there for
+ * people who want maximal flexibility for their hacks.
+ * @config
+ */
+private static final String PAR_NOMAIN = "simulation.nodefaultcycle";
+
+/**
+ * This is the prefix for initializers. These have to be of type
+ * {@link Control}. They are run at the beginning of each experiment, in
+ * the order specified by the configuration.
+ * @see Configuration
+ * @config
+ */
+private static final String PAR_INIT = "init";
+
+/**
+ * This is the prefix for controls. These have to be of type
+ * {@link Control}. They are run before each cycle, in the order specified
+ * by the configuration.
+ * @see Configuration
+ * @config
+ */
+private static final String PAR_CTRL = "control";
+
+// --------------------------------------------------------------------
+
+/** The maximum number of cycles to be performed */
+private static int cycles;
+
+/** holds the modifiers of this simulation */
+private static Control[] controls = null;
+
+/** Holds the control schedulers of this simulation */
+private static Scheduler[] ctrlSchedules = null;
+
+// =============== initialization ======================================
+// =====================================================================
+
+/** to prevent construction */
+private CDSimulator()
+{
+}
+
+// =============== private methods =====================================
+// =====================================================================
+
+/**
+ * Load and run initializers.
+ */
+private static void runInitializers()
+{
+
+ Object[] inits = Configuration.getInstanceArray(PAR_INIT);
+ String names[] = Configuration.getNames(PAR_INIT);
+
+ for (int i = 0; i < inits.length; ++i) {
+ System.err.println("- Running initializer " + names[i] + ": "
+ + inits[i].getClass());
+ ((Control) inits[i]).execute();
+ }
+}
+
+// --------------------------------------------------------------------
+
+private static String[] loadControls()
+{
+
+ boolean nomaincycle = Configuration.contains(PAR_NOMAIN);
+ String[] names = Configuration.getNames(PAR_CTRL);
+ if (nomaincycle) {
+ controls = new Control[names.length];
+ ctrlSchedules = new Scheduler[names.length];
+ } else {
+ // provide for an extra control that handles the main cycle
+ controls = new Control[names.length + 1];
+ ctrlSchedules = new Scheduler[names.length + 1];
+ // calling with a prefix that cannot exist
+ controls[names.length] = new FullNextCycle(" ");
+ ctrlSchedules[names.length] = new Scheduler(" ");
+ }
+ for (int i = 0; i < names.length; ++i) {
+ controls[i] = (Control) Configuration.getInstance(names[i]);
+ ctrlSchedules[i] = new Scheduler(names[i]);
+ }
+ System.err.println("CDSimulator: loaded controls " + Arrays.asList(names));
+ return names;
+}
+
+// ---------------------------------------------------------------------
+
+/**
+ * This method is used to check whether the current configuration can be
+ * used for cycle-driven simulations. It checks for the existence of
+ * configuration parameter {@value #PAR_CYCLES}.
+ */
+public static final boolean isConfigurationCycleDriven()
+{
+ return Configuration.contains(PAR_CYCLES);
+}
+
+// ---------------------------------------------------------------------
+
+/**
+ * Runs an experiment, resetting everything except the random seed.
+ */
+public static final void nextExperiment()
+{
+
+ // Reading parameter
+ cycles = Configuration.getInt(PAR_CYCLES);
+ if (CommonState.getEndTime() < 0) // not initialized yet
+ CDState.setEndTime(cycles);
+
+ // initialization
+ CDState.setCycle(0);
+ CDState.setPhase(CDState.PHASE_UNKNOWN);
+ System.err.println("CDSimulator: resetting");
+ controls = null;
+ ctrlSchedules = null;
+ Network.reset();
+ System.err.println("CDSimulator: running initializers");
+ runInitializers();
+
+ // main cycle
+ loadControls();
+
+ System.err.println("CDSimulator: starting simulation");
+ for (int i = 0; i < cycles; ++i) {
+ CDState.setCycle(i);
+
+ boolean stop = false;
+ for (int j = 0; j < controls.length; ++j) {
+ if (ctrlSchedules[j].active(i))
+ stop = stop || controls[j].execute();
+ }
+ if (stop)
+ break;
+ //System.err.println("CDSimulator: cycle " + i + " DONE");
+ }
+
+ CDState.setPhase(CDState.POST_SIMULATION);
+
+ // analysis after the simulation
+ for (int j = 0; j < controls.length; ++j) {
+ if (ctrlSchedules[j].fin)
+ controls[j].execute();
+ }
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.cdsim;
+
+import peersim.core.CommonState;
+
+
+/**
+ * This is the common state of a cycle driven simulation that all objects see.
+ * It contains additional information, specific to the cycle driven model,
+ * in addition to the info in {@link peersim.core.CommonState}.
+ */
+public class CDState extends CommonState {
+
+
+// ======================= fields ==================================
+// =================================================================
+
+/**
+ * Current time within the current cycle.
+ * Note that {@link #cycle} gives the cycle id to which this value is relative.
+ */
+private static int ctime = -1;
+
+/**
+ * Current cycle in the simulation. It makes sense only in the case of a
+ * cycle based simulator, that is, cycle based simulators will maintain this
+ * value, others will not. It still makes sense to keep it separate from
+ * {@link #time} because it is an int, while time is a long.
+ */
+private static int cycle = -1;
+
+
+// ======================== initialization =========================
+// =================================================================
+
+
+static {}
+
+/** to avoid construction */
+private CDState() {}
+
+// ======================= methods =================================
+// =================================================================
+
+
+/**
+* Returns true if and only if there is a cycle driven simulation going on.
+*/
+public static boolean isCD() { return cycle >= 0; }
+
+//-----------------------------------------------------------------
+
+/**
+ * Returns the current cycle.
+ * Note that {@link #getTime()} returns the same value.
+ * @throws UnsupportedOperationException if no cycle-driven state is available
+ */
+public static int getCycle()
+{
+ if( cycle >= 0 ) return cycle;
+ else throw new UnsupportedOperationException(
+ "Cycle driven state accessed when "+
+ "no cycle state information is available.");
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Sets current cycle. Resets also cycle time to 0. It also calls
+ * {@link #setTime(long)} with the given parameter, to make sure
+ * {@link #getTime()} is indeed independent of the simulation model.
+ */
+public static void setCycle(int t)
+{
+ cycle = t;
+ ctime = 0;
+ setTime(t);
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Returns current cycle as an Integer object.
+ * @throws UnsupportedOperationException if no cycle-driven state is available
+ */
+public static Integer getCycleObj()
+{
+ if( cycle >= 0 ) return Integer.valueOf(cycle);
+ else throw new UnsupportedOperationException(
+ "Cycle driven state accessed when "+
+ "no cycle state information is available.");
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Returns the current time within the current cycle.
+ * Note that the time returned by {@link #getCycle} is the cycle id
+ * in this case. In other words, it returns the number of nodes that have
+ * already been visited in a given cycle.
+ * @throws UnsupportedOperationException if no cycle-driven state is available
+ */
+public static int getCycleT()
+{
+ if( ctime >= 0 ) return ctime;
+ else throw new UnsupportedOperationException(
+ "Cycle driven state accessed when "+
+ "no cycle state information is available.");
+}
+
+// -----------------------------------------------------------------
+
+public static void setCycleT(int t)
+{
+ ctime = t;
+}
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.cdsim;
+
+import java.util.Arrays;
+import peersim.config.Configuration;
+import peersim.core.Node;
+import peersim.core.Control;
+
+/**
+* A protocol that is not really a protocol, but a trick to carry out all
+* kinds of tasks during the simulation. Many users will probably not need it,
+* but it is a nice way to e.g. run controls at any time, not only between cycles.
+*/
+public class DaemonProtocol implements CDProtocol {
+
+
+// ========================= fields =================================
+// ==================================================================
+
+
+/**
+* This is the prefix for network dynamism managers.
+* @config
+*/
+private static final String PAR_CTRL = "control";
+
+/**
+* The controls will be run according to this frequency.
+* It is interpreted within a cycle, in terms of cycle time
+* ({@link CDState#getCycleT}). The first cycletime is 0.
+* Defaults to 1.
+* @config
+*/
+private static final String PAR_STEP = "cstep";
+
+// --------------------------------------------------------------------
+
+private static Control[] controls=null;
+
+private static int step;
+
+// ========================= initialization =========================
+// ==================================================================
+
+
+public DaemonProtocol(String s)
+{
+ step = Configuration.getInt(s+"."+PAR_STEP,1);
+
+ String[] names = Configuration.getNames(s+"."+PAR_CTRL);
+ controls = new Control[names.length];
+ for(int i=0; i<names.length; ++i)
+ {
+ controls[i]=(Control)Configuration.getInstance(names[i]);
+ }
+ System.err.println(s+": loaded controls "+Arrays.asList(names));
+}
+
+// ------------------------------------------------------------------
+
+public Object clone() {
+
+ DaemonProtocol ip = null;
+ try { ip=(DaemonProtocol)super.clone(); }
+ catch( CloneNotSupportedException e ) {} // never happens
+ return ip;
+}
+
+
+// ========================= methods =================================
+// ===================================================================
+
+
+/**
+* Runs the configured controls if {@link CDState#getCycleT} %
+* {@value #PAR_STEP}=0.
+*/
+public void nextCycle( Node node, int protocolID ) {
+
+ if( CDState.getCycleT() % step != 0 ) return;
+ for(int j=0; j<controls.length; ++j) controls[j].execute();
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.cdsim;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.util.RandPermutation;
+
+/**
+* Control to run a cycle of the cycle driven simulation.
+* This does not need to be explicitly configured (although you can do it for
+* hacking purposes).
+*/
+public class FullNextCycle implements Control {
+
+
+// ============== fields ===============================================
+// =====================================================================
+
+
+/**
+* The type of the getPair function. This parameter is of historic interest and
+* was needed in a publication we wrote. You don't need to care about this.
+* But if you wanna know: if set to "rand", then in a cycle the simulator
+* does not simply iterate through the nodes, but instead picks a random one
+* N times, where N is the network size.
+* @config
+*/
+private static final String PAR_GETPAIR = "getpair";
+
+/**
+* Shuffle iteration order if set. Not set by default. If set, then nodes are
+* iterated in a random order. However, in the network the nodes actually
+* stay in the order they originally were. The price for leaving the
+* network untouched is memory: we need to store the permutation we use
+* to iterate the network.
+* @config
+*/
+private static final String PAR_SHUFFLE = "shuffle";
+
+// --------------------------------------------------------------------
+
+protected final boolean getpair_rand;
+
+protected final boolean shuffle;
+
+/** Holds the protocol schedulers of this simulation */
+protected Scheduler[] protSchedules = null;
+
+/** The random permutation to use if config par {@value #PAR_SHUFFLE} is set. */
+protected RandPermutation rperm = new RandPermutation( CDState.r );
+
+// =============== initialization ======================================
+// =====================================================================
+
+/**
+* Reads config parameters and {@link Scheduler}s.
+*/
+public FullNextCycle(String prefix) {
+
+ getpair_rand = Configuration.contains(prefix+"."+PAR_GETPAIR);
+ shuffle = Configuration.contains(prefix+"."+PAR_SHUFFLE);
+
+ // load protocol schedulers
+ String[] names = Configuration.getNames(Node.PAR_PROT);
+ protSchedules = new Scheduler[names.length];
+ for(int i=0; i<names.length; ++i)
+ {
+ protSchedules[i] = new Scheduler(names[i]);
+ }
+}
+
+// =============== methods =============================================
+// =====================================================================
+
+/**
+ * Execute all the {@link CDProtocol}s on all nodes that are up.
+ * If the node goes down as a result of the execution of a protocol, then
+ * the rest of the protocols on that node are not executed and we move on
+ * to the next node.
+ * It sets the {@link CDState} appropriately.
+ * @return always false
+ */
+public boolean execute() {
+
+ final int cycle=CDState.getCycle();
+ if( shuffle ) rperm.reset( Network.size() );
+ for(int j=0; j<Network.size(); ++j)
+ {
+ Node node = null;
+ if( getpair_rand )
+ node = Network.get(CDState.r.nextInt(Network.size()));
+ else if( shuffle )
+ node = Network.get(rperm.next());
+ else
+ node = Network.get(j);
+ if( !node.isUp() ) continue;
+ CDState.setNode(node);
+ CDState.setCycleT(j);
+ final int len = node.protocolSize();
+ for(int k=0; k<len; ++k)
+ {
+ // Check if the protocol should be executed, given the
+ // associated scheduler.
+ if (!protSchedules[k].active(cycle))
+ continue;
+
+ CDState.setPid(k);
+ Protocol protocol = node.getProtocol(k);
+ if( protocol instanceof CDProtocol )
+ {
+ ((CDProtocol)protocol).nextCycle(node, k);
+ if( !node.isUp() ) break;
+ }
+ }
+ }
+
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.cdsim;
+
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+* It generalizes its superclass so that the list of protocols to run can
+* be specified. The superclass ({@link FullNextCycle}) always runs all the
+* {@link CDProtocol}s.
+*/
+public class NextCycle extends FullNextCycle {
+
+
+// ============== fields ===============================================
+// =====================================================================
+
+
+/**
+* Gives the list of protocols (whitespace separated) that need to be
+* iterated over.
+* @config
+*/
+private static final String PAR_PROTS = "protocol";
+
+private final int[] pids;
+
+
+// =============== initialization ======================================
+// =====================================================================
+
+/**
+* reads configuration parameters and the {@link Scheduler}s.
+*/
+public NextCycle(String prefix) {
+
+ super(prefix);
+
+ String prots = Configuration.getString(prefix+"."+PAR_PROTS);
+ String[] protnames = prots.split("\\s");
+ pids = new int[protnames.length];
+ for(int i=0; i<protnames.length; ++i)
+ {
+ pids[i] = Configuration.lookupPid(protnames[i]);
+ }
+}
+
+// =============== methods =============================================
+// =====================================================================
+
+/**
+ * Execute the configured protocols on all nodes.
+ * It works exactly as {@link FullNextCycle#execute}, only just the configured
+ * protocols are iterated over.
+ */
+public boolean execute() {
+
+ final int cycle=CDState.getCycle();
+ if( shuffle ) rperm.reset( Network.size() );
+ for(int j=0; j<Network.size(); ++j)
+ {
+ Node node = null;
+ if( getpair_rand )
+ node = Network.get(CDState.r.nextInt(Network.size()));
+ else if( shuffle )
+ node = Network.get(rperm.next());
+ else
+ node = Network.get(j);
+ if( !node.isUp() ) continue;
+ CDState.setNode(node);
+ CDState.setCycleT(j);
+ for(int pid: pids)
+ {
+ // Check if the protocol should be executed, given the
+ // associated scheduler.
+ if (!protSchedules[pid].active(cycle))
+ continue;
+
+ CDState.setPid(pid);
+ Protocol protocol = node.getProtocol(pid);
+ if( protocol instanceof CDProtocol )
+ {
+ ((CDProtocol)protocol).nextCycle(node, pid);
+ if( !node.isUp() ) break;
+ }
+ }
+ }
+
+ return false;
+}
+
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.cdsim;
+
+import peersim.core.*;
+
+/**
+* Shuffles the network. After shuffling, the order in which the nodes
+* are iterated over during a cycle of a cycle driven simulation
+* will be random. It has an effect only in cycle driven simulations.
+*/
+public class Shuffle implements Control {
+
+
+// ========================= fields =================================
+// ==================================================================
+
+// ==================== initialization ==============================
+// ==================================================================
+
+/** Does nothing. */
+public Shuffle(String prefix) {}
+
+
+// ===================== public methods ==============================
+// ===================================================================
+
+
+/**
+* Calls {@link Network#shuffle()}.
+* As a result, the order in which the nodes
+* are iterated over during a cycle of a cycle driven simulation
+* will be random. It has an effect only in cycle driven simulations.
+*/
+public final boolean execute() {
+ Network.shuffle();
+ return false;
+}
+
+
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+import java.util.*;
+
+import peersim.cdsim.*;
+import peersim.edsim.*;
+import peersim.util.*;
+
+
+/**
+ * This is utility tool that checks whether a config file can be loaded
+ * or not, without actually performing the simulation. All the error
+ * messages generated by controls and protocols when initialized are
+ * reported. This is useful to check all the configuration files in a
+ * directory.
+ */
+public class CheckConfig {
+
+//========================== parameters ================================
+//======================================================================
+
+/**
+ * This is the prefix of the config properties whose value vary during
+ * a set of experiments.
+ * @config
+ */
+private static final String PAR_RANGE = "range";
+
+
+// ========================== static constants ==========================
+// ======================================================================
+
+/** {@link CDSimulator} */
+protected static final int CDSIM = 0;
+
+/** {@link EDSimulator} */
+protected static final int EDSIM = 1;
+
+protected static final int UNKNOWN = -1;
+
+/** the class names of simulators used */
+protected static final String[] simName = {
+ CDSimulator.class.getCanonicalName(),
+ EDSimulator.class.getCanonicalName(),
+};
+
+
+
+
+// ========================== methods ===================================
+// ======================================================================
+
+/**
+* Returns the numeric id of the simulator to invoke. At the moment this can
+* be {@link #CDSIM}, {@link #EDSIM} or {@link #UNKNOWN}.
+*/
+protected static int getSimID() {
+
+ if( CDSimulator.isConfigurationCycleDriven())
+ {
+ return CDSIM;
+ }
+ else if( EDSimulator.isConfigurationEventDriven() )
+ {
+ return EDSIM;
+ }
+ else return UNKNOWN;
+}
+
+// ----------------------------------------------------------------------
+
+/**
+* Loads the configuration and checks the configuration files against
+* simple configuration errors, such as missing classes, missing
+* parameters or syntax errors.
+* <p>
+* The goal is to provide a mechanism to test a configuration file,
+* without having to perform the actual simulations (that could be
+* time-consuming) and without necessarily blocking after the first
+* error encountered. It may be useful, for example, when a major
+* refactoring of your code requires a thorough check on all your
+* configuration files.
+* <p>
+* Loading the configuration is currently done with the help of
+* constructing an instance of {@link ParsedProperties} using the
+* constructor {@link ParsedProperties#ParsedProperties(String[])},
+* in the same way as the normal simulator.
+* <p>
+* After loading the configuration, the collection of nodes forming a
+* Network is instantiated, together with all protocols forming a
+* node. Initialization controls are executed, and then the simulation
+* stops.
+* <p>
+* For each error encountered, a message is printed ons standard error,
+* and the initialization keeps going without interruption. If multiple
+* errors are present, an error message for each of them is printed.
+* Apart from errors, default choices are also printed as warnings, to
+* allow developers to spot subtle configuration errors such as missing
+* parameters that defaults to standard values.
+*
+* @param args passed on to
+* {@link ParsedProperties#ParsedProperties(String[])}
+*/
+public static void main(String[] args)
+ throws Exception
+{
+ System.setErr(new NullPrintStream());
+ Properties prop = new ParsedProperties(args);
+ Configuration.setConfig( prop, true );
+ parseRanges(prop);
+
+ final int SIMID = getSimID();
+ if( SIMID == UNKNOWN )
+ {
+ System.err.println(
+ "Simulator: unable to identify configuration, exiting.");
+ return;
+ }
+
+ try {
+
+ // XXX could be done through reflection, but
+ // this is easier to read.
+ switch(SIMID)
+ {
+ case CDSIM:
+ // Set cycles to 0, so no simulation is ever performed.
+ prop.setProperty(CDSimulator.PAR_CYCLES, "0");
+ CDSimulator.nextExperiment();
+ break;
+ case EDSIM:
+ // Set endtime to 0, so no simulation is ever performed.
+ prop.setProperty(EDSimulator.PAR_ENDTIME, "0");
+ EDSimulator.nextExperiment();
+ break;
+ }
+
+ } catch (MissingParameterException e) {
+ System.out.println(e.getMessage());
+ System.exit(1);
+ } catch (IllegalParameterException e) {
+ System.out.println(e.getMessage());
+ System.exit(1);
+ }
+}
+
+/**
+ * Parses a collection of range specifications, identifies the set
+ * of parameters that will change during the simulation and
+ * instantiates them with the first value of their ranges.
+ */
+private static void parseRanges(Properties prop)
+{
+ // Get ranges
+ String[] ranges = Configuration.getNames(PAR_RANGE);
+
+ for (int i = 0; i < ranges.length; i++) {
+ String[] array = Configuration.getString(ranges[i]).split(";");
+ if (array.length != 2) {
+ throw new IllegalParameterException(ranges[i],
+ " should be formatted as <parameter>;<value list>");
+ }
+ String[] values = StringListParser.parseList(array[1]);
+ prop.setProperty(array[0], values[0]);
+ }
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+/**
+ * Provides static methods to obtain the package-qualified class name
+ * of a class, given just the non-qualified name, and to obtain
+ * the non-qualified name, given the package-qualified class name.
+ *
+ * Inspired from some code written by David Postill (david@postill.org.uk)
+ * (found in http://groups.google.com).
+ *
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.9 $
+ */
+class ClassFinder
+{
+
+//--------------------------------------------------------------------------
+//Fields and initialization
+//--------------------------------------------------------------------------
+
+
+/** Local map containing the associations */
+private static Map<String,String> map = new TreeMap<String,String>();
+
+/** The number of directories that have been touched by the search.
+This does not include directories in jar files. */
+private static int visitedDirs = 0;
+
+private static final int maxDirs;
+
+static {
+
+ maxDirs = 10000;
+
+ try {
+ findClasses(map);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if(visitedDirs >= maxDirs )
+ {
+ System.err.println("Configuration: some directories in your "+
+ "classpath probably contain filesystem\nConfiguration: "+
+ "loops because the number of visited directories "+
+ "reached "+maxDirs+".\nConfiguration: This means automatic "+
+ "class lookup might fail and you might have\nConfiguration: "+
+ "to fully qualify class names in the configuration.");
+ }
+}
+
+
+//--------------------------------------------------------------------------
+//Public static methods
+//--------------------------------------------------------------------------
+
+/**
+ * Returns the non-qualified name of a class, removing all the package
+ * information.
+ */
+public static String getShortName(String className) {
+
+ int index = className.lastIndexOf('.');
+ if (index < 0) {
+ return className;
+ } else {
+ return className.substring(index+1);
+ }
+}
+
+/**
+ * Returns the package-qualified name associated to the specified
+ * non-qualified name, if exists. Otherwise it returns null.
+ *
+ * Only classes reachable from the classpath defined by the
+ * "java.class.path" property are considered.
+ * Jar files and directories are both parsed.
+ * If multiple classes with the same name but different
+ * fully-qualified names are present, a comma-separated list
+ * of fully-qualified class names is returned.
+ *
+ * @param name the non-qualified name of the class to be searched
+ * @return the qualified name, if exists.
+ */
+public static String getQualifiedName(String name)
+{
+ return map.get(name);
+}
+
+//--------------------------------------------------------------------------
+//Private static methods
+//--------------------------------------------------------------------------
+
+/**
+ * Finds all the classes reachable from the current classpath;
+ * for each of them, inserts an association (name, fully-qualified
+ * name) in the specified map. Both names are String objects.
+ *
+ * Only classes reachable from the classpath defined by the
+ * "java.class.path" property are considered.
+ * Jar files and directories are both parsed.
+ * If multiple classes with the same name but different
+ * fully-qualified names are present, they are inserted
+ * in the map as associations (name, comma-separated list of
+ * fully-qualified names).
+ *
+ * @param map
+ * @throws IOException
+ */
+private static void findClasses(Map<String,String> map)
+throws IOException
+{
+ String classPath = System.getProperty( "java.class.path" );
+ String separator = System.getProperty( "path.separator" );
+ String filesep = System.getProperty( "file.separator");
+ StringTokenizer path = new StringTokenizer( classPath, separator );
+
+ while( path.hasMoreTokens() ) {
+
+ String pathElement = path.nextToken();
+ File pathFile = new File( pathElement );
+
+ if( pathFile.isDirectory() ) {
+ if (!pathElement.endsWith(filesep)) {
+ pathElement = pathElement + filesep;
+ pathFile = new File( pathElement);
+ }
+ findClassInPathDir( map, pathElement, pathFile );
+ // Search directories
+ } else if ( pathFile.exists() ) {
+ findClassInJar( map, pathFile);
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Parses jar file.
+ *
+ * @param map the map where to insert associations
+ * @param pathFile the file name of the associated jar file
+ * @throws IOException
+ */
+private static void findClassInJar(Map<String,String> map, File pathFile)
+throws IOException
+{
+ ZipFile zipFile = new ZipFile( pathFile );
+ Enumeration entries = zipFile.entries();
+ while( entries.hasMoreElements() ) {
+
+ String entry = entries.nextElement().toString();
+ if( entry.endsWith( ".class" ) ) {
+ // File names in ZIP archives (so, also in JARs)
+ // are separated by forward slashes '/', independently
+ // of the architecture.
+ String className = classname( entry, "/" );
+ String shortName = getShortName( className );
+ if (map.containsKey(shortName)) {
+ map.put(shortName,
+ map.get(shortName)+","+className);
+ } else {
+ map.put(shortName, className);
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Recursively parses directories.
+ *
+ * @param map the map where to insert associations
+ * @param pathElement the path string used for recursion
+ * @param pathFile the file (directory) to be analyzed
+ * @throws IOException
+ */
+private static void findClassInPathDir( Map<String,String> map,
+ String pathElement, File pathFile )
+throws IOException
+{
+ visitedDirs++;
+ if(visitedDirs>=maxDirs) return;
+
+ String[] list = pathFile.list();
+ String filesep = System.getProperty( "file.separator");
+
+ for( int i = 0; i < list.length; i++ ) {
+ File file = new File( pathFile, list[i] );
+ if( file.isDirectory() ) {
+ findClassInPathDir( map, pathElement, file );
+ }
+ else if ( file.exists() && (file.length() != 0) && list[i].endsWith( ".class" ) ) {
+ String classFile = file.toString().substring( pathElement.length());
+ String className = classname( classFile, filesep );
+ String shortName = getShortName( className );
+ if (map.containsKey(shortName)) {
+ map.put(shortName, map.get(shortName)+","+className);
+ } else {
+ map.put(shortName, className);
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Translates a class file name in a class name using
+ * the specified file separator.
+ */
+private static String classname(String classFile, String filesep)
+{
+ return classFile.replace( filesep, "." ).substring(
+ 0, classFile.length() - ".class".length() );
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Testing.
+ *
+ * @param argv
+ */
+public static void main( String[] argv )
+{
+ Iterator i = map.keySet().iterator();
+ while (i.hasNext()) {
+ String key = (String) i.next();
+ String name = map.get(key);
+ System.out.println(key + " --> " + name);
+ }
+}
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+import java.lang.reflect.*;
+import java.util.*;
+import org.lsmp.djep.groupJep.*;
+
+/**
+ * This class is the container for the configuration data used in
+ * {@link Configuration}; see that class for more information.
+ */
+public class ConfigContainer
+{
+
+// =================== static fields =================================
+// ===================================================================
+
+/** Symbolic constant for no debug */
+private static final int DEBUG_NO = 0;
+
+/** Symbolic constant for regular debug */
+private static final int DEBUG_REG = 1;
+
+/** Symbolic constant for extended debug */
+private static final int DEBUG_CONTEXT = 2;
+
+//========================== fields =================================
+//===================================================================
+
+/**
+ * The properties object that stores all configuration information.
+ */
+private Properties config;
+
+/**
+ * Map associating string protocol names to the numeric protocol
+ * identifiers. The protocol names are understood without prefix.
+ */
+private Map<String, Integer> protocols;
+
+/**
+ * The maximum depth that can be reached when analyzing expressions. This
+ * value can be substituted by setting the configuration parameter
+ * PAR_MAXDEPTH.
+ */
+private int maxdepth;
+
+/** Debug level */
+private int debugLevel;
+
+/**
+ * If true, no exception is thrown. Instead, an error is printed and the
+ * Configuration tries to return a reasonable return value
+ */
+private boolean check = false;
+
+// =================== initialization ================================
+// ===================================================================
+
+public ConfigContainer(Properties config, boolean check)
+{
+ this.config = config;
+ this.check = check;
+ maxdepth = getInt(Configuration.PAR_MAXDEPTH, Configuration.DEFAULT_MAXDEPTH);
+
+ // initialize protocol id-s
+ protocols = new HashMap<String, Integer>();
+ String[] prots = getNames(Configuration.PAR_PROT);// they're returned in correct order
+ for (int i = 0; i < prots.length; ++i) {
+ protocols.put(prots[i].substring(Configuration.PAR_PROT.length() + 1), Integer.valueOf(i));
+ }
+ String debug = config.getProperty(Configuration.PAR_DEBUG);
+ if (Configuration.DEBUG_EXTENDED.equals(debug))
+ debugLevel = DEBUG_CONTEXT;
+ else if (Configuration.DEBUG_FULL.equals(debug)) {
+ Map<String, String> map = new TreeMap<String, String>();
+ Enumeration e = config.propertyNames();
+ while (e.hasMoreElements()) {
+ String name = (String) e.nextElement();
+ String value = config.getProperty(name);
+ map.put(name, value);
+ }
+ Iterator i = map.keySet().iterator();
+ while (i.hasNext()) {
+ String name = (String) i.next();
+ System.err.println("DEBUG " + name
+ + ("".equals(map.get(name)) ? "" : " = " + map.get(name)));
+ }
+ } else if (debug != null) {
+ debugLevel = DEBUG_REG;
+ } else {
+ debugLevel = DEBUG_NO;
+ }
+}
+
+// =================== static public methods =========================
+// ===================================================================
+
+/**
+ * @return true if and only if name is a specified (existing) property.
+ */
+public boolean contains(String name)
+{
+ boolean ret = config.containsKey(name);
+ debug(name, "" + ret);
+ return ret;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * {@link MissingParameterException}.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public boolean getBoolean(String name, boolean def)
+{
+ try {
+ return getBool(name);
+ } catch (RuntimeException e) {
+ manageDefault(name, def, e);
+ return def;
+ }
+}
+
+// -------------------------------------------------------------------
+
+
+/**
+ * Reads given property. If not found, or the value is empty string then
+ * throws a {@link MissingParameterException}. Empty string is not
+ * accepted as false due to the similar function of {@link #contains} which
+ * returns true in that case. True is returned if the lowercase value of
+ * the property is "true", otherwise false is returned.
+ * @param name
+ * Name of configuration property
+ */
+public boolean getBoolean(String name)
+{
+ try {
+ return getBool(name);
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return false;
+ }
+}
+
+//-------------------------------------------------------------------
+
+/**
+ * The actual methods that implements getBoolean.
+ */
+private boolean getBool(String name)
+{
+ if (config.getProperty(name) == null) {
+ throw new MissingParameterException(name);
+// "\nPossibly incorrect property: " + getSimilarProperty(name));
+ }
+ if (config.getProperty(name).matches("\\p{Blank}*")) {
+ throw new MissingParameterException(name,
+ "Blank value is not accepted when parsing Boolean.");
+ }
+ boolean ret = Boolean.valueOf(config.getProperty(name));
+ debug(name, "" + ret);
+ return ret;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public int getInt(String name, int def)
+{
+ try {
+ Number ret = getVal(name, name, 0);
+ debug(name, "" + ret);
+ return ret.intValue();
+ } catch (RuntimeException e) {
+ manageDefault(name, def, e);
+ return def;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * {@link MissingParameterException}.
+ * @param name
+ * Name of configuration property
+ */
+public int getInt(String name)
+{
+ try {
+ Number ret = getVal(name, name, 0);
+ debug(name, "" + ret);
+ return ret.intValue();
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return 0;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public long getLong(String name, long def)
+{
+ try {
+ Number ret = getVal(name, name, 0);
+ debug(name, "" + ret);
+ return ret.longValue();
+ } catch (RuntimeException e) {
+ manageDefault(name, def, e);
+ return def;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * {@link MissingParameterException}.
+ * @param name
+ * Name of configuration property
+ */
+public long getLong(String name)
+{
+ try {
+ Number ret = getVal(name, name, 0);
+ debug(name, "" + ret);
+ return ret.longValue();
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return 0;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public double getDouble(String name, double def)
+{
+ try {
+ Number ret = getVal(name, name, 0);
+ debug(name, "" + ret);
+ return ret.doubleValue();
+ } catch (RuntimeException e) {
+ manageDefault(name, def, e);
+ return def;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * MissingParameterException.
+ * @param name
+ * Name of configuration property
+ */
+public double getDouble(String name)
+{
+ try {
+ Number ret = getVal(name, name, 0);
+ debug(name, "" + ret);
+ return ret.doubleValue();
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return 0;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Read numeric property values, parsing expression if necessary.
+ *
+ * @param initial
+ * the property name that started this expression evaluation
+ * @param property
+ * the current property name to be evaluated
+ * @param depth
+ * the depth reached so far
+ * @return the evaluation of the expression associated to property
+ */
+private Number getVal(String initial, String property, int depth)
+{
+ if (depth > maxdepth) {
+ throw new IllegalParameterException(initial,
+ "Probable recursive definition - exceeded maximum depth " +
+ maxdepth);
+ }
+
+ String s = config.getProperty(property);
+ if (s == null || s.equals("")) {
+ throw new MissingParameterException(property,
+ " when evaluating property " + initial);
+// + "\nPossibly incorrect property: " + getSimilarProperty(property));
+ }
+
+ GroupJep jep = new GroupJep(new Operators());
+ jep.setAllowUndeclared(true);
+
+ jep.parseExpression(s);
+ String[] symbols = getSymbols(jep);
+ for (int i = 0; i < symbols.length; i++) {
+ Object d = getVal(initial, symbols[i], depth + 1);
+ jep.addVariable(symbols[i], d);
+ }
+ Object ret = jep.getValueAsObject();
+ if (jep.hasError())
+ System.err.println(jep.getErrorInfo());
+ return (Number) ret;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Returns an array of string, containing the symbols contained in the
+ * expression parsed by the specified JEP parser.
+ * @param jep
+ * the java expression parser containing the list of variables
+ * @return an array of strings.
+ */
+private String[] getSymbols(org.nfunk.jep.JEP jep)
+{
+ Hashtable h = jep.getSymbolTable();
+ String[] ret = new String[h.size()];
+ Enumeration e = h.keys();
+ int i = 0;
+ while (e.hasMoreElements()) {
+ ret[i++] = (String) e.nextElement();
+ }
+ return ret;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public String getString(String name, String def)
+{
+ try {
+ return getStr(name);
+ } catch (RuntimeException e) {
+ manageDefault(name, def, e);
+ return def;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * MissingParameterException. Removes trailing whitespace characters.
+ * @param name
+ * Name of configuration property
+ */
+public String getString(String name)
+{
+ try {
+ return getStr(name);
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return "";
+ }
+}
+
+/**
+ * The actual method implementing getString().
+ */
+private String getStr(String name)
+{
+ String result = config.getProperty(name);
+ if (result == null) {
+ throw new MissingParameterException(name);
+// "\nPossibly incorrect property: " + getSimilarProperty(name));
+ }
+ debug(name, "" + result);
+
+ return result.trim();
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads the given property from the configuration interpreting it as a
+ * protocol name. Returns the numeric protocol identifier of this protocol
+ * name. See the discussion of protocol name at {@link Configuration} for
+ * details on how this numeric id is calculated
+ *
+ * @param name
+ * Name of configuration property
+ * @return the numeric protocol identifier associated to the value of the
+ * property
+ */
+public int getPid(String name)
+{
+ try {
+ String protname = getStr(name);
+ return lookupPid(protname);
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return 0;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Calls {@link #getPid(String)}, and returns the default if no property
+ * is defined with the given name.
+ *
+ * @param name
+ * Name of configuration property
+ * @param pid
+ * the default protocol identifier
+ * @return the numeric protocol identifier associated to the value of the
+ * property, or the default if not defined
+ */
+public int getPid(String name, int pid)
+{
+ try {
+ String protname = getStr(name);
+ return lookupPid(protname);
+ } catch (RuntimeException e) {
+ manageDefault(name, pid, e);
+ return pid;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Returns the numeric protocol identifier of the given protocol name.
+ *
+ * @param protname
+ * the protocol name.
+ * @return the numeric protocol identifier associated to the protocol name
+ */
+public int lookupPid(String protname)
+{
+ Integer ret = protocols.get(protname);
+ if (ret == null) {
+ throw new MissingParameterException(Configuration.PAR_PROT + "." + protname);
+// "\nPossibly incorrect property: "
+// + getSimilarProperty(PAR_PROT + "." + protname));
+ }
+ return ret.intValue();
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Returns the name of a protocol that has the given identifier.
+ * <p>
+ * Note that this is not a constant time operation in the number of
+ * protocols, although typically there are very few protocols defined.
+ *
+ * @param pid
+ * numeric protocol identifier.
+ * @return name of the protocol that has the given id. null if no protocols
+ * have the given id.
+ */
+public String lookupPid(int pid)
+{
+
+ if (!protocols.containsValue(pid))
+ return null;
+ for (Map.Entry<String, Integer> i : protocols.entrySet()) {
+ if (i.getValue().intValue() == pid)
+ return i.getKey();
+ }
+
+ // never reached but java needs it...
+ return null;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * {@link MissingParameterException}. When creating the Class object, a
+ * few attempts are done to resolve the classname. See
+ * {@link Configuration} for details.
+ * @param name
+ * Name of configuration property
+ */
+public Class getClass(String name)
+{
+ try {
+ return getClazz(name);
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return null;
+ }
+}
+
+private Class getClazz(String name)
+{
+ String classname = config.getProperty(name);
+ if (classname == null) {
+ throw new MissingParameterException(name);
+// "\nPossibly incorrect property: " + getSimilarProperty(name));
+ }
+ debug(name, classname);
+
+ Class c = null;
+
+ try {
+ // Maybe classname is just a fully-qualified name
+ c = Class.forName(classname);
+ } catch (ClassNotFoundException e) {
+ }
+ if (c == null) {
+ // Maybe classname is a non-qualified name?
+ String fullname = ClassFinder.getQualifiedName(classname);
+ if (fullname != null) {
+ try {
+ c = Class.forName(fullname);
+ } catch (ClassNotFoundException e) {
+ }
+ }
+ }
+ if (c == null) {
+ // Maybe there are multiple classes with the same
+ // non-qualified name.
+ String fullname = ClassFinder.getQualifiedName(classname);
+ if (fullname != null) {
+ String[] names = fullname.split(",");
+ if (names.length > 1) {
+ for (int i = 0; i < names.length; i++) {
+ for (int j = i + 1; j < names.length; j++) {
+ if (names[i].equals(names[j])) {
+ throw new IllegalParameterException(name,
+ "The class " + names[i]
+ + " appears more than once in the classpath; please check"
+ + " your classpath to avoid duplications.");
+ }
+ }
+ }
+ throw new IllegalParameterException(name,
+ "The non-qualified class name " + classname
+ + "corresponds to multiple fully-qualified classes:" + fullname);
+ }
+ }
+ }
+ if (c == null) {
+ // Last attempt: maybe the fully classified name is wrong,
+ // but the classname is correct.
+ String shortname = ClassFinder.getShortName(classname);
+ String fullname = ClassFinder.getQualifiedName(shortname);
+ if (fullname != null) {
+ throw new IllegalParameterException(name, "Class "
+ + classname + " does not exist. Possible candidate(s): " + fullname);
+ }
+ }
+ if (c == null) {
+ throw new IllegalParameterException(name, "Class "
+ + classname + " not found");
+ }
+ return c;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ * @see #getClass(String)
+ */
+public Class getClass(String name, Class def)
+{
+
+ try {
+ return Configuration.getClass(name);
+ } catch (RuntimeException e) {
+ manageDefault(name, def, e);
+ return def;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property for a class name. It returns an
+ * instance of the class. The class must implement a constructor that takes
+ * a String as an argument. The value of this string will be <tt>name</tt>.
+ * The constructor of the class can see the configuration so it can make
+ * use of this name to read its own parameters from it.
+ * @param name
+ * Name of configuration property
+ * @throws MissingParameterException
+ * if the given property is not defined
+ * @throws IllegalParameterException
+ * if there is any problem creating the instance
+ */
+public Object getInstance(String name)
+{
+ try {
+ return getInst(name);
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return null;
+ }
+}
+
+/**
+ * The actual method implementing getInstance().
+ */
+private Object getInst(String name)
+{
+ Class c = getClass(name);
+ if (c == null)
+ return null;
+ final String classname = c.getSimpleName();
+
+ try {
+ Class pars[] = {String.class};
+ Constructor cons = c.getConstructor(pars);
+ Object objpars[] = {name};
+ return cons.newInstance(objpars);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalParameterException(name, "Class "
+ + classname + " has no " + classname + "(String) constructor");
+ } catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof RuntimeException) {
+ throw (RuntimeException) e.getTargetException();
+ } else {
+ e.getTargetException().printStackTrace();
+ throw new RuntimeException("" + e.getTargetException());
+ }
+ } catch (Exception e) {
+ throw new IllegalParameterException(name, e + "");
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property for a class name. It returns an
+ * instance of the class. The class must implement a constructor that takes
+ * a String as an argument. The value of this string will be <tt>name</tt>.
+ * The constructor of the class can see the configuration so it can make
+ * use of this name to read its own parameters from it.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * The default object that is returned if there is no property
+ * defined with the given name
+ * @throws IllegalParameterException
+ * if the given name is defined but there is a problem creating
+ * the instance.
+ */
+public Object getInstance(String name, Object def)
+{
+ if (!contains(name))
+ return def;
+ try {
+ return getInst(name);
+ } catch (RuntimeException e) {
+ manageException(name, e);
+ return def;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * It returns an array of class instances. The instances are constructed by
+ * calling {@link #getInstance(String)} on the names returned by
+ * {@link #getNames(String)}.
+ * @param name
+ * The component type (i.e. prefix of the list of configuration
+ * properties) which will be passed to {@link #getNames(String)}.
+ */
+public Object[] getInstanceArray(String name)
+{
+
+ String names[] = getNames(name);
+ Object[] result = new Object[names.length];
+
+ for (int i = 0; i < names.length; ++i) {
+ result[i] = getInstance(names[i]);
+ }
+
+ return result;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Returns an array of names prefixed by the specified name. The array is
+ * sorted as follows. If there is no config entry
+ * <code>{@value peersim.config.Configuration#PAR_INCLUDE}+"."+name</code> or
+ * <code>{@value peersim.config.Configuration#PAR_ORDER}+"."+name</code> then the order is
+ * alphabetical. Otherwise this entry defines the order. For more
+ * information see {@link Configuration}.
+ * @param name
+ * the component type (i.e., the prefix)
+ * @return the full property names in the order specified by the
+ * configuration
+ */
+public String[] getNames(String name)
+{
+ ArrayList<String> ll = new ArrayList<String>();
+ final String pref = name + ".";
+
+ Enumeration e = config.propertyNames();
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+ if (key.startsWith(pref) && key.indexOf(".", pref.length()) < 0)
+ ll.add(key);
+ }
+ String[] ret = ll.toArray(new String[ll.size()]);
+ return order(ret, name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * The input of this method is a set of property <code>names</code> (e.g.
+ * initializers, controls and protocols) and a string specifying the type
+ * (prefix) of these. The output is in <code>names</code>, which will
+ * contain a permutation of the original array. Parameter
+ * PAR_INCLUDE+"."+type, or if not present, PAR_ORDER+"."+type is read from
+ * the configuration. If none of them are defined then the order is
+ * identical to that of <code>names</code>. Otherwise the configuration
+ * entry must contain entries from <code>names</code>. It is assumed
+ * that the entries in <code>names</code> contain only word characters
+ * (alphanumeric and underscore '_'. The order configuration entry thus
+ * contains a list of entries from <code>names</code> separated by any
+ * non-word characters.
+ * <p>
+ * It is not required that all entries are listed. If PAR_INCLUDE is used,
+ * then only those entries are returned that are listed. If PAR_ORDER is
+ * used, then all names are returned, but the array will start with those
+ * that are listed. The rest of the names follow in alphabetical order.
+ *
+ *
+ * @param names
+ * the set of property names to be searched
+ * @param type
+ * the string identifying the particular set of properties to be
+ * inspected
+ */
+private String[] order(String[] names, String type)
+{
+ String order = getString(Configuration.PAR_INCLUDE + "." + type, null);
+ boolean include = order != null;
+ if (!include)
+ order = getString(Configuration.PAR_ORDER + "." + type, null);
+
+ int i = 0;
+ if (order != null && !order.equals("")) {
+ // split around non-word characters
+ String[] sret = order.split("\\W+");
+ for (; i < sret.length; i++) {
+ int j = i;
+ for (; j < names.length; ++j)
+ if (names[j].equals(type + "." + sret[i]))
+ break;
+ if (j == names.length) {
+ throw new IllegalParameterException(
+ (include ? Configuration.PAR_INCLUDE : Configuration.PAR_ORDER)
+ + "." + type, type + "." + sret[i] + " is not defined.");
+ } else // swap the element to current position
+ {
+ String tmps = names[j];
+ names[j] = names[i];
+ names[i] = tmps;
+ }
+ }
+ }
+
+ Arrays.sort(names, i, names.length);
+ int retsize = (include ? i : names.length);
+ String[] ret = new String[retsize];
+ for (int j = 0; j < retsize; ++j)
+ ret[j] = names[j];
+ return ret;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Print debug information for configuration. The amount of information
+ * depends on the debug level DEBUG. 0 = nothing 1 = just the config name 2 =
+ * config name plus method calling
+ *
+ * @param name
+ */
+private void debug(String name, String result)
+{
+ if (debugLevel == DEBUG_NO)
+ return;
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("DEBUG ");
+ buffer.append(name);
+ buffer.append(" = ");
+ buffer.append(result);
+
+ // Additional info
+ if (debugLevel == DEBUG_CONTEXT) {
+
+ buffer.append("\n at ");
+ // Obtain the stack trace
+ StackTraceElement[] stack = null;
+ try {
+ throw new Exception();
+ } catch (Exception e) {
+ stack = e.getStackTrace();
+ }
+
+ // Search the element that invoked Configuration
+ // It's the first whose class is different from Configuration
+ int pos;
+ for (pos = 0; pos < stack.length; pos++) {
+ if (!stack[pos].getClassName().equals(Configuration.class.getName()))
+ break;
+ }
+
+ buffer.append(stack[pos].getClassName());
+ buffer.append(":");
+ buffer.append(stack[pos].getLineNumber());
+ buffer.append(", method ");
+ buffer.append(stack[pos - 1].getMethodName());
+ buffer.append("()");
+ }
+
+ System.err.println(buffer);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * @return an array of adjacent letter pairs contained in the input string
+ * http://www.catalysoft.com/articles/StrikeAMatch.html
+ */
+private String[] letterPairs(String str)
+{
+ int numPairs = str.length() - 1;
+ String[] pairs = new String[numPairs];
+ for (int i = 0; i < numPairs; i++) {
+ pairs[i] = str.substring(i, i + 2);
+ }
+ return pairs;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * @return an ArrayList of 2-character Strings.
+ * http://www.catalysoft.com/articles/StrikeAMatch.html
+ */
+private ArrayList<String> wordLetterPairs(String str)
+{
+ ArrayList<String> allPairs = new ArrayList<String>();
+ // Tokenize the string and put the tokens/words into an array
+ String[] words = str.split("\\s");
+ // For each word
+ for (int w = 0; w < words.length; w++) {
+ // Find the pairs of characters
+ String[] pairsInWord = letterPairs(words[w]);
+ for (int p = 0; p < pairsInWord.length; p++) {
+ allPairs.add(pairsInWord[p]);
+ }
+ }
+ return allPairs;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * @return lexical similarity value in the range [0,1]
+ * http://www.catalysoft.com/articles/StrikeAMatch.html
+ */
+private double compareStrings(String str1, String str2)
+{
+ ArrayList pairs1 = wordLetterPairs(str1.toUpperCase());
+ ArrayList pairs2 = wordLetterPairs(str2.toUpperCase());
+ int intersection = 0;
+ int union_ = pairs1.size() + pairs2.size();
+ for (int i = 0; i < pairs1.size(); i++) {
+ Object pair1 = pairs1.get(i);
+ for (int j = 0; j < pairs2.size(); j++) {
+ Object pair2 = pairs2.get(j);
+ if (pair1.equals(pair2)) {
+ intersection++;
+ pairs2.remove(j);
+ break;
+ }
+ }
+ }
+ return (2.0 * intersection) / union_;
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Among the defined properties, returns the one more similar to String
+ * property
+ */
+private String getSimilarProperty(String property)
+{
+ String bestProperty = null;
+ double bestValue = 0.0;
+ Enumeration e = config.keys();
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+ double compare = compareStrings(key, property);
+ if (compare > bestValue) {
+ bestValue = compare;
+ bestProperty = key;
+ }
+ }
+ return bestProperty;
+}
+
+//-------------------------------------------------------------------
+
+private void manageDefault(String name, Object def,
+ RuntimeException e)
+{
+ debug(name, "" + def + " (DEFAULT)");
+ if (check) {
+ System.out.println("Warning: Property " + name + " = " +
+ def + " (DEFAULT)");
+ }
+ if (e instanceof MissingParameterException) {
+ // Do nothing
+ } else {
+ manageException(name, e);
+ }
+}
+
+//-------------------------------------------------------------------
+
+private void manageException(String name, RuntimeException e)
+{
+ if (check) {
+ if (e instanceof MissingParameterException) {
+ // Print just the short message in this case
+ System.out.println("Error: " +
+ ((MissingParameterException) e).getShortMessage());
+ } else if (e instanceof IllegalParameterException) {
+ // Print just the short message in this case
+ System.out.println("Error: " +
+ ((IllegalParameterException) e).getShortMessage());
+ } else {
+ System.out.println("Error: " + e.getMessage());
+ }
+ } else {
+ throw e;
+ }
+}
+
+//-------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+import java.util.Properties;
+import java.io.*;
+
+/**
+* Class for handling configuration files. Extends the functionality
+* of Properties by handling files, system resources and command lines.
+*/
+public class ConfigProperties extends Properties {
+
+
+// =========== Public Constructors ===================================
+// ===================================================================
+
+
+/**
+* Calls super constructor.
+*/
+public ConfigProperties() { super(); }
+
+// -------------------------------------------------------------------
+
+/**
+* Constructs a ConfigProperty object from a parameter list.
+* The algorithm is as follows: first <code>resource</code> is used to attempt
+* loading default values from the given system resource.
+* Then all Strings in <code>pars</code> are processed in the order they
+* appear in the array. For <code>pars[i]</code>, first a property file
+* with the name <code>pars[i]</code> is attempted to be loaded. If the file
+* does not exist or loading produces any other IOException, <code>pars[i]</code>
+* is interpreted as a property definition, and it is set.
+* <p>
+* A little inconvenience is that if <code>pars[i]</code> is supposed to be
+* a command line argument, but it is a valid filename at the same time by
+* accident, the algorithm will process it as a file instead of a command line
+* argument. The caller must take care of that.
+* <p>
+* No exceptions are thrown, instead error messages are written to the
+* standard error. Users who want a finer control should use
+* the public methods of this class.
+*
+* @param pars The (probably command line) parameter list.
+* @param resource The name of the system resource that contains the
+* defaults. null if there isn't any.
+*
+*/
+public ConfigProperties( String[] pars, String resource ) {
+
+ try
+ {
+ if( resource != null )
+ {
+ loadSystemResource(resource);
+ System.err.println("ConfigProperties: System resource "
+ +resource+" loaded.");
+ }
+ }
+ catch( Exception e )
+ {
+ System.err.println("ConfigProperties: " + e );
+ }
+
+ if( pars == null || pars.length == 0 ) return;
+
+ for (int i=0; i < pars.length; i++)
+ {
+ try
+ {
+ load( pars[i] );
+ System.err.println(
+ "ConfigProperties: File "+pars[i]+" loaded.");
+ pars[i] = "";
+ }
+ catch( IOException e )
+ {
+ try
+ {
+ loadPropertyString( pars[i] );
+ System.err.println("ConfigProperties: Property '" +
+ pars[i] + "' set.");
+ }
+ catch( Exception e2 )
+ {
+ System.err.println("ConfigProperties: " + e2 );
+ }
+ }
+ catch( Exception e )
+ {
+ System.err.println("ConfigProperties: " + e );
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Constructs a ConfigProperty object by loading a file by calling
+* {@link #load}.
+* @param fileName The name of the configuration file.
+*/
+public ConfigProperties( String fileName ) throws IOException {
+
+ load( fileName );
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Calls super constructor.
+*/
+public ConfigProperties( Properties props ) {
+
+ super( props );
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Calls {@link #ConfigProperties(String[],String)} with resource set to null.
+*/
+public ConfigProperties( String[] pars ) {
+
+ this( pars, null );
+}
+
+
+// =========== Public methods ========================================
+// ===================================================================
+
+
+/**
+* Loads given file. Calls <code>Properties.load</code> with a file
+* input stream to the given file.
+*/
+public void load( String fileName ) throws IOException {
+
+ FileInputStream fis = new FileInputStream( fileName );
+ load( fis );
+ fis.close();
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Adds the properties from the given property file. Searches in the class path
+* for the file with the given name.
+*/
+public void loadSystemResource( String n ) throws IOException {
+
+ ClassLoader cl = getClass().getClassLoader();
+ load( cl.getResourceAsStream( n ) );
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Appends a property defined in the given string.
+* The string is considered as a property file line.
+* It is converted to a byte array according to the
+* default character encoding and then loaded by the
+* <code>Properties.load</code> method. This means that the ISO-8859-1
+* (or compatible) encoding is assumed.
+*/
+public void loadPropertyString( String prop ) throws IOException {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append( prop ).append( "\n" );
+ load( new ByteArrayInputStream(sb.toString().getBytes()) );
+}
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+import java.util.*;
+
+/**
+ * Fully static class to store configuration information. It defines a
+ * method, {@link #setConfig(Properties)}, to set configuration data. This
+ * method is called by the simulator engines as the very first thing they
+ * do. It can be called only once, after that the class becomes read only.
+ * All components can then access this configuration and utility methods to
+ * read property values based on their names.
+ * <p>
+ * The design of this class also hides the actual implementation of the
+ * configuration which can be Properties, XML, whatever. Currently only
+ * Properties is supported.
+ * <p>
+ * Apart from storing (name,value) pairs, this class also does some
+ * processing, and offers some utility functions. This extended
+ * functionality consists of the following: reading values with type
+ * checking, ordering of entries, pre-processing protocol names, parsing
+ * expressions, resolving underspecified classnames, and finally some basic
+ * debugging possibilities. We discuss these in the following.
+ * <p>
+ * Note that the configuration is initialized using a Properties object.
+ * The class of this object might implement some additional pre-processing
+ * on the file or provide an extended syntax for defining property files.
+ * See {@link ParsedProperties} for more details. This is the class that is
+ * currently used by simulation engines.
+ * <h3>Typed reading of values</h3>
+ * Properties can have arbitrary values of type String. This class offers a
+ * set of read methods that perform the appropriate conversion of the
+ * string value to the given type, eg long. They also allow for specifying
+ * default values in case the given property is not specified.
+ * <h3>Resolving class names</h3>
+ *
+ * The possibilities for the typed reading of a value includes interpreting
+ * the value as a class name. In this case an object will be constructed.
+ * It is described at method {@link #getInstance(String)} how this is
+ * achieved exactly. What needs to be noted here is that the property value
+ * need not be a fully specified classname. It might contain only the short
+ * class name without the package specification. In this case, it is
+ * attempted to locate a class with that name in the classpath, and if a
+ * unique class is found, it will be used. This simplifies the
+ * configuration files and also allows to remove their dependence on the
+ * exact location of the class.
+ *
+ * <h3>Components and their ordering</h3>
+ * The idea of the configuration is that it mostly contains components and
+ * their descriptions (parameters). Although this class is blind to the
+ * semantics of these components, it offers some low level functionality
+ * that helps dealing with them. This functionality is based on the
+ * assumption that components have a type and a name. Both types and names
+ * are strings of alphanumeric and underscore characters. For example,
+ * {@value #PAR_PROT} is a type, "foo" can be a name. Method
+ * {@link #getNames} allow the caller to get the list of names for a given
+ * type. Some other methods, like {@link #getInstanceArray} use this list
+ * to return a list of components.
+ *
+ * <p>
+ * Assuming the configuration is in Properties format (which is currently
+ * the only format available) component types and names are defined as
+ * follows. Property names containing two non-empty words separated by one
+ * dot (".") character are treated specially (the words contain word
+ * characters: alphanumeric and underscore ("_")). The first word will be
+ * the type, and the second is the name of a component. For example,
+ *
+ * <pre>
+ * control.conn ConnectivityObserver
+ * control.1 WireKOut
+ * control.2 PrintGraph
+ * </pre>
+ *
+ * defines control components of names "conn","1" an "2" (arguments of the
+ * components not shown). When {@link #getNames} or
+ * {@link #getInstanceArray} are called, eg
+ * <code>getNames("control")</code>, then the order in which these are
+ * returned is alphabetical:
+ * <code>["control.1","control.2","control.conn"]</code>. If you are not
+ * satisfied with lexicographic order, you can specify the order in this
+ * way.
+ *
+ * <pre>
+ * order.control 1,conn,2
+ * </pre>
+ *
+ * where the names are separated by any non-word character (non
+ * alphanumeric or underscore). If not all names are listed then the given
+ * order is followed by alphabetical order of the rest of the items, e.g.
+ *
+ * <pre>
+ * order.control 2
+ * </pre>
+ *
+ * results in <code>["control.2","control.1","control.conn"]</code>.
+ * <p>
+ * It is also possible to exclude elements from the list, while ordering
+ * them. The syntax is identical to that of the above, only the parameter
+ * name begins with <code>include</code>. For example
+ *
+ * <pre>
+ * include.control conn 2
+ * </pre>
+ *
+ * will result in returning <em>only</em> <code>control.conn</code> and
+ * <code>control.2</code>, in this order. Note that for example the
+ * empty list results in a zero length array in this case.
+ * <em>Important!</em> If include is defined then ordering is ignored.
+ * That is, include is stronger than order.
+ * <h3>Protocol names</h3>
+ * As mentioned, the configuration is generally blind to the actual names
+ * of the components. There is an exception: the components of type
+ * {@value #PAR_PROT}. These are pre-processed a bit to enhance
+ * performance: protocol names are mapped to numeric protocol identifiers.
+ * The numeric identifier of a protocol is its index in the array returned
+ * by {@link #getNames}. See above how to control this order. The numeric
+ * identifiers then can be looked up based on the name and vice versa.
+ * Besides, the identifier can be directly requested based on a property
+ * name when the protocol name is the value of a property which is
+ * frequently the case.
+ * <p>
+ * <h3>Expressions</h3>
+ * Numeric property values can be complex expressions, that are parsed
+ * using <a href="http://www.singularsys.com/jep/">JEP</a>. You can write
+ * expression using the syntax that you can find <a
+ * href="http://www.singularsys.com/jep/doc/html/op_and_func.html"> here</a>.
+ * For example,
+ *
+ * <pre>
+ * MAG 2
+ * SIZE 2ˆMAG
+ * </pre>
+ *
+ * SIZE=4. You can also have complex expression trees like this:
+ *
+ * <pre>
+ * A B+C
+ * B D+E
+ * C E+F
+ * D 1
+ * E F
+ * F 2
+ * </pre>
+ *
+ * that results in A=7, B=3, C=4, D=1, E=2, F=2
+ *
+ * <p>
+ * Expressions like "sub-expression op sub-expression" are computed based
+ * on the type of the sub-expressions. If both sub-expressions are integer,
+ * the computation is done using integer arithmetics and the result is an
+ * integer. So, for example, 5/2 returns 2. If one of the sub-expression is
+ * floating point, the computation is based on floating-point arithmetics
+ * (double precision) and the result is a floating point value. So, for
+ * example, 5.0/2 returns 2.5.
+ *
+ * <p>
+ * Expressions are parsed recursively. Note that no optimization is done,
+ * so expression F is evaluated three times here (due to the fact that
+ * appears twice in C and once in B). But since properties are read just
+ * once at initialization, this is not a performance problem.
+ *
+ * <p>
+ * Finally, recursive definitions are not allowed (and without function
+ * definitions, they make no sense). Since it is difficult to discover
+ * complex recursive chains, a simple trick is used: if the depth of
+ * recursion is greater than a given threshold (configurable, currently
+ * {@value #DEFAULT_MAXDEPTH}, an error message is printed. This avoids to
+ * fill the stack, that results in an anonymous OutOfMemoryError. So, if
+ * you write
+ *
+ * <pre>
+ * overlay.size SIZE
+ * SIZE SIZE-1
+ * </pre>
+ *
+ * you get an error message: Parameter "overlay.size": Probable recursive
+ * definition - exceeded maximum depth {@value #DEFAULT_MAXDEPTH}
+ *
+ * <h3>Debug</h3>
+ *
+ * It is possible to obtain debug information about the configuration
+ * properties by activating special configuration properties.
+ * <p>
+ * If property {@value #PAR_DEBUG} is defined, each config property and the
+ * associated value are printed. Properties that are not present in the
+ * config file but have default values are postfixed with the string
+ * "(DEFAULT)".
+ * <p>
+ * If property {@value #PAR_DEBUG} is defined and it is equal to
+ * {@value #DEBUG_EXTENDED}, information about the configuration method
+ * invoked, and where this method is invoked, is also printed. If it is
+ * equal to {@value #DEBUG_FULL}, all the properties are printed, even if
+ * they are not read.
+ * <p>
+ * Each line printed by this debug feature is prefixed by the string
+ * "DEBUG".
+ *
+ * <h3>Use of brackets</h3>
+ *
+ * For the sake of completeness, we mention it here that if this class is
+ * initialized using {@link ParsedProperties}, then it is possible to use
+ * some more compressed format to specify the components. See
+ * {@link ParsedProperties#load}.
+ *
+ */
+public class Configuration
+{
+
+// =================== static fields =================================
+// ===================================================================
+
+/** Default max depth limit to avoid recursive definitions */
+public static final int DEFAULT_MAXDEPTH = 100;
+
+/**
+ * The debug level for the configuration. If defined, a line is printed for
+ * each configuration parameter read. If defined and equal to
+ * {@value #DEBUG_EXTENDED}, additional context information for debug is
+ * printed. If defined and equal to {@value #DEBUG_FULL}, all the
+ * configuration properties are printed at the beginning, not just when
+ * they are called.
+ * @config
+ */
+static final String PAR_DEBUG = "debug.config";
+
+/**
+ * If parameter {@value #PAR_DEBUG} is equal to this string, additional
+ * context information for debug is printed.
+ */
+static final String DEBUG_EXTENDED = "context";
+
+/**
+ * If parameter {value #PAR_DEBUG} is equal to this string, all the
+ * configuration properties are printed at the beginning, not just when
+ * they are called.
+ */
+static final String DEBUG_FULL = "full";
+
+/**
+ * The maximum depth for expressions. This is a simple mechanism to avoid
+ * unbounded recursion. The default is {@value #DEFAULT_MAXDEPTH}, and you
+ * probably don't want to change it.
+ * @config
+ */
+static final String PAR_MAXDEPTH = "expressions.maxdepth";
+
+/**
+ * Used to configure ordering of the components. Determines the ordering in
+ * the array as returned by {@link #getNames}. See the general description
+ * of {@link Configuration} for details.
+ * @config
+ */
+static final String PAR_ORDER = "order";
+
+/**
+ * Used to configure ordering of the components. Determines the ordering in
+ * the array as returned by {@link #getNames}, and can bu used to also
+ * exclude elements. See the general description of {@link Configuration}
+ * for details.
+ * @config
+ */
+static final String PAR_INCLUDE = "include";
+
+// XXX it's ugly because it replicates the definition of Node.PAR_PROT, but
+// this would be the only dependence on the rest of the core...
+/**
+ * The type name of components describing protocols. This is the only point
+ * at which the class is not blind to the actual semantics of the
+ * configuration.
+ */
+static final String PAR_PROT = "protocol";
+
+/**
+ * The properties object that stores all configuration information.
+ */
+private static ConfigContainer config = null;
+
+// =================== initialization ================================
+// ===================================================================
+
+/** to prevent construction */
+private Configuration()
+{
+}
+
+// =================== static public methods =========================
+// ===================================================================
+
+/**
+ * Sets the system-wide configuration in Properties format. It can be
+ * called only once. After that the configuration becomes unmodifiable
+ * (read only). If modification is attempted, a RuntimeException is thrown
+ * and no change is made.
+ * @param p
+ * The Properties object containing configuration info
+ */
+public static void setConfig(Properties p)
+{
+ if (config != null) {
+ throw new RuntimeException("Setting configuration was attempted twice.");
+ }
+ config = new ConfigContainer(p, false);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Sets the system-wide configuration in Properties format. It can be
+ * called only once. After that the configuration becomes unmodifiable
+ * (read only). If modification is attempted, a RuntimeException is thrown
+ * and no change is made.
+ * @param p
+ * The Properties object containing configuration info
+ */
+public static void setConfig(Properties p, boolean check)
+{
+ if (config != null) {
+ throw new RuntimeException("Setting configuration was attempted twice.");
+ }
+ config = new ConfigContainer(p, check);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * @return true if and only if name is a specified (existing) property.
+ */
+public static boolean contains(String name)
+{
+ return config.contains(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * {@link MissingParameterException}.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public static boolean getBoolean(String name, boolean def)
+{
+ return config.getBoolean(name, def);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given property. If not found, or the value is empty string then
+ * throws a {@link MissingParameterException}. Empty string is not
+ * accepted as false due to the similar function of {@link #contains} which
+ * returns true in that case. True is returned if the lowercase value of
+ * the property is "true", otherwise false is returned.
+ * @param name
+ * Name of configuration property
+ */
+public static boolean getBoolean(String name)
+{
+ return config.getBoolean(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public static int getInt(String name, int def)
+{
+ return config.getInt(name, def);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * {@link MissingParameterException}.
+ * @param name
+ * Name of configuration property
+ */
+public static int getInt(String name)
+{
+ return config.getInt(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public static long getLong(String name, long def)
+{
+ return config.getLong(name, def);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * {@link MissingParameterException}.
+ * @param name
+ * Name of configuration property
+ */
+public static long getLong(String name)
+{
+ return config.getLong(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public static double getDouble(String name, double def)
+{
+ return config.getDouble(name, def);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * MissingParameterException.
+ * @param name
+ * Name of configuration property
+ */
+public static double getDouble(String name)
+{
+ return config.getDouble(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ */
+public static String getString(String name, String def)
+{
+ return config.getString(name, def);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * MissingParameterException. Removes trailing whitespace characters.
+ * @param name
+ * Name of configuration property
+ */
+public static String getString(String name)
+{
+ return config.getString(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads the given property from the configuration interpreting it as a
+ * protocol name. Returns the numeric protocol identifier of this protocol
+ * name. See the discussion of protocol name at {@link Configuration} for
+ * details on how this numeric id is calculated
+ *
+ * @param name
+ * Name of configuration property
+ * @return the numeric protocol identifier associated to the value of the
+ * property
+ */
+public static int getPid(String name)
+{
+ return config.getPid(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Calls {@link #getPid(String)}, and returns the default if no property
+ * is defined with the given name.
+ *
+ * @param name
+ * Name of configuration property
+ * @param pid
+ * the default protocol identifier
+ * @return the numeric protocol identifier associated to the value of the
+ * property, or the default if not defined
+ */
+public static int getPid(String name, int pid)
+{
+ return config.getPid(name, pid);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Returns the numeric protocol identifier of the given protocol name.
+ *
+ * @param protname
+ * the protocol name.
+ * @return the numeric protocol identifier associated to the protocol name
+ */
+public static int lookupPid(String protname)
+{
+ return config.lookupPid(protname);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Returns the name of a protocol that has the given identifier.
+ * <p>
+ * Note that this is not a constant time operation in the number of
+ * protocols, although typically there are very few protocols defined.
+ *
+ * @param pid
+ * numeric protocol identifier.
+ * @return name of the protocol that has the given id. null if no protocols
+ * have the given id.
+ */
+public static String lookupPid(int pid)
+{
+ return config.lookupPid(pid);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, throws a
+ * {@link MissingParameterException}. When creating the Class object, a
+ * few attempts are done to resolve the classname. See
+ * {@link Configuration} for details.
+ * @param name
+ * Name of configuration property
+ */
+public static Class getClass(String name)
+{
+ return config.getClass(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property. If not found, returns the default
+ * value.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * default value
+ * @see #getClass(String)
+ */
+public static Class getClass(String name, Class def)
+{
+ return config.getClass(name, def);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property for a class name. It returns an
+ * instance of the class. The class must implement a constructor that takes
+ * a String as an argument. The value of this string will be <tt>name</tt>.
+ * The constructor of the class can see the configuration so it can make
+ * use of this name to read its own parameters from it.
+ * @param name
+ * Name of configuration property
+ * @throws MissingParameterException
+ * if the given property is not defined
+ * @throws IllegalParameterException
+ * if there is any problem creating the instance
+ */
+public static Object getInstance(String name)
+{
+ return config.getInstance(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Reads given configuration property for a class name. It returns an
+ * instance of the class. The class must implement a constructor that takes
+ * a String as an argument. The value of this string will be <tt>name</tt>.
+ * The constructor of the class can see the configuration so it can make
+ * use of this name to read its own parameters from it.
+ * @param name
+ * Name of configuration property
+ * @param def
+ * The default object that is returned if there is no property
+ * defined with the given name
+ * @throws IllegalParameterException
+ * if the given name is defined but there is a problem creating
+ * the instance.
+ */
+public static Object getInstance(String name, Object def)
+{
+ return config.getInstance(name, def);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * It returns an array of class instances. The instances are constructed by
+ * calling {@link #getInstance(String)} on the names returned by
+ * {@link #getNames(String)}.
+ * @param name
+ * The component type (i.e. prefix of the list of configuration
+ * properties) which will be passed to {@link #getNames(String)}.
+ */
+public static Object[] getInstanceArray(String name)
+{
+ return config.getInstanceArray(name);
+}
+
+// -------------------------------------------------------------------
+
+/**
+ * Returns an array of names prefixed by the specified name. The array is
+ * sorted as follows. If there is no config entry
+ * <code>{@value #PAR_INCLUDE}+"."+name</code> or
+ * <code>{@value #PAR_ORDER}+"."+name</code> then the order is
+ * alphabetical. Otherwise this entry defines the order. For more
+ * information see {@link Configuration}.
+ * @param name
+ * the component type (i.e., the prefix)
+ * @return the full property names in the order specified by the
+ * configuration
+ */
+public static String[] getNames(String name)
+{
+ return config.getNames(name);
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+/**
+ * Reads configuration regarding relations between protocols.
+ *
+ * Technically, this class is not necessary because protocols could
+ * access the configuration directly. However, it provides much faster
+ * access to "linkable" and "transport" information, enhancing runtime speed.
+ *
+ * This class is a static singleton and is initialized only when first accessed.
+ * During initialization it reads and caches the configuration info it handles.
+ */
+public class FastConfig
+{
+
+// ======================= fields ===========================================
+// ===========================================================================
+
+/**
+ * Parameter name in configuration that attaches a linkable protocol to a
+ * protocol. The property can contain multiple protocol names, in one line,
+ * separated by non-word characters (e.g. whitespace or ",").
+ * @config
+ */
+private static final String PAR_LINKABLE = "linkable";
+
+/**
+ * Parameter name in configuration that attaches a transport layer protocol to a
+ * protocol.
+ * @config
+ */
+private static final String PAR_TRANSPORT = "transport";
+
+/**
+ * This array stores the protocol ids of the {@link peersim.core.Linkable}
+ * protocols that are linked to the protocol given by the array index.
+ */
+protected static final int[][] links;
+
+/**
+ * This array stores the protocol id of the {@link peersim.transport.Transport}
+ * protocol that is linked to the protocol given by the array index.
+ */
+protected static final int[] transports;
+
+
+// ======================= initialization ===================================
+// ==========================================================================
+
+
+/**
+ * This static initialization block reads the configuration for information that
+ * it understands. Currently it understands property {@value #PAR_LINKABLE}
+ * and {@value #PAR_TRANSPORT}.
+ *
+ * Protocols' linkable and transport definitions are prefetched
+ * and stored in arrays, to enable fast access during simulation.
+ *
+ * Note that this class does not perform any type checks. The purpose of the
+ * class is purely to speed up access to linkable and transport information,
+ * by providing a fast alternative to reading directly from the
+ * <code>Configuration</code> class.
+ */
+static {
+ String[] names = Configuration.getNames(Configuration.PAR_PROT);
+ links = new int[names.length][];
+ transports = new int[names.length];
+ for (int i = 0; i < names.length; ++i)
+ {
+ if (Configuration.contains(names[i] + "." + PAR_LINKABLE))
+ {
+ // get string of linkables
+ String str = Configuration.getString(names[i] + "." + PAR_LINKABLE);
+ // split around non-word characters
+ String[] linkNames = str.split("\\W+");
+ links[i] = new int[linkNames.length];
+ for (int j=0; j<linkNames.length; ++j)
+ links[i][j] = Configuration.lookupPid(linkNames[j]);
+ }
+ else
+ links[i] = new int[0]; // empty set
+
+ if (Configuration.contains(names[i] + "." + PAR_TRANSPORT))
+ transports[i] =
+ Configuration.getPid(names[i] + "." + PAR_TRANSPORT);
+ else
+ transports[i] = -1;
+ }
+}
+
+// ---------------------------------------------------------------------
+
+/** to prevent construction */
+private FastConfig() {}
+
+// ======================= methods ==========================================
+// ==========================================================================
+
+
+/**
+ * Returns true if the given protocol has at least one linkable protocol
+ * associated with it, otherwise false.
+ */
+public static boolean hasLinkable(int pid) { return numLinkables(pid) > 0; }
+
+// ---------------------------------------------------------------------
+
+/**
+ * Returns the number of linkable protocols associated with a given protocol.
+ */
+public static int numLinkables(int pid) { return links[pid].length; }
+
+// ---------------------------------------------------------------------
+
+/**
+ * Returns the protocol id of the <code>linkIndex</code>-th linkable used by
+ * the protocol identified by pid. Throws an
+ * IllegalParameterException if there is no linkable associated with the given
+ * protocol: we assume here that this happens when the configuration is
+ * incorrect.
+ */
+public static int getLinkable(int pid, int linkIndex)
+{
+ if (linkIndex >= numLinkables(pid)) {
+ String[] names = Configuration.getNames(Configuration.PAR_PROT);
+ throw new IllegalParameterException(names[pid],
+ "Protocol " + pid + " has no "+PAR_LINKABLE+
+ " parameter with index" + linkIndex);
+ }
+ return links[pid][linkIndex];
+}
+
+//---------------------------------------------------------------------
+
+/**
+ * Invokes <code>getLinkable(pid, 0)</code>.
+ */
+public static int getLinkable(int pid)
+{
+ return getLinkable(pid, 0);
+}
+
+// ---------------------------------------------------------------------
+
+/**
+ * Returns true if the given protocol has a transport protocol associated with
+ * it, otherwise false.
+ */
+public static boolean hasTransport(int pid)
+{
+ return transports[pid] >= 0;
+}
+
+// ---------------------------------------------------------------------
+
+/**
+ * Returns the id of the transport protocol used by the protocol identified
+ * by pid.
+ * Throws an IllegalParameterException if there is no transport associated
+ * with the given protocol: we assume here that his happens when the
+ * configuration is incorrect.
+ */
+public static int getTransport(int pid)
+{
+ if (transports[pid] < 0) {
+ String[] names = Configuration.getNames(Configuration.PAR_PROT);
+ throw new IllegalParameterException(names[pid],
+ "Protocol " + pid + " has no "+PAR_TRANSPORT + " parameter");
+ }
+ return transports[pid];
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+/**
+* Exception thrown to indicate that a
+* configuration property has an invalid value. It is thrown by
+* several methods in {@link Configuration} and can be thrown by any
+* component that reads the configuration.
+*/
+public class IllegalParameterException extends RuntimeException {
+
+// ================== initialization =====================================
+// =======================================================================
+
+/**
+* Calls super constructor. It passes a string message which is the given
+* message, prefixed with the given property name.
+* @param name Name of configuration property that is invalid
+* @param message Additional info about why the value is invalid
+*/
+public IllegalParameterException(String name, String message) {
+
+ super("Parameter \"" + name + "\": " + message);
+}
+
+// ================== methods ============================================
+// =======================================================================
+
+/**
+* Extends message with info from stack trace.
+* It tries to guess what class called {@link Configuration} and
+* adds relevant info from the stack trace about it to the message.
+*/
+public String getMessage() {
+
+ StackTraceElement[] stack = getStackTrace();
+
+ // Search the element that invoked Configuration
+ // It's the first whose class is different from Configuration
+ int pos;
+ for (pos=0; pos < stack.length; pos++)
+ {
+ if (!stack[pos].getClassName().equals(
+ Configuration.class.getName()))
+ break;
+ }
+
+ return super.getMessage()+"\nAt "+
+ getStackTrace()[pos].getClassName()+"."+
+ getStackTrace()[pos].getMethodName()+":"+
+ getStackTrace()[pos].getLineNumber();
+}
+
+/**
+ * Returns the exception message without stack trace information
+ */
+public String getShortMessage()
+{
+ return super.getMessage();
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+/**
+* Exception thrown to indicate that a
+* configuration property is not defined. It is thrown exclusively by
+* {@link Configuration}, since it is the only class that has access to the
+* set of defined properties.
+ */
+public class MissingParameterException extends RuntimeException {
+
+// ================== initialization =====================================
+// =======================================================================
+
+MissingParameterException(String name) {
+
+ super("Parameter \"" + name + "\" not found.");
+}
+
+MissingParameterException(String name, String motivation) {
+
+ super("Parameter \"" + name + "\" not found " + motivation);
+}
+
+// ================== methods ============================================
+// =======================================================================
+
+/**
+* Extends message with info from stack trace.
+* It tries to guess what class called {@link Configuration} and
+* adds relevant info from the stack trace about it to the message.
+*/
+public String getMessage() {
+
+ StackTraceElement[] stack = getStackTrace();
+
+ // Search the element that invoked Configuration
+ // It's the first whose class is different from Configuration
+ int pos;
+ for (pos=0; pos < stack.length; pos++) {
+ if (!stack[pos].getClassName().equals(
+ Configuration.class.getName()))
+ break;
+ }
+
+ return super.getMessage()+"\nAt "+
+ getStackTrace()[pos].getClassName()+"."+
+ getStackTrace()[pos].getMethodName()+":"+
+ getStackTrace()[pos].getLineNumber();
+}
+
+/**
+ * Returns the exception message without stack trace information
+ */
+public String getShortMessage()
+{
+ return super.getMessage();
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+import java.io.*;
+
+/**
+ * A subclass of PrintStream whose methods ignore the content
+ * being written.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.1 $
+ */
+public class NullPrintStream extends PrintStream
+{
+
+/**
+ * Creates a null print stream that does not print anything.
+ */
+public NullPrintStream()
+{
+ super(System.out);
+}
+
+/**
+ * This methods does not print anything.
+ */
+public synchronized void write(byte[] b, int off, int len)
+{
+}
+
+/**
+ * This methods does not print anything.
+ */
+public synchronized void write(int b)
+{
+}
+
+/**
+ * This methods does not print anything.
+ */
+private void printLine()
+{
+}
+
+}
--- /dev/null
+/*\r
+ * Copyright (c) 2003-2005 The BISON Project\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2 as\r
+ * published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ *\r
+ */\r
+package peersim.config;\r
+import java.math.*;\r
+import org.lsmp.djep.groupJep.groups.*;\r
+import org.lsmp.djep.groupJep.interfaces.*;\r
+\r
+/**\r
+ * This class implements the <code>Group</code> interface of JEP,\r
+ * enabling the configuration system to read integers with arbitrary \r
+ * length. \r
+ */\r
+public class Operators extends Group implements IntegralDomainI,HasDivI,\r
+ OrderedSetI,HasModI,HasPowerI {\r
+ \r
+\r
+ /**\r
+ * Operations on the reals (Implemented as BigInteger).\r
+ */\r
+ public Operators() {\r
+ }\r
+\r
+ public Number getZERO() {\r
+ return BigInteger.ZERO;\r
+ }\r
+\r
+ public Number getONE() {\r
+ return BigInteger.ONE;\r
+ }\r
+\r
+ public Number getInverse(Number num) {\r
+ if (num instanceof BigInteger) {\r
+ BigInteger a = (BigInteger) num;\r
+ return a.negate();\r
+ } else {\r
+ return -num.doubleValue();\r
+ }\r
+ }\r
+\r
+ public Number add(Number num1, Number num2) {\r
+ if (num1 instanceof Double || num2 instanceof Double) {\r
+ // One is double\r
+ return num1.doubleValue() + num2.doubleValue();\r
+ } else {\r
+ // Both integer\r
+ BigInteger a = (BigInteger) num1;\r
+ BigInteger b = (BigInteger) num2;\r
+ return a.add(b);\r
+ }\r
+ }\r
+\r
+ public Number sub(Number num1, Number num2) {\r
+ if (num1 instanceof Double || num2 instanceof Double) {\r
+ // One is double\r
+ return num1.doubleValue() - num2.doubleValue();\r
+ } else {\r
+ // Both integer\r
+ BigInteger a = (BigInteger) num1;\r
+ BigInteger b = (BigInteger) num2;\r
+ return a.subtract(b);\r
+ }\r
+ }\r
+\r
+ public Number mul(Number num1, Number num2) {\r
+ if (num1 instanceof Double || num2 instanceof Double) {\r
+ // One is double\r
+ return num1.doubleValue() * num2.doubleValue();\r
+ } else {\r
+ // Both integer\r
+ BigInteger a = (BigInteger) num1;\r
+ BigInteger b = (BigInteger) num2;\r
+ return a.multiply(b);\r
+ }\r
+ }\r
+\r
+ public Number div(Number num1, Number num2) {\r
+ if (num1 instanceof Double || num2 instanceof Double) {\r
+ // One is double\r
+ return num1.doubleValue() / num2.doubleValue();\r
+ } else {\r
+ // Both integer\r
+ BigInteger a = (BigInteger) num1;\r
+ BigInteger b = (BigInteger) num2;\r
+ return a.divide(b);\r
+ }\r
+ }\r
+\r
+ \r
+ public Number mod(Number num1, Number num2) {\r
+ if (num1 instanceof Double || num2 instanceof Double) {\r
+ // One is double\r
+ return num1.doubleValue() % num2.doubleValue();\r
+ } else {\r
+ // Both integer\r
+ BigInteger a = (BigInteger) num1;\r
+ BigInteger b = (BigInteger) num2;\r
+ return a.remainder(b);\r
+ }\r
+ }\r
+ \r
+ public Number pow(Number num1, Number num2) {\r
+ if (num1 instanceof Double || num2 instanceof Double) {\r
+ // One is double\r
+ return Math.pow(num1.doubleValue(), num2.doubleValue());\r
+ } else {\r
+ // Both integer\r
+ BigInteger a = (BigInteger) num1;\r
+ BigInteger b = (BigInteger) num2;\r
+ return a.pow(b.intValue());\r
+ }\r
+ }\r
+ \r
+ public boolean equals(Number num1, Number num2) {\r
+ if (num1 instanceof Double || num2 instanceof Double) {\r
+ // One is double\r
+ return num1.doubleValue() == num2.doubleValue();\r
+ } else {\r
+ // Both integer\r
+ BigInteger a = (BigInteger) num1;\r
+ BigInteger b = (BigInteger) num2;\r
+ return a.equals(b);\r
+ }\r
+ }\r
+ \r
+ public int compare(Number num1,Number num2) {\r
+ if (num1 instanceof Double || num2 instanceof Double) {\r
+ // One is double\r
+ double n1 = num1.doubleValue();\r
+ double n2 = num2.doubleValue();\r
+ return (n1 < n2 ? -1 : (n1 == n2 ? 0 : 1));\r
+ } else {\r
+ // Both integer\r
+ BigInteger a = (BigInteger) num1;\r
+ BigInteger b = (BigInteger) num2;\r
+ return a.compareTo(b);\r
+ }\r
+ }\r
+\r
+ public Number valueOf(String str) {\r
+ try {\r
+ return new BigInteger(str);\r
+ } catch (NumberFormatException e) {\r
+ return new Double(str);\r
+ }\r
+ }\r
+ \r
+ public String toString() { return ""; }\r
+}\r
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.config;
+
+import java.io.*;
+import java.util.*;
+
+/**
+* Extends the class {@link ConfigProperties} with basic parsing capabilities.
+* @see #load
+*/
+public class ParsedProperties extends ConfigProperties {
+
+//================= variables ======================================
+//==================================================================
+
+// ================= initialization =================================
+// ==================================================================
+
+/**
+* Calls super constructor.
+* @see ConfigProperties#ConfigProperties(String[])
+*/
+public ParsedProperties( String[] pars ) {
+
+ super( pars );
+}
+
+// ------------------------------------------------------------------
+
+/**
+* Calls super constructor.
+* @see ConfigProperties#ConfigProperties(String)
+*/
+public ParsedProperties( String filename ) throws IOException {
+
+ super( filename );
+}
+
+
+// =========== Public methods ========================================
+// ===================================================================
+
+
+/**
+* Loads given file. It works exactly as <code>Properties.load</code>
+* with a file input stream to the given file, except that the file is parsed
+* the following way allowing to compress some property names
+* using <code>{</code> and <code>}</code>.
+
+ When a bracket is present, it must
+ be the only non-space element of a line. The last property defined
+ before the opening bracket define the prefix that is added to all the
+ properties defined included between brackets.
+ In other words, a construct like this:
+ <pre>
+ control.degree GraphObserver
+ {
+ protocol newscast
+ undir
+ }
+ </pre>
+ is equivalent to the definition of these three properties:
+ <pre>
+ control.degree GraphObserver
+ control.degree.protocol newscast
+ control.degree.undir
+ </pre>
+
+ Nested brackets are possible. The rule of the last property before
+ the opening bracket applies also to the inside brackets, with
+ the prefix being the complete property definition (so, including
+ the prefix observed before). Example:
+ <pre>
+ control.1 DynamicNetwork
+ {
+ add CRASH
+ substitute
+ init.0 WireKOut
+ {
+ degree DEGREE
+ protocol 0
+ }
+ }
+ </pre>
+ defines the following properties:
+ <pre>
+ control.1 DynamicNetwork
+ control.1.add CRASH
+ control.1.substitute
+ control.1.init.0 WireKOut
+ control.1.init.0.degree DEGREE
+ control.1.init.0.protocol 0
+ </pre>
+
+ <p>
+ Know limitations:
+ The parsing mechanism is very simple; no complex error messages
+ are provided. In case of missing closing brackets, the method
+ will stop reporting the number of missing brackets. Additional
+ closing brackets (i.e., missing opening brackets) produce an
+ error messages reporting the line where the closing bracket
+ is present. Misplaced brackets (included in lines that
+ contains other characters) are ignored, thus may indirectly produce
+ the previous error messages.
+*/
+public void load( String fileName ) throws IOException {
+
+ /* This set is used to store prefixes that have been associated
+ * to brackets blocks. If a prefix is inserted twice, this means
+ * that there are two blocks referring to the same prefix -
+ * which may be caused by a commented prefix in the config
+ * file, something like this:
+ *
+ * prefix1
+ * {
+ * property 1
+ * }
+ * #prefix2
+ * {
+ * property 2
+ * }
+ *
+ */
+ Set<String> prefixes = new HashSet<String>();
+
+ BufferedReader f =
+ new BufferedReader(new FileReader( fileName ));
+ int lines = 0;
+ parseStream(f, "", 0, lines, prefixes);
+
+ f.close();
+}
+
+// --------------------------------------------------------------------
+
+private int parseStream(BufferedReader f, String prefix, int pars,
+ int lines, Set prefixes)
+throws IOException {
+
+ if (prefix.equals(".")) {
+ System.err.println("Error at line " + lines + ": bracket block not " +
+ "associated with any configuration entry");
+ System.exit(1);
+ }
+ if (prefixes.contains(prefix)) {
+ System.err.println("Error at line " + lines + ": multiple bracket " +
+ "blocks referring to the same configuration entry " + prefix);
+ System.exit(1);
+ } else {
+ prefixes.add(prefix);
+ }
+
+ boolean complete = true;
+ String part;
+ String line = "";
+ String last = "";
+ while ((part = f.readLine()) != null)
+ {
+ lines++;
+
+ // Reset line
+ if (complete) line = "";
+
+ // Check if the line is a comment line
+ // If so, remove the comment
+ int index = part.indexOf('#');
+ if (index >= 0)
+ {
+ part = part.substring(0, index);
+ }
+
+ // Check if the line is empty
+ part = part.trim();
+ if ("".equals(part)) continue;
+
+ complete = (part.charAt(part.length()-1) != '\\');
+ if (!complete)
+ {
+ line = line + part.substring(0, part.length()-2) + " ";
+ continue;
+ }
+
+ // Complete the line
+ line = line + part;
+ if (line.equals("{"))
+ {
+ lines = parseStream(f, last+".", pars+1, lines, prefixes);
+ }
+ else if (line.equals("}"))
+ {
+ if (pars == 0)
+ {
+ System.err.println(
+ "Error: Additional } at line " + lines +
+ " when parsing the configuration file");
+ System.exit(1);
+ }
+ return lines;
+ }
+ else
+ {
+ // Search the first token
+ String[] tokens = line.split("[\\s:=]+", 2);
+ if (tokens.length == 1)
+ {
+ setProperty(prefix+tokens[0], "");
+ }
+ else
+ {
+ setProperty(prefix+tokens[0], tokens[1]);
+ }
+ last = prefix + tokens[0];
+ }
+ }
+ if (pars == 1)
+ {
+ System.err.println("Error: One closing bracket ('}') is missing");
+ System.exit(1);
+ }
+ else if (pars > 1)
+ {
+ System.err.println("Error: " + pars+" closing brackets ('}') are missing");
+ System.exit(1);
+ }
+ return lines;
+}
+
+// --------------------------------------------------------------------
+
+/*
+public static void main(String[] args)
+{
+ java.util.Properties prop = new ParsedProperties(args);
+}
+*/
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+/**
+ * This interface must be implemented by protocols that need to release
+ * some references when the fail state of their host node is set to
+ * {@link Fallible#DEAD}. Recall that this fail state means that the node
+ * will never get back online. However, other nodes and protocols might
+ * still have references to these dead nodes and protocols, and this fact
+ * is not always a bug. So implementing this interface allows for removing
+ * stuff that we know is no longer necessary. Especially for linkable
+ * protocols in the presence of churn, this is essential: they typically
+ * have to release their links to other nodes to prevent the formation of
+ * trees of removed nodes with a live reference to the root.
+ */
+public interface Cleanable
+{
+
+/**
+ * Performs cleanup when removed from the network. This is called by the
+ * host node when its fail state is set to {@link Fallible#DEAD}.
+ * It is very important that after calling this method, NONE of the methods
+ * of the implementing object are guaranteed to work any longer.
+ * They might throw arbitrary exceptions, etc. The idea is that after
+ * calling this, typically no one should access this object.
+ * However, as a recommendation, at least toString should be guaranteed to
+ * execute normally, to aid debugging.
+ */
+public void onKill();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+import org.simgrid.msg.Msg;
+
+import peersim.Simulator;
+import peersim.config.*;
+import peersim.util.*;
+import psgsim.PSGPlatform;
+
+/**
+ * This is the common state of the simulation all objects see.
+ * Static singleton. One of its purposes is
+ * simplification of parameter structures and increasing efficiency by putting
+ * state information here instead of passing parameters.
+ *<p>
+ * <em>The set methods should not be used by applications</em>,
+ * they are for system
+ * components. Use them only if you know exactly what you are doing, e.g.
+ * if you are so advanced that you can write your own simulation engine.
+ * Ideally, they should not be visible, but due to the lack of more
+ * flexibility in java access rights, we are forced to make them public.
+ */
+public class CommonState
+{
+
+//======================= constants ===============================
+//=================================================================
+
+/**
+* Constant that can be used as a value of simulation phase.
+* It means that the simulation has finished.
+* @see #getPhase
+*/
+public static final int POST_SIMULATION = 1;
+
+/**
+* Constant that can be used as a value of simulation phase.
+* It means that the simulation phase information has not been set (unknown).
+* @see #getPhase
+*/
+public static final int PHASE_UNKNOWN = 0;
+
+// ======================= fields ==================================
+// =================================================================
+
+/**
+ * Current time. Note that this value is simulator independent, all simulation
+ * models have a notion related to time. For example, in the cycle based model,
+ * the cycle id gives time, while in even driven simulations there is a more
+ * realistic notion of time.
+ */
+private static long time = 0;
+
+/**
+ * The maximal value {@link #time} can ever take.
+ */
+private static long endtime = -1;
+
+/**
+ * Number of used bits in the long representation of time, calculated
+ * based on the endtime.
+ */
+private static int toshift = -1;
+
+/**
+ * Information about where exactly the simulation is.
+ */
+private static int phase = PHASE_UNKNOWN;
+
+/**
+ * The current pid.
+ */
+private static int pid;
+
+/**
+ * The current node.
+ */
+private static Node node;
+
+/**
+* This source of randomness should be used by all components.
+* This field is public because it doesn't matter if it changes
+* during an experiment (although it shouldn't) until no other sources of
+* randomness are used within the system. Besides, we can save the cost
+* of calling a wrapper method, which is important because this is needed
+* very often.
+*/
+public static ExtendedRandom r = null;
+
+
+// ======================== initialization =========================
+// =================================================================
+
+/**
+* Configuration parameter used to define which random generator
+* class should be used. If not specified, the default implementation
+* {@link ExtendedRandom} is used. User-specified random generators
+* must extend class {@link ExtendedRandom}.
+* @config
+*/
+public static final String PAR_RANDOM = "random";
+
+/**
+* Configuration parameter used to initialize the random seed.
+* If it is not specified the current time is used.
+* @config
+*/
+public static final String PAR_SEED = "random.seed";
+
+
+/**
+* Initializes the field {@link r} according to the configuration.
+* Assumes that the configuration is already
+* loaded.
+*/
+static {
+
+ long seed =
+ Configuration.getLong(PAR_SEED,System.currentTimeMillis());
+ initializeRandom(seed);
+}
+
+
+/** Does nothing. To avoid construction but allow extension. */
+protected CommonState() {}
+
+// ======================= methods =================================
+// =================================================================
+
+
+/**
+ * Returns current time. In event-driven simulations, returns the current
+ * time (a long-value).
+ * In cycle-driven simulations, returns the current cycle (a long that
+ * can safely be cast into an integer).
+ */
+public static long getTime()
+{
+ /* if the engine simulator used is PSG (simId=2 */
+ if(Simulator.getSimID()==2)
+ return (long) PSGPlatform.getTime();
+ else
+ return time;
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Returns current time in integer format. The purpose is to enhance the
+ * performance of protocols (ints are smaller and faster) when absolute
+ * precision is not required. It assumes that endtime has been set via
+ * {@link #setEndTime} by the simulation engine. It uses the endtime for
+ * the optimal mapping to get the maximal precision.
+ * In particular, in the cycle
+ * based model, time is the same as cycle which can be safely cast into
+ * integer, so no precision is lost.
+ */
+public static int getIntTime()
+{
+ return (int)(time>>toshift);
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Sets the current time.
+ */
+public static void setTime(long t)
+{
+ time = t;
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Returns endtime.
+ * It is the maximal value {@link #getTime} ever returns. If it's negative, it
+ * means the endtime is not known.
+ */
+public static long getEndTime()
+{
+ return endtime;
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Sets the endtime.
+ */
+public static void setEndTime(long t)
+{
+ if( endtime >= 0 )
+ throw new RuntimeException("You can set endtime only once");
+ if( t < 0 )
+ throw new RuntimeException("No negative values are allowed");
+
+ endtime = t;
+ toshift = 32-Long.numberOfLeadingZeros(t);
+ if( toshift<0 ) toshift = 0;
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Returns the simulation phase. Currently the following phases are
+ * understood.
+ * <ul>
+ * <li>{@link #PHASE_UNKNOWN} phase is unknown</li>
+ * <li>{@link #POST_SIMULATION} the simulation is completed</li>
+ * </ul>
+ */
+public static int getPhase()
+{
+ return phase;
+}
+
+// -----------------------------------------------------------------
+
+public static void setPhase(int p)
+{
+ phase = p;
+}
+
+// -----------------------------------------------------------------
+
+/**
+* Returns the current protocol identifier. In other words, control is
+* held by the indicated protocol on node {@link #getNode}.
+*/
+public static int getPid()
+{
+ return pid;
+}
+
+//-----------------------------------------------------------------
+
+/** Sets the current protocol identifier.*/
+public static void setPid(int p)
+{
+ pid = p;
+}
+
+//-----------------------------------------------------------------
+
+/**
+ * Returns the current node. When a protocol is executing, it is the node
+ * hosting the protocol.
+ */
+public static Node getNode()
+{
+ return node;
+}
+
+//-----------------------------------------------------------------
+
+/** Sets the current node */
+public static void setNode(Node n)
+{
+ node = n;
+}
+
+//-----------------------------------------------------------------
+
+public static void initializeRandom(long seed)
+{
+ if (r == null) {
+ r = (ExtendedRandom) Configuration.getInstance(PAR_RANDOM, new ExtendedRandom(seed));
+ }
+ r.setSeed(seed);
+}
+
+//-----------------------------------------------------------------
+
+/*
+public static void main(String pars[]) {
+
+ setEndTime(Long.parseLong(pars[0]));
+ setTime(Long.parseLong(pars[1]));
+ System.err.println(getTime()+" "+getIntTime());
+}
+*/
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+/**
+ * Generic interface for classes that are responsible for observing or modifying
+ * the ongoing simulation. It is designed to allow maximal flexibility therefore
+ * poses virtually no restrictions on the implementation.
+ */
+public interface Control
+{
+
+/**
+ * Performs arbitrary modifications or reports arbitrary information over the
+ * components.
+ * @return true if the simulation has to be stopped, false otherwise.
+ */
+public boolean execute();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+
+/**
+* Instances of classes implementing this interface
+* maintain a fail state, i.e. information about the availability
+* of the object.
+*/
+public interface Fallible {
+
+
+ /**
+ * Fail state which indicates that the object is operating normally.
+ */
+ public int OK = 0;
+
+ /**
+ * Fail state indicating that the object is dead and recovery is
+ * not possible. When this state is set, it is a good idea to make sure
+ * that the state of the object becomes such that any attempt to
+ * operate on it causes a visible error of some kind.
+ */
+ public int DEAD = 1;
+
+ /**
+ * Fail state indicating that the object is not dead, but is temporarily
+ * not accessible.
+ */
+ public int DOWN = 2;
+
+ /**
+ * Returns the state of this object. Must be one of the constants
+ * defined in interface {@link Fallible}.
+ */
+ public int getFailState();
+
+ /**
+ * Sets the fails state of this object. Parameter must be one of the
+ * constants defined in interface {@link Fallible}.
+ */
+ public void setFailState(int failState);
+
+ /**
+ * Convenience method to check if the node is up and running
+ * @return must return true if and only if
+ * <code>getFailState()==OK</code>
+ */
+ public boolean isUp();
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+import peersim.config.*;
+
+/**
+* This is the default {@link Node} class that is used to compose the
+* {@link Network}.
+*/
+public class GeneralNode implements Node {
+
+
+// ================= fields ========================================
+// =================================================================
+
+/** used to generate unique IDs */
+private static long counterID = -1;
+
+/**
+* The protocols on this node.
+*/
+protected Protocol[] protocol = null;
+
+/**
+* The current index of this node in the node
+* list of the {@link Network}. It can change any time.
+* This is necessary to allow
+* the implementation of efficient graph algorithms.
+*/
+private int index;
+
+/**
+* The fail state of the node.
+*/
+protected int failstate = Fallible.OK;
+
+/**
+* The ID of the node. It should be final, however it can't be final because
+* clone must be able to set it.
+*/
+private long ID;
+
+// ================ constructor and initialization =================
+// =================================================================
+
+/** Used to construct the prototype node. This class currently does not
+* have specific configuration parameters and so the parameter
+* <code>prefix</code> is not used. It reads the protocol components
+* (components that have type {@value peersim.core.Node#PAR_PROT}) from
+* the configuration.
+*/
+public GeneralNode(String prefix) {
+
+ String[] names = Configuration.getNames(PAR_PROT);
+ CommonState.setNode(this);
+ ID=nextID();
+ protocol = new Protocol[names.length];
+ for (int i=0; i < names.length; i++) {
+ CommonState.setPid(i);
+ Protocol p = (Protocol)
+ Configuration.getInstance(names[i]);
+ protocol[i] = p;
+ }
+}
+
+
+// -----------------------------------------------------------------
+
+public Object clone() {
+
+ GeneralNode result = null;
+ try { result=(GeneralNode)super.clone(); }
+ catch( CloneNotSupportedException e ) {} // never happens
+ result.protocol = new Protocol[protocol.length];
+ CommonState.setNode(result);
+ result.ID=nextID();
+ for(int i=0; i<protocol.length; ++i) {
+ CommonState.setPid(i);
+ result.protocol[i] = (Protocol)protocol[i].clone();
+ }
+ return result;
+}
+
+// -----------------------------------------------------------------
+
+/** returns the next unique ID */
+private long nextID() {
+
+ return counterID++;
+}
+
+// =============== public methods ==================================
+// =================================================================
+
+
+public void setFailState(int failState) {
+
+ // after a node is dead, all operations on it are errors by definition
+ if(failstate==DEAD && failState!=DEAD) throw new IllegalStateException(
+ "Cannot change fail state: node is already DEAD");
+ switch(failState)
+ {
+ case OK:
+ failstate=OK;
+ break;
+ case DEAD:
+ //protocol = null;
+ index = -1;
+ failstate = DEAD;
+ for(int i=0;i<protocol.length;++i)
+ if(protocol[i] instanceof Cleanable)
+ ((Cleanable)protocol[i]).onKill();
+ break;
+ case DOWN:
+ failstate = DOWN;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "failState="+failState);
+ }
+}
+
+// -----------------------------------------------------------------
+
+public int getFailState() { return failstate; }
+
+// ------------------------------------------------------------------
+
+public boolean isUp() { return failstate==OK; }
+
+// -----------------------------------------------------------------
+
+public Protocol getProtocol(int i) { return protocol[i]; }
+
+//------------------------------------------------------------------
+
+public int protocolSize() { return protocol.length; }
+
+//------------------------------------------------------------------
+
+public int getIndex() { return index; }
+
+//------------------------------------------------------------------
+
+public void setIndex(int index) { this.index = index; }
+
+//------------------------------------------------------------------
+
+/**
+* Returns the ID of this node. The IDs are generated using a counter
+* (i.e. they are not random).
+*/
+public long getID() { return ID; }
+
+//------------------------------------------------------------------
+
+public String toString()
+{
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("ID: "+ID+" index: "+index+"\n");
+ for(int i=0; i<protocol.length; ++i)
+ {
+ buffer.append("protocol["+i+"]="+protocol[i]+"\n");
+ }
+ return buffer.toString();
+}
+
+//------------------------------------------------------------------
+
+/** Implemented as <code>(int)getID()</code>. */
+public int hashCode() { return (int)getID(); }
+
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+import peersim.config.Configuration;
+
+/**
+ * A protocol that stores links. It does nothing apart from that.
+ * It is useful to model a static link-structure
+ * (topology). The only function of this protocol is to serve as a source of
+ * neighborhood information for other protocols.
+ */
+public class IdleProtocol implements Protocol, Linkable
+{
+
+// --------------------------------------------------------------------------
+// Parameters
+// --------------------------------------------------------------------------
+
+/**
+ * Default init capacity
+ */
+private static final int DEFAULT_INITIAL_CAPACITY = 10;
+
+/**
+ * Initial capacity. Defaults to {@value #DEFAULT_INITIAL_CAPACITY}.
+ * @config
+ */
+private static final String PAR_INITCAP = "capacity";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** Neighbors */
+protected Node[] neighbors;
+
+/** Actual number of neighbors in the array */
+protected int len;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+public IdleProtocol(String s)
+{
+ neighbors = new Node[Configuration.getInt(s + "." + PAR_INITCAP,
+ DEFAULT_INITIAL_CAPACITY)];
+ len = 0;
+}
+
+//--------------------------------------------------------------------------
+
+public Object clone()
+{
+ IdleProtocol ip = null;
+ try { ip = (IdleProtocol) super.clone(); }
+ catch( CloneNotSupportedException e ) {} // never happens
+ ip.neighbors = new Node[neighbors.length];
+ System.arraycopy(neighbors, 0, ip.neighbors, 0, len);
+ ip.len = len;
+ return ip;
+}
+
+// --------------------------------------------------------------------------
+// Methods
+// --------------------------------------------------------------------------
+
+public boolean contains(Node n)
+{
+ for (int i = 0; i < len; i++) {
+ if (neighbors[i] == n)
+ return true;
+ }
+ return false;
+}
+
+// --------------------------------------------------------------------------
+
+/** Adds given node if it is not already in the network. There is no limit
+* to the number of nodes that can be added. */
+public boolean addNeighbor(Node n)
+{
+ for (int i = 0; i < len; i++) {
+ if (neighbors[i] == n)
+ return false;
+ }
+ if (len == neighbors.length) {
+ Node[] temp = new Node[3 * neighbors.length / 2];
+ System.arraycopy(neighbors, 0, temp, 0, neighbors.length);
+ neighbors = temp;
+ }
+ neighbors[len] = n;
+ len++;
+ return true;
+}
+
+// --------------------------------------------------------------------------
+
+public Node getNeighbor(int i)
+{
+ return neighbors[i];
+}
+
+// --------------------------------------------------------------------------
+
+public int degree()
+{
+ return len;
+}
+
+// --------------------------------------------------------------------------
+
+public void pack()
+{
+ if (len == neighbors.length)
+ return;
+ Node[] temp = new Node[len];
+ System.arraycopy(neighbors, 0, temp, 0, len);
+ neighbors = temp;
+}
+
+// --------------------------------------------------------------------------
+
+public String toString()
+{
+ if( neighbors == null ) return "DEAD!";
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("len=" + len + " maxlen=" + neighbors.length + " [");
+ for (int i = 0; i < len; ++i) {
+ buffer.append(neighbors[i].getIndex() + " ");
+ }
+ return buffer.append("]").toString();
+}
+
+// --------------------------------------------------------------------------
+
+public void onKill()
+{
+ neighbors = null;
+ len = 0;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+
+/**
+* Instances of classes implementing this interface can form networks (graphs)
+* in the simulator framework.
+* The interface is similar to one of a container (containing neighbors),
+* only the types of the contained elements have to be {@link Node}.
+* The neighbor collection is defined in a random access list style, but
+* it must follow the contract of a set too (elements must be unique).
+* <p>
+* Also note that there is no possibility to remove elements from the
+* neighbor set. This is because removal is usually costly and it does not make
+* sense in the context of our applications,
+* where this interface is used for 1) initialization and 2)
+* providing an interface for other protocols for accessing the neighbor list.
+* Protocols that manage links remove, refresh, etc their link set internally
+* or through custom methods.
+* Therefore it would only put an unnecessary burden on implementors.
+*/
+public interface Linkable extends Cleanable {
+
+
+ /**
+ * Returns the size of the neighbor list.
+ */
+ public int degree();
+
+ /**
+ * Returns the neighbor with the given index. The contract is that
+ * listing the elements from index 0 to index degree()-1 should list
+ * each element exactly once if this object is not modified in the
+ * meantime. It throws an IndexOutOfBounds exception if i is negative
+ * or larger than or equal to {@link #degree}.
+ */
+ public Node getNeighbor(int i);
+
+ /**
+ * Add a neighbor to the current set of neighbors. If neighbor
+ * is not yet a neighbor but it cannot be added from other reasons,
+ * this method should not return normally, that is, it must throw
+ * a runtime exception.
+ * @return true if the neighbor has been inserted; false if the
+ * node is already a neighbor of this node
+ */
+ public boolean addNeighbor(Node neighbour);
+
+ /**
+ * Returns true if the given node is a member of the neighbor set.
+ */
+ public boolean contains(Node neighbor);
+
+ /**
+ * A possibility for optimization. An implementation should try to
+ * compress its internal representation. Normally this is called
+ * by initializers or other components when
+ * no increase in the expected size of the neighborhood can be
+ * expected.
+ */
+ public void pack();
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+/**
+ * Tag interface to identify protocols that are malicious.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.2 $
+ */
+public interface MaliciousProtocol
+extends Protocol
+{
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+
+/**
+ * This class extends GeneralNode by allowing to modify single
+ * protocol instances at nodes.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.3 $
+ */
+public class ModifiableNode extends GeneralNode
+{
+
+/**
+ * Invokes the super constructor.
+ */
+public ModifiableNode(String prefix)
+{
+ super(prefix);
+}
+
+/**
+ * Substitutes the specified protocol at this node.
+ *
+ * @param pid protocol identifier
+ * @param prot the protocol object
+ */
+public void setProtocol(int pid, Protocol prot)
+{
+ protocol[pid] = prot;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+import peersim.Simulator;
+import peersim.config.Configuration;
+import psgsim.PSGDynamicNetwork;
+
+import java.util.Comparator;
+import java.util.Arrays;
+
+import org.simgrid.msg.HostNotFoundException;
+import org.simgrid.msg.MsgException;
+
+/**
+ * This class forms the basic framework of all simulations. This is a static
+ * singleton which is based on the assumption that we will simulate only one
+ * overlay network at a time. This allows us to reduce memory usage in many
+ * cases by allowing all the components to directly reach the fields of this
+ * class without having to store a reference.
+ * <p>
+ * The network is a set of nodes implemented via an array list for the sake of
+ * efficiency. Each node has an array of protocols. The protocols within a node
+ * can interact directly as defined by their implementation, and can be imagined
+ * as processes running in a common local environment (i.e. the node). This
+ * class is called a "network" because, although it is only a set of nodes, in
+ * most simulations there is at least one {@link Linkable} protocol that defines
+ * connections between nodes. In fact, such a {@link Linkable} protocol layer
+ * can be accessed through a {@link peersim.graph.Graph} view using
+ * {@link OverlayGraph}.
+ */
+public class Network {
+
+ // ========================= fields =================================
+ // ==================================================================
+
+ /**
+ * This config property defines the node class to be used. If not set, then
+ * {@link GeneralNode} will be used.
+ *
+ * @config
+ */
+ private static final String PAR_NODE = "network.node";
+
+ /**
+ * This config property defines the initial capacity of the overlay network.
+ * If not set then the value of {@value #PAR_SIZE} will be used. The main
+ * purpose of this parameter is that it allows for optimization. In the case
+ * of scenarios when the network needs to grow, setting this to the maximal
+ * expected size of the network avoids reallocation of memory during the
+ * growth of the network.
+ *
+ * @see #getCapacity
+ * @config
+ */
+ private static final String PAR_MAXSIZE = "network.initialCapacity";
+
+ /**
+ * This config property defines the initial size of the overlay network.
+ * This property is required.
+ *
+ * @config
+ */
+ private static final String PAR_SIZE = "network.size";
+
+ /**
+ * The node array. This is not a private array which is not nice but
+ * efficiency has the highest priority here. The main purpose is to allow
+ * the package quick reading of the contents in a maximally flexible way.
+ * Nevertheless, methods of this class should be used instead of the array
+ * when modifying the contents. Because this array is not private, it is
+ * necessary to know that the actual node set is only the first
+ * {@link #size()} items of the array.
+ */
+ static Node[] node = null;
+
+ /**
+ * Actual size of the network.
+ */
+ private static int len;
+
+ /**
+ * The prototype node which is used to populate the simulation via cloning.
+ * After all the nodes have been cloned, {@link Control} components can be
+ * applied to perform any further initialization.
+ */
+ public static Node prototype = null;
+
+ // ====================== initialization ===========================
+ // =================================================================
+
+ /**
+ * Reads configuration parameters, constructs the prototype node, and
+ * populates the network by cloning the prototype.
+ */
+ public static void reset() {
+
+ if (prototype != null) {
+ // not first experiment
+ while (len > 0)
+ remove(); // this is to call onKill on all nodes
+ prototype = null;
+ node = null;
+ }
+
+ len = Configuration.getInt(PAR_SIZE);
+ int maxlen = Configuration.getInt(PAR_MAXSIZE, len);
+ if (maxlen < len)
+ throw new IllegalArgumentException(PAR_MAXSIZE + " is less than "
+ + PAR_SIZE);
+
+ node = new Node[maxlen];
+
+ // creating prototype node
+ Node tmp = null;
+ if (!Configuration.contains(PAR_NODE)) {
+ System.err.println("Network: no node defined, using GeneralNode");
+ tmp = new GeneralNode("");
+ } else {
+ tmp = (Node) Configuration.getInstance(PAR_NODE);
+ }
+ prototype = tmp;
+ prototype.setIndex(-1);
+
+ // cloning the nodes
+ if (len > 0) {
+ for (int i = 0; i < len; ++i) {
+ node[i] = (Node) prototype.clone();
+ node[i].setIndex(i);
+ }
+ }
+ }
+
+ /** Disable instance construction */
+ private Network() {
+ }
+
+ // =============== public methods ===================================
+ // ==================================================================
+
+ /** Number of nodes currently in the network */
+ public static int size() {
+ return len;
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * Sets the capacity of the internal array storing the nodes. The nodes will
+ * remain the same in the same order. If the new capacity is less than the
+ * old size of the node list, than the end of the list is cut. The nodes
+ * that get removed via this cutting are removed through {@link #remove()}.
+ */
+ public static void setCapacity(int newSize) {
+
+ if (node == null || newSize != node.length) {
+ for (int i = newSize; i < len; ++i)
+ remove();
+ Node[] newnodes = new Node[newSize];
+ final int l = Math.min(node.length, newSize);
+ System.arraycopy(node, 0, newnodes, 0, l);
+ node = newnodes;
+ if (len > newSize)
+ len = newSize;
+ }
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * Returns the maximal number of nodes that can be stored without
+ * reallocating the underlying array to increase capacity.
+ */
+ public static int getCapacity() {
+ return node.length;
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * The node will be appended to the end of the list. If necessary, the
+ * capacity of the internal array is increased.
+ */
+ public static void add(Node n) {
+ if (len == node.length)
+ setCapacity(3 * node.length / 2 + 1);
+ node[len] = n;
+ n.setIndex(len);
+ len++;
+ if (Simulator.getSimID() == 2)
+ try {
+ PSGDynamicNetwork.add(n);
+ } catch (HostNotFoundException e) {
+ System.err.println("Host not found in platform file");
+ }
+ System.err.println("node " + n.getID() + " with index " + n.getIndex()
+ + " was added at time " + CommonState.getTime());
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * Returns node with the given index. Note that the same node will normally
+ * have a different index in different times. This can be used as a random
+ * access iterator. This method does not perform range checks to increase
+ * efficiency. The maximal valid index is {@link #size()}.
+ */
+ public static Node get(int index) {
+ return node[index];
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * The node at the end of the list is removed. Returns the removed node. It
+ * also sets the fail state of the node to {@link Fallible#DEAD}.
+ */
+ public static Node remove() {
+ Node n = node[len - 1]; // if len was zero this throws and exception
+ node[len - 1] = null;
+ len--;
+ System.err.println("node " + n.getID() + " with index " + n.getIndex()
+ + " was removed at time " + CommonState.getTime());
+ n.setFailState(Fallible.DEAD);
+ if (Simulator.getSimID() == 2)
+ PSGDynamicNetwork.remove(n);
+ return n;
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * The node with the given index is removed. Returns the removed node. It
+ * also sets the fail state of the node to {@link Fallible#DEAD}.
+ * <p>
+ * Look out: the index of the other nodes will not change (the right hand
+ * side of the list is not shifted to the left) except that of the last
+ * node. Only the last node is moved to the given position and will get
+ * index i.
+ */
+ public static Node remove(int i) {
+
+ if (i < 0 || i >= len)
+ throw new IndexOutOfBoundsException("" + i);
+ swap(i, len - 1);
+ return remove();
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * Swaps the two nodes at the given indexes.
+ */
+ public static void swap(int i, int j) {
+
+ Node n = node[i];
+ node[i] = node[j];
+ node[j] = n;
+ node[j].setIndex(j);
+ node[i].setIndex(i);
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * Shuffles the node array. The index of each node is updated accordingly.
+ */
+ public static void shuffle() {
+
+ for (int i = len; i > 1; i--)
+ swap(i - 1, CommonState.r.nextInt(i));
+
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * Sorts the node array. The index of each node is updated accordingly.
+ *
+ * @param c
+ * The comparator to be used for sorting the nodes. If null, the
+ * natural order of the nodes is used.
+ */
+ public static void sort(Comparator<? super Node> c) {
+
+ Arrays.sort(node, 0, len, c);
+ for (int i = 0; i < len; i++)
+ node[i].setIndex(i);
+ }
+
+ // ------------------------------------------------------------------
+
+ public static void test() {
+
+ System.err.println("number of nodes = " + len);
+ System.err.println("capacity (max number of nodes) = " + node.length);
+ for (int i = 0; i < len; ++i) {
+ System.err.println("node[" + i + "]");
+ System.err.println(node[i].toString());
+ }
+
+ if (prototype == null)
+ return;
+ for (int i = 0; i < prototype.protocolSize(); ++i) {
+ if (prototype.getProtocol(i) instanceof Linkable)
+ peersim.graph.GraphIO.writeUCINET_DL(new OverlayGraph(i),
+ System.err);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+/**
+ * Class that represents one node with a network address. An {@link Network} is
+ * made of a set of nodes. The functionality of this class is thin: it must be
+ * able to represent failure states and store a list of protocols. It is the
+ * protocols that do the interesting job.
+ */
+public interface Node extends Fallible, Cloneable
+{
+
+/**
+ * Prefix of the parameters that defines protocols.
+ * @config
+ */
+public static final String PAR_PROT = "protocol";
+
+/**
+ * Returns the <code>i</code>-th protocol in this node. If <code>i</code>
+ * is not a valid protocol id
+ * (negative or larger than or equal to the number of protocols), then it throws
+ * IndexOutOfBoundsException.
+ */
+public Protocol getProtocol(int i);
+
+/**
+ * Returns the number of protocols included in this node.
+ */
+public int protocolSize();
+
+/**
+ * Sets the index of this node in the internal representation of the node list.
+ * Applications should not use this method. It is defined as public simply
+ * because it is not possible to define it otherwise.
+ * Using this method will result in
+ * undefined behavior. It is provided for the core system.
+ */
+public void setIndex(int index);
+
+/**
+ * Returns the index of this node. It is such that
+ * <code>Network.get(n.getIndex())</code> returns n. This index can
+ * change during a simulation, it is not a fixed id. If you need that, use
+ * {@link #getID}.
+ * @see Network#get
+ */
+public int getIndex();
+
+/**
+* Returns the unique ID of the node. It is guaranteed that the ID is unique
+* during the entire simulation, that is, there will be no different Node
+* objects with the same ID in the system during one invocation of the JVM.
+* Preferably nodes
+* should implement <code>hashCode()</code> based on this ID.
+*/
+public long getID();
+
+/**
+ * Clones the node. It is defined as part of the interface
+ * to change the access right to public and to get rid of the
+ * <code>throws</code> clause.
+ */
+public Object clone();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+/**
+* A protocol that does nothing but knows everything.
+* It provides an interface which models a protocol that knows all nodes
+* in the network, i.e. the neighborhood set of this protocol is always the
+* whole node set. this protocol is also extremely cheap, in fact it
+* has no data fields.
+*/
+public final class OracleIdleProtocol implements Protocol, Linkable {
+
+// =================== initialization, creation ======================
+// ===================================================================
+
+
+/** Does nothing */
+public OracleIdleProtocol(String prefix) {}
+
+// --------------------------------------------------------------------
+
+/** Returns <tt>this</tt> to maximize memory saving. It contains no fields.*/
+public Object clone() { return this; }
+
+
+// ===================== public methods ===============================
+// ====================================================================
+
+
+/** This is an expensive operation, should not be used at all.
+* It returns false only if the given node is not in the current network.
+*/
+public boolean contains(Node n) {
+
+ final int len = Network.size();
+ for (int i=0; i < len; i++)
+ {
+ if (Network.node[i] == n)
+ return true;
+ }
+ return false;
+}
+
+// --------------------------------------------------------------------
+
+/** Unsupported operation */
+public boolean addNeighbor(Node n) {
+
+ throw new UnsupportedOperationException();
+}
+
+// --------------------------------------------------------------------
+
+/**
+* The neighborhood contains the node itself, ie it contains the loop
+* edge.
+*/
+public Node getNeighbor(int i) {
+
+ return Network.node[i];
+}
+
+// --------------------------------------------------------------------
+
+public int degree() {
+
+ return Network.size();
+}
+
+// --------------------------------------------------------------------
+
+public void pack() {}
+
+// --------------------------------------------------------------------
+
+public void onKill() {}
+
+// --------------------------------------------------------------------
+
+public String toString() {
+
+ return degree()+" [all the nodes of the overlay network]";
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+import peersim.graph.Graph;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+* This class is an adaptor which makes a {@link Linkable} protocol layer
+* look like a graph. It is useful because it allows the application of many
+* graph algorithms and graph topology initialization methods.
+* If the overlay network changes after creating this object, the changes
+* will be reflected. However, if the nodes are reshuffled (see
+* {@link Network#shuffle}), or if the node list changes (addition/removal),
+* then the behaviour becomes unspecified.
+*
+* The indices of nodes are from 0 to Network.size()-1.
+*
+* The fail state of nodes has an effect on the graph: all nodes are included
+* but edges are included only if both ends are up. This expresses the fact
+* that this graph is in fact defined by the "can communicate with" relation.
+*/
+public class OverlayGraph implements Graph {
+
+
+// ====================== fields ================================
+// ==============================================================
+
+/**
+* The protocol ID that selects the Linkable protocol to convert to a graph.
+*/
+public final int protocolID;
+
+/**
+* Tells if the graph should be wired in an undirected way.
+* Method {@link #directed} returns true always, this affects only
+* method {@link #setEdge}: if false, then the opposite edge is set too.
+*/
+public final boolean wireDirected;
+
+// ====================== public constructors ===================
+// ==============================================================
+
+/**
+* @param protocolID The protocol on which this adaptor is supposed
+* to operate.
+*/
+public OverlayGraph( int protocolID ) {
+
+ this.protocolID = protocolID;
+ wireDirected = true;
+}
+
+// --------------------------------------------------------------
+
+/**
+* @param protocolID The protocol on which this adaptor is supposed
+* to operate.
+* @param wireDirected specifies if {@link #setEdge} would wire the
+* opposite edge too.
+*/
+public OverlayGraph( int protocolID, boolean wireDirected ) {
+
+ this.protocolID = protocolID;
+ this.wireDirected = wireDirected;
+}
+
+
+// ======================= Graph implementations ================
+// ==============================================================
+
+
+public boolean isEdge(int i, int j) {
+
+ return
+ ((Linkable)Network.node[i].getProtocol(protocolID)
+ ).contains(Network.node[j]) &&
+ Network.node[j].isUp() &&
+ Network.node[i].isUp();
+}
+
+// ---------------------------------------------------------------
+
+/**
+* Returns those neighbors that are up. If node i is not up, it returns
+* an empty list.
+*/
+public Collection<Integer> getNeighbours(int i) {
+
+ Linkable lble=(Linkable)Network.node[i].getProtocol(protocolID);
+ ArrayList<Integer> al = new ArrayList<Integer>(lble.degree());
+ if( Network.node[i].isUp() )
+ {
+ for(int j=0; j<lble.degree(); ++j)
+ {
+ final Node n = lble.getNeighbor(j);
+ // if accessible, we include it
+ if(n.isUp()) al.add(Integer.valueOf(n.getIndex()));
+ }
+ }
+ return Collections.unmodifiableList(al);
+}
+
+// ---------------------------------------------------------------
+
+/** Returns <code>Network.node[i]</code> */
+public Object getNode(int i) { return Network.node[i]; }
+
+// ---------------------------------------------------------------
+
+/**
+* Returns null always
+*/
+public Object getEdge(int i, int j) { return null; }
+
+// ---------------------------------------------------------------
+
+/** Returns <code>Network.size()</code> */
+public int size() { return Network.size(); }
+
+// --------------------------------------------------------------------
+
+/** Returns always true */
+public boolean directed() { return true; }
+
+// --------------------------------------------------------------------
+
+/**
+* Sets given edge.
+* In some cases this behaves strangely. Namely, when node i or j is not up,
+* but is not dead (e.g. it can be down temporarily).
+* In such situations the relevant link is made, but afterwards
+* getEdge(i,j) will NOT return true, only when the fail state has changed back
+* to OK.
+*
+* <p>Conceptually one can think of it as a successful operation which is
+* immediately overruled by the dynamics of the underlying overlay network.
+* Let's not forget that this class is an adaptor only.
+*
+* <p>
+* The behaviour of this method is affected by parameter {@link #wireDirected}.
+* If it is false, then the opposite edge is set too.
+*/
+public boolean setEdge( int i, int j ) {
+// XXX slightly unintuitive behavior but makes sense when understood
+
+ if( !wireDirected )
+ ((Linkable)Network.node[j].getProtocol(protocolID)
+ ).addNeighbor(Network.node[i]);
+
+
+ return
+ ((Linkable)Network.node[i].getProtocol(protocolID)
+ ).addNeighbor(Network.node[j]);
+}
+
+// ---------------------------------------------------------------
+
+/** Not supported */
+public boolean clearEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// ---------------------------------------------------------------
+
+/**
+* Returns number of neighbors that are up. If node i is down, returns 0.
+*/
+public int degree(int i) {
+
+ if( !Network.node[i].isUp() ) return 0;
+ Linkable lble=(Linkable)Network.node[i].getProtocol(protocolID);
+ int numNeighbours = 0;
+ for(int j=0; j<lble.degree(); ++j)
+ {
+ final Node n = lble.getNeighbor(j);
+ // if accessible, we count it
+ if(n.isUp()) numNeighbours++;
+ }
+ return numNeighbours;
+}
+
+
+// ========================= other methods =======================
+// ===============================================================
+
+
+/**
+* Returns number of neighbors that are either up or down.
+* If node i is down, returns 0.
+*/
+public int fullDegree(int i) {
+
+ if( !Network.node[i].isUp() ) return 0;
+ Linkable lble=(Linkable)Network.node[i].getProtocol(protocolID);
+ return lble.degree();
+}
+
+
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+/**
+ * Interface to identify protocols.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.5 $
+ */
+public interface Protocol extends Cloneable
+{
+
+/**
+ * Returns a clone of the protocol. It is important to pay attention to
+ * implement this carefully because in peersim all nodes are generated by
+ * cloning except a prototype node. That is, the constructor of protocols is
+ * used only to construct the prototype. Initialization can be done
+ * via {@link Control}s.
+ */
+public Object clone();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.core;
+
+import peersim.config.*;
+
+// XXX a quite primitive scheduler, should be able to be configured
+// much more flexibly using a simple syntax for time ranges.
+/**
+* A binary function over the time points. That is,
+* for each time point returns a boolean value.
+*
+* The concept of time depends on the simulation model. Current time
+* has to be set by the simulation engine, irrespective of the model,
+* and can be read using {@link CommonState#getTime()}. This scheduler
+* is interpreted over those time points.
+*
+* <p>In this simple implementation the valid times will be
+* <tt>from, from+step, from+2*step, etc,</tt>
+* where the last element is strictly less than <tt>until</tt>.
+* Alternatively, if <tt>at</tt> is defined, then the schedule will be a single
+* time point. If FINAL is
+* defined, it is also added to the set of active time points.
+* It refers to the time after the simulation has finished (see
+* {@link CommonState#getPhase}).
+*/
+public class Scheduler {
+
+
+// ========================= fields =================================
+// ==================================================================
+
+
+/**
+* Defaults to 1.
+* @config
+*/
+private static final String PAR_STEP = "step";
+
+/**
+* Defaults to -1. That is, defaults to be ineffective.
+* @config
+*/
+private static final String PAR_AT = "at";
+
+
+/**
+* Defaults to 0.
+* @config
+*/
+private static final String PAR_FROM = "from";
+
+/**
+* Defaults to <tt>Long.MAX_VALUE</tt>.
+* @config
+*/
+private static final String PAR_UNTIL = "until";
+
+/**
+* Defines if component is active after the simulation has finished.
+* Note that the exact time the simulation finishes is not know in advance
+* because other components can stop the simulation at any time.
+* By default not set.
+* @see CommonState#getPhase
+* @config
+*/
+private static final String PAR_FINAL = "FINAL";
+
+public final long step;
+
+public final long from;
+
+public final long until;
+
+public final boolean fin;
+
+/** The next scheduled time point.*/
+protected long next = -1;
+
+// ==================== initialization ==============================
+// ==================================================================
+
+
+/** Reads configuration parameters from the component defined by
+* <code>prefix</code>. {@value #PAR_STEP} defaults to 1.
+*/
+public Scheduler(String prefix) {
+
+ this(prefix, true);
+}
+
+// ------------------------------------------------------------------
+
+/** Reads configuration parameters from the component defined by
+* <code>prefix</code>. If useDefault is false, then at least parameter
+* {@value #PAR_STEP} must be explicitly defined. Otherwise {@value #PAR_STEP}
+* defaults to 1.
+*/
+public Scheduler(String prefix, boolean useDefault)
+{
+ if (Configuration.contains(prefix+"."+PAR_AT)) {
+ // FROM, UNTIL, and STEP should *not* be defined
+ if (Configuration.contains(prefix+"."+PAR_FROM) ||
+ Configuration.contains(prefix+"."+PAR_UNTIL) ||
+ Configuration.contains(prefix+"."+PAR_STEP))
+ throw new IllegalParameterException(prefix,
+ "Cannot use \""+PAR_AT+"\" together with \""
+ +PAR_FROM+"\", \""+PAR_UNTIL+"\", or \""+
+ PAR_STEP+"\"");
+
+ from = Configuration.getLong(prefix+"."+PAR_AT);
+ until = from+1;
+ step = 1;
+ } else {
+ if (useDefault)
+ step = Configuration.getLong(prefix+"."+PAR_STEP,1);
+ else
+ step = Configuration.getLong(prefix+"."+PAR_STEP);
+ if( step < 1 )
+ throw new IllegalParameterException(prefix,
+ "\""+PAR_STEP+"\" must be >= 1");
+
+ from = Configuration.getLong(prefix+"."+PAR_FROM,0);
+ until = Configuration.getLong(prefix+"."+PAR_UNTIL,Long.MAX_VALUE);
+ }
+
+ if( from < until ) next = from;
+ else next = -1;
+
+ fin = Configuration.contains(prefix+"."+PAR_FINAL);
+}
+
+
+// ===================== public methods ==============================
+// ===================================================================
+
+/** true if given time point is covered by this scheduler */
+public boolean active(long time) {
+
+ if( time < from || time >= until ) return false;
+ return (time - from)%step == 0;
+}
+
+// -------------------------------------------------------------------
+
+/** true if current time point is covered by this scheduler */
+public boolean active() {
+
+ return active( CommonState.getTime() );
+}
+
+//-------------------------------------------------------------------
+
+/**
+* Returns the next time point. If the returned value is negative, there are
+* no more time points. As a side effect, it also updates the next time point,
+* so repeated calls to this method return the scheduled times.
+*/
+public long getNext()
+{
+ long ret = next;
+ // check like this to prevent integer overflow of "next"
+ if( until-next > step ) next += step;
+ else next = -1;
+ return ret;
+}
+
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import java.util.Map;
+
+import org.simgrid.msg.Host;
+import org.simgrid.msg.MsgException;
+
+import peersim.Simulator;
+import peersim.config.Configuration;
+import peersim.core.*;
+import psgsim.NodeHost;
+import psgsim.PSGDynamicNetwork;
+
+/**
+ * This {@link Control} can change the size of networks by adding and removing
+ * nodes. Can be used to model churn. This class supports only permanent removal
+ * of nodes and the addition of brand new nodes. That is, temporary downtime is
+ * not supported by this class.
+ */
+public class DynamicNetwork implements Control {
+
+ // --------------------------------------------------------------------------
+ // Parameters
+ // --------------------------------------------------------------------------
+
+ /**
+ * Config parameter which gives the prefix of node initializers. An
+ * arbitrary number of node initializers can be specified (Along with their
+ * parameters). These will be applied on the newly created nodes. The
+ * initializers are ordered according to alphabetical order if their ID.
+ * Example:
+ *
+ * <pre>
+ * control.0 DynamicNetwork
+ * control.0.init.0 RandNI
+ * control.0.init.0.k 5
+ * control.0.init.0.protocol somelinkable
+ * ...
+ * </pre>
+ *
+ * @config
+ */
+ private static final String PAR_INIT = "init";
+
+ /**
+ * If defined, nodes are substituted (an existing node is removed, a new one
+ * is added. That is, first the number of nodes to add (or remove if
+ * {@value #PAR_ADD} is negative) is calculated, and then exactly the same
+ * number of nodes are removed (or added) immediately so that the network
+ * size remains constant. Not set by default.
+ *
+ * @config
+ */
+ private static final String PAR_SUBST = "substitute";
+
+ /**
+ * Specifies the number of nodes to add or remove. It can be negative in
+ * which case nodes are removed. If its absolute value is less than one,
+ * then it is interpreted as a proportion of current network size, that is,
+ * its value is substituted with <tt>add*networksize</tt>. Its value is
+ * rounded to an integer.
+ *
+ * @config
+ */
+ private static final String PAR_ADD = "add";
+
+ /**
+ * Nodes are added until the size specified by this parameter is reached.
+ * The network will never exceed this size as a result of this class. If not
+ * set, there will be no limit on the size of the network.
+ *
+ * @config
+ */
+ private static final String PAR_MAX = "maxsize";
+
+ /**
+ * Nodes are removed until the size specified by this parameter is reached.
+ * The network will never go below this size as a result of this class.
+ * Defaults to 0.
+ *
+ * @config
+ */
+ private static final String PAR_MIN = "minsize";
+
+ // --------------------------------------------------------------------------
+ // Fields
+ // --------------------------------------------------------------------------
+
+ /** value of {@value #PAR_ADD} */
+ protected final double add;
+
+ /** value of {@value #PAR_SUBST} */
+ protected final boolean substitute;
+
+ /** value of {@value #PAR_MIN} */
+ protected final int minsize;
+
+ /** value of {@value #PAR_MAX} */
+ protected final int maxsize;
+
+ /** node initializers to apply on the newly added nodes */
+ protected final NodeInitializer[] inits;
+
+ // --------------------------------------------------------------------------
+ // Protected methods
+ // --------------------------------------------------------------------------
+
+ /**
+ * Adds n nodes to the network. Extending classes can implement any
+ * algorithm to do that. The default algorithm adds the given number of
+ * nodes after calling all the configured initializers on them.
+ *
+ * @param n
+ * the number of nodes to add, must be non-negative.
+ */
+ protected void add(int n) {
+ for (int i = 0; i < n; ++i) {
+ Node newnode = (Node) Network.prototype.clone();
+ for (int j = 0; j < inits.length; ++j) {
+ inits[j].initialize(newnode);
+ }
+ Network.add(newnode);
+
+ }
+ }
+
+ // ------------------------------------------------------------------
+
+ /**
+ * Removes n nodes from the network. Extending classes can implement any
+ * algorithm to do that. The default algorithm removes <em>random</em> nodes
+ * <em>permanently</em> simply by calling {@link Network#remove(int)}.
+ *
+ * @param n
+ * the number of nodes to remove
+ */
+ protected void remove(int n) {
+ for (int i = 0; i < n; ++i) {
+ int nr = CommonState.r.nextInt(Network.size());
+ Network.remove(nr);
+
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Initialization
+ // --------------------------------------------------------------------------
+
+ /**
+ * Standard constructor that reads the configuration parameters. Invoked by
+ * the simulation engine.
+ *
+ * @param prefix
+ * the configuration prefix for this class
+ */
+ public DynamicNetwork(String prefix) {
+ add = Configuration.getDouble(prefix + "." + PAR_ADD);
+ substitute = Configuration.contains(prefix + "." + PAR_SUBST);
+ Object[] tmp = Configuration.getInstanceArray(prefix + "." + PAR_INIT);
+ inits = new NodeInitializer[tmp.length];
+ for (int i = 0; i < tmp.length; ++i) {
+ // System.out.println("Inits " + tmp[i]);
+ inits[i] = (NodeInitializer) tmp[i];
+ }
+ maxsize = Configuration.getInt(prefix + "." + PAR_MAX,
+ Integer.MAX_VALUE);
+ minsize = Configuration.getInt(prefix + "." + PAR_MIN, 0);
+ }
+
+ // --------------------------------------------------------------------------
+ // Public methods
+ // --------------------------------------------------------------------------
+
+ /**
+ * Calls {@link #add(int)} or {@link #remove} with the parameters defined by
+ * the configuration.
+ *
+ * @return always false
+ */
+ public final boolean execute() {
+ if (add == 0)
+ return false;
+ if (!substitute) {
+ if ((maxsize <= Network.size() && add > 0)
+ || (minsize >= Network.size() && add < 0))
+ return false;
+ }
+ int toadd = 0;
+ int toremove = 0;
+ if (add > 0) {
+ toadd = (int) Math.round(add < 1 ? add * Network.size() : add);
+ if (!substitute && toadd > maxsize - Network.size())
+ toadd = maxsize - Network.size();
+ if (substitute)
+ toremove = toadd;
+ } else if (add < 0) {
+ toremove = (int) Math
+ .round(add > -1 ? -add * Network.size() : -add);
+ if (!substitute && toremove > Network.size() - minsize)
+ toremove = Network.size() - minsize;
+ if (substitute)
+ toadd = toremove;
+ }
+ remove(toremove);
+ add(toadd);
+ return false;
+ }
+
+ // --------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import java.lang.reflect.*;
+import peersim.config.*;
+import peersim.core.*;
+import java.util.ArrayList;
+
+/**
+ * This {@link Control} invokes a specified method on a protocol.
+ * The method is defined by config parameter {@value #PAR_METHOD} and
+ * the protocol is by {@value #PAR_PROT}. The method must not have any
+ * parameters and must return void. If no protocol is specified, then the
+ * method will be invoked on all protocol in the protocol stack that define
+ * it.
+ * <p>
+ * Although the method cannot have any parameters, it can of course read
+ * {@link CommonState}. It is guaranteed that the state is up-to-date,
+ * inlcuding eg method {@link CommonState#getNode}.
+ */
+public class MethodInvoker implements Control, NodeInitializer {
+
+
+// --------------------------------------------------------------------------
+// Parameter names
+// --------------------------------------------------------------------------
+
+/**
+ * The protocol to operate on.
+ * If not defined, the given method will be invoked on all protocols that
+ * define it. In all cases, at least one protocol has to define it.
+ * @config
+ */
+private static final String PAR_PROT = "protocol";
+
+/**
+ * The method to be invoked. It must return void and should not have any
+ * parameters specified.
+ * @config
+ */
+private static final String PAR_METHOD = "method";
+
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** Identifiers of the protocols to be initialized */
+private final int pid[];
+
+/** Method name */
+private final String methodName;
+
+/** Methods corresponding to the protocols in {@link #pid}. */
+private final Method method[];
+
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public MethodInvoker(String prefix) {
+
+ methodName = Configuration.getString(prefix+"."+PAR_METHOD);
+ if(!Configuration.contains(prefix+"."+PAR_PROT))
+ {
+ // find protocols that implement method
+ ArrayList<Integer> pids = new ArrayList<Integer>();
+ ArrayList<Method> methods = new ArrayList<Method>();
+ for(int i=0; i<Network.prototype.protocolSize(); ++i)
+ {
+ Method m = null;
+ try
+ {
+ m = MethodInvoker.getMethod(
+ Network.prototype.getProtocol(i).getClass(),
+ methodName );
+ }
+ catch(NoSuchMethodException e) {}
+
+ if(m!=null)
+ {
+ pids.add(i);
+ methods.add(m);
+ }
+ }
+
+ if( pids.isEmpty() )
+ {
+ throw new IllegalParameterException(prefix + "." +
+ PAR_METHOD,
+ "No protocols found that implement 'void "+
+ methodName+"()'");
+ }
+
+ pid=new int[pids.size()];
+ int j=0;
+ for(int i: pids) pid[j++]=i;
+ method=methods.toArray(new Method[methods.size()]);
+ }
+ else
+ {
+ pid = new int[1];
+ pid[0] = Configuration.getPid(prefix+"."+PAR_PROT);
+ try
+ {
+ method = new Method[1];
+ method[0]=MethodInvoker.getMethod(
+ Network.prototype.getProtocol(pid[0]).getClass(),
+ methodName );
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new IllegalParameterException(prefix + "." +
+ PAR_METHOD, e+"");
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+// Methods
+// --------------------------------------------------------------------------
+
+private static Method getMethod(Class cl, String methodName)
+throws NoSuchMethodException {
+
+ Method[] methods = cl.getMethods();
+ ArrayList<Method> list = new ArrayList<Method>();
+ for(Method m: methods)
+ {
+ if(m.getName().equals(methodName))
+ {
+ Class[] pars = m.getParameterTypes();
+ Class ret = m.getReturnType();
+ if( pars.length == 0 && ret==void.class )
+ list.add(m);
+ }
+ }
+
+ if(list.size() == 0)
+ {
+ throw new NoSuchMethodException("Method "
+ + methodName + " in class " + cl.getName());
+ }
+
+ return list.get(0);
+}
+
+//--------------------------------------------------------------------------
+
+/** Invokes method on all the nodes. */
+public boolean execute() {
+
+ for(int i=0; i<Network.size(); ++i)
+ {
+ initialize(Network.get(i));
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------------
+
+/** Invokes method on given node. */
+public void initialize(Node n) {
+
+ try
+ {
+ for(int i=0; i<pid.length; ++i)
+ {
+ CommonState.setNode(n);
+ CommonState.setPid(pid[i]);
+ method[i].invoke(n.getProtocol(pid[i]));
+ }
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ System.exit(1);
+ }
+}
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.core.Node;
+
+/**
+ * Generic interface to initialize a node before inserting it into the
+ * simulation. Other components like {@link DynamicNetwork} can use a
+ * NodeInitializer. It is designed to allow maximal flexibility therefore poses
+ * virtually no restrictions on the implementation. It can even be used to
+ * implement initializations that require global knowledge of the system.
+ */
+public interface NodeInitializer
+{
+
+/**
+ * Performs arbitrary initializations on the given node. It is guaranteed that
+ * this is called <em>before</em> inserting the node into the network.
+ */
+public void initialize(Node n);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.config.Configuration;
+import peersim.core.*;
+
+/**
+ * Makes the network size oscillate.
+ * The network size will be the function of time, parameterized by this
+ * parameter. The size function is
+ * <code>avg+sin(time*pi/{@value #PAR_PERIOD})*ampl</code> where
+ * <code>avg=({@value #PAR_MAX}+{@value #PAR_MIN})/2</code> and
+ * <code>ampl=({@value #PAR_MAX}-{@value #PAR_MIN})/2</code>.
+ * This function is independent of how many times this class is executed, that
+ * is, whenever it is executed, it takes the current time and sets the network
+ * size accordingly.
+ */
+public class OscillatingNetwork implements Control
+{
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * Config parameter which gives the prefix of node initializers. An arbitrary
+ * number of node initializers can be specified (Along with their parameters).
+ * These will be applied
+ * on the newly created nodes. The initializers are ordered according to
+ * alphabetical order if their ID.
+ * Example:
+ * <pre>
+control.0 DynamicNetwork
+control.0.init.0 RandNI
+control.0.init.0.k 5
+control.0.init.0.protocol somelinkable
+...
+ * </pre>
+ * @config
+ */
+private static final String PAR_INIT = "init";
+
+/**
+ * Nodes are added until the size specified by this parameter is reached. The
+ * network will never exceed this size as a result of this class.
+ * If not set, there will be no limit on the size of the network.
+ * @config
+ */
+private static final String PAR_MAX = "maxsize";
+
+/**
+ * Nodes are removed until the size specified by this parameter is reached. The
+ * network will never go below this size as a result of this class.
+ * Defaults to 0.
+ * @config
+ */
+private static final String PAR_MIN = "minsize";
+
+/**
+ * Config parameter used to define the length of one period of the oscillation.
+ * The network size will be the function of time, parameterized by this
+ * parameter. The size function is
+ * <code>avg+sin(time*pi/{@value #PAR_PERIOD})*ampl</code> where
+ * <code>avg=({@value #PAR_MAX}+{@value #PAR_MIN})/2</code> and
+ * <code>ampl=({@value #PAR_MAX}-{@value #PAR_MIN})/2</code>.
+ * @config
+ */
+private static final String PAR_PERIOD = "period";
+
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+/** Period */
+private final int period;
+
+/** Maximum size */
+private final int minsize;
+
+/** Minimum size */
+private final int maxsize;
+
+/** New nodes initializers */
+private final NodeInitializer[] inits;
+
+
+//--------------------------------------------------------------------------
+// Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters. Invoked by the
+ * simulation engine.
+ * @param prefix
+ * the configuration prefix for this class
+ */
+public OscillatingNetwork(String prefix)
+{
+
+ period = Configuration.getInt(prefix + "." + PAR_PERIOD);
+ maxsize =
+ Configuration.getInt(
+ prefix + "." + PAR_MAX,
+ Integer.MAX_VALUE);
+ minsize = Configuration.getInt(prefix + "." + PAR_MIN, 0);
+
+ Object[] tmp = Configuration.getInstanceArray(prefix + "." + PAR_INIT);
+ inits = new NodeInitializer[tmp.length];
+ for (int i = 0; i < tmp.length; ++i)
+ {
+ inits[i] = (NodeInitializer) tmp[i];
+ }
+}
+
+//--------------------------------------------------------------------------
+// Methods
+//--------------------------------------------------------------------------
+
+/**
+ * Adds n nodes to the network. Extending classes can implement any algorithm to
+ * do that. The default algorithm adds the given number of nodes after calling
+ * all the configured initializers on them.
+ *
+ * @param n
+ * the number of nodes to add, must be non-negative.
+ */
+protected void add(int n)
+{
+ for (int i = 0; i < n; ++i) {
+ Node newnode = (Node) Network.prototype.clone();
+ for (int j = 0; j < inits.length; ++j) {
+ inits[j].initialize(newnode);
+ }
+ Network.add(newnode);
+ }
+}
+
+// ------------------------------------------------------------------
+
+/**
+ * Removes n nodes from the network. Extending classes can implement any
+ * algorithm to do that. The default algorithm removes <em>random</em>
+ * nodes <em>permanently</em> simply by calling {@link Network#remove(int)}.
+ * @param n the number of nodes to remove
+ */
+protected void remove(int n)
+{
+ for (int i = 0; i < n; ++i) {
+ Network.remove(CommonState.r.nextInt(Network.size()));
+ }
+}
+
+// ------------------------------------------------------------------
+
+/**
+ * Takes the current time and sets the network size according to a periodic
+ * function of time.
+ * The size function is
+ * <code>avg+sin(time*pi/{@value #PAR_PERIOD})*ampl</code> where
+ * <code>avg=({@value #PAR_MAX}+{@value #PAR_MIN})/2</code> and
+ * <code>ampl=({@value #PAR_MAX}-{@value #PAR_MIN})/2</code>.
+ * Calls {@link #add(int)} or {@link #remove} depending on whether the size
+ * needs to be increased or decreased to get the desired size.
+ * @return always false
+ */
+public boolean execute()
+{
+ long time = CommonState.getTime();
+ int amplitude = (maxsize - minsize) / 2;
+ int newsize = (maxsize + minsize) / 2 +
+ (int) (Math.sin(((double) time) / period * Math.PI) *
+ amplitude);
+ int diff = newsize - Network.size();
+ if (diff < 0)
+ remove(-diff);
+ else
+ add(diff);
+
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.core.*;
+import peersim.config.Configuration;
+
+/**
+ * Initializes the neighbor list of a node with random links.
+ */
+public class RandNI implements NodeInitializer {
+
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * The protocol to operate on.
+ * @config
+ */
+private static final String PAR_PROT = "protocol";
+
+/**
+ * The number of samples (with replacement) to draw to initialize the
+ * neighbor list of the node.
+ * @config
+ */
+private static final String PAR_DEGREE = "k";
+
+/**
+ * If this config property is defined, method {@link Linkable#pack()} is
+ * invoked on the specified protocol at the end of the wiring phase.
+ * Default to false.
+ * @config
+ */
+private static final String PAR_PACK = "pack";
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+/**
+ * The protocol we want to wire
+ */
+private final int pid;
+
+/**
+ * The degree of the regular graph
+ */
+private final int k;
+
+/**
+ * If true, method pack() is invoked on the initialized protocol
+ */
+private final boolean pack;
+
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters. Invoked by the
+ * simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public RandNI(String prefix)
+{
+ pid = Configuration.getPid(prefix + "." + PAR_PROT);
+ k = Configuration.getInt(prefix + "." + PAR_DEGREE);
+ pack = Configuration.contains(prefix + "." + PAR_PACK);
+}
+
+//--------------------------------------------------------------------------
+//Methods
+//--------------------------------------------------------------------------
+
+/**
+ * Takes {@value #PAR_DEGREE} random samples with replacement from the nodes of
+ * the overlay network. No loop edges are added.
+ */
+public void initialize(Node n)
+{
+ if (Network.size() == 0) return;
+
+ Linkable linkable = (Linkable) n.getProtocol(pid);
+ for (int j = 0; j < k; ++j)
+ {
+ int r = CommonState.r.nextInt(Network.size());
+ linkable.addNeighbor(Network.get(r));
+ }
+
+ if (pack) linkable.pack();
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.core.*;
+import peersim.config.Configuration;
+
+/**
+ * Initializes a node's neighbor list with a fixed center.
+ */
+public class StarNI implements NodeInitializer {
+
+
+// ========================= fields =================================
+// ==================================================================
+
+
+/**
+ * The protocol to operate on.
+ * @config
+ */
+private static final String PAR_PROT = "protocol";
+
+/**
+ * If this config property is defined, method {@link Linkable#pack()} is
+ * invoked on the specified protocol at the end of the wiring phase.
+ * Default to false.
+ * @config
+ */
+private static final String PAR_PACK = "pack";
+
+/**
+ * The protocol we want to wire
+ */
+protected final int pid;
+
+/** If true, method pack() is invoked on the initialized protocol */
+protected final boolean pack;
+
+/**
+ * Used as center: nodes will link to this node.
+ */
+protected Node center = null;
+
+
+// ==================== initialization ==============================
+//===================================================================
+
+
+public StarNI(String prefix) {
+
+ pid = Configuration.getPid(prefix+"."+PAR_PROT);
+ pack = Configuration.contains(prefix+"."+PAR_PACK);
+}
+
+
+// ===================== public methods ==============================
+// ===================================================================
+
+
+/**
+ * Adds a link to a fixed node, the center. This fixed node remains the same
+ * throughout consecutive calls to this method. If the center fails in the
+ * meantime, a new one is chosen so care should be taken. The center is the
+ * first node that is not down (starting from node 0, 1, etc)
+ * at the time of the first call to the function. When a new center needs to
+ * be chosen (the current one is down), the new center is again the first
+ * one which is up. If no nodes are up, the node with the last index is set
+ * as center, and selection of a new center is always attempted when this
+ * method is called again.
+ */
+public void initialize(Node n) {
+
+ if( Network.size() == 0 ) return;
+
+ for(int i=0; (center==null || !center.isUp()) && i<Network.size(); ++i)
+ center=Network.get(i);
+
+ ((Linkable)n.getProtocol(pid)).addNeighbor(center);
+
+ if(pack)
+ {
+ ((Linkable)n.getProtocol(pid)).pack();
+ }
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.graph.*;
+import peersim.core.*;
+import peersim.config.*;
+import java.lang.reflect.*;
+import java.util.ArrayList;
+
+/**
+ * Takes a {@link Linkable} protocol and adds connections using an arbitrary
+ * method.
+ * No connections are removed.
+ * The connections are added by an arbitrary method that can be specified
+ * in the configuration.
+ * The properties the method has to fulfill are the following
+ <ul>
+ <li>It MUST be static</li>
+ <li>It MUST have a first argument that can be assigned from a class
+ that implements {@link Graph}.</li>
+ <li>It MAY contain zero or more arguments following the first one.</li>
+ <li>All the arguments after the first one MUST be of primitive type int,
+ long or double, except
+ the last one, which MAY be of type that can be assigned from
+ <code>java.util.Random</code></li>
+ </ul>
+ The arguments are initialized using the configuration as follows.
+ <ul>
+ <li>The first argument is the {@link Graph} that is passed to
+ {@link #wire}.</li>
+ <li>The arguments after the first one (indexed as 1,2,etc) are initialized
+ from configuration parameters of the form {@value #PAR_ARG}i, where i is the
+ index.
+ <li>If the last argument can be assigned from
+ <code>java.util.Random</code> then it is initialized with
+ {@link CommonState#r}, the central source of randomness for the
+ simulator.</li>
+ </ul>
+ For example, the class {@link WireWS} can be emulated by configuring
+ <pre>
+ init.0 WireByMethod
+ init.0.class GraphFactory
+ init.0.method wireWS
+ init.0.arg1 10
+ init.0.arg2 0.1
+ ...
+ </pre>
+ Note that the {@value #PAR_CLASS} parameter defaults to {@link GraphFactory},
+ and {@value #PAR_METHOD} defaults to "wire".
+ */
+public class WireByMethod extends WireGraph {
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * The prefix for the configuration properties that describe parameters.
+ * @config
+ */
+private static final String PAR_ARG = "arg";
+
+/**
+* The class that has the method we want to use. Defaults to
+* {@link GraphFactory}.
+* @config
+*/
+private static final String PAR_CLASS = "class";
+
+/**
+* The name of the method for wiring the graph. Defaults to <code>wire</code>.
+* @config
+*/
+private static final String PAR_METHOD = "method";
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+private final Object[] args;
+
+private final Method method;
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Loads configuration. It verifies the constraints defined
+ * in {@link WireByMethod}.
+ * @param prefix the configuration prefix for this class
+ */
+public WireByMethod(String prefix)
+{
+ super(prefix);
+
+ // get the method
+ try
+ {
+ final Class wire =
+ Configuration.getClass(prefix + "." + PAR_CLASS,
+ Class.forName("peersim.graph.GraphFactory"));
+ method = WireByMethod.getMethod(
+ wire,
+ Configuration.getString(prefix+"."+PAR_METHOD,"wire"));
+ }
+ catch( Exception e )
+ {
+ throw new RuntimeException(e);
+ }
+
+ // set the constant args (those other than 0th)
+ Class[] argt = method.getParameterTypes();
+ args = new Object[argt.length];
+ for(int i=1; i<args.length; ++i)
+ {
+
+ if( argt[i]==int.class )
+ args[i]=Configuration.getInt(prefix+"."+PAR_ARG+i);
+ else if( argt[i]==long.class )
+ args[i]=Configuration.getLong(prefix+"."+PAR_ARG+i);
+ else if( argt[i]==double.class )
+ args[i]=Configuration.getDouble(prefix+"."+PAR_ARG+i);
+ else if(i==args.length-1 && argt[i].isInstance(CommonState.r))
+ args[i]=CommonState.r;
+ else
+ {
+ // we should neve get here
+ throw new RuntimeException("Unexpected error, please "+
+ "report this problem to the peersim team");
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Search a wiring method in the specified class.
+ * @param cl
+ * the class where to find the method
+ * @param methodName
+ * the method to be searched
+ * @return the requested method, if it fully conforms to the definition of
+ * the wiring methods.
+ */
+private static Method getMethod(Class cl, String methodName)
+ throws NoSuchMethodException, ClassNotFoundException {
+
+ // Search methods
+ Method[] methods = cl.getMethods();
+ ArrayList<Method> list = new ArrayList<Method>();
+ for (Method m: methods) {
+ if (m.getName().equals(methodName)) {
+ list.add(m);
+ }
+ }
+
+ if (list.size() == 0) {
+ throw new NoSuchMethodException("No method "
+ + methodName + " in class " + cl.getSimpleName());
+ } else if (list.size() > 1) {
+ throw new NoSuchMethodException("Multiple methods called "
+ + methodName + " in class " + cl.getSimpleName());
+ }
+
+ // Found a single method with the right name; check if
+ // it is a setter.
+ final Class graphClass = Class.forName("peersim.graph.Graph");
+ final Class randomClass = Class.forName("java.util.Random");
+ Method method = list.get(0);
+ Class[] pars = method.getParameterTypes();
+ if( pars.length < 1 || !pars[0].isAssignableFrom(graphClass) )
+ throw new NoSuchMethodException(method.getName() + " of class "
+ + cl.getSimpleName() + " is not a valid graph wiring method,"+
+ " it has to have peersim.graph.Graph as first argument type");
+ for(int i=1; i<pars.length; ++i)
+ if( !( pars[i]==int.class || pars[i]==long.class ||
+ pars[i]==double.class ||
+ (i==pars.length-1 && pars[i].isAssignableFrom(randomClass)) ) )
+ throw new NoSuchMethodException(method.getName() +
+ " of class "+ cl.getSimpleName()
+ + " is not a valid graph wiring method");
+
+ if(method.toString().indexOf("static")<0)
+ throw new NoSuchMethodException(method.getName() +
+ " of class "+ cl.getSimpleName()
+ + " is not a valid graph wiring method; it is not static");
+
+ return method;
+}
+
+
+//--------------------------------------------------------------------------
+//Methods
+//--------------------------------------------------------------------------
+
+/** Invokes the method passing g to it.*/
+public void wire(Graph g) {
+
+ args[0]=g;
+ try { method.invoke(null,args); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+
+import java.io.IOException;
+import java.io.FileReader;
+import java.io.LineNumberReader;
+import java.util.StringTokenizer;
+import peersim.graph.Graph;
+import peersim.core.*;
+import peersim.config.Configuration;
+
+/**
+* Takes a {@link Linkable} protocol and adds connections that are stored in a
+* file. Note that no
+* connections are removed, they are only added. So it can be used in
+* combination with other initializers.
+* The format of the file is as follows. Each line begins with a node ID
+* (IDs start from 0) followed by a list of neighbors, separated by whitespace.
+* All node IDs larger than the actual network size will be discarded, but
+* it does not trigger an error. Lines starting with a "#" character and
+* empty lines are ignored.
+*/
+public class WireFromFile extends WireGraph {
+
+
+// ========================= fields =================================
+// ==================================================================
+
+
+/**
+* The filename to load links from.
+* @config
+*/
+private static final String PAR_FILE = "file";
+
+/**
+* The number of neighbors to be read from the file. If unset, the default
+* behavior is to read all links in the file. If set, then the first k
+* neighbors will be read only.
+* @config
+*/
+private static final String PAR_K = "k";
+
+private final String file;
+
+private final int k;
+
+// ==================== initialization ==============================
+// ==================================================================
+
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public WireFromFile(String prefix) {
+
+ super(prefix);
+ file = Configuration.getString(prefix+"."+PAR_FILE);
+ k = Configuration.getInt(prefix + "." + PAR_K, Integer.MAX_VALUE);
+}
+
+
+// ===================== public methods ==============================
+// ===================================================================
+
+
+/**
+* Wires the graph from a file.
+* The format of the file is as follows. Each line begins with a node ID
+* (IDs start from 0) followed by a list of neighbors, separated by whitespace.
+* All node IDs larger than the actual network size will be discarded, but
+* it does not trigger an error. Lines starting with a "#" character and
+* empty lines are ignored.
+*/
+public void wire(Graph g) {
+try
+{
+ FileReader fr = new FileReader(file);
+ LineNumberReader lnr = new LineNumberReader(fr);
+ String line;
+ boolean wasOutOfRange=false;
+ while((line=lnr.readLine()) != null)
+ {
+ if( line.startsWith("#") ) continue;
+ StringTokenizer st = new StringTokenizer(line);
+ if(!st.hasMoreTokens()) continue;
+
+ final int from = Integer.parseInt(st.nextToken());
+ if( from < 0 || from >= Network.size() )
+ {
+ wasOutOfRange = true;
+ continue;
+ }
+
+ for(int i=0; i<k && st.hasMoreTokens(); ++i)
+ {
+ final int to = Integer.parseInt(st.nextToken());
+ if( to < 0 || to >= Network.size() )
+ wasOutOfRange = true;
+ else
+ g.setEdge(from,to);
+ }
+ }
+
+ if( wasOutOfRange )
+ System.err.println("WireFromFile warning: in "+file+" "+
+ "some nodes were out of range and so ignored.");
+ lnr.close();
+}
+catch( IOException e )
+{
+ throw new RuntimeException(e);
+}
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.graph.Graph;
+import peersim.core.*;
+import peersim.config.Configuration;
+
+/**
+ * This class is the superclass of classes that
+ * takes a {@link Linkable} protocol or a graph and add edges that define a
+ * certain topology.
+ * Note that no connections are removed, they are only added. So it can be used
+ * in combination with other initializers.
+ */
+public abstract class WireGraph implements Control
+{
+
+// --------------------------------------------------------------------------
+// Parameters
+// --------------------------------------------------------------------------
+
+/**
+ * The {@link Linkable} protocol to operate on. If it is not specified,
+ * then operates on {@link #g}. If {@link #g} is null, {@link #execute} throws
+ * an Exception. Note that if {@link #g} is set, it will be used irrespective
+ * of the setting of the protocol in this field.
+ * @config
+ */
+private static final String PAR_PROT = "protocol";
+
+/**
+ * If this config property is defined, method {@link Linkable#pack()} is
+ * invoked on the specified protocol at the end of the wiring phase.
+ * Default to false.
+ * @config
+ */
+private static final String PAR_PACK = "pack";
+
+/**
+ * If set, the generated graph is undirected. In other words, for each link
+ * (i,j) a link (j,i) will also be added. Defaults to false.
+ * @config
+ */
+private static final String PAR_UNDIR = "undir";
+
+/**
+* Alias for {@value #PAR_UNDIR}.
+* @config
+*/
+private static final String PAR_UNDIR_ALT = "undirected";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/**
+ * The protocol we want to wire. It is negative if no protocol was set
+ * (in that case, a graph must be specified, see {@link #g}).
+ */
+protected final int pid;
+
+/** If true, method pack() is invoked on the initialized protocol */
+private final boolean pack;
+
+/** If true, edges are added in an undirected fashion.*/
+public final boolean undir;
+
+/**
+* If set (not null), this is the graph to wire. If null, the current overlay
+* is wired each time {@link #execute} is called, as specified by {@value
+* #PAR_PROT}.
+*/
+public Graph g=null;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters. Normally
+ * invoked by the simulation engine.
+ * @param prefix
+ * the configuration prefix for this class
+ */
+protected WireGraph(String prefix) {
+
+ if( Configuration.contains(prefix + "." + PAR_PROT) )
+ pid = Configuration.getPid(prefix + "." + PAR_PROT);
+ else
+ pid = -10;
+ pack = Configuration.contains(prefix + "." + PAR_PACK);
+ undir = (Configuration.contains(prefix + "." + PAR_UNDIR) |
+ Configuration.contains(prefix + "." + PAR_UNDIR_ALT));
+}
+
+
+//--------------------------------------------------------------------------
+//Public methods
+//--------------------------------------------------------------------------
+
+/**
+* Calls method {@link #wire} with the graph {@link #g},
+* or if null, on the overlay specified by the protocol given by config
+* parameter {@value #PAR_PROT}. If neither {@link #g}, nor {@value #PAR_PROT}
+* is set, throws a RuntimException.
+*/
+public final boolean execute() {
+
+ Graph gr;
+ if(g==null && pid==-10)
+ {
+ throw new RuntimeException(
+ "Neither a protocol, nor a graph is specified.");
+ }
+ if(g==null) gr = new OverlayGraph(pid,!undir);
+ else gr=g;
+
+ if(gr.size()==0) return false;
+ wire(gr);
+
+ if( g==null && pack)
+ {
+ int size = Network.size();
+ for (int i = 0; i < size; i++)
+ {
+ Linkable link =
+ (Linkable) Network.get(i).getProtocol(pid);
+ link.pack();
+ }
+ }
+ return false;
+}
+
+//--------------------------------------------------------------------------
+
+/** The method that should wire (add edges to) the given graph. Has to
+* be implemented by extending classes */
+public abstract void wire(Graph g);
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.graph.*;
+import peersim.core.*;
+import peersim.config.*;
+
+/**
+ * Takes a {@link Linkable} protocol and adds random connections. Note that no
+ * connections are removed, they are only added. So it can be used in
+ * combination with other initializers.
+ * @see GraphFactory#wireKOut
+ */
+public class WireKOut extends WireGraph {
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * The number of outgoing edges to generate from each node.
+ * Passed to {@link GraphFactory#wireKOut}.
+ * No loop edges are generated.
+ * In the undirected case, the degree
+ * of nodes will be on average almost twice as much because the incoming links
+ * also become links out of each node.
+ * @config
+ */
+private static final String PAR_DEGREE = "k";
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+/**
+ * The number of outgoing edges to generate from each node.
+ */
+private final int k;
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public WireKOut(String prefix)
+{
+ super(prefix);
+ k = Configuration.getInt(prefix + "." + PAR_DEGREE);
+}
+
+//--------------------------------------------------------------------------
+//Methods
+//--------------------------------------------------------------------------
+
+/** Calls {@link GraphFactory#wireKOut}. */
+public void wire(Graph g) {
+
+ GraphFactory.wireKOut(g,k,CommonState.r);
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.graph.*;
+import peersim.config.*;
+
+/**
+ * Takes a {@link peersim.core.Linkable} protocol and adds connections that
+ * define a regular
+ * rooted tree. Note that no
+ * connections are removed, they are only added. So it can be used in
+ * combination with other initializers.
+ * @see #wire
+ * @see GraphFactory#wireRegRootedTree
+ */
+public class WireRegRootedTree extends WireGraph {
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * The parameter of the tree wiring method.
+ * It is passed to {@link GraphFactory#wireRegRootedTree}.
+ * @config
+ */
+private static final String PAR_DEGREE = "k";
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+private final int k;
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public WireRegRootedTree(String prefix)
+{
+ super(prefix);
+ k = Configuration.getInt(prefix + "." + PAR_DEGREE);
+}
+
+//--------------------------------------------------------------------------
+//Methods
+//--------------------------------------------------------------------------
+
+/** Calls {@link GraphFactory#wireRegRootedTree}. */
+public void wire(Graph g) {
+
+ GraphFactory.wireRegRootedTree(g,k);
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.graph.*;
+import peersim.config.Configuration;
+
+/**
+ * Takes a {@link peersim.core.Linkable} protocol and adds edges that
+ * define a ring lattice.
+ * Note that no connections are removed, they are only added. So it can be used
+ * in combination with other initializers.
+ * @see GraphFactory#wireRingLattice
+ */
+public class WireRingLattice extends WireGraph {
+
+// --------------------------------------------------------------------------
+// Parameters
+// --------------------------------------------------------------------------
+
+/**
+ * The "lattice parameter" of the graph. The out-degree of the graph is equal to
+ * 2k. See {@link GraphFactory#wireRingLattice} (to which this parameter is
+ * passed) for further details.
+ * @config
+ */
+private static final String PAR_K = "k";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/**
+ */
+private final int k;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public WireRingLattice(String prefix)
+{
+ super(prefix);
+ k = Configuration.getInt(prefix + "." + PAR_K);
+}
+
+//--------------------------------------------------------------------------
+//Public methods
+//--------------------------------------------------------------------------
+
+/** calls {@link GraphFactory#wireRingLattice}. */
+public void wire(Graph g)
+{
+ GraphFactory.wireRingLattice(g, k);
+}
+
+//--------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.graph.*;
+
+/**
+* This class contains the implementation of the Barabasi-Albert model
+* of growing scale free networks. The original model is described in
+* <a href="http://arxiv.org/abs/cond-mat/0106096">http://arxiv.org/abs/cond-mat/0106096</a>. It also contains the option of building
+* a directed network, in which case the model is a variation of the BA model
+* described in <a href="http://arxiv.org/pdf/cond-mat/0408391">
+http://arxiv.org/pdf/cond-mat/0408391</a>. In both cases, the number of the
+* initial set of nodes is the same as the degree parameter, and no links are
+* added. The first added node is connected to all of the initial nodes,
+* and after that the BA model is used normally.
+* @see GraphFactory#wireScaleFreeBA
+*/
+public class WireScaleFreeBA extends WireGraph {
+
+
+// ================ constants ============================================
+// =======================================================================
+
+/**
+ * The number of edges added to each new node (apart from those forming the
+ * initial network).
+ * Passed to {@link GraphFactory#wireScaleFreeBA}.
+ * @config
+ */
+private static final String PAR_DEGREE = "k";
+
+
+// =================== fields ============================================
+// =======================================================================
+
+/** Parameter of the BA model. */
+private int k;
+
+// ===================== initialization ==================================
+// =======================================================================
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+*/
+public WireScaleFreeBA(String prefix)
+{
+ super(prefix);
+ k = Configuration.getInt(prefix + "." + PAR_DEGREE);
+}
+
+
+// ======================== methods =======================================
+// ========================================================================
+
+
+/** calls {@link GraphFactory#wireScaleFreeBA}.*/
+public void wire(Graph g) {
+
+ GraphFactory.wireScaleFreeBA(g,k,CommonState.r );
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.graph.Graph;
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * Wires a scale free graph using a method described in
+ * <a href="http://xxx.lanl.gov/abs/cond-mat/0106144">this paper</a>.
+ * It is an incremental technique, where the new nodes are connected to
+ * the two ends of an edge that is already in the network.
+ * This model always wires undirected links.
+ */
+public class WireScaleFreeDM extends WireGraph {
+
+
+//--------------------------------------------------------------------------
+// Constants
+//--------------------------------------------------------------------------
+
+/**
+ * The number of edges added to each new
+ * node (apart from those forming the initial network) is twice this
+ * value.
+ * @config
+ */
+private static final String PAR_EDGES = "k";
+
+
+//--------------------------------------------------------------------------
+// Fields
+//--------------------------------------------------------------------------
+
+
+/** The number of edges created for a new node is 2*k. */
+private final int k;
+
+
+//--------------------------------------------------------------------------
+// Constructor
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public WireScaleFreeDM(String prefix)
+{
+ super(prefix);
+ k = Configuration.getInt(prefix + "." + PAR_EDGES);
+}
+
+
+//--------------------------------------------------------------------------
+// Methods
+//--------------------------------------------------------------------------
+
+/**
+ * Wires a scale free graph using a method described in
+ * <a href="http://xxx.lanl.gov/abs/cond-mat/0106144">this paper</a>.
+ * It is an incremental technique, where the new nodes are connected to
+ * the two ends of an edge that is already in the network.
+ * This model always wires undirected links.
+*/
+public void wire(Graph g) {
+
+ int nodes=g.size();
+ int[] links = new int[4*k*nodes];
+
+ // Initial number of nodes connected as a clique
+ int clique = (k > 3 ? k : 3);
+
+ // Add initial edges, to form a clique
+ int len=0;
+ for (int i=0; i < clique; i++)
+ for (int j=0; j < clique; j++)
+ {
+ if (i != j)
+ {
+ g.setEdge(i,j);
+ g.setEdge(j,i);
+ links[len*2] = i;
+ links[len*2+1] = j;
+ len++;
+ }
+ }
+
+ for (int i=clique; i < nodes; i++)
+ for (int l=0; l < k; l++)
+ {
+ int edge = CommonState.r.nextInt(len);
+ int m = links[edge*2];
+ int j = links[edge*2+1];
+ g.setEdge(i, m);
+ g.setEdge(m, i);
+ g.setEdge(j, m);
+ g.setEdge(m, j);
+ links[len*2] = i;
+ links[len*2+1] = m;
+ len++;
+ links[len*2] = j;
+ links[len*2+1] = m;
+ len++;
+ }
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.graph.*;
+
+/**
+ * Takes a {@link peersim.core.Linkable} protocol and adds connection
+ * which for a star
+ * topology. No connections are removed, they are only added. So it can be used
+ * in combination with other initializers.
+ * @see GraphFactory#wireStar
+ */
+public class WireStar extends WireGraph {
+
+// ===================== initialization ==============================
+// ===================================================================
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public WireStar(String prefix) { super(prefix); }
+
+// ===================== public methods ==============================
+// ===================================================================
+
+
+/** Calls {@link GraphFactory#wireStar}.*/
+public void wire(Graph g) {
+
+ GraphFactory.wireStar(g);
+}
+
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.dynamics;
+
+import peersim.graph.*;
+import peersim.core.*;
+import peersim.config.Configuration;
+
+/**
+* Takes a {@link Linkable} protocol and adds connections following the
+* small-world model of Watts and Strogatz. Note that no
+* connections are removed, they are only added. So it can be used in
+* combination with other initializers.
+* @see GraphFactory#wireWS
+*/
+public class WireWS extends WireGraph {
+
+
+// ========================= fields =================================
+// ==================================================================
+
+/**
+ * The beta parameter of a Watts-Strogatz graph represents the probability for a
+ * node to be re-wired.
+ * Passed to {@link GraphFactory#wireWS}.
+ * @config
+ */
+private static final String PAR_BETA = "beta";
+
+/**
+ * The degree of the graph. See {@link GraphFactory#wireRingLattice}.
+ * Passed to {@link GraphFactory#wireWS}.
+ * @config
+ */
+private static final String PAR_DEGREE = "k";
+
+/**
+ * The degree of the regular graph
+ */
+private final int k;
+
+/**
+ * The degree of the regular graph
+ */
+private final double beta;
+
+
+// ==================== initialization ==============================
+//===================================================================
+
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public WireWS(String prefix) {
+
+ super(prefix);
+ k = Configuration.getInt(prefix+"."+PAR_DEGREE);
+ beta = Configuration.getDouble(prefix+"."+PAR_BETA);
+}
+
+
+// ===================== public methods ==============================
+// ===================================================================
+
+
+/** calls {@link GraphFactory#wireWS}.*/
+public void wire(Graph g) {
+
+ GraphFactory.wireWS(g,k,beta,CommonState.r);
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import peersim.core.*;
+import peersim.cdsim.CDProtocol;
+import peersim.config.*;
+import peersim.dynamics.NodeInitializer;
+
+/**
+ * Schedules the first execution of the cycle based protocol instances in the
+ * event driven engine. It implements {@link Control} but it will most often be
+ * invoked only once for each protocol as an initializer, since the scheduled
+ * events schedule themselves for the consecutive executions (see
+ * {@link NextCycleEvent}).
+ *
+ * <p>
+ * All {@link CDProtocol} specifications in the configuration need to contain a
+ * {@link Scheduler} specification at least for the step size (see config
+ * parameter {@value peersim.core.Scheduler#PAR_STEP} of {@link Scheduler}).
+ * This value is used as the cycle length for the corresponding protocol.
+ *
+ * @see NextCycleEvent
+ */
+public class CDScheduler implements Control, NodeInitializer {
+
+ // ============================== fields ==============================
+ // ====================================================================
+
+ /**
+ * Parameter that is used to define the class that is used to schedule the
+ * next cycle. Its type is (or extends) {@link NextCycleEvent}. Defaults to
+ * {@link NextCycleEvent}.
+ *
+ * @config
+ */
+ private static final String PAR_NEXTC = "nextcycle";
+
+ /**
+ * The protocols that this scheduler schedules for the first execution. It
+ * might contain several protocol names, separated by whitespace. All
+ * protocols will be scheduled based on the common parameter set for this
+ * scheduler and the parameters of the protocol (cycle length). Protocols
+ * are scheduled independently of each other.
+ *
+ * @config
+ */
+ private static final String PAR_PROTOCOL = "protocol";
+
+ /**
+ * If set, it means that the initial execution of the given protocol is
+ * scheduled for a different random time for all nodes. The random time is a
+ * sample between the current time (inclusive) and the cycle length
+ * (exclusive), the latter being specified by the step parameter (see
+ * {@link Scheduler}) of the assigned protocol.
+ *
+ * @see #execute
+ * @config
+ */
+ private static final String PAR_RNDSTART = "randstart";
+
+ /**
+ * Contains the scheduler objects for all {@link CDProtocol}s defined in the
+ * configuration. The length of the array is the number of protocols
+ * defined, but those entries that belong to protocols that are not
+ * {@link CDProtocol}s are null.
+ */
+ public static final Scheduler[] sch;
+
+ private final NextCycleEvent[] nce;
+
+ private final int[] pid;
+
+ private final boolean randstart;
+
+ // =============================== initialization ======================
+ // =====================================================================
+
+ /**
+ * Loads protocol schedulers for all protocols.
+ */
+ static {
+
+ String[] names = Configuration.getNames(Node.PAR_PROT);
+ sch = new Scheduler[names.length];
+ for (int i = 0; i < names.length; ++i) {
+ if (Network.prototype.getProtocol(i) instanceof CDProtocol)
+ // with no default values for step to avoid
+ // "overscheduling" due to lack of step option.
+ sch[i] = new Scheduler(names[i], false);
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Initialization based on configuration parameters.
+ */
+ public CDScheduler(String n) {
+ String[] prots = Configuration.getString(n + "." + PAR_PROTOCOL).split("\\s");
+ pid = new int[prots.length];
+ nce = new NextCycleEvent[prots.length];
+ for (int i = 0; i < prots.length; ++i) {
+ pid[i] = Configuration.lookupPid(prots[i]);
+ if (!(Network.prototype.getProtocol(pid[i]) instanceof CDProtocol)) {
+ throw new IllegalParameterException(n + "." + PAR_PROTOCOL,
+ "Only CDProtocols are accepted here");
+ }
+ nce[i] = (NextCycleEvent) Configuration.getInstance(n + "."
+ + PAR_NEXTC, new NextCycleEvent(null));
+ }
+ randstart = Configuration.contains(n + "." + PAR_RNDSTART);
+ }
+
+ // ========================== methods ==================================
+ // =====================================================================
+
+ /**
+ * Schedules the protocol at all nodes for the first execution adding it to
+ * the priority queue of the event driven simulation. The time of the first
+ * execution is determined by {@link #firstDelay}. The implementation calls
+ * {@link #initialize} for all nodes.
+ *
+ * @see #initialize
+ */
+ public boolean execute() {
+
+ for (int i = 0; i < Network.size(); ++i) {
+ initialize(Network.get(i));
+ }
+
+ return false;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Schedules the protocol at given node for the first execution adding it to
+ * the priority queue of the event driven simulation. The time of the first
+ * execution is determined by a reference point in time and
+ * {@link #firstDelay}, which defines the delay from the reference point.
+ * The reference point is the maximum of the current time, and the value of
+ * parameter {@value peersim.core.Scheduler#PAR_FROM} of the protocol being
+ * scheduled. If the calculated time of the first execution is not valid
+ * according to the schedule of the protocol then no execution is scheduled
+ * for that protocol.
+ * <p>
+ * A final note: for performance reasons, the recommended practice is not to
+ * use parameter {@value peersim.core.Scheduler#PAR_FROM} in protocols, but
+ * to schedule {@link CDScheduler} itself for the desired time, whenever
+ * possible (e.g., it is not possible if {@link CDScheduler} is used as a
+ * {@link NodeInitializer}).
+ */
+ public void initialize(Node n) {
+ /*
+ * XXX If "from" is not the current time and this is used as a control
+ * (not node initializer) then we dump _lots_ of events in the queue
+ * that are just stored there until "from" comes. This reduces
+ * performance, and should be fixed. When fixed, the final comment can
+ * be removed from the docs.
+ */
+
+ final long time = CommonState.getTime();
+ for (int i = 0; i < pid.length; ++i) {
+ Object nceclone = null;
+ try {
+ nceclone = nce[i].clone();
+ } catch (CloneNotSupportedException e) {
+ } // cannot possibly happen
+
+ final long delay = firstDelay(sch[pid[i]].step);
+ final long nexttime = Math.max(time, sch[pid[i]].from) + delay;
+ if (nexttime < sch[pid[i]].until)
+ EDSimulator.add(nexttime - time, nceclone, n, pid[i]);
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns the time (through giving the delay from the current time) when
+ * this even is first executed. If {@value #PAR_RNDSTART} is not set, it
+ * returns zero, otherwise a random value between 0, inclusive, and
+ * cyclelength, exclusive.
+ *
+ * @param cyclelength
+ * The cycle length of the cycle based protocol for which this
+ * method is called
+ */
+ protected long firstDelay(long cyclelength) {
+
+ if (randstart)
+ return CommonState.r.nextLong(cyclelength);
+ else
+ return 0;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import peersim.core.Control;
+import peersim.core.Scheduler;
+
+
+/**
+ * Wrapper for {@link Control}s to be executed in an event driven simulation.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.5 $
+ */
+class ControlEvent
+{
+
+//---------------------------------------------------------------------
+//Fields
+//---------------------------------------------------------------------
+
+/**
+ * The reference to the dynamics to be executed; null if this cycle event
+ * refers to an observer.
+ */
+private Control control;
+
+/** Order index used to maintain order between cycle-based events */
+private int order;
+
+
+//---------------------------------------------------------------------
+//Initialization
+//---------------------------------------------------------------------
+
+/**
+ * Scheduler object to obtain the next schedule time of this event
+ */
+private Scheduler scheduler;
+
+/**
+ * Creates a cycle event for a control object. It also schedules the object
+ * for the first execution adding it to the priority queue of the event driven
+ * simulation.
+ */
+public ControlEvent(Control control, Scheduler scheduler, int order)
+{
+ this.control = control;
+ this.order = order;
+ this.scheduler = scheduler;
+ long next = scheduler.getNext();
+ if( next>=0 ) EDSimulator.addControlEvent(next, order, this);
+}
+
+//---------------------------------------------------------------------
+//Methods
+//---------------------------------------------------------------------
+
+/**
+* Executes the control object, and schedules the object for the next execution
+* adding it to the priority queue of the event driven simulation.
+*/
+public boolean execute() {
+
+ boolean ret = control.execute();
+ long next = scheduler.getNext();
+ if( next>=0 ) EDSimulator.addControlEvent(next, order, this);
+ return ret;
+}
+
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import peersim.core.*;
+
+/**
+ * The interface to be implemented by protocols run under the event-driven
+ * model. A single method is provided, to deliver events to the protocol.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.5 $
+ */
+public interface EDProtocol
+extends Protocol
+{
+
+ /**
+ * This method is invoked by the scheduler to deliver events to the
+ * protocol. Apart from the event object, information about the node
+ * and the protocol identifier are also provided. Additional information
+ * can be accessed through the {@link CommonState} class.
+ *
+ * @param node the local node
+ * @param pid the identifier of this protocol
+ * @param event the delivered event
+ */
+ public void processEvent( Node node, int pid, Object event );
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import java.util.Arrays;
+
+import peersim.Simulator;
+import peersim.config.*;
+import peersim.core.*;
+import psgsim.PSGSimulator;
+
+/**
+ * Event-driven simulator engine. It is a fully static singleton class. For an
+ * event driven simulation the configuration has to describe a set of
+ * {@link Protocol}s, a set of {@link Control}s and their ordering and a set of
+ * initializers and their ordering. See parameters {@value #PAR_INIT},
+ * {@value #PAR_CTRL}.
+ * <p>
+ * One experiment run by {@link #nextExperiment} works as follows. First the
+ * initializers are run in the specified order. Then the first execution of all
+ * specified controls is scheduled in the event queue. This scheduling is
+ * defined by the {@link Scheduler} parameters of each control component. After
+ * this, the first event is taken from the event queue. If the event wraps a
+ * control, the control is executed, otherwise the event is delivered to the
+ * destination protocol, that must implement {@link EDProtocol}. This is
+ * iterated while the current time is less than {@value #PAR_ENDTIME} or the
+ * queue becomes empty. If more control events fall at the same time point, then
+ * the order given in the configuration is respected. If more non-control events
+ * fall at the same time point, they are processed in a random order.
+ * <p>
+ * The engine also provides the interface to add events to the queue. Note that
+ * this engine does not explicitly run the protocols. In all cases at least one
+ * control or initializer has to be defined that sends event(s) to protocols.
+ * <p>
+ * Controls can be scheduled (using the {@link Scheduler} parameters in the
+ * configuration) to run after the experiment has finished. That is, each
+ * experiment is finished by running the controls that are scheduled to be run
+ * after the experiment.
+ * <p>
+ * Any control can interrupt an experiment at any time it is executed by
+ * returning true in method {@link Control#execute}. However, the controls
+ * scheduled to run after the experiment are still executed completely,
+ * irrespective of their return value and even if the experiment was
+ * interrupted.
+ * <p>
+ * {@link CDScheduler} has to be mentioned that is a control that can bridge the
+ * gap between {@link peersim.cdsim} and the event driven engine. It can wrap
+ * {@link peersim.cdsim.CDProtocol} appropriately so that the execution of the
+ * cycles are scheduled in configurable ways for each node individually. In some
+ * cases this can add a more fine-grained control and more realism to
+ * {@link peersim.cdsim.CDProtocol} simulations, at the cost of some loss in
+ * performance.
+ * <p>
+ * When protocols at different nodes send messages to each other, they might
+ * want to use a model of the transport layer so that in the simulation message
+ * delay and message omissions can be modeled in a modular way. This
+ * functionality is implemented in package {@link peersim.transport}.
+ *
+ * @see Configuration
+ */
+public class EDSimulator {
+
+ // ---------------------------------------------------------------------
+ // Parameters
+ // ---------------------------------------------------------------------
+
+ /**
+ * The ending time for simulation. Only events that have a strictly smaller
+ * value are executed. It must be positive. Although in principle negative
+ * timestamps could be allowed, we assume time will be positive.
+ *
+ * @config
+ */
+ public static final String PAR_ENDTIME = "simulation.endtime";
+
+ /**
+ * This parameter specifies how often the simulator should log the current
+ * time on the standard error. The time is logged only if there were events
+ * in the respective interval, and only the time of some actual event is
+ * printed. That is, the actual log is not guaranteed to happen in identical
+ * intervals of time. It is merely a way of seeing whether the simulation
+ * progresses and how fast...
+ *
+ * @config
+ */
+ private static final String PAR_LOGTIME = "simulation.logtime";
+
+ /**
+ * This parameter specifies the event queue to be used. It must be an
+ * implementation of interface {@link PriorityQ}. If it is not defined, the
+ * internal implementation is used.
+ *
+ * @config
+ */
+ private static final String PAR_PQ = "simulation.eventqueue";
+
+ /**
+ * This is the prefix for initializers. These have to be of type
+ * {@link Control}. They are run at the beginning of each experiment, in the
+ * order specified by the configuration.
+ *
+ * @see Configuration
+ * @config
+ * @config
+ */
+ private static final String PAR_INIT = "init";
+
+ /**
+ * This is the prefix for {@link Control} components. They are run at the
+ * time points defined by the {@link Scheduler} associated to them. If some
+ * controls have to be executed at the same time point, they are executed in
+ * the order specified in the configuration.
+ *
+ * @see Configuration
+ * @config
+ */
+ private static final String PAR_CTRL = "control";
+
+ // ---------------------------------------------------------------------
+ // Fields
+ // ---------------------------------------------------------------------
+
+ /** Maximum time for simulation */
+ private static long endtime;
+
+ /** Log time */
+ private static long logtime;
+
+ /** holds the modifiers of this simulation */
+ private static Control[] controls = null;
+
+ /** Holds the control schedulers of this simulation */
+ private static Scheduler[] ctrlSchedules = null;
+
+ /** Ordered list of events (heap) */
+ private static PriorityQ heap = null;
+
+ private static long nextlog = 0;
+
+ // =============== initialization ======================================
+ // =====================================================================
+
+ /** to prevent construction */
+ private EDSimulator() {
+ }
+
+ // ---------------------------------------------------------------------
+ // Private methods
+ // ---------------------------------------------------------------------
+
+ /**
+ * Load and run initializers.
+ */
+ private static void runInitializers() {
+
+ Object[] inits = Configuration.getInstanceArray(PAR_INIT);
+ String names[] = Configuration.getNames(PAR_INIT);
+
+ for (int i = 0; i < inits.length; ++i) {
+
+ System.err.println("- Running initializer " + names[i] + ": "
+ + inits[i].getClass());
+ ((Control) inits[i]).execute();
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ private static void scheduleControls() {
+ // load controls
+ String[] names = Configuration.getNames(PAR_CTRL);
+ controls = new Control[names.length];
+ ctrlSchedules = new Scheduler[names.length];
+ for (int i = 0; i < names.length; ++i) {
+ controls[i] = (Control) Configuration.getInstance(names[i]);
+ ctrlSchedules[i] = new Scheduler(names[i], false);
+ }
+ System.err.println("EDSimulator: loaded controls "
+ + Arrays.asList(names));
+
+ // Schedule controls execution
+ if (controls.length > heap.maxPriority() + 1)
+ throw new IllegalArgumentException("Too many control objects");
+ for (int i = 0; i < controls.length; i++) {
+ new ControlEvent(controls[i], ctrlSchedules[i], i);
+ }
+ }
+
+ // ---------------------------------------------------------------------
+
+ /**
+ * Adds a new event to be scheduled, specifying the number of time units of
+ * delay, and the execution order parameter.
+ *
+ * @param time
+ * The actual time at which the next event should be scheduled.
+ * @param order
+ * The index used to specify the order in which control events
+ * should be executed, if they happen to be at the same time,
+ * which is typically the case.
+ * @param event
+ * The control event
+ */
+ static void addControlEvent(long time, int order, ControlEvent event) {
+ // we don't check whether time is negative or in the past: we trust
+ // the caller, which must be from this package
+ if (time >= endtime)
+ return;
+ heap.add(time, event, null, (byte) 0, order);
+ }
+
+ // ---------------------------------------------------------------------
+
+ /**
+ * This method is used to check whether the current configuration can be
+ * used for event driven simulations. It checks for the existence of config
+ * parameter {@value #PAR_ENDTIME}.
+ */
+ public static final boolean isConfigurationEventDriven() {
+ return Configuration.contains(PAR_ENDTIME);
+ }
+
+ // ---------------------------------------------------------------------
+
+ /**
+ * Execute and remove the next event from the ordered event list.
+ *
+ * @return true if the execution should be stopped.
+ */
+ private static boolean executeNext() {
+ PriorityQ.Event ev = heap.removeFirst();
+ if (ev == null) {
+ System.err.println("EDSimulator: queue is empty, quitting"
+ + " at time " + CommonState.getTime());
+ return true;
+ }
+
+ long time = ev.time;
+
+ if (time >= nextlog) {
+ // System.err.println("Current time: " + time);
+ // seemingly complicated: to prevent overflow
+ while (time - nextlog >= logtime)
+ nextlog += logtime;
+ if (endtime - nextlog >= logtime)
+ nextlog += logtime;
+ else
+ nextlog = endtime;
+ }
+ if (time >= endtime) {
+ System.err.println("EDSimulator: reached end time, quitting,"
+ + " leaving " + heap.size()
+ + " unprocessed events in the queue");
+ return true;
+ }
+
+ CommonState.setTime(time);
+ int pid = ev.pid;
+ if (ev.node == null) {
+ // might be control event; handled through a special method
+ ControlEvent ctrl = null;
+ try {
+ ctrl = (ControlEvent) ev.event;
+ } catch (ClassCastException e) {
+ throw new RuntimeException(
+ "No destination specified (null) for event " + ev);
+ }
+ return ctrl.execute();
+ } else if (ev.node != Network.prototype && ev.node.isUp()) {
+ CommonState.setPid(pid);
+ CommonState.setNode(ev.node);
+ if (ev.event instanceof NextCycleEvent) {
+ NextCycleEvent nce = (NextCycleEvent) ev.event;
+ nce.execute();
+ } else {
+ EDProtocol prot = null;
+ try {
+ prot = (EDProtocol) ev.node.getProtocol(pid);
+ // System.out.println("prot "+prot.getClass().getName());
+ } catch (ClassCastException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException("Protocol "
+ + Configuration.lookupPid(pid)
+ + " does not implement EDProtocol; "
+ + ev.event.getClass());
+ }
+ prot.processEvent(ev.node, pid, ev.event);
+ }
+ }
+
+ return false;
+ }
+
+ // ---------------------------------------------------------------------
+ // Public methods
+ // ---------------------------------------------------------------------
+
+ /**
+ * Runs an experiment, resetting everything except the random seed.
+ */
+ public static void nextExperiment() {
+ // Reading parameter
+ if (Configuration.contains(PAR_PQ))
+ heap = (PriorityQ) Configuration.getInstance(PAR_PQ);
+ else
+ heap = new Heap();
+ endtime = Configuration.getLong(PAR_ENDTIME);
+ if (CommonState.getEndTime() < 0) // not initialized yet
+ CommonState.setEndTime(endtime);
+ if (heap.maxTime() < endtime)
+ throw new IllegalParameterException(PAR_ENDTIME,
+ "End time is too large: configured event queue only"
+ + " supports " + heap.maxTime());
+ logtime = Configuration.getLong(PAR_LOGTIME, Long.MAX_VALUE);
+
+ // initialization
+ System.err.println("EDSimulator: resetting");
+ CommonState.setPhase(CommonState.PHASE_UNKNOWN);
+ CommonState.setTime(0); // needed here
+ controls = null;
+ ctrlSchedules = null;
+ nextlog = 0;
+ Network.reset();
+ System.err.println("EDSimulator: running initializers");
+ runInitializers();
+ scheduleControls();
+ // Perform the actual simulation; executeNext() will tell when to
+ // stop.
+ boolean exit = false;
+ while (!exit) {
+ exit = executeNext();
+ }
+
+ // analysis after the simulation
+ CommonState.setPhase(CommonState.POST_SIMULATION);
+ for (int j = 0; j < controls.length; ++j) {
+ if (ctrlSchedules[j].fin)
+ controls[j].execute();
+ }
+
+ }
+
+ // ---------------------------------------------------------------------
+
+ /**
+ * Adds a new event to be scheduled, specifying the number of time units of
+ * delay, and the node and the protocol identifier to which the event will
+ * be delivered.
+ *
+ * @param delay
+ * The number of time units before the event is scheduled. Has to
+ * be non-negative.
+ * @param event
+ * The object associated to this event
+ * @param node
+ * The node associated to the event.
+ * @param pid
+ * The identifier of the protocol to which the event will be
+ * delivered
+ */
+ public static void add(long delay, Object event, Node node, int pid) {
+ //if (event instanceof NextCycleEvent)
+ //System.err.println("************* edsim delay="+delay +" pid="+pid+" event="+event+" time="+CommonState.getTime());
+ if (Simulator.getSimID() == 2){
+ PSGSimulator.add(delay, event, node, pid);
+ }
+
+ else {
+ if (delay < 0)
+ throw new IllegalArgumentException("Protocol "
+ + node.getProtocol(pid) + " is trying to add event "
+ + event + " with a negative delay: " + delay);
+ if (pid > Byte.MAX_VALUE)
+ throw new IllegalArgumentException(
+ "This version does not support more than "
+ + Byte.MAX_VALUE + " protocols");
+
+ long time = CommonState.getTime();
+ if (endtime - time > delay) // check like this to deal with overflow
+ heap.add(time + delay, event, node, (byte) pid);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2001 The Anthill Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import peersim.core.Node;
+import peersim.core.CommonState;
+import peersim.config.Configuration;
+import peersim.config.IllegalParameterException;
+
+/**
+ * The Heap data structure used to maintain events "sorted" by
+ * scheduled time and to obtain the next event to be executed.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.10 $
+ */
+public class Heap implements PriorityQ {
+
+//--------------------------------------------------------------------------
+// Constants
+//--------------------------------------------------------------------------
+
+/**
+ * This parameter specifies how many
+ * bits are used to order events that occur at the same time. Defaults
+ * to 8. A value smaller than 8 causes an IllegalParameterException.
+ * Higher values allow for a better discrimination, but reduce
+ * the maximal time steps that can be simulated.
+ * @config
+ */
+private static final String PAR_PBITS = "pbits";
+private static final String PAR_PBITS_LEGACY = "simulation.timebits";
+
+/**
+ * Specifies the initial capacity of the heap. Defaults to 65536.
+ * @config
+ */
+private static final String PAR_SIZE = "size";
+
+
+//--------------------------------------------------------------------------
+// Fields
+//--------------------------------------------------------------------------
+
+// The following arrays are four heaps ordered by time. The alternative
+// approach (i.e. to store event objects) requires much more memory,
+// and based on some tests that I've done is not really much faster.
+
+/** Event component of the heap */
+private Object[] events;
+
+/** Time component of the heap */
+private long[] times;
+
+/** Node component of the heap */
+private Node[] nodes;
+
+/** Pid component of the heap */
+private byte[] pids;
+
+/** Number of elements */
+private int size;
+
+/** Singleton event object used to return (event, time, node, pid) tuples */
+private final Event ev = new Event();
+
+/** The number of bits reserved to order event with the same timestamp */
+private final int pbits;
+
+/** The mask to test whether the time value fits into the range we can
+represent */
+private final long overflowMask;
+
+//--------------------------------------------------------------------------
+// Contructor
+//--------------------------------------------------------------------------
+
+/**
+ * Initializes a new heap using defaults.
+ */
+public Heap() {
+ this(""); // "" is not a valid prefix for a component
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Initializes a new heap using the configuration.
+ */
+public Heap(String prefix) {
+
+ int size = Configuration.getInt(prefix+"."+PAR_SIZE,65536);
+
+ // some complex stuff to deal with legacy parameter names...
+ if( !Configuration.contains(PAR_PBITS_LEGACY) )
+ pbits = Configuration.getInt(prefix+"."+PAR_PBITS,8);
+ else
+ {
+ pbits = Configuration.getInt(PAR_PBITS_LEGACY);
+ if( Configuration.contains(prefix+"."+PAR_PBITS) )
+ throw new IllegalParameterException(PAR_PBITS_LEGACY,
+ "Your configuration file contains both "+
+ prefix+"."+PAR_PBITS+ " and "+
+ PAR_PBITS_LEGACY+"; please remove "+
+ PAR_PBITS_LEGACY);
+ }
+
+ if (pbits < 8 || pbits >= 31) {
+ throw new IllegalParameterException(prefix+"."+PAR_PBITS,
+ "This parameter should be >= 8 or < 31");
+ }
+ overflowMask = ~maxTime();
+ events = new Object[size];
+ times = new long[size];
+ nodes = new Node[size];
+ pids = new byte[size];
+}
+
+//--------------------------------------------------------------------------
+// Methods
+//--------------------------------------------------------------------------
+
+/**
+ * Returns the current number of events in the system.
+ */
+public int size()
+{
+ return size;
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Add a new event, to be scheduled at the specified time.
+ *
+ * @param time the time at which this event should be scheduled
+ * @param event the object describing the event
+ * @param node the node at which the event has to be delivered
+ * @param pid the protocol that handles the event
+ */
+public void add(long time, Object event, Node node, byte pid)
+{
+ add(time,event,node,pid,CommonState.r.nextInt(1 << pbits));
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Add a new event, to be scheduled at the specified time.
+ *
+ * @param time the time at which this event should be scheduled
+ * @param event the object describing the event
+ * @param node the node at which the event has to be delivered
+ * @param pid the protocol that handles the event
+ */
+public void add(long time, Object event, Node node, byte pid, long priority)
+{
+ if( (time&overflowMask) != 0 ) throw new
+ IllegalArgumentException("Time overflow: time="+time);
+//XXX should we test priority overflow? How much does it cost?
+
+ time = (time << pbits) | priority;
+
+ size++;
+ int pos = size;
+ put(pos, time, event, node, pid);
+ while (pos > 1 && getTime(pos / 2) > time) {
+ swap(pos, pos / 2);
+ pos = pos / 2;
+ }
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Removes the first event in the heap and returns it.
+ * Note that, to avoid garbage collection, a singleton instance of
+ * the Event class is used. This means that data contained in the
+ * returned event are overwritten when a new invocation of this
+ * method is performed.
+ * @return first event or null if size is zero
+ */
+public Event removeFirst() {
+
+ if(size==0) return null;
+
+ ev.time = times[0] >> pbits;
+ ev.event = events[0];
+ ev.node = nodes[0];
+ ev.pid = pids[0];
+ swap(1, size);
+ size--;
+ minHeapify(1);
+ return ev;
+}
+
+//--------------------------------------------------------------------------
+
+public long maxTime() { return Long.MAX_VALUE >> pbits; }
+
+//--------------------------------------------------------------------------
+
+public long maxPriority() { return (1L << pbits)-1; }
+
+//--------------------------------------------------------------------------
+
+/**
+ * Prints the time values contained in the heap.
+ */
+public String toString()
+{
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("[Size: " + size + " Times: ");
+ for (int i=1; i <= size; i++) {
+ buffer.append(getTime(i)+",");
+ }
+ buffer.append("]");
+ return buffer.toString();
+}
+
+
+//--------------------------------------------------------------------------
+// Private methods
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+private void minHeapify(int index)
+{
+ // The time to be placed of the current node
+ long time = getTime(index);
+ // Left, right children of the current index
+ int l,r;
+ // Their associated time
+ long lt, rt;
+ // The minimum time between val, lt, rt
+ long mintime;
+ // The index of the mininum time
+ int minindex = index;
+ do {
+ index = minindex;
+ mintime = time;
+ l = index << 1;
+ r = l + 1;
+ if (l <= size && (lt = getTime(l)) < mintime) {
+ minindex = l;
+ mintime = lt;
+ }
+ if (r <= size && (rt = getTime(r)) < mintime) {
+ minindex = r;
+ mintime = rt;
+ }
+ if (minindex != index) {
+ swap(minindex, index);
+ }
+ } while (minindex != index);
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+private void swap(int i1, int i2) {
+
+ i1--;
+ i2--;
+
+ Object te = events[i1];
+ events[i1] = events[i2];
+ events[i2] = te;
+
+ long tt = times[i1];
+ times[i1] = times[i2];
+ times[i2] = tt;
+
+ Node tn = nodes[i1];
+ nodes[i1] = nodes[i2];
+ nodes[i2] = tn;
+
+ byte tp = pids[i1];
+ pids[i1] = pids[i2];
+ pids[i2] = tp;
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+private long getTime(int index) {
+ /* Compute first and second index, and return the value */
+ index--;
+ return times[index];
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+private void put(int index, long time, Object event, Node node, byte pid) {
+
+ index--;
+ if (index >= events.length) {
+ doubleCapacity();
+ }
+ events[index] = event;
+ times[index] = time;
+ nodes[index] = node;
+ pids[index] = pid;
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+private void doubleCapacity() {
+ int oldsize = events.length;
+ int newsize = oldsize*2;
+ Object[] te = new Object[newsize];
+ System.arraycopy(events, 0, te, 0, oldsize);
+ events = te;
+ long[] tt = new long[newsize];
+ System.arraycopy(times, 0, tt, 0, oldsize);
+ times = tt;
+ Node[] tn = new Node[newsize];
+ System.arraycopy(nodes, 0, tn, 0, oldsize);
+ nodes = tn;
+ byte[] tp = new byte[newsize];
+ System.arraycopy(pids, 0, tp, 0, oldsize);
+ pids = tp;
+}
+
+//--------------------------------------------------------------------------
+// Testing
+//--------------------------------------------------------------------------
+
+/*
+public static void main(String[] args) {
+ Random random = new Random();
+ Heap heap = new Heap();
+ int rep = 1000000;
+ if( args.length > 0 ) rep = Integer.parseInt(args[0]);
+ int[] values1 = new int[rep];
+ long[] values2 = new long[rep];
+ for (int i = 0; i < rep; i++) {
+ values1[i] = random.nextInt(1000000000);
+ }
+
+ long time1 = System.currentTimeMillis();
+ for (int i = 0; i < rep; i++) {
+ heap.add(values1[i], null, null, (byte) 1);
+ }
+ long time2 = System.currentTimeMillis();
+ System.out.println("Inserting: " + (time2-time1));
+
+ time1 = System.currentTimeMillis();
+ for (int i = 0; i < rep; i++) {
+ values2[i] = heap.removeFirst().time;
+ }
+ time2 = System.currentTimeMillis();
+ System.out.println("Removing: " + (time2-time1));
+
+ Arrays.sort(values1);
+ for (int i=0; i<rep; i++) {
+ if (values1[i] != values2[i])
+ System.out.print("+");
+ }
+}
+*/
+
+} // END Heap
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import peersim.core.*;
+import peersim.cdsim.CDProtocol;
+
+/**
+ * This class is used to wrap a {@link CDProtocol} instance into an event so
+ * that it can be used in the event based simulation engine. This class is
+ * responsible for calling {@link CDProtocol#nextCycle} and also to schedule the
+ * consecutive cycle. In the configuration of an event driven simulation
+ * {@link CDProtocol}s can be configured using {@link CDScheduler}, which places
+ * appropriate instances of this events in the queue.
+ *
+ * <p>
+ * Note that reimplementing method {@link #nextDelay} of this class allows for
+ * arbitrary scheduling, including adaptively changing or irregular cycle
+ * lengths, etc.
+ *
+ * @see CDScheduler
+ * @see CDProtocol
+ */
+public class NextCycleEvent implements Cloneable {
+
+ // =============================== initialization ======================
+ // =====================================================================
+
+ /**
+ * Reads configuration to initialize the object. Extending classes should
+ * have a constructor with the same signature, often as simple as
+ * <code>super(n)</code>.
+ */
+ public NextCycleEvent(String n) {
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns a clone of the object. Overriding this method is necessary and
+ * typically is as simple as <code>return super.clone()</code>. In general,
+ * always use <code>super.clone()</code> to obtain the object to be returned
+ * on which you can perform optional deep cloning operations (arrays, etc).
+ */
+ public Object clone() throws CloneNotSupportedException {
+
+ return super.clone();
+ }
+
+ // ========================== methods ==================================
+ // =====================================================================
+
+ /**
+ * Executes the nextCycle method of the protocol, and schedules the next
+ * call using the delay returned by {@link #nextDelay}. If the next
+ * execution time as defined by the delay is outside of the valid times as
+ * defined by {@link CDScheduler#sch}, then the next event is not scheduled.
+ * Note that this means that this protocol will no longer be scheduled
+ * because the next event after the next event is scheduled by the next
+ * event.
+ */
+ public final void execute() {
+
+ int pid = CommonState.getPid();
+ Node node = CommonState.getNode();
+ CDProtocol cdp = (CDProtocol) node.getProtocol(pid);
+ cdp.nextCycle(node, pid);
+ long delay = nextDelay(CDScheduler.sch[pid].step);
+
+ if (CommonState.getTime() + delay < CDScheduler.sch[pid].until)
+ EDSimulator.add(delay, this, node, pid);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Calculates the delay until the next execution of the protocol. This
+ * default implementation returns a constant delay equal to the step
+ * parameter (cycle length in this case) of the schedule of this event (as
+ * set in the config file).
+ */
+ protected long nextDelay(long step) {
+
+ return step;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c)2008 The Peersim Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import peersim.core.Node;
+
+/**
+ * The interface to be implemented by the event queue of the evend based
+ * engine. An implementation must also provide the standard cosntructor
+ * required by any peersim components: one that takes a String argument,
+ * the component name in the configuration.
+ */
+public interface PriorityQ {
+
+
+/**
+ * Returns the current number of events in the queue.
+ */
+public int size();
+
+/**
+ * Add a new event, to be scheduled at the specified time. If there are other
+ * events scheduled at the same time, then the time of execution if this event
+ * relative to the other events is unspecified.
+ *
+ * @param time The time at which this event should be scheduled. It is
+ * guaranteed to be non-negative (so no extra checks are needed)
+ * @param event the object describing the event
+ * @param node the node at which the event has to be delivered
+ * @param pid the protocol that handles the event
+ */
+public void add(long time, Object event, Node node, byte pid);
+
+/**
+ * Add a new event, to be scheduled at the specified time, specifying also
+ * the priority of the event, should there be other events scheduled at the
+ * same time. If both time and priority is the same for an event, then the
+ * scheduling order is unspecified.
+ *
+ * @param time The time at which this event should be scheduled. It is
+ * guaranteed to be non-negative (so no extra checks are needed)
+ * @param event the object describing the event
+ * @param node the node at which the event has to be delivered
+ * @param pid the protocol that handles the event
+ * @param priority if for two events the "time" value is the same, this
+ * value should be used to order them. Lower value means higher priority.
+ * Like with time, non-negativity as assumed.
+ */
+public void add(long time, Object event, Node node, byte pid, long priority);
+
+/**
+ * Removes the first event in the heap and returns it.
+ * The returned object is not guaranteed to be a freshly generated object,
+ * that is, we allow for an implementation that keeps one copy of an event
+ * object and always returns a reference to that copy.
+ * @return first event or null if size is zero
+ */
+public Event removeFirst();
+
+/**
+* Maximal value of time this interpretation can represent.
+*/
+public long maxTime();
+
+/**
+* Maximal value of priority this interpretation can deal with. That is,
+* the number of different priority levels is <tt>maxPriority()+1</tt> because
+* 0 is also a valid level.
+* @see #add(long,Object,Node,byte,long)
+*/
+public long maxPriority();
+
+/**
+ * Return type of {@link #removeFirst()}.
+ */
+public class Event
+{
+ public Object event;
+ public long time;
+ public Node node;
+ public byte pid;
+ public String toString() {
+ return event+" to node "+node+"prot "+pid+"at "+time; }
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import peersim.core.*;
+
+
+/**
+* Implements random delay between calling the nextCycle method of the protocol.
+* @see #nextDelay
+*/
+public class RandNextCycle extends NextCycleEvent {
+
+
+// =============================== initialization ======================
+// =====================================================================
+
+
+/**
+* Calls super constructor.
+*/
+public RandNextCycle(String n) { super(n); }
+
+// --------------------------------------------------------------------
+
+/**
+* Calls super.clone().
+*/
+public Object clone() throws CloneNotSupportedException {
+
+ return super.clone();
+}
+
+
+// ========================== methods ==================================
+// =====================================================================
+
+
+/**
+* Returns a random delay with uniform distribution between 1 (inclusive) and
+* 2*<code>step</code> (exclusive)
+* (expected value is therefore <code>step</code>).
+*/
+protected long nextDelay(long step) {
+
+ return 1+CommonState.r.nextLong((step<<1)-1);
+}
+
+
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import peersim.core.*;
+
+
+/**
+* Implements a random delay, but making sure there is exactly one call in each
+* consecutive <code>step</code> time units.
+*/
+public class RegRandNextCycle extends NextCycleEvent {
+
+// ============================== fields ==============================
+// ====================================================================
+
+/**
+* Indicates the start of the next cycle for a particular protocol
+* instance. If negative it means it has not been initialized yet.
+*/
+private long nextCycleStart = -1;
+
+// =============================== initialization ======================
+// =====================================================================
+
+
+/**
+* Calls super constructor.
+*/
+public RegRandNextCycle(String n) {
+
+ super(n);
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Calls super.clone().
+*/
+public Object clone() throws CloneNotSupportedException {
+
+ return super.clone();
+}
+
+
+// ========================== methods ==================================
+// =====================================================================
+
+
+/**
+* Returns a random delay but making sure there is exactly one invocation in each
+* consecutive interval of length <code>step</code>. The beginning of these
+* intervals is defined by the first invocation which is in turn defined by
+* {@link CDScheduler} that initiates the protocol in question.
+*/
+protected long nextDelay(long step) {
+
+ // at this point nextCycleStart points to the start of the next cycle
+ // (the cycle after the one in which this execution is taking place)
+ // (note that the start of the cycle is included in the cycle)
+
+ final long now = CommonState.getTime();
+ if(nextCycleStart<0)
+ {
+ // not initialized
+ nextCycleStart=now+step;
+ }
+
+ // to be on the safe side, we do the next while loop.
+ // although currently it never executes
+ while(nextCycleStart<=now) nextCycleStart+=step;
+
+ // we increment nextCycleStart to point to the start of the cycle
+ // after the next cycle
+ nextCycleStart+=step;
+
+ return nextCycleStart-now-CommonState.r.nextLong(step)-1;
+}
+
+}
+
+
--- /dev/null
+<?xml version="1.0" encoding="ASCII"?>
+<jsp:Model xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:jsp="http://www.eclipse.org/MoDisco/JSP/0.1.incubation/jsp"/>
--- /dev/null
+<?xml version="1.0" encoding="ASCII"?>
+<kdm:Segment xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:kdm="http://www.eclipse.org/MoDisco/kdm/kdm" xmlns:source="http://www.eclipse.org/MoDisco/kdm/source">
+ <model xsi:type="source:InventoryModel" name="edsim">
+ <inventoryElement xsi:type="source:Directory" name="edsim" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim">
+ <inventoryElement xsi:type="source:SourceFile" name="RandNextCycle.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/RandNextCycle.java"/>
+ <inventoryElement xsi:type="source:SourceFile" name="RegRandNextCycle.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/RegRandNextCycle.java"/>
+ <inventoryElement xsi:type="source:SourceFile" name="Heap.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/Heap.java"/>
+ <inventoryElement xsi:type="source:SourceFile" name="ControlEvent.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/ControlEvent.java"/>
+ <inventoryElement xsi:type="source:SourceFile" name="EDSimulator.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/EDSimulator.java"/>
+ <inventoryElement xsi:type="source:SourceFile" name="CDScheduler.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/CDScheduler.java"/>
+ <inventoryElement xsi:type="source:SourceFile" name="PriorityQ.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/PriorityQ.java"/>
+ <inventoryElement xsi:type="source:SourceFile" name="NextCycleEvent.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/NextCycleEvent.java"/>
+ <inventoryElement xsi:type="source:SourceFile" name="EDProtocol.java" path="/home/imb/Bureau/kbaati/pseclipse/work/PSG/src/peersim/edsim/EDProtocol.java"/>
+ </inventoryElement>
+ </model>
+</kdm:Segment>
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+
+/**
+* This class implements a graph which uses a bitmatrix as inner representation
+* of edges.
+*/
+public class BitMatrixGraph implements Graph {
+
+
+// ====================== private fileds ========================
+// ==============================================================
+
+private final List<BitSet> sets;
+
+private final boolean directed;
+
+// ====================== public constructors ===================
+// ==============================================================
+
+
+/**
+* Constructs a directed graph with the given number of nodes.
+* The graph has no edges initially. The graph is directed.
+* @param n size of graph
+*/
+public BitMatrixGraph( int n ) {
+
+ this(n,true);
+}
+
+// ---------------------------------------------------------------
+
+/**
+* Constructs an graph with the given number of nodes.
+* The graph has no edges initially.
+* @param n size of graph
+* @param directed if true graph is directed
+*/
+public BitMatrixGraph( int n, boolean directed ) {
+
+ sets = new ArrayList<BitSet>(n);
+ for(int i=0; i<n; ++i) sets.add(new BitSet());
+ this.directed = directed;
+}
+
+
+// ======================= Graph implementations ================
+// ==============================================================
+
+
+public boolean isEdge(int i, int j) {
+
+ return sets.get(i).get(j);
+}
+
+// ---------------------------------------------------------------
+
+public Collection<Integer> getNeighbours(int i) {
+
+ Set<Integer> result = new HashSet<Integer>();
+ BitSet neighb = sets.get(i);
+ final int max = size();
+ for(int j=0; j<max; ++j)
+ {
+ if( neighb.get(j) ) result.add(j);
+ }
+
+ return Collections.unmodifiableCollection(result);
+}
+
+// ---------------------------------------------------------------
+
+/** Returns null always */
+public Object getNode(int i) { return null; }
+
+// ---------------------------------------------------------------
+
+/**
+* Returns null always.
+*/
+public Object getEdge(int i, int j) { return null; }
+
+// ---------------------------------------------------------------
+
+public int size() { return sets.size(); }
+
+// --------------------------------------------------------------------
+
+public boolean directed() { return directed; }
+
+// --------------------------------------------------------------------
+
+public boolean setEdge(int i, int j) {
+
+ if( i > size() || j > size() || i<0 || j<0 ) throw new
+ IndexOutOfBoundsException();
+
+ BitSet neighb = sets.get(i);
+ boolean old = neighb.get(j);
+ neighb.set(j);
+
+ if( !old && !directed )
+ {
+ neighb = sets.get(j);
+ neighb.set(i);
+ }
+
+ return !old;
+}
+
+// --------------------------------------------------------------------
+
+public boolean clearEdge(int i, int j) {
+
+ if( i > size() || j > size() || i<0 || j<0 ) throw new
+ IndexOutOfBoundsException();
+
+ BitSet neighb = sets.get(i);
+ boolean old = neighb.get(j);
+ neighb.clear(j);
+
+ if( old && !directed )
+ {
+ neighb = sets.get(i);
+ neighb.clear(j);
+ }
+
+ return old;
+}
+
+// --------------------------------------------------------------------
+
+public int degree(int i) {
+
+ BitSet neighb = sets.get(i);
+ return neighb.cardinality(); // only from jdk 1.4
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+
+/**
+* This class is an adaptor making any Graph an undirected graph
+* by making its edges bidirectional. The graph to be made undirected
+* is passed to the constructor. Only the reference is stored.
+* However, at construction time the incoming edges are stored
+* for each node, so if the graph
+* passed to the constructor changes over time then
+* methods {@link #getNeighbours(int)} and {@link #degree(int)}
+* become inconsistent (but only those).
+* The upside of this inconvenience is that {@link #getNeighbours} will have
+* constant time complexity.
+* @see UndirectedGraph
+*/
+public class ConstUndirGraph implements Graph {
+
+
+// ====================== private fileds ========================
+// ==============================================================
+
+
+protected final Graph g;
+
+protected final List<Integer>[] in;
+
+// ====================== public constructors ===================
+// ==============================================================
+
+/**
+* Initialization based on given graph. Stores the graph and if necessary
+* (if the graph is directed) searches for the incoming edges and stores
+* them too. The given graph is stored by reference (not cloned) so it should
+* not be modified while this object is in use.
+*/
+public ConstUndirGraph( Graph g ) {
+
+ this.g = g;
+ if( !g.directed() )
+ {
+ in = null;
+ }
+ else
+ {
+ in = new List[g.size()];
+ }
+
+ initGraph();
+}
+
+// --------------------------------------------------------------
+
+/** Finds and stores incoming edges */
+protected void initGraph() {
+
+ final int max = g.size();
+ for(int i=0; i<max; ++i) in[i] = new ArrayList<Integer>();
+ for(int i=0; i<max; ++i)
+ {
+ for(Integer j:g.getNeighbours(i))
+ {
+ if( ! g.isEdge(j,i) ) in[j].add(i);
+ }
+ }
+}
+
+
+// ======================= Graph implementations ================
+// ==============================================================
+
+
+public boolean isEdge(int i, int j) {
+
+ return g.isEdge(i,j) || g.isEdge(j,i);
+}
+
+// ---------------------------------------------------------------
+
+/**
+* Uses sets as collection so does not support multiple edges now, even if
+* the underlying directed graph does.
+*/
+public Collection<Integer> getNeighbours(int i) {
+
+ List<Integer> result = new ArrayList<Integer>();
+ result.addAll(g.getNeighbours(i));
+ if( in != null ) result.addAll(in[i]);
+ return Collections.unmodifiableCollection(result);
+}
+
+// ---------------------------------------------------------------
+
+/** Returns the node from the underlying graph */
+public Object getNode(int i) { return g.getNode(i); }
+
+// ---------------------------------------------------------------
+
+/**
+* If there is an (i,j) edge, returns that, otherwise if there is a (j,i)
+* edge, returns that, otherwise returns null.
+*/
+public Object getEdge(int i, int j) {
+
+ if( g.isEdge(i,j) ) return g.getEdge(i,j);
+ if( g.isEdge(j,i) ) return g.getEdge(j,i);
+ return null;
+}
+
+// ---------------------------------------------------------------
+
+public int size() { return g.size(); }
+
+// --------------------------------------------------------------------
+
+public boolean directed() { return false; }
+
+// --------------------------------------------------------------------
+
+/** not supported */
+public boolean setEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// ---------------------------------------------------------------
+
+/** not supported */
+public boolean clearEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// ---------------------------------------------------------------
+
+public int degree(int i) { return g.degree(i)+(in==null?0:in[i].size()); }
+
+// ---------------------------------------------------------------
+/*
+public static void main( String[] args ) {
+
+ Graph net = new BitMatrixGraph(20);
+ GraphFactory.wireKOut(net,5,new Random());
+ ConstUndirGraph ug = new ConstUndirGraph(net);
+ for(int i=0; i<net.size(); ++i)
+ System.err.println(
+ i+" "+net.getNeighbours(i)+" "+net.degree(i));
+ System.err.println("============");
+ for(int i=0; i<ug.size(); ++i)
+ System.err.println(i+" "+ug.getNeighbours(i)+" "+ug.degree(i));
+ System.err.println("============");
+ for(int i=0; i<ug.size(); ++i)
+ System.err.println(i+" "+ug.in[i]);
+ for(int i=0; i<ug.size(); ++i)
+ {
+ for(int j=0; j<ug.size(); ++j)
+ System.err.print(ug.isEdge(i,j)?"W ":"+ ");
+ System.err.println();
+ }
+
+ GraphIO.writeUCINET_DL(net,System.out);
+ GraphIO.writeUCINET_DL(ug,System.out);
+}
+*/
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * Created on Jan 30, 2005 by Spyros Voulgaris
+ *
+ */
+package peersim.graph;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+* Speeds up {@link ConstUndirGraph#isEdge} by storing the links in an
+* adjacency matrix (in fact in a triangle).
+* Its memory consumption is huge but it's much faster if the isEdge method
+* of the original underlying graph is slow.
+*/
+public class FastUndirGraph extends ConstUndirGraph
+{
+
+private BitSet[] triangle;
+
+
+// ======================= initializarion ==========================
+// =================================================================
+
+
+/** Calls super constructor */
+public FastUndirGraph(Graph graph)
+{
+ super(graph);
+}
+
+// -----------------------------------------------------------------
+
+protected void initGraph()
+{
+ final int max = g.size();
+ triangle = new BitSet[max];
+ for (int i=0; i<max; ++i)
+ {
+ in[i] = new ArrayList<Integer>();
+ triangle[i] = new BitSet(i);
+ }
+
+ for(int i=0; i<max; ++i)
+ {
+ for(Integer out:g.getNeighbours(i))
+ {
+ int j=out.intValue();
+ if( ! g.isEdge(j,i) )
+ in[j].add(i);
+ // But always add the link to the triangle
+ if (i>j) // make sure i>j
+ triangle[i].set(j);
+ else
+ triangle[j].set(i);
+ }
+ }
+}
+
+
+// ============================ Graph functions ====================
+// =================================================================
+
+
+public boolean isEdge(int i, int j)
+{
+ // make sure i>j
+ if (i<j)
+ {
+ int ii=i;
+ i=j;
+ j=ii;
+ }
+ return triangle[i].get(j);
+}
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.Collection;
+
+/**
+* A general graph interface. It follows the following model:
+* the graph has n nodes which are indexed from 0 to n-1.
+* The parameters of operators refer to indices only.
+* Implementations might return objects that represent the
+* nodes or edges, although this is not required.
+*
+* Undirected graphs are modelled by the interface as directed graphs in which
+* every edge (i,j) has a corresponding reverse edge (j,i).
+*/
+public interface Graph {
+
+ /**
+ * Returns true if there is a directed edge between node i
+ * and node j.
+ */
+ boolean isEdge(int i, int j);
+
+ /**
+ * Returns a collection view to all outgoing edges from
+ * i. The collection should ideally be unmodifiable.
+ * In any case, modifying the returned collection is not safe and
+ * may result in unspecified behavior.
+ */
+ Collection<Integer> getNeighbours(int i);
+
+ /**
+ * Returns the node object associated with the index. Optional
+ * operation.
+ */
+ Object getNode(int i);
+
+ /**
+ * Returns the edge object associated with the index. Optional
+ * operation.
+ */
+ Object getEdge(int i, int j);
+
+ /**
+ * The number of nodes in the graph.
+ */
+ int size();
+
+ /**
+ * Returns true if the graph is directed otherwise false.
+ */
+ boolean directed();
+
+ /**
+ * Sets given edge, returns true if it did not exist before.
+ * If the graph is
+ * undirected, sets the edge (j,i) as well. Optional operation.
+ */
+ public boolean setEdge(int i, int j);
+
+ /**
+ * Removes given edge, returns true if it existed before. If the graph is
+ * undirected, removes the edge (j,i) as well. Optional operation.
+ */
+ public boolean clearEdge(int i, int j);
+
+ /**
+ * Returns the degree of the given node. If the graph is directed,
+ * returns out degree.
+ */
+ public int degree(int i);
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+
+/**
+* Implements graph algorithms. The current implementation is NOT thread
+* safe. Some algorithms are not static, many times the result of an
+* algorithm can be read from non-static fields.
+*/
+public class GraphAlgorithms {
+
+// =================== public fields ==================================
+// ====================================================================
+
+/** output of some algorithms is passed here */
+public int[] root = null;
+private Stack<Integer> stack = new Stack<Integer>();
+private int counter=0;
+
+private Graph g=null;
+
+public final static int WHITE=0;
+public final static int GREY=1;
+public final static int BLACK=2;
+
+/** output of some algorithms is passed here */
+public int[] color = null;
+
+/** output of some algorithms is passed here */
+public Set<Integer> cluster = null;
+
+/** output of some algorithms is passed here */
+public int[] d = null;
+
+// =================== private methods ================================
+// ====================================================================
+
+
+/**
+* Collects nodes accessible from node "from" using depth-first search.
+* Works on the array {@link #color} which must be of the same length as
+* the size of the graph, and must contain values according to the
+* following semantics:
+* WHITE (0): not seen yet, GREY (1): currently worked upon. BLACK
+* (other than 0 or 1): finished.
+* If a negative color is met, it is saved in the {@link #cluster} set
+* and is treated as black. This can be used to check if the currently
+* visited cluster is weakly connected to another cluster.
+* On exit no nodes are GREY.
+* The result is the modified array {@link #color} and the modified set
+* {@link #cluster}.
+*/
+private void dfs( int from ) {
+
+ color[from]=GREY;
+
+ for(int j:g.getNeighbours(from))
+ {
+ if( color[j]==WHITE )
+ {
+ dfs(j);
+ }
+ else
+ {
+ if( color[j]<0 ) cluster.add(color[j]);
+ }
+ }
+
+ color[from]=BLACK;
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Collects nodes accessible from node "from" using breadth-first search.
+* Its parameters and side-effects are identical to those of dfs.
+* In addition, it stores the shortest distances from "from" in {@link #d},
+* if it is not null. On return, <code>d[i]</code> contains the length of
+* the shortest path from "from" to "i", if such a path exists, or it is
+* unchanged (ie the original value of <code>d[i]</code> is kept,
+* whatever that was.
+* <code>d</code> must either be long enough or null.
+*/
+private void bfs( int from ) {
+
+ List<Integer> q = new LinkedList<Integer>();
+ int u, du;
+
+ q.add(from);
+ q.add(0);
+ if( d != null ) d[from] = 0;
+
+ color[from]=GREY;
+
+ while( ! q.isEmpty() )
+ {
+ u = q.remove(0).intValue();
+ du = q.remove(0).intValue();
+
+ for(int j:g.getNeighbours(u))
+ {
+ if( color[j]==WHITE )
+ {
+ color[j]=GREY;
+
+ q.add(j);
+ q.add(du+1);
+ if( d != null ) d[j] = du+1;
+ }
+ else
+ {
+ if( color[j]<0 )
+ cluster.add(color[j]);
+ }
+ }
+ color[u]=BLACK;
+ }
+}
+
+// --------------------------------------------------------------------
+
+/** The recursive part of the Tarjan algorithm. */
+private void tarjanVisit(int i) {
+
+ color[i]=counter++;
+ root[i]=i;
+ stack.push(i);
+
+ for(int j:g.getNeighbours(i))
+ {
+ if( color[j]==WHITE )
+ {
+ tarjanVisit(j);
+ }
+ if( color[j]>0 && color[root[j]]<color[root[i]] )
+ // inComponent is false and have to update root
+ {
+ root[i]=root[j];
+ }
+ }
+
+ int j;
+ if(root[i]==i) //this node is the root of its cluster
+ {
+ do
+ {
+ j=stack.pop();
+ color[j]=-color[j];
+ root[j]=i;
+ }
+ while(j!=i);
+ }
+}
+
+// =================== public methods ================================
+// ====================================================================
+
+/** Returns the weakly connected cluster indexes with size as a value.
+* Cluster membership can be seen from the content of the array {@link #color};
+* each node has the cluster index as color. The cluster indexes carry no
+* information; we guarantee only that different clusters have different indexes.
+*/
+public Map weaklyConnectedClusters( Graph g ) {
+
+ this.g=g;
+ if( cluster == null ) cluster = new HashSet<Integer>();
+ if( color==null || color.length<g.size() ) color = new int[g.size()];
+
+ // cluster numbers are negative integers
+ int i, j, actCluster=0;
+ for(i=0; i<g.size(); ++i) color[i]=WHITE;
+ for(i=0; i<g.size(); ++i)
+ {
+ if( color[i]==WHITE )
+ {
+ cluster.clear();
+ bfs(i); // dfs is recursive, for large graphs not ok
+ --actCluster;
+ for(j=0; j<g.size(); ++j)
+ {
+ if( color[j] == BLACK ||
+ cluster.contains(color[j]) )
+ color[j] = actCluster;
+ }
+ }
+ }
+
+ Hashtable<Integer,Integer> ht = new Hashtable<Integer,Integer>();
+ for(j=0; j<g.size(); ++j)
+ {
+ Integer num = ht.get(color[j]);
+ if( num == null ) ht.put(color[j],Integer.valueOf(1));
+ else ht.put(color[j],num+1);
+ }
+
+ return ht;
+}
+
+// --------------------------------------------------------------------
+
+/**
+* In <code>{@link #d}[j]</code> returns the length of the shortest path between
+* i and j. The value -1 indicates that j is not accessible from i.
+*/
+public void dist( Graph g, int i ) {
+
+ this.g=g;
+ if( d==null || d.length<g.size() ) d = new int[g.size()];
+ if( color==null || color.length<g.size() ) color = new int[g.size()];
+
+ for(int j=0; j<g.size(); ++j)
+ {
+ color[j]=WHITE;
+ d[j] = -1;
+ }
+
+ bfs(i);
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Calculates the clustering coefficient for the given node in the given
+* graph. The clustering coefficient is the number of edges between
+* the neighbours of i divided by the number of possible edges.
+* If the graph is directed, an exception is thrown.
+* If the number of neighbours is 1, returns 1. For zero neighbours
+* returns NAN.
+* @throws IllegalArgumentException if g is directed
+*/
+public static double clustering( Graph g, int i ) {
+
+ if( g.directed() ) throw new IllegalArgumentException(
+ "graph is directed");
+
+ Object[] n = g.getNeighbours(i).toArray();
+
+ if( n.length==1 ) return 1.0;
+
+ int edges = 0;
+
+ for(int j=0; j<n.length; ++j)
+ for(int k=j+1; k<n.length; ++k)
+ if( g.isEdge((Integer)n[j],(Integer)n[k]) ) ++edges;
+
+ return ((edges*2.0)/n.length)/(n.length-1);
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Performs anti-entropy epidemic multicasting from node 0.
+* As a result the number of nodes that have been reached in cycle i
+* is put into <code>b[i]</code>.
+* The number of cycles performed is determined by <code>b.length</code>.
+* In each cycle each node contacts a random neighbour and exchanges
+* information. The simulation is generational: when a node contacts a neighbor
+* in cycle i, it sees their state as in cycle i-1, besides, all nodes update
+* their state at the same time point, synchronously.
+*/
+public static void multicast( Graph g, int[] b, Random r ) {
+
+ int c1[] = new int[g.size()];
+ int c2[] = new int[g.size()];
+ for(int i=0; i<c1.length; ++i) c2[i]=c1[i]=WHITE;
+ c2[0]=c1[0]=BLACK;
+ Collection<Integer> neighbours=null;
+ int black=1;
+
+ int k=0;
+ for(; k<b.length || black<g.size(); ++k)
+ {
+ for(int i=0; i<c2.length; ++i)
+ {
+ neighbours=g.getNeighbours(i);
+ Iterator<Integer> it=neighbours.iterator();
+ for(int j=r.nextInt(neighbours.size()); j>0; --j)
+ it.next();
+ int randn = it.next();
+
+ // push pull exchane with random neighbour
+ if( c1[i]==BLACK ) //c2[i] is black too
+ {
+ if(c2[randn]==WHITE) ++black;
+ c2[randn]=BLACK;
+ }
+ else if( c1[randn]==BLACK )
+ {
+ if(c2[i]==WHITE) ++black;
+ c2[i]=BLACK;
+ }
+ }
+ System.arraycopy(c2,0,c1,0,c1.length);
+ b[k]=black;
+ }
+
+ for(; k<b.length; ++k) b[k]=g.size();
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Performs flooding from given node.
+* As a result <code>b[i]</code> contains the number of nodes
+* reached in exactly i steps, and always <code>b[0]=1</code>.
+* If the maximal distance from k is lower than <code>b.length</code>,
+* then the remaining elements of b are zero.
+*/
+public void flooding( Graph g, int[] b, int k ) {
+
+ dist(g, k);
+
+ for(int i=0; i<b.length; ++i) b[i]=0;
+ for(int i=0; i<d.length; ++i)
+ {
+ if( d[i] >= 0 && d[i] < b.length ) b[d[i]]++;
+ }
+}
+
+// --------------------------------------------------------------------
+
+/** Returns the strongly connected cluster roots with size as a value.
+* Cluster membership can be seen from the content of the array {@link #root};
+* each node has the root of the strongly connected cluster it belongs to.
+* A word of caution: for large graphs that have a large diameter and that
+* are strongly connected (such as large rings) you can get stack overflow
+* because of the large depth of recursion.
+*/
+//XXX implement a non-recursive version ASAP!!!
+public Map tarjan( Graph g ) {
+
+ this.g=g;
+ stack.clear();
+ if( root==null || root.length<g.size() ) root = new int[g.size()];
+ if( color==null || color.length<g.size() ) color = new int[g.size()];
+ for( int i=0; i<g.size(); ++i) color[i]=WHITE;
+ counter = 1;
+
+ // color is WHITE (0): not visited
+ // not WHITE, positive (c>1): visited as the c-th node
+ // color is negative (c<1): inComponent true
+ for(int i=0; i<g.size(); ++i)
+ {
+ if( color[i]==WHITE ) tarjanVisit(i);
+ }
+ for( int i=0; i<g.size(); ++i) color[i]=0;
+ for( int i=0; i<g.size(); ++i) color[root[i]]++;
+ Hashtable<Integer,Integer> ht = new Hashtable<Integer,Integer>();
+ for(int j=0; j<g.size(); ++j)
+ {
+ if(color[j]>0)
+ {
+ ht.put(j,color[j]);
+ }
+ }
+
+ return ht;
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+
+/**
+* Contains static methods for wiring certain kinds of graphs. The general
+* contract of all methods is that they accept any graph and add edges
+* as specified in the documentation.
+*/
+public class GraphFactory {
+
+/** Disable instance construction */
+private GraphFactory() {}
+
+// ===================== public static methods ======================
+// ==================================================================
+
+/**
+* Wires a ring lattice.
+* The added connections are defined as follows. If k is even, links to
+* i-k/2, i-k/2+1, ..., i+k/2 are added (but not to i), thus adding an
+* equal number of predecessors and successors.
+* If k is odd, then we add one more successors than predecessors.
+* For example, for k=4: 2 predecessors, 2 successors.
+* For k=5: 2 predecessors, 3 successors.
+* For k=1: each node is linked only to its successor.
+* All values are understood mod n to make the lattice circular, where n is the
+* number of nodes in g.
+* @param g the graph to be wired
+* @param k lattice parameter
+* @return returns g for convenience
+*/
+public static Graph wireRingLattice(Graph g, int k) {
+
+ final int n = g.size();
+
+ int pred = k/2;
+ int succ = k-pred;
+
+ for(int i=0; i<n; ++i)
+ for(int j=-pred; j<=succ; ++j)
+ {
+ if( j==0 ) continue;
+ final int v = (i+j+n)%n;
+ g.setEdge(i,v);
+ }
+ return g;
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Watts-Strogatz model. A bit modified though: by default assumes a directed
+* graph. This means that directed
+* links are re-wired, and the undirected edges in the original (undirected)
+* lattice are modeled
+* by double directed links pointing in opposite directions. Rewiring is done
+* with replacement, so the possibility of wiring two links to the same target
+* is positive (though very small).
+* <p>
+* Note that it is possible to pass an undirected graph as a parameter. In that
+* case the output is the directed graph produced by the method, converted to
+* an undirected graph by dropping directionality of the edges. This graph is
+* still not from the original undirected WS model though.
+* @param g the graph to be wired
+* @param k lattice parameter: this is the out-degree of a node in the
+* ring lattice before rewiring
+* @param p the probability of rewiring each
+* @param r source of randomness
+* @return returns g for convenience
+*/
+public static Graph wireWS( Graph g, int k, double p, Random r ) {
+//XXX unintuitive to call it WS due to the slight mods
+ final int n = g.size();
+ for(int i=0; i<n; ++i)
+ for(int j=-k/2; j<=k/2; ++j)
+ {
+ if( j==0 ) continue;
+ int newedge = (i+j+n)%n;
+ if( r.nextDouble() < p )
+ {
+ newedge = r.nextInt(n-1);
+ if( newedge >= i ) newedge++; // random _other_ node
+ }
+ g.setEdge(i,newedge);
+ }
+ return g;
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Random graph. Generates randomly k directed edges out of each node.
+* The neighbors
+* (edge targets) are chosen randomly without replacement from the nodes of the
+* graph other than the source node (i.e. no loop edge is added).
+* If k is larger than N-1 (where N is the number of nodes) then k is set to
+* be N-1 and a complete graph is returned.
+* @param g the graph to be wired
+* @param k samples to be drawn for each node
+* @param r source of randomness
+* @return returns g for convenience
+*/
+public static Graph wireKOut( Graph g, int k, Random r ) {
+
+ final int n = g.size();
+ if( n < 2 ) return g;
+ if( n <= k ) k=n-1;
+ int[] nodes = new int[n];
+ for(int i=0; i<nodes.length; ++i) nodes[i]=i;
+ for(int i=0; i<n; ++i)
+ {
+ int j=0;
+ while(j<k)
+ {
+ int newedge = j+r.nextInt(n-j);
+ int tmp = nodes[j];
+ nodes[j] = nodes[newedge];
+ nodes[newedge] = tmp;
+ if( nodes[j] != i )
+ {
+ g.setEdge(i,nodes[j]);
+ j++;
+ }
+ }
+ }
+ return g;
+}
+
+// -------------------------------------------------------------------
+
+/**
+* A sink star.
+* Wires a sink star topology adding a link to 0 from all other nodes.
+* @param g the graph to be wired
+* @return returns g for convenience
+*/
+public static Graph wireStar( Graph g ) {
+
+ final int n = g.size();
+ for(int i=1; i<n; ++i) g.setEdge(i,0);
+ return g;
+}
+
+// -------------------------------------------------------------------
+
+/**
+* A regular rooted tree.
+* Wires a regular rooted tree. The root is 0, it has links to 1,...,k.
+* In general, node i has links to i*k+1,...,i*k+k.
+* @param g the graph to be wired
+* @param k the number of outgoing links of nodes in the tree (except
+* leaves that have zero out-links, and exactly one node that might have
+* less than k).
+* @return returns g for convenience
+*/
+public static Graph wireRegRootedTree( Graph g, int k ) {
+
+ if( k==0 ) return g;
+ final int n = g.size();
+ int i=0; // node we wire
+ int j=1; // next free node to link to
+ while(j<n)
+ {
+ for(int l=0; l<k && j<n; ++l,++j) g.setEdge(i,j);
+ ++i;
+ }
+ return g;
+}
+
+// -------------------------------------------------------------------
+
+/**
+* A hypercube.
+* Wires a hypercube.
+* For a node i the following links are added: i xor 2^0, i xor 2^1, etc.
+* this define a log(graphsize) dimensional hypercube (if the log is an
+* integer).
+* @param g the graph to be wired
+* @return returns g for convenience
+*/
+public static Graph wireHypercube( Graph g ) {
+
+ final int n = g.size();
+ if(n<=1) return g;
+ final int highestone = Integer.highestOneBit(n-1); // not zero
+ for(int i=0; i<n; ++i)
+ {
+ int mask = highestone;
+ while(mask>0)
+ {
+ int j = i^mask;
+ if(j<n) g.setEdge(i,j);
+ mask = mask >> 1;
+ }
+
+ }
+ return g;
+}
+
+// -------------------------------------------------------------------
+
+/**
+* This contains the implementation of the Barabasi-Albert model
+* of growing scale free networks. The original model is described in
+* <a href="http://arxiv.org/abs/cond-mat/0106096">
+http://arxiv.org/abs/cond-mat/0106096</a>.
+* It also works if the graph is directed, in which case the model is a
+* variation of the BA model
+* described in <a href="http://arxiv.org/pdf/cond-mat/0408391">
+http://arxiv.org/pdf/cond-mat/0408391</a>. In both cases, the number of the
+* initial set of nodes is the same as the degree parameter, and no links are
+* added. The first added node is connected to all of the initial nodes,
+* and after that the BA model is used normally.
+* @param k the number of edges that are generated for each new node, also
+* the number of initial nodes (that have no edges).
+* @param r the randomness to be used
+* @return returns g for convenience
+*/
+public static Graph wireScaleFreeBA( Graph g, int k, Random r ) {
+
+ final int nodes = g.size();
+ if( nodes <= k ) return g;
+
+ // edge i has ends (ends[2*i],ends[2*i+1])
+ int[] ends = new int[2*k*(nodes-k)];
+
+ // Add initial edges from k to 0,1,...,k-1
+ for(int i=0; i < k; i++)
+ {
+ g.setEdge(k,i);
+ ends[2*i]=k;
+ ends[2*i+1]=i;
+ }
+
+ int len = 2*k; // edges drawn so far is len/2
+ for(int i=k+1; i < nodes; i++) // over the remaining nodes
+ {
+ for (int j=0; j < k; j++) // over the new edges
+ {
+ int target;
+ do
+ {
+ target = ends[r.nextInt(len)];
+ int m=0;
+ while( m<j && ends[len+2*m+1]!=target) ++m;
+ if(m==j) break;
+ // we don't check in the graph because
+ // this wire method should accept graphs
+ // that already have edges.
+ }
+ while(true);
+ g.setEdge(i,target);
+ ends[len+2*j]=i;
+ ends[len+2*j+1]=target;
+ }
+ len += 2*k;
+ }
+
+ return g;
+}
+
+// -------------------------------------------------------------------
+/*
+public static void main(String[] pars) {
+
+ int n = Integer.parseInt(pars[0]);
+ //int k = Integer.parseInt(pars[1]);
+ Graph g = new BitMatrixGraph(n);
+
+ //wireWS(g,20,.1,new Random());
+ //GraphIO.writeChaco(new UndirectedGraph(g),System.out);
+
+ //wireScaleFreeBA(g,3,new Random());
+ //wireKOut(g,k,new Random());
+ //wireRegRootedTree(g,k);
+ wireHypercube(g);
+ GraphIO.writeNeighborList(g,System.out);
+}
+*/
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+import java.io.*;
+
+/**
+* Implements static methods to load and write graphs.
+*/
+public class GraphIO {
+private GraphIO() {}
+
+
+// ================== public static methods =========================
+// ==================================================================
+
+
+/**
+* Prints graph in edge list format. Each line contains exactly two
+* node IDs separated by whitespace.
+*/
+public static void writeEdgeList( Graph g, PrintStream out ) {
+
+ for(int i=0; i<g.size(); ++i)
+ {
+ Iterator it=g.getNeighbours(i).iterator();
+ while(it.hasNext())
+ {
+ out.println(i+" "+it.next());
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+
+/**
+* Prints graph in neighbor list format. Each line starts with the
+* id of a node followed by the ids of its neighbors separated by space.
+*/
+public static void writeNeighborList( Graph g, PrintStream out ) {
+
+ out.println("# "+g.size());
+
+ for(int i=0; i<g.size(); ++i)
+ {
+ out.print(i+" ");
+ Iterator it=g.getNeighbours(i).iterator();
+ while(it.hasNext())
+ {
+ out.print(it.next()+" ");
+ }
+ out.println();
+ }
+}
+
+// ------------------------------------------------------------------
+
+/**
+* Saves the given graph to
+* the given stream in DOT format. Good for the graphviz package.
+*/
+public static void writeDOT( Graph g, PrintStream out ) {
+
+ out.println((g.directed()?"digraph":"graph")+" {");
+
+ for(int i=0; i<g.size(); ++i)
+ {
+ Iterator<Integer> it=g.getNeighbours(i).iterator();
+ while(it.hasNext())
+ {
+ final int j = it.next();
+ if(g.directed())
+ out.println(i+" -> "+j+";");
+ else if( i<=j )
+ out.println(i+" -- "+j+";");
+ }
+ }
+
+ out.println("}");
+}
+
+// ------------------------------------------------------------------
+
+/**
+* Saves the given graph to
+* the given stream in GML format.
+*/
+public static void writeGML( Graph g, PrintStream out ) {
+
+ out.println("graph [ directed "+(g.directed()?"1":"0"));
+
+ for(int i=0; i<g.size(); ++i)
+ out.println("node [ id "+i+" ]");
+
+ for(int i=0; i<g.size(); ++i)
+ {
+ Iterator it=g.getNeighbours(i).iterator();
+ while(it.hasNext())
+ {
+ out.println(
+ "edge [ source "+i+" target "+it.next()+" ]");
+ }
+ }
+
+ out.println("]");
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Saves the given graph to
+* the given stream to be read by NETMETER. It should be ok also for Pajek.
+*/
+public static void writeNetmeter( Graph g, PrintStream out ) {
+
+ out.println("*Vertices "+g.size());
+ for(int i=0; i<g.size(); ++i)
+ out.println((i+1)+" \""+(i+1)+"\"");
+
+ out.println("*Arcs");
+ for(int i=0; i<g.size(); ++i)
+ {
+ Iterator it=g.getNeighbours(i).iterator();
+ while(it.hasNext())
+ {
+ out.println((i+1)+" "+
+ (((Integer)it.next()).intValue()+1)+" 1");
+ }
+ }
+ out.println("*Edges");
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Saves the given graph to
+* the given stream in UCINET DL nodelist format.
+*/
+public static void writeUCINET_DL( Graph g, PrintStream out ) {
+
+ out.println("DL\nN="+g.size()+"\nFORMAT=NODELIST\nDATA:");
+
+ for(int i=0; i<g.size(); ++i)
+ {
+ out.print(" " + (i+1));
+ Iterator it=g.getNeighbours(i).iterator();
+ while(it.hasNext())
+ {
+ out.print(" "+(((Integer)it.next()).intValue()+1));
+ }
+ out.println();
+ }
+ out.println();
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Saves the given graph to
+* the given stream in UCINET DL matrix format.
+*/
+public static void writeUCINET_DLMatrix( Graph g, PrintStream out ) {
+
+ out.println("DL\nN="+g.size()+"\nDATA:");
+
+ for(int i=0; i<g.size(); ++i)
+ {
+ BitSet bs = new BitSet(g.size());
+ Iterator it=g.getNeighbours(i).iterator();
+ while(it.hasNext())
+ {
+ bs.set( ((Integer)it.next()).intValue() );
+ }
+ for(int j=0; j<g.size(); ++j)
+ {
+ out.print(bs.get(j)?" 1":" 0");
+ }
+ out.println();
+ }
+ out.println();
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Saves the given graph to
+* the given stream in Chaco format. We need to output the number of edges
+* so they have to be counted first which might not be very efficient.
+* Note that this format is designed for undirected graphs only.
+*/
+public static void writeChaco( Graph g, PrintStream out ) {
+
+ if( g.directed() ) System.err.println(
+ "warning: you're saving a directed graph in Chaco format");
+
+ long edges = 0;
+ for(int i=0; i<g.size(); ++i) edges += g.getNeighbours(i).size();
+
+ out.println( g.size() + " " + edges/2 );
+
+ for(int i=0; i<g.size(); ++i)
+ {
+ Iterator it=g.getNeighbours(i).iterator();
+ while(it.hasNext())
+ {
+ out.print((((Integer)it.next()).intValue()+1)+" ");
+ }
+ out.println();
+ }
+
+ out.println();
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Read a graph in newscast graph format.
+* The format depends on mode, the parameter.
+* The file begins with the three byte latin 1 coded "NCG" string followed
+* by the int MODE which is the
+* given parameter. The formats are the following as a function of mode:
+* <ul>
+* <li> 1: Begins with cacheSize in binary format (int), followed by the
+* numberOfNodes (int), and then a continuous series of exactly
+* numberOfNodes records, where a record describes a node's
+* neighbours and their timestamps.
+* A record is a series of exactly cacheSize (int,long) pairs where
+* the int is the node id, and the long is the timestamp.
+* Node id-s start from 1. Node id 0 means no node and used if the parent
+* node has less that cacheSize nodes.</li>
+* </ul>
+* @param file Filename to read
+* @param direction If 0, the original directionality is preserved, if 1,
+* than each edge is reversed, if 2 then directionality is dropped and the
+* returned graph will be undirected.
+*/
+public static Graph readNewscastGraph( String file, int direction )
+throws IOException {
+
+ NeighbourListGraph gr = new NeighbourListGraph( direction != 2 );
+ FileInputStream fis = new FileInputStream(file);
+ DataInputStream dis = new DataInputStream(fis);
+
+ dis.readByte();
+ dis.readByte();
+ dis.readByte();
+
+ final int MODE = dis.readInt();
+ if( MODE != 1 ) throw new IOException("Unknown mode "+MODE);
+
+ final int CACHESIZE = dis.readInt();
+ final int GRAPHSIZE = dis.readInt();
+
+//System.out.println("header: "+MODE+" "+CACHESIZE+" "+GRAPHSIZE);
+
+ for(int i=1; i<=GRAPHSIZE; ++i)
+ {
+ int iind = gr.addNode(i);
+
+ for(int j=0; j<CACHESIZE; ++j)
+ {
+ int a = dis.readInt();
+ dis.readLong();
+
+ int agentIndex = gr.addNode(a);
+ if( direction == 0 ) gr.setEdge(iind,agentIndex);
+ else gr.setEdge(agentIndex,iind);
+ }
+ }
+
+ dis.close();
+
+ return gr;
+}
+
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+
+/**
+* Implements a graph which uses the neighbour list representation.
+* No multiple edges are allowed. The implementation also supports the
+* growing of the graph. This is very useful when the number of nodes is
+* not known in advance or when we construct a graph reading a file.
+*/
+public class NeighbourListGraph implements Graph, java.io.Serializable {
+
+// =================== private fields ============================
+// ===============================================================
+
+/** Contains the objects associated with the node indeces.*/
+private final ArrayList<Object> nodes;
+
+/**
+* Contains the indices of the nodes. The vector "nodes" contains this
+* information implicitly but this way we can find indexes in log time at
+* the cost of memory (node that the edge lists typically use much more memory
+* than this anyway). Note that the nodes vector is still necessary to
+* provide constant access to nodes based on indexes.
+*/
+private final HashMap<Object,Integer> nodeindex;
+
+/** Contains sets of node indexes. If "nodes" is not null, indices are
+* defined by "nodes", otherwise they correspond to 0,1,... */
+private final ArrayList<Set<Integer>> neighbors;
+
+/** Indicates if the graph is directed. */
+private final boolean directed;
+
+// =================== public constructors ======================
+// ===============================================================
+
+/**
+* Constructs an empty graph. That is, the graph has zero nodes, but any
+* number of nodes and edges can be added later.
+* @param directed if true the graph will be directed
+*/
+public NeighbourListGraph( boolean directed ) {
+
+ nodes = new ArrayList<Object>(1000);
+ neighbors = new ArrayList<Set<Integer>>(1000);
+ nodeindex = new HashMap<Object,Integer>(1000);
+ this.directed = directed;
+}
+
+// ---------------------------------------------------------------
+
+/**
+* Constructs a graph with a fixed size without edges. If the graph is
+* constructed this way, it is not possible to associate objects to nodes,
+* nor it is possible to grow the graph using {@link #addNode}.
+* @param directed if true the graph will be directed
+*/
+public NeighbourListGraph( int size, boolean directed ) {
+
+ nodes = null;
+ neighbors = new ArrayList<Set<Integer>>(size);
+ for(int i=0; i<size; ++i) neighbors.add(new HashSet<Integer>());
+ nodeindex = null;
+ this.directed = directed;
+}
+
+// =================== public methods =============================
+// ================================================================
+
+/**
+* If the given object is not associated with a node yet, adds a new
+* node. Returns the index of the node. If the graph was constructed to have
+* a specific size, it is not possible to add nodes and therefore calling
+* this method will throw an exception.
+* @throws NullPointerException if the size was specified at construction time.
+*/
+public int addNode( Object o ) {
+
+ Integer index = nodeindex.get(o);
+ if( index == null )
+ {
+ index = nodes.size();
+ nodes.add(o);
+ neighbors.add(new HashSet<Integer>());
+ nodeindex.put(o,index);
+ }
+
+ return index;
+}
+
+
+// =================== graph implementations ======================
+// ================================================================
+
+
+public boolean setEdge( int i, int j ) {
+
+ boolean ret = neighbors.get(i).add(j);
+ if( ret && !directed ) neighbors.get(j).add(i);
+ return ret;
+}
+
+// ---------------------------------------------------------------
+
+public boolean clearEdge( int i, int j ) {
+
+ boolean ret = neighbors.get(i).remove(j);
+ if( ret && !directed ) neighbors.get(j).remove(i);
+ return ret;
+}
+
+// ---------------------------------------------------------------
+
+public boolean isEdge(int i, int j) {
+
+ return neighbors.get(i).contains(j);
+}
+
+// ---------------------------------------------------------------
+
+public Collection<Integer> getNeighbours(int i) {
+
+ return Collections.unmodifiableCollection(neighbors.get(i));
+}
+
+// ---------------------------------------------------------------
+
+/** If the graph was gradually grown using {@link #addNode}, returns the
+* object associated with the node, otherwise null */
+public Object getNode(int i) { return (nodes==null?null:nodes.get(i)); }
+
+// ---------------------------------------------------------------
+
+/**
+* Returns null always.
+*/
+public Object getEdge(int i, int j) { return null; }
+
+// ---------------------------------------------------------------
+
+public int size() { return neighbors.size(); }
+
+// --------------------------------------------------------------------
+
+public boolean directed() { return directed; }
+
+// --------------------------------------------------------------------
+
+public int degree(int i) { return neighbors.get(i).size(); }
+}
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+
+/**
+* This class is an adaptor for representing special subgraphs of any graph.
+* It can represent the subgraphs spanned by the nodes 0,...,i where
+* i is less than or equal to n-1, the last node of the original graph.
+* The underlying graph is stored by reference. This means that if the
+* graph changes, then these changes will be reflected by this class as well.
+* Besides, the size of the prefix can be changed at will at any time
+* using {@link #setSize}.
+*/
+public class PrefixSubGraph implements Graph {
+
+
+// ====================== private fileds ========================
+// ==============================================================
+
+
+private final Graph g;
+
+/** The graph represents the subgraph defined by nodes 0,...,prefSize */
+private int prefSize;
+
+
+// ====================== public constructors ===================
+// ==============================================================
+
+
+/**
+* Constructs an initially max size subgraph of g. That is, the subgraph will
+* contain all nodes.
+*/
+public PrefixSubGraph( Graph g ) {
+
+ this.g = g;
+ prefSize = g.size();
+}
+
+
+// ======================= Graph implementations ================
+// ==============================================================
+
+
+public boolean isEdge(int i, int j) {
+
+ if( i<0 || i>=prefSize ) throw new IndexOutOfBoundsException();
+ if( j<0 || j>=prefSize ) throw new IndexOutOfBoundsException();
+ return g.isEdge(i,j);
+}
+
+// ---------------------------------------------------------------
+
+public Collection<Integer> getNeighbours(int i) {
+
+ if( i<0 || i>=prefSize ) throw new IndexOutOfBoundsException();
+
+ List<Integer> result = new LinkedList<Integer>();
+ for(Integer j:g.getNeighbours(i))
+ {
+ if( j < prefSize ) result.add(j);
+ }
+
+ return Collections.unmodifiableCollection(result);
+}
+
+// ---------------------------------------------------------------
+
+public Object getNode(int i) {
+
+ if( i<0 || i>=prefSize ) throw new IndexOutOfBoundsException();
+ return g.getNode(i);
+}
+
+// ---------------------------------------------------------------
+
+/**
+* Returns the edge in the original graph if both i and j are smaller than
+* size().
+*/
+public Object getEdge(int i, int j) {
+
+ if( isEdge(i,j) ) return g.getEdge(i,j);
+ return null;
+}
+
+// --------------------------------------------------------------------
+
+public int size() { return prefSize; }
+
+// --------------------------------------------------------------------
+
+public boolean directed() { return g.directed(); }
+
+// --------------------------------------------------------------------
+
+/** not supported */
+public boolean setEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// ---------------------------------------------------------------
+
+/** not supported */
+public boolean clearEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// ---------------------------------------------------------------
+
+public int degree(int i) {
+
+ if( i<0 || i>=prefSize ) throw new IndexOutOfBoundsException();
+ return g.degree(i);
+}
+
+
+// ================= public functions =================================
+// ====================================================================
+
+
+/**
+* Sets the size of the subgraph. If i is negative, it is changed to 0 and
+* if it is larger than the underlying graph size, it is changed to the
+* underlying graph size (set at construction time).
+* @return old size.
+*/
+public int setSize(int i) {
+
+ int was = prefSize;
+ if( i < 0 ) i = 0;
+ if( i > g.size() ) i=g.size();
+ prefSize=i;
+ return was;
+}
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+
+/**
+* This class is an adaptor for representing subgraphs of any graph.
+* The subgraph is defined the following way.
+* The subgraph always contains all the nodes of the original underlying
+* graph. However, it is possible to remove edges by flagging nodes as
+* removed, in which case
+* the edges that have at least one end on those nodes are removed.
+* If the underlying graph changes after initialization, this class follows
+* the change.
+*/
+public class SubGraphEdges implements Graph {
+
+
+// ====================== private fields ========================
+// ==============================================================
+
+
+private final Graph g;
+
+private final BitSet nodes;
+
+
+// ====================== public constructors ===================
+// ==============================================================
+
+
+/**
+* Constructs an initially empty subgraph of g. That is, the subgraph will
+* contain no nodes.
+*/
+public SubGraphEdges( Graph g ) {
+
+ this.g = g;
+ nodes = new BitSet(g.size());
+}
+
+
+// ======================= Graph implementations ================
+// ==============================================================
+
+
+public boolean isEdge(int i, int j) {
+
+ return nodes.get(i) && nodes.get(j) && g.isEdge(i,j);
+}
+
+// ---------------------------------------------------------------
+
+public Collection<Integer> getNeighbours(int i) {
+
+ List<Integer> result = new LinkedList<Integer>();
+ if( nodes.get(i) )
+ {
+ for(Integer in:g.getNeighbours(i))
+ {
+ if( nodes.get(in) ) result.add(in);
+ }
+ }
+
+ return Collections.unmodifiableCollection(result);
+}
+
+// ---------------------------------------------------------------
+
+public Object getNode(int i) { return g.getNode(i); }
+
+// ---------------------------------------------------------------
+
+/**
+* If both i and j are within the node set of the subgraph and the original
+* graph has an (i,j) edge, returns that edge.
+*/
+public Object getEdge(int i, int j) {
+
+ if( isEdge(i,j) ) return g.getEdge(i,j);
+ return null;
+}
+
+// --------------------------------------------------------------------
+
+public int size() { return g.size(); }
+
+// --------------------------------------------------------------------
+
+public boolean directed() { return g.directed(); }
+
+// --------------------------------------------------------------------
+
+/** not supported */
+public boolean setEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// ---------------------------------------------------------------
+
+/** not supported */
+public boolean clearEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// ---------------------------------------------------------------
+
+public int degree(int i) {
+
+ int degree=0;
+ if( nodes.get(i) )
+ {
+ for(Integer in:g.getNeighbours(i))
+ {
+ if( nodes.get(in) ) degree++;
+ }
+ }
+ return degree;
+}
+
+
+// ================= public functions =================================
+// ====================================================================
+
+
+/**
+* This function returns the size of the subgraph, i.e. the number of nodes
+* in the subgraph.
+*/
+public int subGraphSize() { return nodes.cardinality(); }
+
+// --------------------------------------------------------------------
+
+/**
+* Removes given node from subgraph.
+* @return true if the node was already in the subgraph otherwise false.
+*/
+public boolean removeNode(int i) {
+
+ boolean was = nodes.get(i);
+ nodes.clear(i);
+ return was;
+}
+
+// --------------------------------------------------------------------
+
+/**
+* Adds given node to subgraph.
+* @return true if the node was already in the subgraph otherwise false.
+*/
+public boolean addNode(int i) {
+
+ boolean was = nodes.get(i);
+ nodes.set(i);
+ return was;
+}
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.graph;
+
+import java.util.*;
+
+/**
+* This class is an adaptor making any Graph an undirected graph
+* by making its edges bidirectional. The graph to be made undirected
+* is passed to the constructor. Only the reference is stored so
+* if the directed graph changes later, the undirected version will
+* follow that change. However, {@link #getNeighbours} has O(n) time complexity
+* (in other words, too slow for large graphs).
+* @see ConstUndirGraph
+*/
+public class UndirectedGraph implements Graph {
+
+
+// ====================== private fileds ========================
+// ==============================================================
+
+
+private final Graph g;
+
+
+// ====================== public constructors ===================
+// ==============================================================
+
+
+public UndirectedGraph( Graph g ) {
+
+ this.g = g;
+}
+
+
+// ======================= Graph implementations ================
+// ==============================================================
+
+
+public boolean isEdge(int i, int j) {
+
+ return g.isEdge(i,j) || g.isEdge(j,i);
+}
+
+// ---------------------------------------------------------------
+
+/**
+* Uses sets as collection so does not support multiple edges now, even if
+* the underlying directed graph does.
+*/
+public Collection<Integer> getNeighbours(int i) {
+
+ Set<Integer> result = new HashSet<Integer>();
+ result.addAll(g.getNeighbours(i));
+ final int max = g.size();
+ for(int j=0; j<max; ++j)
+ {
+ if( g.isEdge(j,i) ) result.add(j);
+ }
+
+ return Collections.unmodifiableCollection(result);
+}
+
+// ---------------------------------------------------------------
+
+public Object getNode(int i) { return g.getNode(i); }
+
+// ---------------------------------------------------------------
+
+/**
+* If there is an (i,j) edge, returns that, otherwise if there is a (j,i)
+* edge, returns that, otherwise returns null.
+*/
+public Object getEdge(int i, int j) {
+
+ if( g.isEdge(i,j) ) return g.getEdge(i,j);
+ if( g.isEdge(j,i) ) return g.getEdge(j,i);
+ return null;
+}
+
+// ---------------------------------------------------------------
+
+public int size() { return g.size(); }
+
+// --------------------------------------------------------------------
+
+public boolean directed() { return false; }
+
+// --------------------------------------------------------------------
+
+/** not supported */
+public boolean setEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// ---------------------------------------------------------------
+
+/** not supported */
+public boolean clearEdge( int i, int j ) {
+
+ throw new UnsupportedOperationException();
+}
+
+// --------------------------------------------------------------------
+
+public int degree(int i) {
+
+ return getNeighbours(i).size();
+}
+
+// --------------------------------------------------------------------
+/*
+public static void main( String[] args ) {
+
+
+ Graph net = null;
+ UndirectedGraph ug = new UndirectedGraph(net);
+ for(int i=0; i<net.size(); ++i)
+ System.err.println(i+" "+net.getNeighbours(i));
+ System.err.println("============");
+ for(int i=0; i<ug.size(); ++i)
+ System.err.println(i+" "+ug.getNeighbours(i));
+ for(int i=0; i<ug.size(); ++i)
+ {
+ for(int j=0; j<ug.size(); ++j)
+ System.err.print(ug.isEdge(i,j)?"W ":"- ");
+ System.err.println();
+ }
+
+ GraphIO.writeGML(net,System.out);
+}
+*/
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.rangesim;
+
+public interface ProcessHandler
+{
+
+/**
+ * Stop the external process managed by this object.
+ */
+public abstract void doStop();
+
+/**
+ * Wait until the process has terminated.
+ * @exception InterruptedException
+ * thrown if the wait is interrupted
+ */
+public abstract void join()
+ throws InterruptedException;
+
+}
\ No newline at end of file
--- /dev/null
+package peersim.rangesim;
+
+import java.util.*;
+
+/**
+ * This thread is used to kill forked processes in the case of an abnormal
+ * termination of the Java virtual machine (for example, due to a signal).
+ */
+public class ProcessManager extends Thread
+{
+
+/** The threads that must be killed */
+private List<ProcessHandler> threads;
+
+public ProcessManager()
+{
+ threads = Collections.synchronizedList(new ArrayList<ProcessHandler>());
+}
+
+public void addThread(ProcessHandler p)
+{
+ threads.add(p);
+}
+
+/**
+ * Assumes that the process manager
+ */
+public void joinAll()
+{
+ int i=0;
+ while (i < threads.size()) {
+ try {
+ threads.get(i).join();
+ i++;
+ } catch (InterruptedException e) {
+ }
+ }
+}
+
+
+/**
+ * Kill the child process.
+ */
+public void run()
+{
+ System.err.println("Terminating simulation.");
+ for (int i=0; i < threads.size(); i++) {
+ if (threads.get(i) != null)
+ threads.get(i).doStop();
+ }
+}
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.rangesim;
+
+import java.io.*;
+import java.util.*;
+
+import peersim.*;
+import peersim.config.*;
+import peersim.core.*;
+import peersim.util.*;
+
+/**
+ * This class is the main class for the Range Simulator. A range is
+ * a collection of values <em>S</em> to be assigned to a variable
+ * <em>v</em>. The Range Simulator invokes the standard Peersim
+ * simulator once for each distinct value. If multiple ranges
+ * <em>S1, S2, ..., Sn</em> are specified, the standard Peersim
+ * simulator is invoked for each element in
+ * <em>S1 * S2 * ... * Sn</em>.
+ * <p>
+ * Ranges are specified with the following syntax:
+<pre>
+range.[id] [var];[range]
+</pre>
+ * where:
+ * <UL>
+ * <LI> {@value #PAR_RANGE} is the prefix for all range
+ * specifications;</LI>
+ * <LI> <code>id</code> is an identifier; since they are not referred anywhere else,
+ * consecutive numbers are a good choice for range identifiers;</LI>
+ * <LI> <code>var</code> is a variable parameter </LI>
+ * <LI> <code>range</code> describes the collection of values to be associated
+ * to <code>var</code>, whose syntax and semantics is defined in
+ * {@link peersim.util.StringListParser}. </LI>
+ * </UL>
+ * Examples of range specifications are the following:
+<pre>
+range.0 SIZE;2^10:2^18|*2
+range.1 K;20:30
+range.2 CHURN;0.05,0.10,0.20
+</pre>
+ * With this specification, the collection of values associated to
+ * <code>SIZE</code> is {2^10,2^11,...,2^18}; <code>K</code> contains
+ * {20,21,22,...,30}, while <code>CHURN</code> contains just the
+ * specified values.
+ * <p>
+ * Values can be specified as constant expressions (like 2^10, (5+10), etc.)
+ * but variables cannot be used.
+ * <p>
+ * A separate Java virtual machine is invoked to run each of the
+ * experiments. An attempt is done to run the same JVM version as
+ * the one running the Range Simulator; if this is not possible
+ * (for example due to path problems), the command shell mechanism
+ * is used to run the first JVM version found in the path.
+ * </p>
+ * It is possible to specify options for the forked JVM using the
+ * {@value #PAR_JVM} parameter on the command line. For example,
+ * a command line like this:
+<pre>
+java peersim.rangesim.RangeSimulator config.file jvm.options=-Xmx256m
+</pre>
+ * can be used to run the forked JVM with a maximum heap of 256MB.
+ * <p>
+ * The new JVM inherits the same classpath as the JVM running the
+ * RangeSimulator. The {@value #PAR_JVM} parameter can be used to
+ * specify additional classpath specification.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.11 $
+ */
+public class RangeSimulator implements ProcessHandler
+{
+
+// --------------------------------------------------------------------------
+// Configuration parameters
+// --------------------------------------------------------------------------
+
+/**
+ * This is the prefix of the config properties whose value vary during
+ * a set of experiments.
+ * @config
+ */
+private static final String PAR_RANGE = "range";
+
+/**
+ * This config property can be used to set options in the JVMs that
+ * are forked to execute experiments with different configuration
+ * parameters.
+ * @config
+ */
+public static final String PAR_JVM = "jvm.options";
+
+
+// --------------------------------------------------------------------------
+// Static variables
+// --------------------------------------------------------------------------
+
+/** Names of range parameters */
+private String[] pars;
+
+/** Values to be simulated, for each parameter */
+private String[][] values;
+
+/** The jvm options to be used when creating jvms */
+private String[] jvmoptions;
+
+/** Command line arguments */
+private String[] args;
+
+/** The current process that is executed */
+private Process p;
+
+
+// --------------------------------------------------------------------------
+// Main
+// --------------------------------------------------------------------------
+
+/**
+ * Main method of the system.
+ */
+public static void main(String[] args)
+{
+ RangeSimulator r = new RangeSimulator(args);
+ r.run();
+}
+
+//--------------------------------------------------------------------------
+// Constructor
+//--------------------------------------------------------------------------
+
+public RangeSimulator(String[] args)
+{
+
+ // Check if there are no arguments or there is an explicit --help
+ // flag; if so, print the usage of the class
+ if (args.length == 0 || args[0].equals("--help")) {
+ usage();
+ System.exit(101);
+ }
+
+ this.args = args.clone();
+
+ // Read property file
+ System.err.println("Simulator: loading configuration");
+ Properties properties = new ParsedProperties(args);
+ Configuration.setConfig(properties);
+
+ // Read jvm options and separate them in different strings
+ String opt = Configuration.getString(PAR_JVM, null);
+ if (opt == null)
+ jvmoptions = new String[0];
+ else
+ jvmoptions = opt.split(" ");
+
+ // Parse range parameters
+ parseRanges();
+
+}
+
+/**
+ * Main method to be executed
+ */
+public void run()
+{
+ // Shutdown thread management
+ ProcessManager t = new ProcessManager();
+ t.addThread(this);
+ Runtime.getRuntime().addShutdownHook(t);
+
+ // Executes experiments; report short messages about exceptions that are
+ // handled by the configuration mechanism.
+ try {
+ doExperiments(args);
+ } catch (MissingParameterException e) {
+ Runtime.getRuntime().removeShutdownHook(t);
+ System.err.println(e + "");
+ System.exit(101);
+ } catch (IllegalParameterException e) {
+ Runtime.getRuntime().removeShutdownHook(t);
+ System.err.println(e + "");
+ System.exit(101);
+ }
+ Runtime.getRuntime().removeShutdownHook(t);
+ System.exit(0);
+}
+
+// --------------------------------------------------------------------
+
+/**
+ * Parses a collection of range specifications and returns the set of
+ * parameter that will change during the simulation and the values that
+ * will be used for those parameters.
+ */
+private void parseRanges()
+{
+ // Get ranges
+ String[] ranges = Configuration.getNames(PAR_RANGE);
+
+ // Start is the first element in which ranges are stored
+ int start;
+
+ // If there is an explicit simulation.experiment or there are no
+ // ranges, put an experiment range at the beginning of the values.
+ // Otherwise, just use the ranges.
+ if (Configuration.contains(Simulator.PAR_EXPS) || ranges.length == 0) {
+ pars = new String[ranges.length + 1];
+ values = new String[ranges.length + 1][];
+ pars[0] = "EXP";
+ values[0] = StringListParser.parseList("1:"
+ + Configuration.getInt(Simulator.PAR_EXPS, 1));
+ start = 1;
+ } else {
+ pars = new String[ranges.length];
+ values = new String[ranges.length][];
+ start = 0;
+ }
+
+ for (int i = start; i < pars.length; i++) {
+ String[] array = Configuration.getString(ranges[i-start]).split(";");
+ if (array.length != 2) {
+ throw new IllegalParameterException(ranges[i],
+ " should be formatted as <parameter>;<value list>");
+ }
+ pars[i] = array[0];
+ values[i] = StringListParser.parseList(array[1]);
+ }
+}
+
+// --------------------------------------------------------------------
+
+/**
+ * Selects the next set of values by incrementing the specified index
+ * array. The index array is treated as a vector of digits; the first is
+ * managed managed as a vector of digits.
+ */
+private void nextValues(int[] idx, String[][] values)
+{
+ idx[idx.length - 1]++;
+ for (int j = idx.length - 1; j > 0; j--) {
+ if (idx[j] == values[j].length) {
+ idx[j] = 0;
+ idx[j - 1]++;
+ }
+ }
+}
+
+// --------------------------------------------------------------------
+
+private void doExperiments(String[] args)
+{
+
+ // Configure the java parameter for exception
+ String filesep = System.getProperty("file.separator");
+ String classpath = System.getProperty("java.class.path");
+ String javapath = System.getProperty("java.home") + filesep + "bin" + filesep
+ + "java";
+ ArrayList<String> list = new ArrayList<String>(20);
+ list.add(javapath);
+ list.add("-cp");
+ list.add(classpath);
+
+ // Add the jvm options
+ for (int i=0; i < jvmoptions.length; i++)
+ list.add(jvmoptions[i]);
+
+ // The class to be run in the forked JVM
+ list.add("peersim.Simulator");
+
+ // Parameters specified on the command line
+ for (int i=0; i < args.length; i++) {
+ list.add(args[i]);
+ }
+
+ // Since multiple experiments are managed here, the value
+ // of standard variable for multiple experiments is changed to 1
+ list.add(Simulator.PAR_EXPS+"=1");
+
+ // Activate redirection to separate stdout from stderr
+ list.add(Simulator.PAR_REDIRECT+"="+TaggedOutputStream.class.getCanonicalName());
+ int startlog = list.size();
+ list.add("");
+
+ // Create a placeholder for the seed
+ int startseed = list.size();
+ list.add("");
+
+ // Create placeholders for the range parameters
+ int startpar = list.size();
+ for (int i=0; i < values.length; i++)
+ list.add("");
+
+ // Execute with different values
+ int[] idx = new int[values.length]; // Initialized to 0
+ while (idx[0] < values[0].length) {
+
+ // Configure the argument string array
+ for (int j = 0; j < pars.length; j++) {
+ list.set(startpar + j, pars[j] + "=" + values[j][idx[j]]);
+ }
+
+ // Fill the log placeholder
+ StringBuffer log = new StringBuffer();
+ for (int j = 0; j < pars.length; j++) {
+ log.append(pars[j]);
+ log.append(" ");
+ log.append(values[j][idx[j]]);
+ log.append(" ");
+ }
+ list.set(startlog, Simulator.PAR_REDIRECT+"."+
+ TaggedOutputStream.PAR_RANGES+"="+log);
+
+ // Fill the seed place holder
+ long seed = CommonState.r.nextLong();
+ list.set(startseed, CommonState.PAR_SEED+"="+seed);
+
+ System.err.println("Experiment: " + log);
+
+ executeProcess(list);
+
+ // Increment values
+ nextValues(idx, values);
+
+ }
+}
+
+//--------------------------------------------------------------------
+
+/**
+ * Execute the "command line" represented by this String list.
+ * The first argument is the process to be executed. We try
+ * to run the same JVM as the current one. If not possible,
+ * we use the first java command found in the path.
+ */
+private void executeProcess(List<String> list)
+{
+ // Prepare the argument array for process forking
+ String[] newargs = new String[list.size()];
+
+ // Execute a new JVM
+ try {
+ ProcessBuilder pb = new ProcessBuilder(list.toArray(newargs));
+ pb.redirectErrorStream(true);
+ p = pb.start();
+ } catch (IOException e1) {
+ try {
+ list.set(0, "java");
+ ProcessBuilder pb = new ProcessBuilder(list.toArray(newargs));
+ pb.redirectErrorStream(true);
+ p = pb.start();
+ } catch (IOException e2) {
+ System.err.println("Unable to launch a Java virtual machine");
+ System.exit(1);
+ }
+ }
+
+ // Read the output from the process and redirect it to System.out
+ // and System.err.
+ BufferedReader toprint = new BufferedReader(new InputStreamReader(p
+ .getInputStream()));
+ String line;
+ while ((line = getLine(toprint)) != null) {
+ if (line.length() == 0) {
+ System.out.println();
+ } else {
+ int last = line.charAt(line.length()-1);
+ if (last != TaggedOutputStream.TAG) {
+ System.err.println(line);
+ } else {
+ line = line.substring(0, line.length()-1);
+ System.out.println(line);
+ }
+ }
+ }
+
+ // We close all the files and we destroy the process. They are not
+ // cleaned when the process is closed. See:
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
+ // http://www.thescripts.com/forum/thread18019.html
+ try {
+ p.getErrorStream().close();
+ p.getInputStream().close();
+ p.getOutputStream().close();
+ p.destroy();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // The static variable p (used also by ShutdownThread) is back to
+ // null - no process must be killed on shutdown.
+ p = null;
+
+
+
+}
+
+//--------------------------------------------------------------------
+
+private static String getLine(BufferedReader toprint)
+{
+ try {
+ return toprint.readLine();
+ } catch (IOException e) {
+ // If we get here, this means that the forked process has
+ // been killed by the shutdown thread. We just exit without
+ // printing this exception.
+ System.exit(1);
+ return null; // Never reached, but needed.
+ }
+}
+
+
+// --------------------------------------------------------------------
+
+private static void usage()
+{
+ System.err.println("Usage:");
+ System.err.println(" peersim.RangeSimulator <configfile> [property]*");
+}
+
+//--------------------------------------------------------------------
+
+RangeSimulator()
+{
+}
+
+/**
+ * Stop the process executing the external java virtual machine.
+ */
+public void doStop()
+{
+ if (p != null)
+ p.destroy();
+}
+
+/**
+ * Wait until the java virtual machine has terminated; it won't be
+ * used in this class, but you never know.
+ */
+public void join() throws InterruptedException
+{
+ p.waitFor();
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.rangesim;
+
+import java.io.*;
+import java.util.*;
+
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * This OutputStream uses an underlying stream to output
+ * data. Each line (terminated with `\n`) is augmented
+ * with a tag character. This is used to discriminate
+ * among standard error and standard output. This
+ * feature is needed for launching new JVMs; it should
+ * not be used for other purposes.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.5 $
+ */
+public class TaggedOutputStream extends PrintStream
+{
+
+//--------------------------------------------------------------------------
+//Constants
+//--------------------------------------------------------------------------
+
+/**
+ * This character is appended at the end of each line.
+ */
+public static final int TAG = 1;
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * This parameter contains the string that should be printed on each
+ * line, containing the values of the range parameters for the experiment
+ * which is being run. The full name of this configuration string is
+ * prefixed by {@value peersim.Simulator#PAR_REDIRECT}.
+ * @config
+ */
+public static final String PAR_RANGES = "ranges";
+
+/**
+ * This parameter contains the list of observers for which the string
+ * contained in parameter {@value #PAR_RANGES} should be augmented with
+ * a "TIME <t>" specification regarding current time. Observers are
+ * separated by one of this characters: ' ' - ',' - ';'.
+ * @config
+ */
+public static final String PAR_TIME = "simulation.timed-observers";
+
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+/** Variable used to save the original System.out to simplify printing */
+private PrintStream stdout;
+
+/** Buffer used to store a single line; it can grow */
+private byte[] buffer = new byte[1024];
+
+/** Current size of the buffer */
+private int size;
+
+/** The value of the PAR_RANGES parameter */
+private final String ranges;
+
+/** The value of the PAR_TIME parameter */
+private final ArrayList<String> obstime;
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+
+/**
+ * Creates a tagged output stream that prints the tagged
+ * output on the specified stream.
+ */
+public TaggedOutputStream(String prefix)
+{
+ super(System.out);
+
+ obstime = new ArrayList<String>();
+ String[] obs = Configuration.getString(PAR_TIME, "").split("[ :,]");
+ for (int i=0; i < obs.length; i++) {
+ obstime.add("control." + obs[i]);
+ }
+ ranges = Configuration.getString(prefix + "." + PAR_RANGES, "");
+ stdout = System.out;
+ size = 0;
+}
+
+//--------------------------------------------------------------------------
+//Methods
+//--------------------------------------------------------------------------
+
+// Comment inherited from interface
+@Override
+public synchronized void write(byte[] b, int off, int len)
+{
+ if (size+len > buffer.length) {
+ byte[] tmp = new byte[Math.max(buffer.length*2, size+len)];
+ System.arraycopy(buffer, 0, tmp, 0, size);
+ buffer = tmp;
+ }
+ int last = off+len;
+ for (int i=off; i < last; i++) {
+ if (b[i] == '\n') {
+ buffer[size++] = TAG;
+ buffer[size++] = b[i];
+ printLine();
+ } else {
+ buffer[size++] = b[i];
+ }
+ }
+}
+
+// Comment inherited from interface
+@Override
+public synchronized void write(int b)
+{
+ if (size == buffer.length) {
+ byte[] tmp = new byte[buffer.length*2];
+ System.arraycopy(buffer, 0, tmp, 0, size);
+ buffer = tmp;
+ }
+ if (b == '\n') {
+ buffer[size++] = TAG;
+ buffer[size++] = (byte) b;
+ printLine();
+ } else {
+ buffer[size++] = (byte) b;
+ }
+}
+
+/**
+ * Actually prints a line, inserting ranges and time
+ * when needed.
+ */
+private void printLine()
+{
+ String line = new String(buffer, 0, size);
+ String[] parts = line.split(":");
+ if (parts.length == 2) {
+ stdout.print(parts[0]);
+ stdout.print(": ");
+ stdout.print(ranges);
+ if (obstime.contains(parts[0]))
+ stdout.print(" TIME " + CommonState.getTime() + " ");
+ stdout.print(parts[1]);
+ } else {
+ stdout.print(line);
+ }
+ size = 0;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.util.*;
+
+/**
+ * Control to observe the ball expansion, that is,
+ * the number of nodes that are
+ * accessible from a given node in at most 1, 2, etc steps.
+ */
+public class BallExpansion extends GraphObserver
+{
+
+// ===================== fields =======================================
+// ====================================================================
+
+/**
+ * This parameter defines the maximal distance we care about.
+ * In other words, two nodes are further away, they will not be taken
+ * into account when calculating statistics.
+ * <p>
+ * Defaults to the
+ * network size (which means all distances are taken into account).
+ * Note that this default is normally way too much; many low diameter graphs
+ * have only short distances between the nodes. Setting a short
+ * (but sufficient) distance saves memory.
+ * Also note that the <em>initial</em> network
+ * size is used if no value is given which might not be what you want if e.g. the
+ * network is growing.
+ * @config
+ */
+private static final String PAR_MAXD = "maxd";
+
+/**
+ * The number of nodes to print info about.
+ * Defaults to 1000. If larger than the current network size, then the
+ * current network size is used.
+ * @config
+ */
+private static final String PAR_N = "n";
+
+/**
+ * If defined, statistics are printed instead over the minimal path lengths. Not
+ * defined by default.
+ * @config
+ */
+private static final String PAR_STATS = "stats";
+
+private final int maxd;
+
+private final int n;
+
+private final boolean stats;
+
+/** working variable */
+private final int[] b;
+
+private final RandPermutation rp = new RandPermutation(CommonState.r);
+
+// ===================== initialization ================================
+// =====================================================================
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+public BallExpansion(String name)
+{
+ super(name);
+ maxd = Configuration.getInt(name + "." + PAR_MAXD, Network.size());
+ n = Configuration.getInt(name + "." + PAR_N, 1000);
+ stats = Configuration.contains(name + "." + PAR_STATS);
+ b = new int[maxd];
+}
+
+// ====================== methods ======================================
+// =====================================================================
+
+/**
+* Prints information about ball expansion. It uses {@value #PAR_N} nodes to
+* collect statistics.
+* If parameter {@value #PAR_STATS} is defined then the output is
+* produced by {@link IncrementalStats#toString}, over the values of minimal
+* distances from the given number of nodes to all other nodes in the network.
+* If at least one node is unreachable from any selected starting node, then
+* the path length is taken as infinity and statistics are calculated
+* accordingly. In that case, {@link IncrementalStats#getMaxCount()} returns
+* the number of nodes of "infinite distance", that is, the number of
+* unreachable nodes.
+* Otherwise one line is printed for all nodes we observe, containing the
+* number of nodes at distance 1, 2, etc, separated by spaces.
+* In this output format, unreachable nodes are simply ignored, but of course
+* the sum of the numbers in one line can be used to detect partitioning if
+* necessary.
+* Finally, note that the {@value #PAR_N} nodes are not guaranteed to be the
+* same nodes over consecutive calls to this method.
+* @return always false
+*/
+public boolean execute() {
+
+ updateGraph();
+ System.out.print(name + ": ");
+ rp.reset(g.size());
+ if (stats)
+ {
+ IncrementalStats is = new IncrementalStats();
+ for (int i = 0; i < n && i < g.size(); ++i)
+ {
+ ga.dist(g, rp.next());
+ for (int j=0; j<g.size(); j++)
+ {
+ if (ga.d[j] > 0)
+ is.add(ga.d[j]);
+ else if (ga.d[j] == -1)
+ is.add(Double.POSITIVE_INFINITY);
+ // deliberately left ga.d[j]==0 out, as we don't
+ // want to count trivial distance to oneself.
+ }
+ }
+ System.out.println(is);
+ }
+ else
+ {
+ System.out.println();
+ for (int i = 0; i < n && i < g.size(); ++i)
+ {
+ ga.flooding(g, b, rp.next());
+ int j = 0;
+ while (j < b.length && b[j] > 0)
+ {
+ System.out.print(b[j++] + " ");
+ }
+ System.out.println();
+ }
+ }
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import peersim.config.Configuration;
+import peersim.graph.GraphAlgorithms;
+import peersim.util.IncrementalStats;
+
+/**
+ * Control to observe the clustering coefficient.
+ * @see GraphAlgorithms#clustering
+ */
+public class Clustering extends GraphObserver
+{
+
+// ===================== fields =======================================
+// ====================================================================
+
+/**
+ * The number of nodes to collect info about. Defaults to the size of the graph.
+ * @config
+ */
+private static final String PAR_N = "n";
+
+private final int n;
+
+// ===================== initialization ================================
+// =====================================================================
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+public Clustering(String name)
+{
+ super(name);
+ n = Configuration.getInt(name + "." + PAR_N, Integer.MAX_VALUE);
+}
+
+// ====================== methods ======================================
+// =====================================================================
+
+/**
+* Prints information about the clustering coefficient.
+* It uses {@value #PAR_N} nodes to collect statistics.
+* The output is
+* produced by {@link IncrementalStats#toString}, over the values of
+* the clustering coefficients of the given number of nodes.
+* Clustering coefficients are calculated by {@link GraphAlgorithms#clustering}.
+* @return always false
+*/
+public boolean execute()
+{
+ IncrementalStats stats = new IncrementalStats();
+ updateGraph();
+ for (int i = 0; i < n && i < g.size(); ++i) {
+ stats.add(GraphAlgorithms.clustering(g, i));
+ }
+ System.out.println(name + ": " + stats);
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import java.util.Iterator;
+import java.util.Map;
+import peersim.config.Configuration;
+import peersim.util.IncrementalStats;
+
+/**
+ * Reports statistics about connectivity properties of the network, such as
+ * weakly or strongly connected clusters.
+ */
+public class ConnectivityObserver extends GraphObserver
+{
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * The parameter used to request cluster size statistics instead of the usual
+ * list of clusters. Not set by default.
+ * @config
+ */
+private static final String PAR_STATS = "stats";
+
+/**
+ * Defines the types of connected clusters to discover.
+ * Possible values are
+ * <ul>
+ * <li>"wcc": weakly connected clusters</li>
+ * <li>"scc": strongly connected clusters</li>
+ * </ul>
+ * Defaults to "wcc".
+ * @config
+ */
+private static final String PAR_TYPE = "type";
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+/** {@link #PAR_STATS} */
+private final boolean sizestats;
+
+/** {@link #PAR_TYPE} */
+private final String type;
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+public ConnectivityObserver(String name)
+{
+ super(name);
+ sizestats = Configuration.contains(name + "." + PAR_STATS);
+ type = Configuration.getString(name + "." + PAR_TYPE,"wcc");
+}
+
+//--------------------------------------------------------------------------
+//Methods
+//--------------------------------------------------------------------------
+
+/**
+* Prints information about clusters.
+* If parameter {@value #PAR_STATS} is defined then the output is
+* produced by {@link IncrementalStats#toString}, over the sizes of the
+* clusters.
+* Otherwise one line is printed that contains the string representation of
+* a map, that holds cluster IDs mapped to cluster sizes.
+* The meaning of the cluster IDs is not specified, but is printed for
+* debugging purposes.
+* @return always false
+* @see peersim.graph.GraphAlgorithms#tarjan
+* @see peersim.graph.GraphAlgorithms#weaklyConnectedClusters
+*/
+public boolean execute()
+{
+ Map clst;
+ updateGraph();
+
+ if(type.equals("wcc"))
+ clst=ga.weaklyConnectedClusters(g);
+ else if(type.equals("scc"))
+ clst=ga.tarjan(g);
+ else
+ throw new RuntimeException(
+ "Unsupported connted cluster type '"+type+"'");
+
+ if (!sizestats) {
+ System.out.println(name + ": " + clst);
+ } else {
+ IncrementalStats stats = new IncrementalStats();
+ Iterator it = clst.values().iterator();
+ while (it.hasNext()) {
+ stats.add(((Integer) it.next()).intValue());
+ }
+ System.out.println(name + ": " + stats);
+ }
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.util.*;
+
+/**
+ * Prints several statistics about the node degrees in the graph.
+ */
+public class DegreeStats extends GraphObserver
+{
+
+//--------------------------------------------------------------------------
+//Parameter
+//--------------------------------------------------------------------------
+
+/**
+ * The number of nodes to be used for sampling the degree.
+ * Defaults to full size of the graph.
+ * @config
+ */
+private static final String PAR_N = "n";
+
+/**
+ * If defined, then the given number of nodes will be traced. That is, it is
+ * guaranteed that in each call the same nodes will be picked in the same order.
+ * If a node being traced fails, its degree will be considered 0. Not defined by
+ * default.
+ * @config
+ */
+private static final String PAR_TRACE = "trace";
+
+/**
+ * Selects a method to use when printing results. Three methods are known:
+ * "stats" will use {@link IncrementalStats#toString}. "freq" will
+ * use {@link IncrementalFreq#print}. "list" will print the
+ * degrees of the sample nodes one by one in one line, separated by spaces.
+ * Default is "stats".
+ * @config
+ */
+private static final String PAR_METHOD = "method";
+
+/**
+ * Selects the types of links to print information about. Three methods are
+ * known: "live": links pointing to live nodes, "dead": links pointing to nodes
+ * that are unavailable and "all": both dead and live links summed. "all" and
+ * "dead" require parameter {@value peersim.reports.GraphObserver#PAR_UNDIR}
+ * to be unset (graph must be directed). Default is "live".
+ * @config
+ */
+private static final String PAR_TYPE = "linktype";
+
+//--------------------------------------------------------------------------
+//Parameter
+//--------------------------------------------------------------------------
+
+private final int n;
+
+private final boolean trace;
+
+private Node[] traced = null;
+
+private final String method;
+
+private final String type;
+
+private final RandPermutation rp = new RandPermutation(CommonState.r);
+
+private int nextnode = 0;
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+public DegreeStats(String name)
+{
+ super(name);
+ n = Configuration.getInt(name + "." + PAR_N, -1);
+ trace = Configuration.contains(name + "." + PAR_TRACE);
+ method = Configuration.getString(name + "." + PAR_METHOD, "stats");
+ type = Configuration.getString(name + "." + PAR_TYPE, "live");
+ if ((type.equals("all") || type.equals("dead")) && undir) {
+ throw new IllegalParameterException(
+ name + "." + PAR_TYPE, " Parameter "+ name + "." +
+ PAR_UNDIR + " must not be defined if " + name + "."
+ + PAR_TYPE + "=" + type + ".");
+ }
+}
+
+//--------------------------------------------------------------------------
+//Methods
+//--------------------------------------------------------------------------
+
+/**
+ * Returns next node to get degree information about.
+ */
+private int nextNodeId()
+{
+ if (trace) {
+ if (traced == null) {
+ int nn = (n < 0 ? Network.size() : n);
+ traced = new Node[nn];
+ for (int j = 0; j < nn; ++j)
+ traced[j] = Network.get(j);
+ }
+ return traced[nextnode++].getIndex();
+ } else
+ return rp.next();
+}
+
+// ---------------------------------------------------------------------
+
+/**
+ * Returns degree information about next node.
+ */
+private int nextDegree()
+{
+ final int nodeid = nextNodeId();
+ if (type.equals("live")) {
+ return g.degree(nodeid);
+ } else if (type.equals("all")) {
+ return ((OverlayGraph) g).fullDegree(nodeid);
+ } else if (type.equals("dead")) {
+ return ((OverlayGraph) g).fullDegree(nodeid) - g.degree(nodeid);
+ } else
+ throw new RuntimeException(name + ": invalid type");
+}
+
+// ---------------------------------------------------------------------
+
+/**
+ * Prints statistics about node degree. The format of the output is specified
+ * by {@value #PAR_METHOD}. See also the rest of the configuration parameters.
+ * @return always false
+ */
+public boolean execute()
+{
+ updateGraph();
+ if (!trace)
+ rp.reset(g.size());
+ else
+ nextnode = 0;
+ final int nn = (n < 0 ? Network.size() : n);
+ if (method.equals("stats")) {
+ IncrementalStats stats = new IncrementalStats();
+ for (int i = 0; i < nn; ++i)
+ stats.add(nextDegree());
+ System.out.println(name + ": " + stats);
+ } else if (method.equals("freq")) {
+ IncrementalFreq stats = new IncrementalFreq();
+ for (int i = 0; i < nn; ++i)
+ stats.add(nextDegree());
+ stats.print(System.out);
+ System.out.println("\n\n");
+ } else if (method.equals("list")) {
+ System.out.print(name + ": ");
+ for (int i = 0; i < nn; ++i)
+ System.out.print(nextDegree() + " ");
+ System.out.println();
+ }
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import peersim.core.*;
+import peersim.config.Configuration;
+import peersim.graph.*;
+import peersim.cdsim.CDState;
+
+/**
+* Class that provides functionality for observing graphs.
+* It can efficiently create an undirected version of the graph, making sure
+* it is updated only when the simulation has advanced already, and provides
+* some common parameters.
+*/
+public abstract class GraphObserver implements Control {
+
+
+// ===================== fields =======================================
+// ====================================================================
+
+/**
+ * The protocol to operate on.
+ * @config
+ */
+private static final String PAR_PROT = "protocol";
+
+/**
+ * If defined, the undirected version of the graph will be analyzed. Not defined
+ * by default.
+ * @config
+ */
+protected static final String PAR_UNDIR = "undir";
+
+/**
+* Alias for {@value #PAR_UNDIR}.
+* @config
+*/
+private static final String PAR_UNDIR_ALT = "undirected";
+
+/**
+ * If defined, the undirected version of the graph will be stored using much
+ * more memory but observers will be in general a few times faster. As a
+ * consequence, it will not work with large graphs. Not defined by default. It
+ * is a static property, that is, it affects all graph observers that are used
+ * in a simulation. That is, it is not a parameter of any observer, the name
+ * should be specified as a standalone property.
+ * @config
+ */
+private static final String PAR_FAST = "graphobserver.fast";
+
+/** The name of this observer in the configuration */
+protected final String name;
+
+protected final int pid;
+
+protected final boolean undir;
+
+protected final GraphAlgorithms ga = new GraphAlgorithms();
+
+protected Graph g;
+
+// ---------------------------------------------------------------------
+
+private static int lastpid = -1234;
+
+private static long time = -1234;
+
+private static int phase = -1234;
+
+private static int ctime = -1234;
+
+private static Graph dirg;
+
+private static Graph undirg;
+
+private static boolean fast;
+
+/** If any instance of some extending class defines undir we need to
+maintain an undir graph. Note that the graph is stored in a static
+field so it is common to all instances. */
+private static boolean needUndir=false;
+
+// ===================== initialization ================================
+// =====================================================================
+
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+protected GraphObserver(String name) {
+
+ this.name = name;
+ pid = Configuration.getPid(name+"."+PAR_PROT);
+ undir = (Configuration.contains(name + "." + PAR_UNDIR) |
+ Configuration.contains(name + "." + PAR_UNDIR_ALT));
+ GraphObserver.fast = Configuration.contains(PAR_FAST);
+ GraphObserver.needUndir = (GraphObserver.needUndir || undir);
+}
+
+
+// ====================== methods ======================================
+// =====================================================================
+
+/**
+* Sets {@link #g}.
+* It MUST be called by any implementation of {@link #execute()} before
+* doing anything else.
+* Attempts to initialize {@link #g} from a
+* pre-calculated graph stored in a static field, but first it
+* checks whether it needs to be updated.
+* If the simulation time has progressed or it was calculated for a different
+* protocol, then updates this static graph as well.
+* The purpose of this mechanism is to save the time of constructing the
+* graph if many observers are run on the same graph. Time savings can be very
+* significant if the undirected version of the same graph is observed by many
+* observers.
+*/
+protected void updateGraph() {
+
+ if( CommonState.getTime() != GraphObserver.time ||
+ (CDState.isCD() && (CDState.getCycleT() != GraphObserver.ctime)) ||
+ CommonState.getPhase() != GraphObserver.phase ||
+ pid != GraphObserver.lastpid )
+ {
+ // we need to update the graphs
+
+ GraphObserver.lastpid = pid;
+ GraphObserver.time = CommonState.getTime();
+ if( CDState.isCD() ) GraphObserver.ctime = CDState.getCycleT();
+ GraphObserver.phase = CommonState.getPhase();
+
+ GraphObserver.dirg = new OverlayGraph(pid);
+ if( GraphObserver.needUndir )
+ {
+ if( fast )
+ GraphObserver.undirg =
+ new FastUndirGraph(GraphObserver.dirg);
+ else
+ GraphObserver.undirg =
+ new ConstUndirGraph(GraphObserver.dirg);
+ }
+ }
+
+ if( undir ) g = GraphObserver.undirg;
+ else g = GraphObserver.dirg;
+}
+
+}
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import peersim.config.Configuration;
+import peersim.graph.GraphIO;
+import peersim.util.FileNameGenerator;
+import java.io.PrintStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+* Prints the whole graph in a given format.
+*/
+public class GraphPrinter extends GraphObserver {
+
+
+// ===================== fields =======================================
+// ====================================================================
+
+/**
+* This is the prefix of the filename where the graph is saved.
+* The extension is ".graph" and after the prefix the basename contains
+* a numeric index that is incremented at each saving point.
+* If not given, the graph is dumped on the standard output.
+* @config
+*/
+private static final String PAR_BASENAME = "outf";
+
+/**
+* The name for the format of the output. Defaults to "neighborlist",
+* which is a plain dump of neighbors. The class
+* {@link peersim.dynamics.WireFromFile} can read this format.
+* Other supported formats are "chaco" to be used with Yehuda Koren's
+* Embedder, "netmeter" to be used with Sergi Valverde's netmeter and also
+* with pajek,
+* "edgelist" that dumps one (directed) node pair in each line for each edge,
+* "gml" that is a generic format of many graph tools, and "dot" that can
+* be used with the graphviz package.
+* @see GraphIO#writeEdgeList
+* @see GraphIO#writeChaco
+* @see GraphIO#writeNeighborList
+* @see GraphIO#writeNetmeter
+* @config
+*/
+private static final String PAR_FORMAT = "format";
+
+private final String baseName;
+
+private final FileNameGenerator fng;
+
+private final String format;
+
+
+// ===================== initialization ================================
+// =====================================================================
+
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+public GraphPrinter(String name) {
+
+ super(name);
+ baseName = Configuration.getString(name+"."+PAR_BASENAME,null);
+ format = Configuration.getString(name+"."+PAR_FORMAT,"neighborlist");
+ if(baseName!=null) fng = new FileNameGenerator(baseName,".graph");
+ else fng = null;
+}
+
+
+// ====================== methods ======================================
+// =====================================================================
+
+
+/**
+* Saves the graph according to the specifications in the configuration.
+* @return always false
+*/
+public boolean execute() {
+try {
+ updateGraph();
+
+ System.out.print(name+": ");
+
+ // initialize output streams
+ FileOutputStream fos = null;
+ PrintStream pstr = System.out;
+ if( baseName != null )
+ {
+ String fname = fng.nextCounterName();
+ fos = new FileOutputStream(fname);
+ System.out.println("writing to file "+fname);
+ pstr = new PrintStream(fos);
+ }
+ else System.out.println();
+
+ if( format.equals("neighborlist") )
+ GraphIO.writeNeighborList(g, pstr);
+ else if( format.equals("edgelist") )
+ GraphIO.writeEdgeList(g, pstr);
+ else if( format.equals("chaco") )
+ GraphIO.writeChaco(g, pstr);
+ else if( format.equals("netmeter") )
+ GraphIO.writeNetmeter(g, pstr);
+ else if( format.equals("gml") )
+ GraphIO.writeGML(g, pstr);
+ else if( format.equals("dot") )
+ GraphIO.writeDOT(g, pstr);
+ else
+ System.err.println(name+": unsupported format "+format);
+
+ if( fos != null ) fos.close();
+
+ return false;
+}
+catch( IOException e )
+{
+ throw new RuntimeException(e);
+}
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import peersim.config.Configuration;
+import peersim.graph.GraphAlgorithms;
+import peersim.util.IncrementalStats;
+
+/**
+* Prints reports on the graph like average clustering and average path length,
+* based on random sampling of the nodes.
+* In fact its functionality is a subset of the union of {@link Clustering}
+* and {@link BallExpansion}, and therefore is redundant,
+* but it is there for historical reasons.
+* @see BallExpansion
+* @see Clustering
+*/
+public class GraphStats extends GraphObserver {
+
+
+// ===================== fields =======================================
+// ====================================================================
+
+/**
+* The number of nodes to use for
+* sampling average path length.
+* Statistics are printed over a set of node pairs.
+* To create the set of pairs, we select the given number of different nodes
+* first, and then pair all these nodes with every other node in the network.
+* If zero is given, then no statistics
+* will be printed about path length. If a negative value is given then
+* the value is the full size of the graph.
+* Defaults to zero.
+* @config
+*/
+private static final String PAR_NL = "nl";
+
+/**
+* The number of nodes to use to sample
+* average clustering.
+* If zero is given, then no statistics
+* will be printed about clustering. If a negative value is given then
+* the value is the full size of the graph.
+* Defaults to zero.
+* @config
+*/
+private static final String PAR_NC = "nc";
+
+private final int nc;
+
+private final int nl;
+
+
+// ===================== initialization ================================
+// =====================================================================
+
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+public GraphStats(String name) {
+
+ super(name);
+ nl = Configuration.getInt(name+"."+PAR_NL,0);
+ nc = Configuration.getInt(name+"."+PAR_NC,0);
+}
+
+
+// ====================== methods ======================================
+// =====================================================================
+
+/**
+* Returns statistics over minimal path length and clustering.
+* The output is the average over the set of
+* clustering coefficients of randomly selected nodes, and the
+* set of distances from randomly selected nodes to all the other nodes.
+* The output is always concatenated in one line, containing zero, one or two
+* numbers (averages) as defined by the config parameters.
+* Note that the path length between a pair of nodes can be infinite, in which
+* case the statistics will reflect this (the average will be infinite, etc).
+* See also the configuration parameters.
+* @return always false
+* @see BallExpansion
+* @see Clustering
+*/
+public boolean execute() {
+
+ System.out.print(name+": ");
+
+ IncrementalStats stats = new IncrementalStats();
+ updateGraph();
+
+ if( nc != 0 )
+ {
+ stats.reset();
+ final int n = ( nc<0 ? g.size() : nc );
+ for(int i=0; i<n && i<g.size(); ++i)
+ {
+ stats.add(GraphAlgorithms.clustering(g,i));
+ }
+ System.out.print(stats.getAverage()+" ");
+ }
+
+ if( nl != 0 )
+ {
+ stats.reset();
+ final int n = ( nl<0 ? g.size() : nl );
+ outerloop:
+ for(int i=0; i<n && i<g.size(); ++i)
+ {
+ ga.dist(g,i);
+ for(int j=0; j<g.size(); ++j)
+ {
+ if( j==i ) continue;
+ if (ga.d[j] == -1)
+ {
+ stats.add(Double.POSITIVE_INFINITY);
+ break outerloop;
+ }
+ else
+ stats.add(ga.d[j]);
+ }
+ }
+ System.out.print(stats.getAverage());
+ }
+
+ System.out.println();
+ return false;
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import peersim.core.*;
+
+/**
+ * This observer reports memory utilization (max, total and
+ * free, as defined by <code>java.lang.Runtime</code>).
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.1 $
+ */
+public class MemoryObserver implements Control
+{
+
+/** The runtime object to obtain memory info */
+private final static Runtime r = Runtime.getRuntime();
+
+/** The prefix to be printed */
+private final String prefix;
+
+/**
+ * Constructor to be instantiated in PeerSim.
+ * @param prefix
+ */
+public MemoryObserver(String prefix)
+{
+ this.prefix = prefix;
+}
+
+public boolean execute()
+{
+ System.out.println(prefix + ": max=" + r.maxMemory() + ", total=" +
+ r.totalMemory() + ", free=" + r.freeMemory());
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.reports;
+
+import peersim.core.*;
+import peersim.config.Configuration;
+import peersim.graph.*;
+import peersim.util.IncrementalStats;
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * It tests the network for robustness to random node removal.
+ * It does not actually remove
+ * nodes, it is only an observer, so can be applied several times during the
+ * simulation. A warning though: as a side effect it <em>may
+ * shuffle the network</em> (see {@value #PAR_N}) so if this is an issue,
+ * it should not be used,
+ * or only after the simulation has finished.
+ */
+public class RandRemoval extends GraphObserver
+{
+
+// ===================== fields =======================================
+// ====================================================================
+
+// XXX remove side effect
+/**
+ * This parameter defines the number of runs of the iterative removal procedure
+ * to get statistics. Look out: if set to a value larger than 1 then as a side
+ * effect <em>the overlay will be shuffled</em>. Defaults to 1.
+ * @config
+ */
+private static final String PAR_N = "n";
+
+private final int n;
+
+// ===================== initialization ================================
+// =====================================================================
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+public RandRemoval(String name)
+{
+ super(name);
+ n = Configuration.getInt(name + "." + PAR_N, 1);
+}
+
+// ====================== methods ======================================
+// =====================================================================
+
+/**
+* Prints results of node removal tests. The following experiment is
+* repeated {@value #PAR_N} times. From the graph 50%, 51%, ..., 99% of the nodes
+* are removed at random. For all percentages it is calculated what is
+* the maximal
+* clustersize (weakly connected clusters) and the number of
+* clusters in the remaining graph.
+* These values are averaged over the experiments, and for all 50 different
+* percentage values a line is printed that contains the respective averages,
+* first the average maximal cluster size, followed by the average number
+* of clusters.
+* @return always false
+*/
+public boolean execute()
+{
+ if( n < 1 ) return false;
+ updateGraph();
+
+ System.out.println(name + ":");
+
+ final int size = Network.size();
+ final int steps = 50;
+ IncrementalStats[] maxClust = new IncrementalStats[steps];
+ IncrementalStats[] clustNum = new IncrementalStats[steps];
+ for (int i = 0; i < steps; ++i) {
+ maxClust[i] = new IncrementalStats();
+ clustNum[i] = new IncrementalStats();
+ }
+ for (int j = 0; j < n; ++j) {
+ PrefixSubGraph sg = new PrefixSubGraph(g);
+ IncrementalStats stats = new IncrementalStats();
+ for (int i = 0; i < steps; i++) {
+ sg.setSize(size / 2 - i * (size / 100));
+ Map clst = ga.weaklyConnectedClusters(sg);
+ stats.reset();
+ Iterator it = clst.values().iterator();
+ while (it.hasNext()) {
+ stats.add(((Integer) it.next()).intValue());
+ }
+ maxClust[i].add(stats.getMax());
+ clustNum[i].add(clst.size());
+ }
+ if( j+1 < n ) Network.shuffle();
+ }
+ for (int i = 0; i < steps; ++i) {
+ System.out.println(maxClust[i].getAverage() + " "
+ + clustNum[i].getAverage());
+ }
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+
+/**
+ * This static singleton emulates an underlying router network
+ * of fixed size, and stores the latency measurements for all pairs
+ * of routers.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.6 $
+ */
+public class E2ENetwork
+{
+
+//---------------------------------------------------------------------
+//Fields
+//---------------------------------------------------------------------
+
+/**
+ * True if latency between nodes is considered symmetric. False otherwise.
+ */
+private static boolean symm;
+
+/**
+ * Size of the router network.
+ */
+private static int size;
+
+/**
+ * Latency distances between nodes.
+ */
+private static int[][] array;
+
+//---------------------------------------------------------------------
+//Initialization
+//---------------------------------------------------------------------
+
+/** Disable instance construction */
+private E2ENetwork() {}
+
+//---------------------------------------------------------------------
+//Methods
+//---------------------------------------------------------------------
+
+/**
+ * Resets the network, by creating a triangular (if symm is true) or
+ * a rectangular (if symm is false) array of integers. Initially all
+ * latencies between any pairs are set to be 0.
+ * @param size the number or routers
+ * @param symm if latency is symmetric between all pairs of routers
+ */
+public static void reset(int size, boolean symm)
+{
+ E2ENetwork.symm = symm;
+ E2ENetwork.size = size;
+ array = new int[size][];
+ for (int i=0; i < size; i++) {
+ if (symm)
+ array[i] = new int[i];
+ else
+ array[i] = new int[size];
+ }
+}
+
+//---------------------------------------------------------------------
+
+/**
+ * Returns the latency associated to the specified (sender, receiver)
+ * pair. Routers are indexed from 0.
+ *
+ * @param sender the index of the sender
+ * @param receiver the index of the receiver
+ * @return the latency associated to the specified (sender, receiver)
+ * pair.
+ */
+public static int getLatency(int sender, int receiver)
+{
+ if (sender == receiver)
+ return 0;
+ // XXX There should be the possibility to fix the delay.
+ if (symm) {
+ // Symmetric network
+ if (sender < receiver) {
+ int tmp = sender;
+ sender = receiver;
+ receiver = tmp;
+ }
+ }
+ return array[sender][receiver];
+}
+
+//---------------------------------------------------------------------
+
+/**
+ * Sets the latency associated to the specified (sender, receiver)
+ * pair. Routers are indexed from 0.
+ *
+ * @param sender the index of the sender
+ * @param receiver the index of the receiver
+ * @param latency the latency to be set
+ */
+public static void setLatency(int sender, int receiver, int latency)
+{
+ if (symm) {
+ // Symmetric network
+ if (sender < receiver) {
+ int tmp = sender;
+ sender = receiver;
+ receiver = tmp;
+ }
+ }
+ array[sender][receiver] = latency;
+}
+
+//---------------------------------------------------------------------
+
+/**
+ * Returns the current size of the underlying network (i.e., the number of
+ * routers).
+ */
+public static int getSize()
+{
+ return size;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.edsim.*;
+
+
+/**
+ * This transport protocol is based on the {@link E2ENetwork} class.
+ * Each instance
+ * of this transport class is assigned to one of the routers contained in
+ * the (fully static singleton) {@link E2ENetwork},
+ * and subsequently the {@link E2ENetwork} class is used to obtain the
+ * latency for messages sending based on the router assignment.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.11 $
+ */
+public class E2ETransport implements Transport, RouterInfo
+{
+
+//---------------------------------------------------------------------
+//Parameters
+//---------------------------------------------------------------------
+
+/**
+ * The delay that corresponds to the time spent on the source (and destination)
+ * nodes. In other words, full latency is calculated by fetching the latency
+ * that belongs to communicating between two routers, incremented by
+ * twice this delay. Defaults to 0.
+ * @config
+ */
+private static final String PAR_LOCAL = "local";
+
+//---------------------------------------------------------------------
+//Static fields
+//---------------------------------------------------------------------
+
+/** Identifier of this transport protocol */
+private static int tid;
+
+/** Local component of latency */
+private static long local;
+
+//---------------------------------------------------------------------
+//Fields
+//---------------------------------------------------------------------
+
+/** Identifier of the internal node */
+private int router = -1;
+
+//---------------------------------------------------------------------
+//Initialization
+//---------------------------------------------------------------------
+
+/**
+ * Reads configuration parameters.
+ */
+public E2ETransport(String prefix)
+{
+ tid = CommonState.getPid();
+ local = Configuration.getLong(prefix + "." + PAR_LOCAL, 0);
+}
+
+//---------------------------------------------------------------------
+
+/**
+ * Clones the object.
+ */
+public Object clone()
+{
+ E2ETransport e2e=null;
+ try { e2e=(E2ETransport)super.clone(); }
+ catch( CloneNotSupportedException e ) {} // never happens
+ return e2e;
+}
+
+//---------------------------------------------------------------------
+//Methods inherited by Transport
+//---------------------------------------------------------------------
+
+/**
+* Delivers the message reliably, with the latency calculated by
+* {@link #getLatency}.
+*/
+public void send(Node src, Node dest, Object msg, int pid)
+{
+ /* Assuming that the sender corresponds to the source node */
+ E2ETransport sender = (E2ETransport) src.getProtocol(tid);
+ E2ETransport receiver = (E2ETransport) dest.getProtocol(tid);
+ long latency =
+ E2ENetwork.getLatency(sender.router, receiver.router) + local*2;
+ EDSimulator.add(latency, msg, dest, pid);
+}
+
+//---------------------------------------------------------------------
+
+/**
+* Calculates latency using the static singleton {@link E2ENetwork}.
+* It looks up which routers the given nodes are assigned to, then
+* looks up the corresponding latency. Finally it increments this value
+* by adding twice the local delay configured by {@value #PAR_LOCAL}.
+*/
+public long getLatency(Node src, Node dest)
+{
+ /* Assuming that the sender corresponds to the source node */
+ E2ETransport sender = (E2ETransport) src.getProtocol(tid);
+ E2ETransport receiver = (E2ETransport) dest.getProtocol(tid);
+ return E2ENetwork.getLatency(sender.router, receiver.router) + local*2;
+}
+
+
+//---------------------------------------------------------------------
+//Methods inherited by RouterInfo
+//---------------------------------------------------------------------
+
+/**
+ * Associates the node hosting this transport protocol instance with
+ * a router in the router network.
+ *
+ * @param router the numeric index of the router
+ */
+public void setRouter(int router)
+{
+ this.router = router;
+}
+
+//---------------------------------------------------------------------
+
+/**
+ * @return the router associated to this transport protocol.
+ */
+public int getRouter()
+{
+ return router;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+import java.io.*;
+import java.util.*;
+import peersim.config.*;
+import peersim.core.Control;
+
+/**
+ * Initializes static singleton {@link E2ENetwork} by reading a king data set.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.9 $
+ */
+public class KingParser implements Control
+{
+
+// ---------------------------------------------------------------------
+// Parameters
+// ---------------------------------------------------------------------
+
+/**
+ * The file containing the King measurements.
+ * @config
+ */
+private static final String PAR_FILE = "file";
+
+/**
+ * The ratio between the time units used in the configuration file and the
+ * time units used in the Peersim simulator.
+ * @config
+ */
+private static final String PAR_RATIO = "ratio";
+
+// ---------------------------------------------------------------------
+// Fields
+// ---------------------------------------------------------------------
+
+/** Name of the file containing the King measurements. */
+private String filename;
+
+/**
+ * Ratio between the time units used in the configuration file and the time
+ * units used in the Peersim simulator.
+ */
+private double ratio;
+
+/** Prefix for reading parameters */
+private String prefix;
+
+// ---------------------------------------------------------------------
+// Initialization
+// ---------------------------------------------------------------------
+
+/**
+ * Read the configuration parameters.
+ */
+public KingParser(String prefix)
+{
+ this.prefix = prefix;
+ ratio = Configuration.getDouble(prefix + "." + PAR_RATIO, 1);
+ filename = Configuration.getString(prefix + "." + PAR_FILE, null);
+}
+
+// ---------------------------------------------------------------------
+// Methods
+// ---------------------------------------------------------------------
+
+/**
+ * Initializes static singleton {@link E2ENetwork} by reading a king data set.
+* @return always false
+*/
+public boolean execute()
+{
+ BufferedReader in = null;
+ if (filename != null) {
+ try {
+ in = new BufferedReader(new FileReader(filename));
+ } catch (FileNotFoundException e) {
+ throw new IllegalParameterException(prefix + "." + PAR_FILE, filename
+ + " does not exist");
+ }
+ } else {
+ in = new BufferedReader( new InputStreamReader(
+ ClassLoader.getSystemResourceAsStream("t-king.map")
+ ) );
+ }
+
+ // XXX If the file format is not correct, we will get quite obscure
+ // exceptions. To be improved.
+
+ String line = null;
+ // Skip initial lines
+ int size = 0;
+ int lc = 1;
+ try {
+ while ((line = in.readLine()) != null && !line.startsWith("node")) lc++;
+ while (line != null && line.startsWith("node")) {
+ size++;
+ lc++;
+ line = in.readLine();
+ }
+ } catch (IOException e) {
+ System.err.println("KingParser: " + filename + ", line " + lc + ":");
+ e.printStackTrace();
+ try { in.close(); } catch (IOException e1) { };
+ System.exit(1);
+ }
+ E2ENetwork.reset(size, true);
+ if (line == null) {
+ System.err.println("KingParser: " + filename + ", line " + lc + ":");
+ System.err.println("No latency matrix contained in the specified file");
+ try { in.close(); } catch (IOException e1) { };
+ System.exit(1);
+ }
+
+ System.err.println("KingParser: read " + size + " entries");
+
+ try {
+ do {
+ StringTokenizer tok = new StringTokenizer(line, ", ");
+ if (tok.countTokens() != 3) {
+ System.err.println("KingParser: " + filename + ", line " + lc + ":");
+ System.err.println("Specified line does not contain a <node1, node2, latency> triple");
+ try { in.close(); } catch (IOException e1) { };
+ System.exit(1);
+ }
+ int n1 = Integer.parseInt(tok.nextToken()) - 1;
+ int n2 = Integer.parseInt(tok.nextToken()) - 1;
+ int latency = (int) (Double.parseDouble(tok.nextToken()) * ratio);
+ E2ENetwork.setLatency(n1, n2, latency);
+ lc++;
+ line = in.readLine();
+ } while (line != null);
+
+ in.close();
+
+ } catch (IOException e) {
+ System.err.println("KingParser: " + filename + ", line " + lc + ":");
+ e.printStackTrace();
+ try { in.close(); } catch (IOException e1) { };
+ System.exit(1);
+ }
+
+
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+/**
+ * Generic interface to be implemented by protocols that need to be assigned to
+ * routers. The idea is that each node is assigned to a router, by
+ * invoking {@link #setRouter(int)} method. Routers are identified by
+ * integer indexes (starting from 0), based on the assumption that the
+ * router network
+ * is static. The router information is then used by different
+ * implementations to compute latency, congestion, etc.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.4 $
+ */
+public interface RouterInfo
+{
+
+/**
+ * Associates the node hosting this transport protocol instance with
+ * a router in the router network.
+ *
+ * @param router the numeric index of the router
+ */
+public void setRouter(int router);
+
+/**
+ * @return the router associated to this transport protocol.
+ */
+public int getRouter();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+import peersim.core.*;
+
+
+/**
+ * This interface represents a generic transport protocol, used to
+ * send messages through the underlying network. Generally, transport
+ * protocols use {@link peersim.edsim.EDSimulator} to schedule the delivery of
+ * messages with some appropriate delay. They can also model message omission
+ * failure as well.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.7 $
+ */
+public interface Transport extends Protocol
+{
+
+/**
+ * Sends message <code>msg</code> from node <code>src</code> to protocol
+ * <code>pid</code> of node <code>dst</code>.
+ *
+ * @param src sender node
+ * @param dest destination node
+ * @param msg message to be sent
+ * @param pid protocol identifier
+ */
+public void send(Node src, Node dest, Object msg, int pid);
+
+
+/**
+ * Return a latency estimate from node <code>src</code> to protocol
+ * <code>pid</code> of node <code>dst</code>.
+ *
+ * @param src sender node
+ * @param dest destination node
+ */
+public long getLatency(Node src, Node dest);
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+import java.io.*;
+
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * Initializes static singleton {@link E2ENetwork} by reading a trace
+ * file containing the latency distance measured between a set of
+ * "virtual" routers. Latency is assumed to be symmetric, so the
+ * latency between x and y is equal to the latency to y and x.
+ *
+ * The format of the file is as follows: all values are stored as
+ * integers. The first value is the number of nodes considered.
+ * The rest of the values correspond to a "strictly upper triangular
+ * matrix" (see this
+ * <a href="http://en.wikipedia.org/w/index.php?title=Triangular_matrix&oldid=82411128">
+ * link</a>), ordered first by row than by column.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.4 $
+ */
+public class TriangularMatrixParser implements Control
+{
+
+// ---------------------------------------------------------------------
+// Parameters
+// ---------------------------------------------------------------------
+
+/**
+ * This configuration parameter identifies the filename of the file
+ * containing the measurements. First, the file is used as a pathname
+ * in the local file system. If no file can be identified in this way,
+ * the file is searched in the local classpath. If the file cannot be
+ * identified again, an error message is reported.
+ * @config
+ */
+private static final String PAR_FILE = "file";
+
+/**
+ * The ratio between the time units used in the configuration file and the
+ * time units used in the Peersim simulator.
+ * @config
+ */
+private static final String PAR_RATIO = "ratio";
+
+// ---------------------------------------------------------------------
+// Fields
+// ---------------------------------------------------------------------
+
+/** Name of the file containing the measurements. */
+private String filename;
+
+/** Ratio read from PAR_RATIO */
+private double ratio;
+
+// ---------------------------------------------------------------------
+// Initialization
+// ---------------------------------------------------------------------
+
+/**
+ * Read the configuration parameters.
+ */
+public TriangularMatrixParser(String prefix)
+{
+ filename = Configuration.getString(prefix + "." + PAR_FILE);
+ ratio = Configuration.getDouble(prefix + "." + PAR_RATIO);
+}
+
+// ---------------------------------------------------------------------
+// Methods
+// ---------------------------------------------------------------------
+
+/**
+ * Initializes static singleton {@link E2ENetwork} by reading a king data set.
+* @return always false
+*/
+public boolean execute()
+{
+ try {
+ ObjectInputStream in = null;
+ try {
+ in = new ObjectInputStream(
+ new BufferedInputStream(
+ new FileInputStream(filename)));
+ System.err.println("TriangularMatrixParser: Reading " + filename + " from local file system");
+ } catch (FileNotFoundException e) {
+ in = new ObjectInputStream(
+ new BufferedInputStream(
+ ClassLoader.getSystemResourceAsStream(filename)));
+ System.err.println("TriangularMatrixParser: Reading " + filename + " through the class loader");
+ }
+
+ // Read the number of nodes in the file (first four bytes).
+ int size = in.readInt();
+
+ // Reset the E2E network
+ E2ENetwork.reset(size, true);
+ System.err.println("TriangularMatrixParser: reading " + size + " rows");
+
+ // If the file format is not correct, data will be read
+ // incorrectly. Probably a good way to spot this is the
+ // presence of negative delays, or an end of file.
+
+ // Reading data
+ int count = 0;
+ for (int r=0; r < size; r++) {
+ for (int c = r+1; c < size; c++) {
+ int x = (int) (ratio*in.readInt());
+ count++;
+ E2ENetwork.setLatency(r,c,x);
+ }
+ }
+ System.err.println("TriangularMatrixParser: Read " + count + " entries");
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ return false;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.edsim.*;
+
+
+/**
+ * Implement a transport layer that reliably delivers messages with a random
+ * delay, that is drawn from the configured interval according to the uniform
+ * distribution.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.14 $
+ */
+public final class UniformRandomTransport implements Transport
+{
+
+//---------------------------------------------------------------------
+//Parameters
+//---------------------------------------------------------------------
+
+/**
+ * String name of the parameter used to configure the minimum latency.
+ * @config
+ */
+private static final String PAR_MINDELAY = "mindelay";
+
+/**
+ * String name of the parameter used to configure the maximum latency.
+ * Defaults to {@value #PAR_MINDELAY}, which results in a constant delay.
+ * @config
+ */
+private static final String PAR_MAXDELAY = "maxdelay";
+
+//---------------------------------------------------------------------
+//Fields
+//---------------------------------------------------------------------
+
+/** Minimum delay for message sending */
+private final long min;
+
+/** Difference between the max and min delay plus one. That is, max delay is
+* min+range-1.
+*/
+private final long range;
+
+
+//---------------------------------------------------------------------
+//Initialization
+//---------------------------------------------------------------------
+
+/**
+ * Reads configuration parameter.
+ */
+public UniformRandomTransport(String prefix)
+{
+ min = Configuration.getLong(prefix + "." + PAR_MINDELAY);
+ long max = Configuration.getLong(prefix + "." + PAR_MAXDELAY,min);
+ if (max < min)
+ throw new IllegalParameterException(prefix+"."+PAR_MAXDELAY,
+ "The maximum latency cannot be smaller than the minimum latency");
+ range = max-min+1;
+}
+
+//---------------------------------------------------------------------
+
+/**
+* Returns <code>this</code>. This way only one instance exists in the system
+* that is linked from all the nodes. This is because this protocol has no
+* node specific state.
+*/
+public Object clone()
+{
+ return this;
+}
+
+//---------------------------------------------------------------------
+//Methods
+//---------------------------------------------------------------------
+
+/**
+ * Delivers the message with a random
+ * delay, that is drawn from the configured interval according to the uniform
+ * distribution.
+*/
+public void send(Node src, Node dest, Object msg, int pid)
+{
+ // avoid calling nextLong if possible
+ long delay = (range==1?min:min + CommonState.r.nextLong(range));
+ EDSimulator.add(delay, msg, dest, pid);
+}
+
+/**
+ * Returns a random
+ * delay, that is drawn from the configured interval according to the uniform
+ * distribution.
+*/
+public long getLatency(Node src, Node dest)
+{
+ return (range==1?min:min + CommonState.r.nextLong(range));
+}
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+import peersim.config.*;
+import peersim.core.*;
+
+
+/**
+ * Initializes {@link RouterInfo} protocols by assigning routers to them.
+ * The number of routers is defined by static singleton {@link E2ENetwork}.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.6 $
+ */
+public class UniformRouterAssignment implements Control
+{
+
+//---------------------------------------------------------------------
+//Parameters
+//---------------------------------------------------------------------
+
+/**
+ * Parameter name used to configure the {@link RouterInfo} protocol
+ * that should be initialized.
+ * @config
+ */
+private static final String PAR_PROT = "protocol";
+
+//---------------------------------------------------------------------
+//Methods
+//---------------------------------------------------------------------
+
+/** Protocol identifier */
+private int pid;
+
+
+//---------------------------------------------------------------------
+//Initialization
+//---------------------------------------------------------------------
+
+/**
+ * Reads configuration parameters.
+ */
+public UniformRouterAssignment(String prefix)
+{
+ pid = Configuration.getPid(prefix+"."+PAR_PROT);
+}
+
+//---------------------------------------------------------------------
+//Methods
+//---------------------------------------------------------------------
+
+/**
+ * Initializes given {@link RouterInfo} protocol layer by assigning
+ * routers randomly.
+ * The number of routers is defined by static singleton {@link E2ENetwork}.
+* @return always false
+*/
+public boolean execute()
+{
+ int nsize = Network.size();
+ int nrouters = E2ENetwork.getSize();
+ for (int i=0; i < nsize; i++) {
+ Node node = Network.get(i);
+ RouterInfo t = (RouterInfo) node.getProtocol(pid);
+ int r = CommonState.r.nextInt(nrouters);
+ t.setRouter(r);
+ }
+
+ return false;
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.transport;
+
+import peersim.config.*;
+import peersim.core.*;
+
+
+/**
+ * This transport protocol can be combined with other transports
+ * to simulate message losses. Its behavior is the following: each message
+ * can be dropped based on the configured probability, or it will be sent
+ * using the underlying transport protocol.
+ * <p>
+ * The memory requirements are minimal, as a single instance is created and
+ * inserted in the protocol array of all nodes (because instances have no state
+ * that depends on the hosting node).
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.13 $
+ */
+public final class UnreliableTransport implements Transport
+{
+
+//---------------------------------------------------------------------
+//Parameters
+//---------------------------------------------------------------------
+
+/**
+ * The name of the underlying transport protocol. This transport is
+ * extended with dropping messages.
+ * @config
+ */
+private static final String PAR_TRANSPORT = "transport";
+
+/**
+ * String name of the parameter used to configure the probability that a
+ * message sent through this transport is lost.
+ * @config
+ */
+private static final String PAR_DROP = "drop";
+
+
+//---------------------------------------------------------------------
+//Fields
+//---------------------------------------------------------------------
+
+/** Protocol identifier for the support transport protocol */
+private final int transport;
+
+/** Probability of dropping messages */
+private final float loss;
+
+//---------------------------------------------------------------------
+//Initialization
+//---------------------------------------------------------------------
+
+/**
+ * Reads configuration parameter.
+ */
+public UnreliableTransport(String prefix)
+{
+ transport = Configuration.getPid(prefix+"."+PAR_TRANSPORT);
+ loss = (float) Configuration.getDouble(prefix+"."+PAR_DROP);
+}
+
+//---------------------------------------------------------------------
+
+/**
+* Returns <code>this</code>. This way only one instance exists in the system
+* that is linked from all the nodes. This is because this protocol has no
+* state that depends on the hosting node.
+ */
+public Object clone()
+{
+ return this;
+}
+
+//---------------------------------------------------------------------
+//Methods
+//---------------------------------------------------------------------
+
+/** Sends the message according to the underlying transport protocol.
+* With the configured probability, the message is not sent (i.e. the method does
+* nothing).
+*/
+public void send(Node src, Node dest, Object msg, int pid)
+{
+ try
+ {
+ if (CommonState.r.nextFloat() >= loss)
+ {
+ // Message is not lost
+ Transport t = (Transport) src.getProtocol(transport);
+ t.send(src, dest, msg, pid);
+ }
+ }
+ catch(ClassCastException e)
+ {
+ throw new IllegalArgumentException("Protocol " +
+ Configuration.lookupPid(transport) +
+ " does not implement Transport");
+ }
+}
+
+/** Returns the latency of the underlying protocol.*/
+public long getLatency(Node src, Node dest)
+{
+ Transport t = (Transport) src.getProtocol(transport);
+ return t.getLatency(src, dest);
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+import java.util.Random;
+import java.lang.Math;
+
+/**
+ * Extends the functionality of <code>java.util.Random</code>.
+ */
+public class ExtendedRandom extends Random {
+
+private long lastSeed;
+
+// -------------------------------------------------------------------------
+
+/** Calls super constructor. Also stores the seed to be returned by
+{@link #getLastSeed}. */
+public ExtendedRandom(long seed) {
+
+ super(seed);
+ lastSeed = seed;
+}
+
+// -------------------------------------------------------------------------
+
+/**
+ * Extracts the next integer, according to a Poisson distribution.
+ *
+ * @param mean The mean of the Poisson distribution.
+ * @return An integer Poisson extraction.
+ */
+public int nextPoisson(double mean) {
+
+ double emean = Math.exp(-1 * mean);
+ double product = 1;
+ int count = 0;
+ int result = 0;
+ while (product >= emean) {
+ product *= nextDouble();
+ result = count;
+ count++; // keep result one behind
+ }
+ return result;
+}
+
+// -------------------------------------------------------------------------
+
+/**
+* Implements nextLong(long) the same way nexInt(int) is implemented in
+* java.util.Random.
+* @param n the bound on the random number to be returned. Must be positive.
+* @return a pseudorandom, uniformly distributed long value between 0
+* (inclusive) and n (exclusive).
+*/
+public long nextLong(long n) {
+
+ if (n<=0)
+ throw new IllegalArgumentException("n must be positive");
+
+ if ((n & -n) == n) // i.e., n is a power of 2
+ {
+ return nextLong()&(n-1);
+ }
+
+ long bits, val;
+ do
+ {
+ bits = (nextLong()>>>1);
+ val = bits % n;
+ }
+ while(bits - val + (n-1) < 0);
+
+ return val;
+}
+
+// -------------------------------------------------------------------------
+
+/** Sets random seed. Calls super method but also stores the seed to be
+returned by {@link #getLastSeed}. */
+public void setSeed( long seed ) {
+
+ super.setSeed(seed);
+ lastSeed = seed;
+}
+
+// -------------------------------------------------------------------------
+
+/**
+* Returns the last random seed that was set explicitly. Either at
+* construction time or through {@link #setSeed}.
+*/
+public long getLastSeed() { return lastSeed; }
+
+// -------------------------------------------------------------------------
+
+/*
+public static void main(String[] args) {
+
+ ExtendedRandom er = new ExtendedRandom(12345678);
+ for(int i=0; i<100; ++i)
+ System.out.println(er.nextLong(Long.parseLong(args[0])));
+
+}
+*/
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+import java.io.*;
+
+
+/**
+* Generates a series of filenames for classes that have to save e.g.
+* snapshots regularly.
+*/
+public class FileNameGenerator {
+
+
+/**
+* The number of filenames already returned.
+*/
+private long counter = 0;
+
+/** The prefix of the filename */
+public final String prefix;
+
+/** The extension of the filename */
+public final String ext;
+
+
+// ==================== initialization ==============================
+// ==================================================================
+
+
+/**
+* @param prefix all returned names will be prefixed by this
+* @param ext will be appended to all returned names
+*/
+public FileNameGenerator(String prefix, String ext) {
+
+ this.prefix=prefix;
+ this.ext=ext;
+}
+
+
+// ===================== methods ====================================
+// ==================================================================
+
+
+/**
+* Generates a name based on a counter.
+* The format of the name returned is {@link #prefix} followed by
+* an 8 digit zero padded number, followed by {@link #ext}.
+* The first number used is zero.
+* @return the next filename after increasing the counter
+*/
+public String nextCounterName() {
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ (new PrintStream(baos)).printf("%08d",counter);
+ counter++;
+ return prefix+baos.toString()+ext;
+}
+
+// ------------------------------------------------------------------
+
+/*
+public static void main(String args[]) {
+
+ FileNameGenerator fng = new FileNameGenerator(args[0],args[1]);
+ for(int i=0; i<100; ++i) System.err.println(fng.nextCounterName());
+}
+*/
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+import java.io.PrintStream;
+
+//XXX This implementation is very restricted, to be made more flexible
+// using hashtables.
+/**
+* A class that can collect frequency information on integer input.
+* right now it can handle only unsigned input. It simply ignores negative
+* numbers.
+*/
+public class IncrementalFreq implements Cloneable {
+
+
+// ===================== fields ========================================
+// =====================================================================
+
+/** The number of items inserted. */
+private int n;
+
+/** freq[i] holds the frequency of i. primitive implementation, to be changed */
+private int[] freq = null;
+
+/**
+* The capacity, if larger than 0. Added values larger than or equal to
+* this one will be ignored.
+*/
+private final int N;
+
+
+// ====================== initialization ==============================
+// ====================================================================
+
+
+/**
+* @param maxvalue Values in the input set larger than this one will be ignored.
+* However, if it is negative, no values are ignored.
+*/
+public IncrementalFreq(int maxvalue) {
+
+ N = maxvalue+1;
+ reset();
+}
+
+// --------------------------------------------------------------------
+
+/** Calls <code>this(-1)</code>, that is, no values will be ignored.
+* @see #IncrementalFreq(int) */
+public IncrementalFreq() {
+
+ this(-1);
+}
+
+// --------------------------------------------------------------------
+
+/** Reset the state of the object. After calling this, all public methods
+* behave the same as they did after constructing the object.
+*/
+public void reset() {
+
+ if( freq==null || N==0 ) freq = new int[0];
+ else for(int i=0; i<freq.length; ++i) freq[i]=0;
+ n = 0;
+}
+
+
+// ======================== methods ===================================
+// ====================================================================
+
+/**
+ * Adds item <code>i</code> to the input set.
+ * It calls <code>add(i,1)</code>.
+ * @see #add(int,int)
+ */
+public final void add( int i ) { add(i,1); }
+
+
+// --------------------------------------------------------------------
+
+/**
+ * Adds item <code>i</code> to the input set <code>k</code> times.
+ * That is, it increments counter <code>i</code> by <code>k</code>.
+ * If, however, <code>i</code> is negative, or larger than the maximum defined
+ * at construction time (if a maximum was set at all) the operation is ignored.
+ */
+public void add( int i, int k ) {
+
+ if( N>0 && i>=N ) return;
+ if( i<0 || k<=0 ) return;
+
+ // Increase number of items by k.
+ n+=k;
+
+ // If index i is out of bounds for the current array of counters,
+ // increase the size of the array to i+1.
+ if( i>=freq.length )
+ {
+ int tmp[] = new int[i+1];
+ System.arraycopy(freq, 0, tmp, 0, freq.length);
+ freq=tmp;
+ }
+
+ // Finally, increase counter i by k.
+ freq[i]+=k;
+}
+
+// --------------------------------------------------------------------
+
+/** Returns number of processed data items.
+* This is the number of items over which the class holds statistics.
+*/
+public int getN() { return n; }
+
+// --------------------------------------------------------------------
+
+/** Returns the number of occurrences of the given integer. */
+public int getFreq(int i) {
+
+ if( i>=0 && i<freq.length ) return freq[i];
+ else return 0;
+}
+
+// --------------------------------------------------------------------
+
+
+/**
+ * Performs an element-by-element vector subtraction of the
+ * frequency vectors. If <code>strict</code> is true, it
+ * throws an IllegalArgumentException if <code>this</code> is
+ * not strictly larger than <code>other</code> (element by element)
+ * (Note that both frequency vectors are positive.)
+ * Otherwise just sets those elements in <code>this</code> to zero
+ * that are smaller than those of <code>other</code>.
+ * @param other The instance of IncrementalFreq to subtract
+ * @param strict See above explanation
+ */
+public void remove(IncrementalFreq other, boolean strict) {
+
+ // check if other has non-zero elements in non-overlapping part
+ if(strict && other.freq.length>freq.length)
+ {
+ for(int i=other.freq.length-1; i>=freq.length; --i)
+ {
+ if (other.freq[i]!=0)
+ throw new IllegalArgumentException();
+ }
+ }
+
+ final int minLength = Math.min(other.freq.length, freq.length);
+ for (int i=minLength-1; i>=0; i--)
+ {
+ if (strict && freq[i] < other.freq[i])
+ throw new IllegalArgumentException();
+ final int remove = Math.min(other.freq[i], freq[i]);
+ n -= remove;
+ freq[i] -= remove;
+ }
+}
+
+// ---------------------------------------------------------------------
+
+/**
+* Prints current frequency information. Prints a separate line for
+* all values from 0 to the capacity of the internal representation using the
+* format
+* <pre>
+* value occurrences
+* </pre>
+* That is, numbers with zero occurrences will also be printed.
+*/
+public void printAll( PrintStream out ) {
+
+ for(int i=0; i<freq.length; ++i)
+ {
+ out.println(i+" "+freq[i]);
+ }
+}
+
+// ---------------------------------------------------------------------
+
+/**
+* Prints current frequency information. Prints a separate line for
+* all values that have a number of occurrences different from zero using the
+* format
+* <pre>
+* value occurrences
+* </pre>
+*/
+public void print( PrintStream out ) {
+
+ for(int i=0; i<freq.length; ++i)
+ {
+ if(freq[i]!=0) out.println(i+" "+freq[i]);
+ }
+}
+
+// ---------------------------------------------------------------------
+
+public String toString() {
+
+ StringBuilder result=new StringBuilder("");
+ for(int i=0; i<freq.length; ++i)
+ {
+ if (freq[i] != 0)
+ result.append(" (").append(i).append(","
+ ).append(freq[i]).append(")");
+ }
+ return result.toString();
+}
+
+// ---------------------------------------------------------------------
+
+/** An alternative method to convert the object to String */
+public String toArithmeticExpression() {
+
+ StringBuilder result=new StringBuilder("");
+ for (int i=freq.length-1; i>=0; i--)
+ {
+ if (freq[i] != 0)
+ result.append(freq[i]).append(
+ "*").append(i).append("+");
+ }
+
+ if (result.length()==0)
+ return "(empty)";
+ else
+ return result.substring(0, result.length()-1);
+}
+
+// ---------------------------------------------------------------------
+
+public Object clone() throws CloneNotSupportedException {
+
+ IncrementalFreq result = (IncrementalFreq)super.clone();
+ if( freq != null ) result.freq = freq.clone();
+ return result;
+}
+
+// ---------------------------------------------------------------------
+
+/**
+* Tests equality between two IncrementalFreq instances.
+* Two objects are equal if both hold the same set of numbers that have
+* occurred non-zero times and the number of occurrences is also equal for
+* these numbers.
+*/
+public boolean equals(Object obj) {
+
+ if( !( obj instanceof IncrementalFreq) ) return false;
+ IncrementalFreq other = (IncrementalFreq)obj;
+ final int minlength = Math.min(other.freq.length, freq.length);
+
+ for (int i=minlength-1; i>=0; i--)
+ if (freq[i] != other.freq[i])
+ return false;
+
+ if( freq.length > minlength ) other=this;
+ for (int i=minlength; i<other.freq.length; i++)
+ if( other.freq[i] != 0 )
+ return false;
+
+ return true;
+}
+
+// ---------------------------------------------------------------------
+
+/**
+* Hashcode (consistent with {@link #equals}). Probably you will never want to
+* use this, but since we have {@link #equals}, we must implement it.
+*/
+public int hashCode() {
+
+ int sum = 0;
+ for(int i=0; i<freq.length; ++i) sum += freq[i]*i;
+ return sum;
+}
+
+// ---------------------------------------------------------------------
+
+/*
+public static void main(String[] pars) {
+
+ IncrementalFreq ifq = new IncrementalFreq(Integer.parseInt(pars[0]));
+ for(int i=1; i<pars.length; ++i)
+ {
+ ifq.add(Integer.parseInt(pars[i]));
+ }
+ ifq.print(System.out);
+ System.out.println(ifq);
+}
+*/
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+/**
+* A class that can keep track of some statistics like variance, average, min,
+* max incrementally. That is, when adding a new data item, it updates the
+* statistics.
+*/
+public class IncrementalStats {
+
+
+// ===================== fields ========================================
+// =====================================================================
+
+
+private double min;
+
+private double max;
+
+private double sum;
+
+private double sqrsum;
+
+private int n;
+
+private int countmin;
+
+private int countmax;
+
+// ====================== initialization ==============================
+// ====================================================================
+
+
+/** Calls {@link #reset}. */
+public IncrementalStats() { reset(); }
+
+// --------------------------------------------------------------------
+
+/** Resets the statistics to reflect the zero elements set.
+* Min and max are set to positive and negative infinity, respectively.
+*/
+public void reset() {
+
+ countmin=0;
+ countmax=0;
+ min = Double.POSITIVE_INFINITY;
+ max = Double.NEGATIVE_INFINITY;
+ sum = 0.0;
+ sqrsum = 0.0;
+ n = 0;
+}
+
+
+// ======================== methods ===================================
+// ====================================================================
+
+
+/** Updates the statistics according to this element. It calls
+* <code>add(item,1)</code>.
+* @see #add(double,int) */
+public final void add( double item ) { add(item,1); }
+
+// --------------------------------------------------------------------
+
+/** Updates the statistics assuming element <code>item</code> is added
+* <code>k</code> times.*/
+public void add( double item, int k ) {
+
+ if( item < min )
+ {
+ min = item;
+ countmin = 0;
+ }
+ if( item == min ) countmin+=k;
+ if( item > max )
+ {
+ max = item;
+ countmax = 0;
+ }
+ if(item == max) countmax+=k;
+ n+=k;
+ if( k == 1 )
+ {
+ sum += item;
+ sqrsum += item*item;
+ }
+ else
+ {
+ sum += item*k;
+ sqrsum += item*item*k;
+ }
+}
+
+// --------------------------------------------------------------------
+
+/** The number of data items processed so far */
+public int getN() { return n; }
+
+// --------------------------------------------------------------------
+
+/** The maximum of the data items */
+public double getMax() { return max; }
+
+// --------------------------------------------------------------------
+
+/** The minimum of the data items */
+public double getMin() { return min; }
+
+// --------------------------------------------------------------------
+
+/** Returns the number of data items whose value equals the maximum. */
+public int getMaxCount() { return countmax; }
+
+// --------------------------------------------------------------------
+
+/** Returns the number of data items whose value equals the minimum. */
+public int getMinCount() { return countmin; }
+
+// --------------------------------------------------------------------
+
+/** The sum of the data items */
+public double getSum() { return sum; }
+
+// --------------------------------------------------------------------
+
+/** The sum of the squares of the data items */
+public double getSqrSum() { return sqrsum; }
+
+// --------------------------------------------------------------------
+
+/** The average of the data items */
+public double getAverage() { return sum/n; }
+
+// --------------------------------------------------------------------
+
+/** The empirical variance of the data items. Guaranteed to be larger or
+equal to 0.0. If due to rounding errors the value becomes negative,
+it returns 0.0.*/
+public double getVar() {
+
+ double var=
+ (((double)n) / (n-1)) * (sqrsum/n - getAverage()*getAverage());
+ return (var>=0.0?var:0.0);
+ // XXX note that we have very little possibility to increase numeric
+ // stability if this class is "greedy", ie, if it has no memory
+ // In a more precise implementation we could delay the calculation of
+ // statistics and store the data in some intelligent structure
+}
+
+// --------------------------------------------------------------------
+
+/** the empirical standard deviation of the data items */
+public double getStD() { return Math.sqrt(getVar()); }
+
+// --------------------------------------------------------------------
+
+/**
+* Prints the following quantities separated by spaces in a single line
+* in this order.
+* Minimum, maximum, number of items, average, variance, number of minimal
+* items, number of maximal items.
+*/
+public String toString() {
+
+ return min+" "+max+" "+n+" "+sum/n+" "+getVar()+" "+
+ countmin+" "+countmax;
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+/**
+* This class provides iterations over the set of integers [0...k-1].
+*/
+public interface IndexIterator {
+
+ /**
+ * This resets the iteration. The set of integers will be 0,..,k-1.
+ */
+ public void reset(int k);
+
+ /**
+ * Returns next index.
+ */
+ public int next();
+
+ /**
+ * Returns true if {@link #next} can be called at least one more time.
+ * Note that {@link #next} can be called k times after {@link #reset}.
+ */
+ public boolean hasNext();
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+import java.util.NoSuchElementException;
+
+/**
+* This class gives the linear order 0,1,etc or alternatively k-1, k-2, etc.,
+* depending on the constructor.
+*/
+public class LinearIterator implements IndexIterator {
+
+
+// ======================= private fields ============================
+// ===================================================================
+
+private final boolean reverse;
+
+private int len = 0;
+
+private int pointer = 0;
+
+
+// ======================= initialization ============================
+// ===================================================================
+
+
+/**
+* Construct an iterator for an empty set of numbers.
+* You have to call {@link #reset} to actually fully initialize the object.
+* The numbers returned by consecutive calls to {@link #next} are 0,1,...
+*/
+public LinearIterator() { reverse=false; }
+
+// -------------------------------------------------------------------
+
+/**
+* Construct an interator for an empty set of numbers.
+* You have to call {@link #reset} to actually fully initialize the object.
+* If parameter is true then the numbers returned by consecutive calls to
+* {@link #next} are k-1,k-2,..., otherwise 0,1,...
+*/
+public LinearIterator( boolean rev ) { reverse=rev; }
+
+
+// ======================= public methods ============================
+// ===================================================================
+
+
+public void reset(int k) {
+
+ len = k;
+ pointer = (reverse ? len-1 : 0);
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Returns next index. The indices are returned in increasing or decreasing
+* order depending on the parameter given at construction time.
+*/
+public int next() {
+
+ if( !hasNext() ) throw new NoSuchElementException();
+
+ return (reverse ? pointer-- : pointer++);
+}
+
+// -------------------------------------------------------------------
+
+public boolean hasNext() { return (reverse ? pointer >= 0 : pointer < len); }
+
+// -------------------------------------------------------------------
+
+/*
+public static void main( String pars[] ) throws Exception {
+
+ LinearIterator rp = new LinearIterator(pars[0].equals("rev"));
+
+ int k = Integer.parseInt(pars[1]);
+ rp.reset(k);
+ while(rp.hasNext()) System.out.println(rp.next());
+
+ System.out.println();
+
+ k = Integer.parseInt(pars[2]);
+ rp.reset(k);
+ while(rp.hasNext()) System.out.println(rp.next());
+ System.out.println(rp.next());
+}
+*/
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * This class adds the ability to retrieve the median element to the
+ * {@link IncrementalStats} class. Note that this class actually stores all
+ * the elements, so (unlike in its superclass) storage requirements depend
+ * on the number of items processed.
+ *
+ * @author giampa
+ */
+public class MedianStats extends IncrementalStats
+{
+
+/** Structure to store each entry. */
+private final ArrayList<Double> data=new ArrayList<Double>();
+
+/** Calls {@link #reset}. */
+public MedianStats()
+{
+ reset();
+}
+
+/**
+ * Retrieves the median in the current data collection.
+ *
+ * @return The current median value.
+ */
+public double getMedian()
+{
+ double result;
+
+ if (data.isEmpty())
+ throw new IllegalStateException("Data vector is empty!");
+
+ // Sort the arraylist
+ Collections.sort(data);
+ if (data.size() % 2 != 0) { // odd number
+ result = data.get(data.size() / 2);
+ } else { // even number:
+ double a = data.get(data.size() / 2);
+ double b = data.get(data.size() / 2 - 1);
+ result = (a + b) / 2;
+ }
+ return result;
+}
+
+public void add(double item, int k)
+{
+ for (int i = 0; i < k; ++i) {
+ super.add(item, 1);
+ data.add(new Double(item));
+ }
+}
+
+public void reset()
+{
+ super.reset();
+ if (data != null)
+ data.clear();
+}
+
+
+public static void main( String[] args ) {
+ MedianStats s = new MedianStats();
+ for(int i=0; i<args.length; i++) s.add(Double.parseDouble(args[i]));
+ System.out.println("Average: "+s.getAverage());
+ System.out.println("Median: "+s.getMedian());
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+/**
+ * This class provides extended statistical informations about the inspected
+ * distribution. In particular, it provides functions to compute the skewness
+ * (the 3rd degree moment) and the kurtosis (4th degree moment).
+ *
+ * @author Gian Paolo Jesi
+ */
+public class MomentStats extends IncrementalStats {
+
+ private double cubicsum, quadsum; // incremental sums
+
+ /** Calls {@link #reset} */
+ public MomentStats() {
+ reset();
+ }
+
+ public void reset() {
+ super.reset();
+ cubicsum = quadsum = 0.0;
+ }
+
+ public void add(double item, int k) {
+ for(int i=0; i<k; ++i)
+ {
+ super.add(item,1);
+ cubicsum += item * item * item;
+ quadsum += item * cubicsum;
+ }
+ }
+
+ /** Outputs on a single line the superclass statistics postfixed by the
+ * current skewness and kurtosis.
+ */
+ public String toString() {
+ return super.toString()+" "+getSkewness()+" "+getKurtosis();
+ }
+
+ /** Computes the skewness on the node values distribution and
+ * returns the asymmetry coefficient. It gives an indication about the
+ * distribution symmetry compared to the average.
+ *
+ *@return The skewness value as a double.
+ */
+ public double getSkewness() {
+ int n = this.getN();
+ double m3 = (((double)n) / (n-1)) * (cubicsum/n - Math.pow(getAverage(), 3) );
+ return ( m3 / Math.pow(getStD(), 3 ) );
+ }
+
+ /** Computes the kurtosis on the node values distribution and
+ * returns the flatness coefficient. It gives an indication about the
+ * distribution sharpness or flatness.
+ *
+ * @return The kurtosis momentus value as a double.
+ */
+ public double getKurtosis(){
+ int n = this.getN();
+ double m4 = (((double)n) / (n-1)) * (quadsum/n - Math.pow(getAverage(), 4) );
+ return ( m4 / Math.pow(getStD(), 4) )-3;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+import java.util.NoSuchElementException;
+import java.util.Random;
+
+/**
+* This class provides a random permutation of indexes. Useful for
+* random sampling without replacement.
+*/
+public class RandPermutation implements IndexIterator {
+
+
+// ======================= private fields ============================
+// ===================================================================
+
+
+private int[] buffer = null;
+
+private int len = 0;
+
+private int pointer = 0;
+
+private final Random r;
+
+
+// ======================= initialization ============================
+// ===================================================================
+
+
+/** Sets source of randomness to be used. You need to call
+* {@link #reset} to fully initialize the object.
+* @param r Source of randomness
+*/
+public RandPermutation( Random r ) { this.r=r; }
+
+// -------------------------------------------------------------------
+
+/** Sets source of randomness and initial size. It calls
+* {@link #setPermutation} to fully initialize the object with a
+* permuation ready to use.
+* @param r Source of randomness
+* @param k size of permutation
+*/
+public RandPermutation( int k, Random r ) {
+
+ this.r=r;
+ setPermutation(k);
+}
+
+
+// ======================= public methods ============================
+// ===================================================================
+
+
+/**
+* It calculates a random permutation of the integers from 0 to k-1.
+* The permutation can be read using method {@link #get}.
+* If the previous permutation was of the same length, it is more efficient.
+* Note that after calling this the object is reset, so {@link #next} can
+* be called k times, even if {@link #get} was called an arbitrary number of
+* times. Note however that mixing {@link #get} and {@link #next} results in
+* incorrect behavior for {@link #get} (but {@link #next} works fine).
+* The idea is to use this method only in connection with {@link #get}.
+*/
+public void setPermutation(int k) {
+
+ reset(k);
+
+ for(int i=len; i>1; i--)
+ {
+ int j = r.nextInt(i);
+ int a = buffer[j];
+ buffer[j] = buffer[i-1];
+ buffer[i-1] = a;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+* Returns the ith element of the permutation set by {@link #setPermutation}.
+* If {@link #next} is called after {@link #setPermutation} and before
+* this method, then the behavior of this method is unspecified.
+*/
+public int get(int i) {
+
+ if( i >= len ) throw new IndexOutOfBoundsException();
+ return buffer[i];
+}
+
+// -------------------------------------------------------------------
+
+/**
+* It initiates a random permutation of the integers from 0 to k-1.
+* It does not actually calculate the permutation.
+* The permutation can be read using method {@link #next}.
+* Calls to {@link #get} return undefined values, so {@link #next} must be used.
+* If the previous permutation was of the same length, it is more efficient.
+*/
+public void reset(int k) {
+
+ pointer = k;
+ if( len == k ) return;
+
+ if( buffer == null || buffer.length < k )
+ {
+ buffer = new int[k];
+ }
+
+ len = k;
+ for( int i=0; i<len; ++i ) buffer[i]=i;
+}
+
+// -------------------------------------------------------------------
+
+/** Next random sample without replacement */
+public int next() {
+
+ if( pointer < 1 ) throw new NoSuchElementException();
+
+ int j = r.nextInt(pointer);
+ int a = buffer[j];
+ buffer[j] = buffer[pointer-1];
+ buffer[pointer-1] = a;
+
+ return buffer[--pointer];
+}
+
+// -------------------------------------------------------------------
+
+public boolean hasNext() { return pointer > 0; }
+
+// -------------------------------------------------------------------
+
+/*
+public static void main( String pars[] ) throws Exception {
+
+ RandPermutation rp = new RandPermutation(new Random());
+
+ int k;
+
+ k = Integer.parseInt(pars[0]);
+ rp.setPermutation(k);
+ for(int i=0; i<k; ++i) System.out.println(rp.get(i));
+
+ System.out.println();
+
+ k = Integer.parseInt(pars[1]);
+ rp.reset(k);
+ while(rp.hasNext()) System.out.println(rp.next());
+
+ System.out.println();
+
+ k = Integer.parseInt(pars[2]);
+ rp.reset(k);
+ while(rp.hasNext()) System.out.println(rp.next());
+ System.out.println(rp.next());
+}
+*/
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+import java.math.*;
+import java.util.*;
+
+import org.lsmp.djep.groupJep.*;
+
+import peersim.config.*;
+
+/**
+ * This utility class can be used to parse range expressions. In particular,
+ * it is used by {@link peersim.rangesim.RangeSimulator} to express ranges for
+ * configuration properties.
+ * <p>
+ * The language for range expression is the following:
+ * <pre>
+ * [rangelist] := [range] | [range],[rangelist]
+ * [range] := value | min:max | min:max|step |
+ * min:max*|step
+ * </pre>
+ * where <tt>value</tt>, <tt>min</tt>, <tt>max</tt> and <tt>step</tt>
+ * are numeric atoms that defines ranges.
+ * <p>
+ * For example, the following range expression:
+ * <pre>
+ * 5,9:11,13:17|2,32:128*|2
+ * </pre>
+ * corresponds to 5 (single value), 9-10-11 (range between 9 and 11,
+ * default increment 1), 13-15-17 (range between 13 and 17, specified
+ * step 2, 32-64-128 (range between 32 and 128, multiplicative step 2).
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.8 $
+ */
+public class StringListParser
+{
+
+/** Disable instance construction */
+private StringListParser() { }
+
+/**
+ * Parse the specified string.
+ *
+ * @param s the string to be parsed
+ * @return an array of strings containing all the values defined by the
+ * range string
+ */
+public static String[] parseList(String s)
+{
+ ArrayList<String> list = new ArrayList<String>();
+ String[] tokens = s.split(",");
+ for (int i = 0; i < tokens.length; i++) {
+ parseItem(list, tokens[i]);
+ }
+ return list.toArray(new String[list.size()]);
+}
+
+private static void parseItem(List<String> list, String item)
+{
+ String[] array = item.split(":");
+ if (array.length == 1) {
+ parseSingleItem(list, item);
+ } else if (array.length == 2) {
+ parseRangeItem(list, array[0], array[1]);
+ } else {
+ throw new IllegalArgumentException("Element " + item
+ + "should be formatted as <start>:<stop> or <value>");
+ }
+}
+
+private static void parseSingleItem(List<String> list, String item)
+{
+ list.add(item);
+}
+
+private static void parseRangeItem(List<String> list, String start, String stop)
+{
+ Number vstart;
+ Number vstop;
+ Number vinc;
+ boolean sum;
+
+ GroupJep jep = new GroupJep(new Operators());
+ jep.parseExpression(start);
+ vstart = (Number) jep.getValueAsObject();
+ int pos = stop.indexOf("|*");
+ if (pos >= 0) {
+ // The string contains a multiplicative factor
+ jep.parseExpression(stop.substring(0, pos));
+ vstop = (Number) jep.getValueAsObject();
+ jep.parseExpression(stop.substring(pos + 2));
+ vinc = (Number) jep.getValueAsObject();
+ sum = false;
+ } else {
+ pos = stop.indexOf("|");
+ // The string contains an additive factor
+ if (pos >= 0) {
+ // The string contains just the final value
+ jep.parseExpression(stop.substring(0, pos));
+ vstop = (Number) jep.getValueAsObject();
+ jep.parseExpression(stop.substring(pos + 1));
+ vinc = (Number) jep.getValueAsObject();
+ sum = true;
+ } else {
+ jep.parseExpression(stop);
+ vstop = (Number) jep.getValueAsObject();
+ vinc = BigInteger.ONE;
+ sum = true;
+ }
+ }
+
+ if (vstart instanceof BigInteger && vstart instanceof BigInteger && vinc instanceof BigInteger) {
+ long vvstart = vstart.longValue();
+ long vvstop = vstop.longValue();
+ long vvinc = vinc.longValue();
+ if (sum) {
+ for (long i = vvstart; i <= vvstop; i += vvinc)
+ list.add("" + i);
+ } else {
+ for (long i = vvstart; i <= vvstop; i *= vvinc)
+ list.add("" + i);
+ }
+ } else {
+ double vvstart = vstart.doubleValue();
+ double vvstop = vstop.doubleValue();
+ double vvinc = vinc.doubleValue();
+ if (sum) {
+ for (double i = vvstart; i <= vvstop; i += vvinc)
+ list.add("" + i);
+ } else {
+ for (double i = vvstart; i <= vvstop; i *= vvinc)
+ list.add("" + i);
+ }
+ }
+}
+
+/*
+public static void main(String[] args)
+{
+ String[] ret = parseList(args[0]);
+ for (int i = 0; i < ret.length; i++)
+ System.out.print(ret[i] + " ");
+ System.out.println("");
+}
+*/
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.util;
+
+import java.util.NoSuchElementException;
+import java.util.Random;
+
+/**
+* This class provides a weighted random permutation of indexes.
+* Useful for weighted random sampling without replacement.
+* The next sample is taken according to the weights given as a parameter
+* to {@link #reset(int)}.
+* The weights work as follows.
+* The first sample is drawn according to the probability distribution
+* defined by the (normalized) weights.
+* After this the remaining k-1 elements and the associated k-1
+* (re-normalized) weights
+* define a new probability distribution, according to which the 2nd element
+* is drawn, and so on.
+*/
+public class WeightedRandPerm implements IndexIterator {
+
+
+// ======================= private fields ============================
+// ===================================================================
+
+/** Holds the weights that are used to initialize the permutation */
+private final double[] w;
+
+/** Holds the sum of the weights until the given index, inclusive. */
+private final double[] wsum;
+
+private int[] buffer = null;
+
+/** Working array for calculating the permutation */
+private double[] weights = null;
+
+private int len = 0;
+
+private int pointer = 0;
+
+private double sum = 0.0;
+
+private final Random r;
+
+
+// ======================= initialization ============================
+// ===================================================================
+
+
+/** Set the source of randomness to use and the weights. You need to call
+* {@link #reset} to fully initialize the object.
+* @param r source of randomness
+* @param weights The array that holds the weights for the calculation of the
+* permutation. The length of the array will be an upper bound on the
+* parameter {@link #reset} accepts. If {@link #reset} is called with a
+* parameter less than the length of weights, the prefix of the same length
+* is used.
+* The vector elements must be positive, that is, zero is not accepted either.
+*/
+public WeightedRandPerm( Random r, double[] weights ) {
+
+ this.r=r;
+ w = weights.clone();
+ wsum = weights.clone();;
+ this.weights = new double[w.length];
+ buffer = new int[w.length];
+
+ for(int i=0; i<w.length; ++i)
+ {
+ if( w[i] <= 0.0 ) throw new IllegalArgumentException(
+ "weights should be positive: w["+i+"]="+w[i]);
+ }
+
+ for(int i=1; i<w.length; ++i) wsum[i]+=wsum[i-1];
+}
+
+
+// ======================= public methods ============================
+// ===================================================================
+
+
+/**
+* It initiates a random weighted permutation of the integeres from 0 to k-1.
+* It does not actually calculate the permutation.
+* The permutation can be read using method {@link #next}.
+* If the previous permutation was of the same length, it is more efficient.
+* The weights set at construction time work as follows.
+* The first sample is drawn according to the probability distribution
+* defined by the (normalized) weights.
+* After this the remaining k-1 elements and the associated k-1
+* (re-normalized) weights
+* define a new probability distribution, according to which the 2nd element
+* is drawn, and so on.
+* @param k the set is defined as 0,...,k-1
+*/
+public void reset(int k) {
+
+ if( k<0 || k>w.length )
+ throw new IllegalArgumentException(
+ "k should be non-negative and <= "+w.length);
+
+ pointer = k;
+ sum = wsum[k-1];
+
+ if( k != len )
+ {
+ // we need to initialize weights and buffer
+ for(int i=0; i<k; ++i)
+ {
+ weights[i]=w[i];
+ buffer[i]=i;
+ }
+ len=k;
+ }
+}
+
+// -------------------------------------------------------------------
+
+/**
+* The first sample is drawn according to the probability distribution
+* defined by the (normalized) weights.
+* After this the remaining k-1 elements and the associated k-1
+* (re-normalized) weights
+* define a new probability distribution, according to which the 2nd element
+* is drawn, and so on.
+* @see #reset
+*/
+public int next() {
+
+ if( pointer < 1 ) throw new NoSuchElementException();
+
+ double d = sum*r.nextDouble();
+ int i = pointer;
+ double tmp = weights[i-1];
+ while( tmp < d && i>1 ) tmp += weights[--i-1];
+
+ // now i-1 is the selected element, we shift it to next position
+ int a = buffer[i-1];
+ double b = weights[i-1];
+ buffer[i-1] = buffer[pointer-1];
+ weights[i-1] = weights[pointer-1];
+ buffer[pointer-1] = a;
+ weights[pointer-1] = b;
+ sum -= b;
+
+ return buffer[--pointer];
+}
+
+// -------------------------------------------------------------------
+
+public boolean hasNext() { return pointer > 0; }
+
+// -------------------------------------------------------------------
+
+/*
+public static void main( String pars[] ) throws Exception {
+
+
+ int k = pars.length;
+ double w[] = new double[k];
+ for(int i=0; i<k; ++i) w[i] = Double.parseDouble(pars[i]);
+
+ WeightedRandPerm rp = new WeightedRandPerm(new Random(),w);
+ rp.reset(k);
+ for(int i=0; i<1000; ++i)
+ {
+ if(i%2==0) rp.reset(k);
+ if(i%2==1) rp.reset(k-1);
+ while(rp.hasNext()) System.out.print(rp.next()+" ");
+ System.out.println();
+ }
+
+ System.out.println();
+}
+*/
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import java.lang.reflect.*;
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * Vectors can be written through this class. Typically {@link Control} classes
+ * use this to manipulate vectors.
+ * <p>
+ * The method to be used is specified at construction time.
+ * For backward compatibility, if no method is specified, the method
+ * <code>getValue</code> is used. In this way, protocols
+ * implementing the {@link SingleValue} interface can be manipulated using the
+ * old configuration syntax (i.e., without specifying the method).
+ * <p>
+ * Please refer to package {@link peersim.vector} for a detailed description of
+ * the concept of protocol vector and the role of getters and setters.
+ */
+public class Getter {
+
+// ============================ fields ===================================
+// =======================================================================
+
+private final String protocol;
+private final String methodn;
+private final String prefix;
+
+/** Identifier of the protocol that defines the vector */
+private int pid;
+
+/** Getter method name */
+private String methodName;
+
+/** Getter method */
+private Method method = null;
+
+/** Parameter type of getter method */
+private Class type;
+
+
+// ========================== initialization =============================
+// =======================================================================
+
+
+/**
+ * Constructs a Getter class based on the configuration. Note that the
+ * actual initialization is delayed until the first access to the class,
+ * so that if a class is not used, no unnecessary error messages and exceptions
+ * are generated.
+ * @param prefix the configuration prefix to use when reading the configuration
+ * @param protocol the configuration parameter name that contains
+ * the protocol we want to manipulate using a getter method.
+ * The parameter <code>prefix + "." + protocol</code> is read.
+ * @param methodn the configuration parameter name that contains the getter
+ * method name.
+ * The parameter <code>prefix + "." + methodn</code> is read, with the default
+ * value <code>getValue</code>.
+ */
+public Getter(String prefix, String protocol, String methodn) {
+
+ this.prefix=prefix;
+ this.protocol=protocol;
+ this.methodn=methodn;
+}
+
+// --------------------------------------------------------------------------
+
+/** Performs actual initialization */
+private void init() {
+
+ if( method!=null) return;
+
+ // Read configuration parameter
+ pid = Configuration.getPid(prefix + "." + protocol);
+ methodName = Configuration.getString(prefix+"."+methodn,"getValue");
+ // Search the method
+ Class clazz = Network.prototype.getProtocol(pid).getClass();
+ try {
+ method = GetterSetterFinder.getGetterMethod(clazz, methodName);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalParameterException(prefix + "." +
+ methodn, e+"");
+ }
+ // Obtain the type of the field
+ type = GetterSetterFinder.getGetterType(method);
+}
+
+
+// =============================== methods =============================
+// =====================================================================
+
+/**
+* @return type of return value of getter method
+*/
+public Class getType() {
+
+ init();
+ return type;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+* Gets the given value as a Number.
+* @param n The node to get the value on. The protocol is defined
+* by {@link #pid}.
+* @return the read value.
+*/
+public Number get(Node n) {
+
+ init();
+
+ try
+ {
+ Object ret =method.invoke(n.getProtocol(pid));
+ if (ret instanceof Boolean)
+ return ((Boolean) ret) ? 1 : 0;
+ else
+ return (Number) ret;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("While using getter "+methodName,e);
+ }
+}
+
+// --------------------------------------------------------------------------
+
+/**
+* Gets the given integer value.
+* @param n The node to get the value on. The protocol is defined
+* by {@link #pid}.
+* @return the read value.
+*/
+public long getLong(Node n) {
+
+ init();
+
+ if(type==long.class || type==int.class)
+ {
+ try
+ {
+ return ((Number)
+ method.invoke(n.getProtocol(pid))).longValue();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(
+ "While using getter "+methodName,e);
+ }
+ }
+ else throw new RuntimeException("type has to be int or long");
+}
+
+// --------------------------------------------------------------------------
+
+/**
+* Gets the given real value.
+* @param n The node to get the value on. The protocol is defined
+* by {@link #pid}.
+* @return the read value.
+*/
+public double getDouble(Node n) {
+
+ init();
+
+ if(type==double.class || type==float.class)
+ {
+ try
+ {
+ return ((Number)
+ method.invoke(n.getProtocol(pid))).doubleValue();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(
+ "While using getter "+methodName,e);
+ }
+ }
+ else throw new RuntimeException(
+ "type has to be double or float");
+}
+
+// --------------------------------------------------------------------------
+
+/**
+* Gets the given value as a Number.
+* @param i The index of the node to get the value on in the network.
+* The protocol is defined
+* by {@link #pid}.
+* @return the read value.
+*/
+public Number get(int i) { return get(Network.get(i)); }
+
+// --------------------------------------------------------------------------
+
+/**
+* Gets the given integer value.
+* @param i The index of the node to get the value on in the network.
+* The protocol is defined
+* by {@link #pid}.
+* @return the read value.
+*/
+public long getLong(int i) { return getLong(Network.get(i)); }
+
+// --------------------------------------------------------------------------
+
+/**
+* Gets the given real value.
+* @param i The index of the node to get the value on in the network.
+* The protocol is defined
+* by {@link #pid}.
+* @return the read value.
+*/
+public double getDouble(int i) { return getDouble(Network.get(i)); }
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ * This utility class can be used to obtain get/set methods from classes. In
+ * particular, it is used in the vector package to locate get/set methods for
+ * observing and modifying protocol fields.
+ * Please refer to package {@link peersim.vector} for a definition of
+ * getter and setter methods.
+ */
+class GetterSetterFinder
+{
+
+//--------------------------------------------------------------------------
+
+/**
+ * Search a getter method in the specified class. It succeeds only of there
+ * is exactly one method with the given name that is a getter method.
+ * Please refer to package {@link peersim.vector} for a definition of
+ * getter methods.
+ *
+ * @param clazz
+ * the class where to find get/set method
+ * @param methodName
+ * the method to be searched
+ * @return the requested method
+ */
+public static Method getGetterMethod(Class clazz, String methodName)
+ throws NoSuchMethodException
+{
+ // Search methods
+ Method[] methods = clazz.getMethods();
+ ArrayList<Method> list = new ArrayList<Method>();
+ for (Method m: methods) {
+ if (m.getName().equals(methodName)) {
+ list.add(m);
+ }
+ }
+ if (list.size() == 0) {
+ throw new NoSuchMethodException("No getter method for method "
+ + methodName + " in class " + clazz.getName());
+ } else if (list.size() > 1) {
+ throw new NoSuchMethodException("Multiple getter for method "
+ + methodName + " in class " + clazz.getName());
+ }
+
+ // Found a single method with the right name; check if
+ // it is a gettter.
+ Method method = list.get(0);
+ Class[] pars = method.getParameterTypes();
+ if (pars.length > 0) {
+ throw new NoSuchMethodException(method.getName() + " of class "
+ + clazz.getName()
+ + " is not a valid getter method: "+
+ "its argument list is not empty");
+ }
+
+ Class ret = method.getReturnType();
+ if ( !( ret==int.class || ret==long.class ||
+ ret==double.class || ret==float.class || ret==boolean.class)
+ ) {
+ throw new NoSuchMethodException(method.getName() + " of class "
+ + clazz.getName() + " is not a valid getter method: "+
+ "it should have a return type of int, long, short or double");
+ }
+
+ return method;
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Search a setter method in the specified class.
+ * It succeeds only of there
+ * is exactly one method with the given name that is a setter method.
+ * Please refer to package {@link peersim.vector} for a definition of
+ * setter methods.
+ * @param clazz
+ * the class where to find get/set method
+ * @param methodName
+ * the method to be searched
+ * @return the requested method, if it fully conforms to the definition of
+ * the setter methods.
+ */
+public static Method getSetterMethod(Class clazz, String methodName)
+ throws NoSuchMethodException
+{
+ // Search methods
+ Method[] methods = clazz.getMethods();
+ ArrayList<Method> list = new ArrayList<Method>();
+ for (Method m: methods) {
+ if (m.getName().equals(methodName)) {
+ list.add(m);
+ }
+ }
+
+ if (list.size() == 0) {
+ throw new NoSuchMethodException("No setter method for method "
+ + methodName + " in class " + clazz.getName());
+ } else if (list.size() > 1) {
+ throw new NoSuchMethodException("Multiple setter for method "
+ + methodName + " in class " + clazz.getName());
+ }
+
+ // Found a single method with the right name; check if
+ // it is a setter.
+ Method method = list.get(0);
+ Class[] pars = method.getParameterTypes();
+ if ( pars.length != 1 ||
+ !( pars[0]==int.class || pars[0]==long.class ||
+ pars[0]==double.class || pars[0]==float.class )
+ ) {
+ throw new NoSuchMethodException(method.getName() + " of class "
+ + clazz.getName()
+ + " is not a valid setter method: "+
+ "it should have exactly one argument of type "+
+ "int, long, short or double");
+ }
+
+ Class ret = method.getReturnType();
+ if (!ret.equals(void.class)) {
+ throw new NoSuchMethodException(method.getName() + " of class "
+ + clazz.getName() +
+ " is not a valid setter method; it returns a value");
+ }
+
+ return method;
+}
+
+//--------------------------------------------------------------------------
+
+
+/**
+ * Returns the field type for the specified getter.
+ */
+public static Class getGetterType(Method m)
+{
+ return m.getReturnType();
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Returns the field type for the specified setter.
+ */
+public static Class getSetterType(Method m)
+{
+ Class[] pars = m.getParameterTypes();
+ return pars[0];
+}
+
+//--------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import java.io.*;
+import java.util.*;
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * Initializes a protocol vector from data read from a file.
+ * The file format is as follows:
+ * lines starting with # or lines that contain only
+ * whitespace are ignored.
+ * From the rest of the lines the first field separated by whitespace is
+ * read. Only the first field is read from each line, the rest of the line
+ * is ignored.
+ * The file can contain more values than necessary but
+ * enough values must be present.
+ * @see VectControl
+ * @see peersim.vector
+ */
+public class InitVectFromFile extends VectControl
+{
+
+// --------------------------------------------------------------------------
+// Parameter names
+// --------------------------------------------------------------------------
+
+/**
+ * The filename to load links from.
+ * @config
+ */
+private static final String PAR_FILE = "file";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** The file to be read */
+private final String file;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public InitVectFromFile(String prefix)
+{
+ super(prefix);
+ file = Configuration.getString(prefix + "." + PAR_FILE);
+}
+
+// --------------------------------------------------------------------------
+// Methods
+// --------------------------------------------------------------------------
+
+/**
+ * Initializes values from a file.
+ * The file format is as follows:
+ * lines starting with # or lines that contain only
+ * whitespace are ignored.
+ * From the rest of the lines the first field separated by whitespace is
+ * read. Only the first field is read from each line, the rest of the line
+ * is ignored.
+ * The file can contain more values than necessary but
+ * enough values must be present.
+ * @throws RuntimeException if the file cannot be read or contains too few
+ * values
+ * @return always false
+ */
+public boolean execute() {
+
+ int i = 0;
+
+try {
+ FileReader fr = new FileReader(file);
+ LineNumberReader lnr = new LineNumberReader(fr);
+ String line;
+ while ((line = lnr.readLine()) != null && i < Network.size()) {
+ if (line.startsWith("#"))
+ continue;
+ StringTokenizer st = new StringTokenizer(line);
+ if (!st.hasMoreTokens())
+ continue;
+ if( setter.isInteger() )
+ setter.set(i,Long.parseLong(st.nextToken()));
+ else setter.set(i,Double.parseDouble(st.nextToken()));
+ i++;
+ }
+ lnr.close();
+}
+catch(IOException e)
+{
+ throw new RuntimeException("Unable to read file: " + e);
+}
+
+ if (i < Network.size())
+ throw new RuntimeException(
+ "Too few values in file '" + file + "' (only "
+ + i + "); we need " + Network.size() + ".");
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * Initializes a protocol vector with values in the range [{@value #PAR_MIN},
+ * {@value #PAR_MAX}] (inclusive both ends), linearly increasing.
+ * @see VectControl
+ * @see peersim.vector
+ */
+public class LinearDistribution extends VectControl
+{
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * Upper end of the interval..
+ * @config
+ */
+private static final String PAR_MAX = "max";
+
+/**
+ * Lower end of the interval. Defaults to -{@value #PAR_MAX}.
+ * @config
+ */
+private static final String PAR_MIN = "min";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** Minimum value */
+private final Number min;
+
+/** Maximum value */
+private final Number max;
+
+/** Step value */
+private final double step;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public LinearDistribution(String prefix)
+{
+ super(prefix);
+
+ // Read parameters based on type
+ if (setter.isInteger()) {
+ max=Long.valueOf(Configuration.getLong(prefix + "." + PAR_MAX));
+ min=Long.valueOf(Configuration.getLong(prefix + "." + PAR_MIN,
+ -max.longValue()));
+ step= (max.longValue()-min.longValue())/
+ ((double)(Network.size()-1));
+ } else { // we know it's double or float
+ max = new Double(Configuration.getDouble(prefix+"."+PAR_MAX));
+ min = new Double(Configuration.getDouble(prefix+"."+PAR_MIN,
+ -max.doubleValue()));
+ step= (max.doubleValue()-min.doubleValue())/(Network.size()-1);
+ }
+}
+
+// --------------------------------------------------------------------------
+// Methods
+// --------------------------------------------------------------------------
+
+/**
+ * Initializes a protocol vector with values in the range [{@value #PAR_MIN},
+ * {@value #PAR_MAX}] (inclusive both ends), linearly increasing.
+ * @return always false
+ */
+public boolean execute() {
+
+ if ( setter.isInteger() )
+ {
+ for(int i=0; i<Network.size(); ++i)
+ {
+ // we avoid the entire expression being cast to double
+ setter.set(i,Math.round(i*step)+min.longValue());
+ }
+ }
+ else
+ {
+ for(int i=0; i<Network.size(); ++i)
+ {
+ setter.set(i,i*step+min.doubleValue());
+ }
+ }
+
+ return false;
+}
+
+
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * Normalizes the values of a protocol vector.
+ * The normalization is based on the L1 norm, that is, the sum of the
+ * absolute values of the vector elements. Parameter {@value #PAR_L1} defines
+ * the L1 norm that the vector will have after normalization.
+ * @see VectControl
+ * @see peersim.vector
+ */
+public class Normalizer extends VectControl
+{
+
+// --------------------------------------------------------------------------
+// Parameters
+// --------------------------------------------------------------------------
+
+/**
+ * The L1 norm (sum of absolute values) to normalize to. After the operation the
+ * L1 norm will be the value given here. Defaults to 1.
+ * @config
+ */
+private static final String PAR_L1 = "l1";
+
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** L1 norm */
+private final double l1;
+
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public Normalizer(String prefix)
+{
+ super(prefix);
+ l1 = Configuration.getDouble(prefix + "." + PAR_L1, 1);
+
+ if( setter.isInteger() )
+ throw new IllegalParameterException(prefix + "." + PAR_METHOD,
+ "setter value must be floating point, instead of "+
+ setter.getType());
+
+ if( setter.getType() != getter.getType() )
+ throw new IllegalParameterException(prefix + "." + PAR_GETTER,
+ "getter and setter must have the same numeric type, "+
+ "but we have "+setter.getType()+" and "+getter.getType());
+}
+
+//--------------------------------------------------------------------------
+//Methods
+//--------------------------------------------------------------------------
+
+/**
+ * Makes the sum of the absolute values (L1 norm) equal to the value
+ * given in the configuration parameter {@value #PAR_L1}. If the value is
+ * negative, the L1 norm will be the absolute value and the vector elements
+ * change sign.
+ * @return always false
+ */
+public boolean execute() {
+
+ double sum = 0.0;
+ for (int i = 0; i < Network.size(); ++i)
+ {
+ sum += getter.getDouble(i);
+ }
+ if (sum == 0.0)
+ {
+ throw new
+ RuntimeException("Attempted to normalize all zero vector.");
+ }
+ double factor = l1 / sum;
+ for (int i = 0; i < Network.size(); ++i)
+ {
+ double val = getter.getDouble(i)*factor;
+ setter.set(i,val);
+ }
+ return false;
+}
+
+//--------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.config.*;
+import peersim.core.*;
+
+
+/**
+ * Initializes the values so that {@value #PAR_PEAKS} nodes have value
+ * {@value #PAR_VALUE}/{@value #PAR_PEAKS}, the rest {@value #PAR_LVALUE}
+ * (zero by default).
+ * @see VectControl
+ * @see peersim.vector
+ */
+public class PeakDistribution extends VectControl
+{
+
+// --------------------------------------------------------------------------
+// Parameters
+// --------------------------------------------------------------------------
+
+/**
+ * The sum of values in the system, to be equally distributed between peak
+ * nodes.
+ * @config
+ */
+private static final String PAR_VALUE = "value";
+
+/**
+ * The value for the nodes that are not peaks. This parameter is optional,
+ * by default, the nodes that are
+ * not peaks are set to zero. This value overrides that behavior.
+ * @config
+ */
+private static final String PAR_LVALUE = "background";
+
+/**
+ * The number of peaks in the system. If this value is greater than or equal to
+ * 1, it is interpreted as the actual number of peaks. If it is included in
+ * the range [0, 1] it is interpreted as a percentage with respect to the
+ * current network size. Defaults to 1.
+ * @config
+ */
+private static final String PAR_PEAKS = "peaks";
+
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** Total load */
+private final Number lvalue;
+
+/** Total load */
+private final Number value;
+
+/** Number of peaks */
+private final double peaks;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public PeakDistribution(String prefix)
+{
+ super(prefix);
+
+ peaks = Configuration.getDouble(prefix+"."+PAR_PEAKS, 1);
+
+ if( setter.isInteger() )
+ {
+ value=Long.valueOf(Configuration.getLong(prefix+"."+PAR_VALUE));
+ lvalue=Long.valueOf(Configuration.getLong(prefix+"."+PAR_LVALUE,0));
+ }
+ else
+ {
+ value = new Double(Configuration.getDouble(prefix + "." +
+ PAR_VALUE));
+ lvalue = new Double(Configuration.getDouble(prefix + "." +
+ PAR_LVALUE,0));
+ }
+}
+
+// --------------------------------------------------------------------------
+// Methods
+// --------------------------------------------------------------------------
+
+/**
+ * Initializes the values so that {@value #PAR_PEAKS} nodes have value
+ * {@value #PAR_VALUE}/{@value #PAR_PEAKS}, the rest zero.
+ * @return always false
+ */
+public boolean execute()
+{
+ int pn = (peaks < 1 ? (int) (peaks*Network.size()) : (int) peaks);
+
+ if( setter.isInteger() )
+ {
+ long v = value.longValue()/pn;
+ long lv = lvalue.longValue();
+ for (int i=0; i < pn; i++) setter.set(i, v);
+ for (int i=pn; i < Network.size(); i++) setter.set(i,lv);
+ }
+ else
+ {
+ double v = value.doubleValue()/pn;
+ double lv = lvalue.doubleValue();
+ for (int i=0; i < pn; i++) setter.set(i, v);
+ for (int i=pn; i < Network.size(); i++) setter.set(i,lv);
+ }
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import java.lang.reflect.*;
+import peersim.config.*;
+import peersim.core.*;
+
+/**
+ * Vectors can be written through this class. Typically {@link Control} classes
+ * use this to manipulate vectors.
+ * <p>
+ * The method to be used is specified at construction time.
+ * For backward compatibility, if no method is specified, the method
+ * <code>setValue</code> is used. In this way, protocols
+ * implementing the {@link SingleValue} interface can be manipulated using the
+ * old configuration syntax (i.e., without specifying the method).
+ * <p>
+ * Please refer to package {@link peersim.vector} for a detailed description of
+ * the concept of protocol vector and the role of getters and setters.
+ */
+public class Setter {
+
+// ============================ fields ===================================
+// =======================================================================
+
+private final String protocol;
+private final String methodn;
+private final String prefix;
+
+/** Identifier of the protocol that defines the vector */
+private int pid;
+
+/** Setter method name */
+private String methodName;
+
+/** Setter method */
+private Method method=null;
+
+/** Parameter type of setter method */
+private Class type;
+
+
+// ========================== initialization =============================
+// =======================================================================
+
+
+/**
+ * Constructs a Setter class based on the configuration.
+ * Note that the
+ * actual initialization is delayed until the first access to the class,
+ * so that if a class is not used, no unnecessary error messages and exceptions
+ * are generated.
+ * @param prefix the configuration prefix to use when reading the configuration
+ * @param protocol the configuration parameter name that contains
+ * the protocol we want to manipulate using a setter method.
+ * The parameter <code>prefix + "." + protocol</code> is read.
+ * @param methodn the configuration parameter name that contains the setter
+ * method name.
+ * The parameter <code>prefix + "." + methodn</code> is read, with the default
+ * value <code>setValue</code>.
+ */
+public Setter(String prefix, String protocol, String methodn) {
+
+ this.prefix=prefix;
+ this.protocol=protocol;
+ this.methodn=methodn;
+}
+
+// --------------------------------------------------------------------------
+
+private void init() {
+
+ if( method!=null) return;
+
+ // Read configuration parameter
+ pid = Configuration.getPid(prefix + "." + protocol);
+ methodName = Configuration.getString(prefix+"."+methodn,"setValue");
+ // Search the method
+ Class clazz = Network.prototype.getProtocol(pid).getClass();
+ try {
+ method = GetterSetterFinder.getSetterMethod(clazz, methodName);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalParameterException(prefix + "." +
+ methodn, e+"");
+ }
+ // Obtain the type of the field
+ type = GetterSetterFinder.getSetterType(method);
+}
+
+
+// =============================== methods =============================
+// =====================================================================
+
+
+/**
+* @return type of parameter of setter method
+*/
+public Class getType() {
+
+ init();
+ return type;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+* @return true if the setter type is long or int
+*/
+public boolean isInteger() {
+
+ init();
+ return type==long.class || type==int.class;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+* Sets the given integer value.
+* @param n The node to set the value on. The protocol is defined
+* by {@link #pid}.
+* @param val the value to set.
+*/
+public void set(Node n, long val) {
+
+ init();
+
+ try
+ {
+ if(type==long.class)
+ {
+ method.invoke(n.getProtocol(pid),val);
+ return;
+ }
+ if(type==int.class)
+ {
+ method.invoke(n.getProtocol(pid),(int)val);
+ return;
+ }
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("While using setter "+methodName,e);
+
+ }
+
+ throw new RuntimeException("type has to be int or long");
+}
+
+// --------------------------------------------------------------------------
+
+/**
+* Sets the given real value.
+* @param n The node to set the value on. The protocol is defined
+* by {@link #pid}.
+* @param val the value to set.
+*/
+public void set(Node n, double val) {
+
+ init();
+
+ try
+ {
+ if(type==double.class)
+ {
+ method.invoke(n.getProtocol(pid),val);
+ return;
+ }
+ if(type==float.class)
+ {
+ method.invoke(n.getProtocol(pid),(float)val);
+ return;
+ }
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("While using setter "+methodName,e);
+ }
+
+ throw new RuntimeException("type has to be double or float");
+}
+
+// --------------------------------------------------------------------------
+
+/**
+* Sets the given integer value.
+* @param i The index of the node to set the value on in the network.
+* The protocol is defined
+* by {@link #pid}.
+* @param val the value to set.
+*/
+public void set(int i, long val) { set(Network.get(i),val); }
+
+// --------------------------------------------------------------------------
+
+/**
+* Sets the given real value.
+* @param i The index of the node to set the value on in the network.
+* The protocol is defined
+* by {@link #pid}.
+* @param val the value to set.
+*/
+public void set(int i, double val) { set(Network.get(i),val); }
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+/**
+* The implementor class has a single parameter. This interface
+* provides access to that parameter.
+*/
+public interface SingleValue {
+
+/**
+ * Returns the value of the parameter hold by the implementor
+ * of this interface.
+ */
+public double getValue();
+
+/**
+ * Modifies the value of the parameter hold by the implementor
+ * of this interface.
+ */
+public void setValue(double value);
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.core.*;
+import java.util.*;
+
+/**
+ * This comparator class compares two node objects based on the value
+ * maintained by one of its protocols. The protocol must implement the
+ * {@link SingleValue} interface; its identifier has to be specified when a
+ * new comparator is built.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.4 $
+ */
+public class SingleValueComparator implements Comparator
+{
+
+/** Protocol to be be compared */
+private int pid;
+
+/**
+ * Builds a new comparator that compares the double values maintained
+ * by protocol identified by <code>pid</code>.
+ */
+public SingleValueComparator(int pid) { this.pid = pid; }
+
+/**
+ * Compares the values of two protocols. The parameters must have dynamic type
+ * {@link Node}. The protocol {@link #pid} is accessed on both nodes. These
+ * protocols have to implement the {@link SingleValue} interface. The values
+ * held by these protocol instances are then compared.
+ */
+public int compare(Object o1, Object o2)
+{
+ SingleValue s1 = (SingleValue) ((Node) o1).getProtocol(pid);
+ SingleValue s2 = (SingleValue) ((Node) o2).getProtocol(pid);
+ return (int) (s1.getValue() - s2.getValue());
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.core.*;
+
+/**
+ * The task of this protocol is to store a single double value and make it
+ * available through the {@link SingleValue} interface.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.6 $
+ */
+public class SingleValueHolder
+implements SingleValue, Protocol
+{
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+/** Value held by this protocol */
+protected double value;
+
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Does nothing.
+ */
+public SingleValueHolder(String prefix)
+{
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Clones the value holder.
+ */
+public Object clone()
+{
+ SingleValueHolder svh=null;
+ try { svh=(SingleValueHolder)super.clone(); }
+ catch( CloneNotSupportedException e ) {} // never happens
+ return svh;
+}
+
+//--------------------------------------------------------------------------
+//methods
+//--------------------------------------------------------------------------
+
+/**
+ * @inheritDoc
+ */
+public double getValue()
+{
+ return value;
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * @inheritDoc
+ */
+public void setValue(double value)
+{
+ this.value = value;
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Returns the value as a string.
+ */
+public String toString() { return ""+value; }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.util.*;
+
+/**
+* Print statistics over a vector. The vector is defined by a protocol,
+* specified by {@value #PAR_PROT}, that has to implement
+* {@link SingleValue}.
+* Statistics printed are: min, max, number of samples, average, variance,
+* number of minimal instances, number of maximal instances (using
+* {@link IncrementalStats#toString}).
+* @see IncrementalStats
+*/
+public class SingleValueObserver implements Control {
+
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * The parameter used to determine the accuracy
+ * (standard deviation) before stopping the simulation. If not
+ * defined, a negative value is used which makes sure the observer
+ * does not stop the simulation.
+ * @see #execute
+ * @config
+ */
+private static final String PAR_ACCURACY = "accuracy";
+
+/**
+ * The protocol to operate on.
+ * @config
+ */
+private static final String PAR_PROT = "protocol";
+
+
+//--------------------------------------------------------------------------
+// Fields
+//--------------------------------------------------------------------------
+
+/** The name of this observer in the configuration */
+private final String name;
+
+/** Accuracy for standard deviation used to stop the simulation */
+private final double accuracy;
+
+/** Protocol identifier */
+private final int pid;
+
+
+//--------------------------------------------------------------------------
+// Constructor
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param name the configuration prefix for this class
+ */
+public SingleValueObserver(String name)
+{
+ this.name = name;
+ accuracy = Configuration.getDouble(name + "." + PAR_ACCURACY, -1);
+ pid = Configuration.getPid(name + "." + PAR_PROT);
+}
+
+
+//--------------------------------------------------------------------------
+// Methods
+//--------------------------------------------------------------------------
+
+/**
+* Print statistics over a vector. The vector is defined by a protocol,
+* specified by {@value #PAR_PROT}, that has to implement
+* {@link SingleValue}.
+* Statistics printed are: min, max, number of samples, average, variance,
+* number of minimal instances, number of maximal instances (using
+* {@link IncrementalStats#toString}).
+* @return true if the standard deviation is below the value of
+ * {@value #PAR_ACCURACY}, and the time of the simulation is larger then zero
+ * (i.e. it has started).
+ */
+public boolean execute()
+{
+ IncrementalStats stats = new IncrementalStats();
+
+ /* Compute max, min, average */
+ for (int i = 0; i < Network.size(); i++)
+ {
+ SingleValue v = (SingleValue)Network.get(i).getProtocol(pid);
+ stats.add( v.getValue() );
+ }
+
+ /* Printing statistics */
+ System.out.println(name+": "+stats);
+
+ /* Terminate if accuracy target is reached */
+ return (stats.getStD()<=accuracy && CommonState.getTime()>0);
+}
+
+//--------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+
+/**
+ * Do testing the vector package.
+ */
+public class TestVectors extends SingleValueHolder
+{
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+/** Value held by this protocol */
+protected float fvalue;
+
+/** Value held by this protocol */
+protected int ivalue;
+
+/** Value held by this protocol */
+protected long lvalue;
+
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Builds a new (not initialized) value holder.
+ * Calls super constructor.
+ */
+public TestVectors(String prefix) { super(prefix); }
+
+//--------------------------------------------------------------------------
+//methods
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+public int getIValue() { return ivalue; }
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+public void setIValue(int value) { ivalue = value; }
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+public float getFValue() { return fvalue; }
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+public void setFValue(float value) { fvalue = value; }
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+public long getLValue() { return lvalue; }
+
+//--------------------------------------------------------------------------
+
+/**
+ *
+ */
+public void setLValue(long value) { lvalue = value; }
+
+//--------------------------------------------------------------------------
+
+/**
+ * Returns the value as a string.
+ */
+public String toString() { return value+" "+fvalue+" "+ivalue+" "+lvalue; }
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.dynamics.*;
+
+/**
+ * Initializes the values drawing uniform random samples from the range
+ * [{@value #PAR_MIN}, {@value #PAR_MAX}[.
+ * @see VectControl
+ * @see peersim.vector
+ */
+public class UniformDistribution extends VectControl implements NodeInitializer
+{
+
+//--------------------------------------------------------------------------
+//Parameter names
+//--------------------------------------------------------------------------
+
+/**
+ * The upper bound of the uniform random variable, exclusive.
+ * @config
+ */
+private static final String PAR_MAX = "max";
+
+/**
+ * The lower bound of the uniform
+ * random variable, inclusive. Defaults to -{@value #PAR_MAX}.
+ * @config
+ */
+private static final String PAR_MIN = "min";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** Minimum value */
+private final Number min;
+
+/** Maximum value */
+private final Number max;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public UniformDistribution(String prefix)
+{
+ super(prefix);
+
+ // Read parameters based on type
+ if (setter.isInteger()) {
+ max=Long.valueOf(Configuration.getLong(prefix + "." + PAR_MAX));
+ min=Long.valueOf(Configuration.getLong(prefix + "." + PAR_MIN,
+ -max.longValue()));
+ } else { // we know it's double or float
+ max = new Double(Configuration.getDouble(prefix+"."+PAR_MAX));
+ min = new Double(Configuration.getDouble(prefix+"."+PAR_MIN,
+ -max.doubleValue()));
+ }
+}
+
+// --------------------------------------------------------------------------
+// Methods
+// --------------------------------------------------------------------------
+
+/**
+ * Initializes the values drawing uniform random samples from the range
+ * [{@value #PAR_MIN}, {@value #PAR_MAX}[.
+ * @return always false
+ */
+public boolean execute() {
+
+ if(setter.isInteger())
+ {
+ long d = max.longValue() - min.longValue();
+ for (int i = 0; i < Network.size(); ++i)
+ {
+ setter.set(i,CommonState.r.nextLong(d)+min.longValue());
+ }
+ }
+ else
+ {
+ double d = max.doubleValue() - min.doubleValue();
+ for (int i = 0; i < Network.size(); ++i)
+ {
+ setter.set(i,CommonState.r.nextDouble()*d+
+ min.doubleValue());
+ }
+ }
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+ * Initializes the value drawing a uniform random sample from the range
+ * [{@value #PAR_MIN}, {@value #PAR_MAX}[.
+ * @param n the node to initialize
+ */
+public void initialize(Node n) {
+
+ if( setter.isInteger() )
+ {
+ long d = max.longValue() - min.longValue();
+ setter.set(n,CommonState.r.nextLong(d) + min.longValue());
+ }
+ else
+ {
+ double d = max.doubleValue() - min.doubleValue();
+ setter.set(n,CommonState.r.nextDouble()*d);
+ }
+}
+
+// --------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import java.io.*;
+
+import peersim.config.*;
+import peersim.core.*;
+import peersim.util.*;
+
+/**
+ * Dump the content of a vector in a file. Each line
+ * represent a single node.
+ * Values are dumped to a file whose name is obtained from a
+ * configurable prefix (set by {@value #PAR_BASENAME}), a number that is
+ * increased before each dump by one, and the extension ".vec".
+ * <p>
+ * This observer class can observe any protocol field containing a
+ * primitive value, provided that the field is associated with a getter method
+ * that reads it.
+ * @see VectControl
+ * @see peersim.vector
+ */
+public class ValueDumper extends VectControl {
+
+
+// --------------------------------------------------------------------------
+// Parameter names
+// --------------------------------------------------------------------------
+
+/**
+ * This is the base name of the file where the values are saved. The full name
+ * will be baseName+cycleid+".vec".
+ * @config
+ */
+private static final String PAR_BASENAME = "outf";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** Prefix name of this observer */
+private final String prefix;
+
+/** Base name of the file to be written */
+private final String baseName;
+
+private final FileNameGenerator fng;
+
+// --------------------------------------------------------------------------
+// Constructor
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public ValueDumper(String prefix) {
+
+ super(prefix);
+ this.prefix = prefix;
+ baseName = Configuration.getString(prefix + "." + PAR_BASENAME, null);
+ if(baseName!=null) fng = new FileNameGenerator(baseName,".vec");
+ else fng = null;
+}
+
+// --------------------------------------------------------------------------
+// Methods
+// --------------------------------------------------------------------------
+
+/**
+ * Dump the content of a vector in a file. Each line
+ * represent a single node.
+ * Values are dumped to a file whose name is obtained from a
+ * configurable prefix (set by {@value #PAR_BASENAME}), a number that is
+ * increased before each dump by one, and the extension ".vec".
+ * @return always false
+ * @throws RuntimeException if there is an I/O problem
+ */
+public boolean execute() {
+try
+{
+ System.out.print(prefix + ": ");
+
+ // initialize output streams
+ if (baseName != null)
+ {
+ String filename = fng.nextCounterName();
+ System.out.println("writing "+filename);
+ PrintStream pstr =
+ new PrintStream(new FileOutputStream(filename));
+ for (int i = 0; i < Network.size(); ++i)
+ {
+ pstr.println(getter.get(i));
+ }
+ pstr.close();
+ }
+ else
+ {
+ System.out.println();
+ for (int i = 0; i < Network.size(); ++i)
+ {
+ System.out.println(getter.get(i));
+ }
+ }
+}
+catch (IOException e)
+{
+ throw new RuntimeException(prefix + ": Unable to write to file: " + e);
+}
+
+ return false;
+}
+
+// ---------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.core.*;
+
+/**
+ * Observes the cosine angle between two vectors. The number which is output is
+ * the inner product divided by the product of the length of the vectors.
+ * All values are converted to double before processing.
+ * <p>
+ * This observer class can observe any protocol field containing a
+ * primitive value, provided that the field is associated with a getter method
+ * that reads it.
+ * The methods to be used are specified through parameter {@value #PAR_METHOD1}
+ * and {@value #PAR_METHOD2}.
+ * <p>
+ * Please refer to package {@link peersim.vector} for a detailed description of
+ * this mechanism.
+ */
+public class VectAngle implements Control
+{
+
+// --------------------------------------------------------------------------
+// Parameters
+// --------------------------------------------------------------------------
+
+/**
+ * The first protocol to be observed.
+ * @config
+ */
+private static final String PAR_PROT1 = "protocol1";
+
+/**
+ * The second protocol to be observed.
+ * @config
+ */
+private static final String PAR_PROT2 = "protocol2";
+
+/**
+ * The getter method used to obtain the values of the first protocol.
+ * Defaults to <code>getValue</code> (for backward compatibility with previous
+ * implementation of this class, that were based on the
+ * {@link SingleValue} interface).
+ * Refer to the {@linkplain peersim.vector vector package description} for more
+ * information about getters and setters.
+ * @config
+ */
+private static final String PAR_METHOD1 = "getter1";
+
+/**
+ * The getter method used to obtain the values of the second protocol.
+ * Defaults to <code>getValue</code> (for backward compatibility with previous
+ * implementation of this class, that were based on the
+ * {@link SingleValue} interface).
+ * Refer to the {@linkplain peersim.vector vector package description} for more
+ * information about getters and setters.
+ * @config
+ */
+private static final String PAR_METHOD2 = "getter2";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** The prefix for this observer*/
+private final String name;
+
+private final Getter getter1;
+
+private final Getter getter2;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public VectAngle(String prefix)
+{
+ name = prefix;
+ getter1 = new Getter(prefix,PAR_PROT1,PAR_METHOD1);
+ getter2 = new Getter(prefix,PAR_PROT2,PAR_METHOD2);
+}
+
+// --------------------------------------------------------------------------
+// Methods
+// --------------------------------------------------------------------------
+
+/**
+ * Observes the cosine angle between two vectors. The printed values
+ * are: cosine, Eucledian norm of vect 1, Eucledian norm of vector 2,
+ * angle in radians.
+* @return always false
+*/
+public boolean execute() {
+
+ double sqrsum1 = 0, sqrsum2 = 0, prod = 0;
+ for (int i = 0; i < Network.size(); ++i)
+ {
+ double v1= getter1.get(i).doubleValue();
+ double v2= getter2.get(i).doubleValue();
+ sqrsum1 += v1 * v1;
+ sqrsum2 += v2 * v2;
+ prod += v2 * v1;
+ }
+
+ double cos = prod / Math.sqrt(sqrsum1) / Math.sqrt(sqrsum2);
+
+ // deal with numeric errors
+ if( cos > 1 ) cos=1;
+ if( cos < -1 ) cos = -1;
+
+ System.out.println(name+": " + cos + " "
+ + Math.sqrt(sqrsum1) + " " + Math.sqrt(sqrsum2) + " "
+ + Math.acos(cos));
+ return false;
+}
+
+//--------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.core.*;
+
+/**
+ * Serves as an abstract superclass for {@link Control}s that deal
+ * with vectors.
+ * It reads a {@link Setter} to be used by extending classes.
+ */
+public abstract class VectControl implements Control {
+
+
+// --------------------------------------------------------------------------
+// Parameter names
+// --------------------------------------------------------------------------
+
+/**
+ * The protocol to operate on.
+ * @config
+ */
+protected static final String PAR_PROT = "protocol";
+
+/**
+ * The setter method used to set values in the protocol instances. Defaults to
+ * <code>setValue</code>
+ * (for backward compatibility with previous implementation of this
+ * class, that were based on the {@link SingleValue} interface). Refer to the
+ * {@linkplain peersim.vector vector package description} for more
+ * information about getters and setters.
+ * @config
+ */
+protected static final String PAR_METHOD = "setter";
+
+/**
+ * The getter method used to obtain the protocol values.
+ * Defaults to <code>getValue</code>
+ * (for backward compatibility with previous
+ * implementation of this class, that were based on the
+ * {@link SingleValue} interface).
+ * Refer to the {@linkplain peersim.vector vector package description} for more
+ * information about getters and setters.
+ * @config
+ */
+protected static final String PAR_GETTER = "getter";
+
+// --------------------------------------------------------------------------
+// Fields
+// --------------------------------------------------------------------------
+
+/** The setter to be used to write a vector. */
+protected final Setter setter;
+
+/** The getter to be used to read a vector. */
+protected final Getter getter;
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+protected VectControl(String prefix)
+{
+ setter = new Setter(prefix,PAR_PROT,PAR_METHOD);
+ getter = new Getter(prefix,PAR_PROT,PAR_GETTER);
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.core.*;
+import peersim.dynamics.*;
+
+/**
+ * Sets values in a protocol vector by copying the values of another
+ * protocol vector.
+ * The source is defined by {@value #PAR_SOURCE},
+ * and getter method {@value peersim.vector.VectControl#PAR_GETTER}.
+ * <p>
+ * This dynamics class can copy any primitive field in the source
+ * protocol to any primitive field in the destination protocol,
+ * provided that the former field is associated with a getter method,
+ * while the latter is associated with a setter method.
+ * @see VectControl
+ * @see peersim.vector
+ */
+public class VectCopy extends VectControl implements NodeInitializer {
+
+
+//--------------------------------------------------------------------------
+//Parameters
+//--------------------------------------------------------------------------
+
+/**
+ * The identifier of the protocol to be copied.
+ * The vector values are copied from this vector.
+ * @config
+ */
+private static final String PAR_SOURCE = "source";
+
+
+// --------------------------------------------------------------------------
+// Variables
+// --------------------------------------------------------------------------
+
+/** Source getter */
+private final Getter source;
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public VectCopy(String prefix)
+{
+ super(prefix);
+ source = new Getter(prefix,PAR_SOURCE,PAR_GETTER);
+}
+
+//--------------------------------------------------------------------------
+//Method
+//--------------------------------------------------------------------------
+
+/**
+ * Sets values in a protocol vector by copying the values of another
+ * protocol vector. The source is defined by {@value #PAR_SOURCE},
+ * and getter method {@value peersim.vector.VectControl#PAR_GETTER}.
+ * @return always false
+ */
+public boolean execute() {
+
+ int size = Network.size();
+ for (int i = 0; i < size; i++) {
+ Number ret = source.get(i);
+ if(setter.isInteger()) setter.set(i,ret.longValue());
+ else setter.set(i,ret.doubleValue());
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------------
+
+/**
+ * Sets the value by copying the value of another
+ * protocol. The source is defined by {@value #PAR_SOURCE},
+ * and getter method {@value peersim.vector.VectControl#PAR_GETTER}.
+ * @param n the node to initialize
+ */
+public void initialize(Node n) {
+
+ Number ret = source.get(n);
+ if(setter.isInteger()) setter.set(n,ret.longValue());
+ else setter.set(n,ret.doubleValue());
+}
+
+//--------------------------------------------------------------------------
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import peersim.core.*;
+
+/**
+ * This class provides a generic implementation of the
+ * <code>java.lang.Comparator<code> interface specialized
+ * for {@link Node} objects. Nodes are compared based
+ * on one of their protocols, on which a configurable
+ * method is invoked. Both the protocol id and the
+ * method are specified in the constructor.
+ * <br>
+ * This comparator can be used, for example, to sort
+ * an array of nodes based on method <code>getValue</code>
+ * associated to the protocol <code>pid</code>:
+ * <PRE>
+ * Comparator c = new VectorComparator(pid, "getValue");
+ * Array.sort(Node[] array, c);
+ * </PRE>
+ * Note that differently from other classes in this package,
+ * VectorComparator is declared programmatically in the code
+ * and not in the configuration file. It is included in this
+ * package because it shares the same philosophy of the other
+ * classes.
+ *
+ * @author Alberto Montresor
+ * @version $Revision: 1.1 $
+ */
+public class VectorComparator implements Comparator
+{
+
+//--------------------------------------------------------------------------
+//Fields
+//--------------------------------------------------------------------------
+
+/** Protocol identifier of the protocol to be observed */
+private final int pid;
+
+/** The getter to be used to obtain comparable values */
+private final Method method;
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+public VectorComparator(int pid, String methodName)
+{
+ this.pid = pid;
+ Node n = Network.prototype;
+ if (n == null) {
+ throw new IllegalStateException("No prototype node can be used to search methods");
+ }
+ Object p = n.getProtocol(pid);
+ Class c = p.getClass();
+ try {
+ method = GetterSetterFinder.getGetterMethod(c, methodName);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+}
+
+
+public int compare(Object o1, Object o2)
+{
+ try {
+ Comparable c1 = (Comparable) method.invoke(((Node) o1).getProtocol(pid));
+ Comparable c2 = (Comparable) method.invoke(((Node) o2).getProtocol(pid));
+ return c1.compareTo(c2);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getCause().getMessage());
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e.getCause().getMessage());
+ }
+}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.vector;
+
+import peersim.core.*;
+import peersim.util.*;
+
+/**
+ * This class computes and reports statistics information about a vector.
+ * Provided statistics include average, max, min, variance,
+ * etc. Values are printed according to the string format of {@link
+ * IncrementalStats#toString}.
+ * @see VectControl
+ * @see peersim.vector
+ */
+public class VectorObserver extends VectControl {
+
+
+/** The name of this observer in the configuration */
+private final String prefix;
+
+
+//--------------------------------------------------------------------------
+//Initialization
+//--------------------------------------------------------------------------
+
+/**
+ * Standard constructor that reads the configuration parameters.
+ * Invoked by the simulation engine.
+ * @param prefix the configuration prefix for this class
+ */
+public VectorObserver(String prefix) {
+
+ super(prefix);
+ this.prefix = prefix;
+}
+
+//--------------------------------------------------------------------------
+// Methods
+//--------------------------------------------------------------------------
+
+/**
+ * Prints statistics information about a vector.
+ * Provided statistics include average, max, min, variance,
+ * etc. Values are printed according to the string format of {@link
+ * IncrementalStats#toString}.
+ * @return always false
+ */
+public boolean execute() {
+
+ IncrementalStats stats = new IncrementalStats();
+
+ for (int j = 0; j < Network.size(); j++)
+ {
+ Number v = getter.get(j);
+ stats.add( v.doubleValue() );
+ }
+
+ System.out.println(prefix+": "+stats);
+
+ return false;
+}
+
+}
--- /dev/null
+package psgsim;
+
+import org.simgrid.msg.Host;
+import peersim.core.Network;
+import peersim.core.Node;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ *
+ * NodeHost class used to make the mapping Node-Host.
+ *
+ * @author Khaled Baati 26/10/2014
+ * @version version 1.1
+ */
+public class NodeHost {
+
+ /**
+ * A collection of map contained the couple (host,node)
+ */
+ public static TreeMap<Node, Host> mapHostNode = new TreeMap<Node, Host>(
+ new Comparator<Node>() {
+ public int compare(Node n1, Node n2) {
+ return String.valueOf(n1.getID()).compareTo(
+ String.valueOf(n2.getID()));
+ }
+ });
+
+ /**
+ * The main method to make the mapping Node to Host in the
+ * {@link #mapHostNode} field
+ */
+ public static void start() {
+ Host host = null;
+ for (Integer i = 0; i < PSGSimulator.size; i++) {
+ host = PSGPlatform.hostList[i];
+ mapHostNode.put(Network.get(i), host);
+ }
+ }
+
+ /**
+ * This static method gets a Node instance associated with a host of your
+ * platform.
+ *
+ * @param host
+ * The host associated in your platform.
+ * @return The node associated.
+ */
+ public static Node getNode(Host host) {
+ for (Map.Entry<Node, Host> element : mapHostNode.entrySet()) {
+ if (element.getValue() == host)
+ return element.getKey();
+ }
+ return null;
+ }
+
+ /**
+ * This static method gets a host instance associated with the node of your
+ * platform.
+ *
+ * @param node
+ * The node associated in your platform.
+ * @return The host associated, else return null (host doesn't exist).
+ */
+ public static Host getHost(Node node) {
+ for (Map.Entry<Node, Host> element : mapHostNode.entrySet()) {
+ if (element.getKey() == node)
+ return element.getValue();
+ }
+ return null;
+ }
+}
--- /dev/null
+package psgsim;
+
+import org.simgrid.msg.Host;
+import org.simgrid.msg.HostNotFoundException;
+
+import peersim.core.Node;
+
+/**
+ *
+ * This class can change the size of networks by adding and removing nodes, as
+ * {@link peersim.dynamics.DynamicNetwork} in peersim.
+ *
+ * @author Khaled Baati 09/02/2015
+ * @version version 1.1
+ */
+public class PSGDynamicNetwork {
+
+ /**
+ * Removes the node from the network.
+ *
+ * @param node
+ * the node to be removed
+ */
+ public static void remove(Node node) {
+ // NodeHost.getHost(node).off();
+ // Host h=NodeHost.mapHostNode.get(node);
+ // NodeHost.mapHostNode.remove(node);
+ PSGSimulator.size = PSGSimulator.size - 1;
+ }
+
+ /**
+ * Adds a node to the network.
+ *
+ * @param node
+ * the node to be added
+ * @throws HostNotFoundException
+ */
+ public static void add(Node node) throws HostNotFoundException {
+ Host host = PSGPlatform.hostList[(int) node.getID()];
+ NodeHost.mapHostNode.put(node, host);
+ if (PSGPlatform.interfED)
+ new PSGProcessEvent(host, host.getName(), null).start();
+ PSGSimulator.size = PSGSimulator.size + 1;
+ }
+}
--- /dev/null
+package psgsim;
+
+import java.io.*;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.jdom2.*;
+import org.jdom2.output.*;
+import org.simgrid.msg.Host;
+import org.simgrid.msg.Msg;
+
+import peersim.config.Configuration;
+import peersim.core.Control;
+import peersim.core.Protocol;
+
+/**
+ * A class store different configuration information for simulation. It creates
+ * the deployment file according to this informations.
+ *
+ * @author Khaled Baati 26/10/2014
+ * @version version 1.1
+ */
+
+public class PSGPlatform {
+
+ enum timeUnit {
+ us, ms, sec;
+ }
+
+ /** unit of measure. **/
+ static int unit;
+
+ /** the clock. **/
+ static double time;
+
+ /** the default unit of measure **/
+ static final String sec = "sec";
+
+ /** All protocols defined in the configuration file. **/
+ static Protocol[] protocolsName;
+
+ /** A numeric identifier associated for each protocol. **/
+ static int[] pid;
+
+ /** List of hos.t **/
+ static Host[] hostList;
+
+ /** A collection map represents the Control and its associated step. **/
+ static Map<Control, Double> controlStepMap = new LinkedHashMap<Control, Double>();
+
+ /** A collection map represents the protocol and its associated pid. **/
+ static TreeMap<Protocol, Integer> protocolsPidsMap = new TreeMap<Protocol, Integer>(
+ new Comparator<Protocol>() {
+ public int compare(Protocol p1, Protocol p2) {
+ return p1.toString().compareTo(p2.toString());
+ }
+ });
+
+ /** A collection map represents all CDProtocol and its associated step **/
+ static TreeMap<Protocol, Double> cdProtocolsStepMap = new TreeMap<Protocol, Double>(
+ new Comparator<Protocol>() {
+ public int compare(Protocol p1, Protocol p2) {
+ return p1.toString().compareTo(p2.toString());
+ }
+ });
+ /** the default platform file **/
+ static final String platformFile = "platforms/psg.xml";
+
+ /** the deployment file **/
+ static final String deploymentFile = "deployment.xml";
+
+ static Element racine;
+ static Document document;
+ static boolean interfED = false;
+ static boolean interfCD = false;
+
+ /** Prepare the deployment file **/
+ static {
+ DocType dtype = new DocType("platform",
+ "http://simgrid.gforge.inria.fr/simgrid.dtd");
+ racine = new Element("platform");
+ document = new Document(racine, dtype);
+ Attribute version = new Attribute("version", "3");
+ racine.setAttribute(version);
+ }
+
+ // ========================== methods ==================================
+ // =====================================================================
+
+ /**
+ * Convert PS unit time to Simgrid unit time
+ *
+ * @param valeur
+ * the value to convert
+ * @return time converted
+ */
+ public static double psToSgTime(long valeur) {
+ timeUnit unit = unit();
+ switch (unit) {
+ case us:
+ return ((double) valeur) / 1000000;
+ case ms:
+ return ((double) valeur) / 1000;
+ default:
+ return (double) valeur;
+
+ }
+ }
+
+ /**
+ * Convert Simgrid unit time to PS unit time
+ *
+ * @param valeur
+ * the value to convert
+ * @return time converted
+ */
+ public static long sgToPsTime(double valeur) {
+ timeUnit unit = unit();
+ switch (unit) {
+ case us:
+ return (long) valeur * 1000000;
+ case ms:
+ return (long) valeur * 1000;
+ default:
+ return (long) valeur;
+
+ }
+ }
+
+ /**
+ *
+ * @return the duration of simulation.
+ */
+ public static long getDuration() {
+ return Configuration.getLong("simulation.duration");
+ }
+
+ /**
+ *
+ * @return PeerSim Time
+ */
+ public static long getTime() {
+ return sgToPsTime(Msg.getClock());
+ }
+
+ /**
+ *
+ * @return the Simgrid Clock
+ */
+ public static double getClock() {
+ return Msg.getClock();
+ }
+
+ /**
+ * Load and run initializers.
+ */
+ public static void init() {
+ Object[] inits = Configuration.getInstanceArray("init");
+ String names[] = Configuration.getNames("init");
+ for (int i = 0; i < inits.length; ++i) {
+ System.err.println("- Running initializer " + names[i] + ": "
+ + inits[i].getClass().toString());
+ ((Control) inits[i]).execute();
+ }
+ }
+
+ /**
+ * Load all controls and stores them in {@link #controlStepMap} collection
+ * to be scheduled, and executed in {@link psgsim.PSGProcessController}.
+ */
+ public static void control() {
+ // load controls
+ String[] names = Configuration.getNames("control");
+ Control control;
+ for (int i = 0; i < names.length; ++i) {
+ control = (Control) Configuration.getInstance(names[i]);
+ Long stepControl = Configuration.getLong(names[i] + "." + "step");
+ controlStepMap.put(control, psToSgTime(stepControl));
+ }
+ }
+
+ /**
+ * Lookup all protocols in the configuration file
+ */
+ public static void protocols() {
+ String[] names = Configuration.getNames("protocol");
+ Class[] interfaces;
+ protocolsName = new Protocol[names.length];
+ pid = new int[names.length];
+ boolean save = false;
+ for (int i = 0; i < names.length; i++) {
+ protocolsName[i] = (Protocol) Configuration.getInstance(names[i]);
+ if (i == names.length - 1)
+ save = true;
+ userProtocol(protocolsName[i], names[i], save);
+ pid[i] = i;
+ protocolsPidsMap.put(protocolsName[i], pid[i]);
+ }
+
+ }
+
+ /**
+ * Lookup CDProtocol and EDProtocol among all protocols
+ *
+ * @param prot
+ * the protocol class
+ * @param names
+ * the protocol name
+ * @param save
+ * parameter equal true when parsing all protocols
+ */
+ public static void userProtocol(Protocol prot, String names, boolean save) {
+ Class[] interfaces = prot.getClass().getInterfaces();
+ for (int j = 0; j < interfaces.length; j++) {
+ if (interfaces[j].getName().endsWith("EDProtocol")) {
+ interfED = true;
+ }
+ if (interfaces[j].getName().endsWith("CDProtocol")) {
+ String protName = names.substring("protocol".length() + 1);
+ long step = Configuration.getLong("protocol" + "." + protName
+ + "." + "step");
+ cdProtocolsStepMap.put(prot, psToSgTime(step));
+ }
+ }
+ if (save) {
+ edProt();
+ }
+ }
+
+ /**
+ *
+ */
+ private static void edProt() {
+ Host hostVal;
+ hostList = Host.all();
+ for (int i = 0; i < PSGSimulator.size; i++) {
+ hostVal = hostList[i];
+ Element process = new Element("process");
+ racine.addContent(process);
+ Attribute host = new Attribute("host", hostVal.getName());
+ Attribute function = new Attribute("function",
+ "psgsim.PSGProcessEvent");
+ process.setAttribute(host);
+ process.setAttribute(function);
+ }
+ save(deploymentFile);
+
+ }
+
+ /**
+ *
+ */
+ @Deprecated
+ private static void cdProt() {
+ for (int i = 0; i < PSGSimulator.size; i++) {
+ Element process = new Element("process");
+ racine.addContent(process);
+ Attribute host = new Attribute("host", String.valueOf(i));
+ Attribute function = new Attribute("function",
+ "psgsim.PSGProcessCycle");
+ process.setAttribute(host);
+ process.setAttribute(function);
+
+ }
+ save("deployment.xml");
+
+ }
+
+ /**
+ * Reads given configuration property: "platform". If not found, returns the
+ * default value.
+ *
+ * @return the platform file
+ */
+ public static String platformFile() {
+ String defFile = platformFile;
+ String file = Configuration.getString("platform", defFile);
+ return file;
+ }
+
+ /**
+ * Reads given configuration property: "unit". If not found, returns the
+ * default value (ms).
+ *
+ * @return the unit of measure
+ */
+ public static timeUnit unit() {
+ String defUnit = sec;
+ String unit = Configuration.getString("unit", defUnit);
+ timeUnit t = timeUnit.valueOf(unit);
+ return t;
+ }
+
+ /**
+ * Create the deployment file
+ *
+ * @param file
+ * the name of the deployment file
+ */
+ public static void save(String file) {
+ try {
+ // On utilise ici un affichage classique avec getPrettyFormat()
+ XMLOutputter out = new XMLOutputter(Format.getPrettyFormat());
+ out.output(document, new FileOutputStream(file));
+ } catch (java.io.IOException e) {
+ }
+ }
+
+ /**
+ * Delete the deployment file
+ *
+ * @param path
+ * the path of the deployment file
+ */
+ public static void delete(String path) {
+ File file = new File(path);
+ try {
+ file.delete();
+ } catch (Exception e) {
+ System.err.println("deployment file not found");
+
+ }
+ System.err.println(path + "file deleted");
+
+ }
+
+}
--- /dev/null
+package psgsim;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.simgrid.msg.Host;
+import org.simgrid.msg.MsgException;
+
+import peersim.core.Control;
+
+/**
+ * This class executes all controls object scheduled in the
+ * {@link PSGPlatform#controlStepMap} collection.
+ *
+ * @author Khaled Baati 10/12/2014
+ * @version version 1.1
+ */
+public class PSGProcessController extends org.simgrid.msg.Process {
+
+ private Map<Control, Double> controlStepMapTmp = new LinkedHashMap<Control, Double>();
+
+ public PSGProcessController(Host host, String name, String[] args) {
+ super(host, name, null);
+ }
+
+ @Override
+ public void main(String[] args) throws MsgException {
+ Double nextControlEvent;
+ for (Map.Entry<Control, Double> entry : PSGPlatform.controlStepMap
+ .entrySet()) {
+ controlStepMapTmp.put(entry.getKey(), entry.getValue());
+ }
+ while (PSGPlatform.getTime() <= PSGPlatform.getDuration()) {
+ for (Map.Entry<Control, Double> entrytmp : controlStepMapTmp
+ .entrySet()) {
+ Control cle = entrytmp.getKey();
+ Double valeur = entrytmp.getValue();
+ if (PSGPlatform.getTime() % valeur == 0) {
+ cle.execute();
+ if (PSGPlatform.getTime() != 0)
+ for (Map.Entry<Control, Double> entry : PSGPlatform.controlStepMap
+ .entrySet()) {
+ if (cle == entry.getKey())
+ controlStepMapTmp.replace(cle, valeur, valeur
+ + entry.getValue());
+
+ }
+ }
+ }
+ nextControlEvent = next();
+ if (nextControlEvent + PSGPlatform.getTime() >= PSGPlatform.getDuration()) {
+ break;
+ } else {
+ waitFor(nextControlEvent);
+ }
+ }
+ }
+
+ private Double next() {
+ Double min = controlStepMapTmp.values().iterator().next();
+ for (Map.Entry<Control, Double> entry : controlStepMapTmp.entrySet()) {
+ Double valeur = (entry.getValue() - PSGPlatform.getClock());
+ if (min > valeur)
+ min = valeur;
+ }
+
+ return min;
+ }
+}
\ No newline at end of file
--- /dev/null
+package psgsim;
+
+import java.util.Map.Entry;
+
+import org.simgrid.msg.Host;
+import peersim.cdsim.CDProtocol;
+import peersim.core.Node;
+import peersim.core.Protocol;
+
+/**
+ * This class handle an event of type NextCycleEvent, received on the protocol
+ * identified by a pid among all CDProtocols defined in the
+ * {@link PSGPlatform#cdProtocolsStepMap} collection.
+ * <p>
+ * It executes the nextCyle method associated to this protocol.
+ *
+ * @author Khaled Baati 27/10/2014
+ * @version version 1.1
+ */
+public class PSGProcessCycle {
+
+ /**
+ * Executes the nextCycle method of the CDprotocol with the appropriate
+ * parameters, and schedules the next call using {@link PSGSimulator#add}.
+ *
+ * @param host
+ * the host on which this component is run
+ * @param name
+ * the host's name
+ * @param delay
+ * the start time
+ * @param event
+ * the actual event
+ * @param pid
+ * the protocol identifier
+ */
+ public static void nextCycle(Host host, String name, double delay,
+ Object event, int pid) {
+ CDProtocol cdp = null;
+ Node node = NodeHost.getNode(host);
+ cdp = (CDProtocol) node.getProtocol(pid);
+ cdp.nextCycle(node, pid);
+ PSGTransport.flush();
+ for (Entry<Protocol, Double> entry : PSGPlatform.cdProtocolsStepMap
+ .entrySet()) {
+ Double step = entry.getValue();
+ for (Entry<Protocol, Integer> p : PSGPlatform.protocolsPidsMap
+ .entrySet()) {
+ if (p.getValue() == pid) {
+ break;
+ }
+ }
+ if (PSGPlatform.getTime() <= PSGPlatform.getDuration() && host.isOn()) {
+ PSGSimulator.add(PSGPlatform.sgToPsTime(step), event, node, pid);
+ }
+ }
+ }
+
+}
--- /dev/null
+package psgsim;
+
+import org.simgrid.msg.Host;
+import org.simgrid.msg.MsgException;
+
+import peersim.core.Node;
+import peersim.edsim.EDProtocol;
+
+/**
+ * This class extends {@link org.simgrid.msg.Process} which creates a process
+ * for each host (corresponding to node in peersim) in the system.
+ * <p>
+ * The main method of this class is to handle events received, by calling the
+ * processEvent method on the corresponding node and pid.
+ * <p>
+ * See {@link peersim.edsim.EDProtocol#processEvent}
+ *
+ * @author Khaled Baati 28/10/2014
+ * @version version 1.1
+ */
+public class PSGProcessEvent extends org.simgrid.msg.Process {
+ /** The delivered event **/
+ private PSGTask task;
+ /** The current protocol **/
+ private EDProtocol prot;
+ /** The identifier of the current protocol **/
+ private int pid;
+
+ /**
+ * Constructs a new process from the name of a host.
+ *
+ * @param host
+ * the local host to create according to the active node in
+ * peersim
+ * @param name
+ * the host's name
+ * @param args
+ * The arguments of main method of the process.
+ */
+ public PSGProcessEvent(Host host, String name, String[] args) {
+ super(host, name, args);
+ }
+
+ @Override
+ public void main(String[] args) throws MsgException {
+ Node node = NodeHost.getNode(getHost());
+ Host.setAsyncMailbox(getHost().getName());
+ while (PSGPlatform.getTime() < PSGPlatform.getDuration()) {
+ task = null;
+ task = (PSGTask) PSGTask.receive(Host.currentHost().getName(),
+ PSGPlatform.psToSgTime(PSGPlatform.getDuration() - PSGPlatform.getTime()-1));
+ if (task != null && PSGPlatform.getTime() < PSGPlatform.getDuration()) {
+ pid = task.getPid();
+ prot = (EDProtocol) node.getProtocol(pid);
+ prot.processEvent(node, pid, task.getEvent());
+ PSGTransport.flush();
+ } else
+ break;
+ }
+ }
+
+}
--- /dev/null
+package psgsim;
+
+import org.simgrid.msg.Host;
+import org.simgrid.msg.MsgException;
+
+import peersim.core.Node;
+import peersim.edsim.EDProtocol;
+import peersim.edsim.NextCycleEvent;
+
+/**
+ * This class extends {@link org.simgrid.msg.Process}, it creates a process for
+ * each event added in {@link PSGSimulator#add}.
+ * <p>
+ * This class performs to launch the appropriate call according to the type of
+ * the event;
+ * <p>
+ * - A NextCycleEvent: the event will be delivered to the
+ * {@link PSGProcessCycle} for treatment.
+ * <p>
+ * - Otherwise the event is delivered to the destination protocol, that must
+ * implement {@link EDProtocol}, and the processEvent method is executed.
+ *
+ * @author Khaled Baati 12/11/2014
+ * @version version 1.1
+ */
+public class PSGProcessLauncher extends org.simgrid.msg.Process {
+ private EDProtocol prot = null;
+ private int pid;
+ private double delay;
+ private Object event;
+ private Host host;
+
+ /**
+ * Constructs a new process from the name of a host and with the associated
+ * parameters
+ *
+ * @param host
+ * the local host
+ * @param name
+ * the host's name
+ * @param delay
+ * the start time of the process
+ * @param event
+ * the event added to the simulator
+ * @param pid
+ * the protocol identifier
+ */
+ public PSGProcessLauncher(Host host, String name, double delay, Object event,
+ int pid) {
+ super(host, name, null, delay, -1);
+ this.host = host;
+ this.pid = pid;
+ this.event = event;
+ this.delay = delay;
+ }
+
+ public PSGProcessLauncher(Host host, String name, String[] args) {
+
+ }
+
+ @Override
+ public void main(String[] args) throws MsgException {
+ Node node = NodeHost.getNode(host);
+ if (event instanceof NextCycleEvent) {
+ PSGProcessCycle.nextCycle(Host.currentHost(), host.getName(),
+ delay, event, pid);
+ } else {
+ prot = (EDProtocol) node.getProtocol(pid);
+ prot.processEvent(node, pid, event);
+ PSGTransport.flush();
+ }
+ waitFor(500);
+ }
+}
\ No newline at end of file
--- /dev/null
+package psgsim;
+
+import org.simgrid.msg.Host;
+import org.simgrid.msg.HostNotFoundException;
+import org.simgrid.msg.Msg;
+import org.simgrid.msg.NativeException;
+
+import peersim.core.CommonState;
+import peersim.core.Network;
+import peersim.core.Node;
+
+/**
+ * This is the main entry point to Simgrid simulator. This class loads the
+ * different parameters and initializes the simulation.
+ *
+ * @author Khaled Baati 14/10/2014
+ * @version version 1.1
+ */
+public class PSGSimulator {
+ public static int size;
+
+ static {
+ Network.reset();
+ size = Network.size();
+ }
+
+ // ========================== methods ==================================
+ // =====================================================================
+
+ /**
+ * Adds a new event to be scheduled, specifying the number of time units of
+ * delay (in seconds), the node and the protocol identifier to which the
+ * event will be delivered. A {@link psgsim.PSGProcessLauncher} process will
+ * be created according to this event.
+ *
+ * @param delay
+ * The number of time units (seconds in simgrid) before the event
+ * is scheduled.
+ * @param event
+ * The object associated to this event
+ * @param src
+ * The node associated to the event.
+ * @param pid
+ * The identifier of the protocol to which the event will be
+ * delivered
+ */
+ public static void add(long delay, Object event, Node src, int pid) {
+ Host host = NodeHost.getHost(src);
+ double startTime = PSGPlatform.psToSgTime(delay) + Msg.getClock();
+ if (startTime < PSGPlatform.psToSgTime(PSGPlatform.getDuration()) ) {
+ try {
+ /**
+ * random instruction associated to Heap.add(...) method in
+ * peersim.edsim
+ **/
+ CommonState.r.nextInt(1 << 8);
+ new PSGProcessLauncher(host, host.getName(), startTime, event,
+ pid).start();
+ } catch (HostNotFoundException e) {
+ System.err.println("Host not found");
+ }
+ }
+
+ }
+
+ // ========================== main method ==================================
+ // =====================================================================
+ public static void main() throws NativeException, HostNotFoundException {
+
+ String platformfile = PSGPlatform.platformFile();
+ System.err.println(platformfile + " loaded");
+ String[] arguments = { platformfile, "deployment.xml" };
+ Msg.init(arguments);
+
+ /** construct the platform */
+ Msg.createEnvironment(arguments[0]);
+
+ PSGPlatform.protocols();
+
+ /** deploy the application **/
+ Msg.deployApplication(arguments[1]);
+
+ /** construct the host-node mapping **/
+ NodeHost.start();
+
+ /** Process Controller **/
+ PSGPlatform.control();
+ new PSGProcessController(PSGPlatform.hostList[0],
+ PSGPlatform.hostList[0].getName(), null).start();
+
+ /** Load and execute the initializers classes in the configuration file **/
+ PSGPlatform.init();
+
+ PSGPlatform.delete("deployment.xml");
+ /** execute the simulation. **/
+ Msg.run();
+ }
+}
--- /dev/null
+package psgsim;
+
+import org.simgrid.msg.HostFailureException;
+import org.simgrid.msg.Task;
+import org.simgrid.msg.TimeoutException;
+import org.simgrid.msg.TransferFailureException;
+
+/**
+ * The PSGTask includes all the parameters of sending a message as the size, the
+ * compute duration and the protocol identifier.
+ *
+ * @author Khaled Baati 28/10/2014
+ * @version version 1.1
+ */
+public class PSGTask extends org.simgrid.msg.Task {
+ /** The Message to be sent **/
+ private Object event;
+ /** The protocol identifier **/
+ private int pid;
+
+ /**
+ * Construct a new task to be sent.
+ *
+ * @param name
+ * The name of task
+ * @param computeDuration
+ * The compute duration
+ *
+ * @param messageSize
+ * The size of the message
+ * @param event
+ * The message to be sent
+ * @param pid
+ * The protocol identifier
+ */
+ public PSGTask(String name, double computeDuration, double messageSize,
+ Object event, int pid) {
+ super(name, computeDuration, messageSize);
+ this.event = event;
+ this.pid = pid;
+ }
+
+ /**
+ *
+ * @return the protocol identifier
+ */
+ public int getPid() {
+ return pid;
+ }
+
+ /**
+ *
+ * @return the message
+ */
+ public Object getEvent() {
+ return event;
+ }
+
+ /**
+ * Retrieves next task on the mailbox identified by the specified name (wait
+ * at most timeout seconds)
+ *
+ * @param mailbox
+ * the mailbox on where to receive the task
+ * @param timeout
+ * the timeout to wait for receiving the task
+ * @return the task
+ */
+ public static Task receive(String mailbox, double timeout) {
+ double time = PSGPlatform.getClock();
+ if (time + timeout > PSGPlatform.getClock()) {
+ try {
+ return receive(mailbox, timeout, null);
+ } catch (TimeoutException e) {
+ } catch (TransferFailureException e) {
+ e.printStackTrace();
+ } catch (HostFailureException e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+
+ }
+
+}
--- /dev/null
+package psgsim;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import peersim.config.Configuration;
+import peersim.config.IllegalParameterException;
+import peersim.core.CommonState;
+import peersim.core.Node;
+import peersim.transport.Transport;
+
+/**
+ * PSGTransport is the transport layer. it is responsible for sending messages.
+ *
+ * @author Khaled Baati 28/10/2014
+ * @version 1.1
+ */
+public class PSGTransport implements Transport {
+
+ private static double computeDuration = 0;
+ private PSGTask task;
+ private static Map<PSGTask, String> taskToSend = new LinkedHashMap<PSGTask, String>();
+
+ /**
+ * String name of the parameter used to configure the minimum latency. * @config
+ */
+ private static final String PAR_MINDELAY = "mindelay";
+
+ /**
+ * String name of the parameter used to configure the maximum latency.
+ * Defaults to {@value #PAR_MINDELAY}, which results in a constant delay.
+ *
+ * @config
+ */
+ private static final String PAR_MAXDELAY = "maxdelay";
+
+ /** Minimum delay for message sending */
+ private long min;
+ /** Maximum delay for message sending */
+ private long max;
+
+ /**
+ * Difference between the max and min delay plus one. That is, max delay is
+ * min+range-1.
+ */
+ private long range;
+
+ public PSGTransport() {
+
+ }
+
+ public PSGTransport(String prefix) {
+ min = Configuration.getLong(prefix + "." + PAR_MINDELAY);
+ max = Configuration.getLong(prefix + "." + PAR_MAXDELAY, min);
+ if (max < min)
+ throw new IllegalParameterException(prefix + "." + PAR_MAXDELAY,
+ "The maximum latency cannot be smaller than the minimum latency");
+ range = max - min + 1;
+ }
+
+ /**
+ * Returns <code>this</code>. This way only one instance exists in the
+ * system that is linked from all the nodes. This is because this protocol
+ * has no node specific state.
+ */
+ public Object clone() {
+ return this;
+ }
+
+ @Override
+ public void send(Node src, Node dest, Object msg, int pid) {
+ double commSizeLat = 0;
+ /**
+ * random instruction associated to UniformRandomTransport.send(...)
+ * method in peersim.transport
+ **/
+ long delay = (range == 1 ? min : min + CommonState.r.nextLong(range));
+ CommonState.r.nextInt(1 << 8);
+ if (msg instanceof Sizable) {
+ commSizeLat = ((Sizable) msg).getSize();
+ }
+
+ task = new PSGTask("task sender_" + src.getID(), computeDuration,
+ commSizeLat, msg, pid);
+ taskToSend.put(this.task, NodeHost.getHost(dest).getName());
+
+ }
+
+ /**
+ * Process for sending all messages in the queue.
+ */
+ public static void flush() {
+ Map<PSGTask, String> taskToSendCopy = new LinkedHashMap<PSGTask, String>();
+ for (Map.Entry<PSGTask, String> entry : taskToSend.entrySet()) {
+ taskToSendCopy.put(entry.getKey(), entry.getValue());
+ }
+ taskToSend.clear();
+ for (Map.Entry<PSGTask, String> entry : taskToSendCopy.entrySet()) {
+ PSGTask task = entry.getKey();
+ String dest = entry.getValue();
+ task.dsend(dest);
+ }
+ taskToSendCopy.clear();
+
+ }
+
+ @Override
+ public long getLatency(Node src, Node dest) {
+ /**
+ * random instruction associated to
+ * UniformRandomTransport.getLatency(...) method in peersim.transport
+ **/
+ return (range == 1 ? min : min + CommonState.r.nextLong(range));
+ }
+}
--- /dev/null
+package psgsim;
+
+/**
+ * An interface which defines a size for the message or the event to be sent. If you
+ * wish that your message has a size, your class message must implements this
+ * interface and defines a size parameter in the constructor.
+ *
+ * @author Khaled Baati 05/02/2015
+ * @version 1.1
+ *
+ */
+public interface Sizable {
+ /**
+ *
+ * @return The size of the message
+ */
+ public double getSize();
+}
--- /dev/null
+#!/bin/bash
+
+if [ $(uname -m) = "i686" ]; then
+ eval ulimit -s 64
+else
+ eval ulimit -s 128
+fi
+
+echo -e "\n";
+echo '------------- Execute the edaggregation example under PSG -------------';
+echo -e "\n";
+java -Xmx1024m -cp lib.jar:classes peersim.Simulator configs/edaggregationPSG.txt
+echo -e "\n";
+echo '------------- Execute the edaggregation example under PS -------------';
+echo -e "\n";
+java -Xmx1024m -cp lib.jar:classes peersim.Simulator configs/edaggregation.txt
+echo -e "\n";
+echo '------------- Execute the chord example under PSG -------------';
+echo -e "\n";
+java -Xmx1024m -cp lib.jar:classes peersim.Simulator configs/chordPSG.txt
+echo -e "\n";
+echo '------------- Execute the chord example under PS -------------';
+echo -e "\n";
+java -Xmx1024m -cp lib.jar:classes peersim.Simulator configs/chord.txt
+echo -e "\n";
+echo '------------- Compare the 2 results PS and PSG -------------';
+echo -e "\n";
+
+cd outputs
+
+ListeRep="$(find * -type d -prune)" # liste des repertoires
+for Rep in ${ListeRep}; do
+ cd $Rep
+ VAR=$(diff ps.txt psg.txt)
+ if [ "${VAR}"1 = 1 ]
+ then
+ echo The results of diff "for" the $Rep example is '.............:)';
+ else
+ echo The results of diff "for" the $Rep example is '.............:(';
+ fi
+ cd ..
+done
+echo -e "\n";
+exit 0
+