From: kbaati Date: Fri, 24 Jul 2015 10:24:03 +0000 (+0200) Subject: peersimgrid release 1.0 X-Git-Tag: v3_12~438^2~7 X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/commitdiff_plain/200986a368bbbbb5df459d43cbc7f5ef3d7678db peersimgrid release 1.0 --- diff --git a/contrib/psg/README.txt b/contrib/psg/README.txt new file mode 100644 index 0000000000..44359907fa --- /dev/null +++ b/contrib/psg/README.txt @@ -0,0 +1,48 @@ +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 diff --git a/contrib/psg/configs/bittorrent.txt b/contrib/psg/configs/bittorrent.txt new file mode 100644 index 0000000000..356b3028df --- /dev/null +++ b/contrib/psg/configs/bittorrent.txt @@ -0,0 +1,46 @@ +#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 diff --git a/contrib/psg/configs/bittorrentPSG.txt b/contrib/psg/configs/bittorrentPSG.txt new file mode 100644 index 0000000000..647ccb4333 --- /dev/null +++ b/contrib/psg/configs/bittorrentPSG.txt @@ -0,0 +1,48 @@ +#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 diff --git a/contrib/psg/configs/chord.txt b/contrib/psg/configs/chord.txt new file mode 100644 index 0000000000..deeee8f4d5 --- /dev/null +++ b/contrib/psg/configs/chord.txt @@ -0,0 +1,51 @@ +# PEERSIM CHORD + +random.seed 1234567890 +simulation.endtime 10^4 +simulation.logtime 10^6 +OutputName chord +simulation.experiments 1 + +network.size 40 +protocol.tr UniformRandomTransport +{ + mindelay 0 + maxdelay 0 +} + +protocol.chord example.chord.ChordProtocol +{ + transport tr +} + +control.traffic example.chord.TrafficGenerator +{ + protocol chord + step 100 +} + +init.create example.chord.CreateNw +{ + protocol chord + idLength 128 + succListSize 12 +} + +control.observer example.chord.MessageCounterObserver +{ + protocol chord + step 90000 +} + +#control.dnet DynamicNetwork +#{ +# #add 2 +# add -2 +# minsize 18#3000 +# maxsize 60#7000 +# step 100000 +# init.0 example.chord.ChordInitializer +# { +# protocol chord +# } +#} \ No newline at end of file diff --git a/contrib/psg/configs/chordPSG.txt b/contrib/psg/configs/chordPSG.txt new file mode 100644 index 0000000000..7e670474b5 --- /dev/null +++ b/contrib/psg/configs/chordPSG.txt @@ -0,0 +1,54 @@ +# PEERSIM CHORD + +random.seed 1234567890 +simulation.duration 10^4 +simulation.logtime 10^6 +unit sec +OutputName chord +platform platforms/psg.xml +simulation.experiments 1 + +network.size 40 +protocol.tr psgsim.PSGTransport +{ + mindelay 0 + maxdelay 0 +} + +protocol.chord example.chord.ChordProtocol +{ + transport tr +} + +control.traffic example.chord.TrafficGenerator +{ + protocol chord + step 100 +} + +init.create example.chord.CreateNw +{ + protocol chord + idLength 128 + succListSize 12 +} + +control.observer example.chord.MessageCounterObserver +{ + protocol chord + step 90000 +} + + +#control.dnet DynamicNetwork +#{ +# #add 2 +# add -2 +# minsize 18#3000 +# maxsize 60#7000 +# step 100000 +# init.0 example.chord.ChordInitializer +# { +# protocol chord +# } +#} diff --git a/contrib/psg/configs/edaggregation.txt b/contrib/psg/configs/edaggregation.txt new file mode 100644 index 0000000000..d3aec1237b --- /dev/null +++ b/contrib/psg/configs/edaggregation.txt @@ -0,0 +1,57 @@ +# 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 diff --git a/contrib/psg/configs/edaggregationPSG.txt b/contrib/psg/configs/edaggregationPSG.txt new file mode 100644 index 0000000000..8510e90544 --- /dev/null +++ b/contrib/psg/configs/edaggregationPSG.txt @@ -0,0 +1,57 @@ +# 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 diff --git a/contrib/psg/configs/symphony.txt b/contrib/psg/configs/symphony.txt new file mode 100644 index 0000000000..fb5c778f0a --- /dev/null +++ b/contrib/psg/configs/symphony.txt @@ -0,0 +1,154 @@ +# :::::::::::::::::::::::::::::::::::::::::::::::::::::: +# :: Symphony Default Configuration +# :::::::::::::::::::::::::::::::::::::::::::::::::::::: + +# network size +SIZE 50 + +# parameters of periodic execution +CYCLES 100 +CYCLE SIZE/2 +OutputName symphony + +# 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 + +random.seed 1234567890 +network.size SIZE +simulation.experiments 1 +simulation.endtime 2000#CYCLE*CYCLES +simulation.logtime CYCLE + +################### transports =========================== + +protocol.tr UniformRandomTransport +{ + mindelay (CYCLE*MINDELAY)/100 + maxdelay (CYCLE*MAXDELAY)/100 +} + +################### protocols =========================== + +order.protocol link networkestimator symphony symphonynetworkmanager + +protocol.link peersim.core.IdleProtocol + +protocol.symphony example.symphony.SymphonyProtocol +{ + linkable link + transport tr + shortlink 4 + # if commented means: longlink log(n) + #longlink 4 + routing unidirectional + lookahead off +} + +#protocol.networkestimator example.symphony.SimpleNetworkSizeEstimatorProtocol + +protocol.networkestimator example.symphony.SymphonyEstimationProtocol +{ + symphony symphony + # if commented means: s log(n) + #s 3 +} + +protocol.symphonynetworkmanager example.symphony.SymphonyNetworkManager +{ + symphony symphony + transport tr + networkestimator networkestimator + attempts 3 + nTimeout 5 + relinking on + relinkingLowerBound 0.5 + relinkingUpperBound 2.0 + step 4*CYCLE #useless +} + +################### initialization ====================== + +order.init netbuild checknet + +init.netbuild example.symphony.SymphonyNetworkBuilder +{ + symphony symphony + createLongLinks true + attempts 5 +} + +init.checknet example.symphony.SymphonyNetworkChecker +{ + symphony symphony + networkestimator networkestimator +} + +################ control ============================== + +order.control sch checknet randomroutetest ringroutetest leavetest dnet estimationtest statistics + +control.randomroutetest example.symphony.RandomRouteTest +{ + symphony symphony + step CYCLE +} + +control.ringroutetest example.symphony.RingRouteTest +{ + symphony symphony + startnode 0 + step CYCLE +} + +control.sch CDScheduler +{ + protocol symphonynetworkmanager + step CYCLE*2 + randstart +} + +control.checknet example.symphony.SymphonyNetworkChecker +{ + symphony symphony + networkestimator networkestimator + step CYCLE +} + +control.dnet peersim.dynamics.DynamicNetwork +{ + add 0 + maxsize 50 + minsize SIZE/2 + step CYCLE*2 + init.0 example.symphony.SymphonyNodeInizializer + { + symphonynetworkmanager symphonynetworkmanager + symphony symphony + bootstrapnode 0 + } +} + +control.leavetest example.symphony.LeaveTest +{ + symphonynetworkmanager symphonynetworkmanager + n 1 + minsizeOnline SIZE-1 + waitTargetSizeToStart 2*SIZE + step CYCLE*2 +} + +control.statistics example.symphony.SymphonyStatistics +{ + symphony symphony + step (CYCLE*CYCLES)-1 +} + +control.estimationtest example.symphony.test.NetworkEstimationTest +{ + symphony symphony + symphonynetworkmanager symphonynetworkmanager + step CYCLE*4 +} \ No newline at end of file diff --git a/contrib/psg/configs/symphonyPSG.txt b/contrib/psg/configs/symphonyPSG.txt new file mode 100644 index 0000000000..20b7dcecd1 --- /dev/null +++ b/contrib/psg/configs/symphonyPSG.txt @@ -0,0 +1,155 @@ +# :::::::::::::::::::::::::::::::::::::::::::::::::::::: +# :: Symphony Default Configuration +# :::::::::::::::::::::::::::::::::::::::::::::::::::::: + +# network size +SIZE 50 +unit sec +# parameters of periodic execution +CYCLES 100 +CYCLE SIZE/2 +OutputName symphony +platform platforms/psg.xml + +# 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 + +random.seed 1234567890 +network.size SIZE +simulation.experiments 1 +simulation.duration 2000#CYCLE*CYCLES +simulation.logtime CYCLE + +################### transports =========================== + +protocol.tr psgsim.PSGTransport +{ + mindelay (CYCLE*MINDELAY)/100 + maxdelay (CYCLE*MAXDELAY)/100 +} + +################### protocols =========================== + +order.protocol link networkestimator symphony symphonynetworkmanager + +protocol.link peersim.core.IdleProtocol + +protocol.symphony example.symphony.SymphonyProtocol +{ + linkable link + transport tr + shortlink 4 + # if commented means: longlink log(n) + #longlink 4 + routing unidirectional + lookahead off +} + +#protocol.networkestimator example.symphony.SimpleNetworkSizeEstimatorProtocol + +protocol.networkestimator example.symphony.SymphonyEstimationProtocol +{ + symphony symphony + # if commented means: s log(n) + #s 3 +} + +protocol.symphonynetworkmanager example.symphony.SymphonyNetworkManager +{ + symphony symphony + transport tr + networkestimator networkestimator + attempts 3 + nTimeout 5 + relinking on + relinkingLowerBound 0.5 + relinkingUpperBound 2.0 + step 4*CYCLE #useless +} + +################### initialization ====================== + +order.init netbuild checknet + +init.netbuild example.symphony.SymphonyNetworkBuilder +{ + symphony symphony + createLongLinks true + attempts 5 +} + +init.checknet example.symphony.SymphonyNetworkChecker +{ + symphony symphony + networkestimator networkestimator +} + +################ control ============================== + +order.control sch checknet randomroutetest ringroutetest leavetest dnet estimationtest statistics + +control.randomroutetest example.symphony.RandomRouteTest +{ + symphony symphony + step CYCLE +} + +control.ringroutetest example.symphony.RingRouteTest +{ + symphony symphony + startnode 0 + step CYCLE +} + +control.sch CDScheduler +{ + protocol symphonynetworkmanager + step CYCLE*2 + randstart +} + +control.checknet example.symphony.SymphonyNetworkChecker +{ + symphony symphony + networkestimator networkestimator + step CYCLE +} + +control.dnet peersim.dynamics.DynamicNetwork +{ + add 0 + maxsize 50 + minsize SIZE/2 + step CYCLE*2 + init.0 example.symphony.SymphonyNodeInizializer + { + symphonynetworkmanager symphonynetworkmanager + symphony symphony + bootstrapnode 0 + } +} + +control.leavetest example.symphony.LeaveTest +{ + symphonynetworkmanager symphonynetworkmanager + n 1 + minsizeOnline SIZE-1 + waitTargetSizeToStart 2*SIZE + step CYCLE*2 +} + +control.statistics example.symphony.SymphonyStatistics +{ + symphony symphony + step (CYCLE*CYCLES)-1 +} + +control.estimationtest example.symphony.test.NetworkEstimationTest +{ + symphony symphony + symphonynetworkmanager symphonynetworkmanager + step CYCLE*4 +} diff --git a/contrib/psg/lib.jar b/contrib/psg/lib.jar new file mode 100644 index 0000000000..8fd65fa45f Binary files /dev/null and b/contrib/psg/lib.jar differ diff --git a/contrib/psg/platforms/psg.xml b/contrib/psg/platforms/psg.xml new file mode 100644 index 0000000000..bdd2e1956b --- /dev/null +++ b/contrib/psg/platforms/psg.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/contrib/psg/run.sh b/contrib/psg/run.sh new file mode 100755 index 0000000000..ecac88ca98 --- /dev/null +++ b/contrib/psg/run.sh @@ -0,0 +1,15 @@ +#!/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 + + + + diff --git a/contrib/psg/src/example/bittorrent/BTObserver.java b/contrib/psg/src/example/bittorrent/BTObserver.java new file mode 100644 index 0000000000..ddd38dab2b --- /dev/null +++ b/contrib/psg/src/example/bittorrent/BTObserver.java @@ -0,0 +1,132 @@ +/* + * 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; iTRACKER message. + * @config + */ + private static final String PAR_PEERSET_SIZE="peerset_size"; + /** + * Defines how much the network can grow with respect to the network.size + * 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 network.size + * 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 CHOKE events. + */ + int n_choke_time = 0; + + /** + * Used to send the TRACKER 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 INTERESTED 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 null, 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; iChecks the number of neighbors and if it is equal to 20 + * sends a TRACKER messages to the tracker, asking for a new + * peer set.

+ * + *

This method *must* be called after every call of {@link #removeNeighbor} + * in {@link #processEvent}. + *

+ */ + 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) + 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 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 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(;j0;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; i0 && (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; ivalue depending + * on part: 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 value. 0 means the piece index, 1 the block index. + * @return the piece or the block index depending about the value of part + */ + 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 completed 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 node 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 false 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=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 id 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 strict priority 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 INTERESTED 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 pendingRequest queue;
+ * the value of the next block to request otherwise

+ */ + 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; jcurrentPiece if there are still + * available places in the {@link #pendingRequest} queue;
+ * -2 if the pendingRequest queue is full;
+ * -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 + * random first and rarest first policy. For the beginning 4 pieces + * of a file the first one is used then the pieces are chosen using rarest first. + * @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(; jInsertionSort + * algorithm. + */ + public void sortByPeer(){ + int i; + + for(int j=1; j=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 QuickSort algorithm. + * null 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 + * QuickSort 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 QuickSort algorithm. + * @see "The QuickSort 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. Element x is greater than y if isn't null + * and in the last 20 seconds the local node has downloaded ("uploaded" if the local node is a + * seeder) more blocks than from y. + * @param x the first Element to compare. + * @param y the second Element to compare + * @return true if x > y;
+ * false 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} i with j in the {@link #byBandwidth}.
+ * 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; + /** + * true if the node is a seeder, false otherwise. + */ + public boolean isSeeder = false; + /** + * Makes a deep copy of the Element to destination + * @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
+ * Other values means the last piece number for which the node is interested. + */ + public int interested; + /** + * 0 means CHOKED
+ * 1 means UNCHOKED
+ * 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 size. + */ + 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 id and its sender + * @param id the id of the block in the request + * @param sender a reference to the sender of the request + * @return true if the request has been correctly added, false + * 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.
+ * null 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 true if the queue is empty, false + * otherwise. + */ + public boolean empty(){ + return (dim == 0); + } + + /** + * Returns true if block given as parameter is in. + * @param value the id of the block to search. + * @return true if the block value is in the queue, false + * otherwise. + */ + public boolean contains(int value){ + if(empty()) + return false; + for(int i=head; itrue if the request has been correctly removed, false + * otherwise. + */ + public boolean remove(Node sender, int value){ + if(empty()) + return false; + for(int i=head; ihead; 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 diff --git a/contrib/psg/src/example/bittorrent/BitfieldMsg.java b/contrib/psg/src/example/bittorrent/BitfieldMsg.java new file mode 100644 index 0000000000..d459482de0 --- /dev/null +++ b/contrib/psg/src/example/bittorrent/BitfieldMsg.java @@ -0,0 +1,87 @@ +/* + * 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 bitfield + * 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 isRequest is true, then + * the message is a request of subscription; otherwise the message is a response. + */ + boolean isRequest; + + /** + *

The ACK value used to implement ack and nack messages.

+ *

It has value true if the message is a reponse and the sender has inserted + * the receiver in its own cache of neighbors.
+ * 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 false.
+ * It has value false also if the message is a request and is sent when occours + * an unespected message. + *

+ * @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 ack or a nack + * @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 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 diff --git a/contrib/psg/src/example/bittorrent/NetworkInitializer.java b/contrib/psg/src/example/bittorrent/NetworkInitializer.java new file mode 100644 index 0000000000..4b5ba72e5f --- /dev/null +++ b/contrib/psg/src/example/bittorrent/NetworkInitializer.java @@ -0,0 +1,105 @@ +/* + * 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; in 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. + *

+ * The allowed bandwidth speed are 640 Kbps, 1 Mbps, 2 Mbps and 4 Mbps. + *

+ * @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 p. + * @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 newer_distr + * 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 diff --git a/contrib/psg/src/example/bittorrent/PeerSetMsg.java b/contrib/psg/src/example/bittorrent/PeerSetMsg.java new file mode 100644 index 0000000000..d0e92d56ff --- /dev/null +++ b/contrib/psg/src/example/bittorrent/PeerSetMsg.java @@ -0,0 +1,58 @@ +/* + * 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 peerset + * 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 peerset 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; + } +} diff --git a/contrib/psg/src/example/bittorrent/SimpleEvent.java b/contrib/psg/src/example/bittorrent/SimpleEvent.java new file mode 100644 index 0000000000..1da537ac00 --- /dev/null +++ b/contrib/psg/src/example/bittorrent/SimpleEvent.java @@ -0,0 +1,79 @@ +/* + * 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. + *

+ * The available identifiers for event type are:
+ *

    + *
  • 1 is KEEP_ALIVE message
  • + *
  • 2 is CHOKE message
  • + *
  • 3 is UNCHOKE message
  • + *
  • 4 is INTERESTED message
  • + *
  • 5 is NOT_INTERESTED message
  • + *
  • 6 is HAVE message
  • + *
  • 7 is BITFIELD message
  • + *
  • 8 is REQUEST message
  • + *
  • 9 is PIECE message
  • + *
  • 10 is CANCEL message
  • + *
  • 11 is TRACKER message
  • + *
  • 12 is PEERSET message
  • + *
  • 13 is CHOKE_TIME event
  • + *
  • 14 is OPTUNCHK_TIME event
  • + *
  • 15 is ANTISNUB_TIME event
  • + *
  • 16 is CHECKALIVE_TIME event
  • + *
  • 17 is TRACKERALIVE_TIME event
  • + *
  • 18 is DOWNLOAD_COMPLETED event
  • + *

+ */ + 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; + } +} + + diff --git a/contrib/psg/src/example/bittorrent/SimpleMsg.java b/contrib/psg/src/example/bittorrent/SimpleMsg.java new file mode 100644 index 0000000000..cf9436ec67 --- /dev/null +++ b/contrib/psg/src/example/bittorrent/SimpleMsg.java @@ -0,0 +1,60 @@ +/* + * 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; + } +} diff --git a/contrib/psg/src/example/chord/ChordInitializer.java b/contrib/psg/src/example/chord/ChordInitializer.java new file mode 100644 index 0000000000..799b0d707b --- /dev/null +++ b/contrib/psg/src/example/chord/ChordInitializer.java @@ -0,0 +1,72 @@ +package example.chord; + +import java.math.BigInteger; +import java.util.Random; +import peersim.config.Configuration; +import peersim.core.CommonState; +import peersim.core.Network; +import peersim.core.Node; +import peersim.dynamics.NodeInitializer; + +public class ChordInitializer implements NodeInitializer { + + private static final String PAR_PROT = "protocol"; + + private int pid = 0; + + private ChordProtocol cp; + + public ChordInitializer(String prefix) { + pid = Configuration.getPid(prefix + "." + PAR_PROT); + } + + public void initialize(Node n) { + cp = (ChordProtocol) n.getProtocol(pid); + join(n); + } + + public void join(Node myNode) { + Random generator = new Random(); + //Random generator = new Random(1234567890); + cp.predecessor = null; + // search a node to join + Node n; + do { + n = Network.get(generator.nextInt(Network.size())); + } while (n == null || n.isUp() == false); + cp.m = ((ChordProtocol) n.getProtocol(pid)).m; + cp.chordId = new BigInteger(cp.m, CommonState.r); + ChordProtocol cpRemote = (ChordProtocol) n.getProtocol(pid); + + Node successor = cpRemote.find_successor(cp.chordId); + cp.fails = 0; + cp.stabilizations = 0; + cp.varSuccList = cpRemote.varSuccList; + cp.varSuccList = 0; + cp.succLSize = cpRemote.succLSize; + cp.successorList = new Node[cp.succLSize]; + cp.successorList[0] = successor; + cp.fingerTable = new Node[cp.m]; + long succId = 0; + BigInteger lastId = ((ChordProtocol) Network.get(Network.size() - 1) + .getProtocol(pid)).chordId; + do { + cp.stabilizations++; + succId = cp.successorList[0].getID(); + cp.stabilize(myNode); + if (((ChordProtocol) cp.successorList[0].getProtocol(pid)).chordId + .compareTo(cp.chordId) < 0) { + cp.successorList[0] = ((ChordProtocol) cp.successorList[0] + .getProtocol(pid)).find_successor(cp.chordId); + } + // controllo di non essere l'ultimo elemento della rete + if (cp.chordId.compareTo(lastId) > 0) { + cp.successorList[0] = Network.get(0); + break; + } + } while (cp.successorList[0].getID() != succId + || ((ChordProtocol) cp.successorList[0].getProtocol(pid)).chordId + .compareTo(cp.chordId) < 0); + cp.fixFingers(); + } +} diff --git a/contrib/psg/src/example/chord/ChordMessage.java b/contrib/psg/src/example/chord/ChordMessage.java new file mode 100644 index 0000000000..f87fafbb72 --- /dev/null +++ b/contrib/psg/src/example/chord/ChordMessage.java @@ -0,0 +1,12 @@ +/** + * + */ +package example.chord; + +/** + * @author Andrea + * + */ +public interface ChordMessage { + +} diff --git a/contrib/psg/src/example/chord/ChordProtocol.java b/contrib/psg/src/example/chord/ChordProtocol.java new file mode 100644 index 0000000000..485c3ba214 --- /dev/null +++ b/contrib/psg/src/example/chord/ChordProtocol.java @@ -0,0 +1,358 @@ +/** + * + */ +package example.chord; + +import peersim.config.Configuration; +import peersim.core.CommonState; +import peersim.core.Network; +import peersim.core.Node; +import peersim.edsim.EDProtocol; +import peersim.transport.Transport; + +import java.math.*; + +import org.simgrid.msg.Host; +import org.simgrid.msg.Msg; + +/** + * @author Andrea + * + */ +public class ChordProtocol implements EDProtocol { + + private static final String PAR_TRANSPORT = "transport"; + + private Parameters p; + + private int[] lookupMessage; + + public int index = 0; + + public Node predecessor; + + public Node[] fingerTable; + + public Node[] successorList; + + public BigInteger chordId; + + public int m; + + public int succLSize; + + public String prefix; + + private int next = 0; + + // campo x debug + private int currentNode = 0; + + public int varSuccList = 0; + + public int stabilizations = 0; + + public int fails = 0; + + /** + * + */ + public ChordProtocol(String prefix) { + this.prefix = prefix; + lookupMessage = new int[1]; + lookupMessage[0] = 0; + p = new Parameters(); + p.tid = Configuration.getPid(prefix + "." + PAR_TRANSPORT); + } + + /* + * (non-Javadoc) + * + * @see peersim.edsim.EDProtocol#processEvent(peersim.core.Node, int, + * java.lang.Object) + */ + public void processEvent(Node node, int pid, Object event) { + // processare le richieste a seconda della routing table del nodo + p.pid = pid; + // currentNode = node.getIndex(); + currentNode = (int) node.getID(); + if (event.getClass() == LookUpMessage.class) { + LookUpMessage message = (LookUpMessage) event; + message.increaseHopCounter(); + BigInteger target = message.getTarget(); + Transport t = (Transport) node.getProtocol(p.tid); + Node n = message.getSender(); + System.out.println("R process " + "at time=" + + CommonState.getTime() + " to dest:" + currentNode + + " from src:" + n.getID() + " message: (" + + message.getSender().getID() + ";" + message.getTarget() + + ")"); + if (target == ((ChordProtocol) node.getProtocol(pid)).chordId) { + // mandare mess di tipo final + Object msg = new FinalMessage(message.getHopCounter()); + System.out.println("S Final Message " + "at time=" + + CommonState.getTime() + " from src:" + node.getID() + + " to dest:" + n.getID() + " message: " + + message.getHopCounter() + " HopCounter"); + t.send(node, n, msg, pid); + + } + if (target != ((ChordProtocol) node.getProtocol(pid)).chordId) { + // funzione lookup sulla fingertabable + Node dest = find_successor(target); + if (dest.isUp() == false) { + do { + varSuccList = 0; + stabilize(node); + stabilizations++; + fixFingers(); + dest = find_successor(target); + } while (dest.isUp() == false); + } + if (dest.getID() == successorList[0].getID() + && (target.compareTo(((ChordProtocol) dest + .getProtocol(p.pid)).chordId) < 0)) { + fails++; + } else { + System.out.println("S process " + "at time=" + + CommonState.getTime() + " from src:" + + node.getID() + " to dest:" + dest.getID() + + " message: (" + message.getSender().getID() + ";" + + message.getTarget() + ")"); + // t.send(message.getSender(), dest, message, pid); + t.send(node, dest, message, pid); + + } + } + } + if (event.getClass() == FinalMessage.class) { + FinalMessage message = (FinalMessage) event; + System.out.println("R Final Message " + "at time=" + + CommonState.getTime() + " to dest:" + node.getID()+"\n"); + lookupMessage = new int[index + 1]; + lookupMessage[index] = message.getHopCounter(); + index++; + } + } + + public Object clone() { + ChordProtocol cp = new ChordProtocol(prefix); + String val = BigInteger.ZERO.toString(); + cp.chordId = new BigInteger(val); + cp.fingerTable = new Node[m]; + cp.successorList = new Node[succLSize]; + cp.currentNode = 0; + return cp; + } + + public int[] getLookupMessage() { + return lookupMessage; + } + + public void stabilize(Node myNode) { + try { + Node node = ((ChordProtocol) successorList[0].getProtocol(p.pid)).predecessor; + if (node != null) { + if (this.chordId == ((ChordProtocol) node.getProtocol(p.pid)).chordId) + return; + BigInteger remoteID = ((ChordProtocol) node.getProtocol(p.pid)).chordId; + if (idInab( + remoteID, + chordId, + ((ChordProtocol) successorList[0].getProtocol(p.pid)).chordId)) + successorList[0] = node; + ((ChordProtocol) successorList[0].getProtocol(p.pid)) + .notify(myNode); + } + updateSuccessorList(); + } catch (Exception e1) { + e1.printStackTrace(); + updateSuccessor(); + } + } + + private void updateSuccessorList() throws Exception { + try { + while (successorList[0] == null || successorList[0].isUp() == false) { + updateSuccessor(); + } + System.arraycopy( + ((ChordProtocol) successorList[0].getProtocol(p.pid)).successorList, + 0, successorList, 1, succLSize - 2); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void notify(Node node) throws Exception { + BigInteger nodeId = ((ChordProtocol) node.getProtocol(p.pid)).chordId; + if ((predecessor == null) + || (idInab( + nodeId, + ((ChordProtocol) predecessor.getProtocol(p.pid)).chordId, + this.chordId))) { + predecessor = node; + } + } + + private void updateSuccessor() { + boolean searching = true; + while (searching) { + try { + Node node = successorList[varSuccList]; + varSuccList++; + successorList[0] = node; + if (successorList[0] == null + || successorList[0].isUp() == false) { + if (varSuccList >= succLSize - 1) { + searching = false; + varSuccList = 0; + } else + updateSuccessor(); + } + updateSuccessorList(); + searching = false; + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private boolean idInab(BigInteger id, BigInteger a, BigInteger b) { + if ((a.compareTo(id) == -1) && (id.compareTo(b) == -1)) { + return true; + } + return false; + } + + public Node find_successor(BigInteger id) { + try { + if (successorList[0] == null || successorList[0].isUp() == false) { + updateSuccessor(); + } + if (idInab( + id, + this.chordId, + ((ChordProtocol) successorList[0].getProtocol(p.pid)).chordId)) { + return successorList[0]; + } else { + Node tmp = closest_preceding_node(id); + return tmp; + } + } catch (Exception e) { + e.printStackTrace(); + } + return successorList[0]; + } + + private Node closest_preceding_node(BigInteger id) { + for (int i = m; i > 0; i--) { + try { + if (fingerTable[i - 1] == null + || fingerTable[i - 1].isUp() == false) { + continue; + } + BigInteger fingerId = ((ChordProtocol) (fingerTable[i - 1] + .getProtocol(p.pid))).chordId; + if ((idInab(fingerId, this.chordId, id)) + || (id.compareTo(fingerId) == 0)) { + return fingerTable[i - 1]; + } + if (fingerId.compareTo(this.chordId) == -1) { + // sono nel caso in cui ho fatto un giro della rete + // circolare + if (idInab(id, fingerId, this.chordId)) { + return fingerTable[i - 1]; + } + } + if ((id.compareTo(fingerId) == -1) + && (id.compareTo(this.chordId) == -1)) { + if (i == 1) + return successorList[0]; + BigInteger lowId = ((ChordProtocol) fingerTable[i - 2] + .getProtocol(p.pid)).chordId; + if (idInab(id, lowId, fingerId)) + return fingerTable[i - 2]; + else if (fingerId.compareTo(this.chordId) == -1) + continue; + else if (fingerId.compareTo(this.chordId) == 1) + return fingerTable[i - 1]; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + if (fingerTable[m - 1] == null) + return successorList[0]; + return successorList[0]; + } + + // debug function + private void printFingers() { + for (int i = fingerTable.length - 1; i > 0; i--) { + if (fingerTable[i] == null) { + System.out.println("Finger " + i + " is null"); + continue; + } + if ((((ChordProtocol) fingerTable[i].getProtocol(p.pid)).chordId) + .compareTo(this.chordId) == 0) + break; + System.out + .println("Finger[" + + i + + "] = " + + fingerTable[i].getIndex() + + " chordId " + + ((ChordProtocol) fingerTable[i] + .getProtocol(p.pid)).chordId); + } + } + + public void fixFingers() { + if (next >= m - 1) + next = 0; + if (fingerTable[next] != null && fingerTable[next].isUp()) { + next++; + return; + } + BigInteger base; + if (next == 0) + base = BigInteger.ONE; + else { + base = BigInteger.valueOf(2); + for (int exp = 1; exp < next; exp++) { + base = base.multiply(BigInteger.valueOf(2)); + } + } + BigInteger pot = this.chordId.add(base); + BigInteger idFirst = ((ChordProtocol) Network.get(0).getProtocol(p.pid)).chordId; + BigInteger idLast = ((ChordProtocol) Network.get(Network.size() - 1) + .getProtocol(p.pid)).chordId; + if (pot.compareTo(idLast) == 1) { + pot = (pot.mod(idLast)); + if (pot.compareTo(this.chordId) != -1) { + next++; + return; + } + if (pot.compareTo(idFirst) == -1) { + this.fingerTable[next] = Network.get(Network.size() - 1); + next++; + return; + } + } + do { + fingerTable[next] = ((ChordProtocol) successorList[0] + .getProtocol(p.pid)).find_successor(pot); + pot = pot.subtract(BigInteger.ONE); + ((ChordProtocol) successorList[0].getProtocol(p.pid)).fixFingers(); + } while (fingerTable[next] == null || fingerTable[next].isUp() == false); + next++; + } + + /** + */ + public void emptyLookupMessage() { + index = 0; + this.lookupMessage = new int[0]; + } +} diff --git a/contrib/psg/src/example/chord/CreateNw.java b/contrib/psg/src/example/chord/CreateNw.java new file mode 100644 index 0000000000..9268653533 --- /dev/null +++ b/contrib/psg/src/example/chord/CreateNw.java @@ -0,0 +1,148 @@ +/** + * + */ +package example.chord; + +import peersim.core.*; +import peersim.config.Configuration; +import java.math.*; + +/** + * @author Andrea + * + */ +public class CreateNw implements Control { + + private int pid = 0; + + private static final String PAR_IDLENGTH = "idLength"; + + private static final String PAR_PROT = "protocol"; + + private static final String PAR_SUCCSIZE = "succListSize"; + + int idLength = 0; + + int successorLsize = 0; + + int fingSize = 0; + //campo x debug + boolean verbose = false; + + /** + * + */ + public CreateNw(String prefix) { + pid = Configuration.getPid(prefix + "." + PAR_PROT); + idLength = Configuration.getInt(prefix + "." + PAR_IDLENGTH); + successorLsize = Configuration.getInt(prefix + "." + PAR_SUCCSIZE); + } + + /* + * (non-Javadoc) + * + * @see peersim.core.Control#execute() + */ + + public boolean execute() { + for (int i = 0; i < Network.size(); i++) { + Node node = (Node) Network.get(i); + ChordProtocol cp = (ChordProtocol) node.getProtocol(pid); + cp.m = idLength; + cp.succLSize = successorLsize; + cp.varSuccList = 0; + cp.chordId = new BigInteger(idLength, CommonState.r); + cp.fingerTable = new Node[idLength]; + cp.successorList = new Node[successorLsize]; + } + NodeComparator nc = new NodeComparator(pid); + Network.sort(nc); + createFingerTable(); + return false; + } + + public Node findId(BigInteger id, int nodeOne, int nodeTwo) { + if (nodeOne >= (nodeTwo - 1)) + return Network.get(nodeOne); + int middle = (nodeOne + nodeTwo) / 2; + if (((middle) >= Network.size() - 1)) + System.out.print("ERROR: Middle is bigger than Network.size"); + if (((middle) <= 0)) + return Network.get(0); + try { + BigInteger newId = ((ChordProtocol) ((Node) Network.get(middle)) + .getProtocol(pid)).chordId; + BigInteger lowId; + if (middle > 0) + lowId = ((ChordProtocol) ((Node) Network.get(middle - 1)) + .getProtocol(pid)).chordId; + else + lowId = newId; + BigInteger highId = ((ChordProtocol) ((Node) Network + .get(middle + 1)).getProtocol(pid)).chordId; + if (id.compareTo(newId) == 0 + || ((id.compareTo(newId) == 1) && (id.compareTo(highId) == -1))) { + return Network.get(middle); + } + if ((id.compareTo(newId) == -1) && (id.compareTo(lowId) == 1)) { + if (middle > 0) + return Network.get(middle - 1); + else + return Network.get(0); + } + if (id.compareTo(newId) == -1) { + return findId(id, nodeOne, middle); + } else if (id.compareTo(newId) == 1) { + return findId(id, middle, nodeTwo); + } + return null; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public void createFingerTable() { + BigInteger idFirst = ((ChordProtocol) Network.get(0).getProtocol(pid)).chordId; + BigInteger idLast = ((ChordProtocol) Network.get(Network.size() - 1) + .getProtocol(pid)).chordId; + for (int i = 0; i < Network.size(); i++) { + Node node = (Node) Network.get(i); + ChordProtocol cp = (ChordProtocol) node.getProtocol(pid); + for (int a = 0; a < successorLsize; a++) { + if (a + i < (Network.size() - 1)) + cp.successorList[a] = Network.get(a + i + 1); + else + cp.successorList[a] = Network.get(a); + } + if (i > 0) + cp.predecessor = (Node) Network.get(i - 1); + else + cp.predecessor = (Node) Network.get(Network.size() - 1); + int j = 0; + for (j = 0; j < idLength; j++) { + BigInteger base; + if (j == 0) + base = BigInteger.ONE; + else { + base = BigInteger.valueOf(2); + for (int exp = 1; exp < j; exp++) { + base = base.multiply(BigInteger.valueOf(2)); + } + } + BigInteger pot = cp.chordId.add(base); + if (pot.compareTo(idLast) == 1) { + pot = (pot.mod(idLast)); + if (pot.compareTo(cp.chordId) != -1) { + break; + } + if (pot.compareTo(idFirst) == -1) { + cp.fingerTable[j] = Network.get(Network.size() - 1); + continue; + } + } + cp.fingerTable[j] = findId(pot, 0, Network.size() - 1); + } + } + } +} diff --git a/contrib/psg/src/example/chord/FinalMessage.java b/contrib/psg/src/example/chord/FinalMessage.java new file mode 100644 index 0000000000..847dba9b46 --- /dev/null +++ b/contrib/psg/src/example/chord/FinalMessage.java @@ -0,0 +1,14 @@ +package example.chord; + +public class FinalMessage implements ChordMessage { + + private int hopCounter = 0; + + public FinalMessage(int hopCounter) { + this.hopCounter = hopCounter; + } + + public int getHopCounter() { + return hopCounter; + } +} diff --git a/contrib/psg/src/example/chord/LookUpMessage.java b/contrib/psg/src/example/chord/LookUpMessage.java new file mode 100644 index 0000000000..091089e625 --- /dev/null +++ b/contrib/psg/src/example/chord/LookUpMessage.java @@ -0,0 +1,44 @@ +package example.chord; + +import java.math.*; +import peersim.core.*; + +public class LookUpMessage implements ChordMessage { + + private Node sender; + + private BigInteger targetId; + + private int hopCounter = -1; + + public LookUpMessage(Node sender, BigInteger targetId) { + this.sender = sender; + this.targetId = targetId; + } + + public void increaseHopCounter() { + hopCounter++; + } + + /** + * @return the senderId + */ + public Node getSender() { + return sender; + } + + /** + * @return the target + */ + public BigInteger getTarget() { + return targetId; + } + + /** + * @return the hopCounter + */ + public int getHopCounter() { + return hopCounter; + } + +} diff --git a/contrib/psg/src/example/chord/MessageCounterObserver.java b/contrib/psg/src/example/chord/MessageCounterObserver.java new file mode 100644 index 0000000000..dbcbb8f06a --- /dev/null +++ b/contrib/psg/src/example/chord/MessageCounterObserver.java @@ -0,0 +1,108 @@ +/** + * + */ +package example.chord; + +import java.util.ArrayList; +import peersim.core.Control; +import peersim.core.Network; +import peersim.config.Configuration; + +/** + * @author Andrea + * + */ +public class MessageCounterObserver implements Control { + + private static final String PAR_PROT = "protocol"; + + private final String prefix; + + private final int pid; + + /** + * + */ + public MessageCounterObserver(String prefix) { + this.prefix = prefix; + this.pid = Configuration.getPid(prefix + "." + PAR_PROT); + } + + /* + * (non-Javadoc) + * + * @see peersim.core.Control#execute() + */ + public boolean execute() { + int size = Network.size(); + int totalStab = 0; + int totFails = 0; + ArrayList hopCounters = new ArrayList(); // struttura dati che + // memorizza gli hop di + // tutti i mess mandati + hopCounters.clear(); + int max = 0; + int min = Integer.MAX_VALUE; + for (int i = 0; i < size; i++) { + ChordProtocol cp = (ChordProtocol) Network.get(i).getProtocol(pid); + // trovare tutti gli hopCOunter dei messaggi lookup mandati + int[] counters = new int[cp.getLookupMessage().length]; + System.arraycopy(cp.getLookupMessage(), 0, counters, 0, cp + .getLookupMessage().length); + totalStab = totalStab + cp.stabilizations; + totFails = totFails + cp.fails; + cp.stabilizations = 0; + cp.fails = 0; + int maxNew = maxArray(counters, cp.index); + if (maxNew > max) + max = maxNew; + if (cp.index != 0) { + for (int j = 0; j < cp.index; j++) + hopCounters.add(counters[j]); + int minNew = minArray(counters, cp.index); + if (minNew < min) + min = minNew; + } + cp.emptyLookupMessage(); + } + double media = meanCalculator(hopCounters); + if (media > 0) + System.out.println("Mean: " + media + " Max Value: " + max + + " Min Value: " + min + " # Observations: " + + hopCounters.size()); + System.out.println(" # Stabilizations: " + totalStab + " # Failures: " + + totFails); + System.out.println(""); + return false; + } + + private double meanCalculator(ArrayList list) { + int lenght = list.size(); + if (lenght == 0) + return 0; + int sum = 0; + for (int i = 0; i < lenght; i++) { + sum = sum + ((Integer) list.get(i)).intValue(); + } + double mean = sum / lenght; + return mean; + } + + private int maxArray(int[] array, int dim) { + int max = 0; + for (int j = 0; j < dim; j++) { + if (array[j] > max) + max = array[j]; + } + return max; + } + + private int minArray(int[] array, int dim) { + int min = 0; + for (int j = 0; j < dim; j++) { + if (array[j] < min) + min = array[j]; + } + return min; + } +} diff --git a/contrib/psg/src/example/chord/NodeComparator.java b/contrib/psg/src/example/chord/NodeComparator.java new file mode 100644 index 0000000000..def16dccd8 --- /dev/null +++ b/contrib/psg/src/example/chord/NodeComparator.java @@ -0,0 +1,21 @@ +package example.chord; + +import java.util.Comparator; +import java.math.*; +import peersim.core.*; + +public class NodeComparator implements Comparator { + + public int pid = 0; + + public NodeComparator(int pid) { + this.pid = pid; + } + + public int compare(Object arg0, Object arg1) { + BigInteger one = ((ChordProtocol) ((Node) arg0).getProtocol(pid)).chordId; + BigInteger two = ((ChordProtocol) ((Node) arg1).getProtocol(pid)).chordId; + return one.compareTo(two); + } + +} diff --git a/contrib/psg/src/example/chord/Parameters.java b/contrib/psg/src/example/chord/Parameters.java new file mode 100644 index 0000000000..d32d82f791 --- /dev/null +++ b/contrib/psg/src/example/chord/Parameters.java @@ -0,0 +1,7 @@ +package example.chord; + +public class Parameters { + int pid; + + int tid; +} diff --git a/contrib/psg/src/example/chord/TrafficGenerator.java b/contrib/psg/src/example/chord/TrafficGenerator.java new file mode 100644 index 0000000000..09c5854bff --- /dev/null +++ b/contrib/psg/src/example/chord/TrafficGenerator.java @@ -0,0 +1,54 @@ +/** + * + */ +package example.chord; + +import org.simgrid.msg.Host; + +import peersim.core.*; +import peersim.config.Configuration; +import peersim.edsim.EDSimulator; +import psgsim.PSGSimulator; + +/** + * @author Andrea + * + */ +public class TrafficGenerator implements Control { + + private static final String PAR_PROT = "protocol"; + + private final int pid; + + /** + * + */ + public TrafficGenerator(String prefix) { + pid = Configuration.getPid(prefix + "." + PAR_PROT); + } + + /* + * (non-Javadoc) + * + * @see peersim.core.Control#execute() + */ + public boolean execute() { + int size = Network.size(); + Node sender, target; + int i = 0; + do { + i++; + sender = Network.get(CommonState.r.nextInt(size)); + target = Network.get(CommonState.r.nextInt(size)); + } while (sender == null || sender.isUp() == false || target == null + || target.isUp() == false); + LookUpMessage message = new LookUpMessage(sender, + ((ChordProtocol) target.getProtocol(pid)).chordId); + System.out.println("TrafficGenerator at time "+CommonState.getTime()+" Node:" + + message.getSender().getID() +" target "+target.getID() + " pid:" + + pid); + EDSimulator.add(10, message, sender, pid); + return false; + } + +} diff --git a/contrib/psg/src/example/edaggregation/AverageED.java b/contrib/psg/src/example/edaggregation/AverageED.java new file mode 100644 index 0000000000..c5f335831a --- /dev/null +++ b/contrib/psg/src/example/edaggregation/AverageED.java @@ -0,0 +1,116 @@ +/* + * 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 diff --git a/contrib/psg/src/example/symphony/AdapterIterableNetwork.java b/contrib/psg/src/example/symphony/AdapterIterableNetwork.java new file mode 100644 index 0000000000..37bf1046af --- /dev/null +++ b/contrib/psg/src/example/symphony/AdapterIterableNetwork.java @@ -0,0 +1,31 @@ +package example.symphony; + +import java.util.Iterator; +import peersim.core.Network; +import peersim.core.Node; + +/** + * Adapter Class absolutely UNSAFE, just to be able to iterate peersim.core.Network + * + * @author Andrea Esposito + */ +public class AdapterIterableNetwork implements Iterable, Iterator { + + private int i = 0; + + public Iterator iterator() { + return this; + } + + public boolean hasNext() { + return i < Network.size(); + } + + public Node next() { + return Network.get(i++); + } + + public void remove() { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/contrib/psg/src/example/symphony/AdapterSymphonyNodeComparator.java b/contrib/psg/src/example/symphony/AdapterSymphonyNodeComparator.java new file mode 100644 index 0000000000..b05bbb601a --- /dev/null +++ b/contrib/psg/src/example/symphony/AdapterSymphonyNodeComparator.java @@ -0,0 +1,28 @@ +package example.symphony; + +import java.util.Comparator; + +import example.symphony.SymphonyProtocol.BootstrapStatus; +import peersim.core.Node; + +/** + * Object-Adapter + * + * @author Andrea Esposito + */ +public class AdapterSymphonyNodeComparator implements Comparator> { + + private SymphonyNodeComparator comparator; + + public AdapterSymphonyNodeComparator(SymphonyNodeComparator comparator) { + this.comparator = comparator; + } + + public int compare(Tuple o1, Tuple o2) { + + Node node1 = o1.x; + Node node2 = o2.x; + + return comparator.compare(node1, node2); + } +} diff --git a/contrib/psg/src/example/symphony/Handler.java b/contrib/psg/src/example/symphony/Handler.java new file mode 100644 index 0000000000..c78d774566 --- /dev/null +++ b/contrib/psg/src/example/symphony/Handler.java @@ -0,0 +1,19 @@ +package example.symphony; + +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public interface Handler { + + /** + * Handler associable to a routing request + * + * @param src Symphony Protocol that has sent the routing request + * @param evt Tuple that contains: Node that manages the identifier, Identifier that the routing + * has done on + */ + void handle(SymphonyProtocol src, Tuple evt); +} diff --git a/contrib/psg/src/example/symphony/LeaveTest.java b/contrib/psg/src/example/symphony/LeaveTest.java new file mode 100644 index 0000000000..b4c9167f21 --- /dev/null +++ b/contrib/psg/src/example/symphony/LeaveTest.java @@ -0,0 +1,77 @@ +package example.symphony; + +import peersim.config.Configuration; +import peersim.core.CommonState; +import peersim.core.Control; +import peersim.core.Network; +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class LeaveTest implements Control { + + private static final String PAR_NETMANAGER = "symphonynetworkmanager"; + private static final String PAR_NUMBER_LEAVES = "n"; + private static final String PAR_MIN_SIZE = "minsizeOnline"; + private static final String PAR_WAIT_TARGET_SIZE = "waitTargetSizeToStart"; + private final int networkManagerID; + private final double n; + private final int minSizeNetwork; + private int targetSize; + + public LeaveTest(String prefix) { + networkManagerID = Configuration.getPid(prefix + "." + PAR_NETMANAGER); + double nAppo = Configuration.getDouble(prefix + "." + PAR_NUMBER_LEAVES); + if (!(nAppo > 0.0 && nAppo < 1.0)) { + n = (int) Math.round(nAppo); + } else { + n = nAppo; + } + + minSizeNetwork = Configuration.getInt(prefix + "." + PAR_MIN_SIZE, -1); + targetSize = Configuration.getInt(prefix + "." + PAR_WAIT_TARGET_SIZE, -1); + } + + public boolean execute() { + + if (minSizeNetwork > 0) { + + int onlineNode = 0; + AdapterIterableNetwork it = new AdapterIterableNetwork(); + for (Node node : it) { + if (node.isUp()) { + onlineNode++; + } + } + + if (targetSize <= 0 || targetSize <= onlineNode) { + targetSize = -1; + + // verify if i have to remove an exact number of nodes or a percentage of them + int actualN = (int) (n < 1.0 ? Math.ceil(Network.size() * n) : n); + + for (int i = 0; i < actualN && Network.size() > 0; i++) { + if (onlineNode > minSizeNetwork) { + Node leaveNode = Network.get(Math.abs(CommonState.r.nextInt()) % Network.size()); + + while (!leaveNode.isUp()) { + leaveNode = Network.get(Math.abs(CommonState.r.nextInt()) % Network.size()); + } + + SymphonyNetworkManager networkManager = (SymphonyNetworkManager) leaveNode.getProtocol(networkManagerID); + + networkManager.leave(leaveNode); + + onlineNode--; + } else { + break; + } + } + } + } + + return false; + } +} diff --git a/contrib/psg/src/example/symphony/Message.java b/contrib/psg/src/example/symphony/Message.java new file mode 100644 index 0000000000..5f63d118a1 --- /dev/null +++ b/contrib/psg/src/example/symphony/Message.java @@ -0,0 +1,90 @@ +package example.symphony; + +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class Message implements Cloneable { + + public enum MessageType { + + ROUTE, ROUTE_RESPONSE, ROUTE_FAIL, + JOIN, JOIN_RESPONSE, + UPDATE_NEIGHBOURS, UPDATE_NEIGHBOURS_RESPONSE, + REQUEST_LONG_RANGE_LINK, ACCEPTED_LONG_RANGE_LINK, REJECT_LONG_RANGE_LINK, DISCONNECT_LONG_RANGE_LINK, UNAVAILABLE_LONG_RANGE_LINK, + UPDATE_STATUS, UPDATE_STATUS_RESPONSE, + LEAVE, + KEEP_ALIVE, KEEP_ALIVE_RESPONSE + } + private long hopCounter; + private MessageType type; + private Node src; + private Node currentHop; + private Object body; + private static long globalID = 0; + private final long id; + + public Message(Object body, Node src, MessageType type) { + this.type = type; + this.src = src; + this.body = body; + hopCounter = 0; + id = globalID++; + currentHop = src; + } + + public long getID() { + return id; + } + + public Object getBody() { + return body; + } + + public void incrementHop() { + hopCounter++; + } + + public long getHop() { + return hopCounter; + } + + public MessageType getType() { + return type; + } + + public Node getSourceNode() { + return src; + } + + public Node getCurrentHop() { + return currentHop; + } + + public void setCurrentHop(Node currentHop) { + this.currentHop = currentHop; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + @Override + public String toString() { + + StringBuilder builder = new StringBuilder(); + builder.append("Message@").append(this.hashCode()).append("[\n"); + + builder.append("\tID : ").append(id).append(",\n"); + builder.append("\tSource ID: ").append(src.getID()).append(",\n"); + builder.append("\tType : ").append(type).append(",\n"); + builder.append("\tBody : ").append(body).append(",\n"); + builder.append("\tCurrent Hop ID: ").append(currentHop.getID()).append(",\n"); + builder.append("\tHop Counter : ").append(hopCounter).append("\n]\n"); + + return builder.toString(); + } +} diff --git a/contrib/psg/src/example/symphony/NetworkSizeEstimatorProtocolInterface.java b/contrib/psg/src/example/symphony/NetworkSizeEstimatorProtocolInterface.java new file mode 100644 index 0000000000..ffb8ba9972 --- /dev/null +++ b/contrib/psg/src/example/symphony/NetworkSizeEstimatorProtocolInterface.java @@ -0,0 +1,13 @@ +package example.symphony; + +import peersim.core.Node; +import peersim.core.Protocol; + +/** + * + * @author Andrea Esposito + */ +public interface NetworkSizeEstimatorProtocolInterface extends Protocol { + + public int getNetworkSize(Node node); +} diff --git a/contrib/psg/src/example/symphony/RandomRouteTest.java b/contrib/psg/src/example/symphony/RandomRouteTest.java new file mode 100644 index 0000000000..fe55f332b0 --- /dev/null +++ b/contrib/psg/src/example/symphony/RandomRouteTest.java @@ -0,0 +1,48 @@ +package example.symphony; + +import java.util.logging.Level; +import java.util.logging.Logger; +import peersim.config.Configuration; +import peersim.core.CommonState; +import peersim.core.Control; +import peersim.core.Network; +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class RandomRouteTest implements Control, Handler { + + private static final String PAR_SYMPHONY = "symphony"; + private final int symphonyID; + + public RandomRouteTest(String prefix) { + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY); + } + + public boolean execute() { + + Node src = Network.get(Math.abs(CommonState.r.nextInt()) % Network.size()); + + SymphonyProtocol symphony = (SymphonyProtocol) src.getProtocol(symphonyID); + try { + symphony.route(src, CommonState.r.nextDouble(), this); + } catch (RoutingException ex) { + Logger.getLogger(RandomRouteTest.class.getName()).log(Level.SEVERE, ex.getMessage()); + } + + return false; + + } + + public void handle(SymphonyProtocol symphony, Tuple tuple) { + + if (tuple == null) { + Logger.getLogger(RandomRouteTest.class.getName()).log(Level.SEVERE, "FAIL ROUTE RANDOMTEST"); + return; + } + + 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()); + } +} diff --git a/contrib/psg/src/example/symphony/RingRouteTest.java b/contrib/psg/src/example/symphony/RingRouteTest.java new file mode 100644 index 0000000000..06262cb119 --- /dev/null +++ b/contrib/psg/src/example/symphony/RingRouteTest.java @@ -0,0 +1,150 @@ +package example.symphony; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import example.symphony.SymphonyProtocol.BootstrapStatus; +import peersim.config.Configuration; +import peersim.core.Control; +import peersim.core.Network; +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class RingRouteTest implements Control, Handler { + + private static final String PAR_SYMPHONY = "symphony"; + private static final String PAR_STARTNODE = "startnode"; + private final int symphonyID; + private final int indexStartNode; + private Node start; + private boolean finished; + private boolean flagTimeout; + private HashSet antiLoopSet; + + public RingRouteTest(String prefix) { + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY); + indexStartNode = Configuration.getInt(prefix + "." + PAR_STARTNODE, 0); + + finished = true; + flagTimeout = false; + antiLoopSet = new HashSet(); + } + + public boolean execute() { + + if (!finished && flagTimeout) { + + Logger.getLogger(RingRouteTest.class.getName()).log(Level.WARNING, "Sent msg but no aswer. Timeout. Ring Route Test terminated for Timeout."); + + finished = true; + flagTimeout = false; + } + + if (finished) { + + flagTimeout = true; + antiLoopSet.clear(); + + int indexRealStartNode = indexStartNode; + Node realStartNode = Network.get(indexStartNode); + SymphonyProtocol symphony = (SymphonyProtocol) realStartNode.getProtocol(symphonyID); + + while (!symphony.isBootstrapped() || !realStartNode.isUp()) { + indexRealStartNode = (indexRealStartNode + 1) % Network.size(); + realStartNode = Network.get(indexRealStartNode); + symphony = (SymphonyProtocol) realStartNode.getProtocol(symphonyID); + + if (indexRealStartNode == indexStartNode) { + Logger.getLogger(RingRouteTest.class.getName()).log(Level.WARNING, "No ONLINE nodes. The ring is vanished. Ring Route Terminated."); + finished = true; + flagTimeout = false; + return false; + } + } + + start = realStartNode; + finished = false; + + Logger.getLogger(RingRouteTest.class.getName()).log(Level.INFO, "RingRoute started."); + + doRoute(start, true); + } + + return false; + } + + public void handle(SymphonyProtocol symphony, Tuple tuple) { + + if (tuple == null) { + Logger.getLogger(RingRouteTest.class.getName()).log(Level.SEVERE, "FAIL RING ROUTING"); + finished = true; + return; + } + + 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()); + + doRoute(tuple.x, false); + } + + private void doRoute(Node node, boolean firstTime) { + + SymphonyProtocol symphonyStartNode = (SymphonyProtocol) start.getProtocol(symphonyID); + + if (!symphonyStartNode.isBootstrapped()) { + Logger.getLogger(RingRouteTest.class.getName()).log(Level.INFO, "The node i started from left. Ring Route Terminated."); + finished = true; + return; + } + + if (!firstTime && node.equals(start)) { + Logger.getLogger(RingRouteTest.class.getName()).log(Level.INFO, "RingRoute Terminated"); + finished = true; + return; + } + + if (antiLoopSet.contains(node)) { + Logger.getLogger(RingRouteTest.class.getName()).log(Level.INFO, "Not able to reach the node that i started from. Ring Route Terminated."); + finished = true; + return; + } else { + antiLoopSet.add(node); + } + + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + AdapterSymphonyNodeComparator adapterSymphonyNodeComparator = new AdapterSymphonyNodeComparator(new SymphonyNodeComparator(symphonyID, node)); + + Collection> collection = (Collection>) symphony.leftShortRangeLinks.clone(); + LinkedList> list = new LinkedList>(collection); + Collections.sort(list, adapterSymphonyNodeComparator); + + Node targetNode = null; + for (Tuple tuple : list) { + if (tuple.y == BootstrapStatus.ONLINE) { + targetNode = tuple.x; + break; + } + } + + if (targetNode == null || !targetNode.isUp()) { + Logger.getLogger(RingRouteTest.class.getName()).log(Level.WARNING, "Terminated Ring Route but not done completely"); + finished = true; + return; + } + + SymphonyProtocol symphonyTarget = (SymphonyProtocol) targetNode.getProtocol(symphonyID); + try { + symphony.route(node, symphonyTarget.getIdentifier(), this); + Logger.getLogger(RingRouteTest.class.getName()).log(Level.FINEST, "Ring from: " + symphony.getIdentifier() + " to " + symphonyTarget.getIdentifier()); + } catch (RoutingException ex) { + Logger.getLogger(RingRouteTest.class.getName()).log(Level.WARNING, "Finito AnelloRoute MA NON FATTO TUTTO"); + finished = true; + } + } +} diff --git a/contrib/psg/src/example/symphony/RoutingException.java b/contrib/psg/src/example/symphony/RoutingException.java new file mode 100644 index 0000000000..1b6bb9b878 --- /dev/null +++ b/contrib/psg/src/example/symphony/RoutingException.java @@ -0,0 +1,15 @@ +package example.symphony; + +/** + * + * @author Andrea Esposito + */ +public class RoutingException extends Exception { + + public RoutingException() { + } + + public RoutingException(String msg) { + super(msg); + } +} diff --git a/contrib/psg/src/example/symphony/SimpleNetworkSizeEstimatorProtocol.java b/contrib/psg/src/example/symphony/SimpleNetworkSizeEstimatorProtocol.java new file mode 100644 index 0000000000..6d17371b87 --- /dev/null +++ b/contrib/psg/src/example/symphony/SimpleNetworkSizeEstimatorProtocol.java @@ -0,0 +1,23 @@ +package example.symphony; + +import peersim.core.Network; +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class SimpleNetworkSizeEstimatorProtocol implements NetworkSizeEstimatorProtocolInterface { + + public SimpleNetworkSizeEstimatorProtocol(String prefix) { + } + + public int getNetworkSize(Node node) { + return Network.size(); + } + + @Override + public Object clone() { + return this; + } +} diff --git a/contrib/psg/src/example/symphony/SymphonyEstimationProtocol.java b/contrib/psg/src/example/symphony/SymphonyEstimationProtocol.java new file mode 100644 index 0000000000..642677d2bb --- /dev/null +++ b/contrib/psg/src/example/symphony/SymphonyEstimationProtocol.java @@ -0,0 +1,106 @@ +package example.symphony; + +import java.util.*; +import peersim.config.Configuration; +import peersim.core.Network; +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class SymphonyEstimationProtocol implements NetworkSizeEstimatorProtocolInterface { + + private static final String PAR_SYMPHONY = "symphony"; + private static final String PAR_S = "s"; + private final int symphonyID; + private final int s; + + public SymphonyEstimationProtocol(String prefix) { + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY); + s = Configuration.getInt(prefix + "." + PAR_S, -1); + } + + /** + * Implementation of the estimated network size as a variant of the paper one. It use anyway the + * idea to calculate the size from the length segments but without exchanging the information + * with the neighbours instead using only the local information. + */ + public int getNetworkSize(Node node) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + + // If the node is not yet inside the ring i return the minimum size (2 nodes) + if (!symphony.isBootstrapped()) { + return 2; + } + + /* + * I clone the short range links views (wrapped into an ArrayList because the returned list + * 'Arrays.asList doesn't support the "removeAll" method or better its size is fixed) + */ + ArrayList> leftList = new ArrayList>(Arrays.asList((Tuple[]) symphony.leftShortRangeLinks.toArray(new Tuple[0]))); + ArrayList> rightList = new ArrayList>(Arrays.asList((Tuple[]) symphony.rightShortRangeLinks.toArray(new Tuple[0]))); + + // Remove the neighbours that are offline + LinkedList> offlineNeighbors = new LinkedList>(); + for (Tuple tuple : leftList) { + if (tuple.y == SymphonyProtocol.BootstrapStatus.OFFLINE) { + offlineNeighbors.add(tuple); + } + } + leftList.removeAll(offlineNeighbors); + offlineNeighbors.clear(); + for (Tuple tuple : rightList) { + if (tuple.y == SymphonyProtocol.BootstrapStatus.OFFLINE) { + offlineNeighbors.add(tuple); + } + } + rightList.removeAll(offlineNeighbors); + + // Sort the neighbours based on the distance from me + Comparator> comparator = new AdapterSymphonyNodeComparator(new SymphonyNodeComparator(symphonyID, symphony.getIdentifier())); + Collections.sort(leftList, comparator); + Collections.sort(rightList, comparator); + + // Calculate the variables to estimated the network size + double Xs = 0; + int countS = 0; + + List> appoList[] = new List[2]; + appoList[0] = leftList; + appoList[1] = rightList; + + double[] appoPrecIdentifier = new double[]{symphony.getIdentifier(), symphony.getIdentifier()}; + int[] appoCurrentIndex = new int[]{0, 0}; + + int realS = (int) (s <= 0 ? Math.log(Network.size() / Math.log(2)) : s); + + for (int i = 0; i < realS; i++) { + double precIdentifier = appoPrecIdentifier[i % 2]; + int currentIndex = appoCurrentIndex[i % 2]; + List> currentList = appoList[i % 2]; + + try { + double currentIdentifier = ((SymphonyProtocol) currentList.get(currentIndex).x.getProtocol(symphonyID)).getIdentifier(); + + appoPrecIdentifier[i % 2] = currentIdentifier; + appoCurrentIndex[i % 2] = appoCurrentIndex[i % 2] + 1; + + double distance = Math.abs(currentIdentifier - precIdentifier); + Xs += Math.min(distance, 1.0 - distance); + countS++; + } catch (IndexOutOfBoundsException ex) { + // Simply i skip the counting + } + } + + int ret = Xs == 0 ? 0 : (int) Math.round(countS / Xs); + + return ret; + } + + @Override + public Object clone() { + return this; + } +} diff --git a/contrib/psg/src/example/symphony/SymphonyNetworkBuilder.java b/contrib/psg/src/example/symphony/SymphonyNetworkBuilder.java new file mode 100644 index 0000000000..a012d600bb --- /dev/null +++ b/contrib/psg/src/example/symphony/SymphonyNetworkBuilder.java @@ -0,0 +1,157 @@ +package example.symphony; + +import java.util.Collection; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import example.symphony.SymphonyProtocol.BootstrapStatus; +import peersim.config.Configuration; +import peersim.core.CommonState; +import peersim.core.Control; +import peersim.core.Network; +import peersim.core.Node; + +/** + * Inizializer that create the initial ring + * + * @author Andrea Esposito + */ +public class SymphonyNetworkBuilder implements Control { + + private static final String PAR_SYMHONY = "symphony"; + private static final String PAR_LONG_LINK = "createLongLinks"; + private static final String PAR_MAX_ATTEMPTS = "attempts"; + private final int symphonyID; + private final boolean createLongRangeLinks; + private final int MAX_ATTEMPTS; + + public SymphonyNetworkBuilder(String prefix) { + + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMHONY); + createLongRangeLinks = Configuration.getBoolean(prefix + "." + PAR_LONG_LINK, true); + MAX_ATTEMPTS = Configuration.getInt(prefix + "." + PAR_MAX_ATTEMPTS, 5); + } + + public boolean execute() { + + // Sort the network for convenience (from 0.0 to 1.0) + Network.sort(new Comparator() { + + public int compare(Node o1, Node o2) { + + SymphonyProtocol symphony1 = (SymphonyProtocol) o1.getProtocol(symphonyID); + SymphonyProtocol symphony2 = (SymphonyProtocol) o2.getProtocol(symphonyID); + + Double identifier1 = symphony1.getIdentifier(); + Double identifier2 = symphony2.getIdentifier(); + + return identifier1.compareTo(identifier2); + } + }); + + int numShortLinksPerSide = ((SymphonyProtocol) Network.get(0).getProtocol(symphonyID)).numberShortRangeLinksPerSide; + + for (int i = 0; i < Network.size(); i++) { + + Node node = Network.get(i); + SymphonyProtocol symphonyNode = (SymphonyProtocol) node.getProtocol(symphonyID); + + // Create the short links + for (int j = 1; j <= numShortLinksPerSide; j++) { + + int pos = i - j; + pos = pos < 0 ? Network.size() + pos : pos; + symphonyNode.rightShortRangeLinks.add(new Tuple(Network.get(pos), BootstrapStatus.ONLINE)); + + pos = (i + j) % Network.size(); + symphonyNode.leftShortRangeLinks.add(new Tuple(Network.get(pos), BootstrapStatus.ONLINE)); + } + + symphonyNode.loggedIntoNetwork = SymphonyProtocol.BootstrapStatus.ONLINE; + } + + /* + * UPDATE: Putted a flag to decide if perform this part of code or not at configuration + * time. At default i create the long range links. + * + * The Long Range Links could be left to the networkmanager but the tests that we'll do have + * to put into account that in an initial phase will be some message exchanging to create + * the long range links and so the latency is faked... for that reason the long range links + * are created manually here such a way to have a complete symphony network from the + * beginning. + */ + if (createLongRangeLinks) { + for (Node node : new AdapterIterableNetwork()) { + + SymphonyProtocol symphonyNode = (SymphonyProtocol) node.getProtocol(symphonyID); + + // Create the long links + int k = (int) Math.ceil(Math.log(Network.size()) / Math.log(2)); + + if (symphonyNode.fixedLongRangeLinks) { + k = symphonyNode.numberFixedLongRangeLinks; + } + + Collection allShortLinks = new LinkedList(); + for (Tuple shortTuple : symphonyNode.leftShortRangeLinks) { + allShortLinks.add(shortTuple.x); + } + for (Tuple shortTuple : symphonyNode.rightShortRangeLinks) { + allShortLinks.add(shortTuple.x); + } + + int j = 0; + int attempts = MAX_ATTEMPTS; + while (j <= k) { + + double distance = Math.exp(k * (CommonState.r.nextDouble() - 1.0)); + double targetIdentifier = (symphonyNode.getIdentifier() + distance) % 1; + + Node targetNode; + try { + + // use the unidirectional routing because i want to catch the manager + targetNode = symphonyNode.findClosestNode(targetIdentifier, new AdapterIterableNetwork(), true); + SymphonyProtocol symphonyTargetNode = (SymphonyProtocol) targetNode.getProtocol(symphonyID); + if (!targetNode.equals(node) + && !symphonyNode.longRangeLinksOutgoing.contains(targetNode) + && symphonyTargetNode.longRangeLinksIncoming.size() < (2 * k) + && !allShortLinks.contains(targetNode)) { + + boolean fresh = symphonyTargetNode.longRangeLinksIncoming.add(node); + + if (fresh) { + j++; + attempts = MAX_ATTEMPTS; + symphonyNode.longRangeLinksOutgoing.add(targetNode); + } else { + attempts--; + if (attempts <= 0) { // Because i don't want to loop i try a finite number of times + attempts = MAX_ATTEMPTS; + j++; + } + + } + } else { + attempts--; + if (attempts <= 0) { // Because i don't want to loop i try a finite number of times + attempts = MAX_ATTEMPTS; + j++; + } + + } + } catch (RoutingException ex) { + Logger.getLogger(SymphonyNetworkBuilder.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + } + + // Shuffle + Network.shuffle(); + + return false; + } +} diff --git a/contrib/psg/src/example/symphony/SymphonyNetworkChecker.java b/contrib/psg/src/example/symphony/SymphonyNetworkChecker.java new file mode 100644 index 0000000000..d5f2aef4aa --- /dev/null +++ b/contrib/psg/src/example/symphony/SymphonyNetworkChecker.java @@ -0,0 +1,123 @@ +package example.symphony; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import example.symphony.SymphonyProtocol.BootstrapStatus; +import peersim.config.Configuration; +import peersim.core.Control; +import peersim.core.Network; +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class SymphonyNetworkChecker implements Control { + + private static final String PAR_SYMHONY = "symphony"; + private static final String PAR_NETSIZE = "networkestimator"; + private final int symphonyID; + private final int networkEstimatorID; + + public SymphonyNetworkChecker(String prefix) { + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMHONY); + networkEstimatorID = Configuration.getPid(prefix + "." + PAR_NETSIZE); + } + + public boolean execute() { + + boolean isNotOK = false; + + Set idSet = new HashSet(); + Iterable coll = new AdapterIterableNetwork(); + + int countOnline = 0; + int count = 0; + int notBootstrapped = 0; + int countKO = 0; + int disconnected = 0; + + for (Node node : coll) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + + if (!node.isUp()) { + disconnected++; + } else { + count++; + } + + if (symphony.loggedIntoNetwork == SymphonyProtocol.BootstrapStatus.ONLINE) { + + countOnline++; + + NetworkSizeEstimatorProtocolInterface networkEstimator = (NetworkSizeEstimatorProtocolInterface) node.getProtocol(networkEstimatorID); + int k = (int) Math.ceil(Math.log(networkEstimator.getNetworkSize(node)) / Math.log(2)); + + boolean checkLeftShortRangeLinks = symphony.leftShortRangeLinks.size() > 0 && symphony.leftShortRangeLinks.size() <= symphony.numberShortRangeLinksPerSide; + boolean checkRightShortRangeLinks = symphony.rightShortRangeLinks.size() > 0 && symphony.rightShortRangeLinks.size() <= symphony.numberShortRangeLinksPerSide; + + boolean oneNeighborOnline = false; + for (Tuple leftTuple : symphony.leftShortRangeLinks) { + if (leftTuple.y != BootstrapStatus.ONLINE && leftTuple.y != BootstrapStatus.OFFLINE) { + notBootstrapped++; + } else { + oneNeighborOnline = true; + checkLeftShortRangeLinks = checkLeftShortRangeLinks && SymphonyProtocol.isLeftNeighbour(node, leftTuple.x); + } + } + checkLeftShortRangeLinks = checkLeftShortRangeLinks && oneNeighborOnline; + + oneNeighborOnline = false; + for (Tuple rightTuple : symphony.rightShortRangeLinks) { + if (rightTuple.y != BootstrapStatus.ONLINE && rightTuple.y != BootstrapStatus.OFFLINE) { + notBootstrapped++; + } else { + oneNeighborOnline = true; + checkRightShortRangeLinks = checkRightShortRangeLinks && !SymphonyProtocol.isLeftNeighbour(node, rightTuple.x); + } + } + checkRightShortRangeLinks = checkRightShortRangeLinks && oneNeighborOnline; + + // Check if the node is in its neighbours + if (checkLeftShortRangeLinks) { + AdapterSymphonyNodeComparator comparator = new AdapterSymphonyNodeComparator(new SymphonyNodeComparator(symphonyID, node)); + checkLeftShortRangeLinks = checkLeftShortRangeLinks && !Collections.min(symphony.leftShortRangeLinks, comparator).x.equals(node); + } + + if (checkRightShortRangeLinks) { + AdapterSymphonyNodeComparator comparator = new AdapterSymphonyNodeComparator(new SymphonyNodeComparator(symphonyID, node)); + checkRightShortRangeLinks = checkRightShortRangeLinks && !Collections.min(symphony.rightShortRangeLinks, comparator).x.equals(node); + } + + boolean checkLongRangeLinksOutgoing = !symphony.longRangeLinksOutgoing.contains(node); + boolean checkLongRangeLinksIncoming = /* + * symphony.longRangeLinksIncoming.size() <= (2 * k) && + */ !symphony.longRangeLinksIncoming.contains(node); + + boolean checkUniqueID = !idSet.contains(symphony.getIdentifier()); + idSet.add(symphony.getIdentifier()); + + boolean nextIsNotOK = !(checkUniqueID && checkLeftShortRangeLinks && checkRightShortRangeLinks && checkLongRangeLinksOutgoing && checkLongRangeLinksIncoming); + + if (nextIsNotOK) { + countKO++; + Logger.getLogger(SymphonyNetworkChecker.class.getName()).log(Level.SEVERE, "OPS"); + } + + isNotOK = isNotOK || nextIsNotOK; + } + } + + System.out.println("Error: " + countKO); + System.out.println("Online: " + countOnline + "/" + count); + System.out.println("Not Bootstrapped: " + notBootstrapped); + System.out.println("Disconnected: " + disconnected); + System.out.println("Network Size: " + Network.size()); + + return isNotOK; + } +} diff --git a/contrib/psg/src/example/symphony/SymphonyNetworkManager.java b/contrib/psg/src/example/symphony/SymphonyNetworkManager.java new file mode 100644 index 0000000000..4a1277fc86 --- /dev/null +++ b/contrib/psg/src/example/symphony/SymphonyNetworkManager.java @@ -0,0 +1,864 @@ +package example.symphony; + +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +import example.symphony.Message.MessageType; +import example.symphony.SymphonyProtocol.BootstrapStatus; +import peersim.cdsim.CDProtocol; +import peersim.config.Configuration; +import peersim.core.CommonState; +import peersim.core.Fallible; +import peersim.core.Node; +import peersim.edsim.EDProtocol; +import peersim.transport.Transport; + +/** + * + * @author Andrea Esposito + */ +public class SymphonyNetworkManager implements EDProtocol, CDProtocol { + + private static final String PAR_SYMPHONY = "symphony"; + private static final String PAR_TRANSP = "transport"; + private static final String PAR_ATTEMPTS = "attempts"; + private static final String PAR_NETSIZE = "networkestimator"; + private static final String PAR_NUM_TIMEOUT = "nTimeout"; + private static final String PAR_RELINKING = "relinking"; + private static final String PAR_RELINKING_LOWER_BOUND = "relinkingLowerBound"; + private static final String PAR_RELINKING_UPPER_BOUND = "relinkingUpperBound"; + private static final int DEFAULT_K = 1; + private static final int DEFAULT_N = 2; + private static final double DEFAULT_RELINKING_LOWER_BOUND = 0.5; + private static final double DEFAULT_RELINKING_UPPER_BOUND = 2.0; + private final String prefix; + private final int symphonyID; + private final int transportID; + private final int networkEstimatorID; + private final int attempts; + private final int pid; + private final int nTimeout; + private final HashMap keepAliveMap; + private final boolean relinkingProtocolActivated; + private final double relinkingUpperBound; + private final double relinkingLowerBound; + private int k = DEFAULT_K; // Number of Long Range Link + private int n = DEFAULT_N; // Estimation Network size + private static boolean firstPrintConfig = true; + /* + * Estimation Network size at which last long distance link was established, at the beginning -1 + * to indicate that we never had Long Range Links + */ + private int nLink = -1; + private int currentAttempts; + + public SymphonyNetworkManager(String prefix) { + + this.prefix = prefix; + pid = Configuration.lookupPid(prefix.replaceAll("protocol.", "")); + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY); + transportID = Configuration.getPid(prefix + "." + PAR_TRANSP); + networkEstimatorID = Configuration.getPid(prefix + "." + PAR_NETSIZE); + attempts = Configuration.getInt(prefix + "." + PAR_ATTEMPTS); + nTimeout = Configuration.getInt(prefix + "." + PAR_NUM_TIMEOUT, 10); + relinkingProtocolActivated = !Configuration.getString(prefix + "." + PAR_RELINKING, "on").toLowerCase().equals("off"); + double relinkingLowerBoundAppo = Configuration.getDouble(prefix + "." + PAR_RELINKING_LOWER_BOUND, DEFAULT_RELINKING_LOWER_BOUND); + double relinkingUpperBoundAppo = Configuration.getDouble(prefix + "." + PAR_RELINKING_UPPER_BOUND, DEFAULT_RELINKING_UPPER_BOUND); + if (relinkingLowerBoundAppo > relinkingUpperBoundAppo) { + relinkingLowerBound = DEFAULT_RELINKING_LOWER_BOUND; + relinkingUpperBound = DEFAULT_RELINKING_UPPER_BOUND; + } else { + relinkingLowerBound = relinkingLowerBoundAppo; + relinkingUpperBound = relinkingUpperBoundAppo; + } + + keepAliveMap = new HashMap(); + + printConfig(); + } + + private void printConfig() { + + if (firstPrintConfig) { + firstPrintConfig = false; + System.out.println(SymphonyNetworkManager.class.getSimpleName() + " Configuration:"); + System.out.println("- Attempts per LongRangeLinks: " + attempts); + System.out.println("- Number of Timeout before a node is considered OFFLINE (through Keep-alive):" + nTimeout); + System.out.println("- Relinking: " + (relinkingProtocolActivated ? "ON" : "OFF")); + System.out.println("- Relinking Range: [" + relinkingLowerBound + ", " + relinkingUpperBound + "]"); + System.out.println("-------------------------------\n"); + } + } + + public void join(final Node node, final Node bootstrapNode) throws RoutingException { + final SymphonyProtocol bootstrapSymphony = (SymphonyProtocol) bootstrapNode.getProtocol(symphonyID); + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + + /* + * Search (through the bootstrap node) and contact the Manager Node of myself such a way to + * be able to insert myself into the ring and create the short links + * + */ + bootstrapSymphony.route(bootstrapNode, symphony.getIdentifier(), new Handler() { + + public void handle(SymphonyProtocol src, Tuple tuple) { + if (tuple == null) { + Logger.getLogger(SymphonyNetworkManager.class.getName()).log(Level.SEVERE, "FAIL ROUTE JOIN"); + node.setFailState(Fallible.DEAD); + return; + } + + Node managerNode = tuple.x; + + Transport transport = (Transport) node.getProtocol(transportID); + Message msg = new Message(node, node, MessageType.JOIN); + transport.send(node, managerNode, msg, pid); + } + }); + + // 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) + } + + /** + * Conservative Re-Linking (i reuse the ones already created: not all fresh) + * + * @param node + */ + public void updateLongRangeLinks(Node node) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + Transport transport = (Transport) node.getProtocol(transportID); + + // if too much links i delete the farest ones + while (symphony.longRangeLinksOutgoing.size() > k) { + Node distantNode = Collections.max(symphony.longRangeLinksOutgoing, new SymphonyNodeComparator(symphonyID, node)); + symphony.longRangeLinksOutgoing.remove(distantNode); + + // Communicate to the outgoing node that it ins't anymore one of my long range links + Message disconnectMsg = new Message(null, node, MessageType.DISCONNECT_LONG_RANGE_LINK); + transport.send(node, distantNode, disconnectMsg, pid); + } + + // I can search Long Range Links only if i'm into the ring and i'm able to do routing + if (symphony.isBootstrapped()) { + // if only few i try again, untill attempts times, to add new ones + int difference = k - symphony.longRangeLinksOutgoing.size(); + currentAttempts = attempts; + for (int i = 0; i < difference; i++) { + sendLongRangeLinkRequest(symphony, node); + } + } + } + private static final int MAX_ANTILOOP_COUNT_MANAGER_MYSELF = 5; + private int antiLoopManagerMySelf = 0; + + private void sendLongRangeLinkRequest(final SymphonyProtocol symphony, final Node node) { + boolean routingOk; + do { + double distance = Math.exp((Math.log(n) / Math.log(2)) * (CommonState.r.nextDouble() - 1.0)); // Harmonic Distribution + double targetIdentifier = (symphony.getIdentifier() + distance) % 1; + try { + + symphony.route(node, targetIdentifier, new Handler() { + + public void handle(SymphonyProtocol src, Tuple tuple) { + + if (tuple == null) { + Logger.getLogger(SymphonyNetworkManager.class.getName()).log(Level.SEVERE, "FAIL ROUTE SENDLONGRANGELINKREQUEST"); + return; + } + + Collection allShortLinks = new LinkedList(); + for (Tuple shortTuple : symphony.leftShortRangeLinks) { + allShortLinks.add(shortTuple.x); + } + for (Tuple shortTuple : symphony.rightShortRangeLinks) { + allShortLinks.add(shortTuple.x); + } + + /* + * + * I'm myself one of my short links, special case... i try again without + * reduce the attempts for a maximum of MAX_ANTILOOP_COUNT_MANAGER_MYSELF + * times after that i start again to reduce the attempts + */ + if (tuple.x.equals(node) || allShortLinks.contains(tuple.x)) { + + if (antiLoopManagerMySelf < MAX_ANTILOOP_COUNT_MANAGER_MYSELF) { + + antiLoopManagerMySelf++; + sendLongRangeLinkRequest(symphony, node); + } else { + antiLoopManagerMySelf = 0; + currentAttempts--; + } + } else { + + boolean alreadyAdded = symphony.longRangeLinksOutgoing.contains(tuple.x); + /* + * + * OPINABLE: DESCREASE ATTEMPTS ONLY FOR REJECT? If yes i have to manage + * the possible loop (nodi exhaurited so already all added) + */ + if (alreadyAdded && currentAttempts > 0) { + currentAttempts--; + sendLongRangeLinkRequest(symphony, node); + } else if (!alreadyAdded) { + Message msg = new Message(null, node, MessageType.REQUEST_LONG_RANGE_LINK); + Transport transport = (Transport) node.getProtocol(transportID); + transport.send(node, tuple.x, msg, pid); + } + } + } + }); + routingOk = true; + } catch (RoutingException ex) { + routingOk = false; + } + } while (!routingOk); + } + + public void leave(Node node) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + + if (symphony.loggedIntoNetwork != BootstrapStatus.OFFLINE) { + Transport transport = (Transport) node.getProtocol(transportID); + + symphony.loggedIntoNetwork = BootstrapStatus.OFFLINE; + + // Communicate that i'm leaving to the outgoing (that i point to) nodes + for (Node outgoingNode : symphony.longRangeLinksOutgoing) { + Message disconnectMsg = new Message(null, node, MessageType.DISCONNECT_LONG_RANGE_LINK); + transport.send(node, outgoingNode, disconnectMsg, pid); + } + + // Communicate that i'm leaving to the incoming (that they point to me) nodes + for (Node incomingNode : symphony.longRangeLinksIncoming) { + Message unavailableMsg = new Message(null, node, MessageType.UNAVAILABLE_LONG_RANGE_LINK); + transport.send(node, incomingNode, unavailableMsg, pid); + } + + // Communicate to my neighbours (short range links) that i'm leaving and i send to them the near neighbours + for (Tuple leftTuple : symphony.leftShortRangeLinks) { + Message leaveMsg = new Message(symphony.rightShortRangeLinks.clone(), node, MessageType.LEAVE); + transport.send(node, leftTuple.x, leaveMsg, pid); + } + + for (Tuple rightTuple : symphony.rightShortRangeLinks) { + Message leaveMsg = new Message(symphony.leftShortRangeLinks.clone(), node, MessageType.LEAVE); + transport.send(node, rightTuple.x, leaveMsg, pid); + } + + node.setFailState(Fallible.DEAD); + } + } + + public void processEvent(Node node, int pid, Object event) { + + Message msg = (Message) event; + + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + Transport transport = (Transport) node.getProtocol(transportID); + + Collection> collection = null; + switch (msg.getType()) { + case JOIN: + // I send my current neighbours to the entering node + collection = (Collection>) symphony.leftShortRangeLinks.clone(); + collection.addAll((Collection>) symphony.rightShortRangeLinks.clone()); + Message responseMsg = new Message(collection, node, MessageType.JOIN_RESPONSE); + transport.send(node, msg.getSourceNode(), responseMsg, pid); + + /* + * Update my neighbours list, adding the new one (for sure it is entering in the + * left side) + * + * Put to "ONLINE_AND_ALL_NEIGHBOURS_OFFLINE" because maybe the bootstrap phase is + * not terminated yet (ashyncronous communication) + */ + symphony.leftShortRangeLinks.add(new Tuple(msg.getSourceNode(), BootstrapStatus.ONLINE_AND_ALL_NEIGHBOURS_OFFLINE)); + + + fixNeighbours(node, symphony.leftShortRangeLinks); + break; + case JOIN_RESPONSE: + + Collection> tupleCollection = (Collection>) msg.getBody(); + + /* + * + * My manager is a right neighbour. The manager is already inside the ring, boostrap + * obliviously ok + */ + symphony.rightShortRangeLinks.add(new Tuple(msg.getSourceNode(), BootstrapStatus.ONLINE)); + + // Set my neighbours in the correct position + for (Tuple tuple : tupleCollection) { + if (SymphonyProtocol.isLeftNeighbour(node, tuple.x)) { + symphony.leftShortRangeLinks.add(tuple); + } else { + symphony.rightShortRangeLinks.add(tuple); + } + } + + fixNeighbours(node, symphony.leftShortRangeLinks); + fixNeighbours(node, symphony.rightShortRangeLinks); + + // Update bootstrap status + checkBootstrapStatus(node); + + // I send the refresh command such a way to exchange the views + refreshNeighbours(node); + + // Update Long Range Links, because it's at the beginning is the same as adding k + updateLongRangeLinks(node); + break; + case UPDATE_NEIGHBOURS: + + Collection> collectionCloned = ((Collection>) symphony.leftShortRangeLinks.clone()); + collectionCloned.addAll(((Collection>) symphony.rightShortRangeLinks.clone())); + + // Send my neighbours such a way it can also update itself + Message responseUpdateMsg = new Message(collectionCloned, node, MessageType.UPDATE_NEIGHBOURS_RESPONSE); + transport.send(node, msg.getSourceNode(), responseUpdateMsg, pid); + + // Update my view with the new node + Tuple neighbourTuple = new Tuple(msg.getSourceNode(), (BootstrapStatus) msg.getBody()); + if (SymphonyProtocol.isLeftNeighbour(node, msg.getSourceNode())) { + collection = symphony.leftShortRangeLinks; + } else { + collection = symphony.rightShortRangeLinks; + } + collection.add(neighbourTuple); + + fixNeighbours(node, collection); + fixLookAheadMap(node); + break; + case UPDATE_NEIGHBOURS_RESPONSE: + + Collection> responseCollection = (Collection>) msg.getBody(); + + for (Tuple neighbourResponseTuple : responseCollection) { + if (SymphonyProtocol.isLeftNeighbour(node, neighbourResponseTuple.x)) { + collection = symphony.leftShortRangeLinks; + } else { + collection = symphony.rightShortRangeLinks; + } + collection.add(neighbourResponseTuple); + } + + // Fix the neighbours number to the maximum allow and maybe remove myself from the list + fixNeighbours(node, symphony.leftShortRangeLinks); + fixNeighbours(node, symphony.rightShortRangeLinks); + fixLookAheadMap(node); + break; + case UPDATE_STATUS: + case UPDATE_STATUS_RESPONSE: + + Node updNode = msg.getSourceNode(); + BootstrapStatus updStatus = (BootstrapStatus) msg.getBody(); + + // I search the neighbour and i update its status + boolean founded = false; + + // Try to see if it is on the left + for (Tuple leftTuple : symphony.leftShortRangeLinks) { + if (leftTuple.x.equals(updNode)) { + symphony.leftShortRangeLinks.remove(leftTuple); + symphony.leftShortRangeLinks.add(new Tuple(updNode, updStatus)); + + founded = true; + break; + } + } + + // If it isn't on the left i try with the neighbours on the right + if (!founded) { + for (Tuple rightTuple : symphony.rightShortRangeLinks) { + if (rightTuple.x.equals(updNode)) { + symphony.rightShortRangeLinks.remove(rightTuple); + symphony.rightShortRangeLinks.add(new Tuple(updNode, updStatus)); + + break; + } + } + + fixNeighbours(node, symphony.rightShortRangeLinks); + } else { + fixNeighbours(node, symphony.leftShortRangeLinks); + } + + checkBootstrapStatusAndAlert(node); + + if (msg.getType() == MessageType.UPDATE_STATUS) { + Message responseUpdStatus = new Message(symphony.loggedIntoNetwork, node, MessageType.UPDATE_STATUS_RESPONSE); + transport.send(node, updNode, responseUpdStatus, pid); + } + + break; + case REQUEST_LONG_RANGE_LINK: + MessageType responseType = MessageType.REJECT_LONG_RANGE_LINK; + if (symphony.longRangeLinksIncoming.size() < (2 * k)) { + boolean added = symphony.longRangeLinksIncoming.add(msg.getSourceNode()); + if (added) { + responseType = MessageType.ACCEPTED_LONG_RANGE_LINK; + } + } + Message responseLongLinkMsg = new Message(null, node, responseType); + transport.send(node, msg.getSourceNode(), responseLongLinkMsg, pid); + break; + case ACCEPTED_LONG_RANGE_LINK: + nLink = n; + symphony.longRangeLinksOutgoing.add(msg.getSourceNode()); + break; + case REJECT_LONG_RANGE_LINK: + if (currentAttempts > 0) { + currentAttempts--; + sendLongRangeLinkRequest(symphony, node); + } + break; + case DISCONNECT_LONG_RANGE_LINK: + symphony.longRangeLinksIncoming.remove(msg.getSourceNode()); + symphony.lookAheadMap.put(msg.getSourceNode(), null); + break; + case UNAVAILABLE_LONG_RANGE_LINK: + symphony.longRangeLinksOutgoing.remove(msg.getSourceNode()); + symphony.lookAheadMap.put(msg.getSourceNode(), null); + break; + case LEAVE: + Tuple foundedTuple = null; + + // Verify if the node that is leaving is a left neighbour + for (Tuple leftTuple : symphony.leftShortRangeLinks) { + if (leftTuple.x.equals(msg.getSourceNode())) { + collection = symphony.leftShortRangeLinks; + foundedTuple = leftTuple; + break; + } + } + + // Verify if the node that is leaving is a right neighbour + if (collection == null) { + for (Tuple rightTuple : symphony.rightShortRangeLinks) { + if (rightTuple.x.equals(msg.getSourceNode())) { + collection = symphony.rightShortRangeLinks; + foundedTuple = rightTuple; + break; + } + } + } + + // if i've found the neighbour i remove it and i add to myself its neighbours + if (collection != null) { + collection.addAll((Collection>) msg.getBody()); + collection.remove(foundedTuple); + fixNeighbours(node, collection); + + // Update status and ready to send an alert in case i'm out of the ring + checkBootstrapStatusAndAlert(node); + } + break; + case KEEP_ALIVE: + Set[] lookAheadSetArray = new LinkedHashSet[2]; + + /* + * Check if the contacting node is doing lookAhead and in case of affirmative answer + * i provide to it the long range link identifiers (according to my routing mode) + */ + if ((Boolean) msg.getBody()) { + int i = 0; + Iterable[] iterableArray; + if (symphony.bidirectionalRouting) { + iterableArray = new Iterable[]{symphony.longRangeLinksOutgoing, symphony.longRangeLinksIncoming}; + } else { + iterableArray = new Iterable[]{symphony.longRangeLinksOutgoing}; + } + + for (Iterable iterable : iterableArray) { + lookAheadSetArray[i] = new LinkedHashSet(); + Set lookAheadSet = lookAheadSetArray[i]; + Iterator it = iterable.iterator(); + while (it.hasNext()) { + Node longLinkNode = it.next(); + lookAheadSet.add(((SymphonyProtocol) longLinkNode.getProtocol(symphonyID)).getIdentifier()); + } + i++; + } + } + + transport.send(node, msg.getSourceNode(), new Message(lookAheadSetArray, node, MessageType.KEEP_ALIVE_RESPONSE), pid); + break; + case KEEP_ALIVE_RESPONSE: + // Reset the counter to 0 + keepAliveMap.put(msg.getSourceNode(), 0); + + if (symphony.lookAhead) { + symphony.lookAheadMap.put(msg.getSourceNode(), (Set[]) msg.getBody()); + } + + break; + } + } + + /** + * + * Update the status and communicate immediately to the neighbours if the node is gone out from + * the ring (and before it was inside) + * + * @param node + */ + private void checkBootstrapStatusAndAlert(Node node) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + BootstrapStatus beforeStatus = symphony.loggedIntoNetwork; + + checkBootstrapStatus(node); + + // Instead of waiting that the update happens periodically i do it now because i'm out of the ring and before i wasn't + if (symphony.loggedIntoNetwork != beforeStatus && !symphony.isBootstrapped()) { + updateBootstrapStatusNeighbours(node, true); + } + } + + private void fixNeighbours(Node node, Collection> neighbours) { + + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + + // Remove duplicates, remove that ones that are in an obsolete status + Collection> removedNeighbours = new LinkedHashSet>(); + for (Tuple tuple : neighbours) { + + // Remove myself from the neighbours list + if (tuple.x.equals(node)) { + removedNeighbours.add(tuple); + continue; + } + + EnumSet status = EnumSet.allOf(BootstrapStatus.class); + status.remove(tuple.y); + + for (BootstrapStatus opposite : status) { + Tuple oppositeNeighbour = new Tuple(tuple.x, opposite); + if (neighbours.contains(oppositeNeighbour)) { + if (tuple.y != BootstrapStatus.ONLINE) { + removedNeighbours.add(new Tuple(tuple.x, BootstrapStatus.OFFLINE)); + if (opposite == BootstrapStatus.ONLINE) { + removedNeighbours.add(new Tuple(tuple.x, BootstrapStatus.ONLINE_AND_ALL_NEIGHBOURS_OFFLINE)); + } + } + } + } + } + neighbours.removeAll(removedNeighbours); + + /* + * + * I count the neighbours that are in the ONLINE status but before i remove the ones that + * are gone in timeout during the keep-alive procedure because can be someone that is old + * but not remove from the exchanging views (UPDATE_NEIGHBOURS) procedure and are not + * effectively online. To do anyway the possibility to the node to join again i decrease its + * timeout value. This only if the node is ONLINE and so i'm really interested that it is ok + * for the routing. + * + */ + int onlineNeighbours = 0; + for (Tuple tuple : neighbours) { + + Integer value = keepAliveMap.get(tuple.x); + if (value != null && value >= nTimeout && tuple.y == BootstrapStatus.ONLINE) { + keepAliveMap.put(tuple.x, value - 1); + removedNeighbours.add(tuple); + } else { + + if (tuple.y == BootstrapStatus.ONLINE) { + onlineNeighbours++; + } + } + } + neighbours.removeAll(removedNeighbours); + + // Fix the neighbours number to the maximum allowed + SymphonyNodeComparator comparator = new SymphonyNodeComparator(symphonyID, node); + AdapterSymphonyNodeComparator adapterComparator = new AdapterSymphonyNodeComparator(comparator); + while (neighbours.size() > symphony.numberShortRangeLinksPerSide) { + Tuple distantTuple = Collections.max(neighbours, adapterComparator); + + // Mantain the link with the ring + if (distantTuple.y == BootstrapStatus.ONLINE) { + if (onlineNeighbours > 1) { + neighbours.remove(distantTuple); + onlineNeighbours--; + } else { + /* + * If will be only one neighbour that is online i save it and i'm going to + * eliminate another one (for sure it'll be not online) + * + */ + Tuple backupOnlineNeighbour = distantTuple; + neighbours.remove(backupOnlineNeighbour); + distantTuple = Collections.max(neighbours, adapterComparator); + neighbours.add(backupOnlineNeighbour); + neighbours.remove(distantTuple); + } + + } else { + neighbours.remove(distantTuple); + } + } + } + + @Override + public Object clone() { + SymphonyNetworkManager dolly = new SymphonyNetworkManager(prefix); + return dolly; + } + + public void nextCycle(Node node, int protocolID) { + + if (node.isUp()) { + + // Update the estimated network size + updateN(node); + + // Update the estimated K + updateK(node); + + // Update the bootstrap status of my neighbours that were joining the ring + updateBootstrapStatusNeighbours(node, false); + + // Refresh the neighbours views + refreshNeighbours(node); + + // I send and check the connection status of the neighbours + keepAlive(node); + + // Update the bootstrap status + checkBootstrapStatus(node); + + // If it's active i check the Relinking criteria + if (relinkingProtocolActivated) { + reLinkingProtocol(node); + } + + // Update the long range links (conservative) + updateLongRangeLinks(node); + } + } + + /** + * + * @param allNeighbours true, communicate/receive the status update from all the neighbours. + * false, communicate/receive the status update only from the neighbours that are NOT ONLINE + * + */ + private void updateBootstrapStatusNeighbours(Node node, boolean allNeighbours) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + Transport transport = (Transport) node.getProtocol(transportID); + + Collection> collection = new LinkedHashSet>(); + collection.addAll(symphony.leftShortRangeLinks); + collection.addAll(symphony.rightShortRangeLinks); + + for (Tuple neighbourTuple : collection) { + if (allNeighbours || neighbourTuple.y != BootstrapStatus.ONLINE) { + Message msg = new Message(symphony.loggedIntoNetwork, node, MessageType.UPDATE_STATUS); + transport.send(node, neighbourTuple.x, msg, pid); + } + } + } + + private void updateN(Node node) { + NetworkSizeEstimatorProtocolInterface networkEstimator = (NetworkSizeEstimatorProtocolInterface) node.getProtocol(networkEstimatorID); + n = networkEstimator.getNetworkSize(node); + if (n <= 0) { + n = DEFAULT_N; + } + } + + /** + * Update the K value with the current expectation of the network size + */ + private void updateK(Node node) { + + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + if (!symphony.fixedLongRangeLinks) { + k = (int) Math.ceil(Math.log(n) / Math.log(2)); + + if (k <= 0) { + k = DEFAULT_K; + } + } else { + k = symphony.numberFixedLongRangeLinks; + } + } + + private void refreshNeighbours(Node node) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + Transport transport = (Transport) node.getProtocol(transportID); + + for (Tuple leftTuple : symphony.leftShortRangeLinks) { + Node leftNode = leftTuple.x; + Message updateNeighbourMsg = new Message(symphony.loggedIntoNetwork, node, MessageType.UPDATE_NEIGHBOURS); + transport.send(node, leftNode, updateNeighbourMsg, pid); + } + + for (Tuple rightTuple : symphony.rightShortRangeLinks) { + Node rightNode = rightTuple.x; + Message updateNeighbourMsg = new Message(symphony.loggedIntoNetwork, node, MessageType.UPDATE_NEIGHBOURS); + transport.send(node, rightNode, updateNeighbourMsg, pid); + } + } + + /** + * Method to update the (connection) status of the node. Perform the update to the "up" so: + * OFFLINE -> ONLINE_AND_ALL_NEIGHBOURS_OFFLINE -> ONLINE + * + * and to the "down" only: ONLINE -> ONLINE_AND_ALL_NEIGHBOURS_OFFLINE + * + * @param node + */ + private void checkBootstrapStatus(Node node) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + + if (symphony.loggedIntoNetwork != BootstrapStatus.OFFLINE) { + + symphony.loggedIntoNetwork = BootstrapStatus.ONLINE_AND_ALL_NEIGHBOURS_OFFLINE; + + // Check if i'm inside the ring and i'm able to do routing + if (!symphony.leftShortRangeLinks.isEmpty() && !symphony.rightShortRangeLinks.isEmpty()) { + + boolean leftOk = false; + for (Tuple leftTuple : symphony.leftShortRangeLinks) { + if (leftTuple.y == BootstrapStatus.ONLINE) { + leftOk = true; + break; + } + } + + if (leftOk) { + for (Tuple rightTuple : symphony.rightShortRangeLinks) { + if (rightTuple.y == BootstrapStatus.ONLINE) { + symphony.loggedIntoNetwork = BootstrapStatus.ONLINE; + break; + } + } + } + } + } + } + + /** + * Remove the possible wrong entries from the lookAhead table + */ + private void fixLookAheadMap(Node node) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + for (Tuple tuple : symphony.leftShortRangeLinks) { + symphony.lookAheadMap.put(tuple.x, null); + } + for (Tuple tuple : symphony.rightShortRangeLinks) { + symphony.lookAheadMap.put(tuple.x, null); + } + } + + /** + * Sent keep-alive messages to verify if the links still online + * + * if enable the lookAhead mode i require the neighbours list from my neighbours (1-lookAhead). + * + * Note: I don't reuse the UPDATE_STATUS messages because i want to mantain separate the + * semantic and have more clear source code + */ + private void keepAlive(Node node) { + + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + Transport transport = (Transport) node.getProtocol(transportID); + + // Send and check for the long range links (both incoming and outgoing) + for (Iterable iterable : new Iterable[]{symphony.longRangeLinksOutgoing, symphony.longRangeLinksIncoming}) { + Iterator longLinksIterator = iterable.iterator(); + while (longLinksIterator.hasNext()) { + Node longLinkNode = longLinksIterator.next(); + Integer value = keepAliveMap.get(longLinkNode); + if (value == null) { + value = 0; + } + + /* + * Verify if i reached the sufficient time of sending and not receiving an answer + * and so i can consider that node as disconnected + */ + if (value >= nTimeout) { + symphony.lookAheadMap.put(longLinkNode, null); // Do it anyway if it's enabled the lookAhead or not + longLinksIterator.remove(); + } else { + keepAliveMap.put(longLinkNode, value + 1); + + Message keepAliveMsg = new Message(symphony.lookAhead, node, MessageType.KEEP_ALIVE); + transport.send(node, longLinkNode, keepAliveMsg, pid); + } + } + } + + // Send and check for the short links + for (Iterable> iterable : new Iterable[]{symphony.rightShortRangeLinks, symphony.leftShortRangeLinks}) { + Iterator> shortLinksIterator = iterable.iterator(); + while (shortLinksIterator.hasNext()) { + Node shortLinkNode = shortLinksIterator.next().x; + Integer value = keepAliveMap.get(shortLinkNode); + if (value == null) { + value = 0; + } + + // the same as above + if (value >= nTimeout) { + shortLinksIterator.remove(); + } else { + keepAliveMap.put(shortLinkNode, value + 1); + + // LookAhead is not to be performed to the short links! + Message keepAliveMsg = new Message(false, node, MessageType.KEEP_ALIVE); + transport.send(node, shortLinkNode, keepAliveMsg, pid); + } + } + } + } + + /** + * Implement the Re-Linking criteria of the Long Range Links. It does the complete refresh. The + * repopulation is done through the 'updateLongRangeLinks' method. + */ + private void reLinkingProtocol(Node node) { + // I do the check only if i succeed at least one time to create a long range link + if (nLink > 0) { + double criterionValue = n / nLink; + + if (!(criterionValue >= relinkingLowerBound && criterionValue <= relinkingUpperBound)) { + + /* + * Not explicitly precised in the paper: if i haven't created a new link i update + * anyway nLink because can happen a special case that i will not be able to create + * links because the reLinkingProtocol procedure is "faster". + */ + nLink = n; + + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + Transport transport = (Transport) node.getProtocol(transportID); + + // Communicate to the all Outgoing Long Range Links that they aren't anymore + for (Node longRangeLinkOutgoingNode : symphony.longRangeLinksOutgoing) { + Message disconnectMsg = new Message(null, node, MessageType.DISCONNECT_LONG_RANGE_LINK); + transport.send(node, longRangeLinkOutgoingNode, disconnectMsg, pid); + } + + symphony.longRangeLinksOutgoing.clear(); + } + } + } + + public int getK() { + return k; + } + + public int getN() { + return n; + } +} diff --git a/contrib/psg/src/example/symphony/SymphonyNodeComparator.java b/contrib/psg/src/example/symphony/SymphonyNodeComparator.java new file mode 100644 index 0000000000..e5a17d2b0d --- /dev/null +++ b/contrib/psg/src/example/symphony/SymphonyNodeComparator.java @@ -0,0 +1,51 @@ +package example.symphony; + +import java.util.Comparator; +import java.util.logging.Level; +import java.util.logging.Logger; +import peersim.core.Node; + +/** + * Comparator that measure the relative distance from a target node + * + * @author Andrea Esposito + */ +public class SymphonyNodeComparator implements Comparator { + + private final int symphonyID; + private double target; + + public SymphonyNodeComparator(int symphonyID) { + this.symphonyID = symphonyID; + } + + public SymphonyNodeComparator(int symphonyID, double target) { + this(symphonyID); + this.target = target; + } + + public SymphonyNodeComparator(int symphonyID, Node targetNode) { + this(symphonyID); + SymphonyProtocol symphony = (SymphonyProtocol) targetNode.getProtocol(symphonyID); + this.target = symphony.getIdentifier(); + } + + public int compare(Node o1, Node o2) { + + SymphonyProtocol symphony1 = (SymphonyProtocol) o1.getProtocol(symphonyID); + SymphonyProtocol symphony2 = (SymphonyProtocol) o2.getProtocol(symphonyID); + + Double identifier1 = symphony1.getIdentifier(); + Double identifier2 = symphony2.getIdentifier(); + + Double distance1 = Math.abs(target - identifier1) % 1; + Double distance2 = Math.abs(target - identifier2) % 1; + + identifier1 = Math.min(1.0 - distance1, distance1); + identifier2 = Math.min(1.0 - distance2, distance2); + + Logger.getLogger(SymphonyNodeComparator.class.getName()).log(Level.FINEST, "id1= " + symphony1.getIdentifier() + " target= " + target + " id2= " + symphony2.getIdentifier() + " res= " + identifier1.compareTo(identifier2)); + + return identifier1.compareTo(identifier2); + } +} diff --git a/contrib/psg/src/example/symphony/SymphonyNodeInizializer.java b/contrib/psg/src/example/symphony/SymphonyNodeInizializer.java new file mode 100644 index 0000000000..ec5dc685f3 --- /dev/null +++ b/contrib/psg/src/example/symphony/SymphonyNodeInizializer.java @@ -0,0 +1,59 @@ +package example.symphony; + +import java.util.logging.Level; +import java.util.logging.Logger; +import peersim.config.Configuration; +import peersim.core.Network; +import peersim.core.Node; +import peersim.dynamics.NodeInitializer; + +/** + * + * @author Andrea Esposito + */ +public class SymphonyNodeInizializer implements NodeInitializer { + + private static final String PAR_NETMANAGER = "symphonynetworkmanager"; + private static final String PAR_SYMPHONY = "symphony"; + private static final String PAR_BOOTNODE = "bootstrapnode"; + private final int networkManagerID; + private final int symphonyID; + private final int indexBootstrapNode; + + public SymphonyNodeInizializer(String prefix) { + + networkManagerID = Configuration.getPid(prefix + "." + PAR_NETMANAGER); + indexBootstrapNode = Configuration.getInt(prefix + "." + PAR_BOOTNODE, 0); + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY); + } + + @Override + public void initialize(Node node) { + int indexRealBootstrapNode = indexBootstrapNode; + Node realBootstrapNode = Network.get(indexBootstrapNode); + SymphonyNetworkManager symphonyNetworkManager = (SymphonyNetworkManager) node.getProtocol(networkManagerID); + SymphonyProtocol symphony = (SymphonyProtocol) realBootstrapNode.getProtocol(symphonyID); + + boolean joinSent; + do { + try { + while (!symphony.isBootstrapped() || !realBootstrapNode.isUp()) { + indexRealBootstrapNode = (indexRealBootstrapNode + 1) % Network.size(); + realBootstrapNode = Network.get(indexRealBootstrapNode); + symphony = (SymphonyProtocol) realBootstrapNode.getProtocol(symphonyID); + + if (indexRealBootstrapNode == indexBootstrapNode) { + Logger.getLogger(SymphonyNodeInizializer.class.getName()).log(Level.WARNING, "No node ONLINE. Impossible to do the network bootstrap."); + return; + } + } + + symphonyNetworkManager.join(node, realBootstrapNode); + joinSent = true; + } catch (RoutingException ex) { + Logger.getLogger(SymphonyNodeInizializer.class.getName()).log(Level.SEVERE, "Join Issue"); + joinSent = false; + } + } while (!joinSent); + } +} diff --git a/contrib/psg/src/example/symphony/SymphonyProtocol.java b/contrib/psg/src/example/symphony/SymphonyProtocol.java new file mode 100644 index 0000000000..478f81ef77 --- /dev/null +++ b/contrib/psg/src/example/symphony/SymphonyProtocol.java @@ -0,0 +1,530 @@ +package example.symphony; + +import java.lang.ref.SoftReference; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +import example.symphony.Message.MessageType; +import peersim.config.Configuration; +import peersim.core.CommonState; +import peersim.core.Node; +import peersim.edsim.EDProtocol; +import peersim.transport.Transport; + +/** + * + * @author Andrea Esposito + */ +public class SymphonyProtocol implements EDProtocol { + + private static final String PAR_SHORT_LINK = "shortlink"; + private static final String PAR_LONG_LINK = "longlink"; + private static final String PAR_TRANSP = "transport"; + private static final String PAR_ROUTING = "routing"; + private static final String PAR_LOOKAHEAD = "lookahead"; + private static Set allIdentifier = new HashSet(); + private final String prefix; + private static int pid; + private final int transportID; + private final double identifier; + public final int sequentialIdentifier; + private static int sequentialCounter = 0; + public final int numberShortRangeLinksPerSide; + public final boolean bidirectionalRouting; + public final boolean lookAhead; + public final boolean fixedLongRangeLinks; + public final int numberFixedLongRangeLinks; + public LinkedHashSet longRangeLinksOutgoing; + public LinkedHashSet longRangeLinksIncoming; + public LinkedHashSet> rightShortRangeLinks; + public LinkedHashSet> leftShortRangeLinks; + /** + * Array Contract: at position 0 -> OutgoingLongRangeLinks, 1 -> IncomingLongRangeLinks + */ + public final LinkedHashMap[]> lookAheadMap; + private HashMap mapHandler; + /** + * IDs Set to verify if there are cycles + */ + private Set messageHistoryID; + /** + * + * Tuple chronology that contains: + * + * I use SoftReference as a trade off between memory usage and accurancy + */ + private Set>> messageHistory; + private static boolean firstPrintConfig = true; + + public enum BootstrapStatus { + + NEW, OFFLINE, ONLINE_AND_ALL_NEIGHBOURS_OFFLINE, ONLINE + } + public BootstrapStatus loggedIntoNetwork; + + public SymphonyProtocol(String prefix) { + + this.prefix = prefix; + pid = Configuration.lookupPid(prefix.replaceAll("protocol.", "")); + transportID = Configuration.getPid(prefix + "." + PAR_TRANSP); + numberShortRangeLinksPerSide = Configuration.getInt(prefix + "." + PAR_SHORT_LINK, 2) / 2; + bidirectionalRouting = !Configuration.getString(prefix + "." + PAR_ROUTING, "bidirectional").toLowerCase().equals("unidirectional"); + lookAhead = !Configuration.getString(prefix + "." + PAR_LOOKAHEAD, "on").toLowerCase().equals("off"); + numberFixedLongRangeLinks = Configuration.getInt(prefix + "." + PAR_LONG_LINK, -1); + fixedLongRangeLinks = numberFixedLongRangeLinks >= 0; + + longRangeLinksOutgoing = new LinkedHashSet(); + longRangeLinksIncoming = new LinkedHashSet(); + rightShortRangeLinks = new LinkedHashSet>(); + leftShortRangeLinks = new LinkedHashSet>(); + lookAheadMap = new LinkedHashMap[]>(); + + identifier = generateUniqueIdentifier(); + sequentialIdentifier = sequentialCounter++; + + mapHandler = new HashMap(); + + messageHistoryID = new HashSet(); + messageHistory = new LinkedHashSet>>(); + loggedIntoNetwork = BootstrapStatus.NEW; + + printConfig(); + } + + private void printConfig() { + + if (firstPrintConfig) { + firstPrintConfig = false; + System.out.println(SymphonyProtocol.class.getSimpleName() + " Configuration:"); + System.out.println("- Number of short range links per side: " + numberShortRangeLinksPerSide); + System.out.println("- Number of long range links: " + (fixedLongRangeLinks ? numberFixedLongRangeLinks : "log(n)")); + System.out.println("- Routing mode: " + (bidirectionalRouting ? "Bidirectional" : "Unidirectional")); + System.out.println("- LookAhead status: " + (lookAhead ? "ON" : "OFF")); + System.out.println("-------------------------------\n"); + } + } + + /** + * + * Method to identify the next node that has to be contacted. It's going to be used the mode + * that is described into the configuration file + */ + public Node getCandidateForRouting(double identifierToRoute) throws RoutingException { + if (bidirectionalRouting) { + return getCandidateForBidirectionalRoute(identifierToRoute); + } else { + return getCandidateForUnidirectionalRoute(identifierToRoute); + } + } + + /** + * + * Method to individuate the next node that as to be contacted through Unidirectional Routing + * mode + */ + public Node getCandidateForUnidirectionalRoute(double identifierToRoute) throws RoutingException { + + LinkedHashSet allLinks = new LinkedHashSet(); + Node manager = putShortRangeLinksIntoContainerForRouting(allLinks, identifierToRoute); + + if (manager != null) { + return manager; + } + + allLinks.addAll(longRangeLinksOutgoing); + + return findClosestNode(identifierToRoute, allLinks, true); + } + + /** + * Method to individuate the next node that as to be contacted through Bidirectional Routing + * mode + */ + public Node getCandidateForBidirectionalRoute(double identifierToRoute) throws RoutingException { + + LinkedHashSet allLinks = new LinkedHashSet(); + Node manager = putShortRangeLinksIntoContainerForRouting(allLinks, identifierToRoute); + + if (manager != null) { + return manager; + } + + allLinks.addAll(longRangeLinksOutgoing); + allLinks.addAll(longRangeLinksIncoming); + + return findClosestNode(identifierToRoute, allLinks, false); + } + + /** + * @return Null if it is NOT found the manager. Node if it is found. + */ + private Node putShortRangeLinksIntoContainerForRouting(Set container, double identifierToRoute) { + for (Tuple rightTuple : rightShortRangeLinks) { + if (rightTuple.y == BootstrapStatus.ONLINE) { + container.add(rightTuple.x); + } + } + + if (!container.isEmpty()) { + + // Special case: i verify if the neighbour at my right (ONLINE) is the manager + SymphonyNodeComparator comparator = new SymphonyNodeComparator(pid, identifier); + Node nearRightNeighbour = Collections.min(container, comparator); + if (nearRightNeighbour != null) { + SymphonyProtocol symphony = (SymphonyProtocol) nearRightNeighbour.getProtocol(pid); + if (!isLeftNeighbour(identifier, identifierToRoute) && isLeftNeighbour(symphony.getIdentifier(), identifierToRoute)) { + return nearRightNeighbour; + } + } + } + + for (Tuple leftTuple : leftShortRangeLinks) { + if (leftTuple.y == BootstrapStatus.ONLINE) { + container.add(leftTuple.x); + } + } + + return null; + } + + /** + * + * Individuates effectively the next candidate for the routing. Checks if the lookahead is + * activated and in case of affirmative answer it's going to use that information. + * + * @param identifierToRoute Identifier to reach + * @param container Candidate Nodes Container + * @param clockwise true, does unidirectional routing. false, does bidirectional routing. + * @return The nearest node to reach identifierToRoute + * @throws RoutingException Throw in case no candidate is found + */ + public Node findClosestNode(final double identifierToRoute, final Iterable container, final boolean clockwise) throws RoutingException { + Node ret = null; + double min = Double.MAX_VALUE; + + for (Node node : container) { + SymphonyProtocol symphonyNodeContainer = (SymphonyProtocol) node.getProtocol(pid); + double realCandidateIdentifier = symphonyNodeContainer.getIdentifier(); + + Set candidateIdentifierSet = new LinkedHashSet(); + candidateIdentifierSet.add(realCandidateIdentifier); + + boolean lookAheadClockwise = true; + + /* + * + * If lookahead is activated add all the reachable identifiers. No checks are performed + * on the node type (short/long) because at maximum the map return null. + */ + if (lookAhead) { + Set[] lookAheadIdentifierSetArray = lookAheadMap.get(node); + + if (lookAheadIdentifierSetArray != null) { + Set lookAheadIdentifierSet = lookAheadIdentifierSetArray[0]; + + if (lookAheadIdentifierSet == null) { + lookAheadIdentifierSet = new LinkedHashSet(); + } + + /* + * + * If bidirectional routing is going to be performed so i put into account also + * the Incoming Long Range Links of the current neighbour + */ + if (bidirectionalRouting && lookAheadIdentifierSetArray[1] != null) { + lookAheadIdentifierSet.addAll(lookAheadIdentifierSetArray[1]); + lookAheadClockwise = false; + } + + if (!lookAheadIdentifierSet.isEmpty()) { + candidateIdentifierSet.addAll(lookAheadIdentifierSet); + } + } + } + + for (Double candidateIdentifier : candidateIdentifierSet) { + // if it is a my neighbour i use my routing mode instead if it is a looAhead one i use its routing mode + boolean currentClockwise = candidateIdentifier.equals(realCandidateIdentifier) ? clockwise : lookAheadClockwise; + + double distance = Math.abs(candidateIdentifier - identifierToRoute); + distance = Math.min(distance, 1.0 - distance); + + // if clockwise i have to exclude the case: candidateIdentifier - indentifierToRoute - identifier + if (currentClockwise) { + if (isLeftNeighbour(candidateIdentifier, identifierToRoute)) { + + // Special case (0.9 - 0.1) the normal order is not more meanful to decide the side + if (identifierToRoute >= candidateIdentifier) { + distance = identifierToRoute - candidateIdentifier; + } else { + distance = (1.0 - candidateIdentifier) + identifierToRoute; + } + } else { + distance = (1.0 - (candidateIdentifier - identifierToRoute)) % 1; + } + } + + /* + * + * Priority to the node that i'm directly connected and only after i use the + * lookAhead information + */ + if (min >= Math.abs(distance) + && (candidateIdentifier.equals(realCandidateIdentifier) + || ret == null + || min > Math.abs(distance))) { + ret = node; + min = Math.abs(distance); + } + } + } + + if (ret == null) { + throw new RoutingException("Impossible do routing. [Hit: Neighbour links (maybe) not yet online."); + } + + return ret; + } + + /** + * + * @param neighbourNode Neighbour Node + * @return true if the node is a left neighbour (or itself), false if it is a right one + */ + public static boolean isLeftNeighbour(Node rootNode, Node neighbourNode) { + SymphonyProtocol rootSymphony = (SymphonyProtocol) rootNode.getProtocol(pid); + SymphonyProtocol neighbourSymphony = (SymphonyProtocol) neighbourNode.getProtocol(pid); + + return isLeftNeighbour(rootSymphony.getIdentifier(), neighbourSymphony.getIdentifier()); + } + + public static boolean isLeftNeighbour(double rootIdentifier, double neighbourIdentifier) { + + // I calculate putting the hypotesis that i have to translate/"normalize", after i'll check if it was useless + double traslateRootIdentifier = (rootIdentifier + 0.5) % 1; + double traslateNeighbourIdentifier = (neighbourIdentifier + 0.5) % 1; + double distance = traslateNeighbourIdentifier - traslateRootIdentifier; + + // I verify if the neighbourIdentifier is over half ring, if yes i don't need to do the translation/"normalization" + if ((neighbourIdentifier + 0.5) != traslateNeighbourIdentifier) { + distance = neighbourIdentifier - rootIdentifier; + } + + return distance >= 0 && distance <= 0.5; + } + + public void route(Node src, double key, Handler handler) throws RoutingException { + + mapHandler.put(key, handler); + + Message msg = new Message(key, src, MessageType.ROUTE); + + Node targetNode = src; + + if (!isManagerOf(key)) { + targetNode = getCandidateForRouting(key); + Transport transport = (Transport) src.getProtocol(transportID); + transport.send(src, targetNode, msg, pid); + } + + // Insert the message into the chronology + Tuple historyTuple = new Tuple(); + try { + historyTuple.x = msg; + historyTuple.y = (Message) msg.clone(); + historyTuple.y.setCurrentHop(targetNode); + } catch (CloneNotSupportedException ex) { + Logger.getLogger(SymphonyProtocol.class.getName()).log(Level.SEVERE, "Impossible to clonate the message!"); + historyTuple.x = null; + historyTuple.y = msg; + msg.setCurrentHop(targetNode); + } + messageHistory.add(new SoftReference>(historyTuple)); + messageHistoryID.add(msg.getID()); + + /* + * + * If i am the manager (brutally through the reference), i don't do the loopback routing but + * i soddisfy immediately the request + */ + if (targetNode == src) { + + // Uppdate the chronology + historyTuple.y = new Message(key, targetNode, MessageType.ROUTE_RESPONSE); + + Tuple tuple = new Tuple(src, key); + mapHandler.remove(key); + handler.handle(this, tuple); + } + } + + public void processEvent(Node node, int pid, Object event) { + Message msg = (Message) event; + msg.incrementHop(); // I increment the message Hop + + Tuple historyTuple = new Tuple(); + try { + // I clone the message such a way to store into the chronology the hop sender's information + historyTuple.x = (Message) msg.clone(); + } catch (CloneNotSupportedException ex) { + Logger.getLogger(SymphonyProtocol.class.getName()).log(Level.SEVERE, "Impossible to clonate the message!"); + historyTuple.x = msg; + } + + messageHistory.add(new SoftReference>(historyTuple)); + + Double key; + Transport transport; + Handler handler; + + // Individuate cycles + if (messageHistoryID.contains(msg.getID())) { + Message responseMsg = new Message(msg, node, MessageType.ROUTE_FAIL); + + historyTuple.y = responseMsg; + + transport = (Transport) node.getProtocol(transportID); + transport.send(node, msg.getSourceNode(), responseMsg, pid); + return; + } + + /* + * If i'm arrived till here means that i'm not into a cycle --> i insert the message ID into + * the chronology + */ + messageHistoryID.add(msg.getID()); + + switch (msg.getType()) { + case ROUTE: + key = (Double) msg.getBody(); + Logger.getLogger(SymphonyProtocol.class.getName()).log(Level.FINEST, key + " " + identifier); + if (isManagerOf(key)) { + transport = (Transport) msg.getSourceNode().getProtocol(transportID); + Message responseMsg = new Message(new Tuple(node, key), node, MessageType.ROUTE_RESPONSE); + historyTuple.y = responseMsg; + transport.send(node, msg.getSourceNode(), responseMsg, pid); + } else { + try { + Node targetNode = getCandidateForRouting(key); + + try { + // I clone the message such a way to store the info (into the chronology) of the hop receiver + historyTuple.y = (Message) msg.clone(); + historyTuple.y.setCurrentHop(targetNode); + } catch (CloneNotSupportedException ex) { + Logger.getLogger(SymphonyProtocol.class.getName()).log(Level.SEVERE, "Impossible to clonate the message!"); + historyTuple.y = msg; + msg.setCurrentHop(targetNode); + } + + transport = (Transport) node.getProtocol(transportID); + transport.send(node, targetNode, msg, pid); + } catch (RoutingException ex) { + /* + * + * I send the same message to myself (it is going to queue into the event + * queue and in this way i "earn" time (postpone) and i hope that the + * network will be ok in the meanwhile) + */ + historyTuple.y = msg; + msg.setCurrentHop(node); + transport = (Transport) node.getProtocol(transportID); + transport.send(node, node, msg, pid); + } + } + break; + case ROUTE_RESPONSE: + Tuple tuple = (Tuple) msg.getBody(); + key = tuple.y; + handler = mapHandler.get(key); + mapHandler.remove(key); + handler.handle(this, tuple); + break; + case ROUTE_FAIL: + Message requestMsg = (Message) msg.getBody(); + key = (Double) requestMsg.getBody(); + handler = mapHandler.get(key); + mapHandler.remove(key); + handler.handle(this, null); + break; + } + } + + public boolean isManagerOf(double key) { + + if (key == identifier) { + return true; + } + + SymphonyNodeComparator comparator = new SymphonyNodeComparator(pid, identifier); + AdapterSymphonyNodeComparator adapterComparator = new AdapterSymphonyNodeComparator(comparator); + + Collection> leftShortRangeLinksCloned = (Collection>) leftShortRangeLinks.clone(); + Node targetNode = null; + + while (targetNode == null && !leftShortRangeLinksCloned.isEmpty()) { + Tuple nearTuple = Collections.min(leftShortRangeLinksCloned, adapterComparator); + if (nearTuple.y == BootstrapStatus.ONLINE) { + targetNode = nearTuple.x; + } else { + leftShortRangeLinksCloned.remove(nearTuple); + } + } + + // SPECIAL CASE: NO LEFT NEIGHBOURS. I became the Manager. + if (targetNode == null) { + return true; + } + + SymphonyProtocol symphony = (SymphonyProtocol) targetNode.getProtocol(pid); + // Check if it's the situation: right neighbour - key - me. So if i'm the manager or not. + boolean ret = isLeftNeighbour(identifier, key) && (!isLeftNeighbour(symphony.getIdentifier(), key) && symphony.getIdentifier() != key); + + return ret; + } + + public double getIdentifier() { + return identifier; + } + + public Tuple[] getHistoryMessage() { + SoftReference>[] array = messageHistory.toArray(new SoftReference[0]); + LinkedList> list = new LinkedList>(); + for (SoftReference> reference : array) { + Tuple tuple = reference.get(); + if (tuple != null) { + list.add(tuple); + } + } + return list.toArray(new Tuple[0]); + } + + public void clearHistoryMessage() { + messageHistory.clear(); + } + + private double generateUniqueIdentifier() { + boolean duplicated = true; + Double id = null; + + while (duplicated) { + id = CommonState.r.nextDouble(); + duplicated = allIdentifier.contains(id); + } + + allIdentifier.add(id); + + return id; + } + + @Override + public Object clone() { + SymphonyProtocol dolly = new SymphonyProtocol(prefix); + return dolly; + } + + public boolean isBootstrapped() { + return loggedIntoNetwork == BootstrapStatus.ONLINE; + } +} diff --git a/contrib/psg/src/example/symphony/SymphonyStatistics.java b/contrib/psg/src/example/symphony/SymphonyStatistics.java new file mode 100644 index 0000000000..4e97219654 --- /dev/null +++ b/contrib/psg/src/example/symphony/SymphonyStatistics.java @@ -0,0 +1,77 @@ +package example.symphony; + +import peersim.config.Configuration; +import peersim.core.Control; +import peersim.core.Network; +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class SymphonyStatistics implements Control { + + private static final String PAR_SYMPHONY = "symphony"; + private final int symphonyID; + private long totalMsg = 0; + private long numRouteResposeMsg = 0; + private long numRouteMsg = 0; + private long numRouteFailMsg = 0; + private long numRouteFoundManagerMsg = 0; + private double mediaHopRouteResponseMsg = 0.0; + + public SymphonyStatistics(String prefix) { + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY); + } + + public boolean execute() { + + AdapterIterableNetwork itNetwork = new AdapterIterableNetwork(); + for (Node node : itNetwork) { + SymphonyProtocol symphony = (SymphonyProtocol) node.getProtocol(symphonyID); + Tuple[] tupleMessages = symphony.getHistoryMessage(); + totalMsg += tupleMessages.length; + + for (Tuple tupleMessage : tupleMessages) { + + Message message = tupleMessage.x; + + if (message != null) { + switch (message.getType()) { + case ROUTE: + numRouteMsg++; + if (tupleMessage.y != null && tupleMessage.y.getType() == Message.MessageType.ROUTE_RESPONSE) { + numRouteFoundManagerMsg++; + mediaHopRouteResponseMsg = ((mediaHopRouteResponseMsg * (numRouteFoundManagerMsg - 1)) + message.getHop()) / (double) numRouteFoundManagerMsg; + } + break; + case ROUTE_FAIL: + numRouteFailMsg++; + break; + case ROUTE_RESPONSE: + numRouteResposeMsg++; + break; + } + } + } + + symphony.clearHistoryMessage(); + } + + printStatistics(); + + return false; + } + + private void printStatistics() { + System.out.println("### Statistics ###"); + System.out.println("- Total Messages: " + totalMsg); + System.out.println("- Total Route Messages: " + numRouteMsg); + System.out.println("- Found Manager Route Message: " + numRouteFoundManagerMsg); + System.out.println("- Response Message: " + numRouteResposeMsg); + System.out.println("- Fail Message: " + numRouteFailMsg); + System.out.println(); + System.out.println("Average Hop:" + mediaHopRouteResponseMsg + " Expected value (k = log n): " + (Math.log(Network.size()) / Math.log(2))); + System.out.println("### END ###\n"); + } +} diff --git a/contrib/psg/src/example/symphony/Tuple.java b/contrib/psg/src/example/symphony/Tuple.java new file mode 100644 index 0000000000..ed82b64a1a --- /dev/null +++ b/contrib/psg/src/example/symphony/Tuple.java @@ -0,0 +1,45 @@ +package example.symphony; + +/** + * + * @author Andrea Esposito + */ +public class Tuple { + + public X x; + public Y y; + + public Tuple() { + } + + public Tuple(X x, Y y) { + this.x = x; + this.y = y; + } + + @Override + public boolean equals(Object obj) { + + if (obj instanceof Tuple) { + Tuple tuple = (Tuple) obj; + + // (x != null && tuple.x != null) ==> (x==tuple.x || x.equals(tuple.x)) + // x == null <==> tuple.x == null + + boolean equalsX = (x == null && tuple.x == null) || ((x != null && tuple.x != null) && (x == tuple.x || x.equals(tuple.x))); + boolean equalsY = (y == null && tuple.y == null) || ((y != null && tuple.y != null) && (y == tuple.y || y.equals(tuple.y))); + + return equalsX && equalsY; + } + + return false; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 89 * hash + (this.x != null ? this.x.hashCode() : 0); + hash = 89 * hash + (this.y != null ? this.y.hashCode() : 0); + return hash; + } +} diff --git a/contrib/psg/src/example/symphony/test/NetworkEstimationTest.java b/contrib/psg/src/example/symphony/test/NetworkEstimationTest.java new file mode 100644 index 0000000000..1d0d945307 --- /dev/null +++ b/contrib/psg/src/example/symphony/test/NetworkEstimationTest.java @@ -0,0 +1,52 @@ +package example.symphony.test; + +import example.symphony.AdapterIterableNetwork; +import example.symphony.SymphonyNetworkManager; +import example.symphony.SymphonyProtocol; +import peersim.config.Configuration; +import peersim.core.Control; +import peersim.core.Node; + +/** + * + * @author Andrea Esposito + */ +public class NetworkEstimationTest implements Control { + + private static final String PAR_NETMANAGER = "symphonynetworkmanager"; + private static final String PAR_SYMPHONY = "symphony"; + private final int symphonyID; + private final int networkManagerID; + + public NetworkEstimationTest(String prefix) { + + networkManagerID = Configuration.getPid(prefix + "." + PAR_NETMANAGER); + symphonyID = Configuration.getPid(prefix + "." + PAR_SYMPHONY); + } + + public boolean execute() { + + AdapterIterableNetwork it = new AdapterIterableNetwork(); + int max = Integer.MIN_VALUE; + int min = Integer.MAX_VALUE; + int sum = 0; + int total = 0; + for (Node node : it) { + if (node.isUp() && ((SymphonyProtocol) node.getProtocol(symphonyID)).isBootstrapped()) { + SymphonyNetworkManager networkManager = (SymphonyNetworkManager) node.getProtocol(networkManagerID); + int n = networkManager.getN(); + min = n < min ? n : min; + max = n > max ? n : max; + sum += n; + total++; + } + } + + System.out.println("Real Dimension: " + (Math.log(total) / Math.log(2))); + System.out.println("Average Estimated Dimension: " + (total == 0 ? "No Node online" : (Math.log((sum / total)) / Math.log(2)))); + System.out.println("MAX: " + Math.log(max) / Math.log(2)); + System.out.println("MIN: " + Math.log(min) / Math.log(2)); + + return false; + } +} diff --git a/contrib/psg/src/peersim/Simulator.java b/contrib/psg/src/peersim/Simulator.java new file mode 100644 index 0000000000..97c7965150 --- /dev/null +++ b/contrib/psg/src/peersim/Simulator.java @@ -0,0 +1,270 @@ +/* + * 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: + *
    + *
  • {@link CDSimulator}: if {@link CDSimulator#isConfigurationCycleDriven} + * returns true
  • + *
  • {@link EDSimulator}: if {@link EDSimulator#isConfigurationEventDriven} + * returns true
  • + *
+ * 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. + *

+ * 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 + * args is simply passed to this class. This class is then used + * to initialize the configuration. + *

+ * After loading the configuration, the experiments are run by invoking the + * appropriate engine, which is identified as follows: + *

    + *
  • {@link CDSimulator}: if + * {@link CDSimulator#isConfigurationCycleDriven} returns true
  • + *
  • {@link EDSimulator}: if + * {@link EDSimulator#isConfigurationEventDriven} returns true
  • + *
+ *

+ * 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(); + } + } + + } + +} diff --git a/contrib/psg/src/peersim/cdsim/CDProtocol.java b/contrib/psg/src/peersim/cdsim/CDProtocol.java new file mode 100644 index 0000000000..c0d2f0de66 --- /dev/null +++ b/contrib/psg/src/peersim/cdsim/CDProtocol.java @@ -0,0 +1,44 @@ +/* + * 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); + +} diff --git a/contrib/psg/src/peersim/cdsim/CDSimulator.java b/contrib/psg/src/peersim/cdsim/CDSimulator.java new file mode 100644 index 0000000000..f4875949ee --- /dev/null +++ b/contrib/psg/src/peersim/cdsim/CDSimulator.java @@ -0,0 +1,221 @@ +/* + * 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. + *

+ * 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}. + *

+ * 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. + *

+ * 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(); + } +} + +} diff --git a/contrib/psg/src/peersim/cdsim/CDState.java b/contrib/psg/src/peersim/cdsim/CDState.java new file mode 100644 index 0000000000..ef52f78682 --- /dev/null +++ b/contrib/psg/src/peersim/cdsim/CDState.java @@ -0,0 +1,136 @@ +/* + * 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; +} +} + + diff --git a/contrib/psg/src/peersim/cdsim/DaemonProtocol.java b/contrib/psg/src/peersim/cdsim/DaemonProtocol.java new file mode 100644 index 0000000000..73f9a4eac4 --- /dev/null +++ b/contrib/psg/src/peersim/cdsim/DaemonProtocol.java @@ -0,0 +1,102 @@ +/* + * 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 +* 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. +*

+* 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. +*

+* 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. +*

+* 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 ;"); + } + String[] values = StringListParser.parseList(array[1]); + prop.setProperty(array[0], values[0]); + } +} + +} diff --git a/contrib/psg/src/peersim/config/ClassFinder.java b/contrib/psg/src/peersim/config/ClassFinder.java new file mode 100644 index 0000000000..b7cab6b326 --- /dev/null +++ b/contrib/psg/src/peersim/config/ClassFinder.java @@ -0,0 +1,258 @@ +/* + * 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 map = new TreeMap(); + +/** 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 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 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 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); + } +} +} diff --git a/contrib/psg/src/peersim/config/ConfigContainer.java b/contrib/psg/src/peersim/config/ConfigContainer.java new file mode 100644 index 0000000000..75f3cb6ad9 --- /dev/null +++ b/contrib/psg/src/peersim/config/ConfigContainer.java @@ -0,0 +1,1011 @@ +/* + * 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 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[] 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 map = new TreeMap(); + 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. + *

+ * 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 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 name. + * 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 name. + * 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 + * {@value peersim.config.Configuration#PAR_INCLUDE}+"."+name or + * {@value peersim.config.Configuration#PAR_ORDER}+"."+name 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 ll = new ArrayList(); + 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 names (e.g. + * initializers, controls and protocols) and a string specifying the type + * (prefix) of these. The output is in names, 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 names. Otherwise the configuration + * entry must contain entries from names. It is assumed + * that the entries in names contain only word characters + * (alphanumeric and underscore '_'. The order configuration entry thus + * contains a list of entries from names separated by any + * non-word characters. + *

+ * 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 wordLetterPairs(String str) +{ + ArrayList allPairs = new ArrayList(); + // 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; + } +} + +//------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/config/ConfigProperties.java b/contrib/psg/src/peersim/config/ConfigProperties.java new file mode 100644 index 0000000000..4f36892d14 --- /dev/null +++ b/contrib/psg/src/peersim/config/ConfigProperties.java @@ -0,0 +1,190 @@ +/* + * 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 resource is used to attempt +* loading default values from the given system resource. +* Then all Strings in pars are processed in the order they +* appear in the array. For pars[i], first a property file +* with the name pars[i] is attempted to be loaded. If the file +* does not exist or loading produces any other IOException, pars[i] +* is interpreted as a property definition, and it is set. +*

+* A little inconvenience is that if pars[i] 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. +*

+* 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 Properties.load 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 +* Properties.load 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()) ); +} +} + diff --git a/contrib/psg/src/peersim/config/Configuration.java b/contrib/psg/src/peersim/config/Configuration.java new file mode 100644 index 0000000000..4188111bef --- /dev/null +++ b/contrib/psg/src/peersim/config/Configuration.java @@ -0,0 +1,668 @@ +/* + * 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. + *

+ * The design of this class also hides the actual implementation of the + * configuration which can be Properties, XML, whatever. Currently only + * Properties is supported. + *

+ * 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. + *

+ * 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. + *

Typed reading of values

+ * 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. + *

Resolving class names

+ * + * 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. + * + *

Components and their ordering

+ * 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. + * + *

+ * 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, + * + *

+ *   control.conn ConnectivityObserver
+ *   control.1 WireKOut
+ *   control.2 PrintGraph
+ * 
+ * + * defines control components of names "conn","1" an "2" (arguments of the + * components not shown). When {@link #getNames} or + * {@link #getInstanceArray} are called, eg + * getNames("control"), then the order in which these are + * returned is alphabetical: + * ["control.1","control.2","control.conn"]. If you are not + * satisfied with lexicographic order, you can specify the order in this + * way. + * + *
+ *   order.control 1,conn,2
+ * 
+ * + * 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. + * + *
+ *   order.control 2
+ * 
+ * + * results in ["control.2","control.1","control.conn"]. + *

+ * 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 include. For example + * + *

+ *   include.control conn 2
+ * 
+ * + * will result in returning only control.conn and + * control.2, in this order. Note that for example the + * empty list results in a zero length array in this case. + * Important! If include is defined then ordering is ignored. + * That is, include is stronger than order. + *

Protocol names

+ * 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. + *

+ *

Expressions

+ * Numeric property values can be complex expressions, that are parsed + * using JEP. You can write + * expression using the syntax that you can find here. + * For example, + * + *
+ *   MAG 2
+ *   SIZE 2ˆMAG
+ * 
+ * + * SIZE=4. You can also have complex expression trees like this: + * + *
+ *   A B+C
+ *   B D+E
+ *   C E+F
+ *   D 1
+ *   E F
+ *   F 2
+ * 
+ * + * that results in A=7, B=3, C=4, D=1, E=2, F=2 + * + *

+ * 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. + * + *

+ * 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. + * + *

+ * 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 + * + *

+ *   overlay.size SIZE
+ *   SIZE SIZE-1
+ * 
+ * + * you get an error message: Parameter "overlay.size": Probable recursive + * definition - exceeded maximum depth {@value #DEFAULT_MAXDEPTH} + * + *

Debug

+ * + * It is possible to obtain debug information about the configuration + * properties by activating special configuration properties. + *

+ * 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)". + *

+ * 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. + *

+ * Each line printed by this debug feature is prefixed by the string + * "DEBUG". + * + *

Use of brackets

+ * + * 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. + *

+ * 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 name. + * 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 name. + * 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 + * {@value #PAR_INCLUDE}+"."+name or + * {@value #PAR_ORDER}+"."+name 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); +} + +} diff --git a/contrib/psg/src/peersim/config/FastConfig.java b/contrib/psg/src/peersim/config/FastConfig.java new file mode 100644 index 0000000000..d2eba1b174 --- /dev/null +++ b/contrib/psg/src/peersim/config/FastConfig.java @@ -0,0 +1,191 @@ +/* + * 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 + * Configuration 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 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 linkIndex-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 getLinkable(pid, 0). + */ +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]; +} + +} diff --git a/contrib/psg/src/peersim/config/IllegalParameterException.java b/contrib/psg/src/peersim/config/IllegalParameterException.java new file mode 100644 index 0000000000..1285350ff9 --- /dev/null +++ b/contrib/psg/src/peersim/config/IllegalParameterException.java @@ -0,0 +1,80 @@ +/* + * 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(); +} + +} + diff --git a/contrib/psg/src/peersim/config/MissingParameterException.java b/contrib/psg/src/peersim/config/MissingParameterException.java new file mode 100644 index 0000000000..58b85cde60 --- /dev/null +++ b/contrib/psg/src/peersim/config/MissingParameterException.java @@ -0,0 +1,77 @@ +/* + * 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(); +} + +} diff --git a/contrib/psg/src/peersim/config/NullPrintStream.java b/contrib/psg/src/peersim/config/NullPrintStream.java new file mode 100644 index 0000000000..612492265d --- /dev/null +++ b/contrib/psg/src/peersim/config/NullPrintStream.java @@ -0,0 +1,62 @@ +/* + * 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() +{ +} + +} diff --git a/contrib/psg/src/peersim/config/Operators.java b/contrib/psg/src/peersim/config/Operators.java new file mode 100644 index 0000000000..1d5d523f31 --- /dev/null +++ b/contrib/psg/src/peersim/config/Operators.java @@ -0,0 +1,163 @@ +/* + * 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.math.*; +import org.lsmp.djep.groupJep.groups.*; +import org.lsmp.djep.groupJep.interfaces.*; + +/** + * This class implements the Group interface of JEP, + * enabling the configuration system to read integers with arbitrary + * length. + */ +public class Operators extends Group implements IntegralDomainI,HasDivI, + OrderedSetI,HasModI,HasPowerI { + + + /** + * Operations on the reals (Implemented as BigInteger). + */ + public Operators() { + } + + public Number getZERO() { + return BigInteger.ZERO; + } + + public Number getONE() { + return BigInteger.ONE; + } + + public Number getInverse(Number num) { + if (num instanceof BigInteger) { + BigInteger a = (BigInteger) num; + return a.negate(); + } else { + return -num.doubleValue(); + } + } + + public Number add(Number num1, Number num2) { + if (num1 instanceof Double || num2 instanceof Double) { + // One is double + return num1.doubleValue() + num2.doubleValue(); + } else { + // Both integer + BigInteger a = (BigInteger) num1; + BigInteger b = (BigInteger) num2; + return a.add(b); + } + } + + public Number sub(Number num1, Number num2) { + if (num1 instanceof Double || num2 instanceof Double) { + // One is double + return num1.doubleValue() - num2.doubleValue(); + } else { + // Both integer + BigInteger a = (BigInteger) num1; + BigInteger b = (BigInteger) num2; + return a.subtract(b); + } + } + + public Number mul(Number num1, Number num2) { + if (num1 instanceof Double || num2 instanceof Double) { + // One is double + return num1.doubleValue() * num2.doubleValue(); + } else { + // Both integer + BigInteger a = (BigInteger) num1; + BigInteger b = (BigInteger) num2; + return a.multiply(b); + } + } + + public Number div(Number num1, Number num2) { + if (num1 instanceof Double || num2 instanceof Double) { + // One is double + return num1.doubleValue() / num2.doubleValue(); + } else { + // Both integer + BigInteger a = (BigInteger) num1; + BigInteger b = (BigInteger) num2; + return a.divide(b); + } + } + + + public Number mod(Number num1, Number num2) { + if (num1 instanceof Double || num2 instanceof Double) { + // One is double + return num1.doubleValue() % num2.doubleValue(); + } else { + // Both integer + BigInteger a = (BigInteger) num1; + BigInteger b = (BigInteger) num2; + return a.remainder(b); + } + } + + public Number pow(Number num1, Number num2) { + if (num1 instanceof Double || num2 instanceof Double) { + // One is double + return Math.pow(num1.doubleValue(), num2.doubleValue()); + } else { + // Both integer + BigInteger a = (BigInteger) num1; + BigInteger b = (BigInteger) num2; + return a.pow(b.intValue()); + } + } + + public boolean equals(Number num1, Number num2) { + if (num1 instanceof Double || num2 instanceof Double) { + // One is double + return num1.doubleValue() == num2.doubleValue(); + } else { + // Both integer + BigInteger a = (BigInteger) num1; + BigInteger b = (BigInteger) num2; + return a.equals(b); + } + } + + public int compare(Number num1,Number num2) { + if (num1 instanceof Double || num2 instanceof Double) { + // One is double + double n1 = num1.doubleValue(); + double n2 = num2.doubleValue(); + return (n1 < n2 ? -1 : (n1 == n2 ? 0 : 1)); + } else { + // Both integer + BigInteger a = (BigInteger) num1; + BigInteger b = (BigInteger) num2; + return a.compareTo(b); + } + } + + public Number valueOf(String str) { + try { + return new BigInteger(str); + } catch (NumberFormatException e) { + return new Double(str); + } + } + + public String toString() { return ""; } +} diff --git a/contrib/psg/src/peersim/config/ParsedProperties.java b/contrib/psg/src/peersim/config/ParsedProperties.java new file mode 100644 index 0000000000..9a58e4114d --- /dev/null +++ b/contrib/psg/src/peersim/config/ParsedProperties.java @@ -0,0 +1,254 @@ +/* + * 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 Properties.load +* 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 { and }. + + 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: +

+  control.degree GraphObserver 
+  {
+    protocol newscast
+    undir
+  }
+  
+ is equivalent to the definition of these three properties: +
+  control.degree GraphObserver 
+  control.degree.protocol newscast
+  control.degree.undir
+  
+ + 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: +
+	control.1 DynamicNetwork
+	{
+	  add CRASH
+	  substitute
+	  init.0 WireKOut 
+	  {
+	    degree DEGREE
+	    protocol 0
+	  }
+	}
+  
+ defines the following properties: +
+	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
+  
+ +

+ 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 prefixes = new HashSet(); + + 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); +} +*/ +} + diff --git a/contrib/psg/src/peersim/core/Cleanable.java b/contrib/psg/src/peersim/core/Cleanable.java new file mode 100644 index 0000000000..ac14f2163f --- /dev/null +++ b/contrib/psg/src/peersim/core/Cleanable.java @@ -0,0 +1,48 @@ +/* + * 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(); + +} diff --git a/contrib/psg/src/peersim/core/CommonState.java b/contrib/psg/src/peersim/core/CommonState.java new file mode 100644 index 0000000000..93b6e21840 --- /dev/null +++ b/contrib/psg/src/peersim/core/CommonState.java @@ -0,0 +1,301 @@ +/* + * 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. + *

+ * The set methods should not be used by applications, + * 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. + *

    + *
  • {@link #PHASE_UNKNOWN} phase is unknown
  • + *
  • {@link #POST_SIMULATION} the simulation is completed
  • + *
+ */ +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()); +} +*/ +} + diff --git a/contrib/psg/src/peersim/core/Control.java b/contrib/psg/src/peersim/core/Control.java new file mode 100644 index 0000000000..7644aa9df0 --- /dev/null +++ b/contrib/psg/src/peersim/core/Control.java @@ -0,0 +1,36 @@ +/* + * 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(); + +} diff --git a/contrib/psg/src/peersim/core/Fallible.java b/contrib/psg/src/peersim/core/Fallible.java new file mode 100644 index 0000000000..089cffef62 --- /dev/null +++ b/contrib/psg/src/peersim/core/Fallible.java @@ -0,0 +1,69 @@ +/* + * 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 + * getFailState()==OK + */ + public boolean isUp(); +} + + diff --git a/contrib/psg/src/peersim/core/GeneralNode.java b/contrib/psg/src/peersim/core/GeneralNode.java new file mode 100644 index 0000000000..2f2c02eb3c --- /dev/null +++ b/contrib/psg/src/peersim/core/GeneralNode.java @@ -0,0 +1,192 @@ +/* + * 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 +* prefix 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(int)getID(). */ +public int hashCode() { return (int)getID(); } + +} + + diff --git a/contrib/psg/src/peersim/core/IdleProtocol.java b/contrib/psg/src/peersim/core/IdleProtocol.java new file mode 100644 index 0000000000..dd602d5b6d --- /dev/null +++ b/contrib/psg/src/peersim/core/IdleProtocol.java @@ -0,0 +1,160 @@ +/* + * 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; +} + +} diff --git a/contrib/psg/src/peersim/core/Linkable.java b/contrib/psg/src/peersim/core/Linkable.java new file mode 100644 index 0000000000..962b427c26 --- /dev/null +++ b/contrib/psg/src/peersim/core/Linkable.java @@ -0,0 +1,80 @@ +/* + * 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). +*

+* 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(); +} + diff --git a/contrib/psg/src/peersim/core/MaliciousProtocol.java b/contrib/psg/src/peersim/core/MaliciousProtocol.java new file mode 100644 index 0000000000..26ed74f126 --- /dev/null +++ b/contrib/psg/src/peersim/core/MaliciousProtocol.java @@ -0,0 +1,31 @@ +/* + * 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 +{ +} + diff --git a/contrib/psg/src/peersim/core/ModifiableNode.java b/contrib/psg/src/peersim/core/ModifiableNode.java new file mode 100644 index 0000000000..7ce80a3acd --- /dev/null +++ b/contrib/psg/src/peersim/core/ModifiableNode.java @@ -0,0 +1,51 @@ +/* + * 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; +} + +} diff --git a/contrib/psg/src/peersim/core/Network.java b/contrib/psg/src/peersim/core/Network.java new file mode 100644 index 0000000000..da9c54531e --- /dev/null +++ b/contrib/psg/src/peersim/core/Network.java @@ -0,0 +1,327 @@ +/* + * 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. + *

+ * 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}. + *

+ * 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 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); + } + } + +} diff --git a/contrib/psg/src/peersim/core/Node.java b/contrib/psg/src/peersim/core/Node.java new file mode 100644 index 0000000000..a64e3a23b9 --- /dev/null +++ b/contrib/psg/src/peersim/core/Node.java @@ -0,0 +1,83 @@ +/* + * 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 i-th protocol in this node. If i + * 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 + * Network.get(n.getIndex()) 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 hashCode() 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 + * throws clause. + */ +public Object clone(); + +} diff --git a/contrib/psg/src/peersim/core/OracleIdleProtocol.java b/contrib/psg/src/peersim/core/OracleIdleProtocol.java new file mode 100644 index 0000000000..4d1896ff15 --- /dev/null +++ b/contrib/psg/src/peersim/core/OracleIdleProtocol.java @@ -0,0 +1,103 @@ +/* + * 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 this 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]"; +} + +} + diff --git a/contrib/psg/src/peersim/core/OverlayGraph.java b/contrib/psg/src/peersim/core/OverlayGraph.java new file mode 100644 index 0000000000..000945917c --- /dev/null +++ b/contrib/psg/src/peersim/core/OverlayGraph.java @@ -0,0 +1,221 @@ +/* + * 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 getNeighbours(int i) { + + Linkable lble=(Linkable)Network.node[i].getProtocol(protocolID); + ArrayList al = new ArrayList(lble.degree()); + if( Network.node[i].isUp() ) + { + for(int j=0; jNetwork.node[i] */ +public Object getNode(int i) { return Network.node[i]; } + +// --------------------------------------------------------------- + +/** +* Returns null always +*/ +public Object getEdge(int i, int j) { return null; } + +// --------------------------------------------------------------- + +/** Returns Network.size() */ +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. +* +*

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. +* +*

+* 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; jIn this simple implementation the valid times will be +* from, from+step, from+2*step, etc, +* where the last element is strictly less than until. +* Alternatively, if at 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 Long.MAX_VALUE. +* @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 +* prefix. {@value #PAR_STEP} defaults to 1. +*/ +public Scheduler(String prefix) { + + this(prefix, true); +} + +// ------------------------------------------------------------------ + +/** Reads configuration parameters from the component defined by +* prefix. 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; +} + +} + + diff --git a/contrib/psg/src/peersim/dynamics/DynamicNetwork.java b/contrib/psg/src/peersim/dynamics/DynamicNetwork.java new file mode 100644 index 0000000000..a581a86ffe --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/DynamicNetwork.java @@ -0,0 +1,229 @@ +/* + * 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: + * + *

+	 * control.0 DynamicNetwork
+	 * control.0.init.0 RandNI
+	 * control.0.init.0.k 5
+	 * control.0.init.0.protocol somelinkable
+	 * ...
+	 * 
+ * + * @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 add*networksize. 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 random nodes + * permanently 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; + } + + // -------------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/dynamics/MethodInvoker.java b/contrib/psg/src/peersim/dynamics/MethodInvoker.java new file mode 100644 index 0000000000..5e9e5fce73 --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/MethodInvoker.java @@ -0,0 +1,204 @@ +/* + * 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. + *

+ * 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 pids = new ArrayList(); + ArrayList methods = new ArrayList(); + for(int i=0; i list = new ArrayList(); + 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; ibefore inserting the node into the network. + */ +public void initialize(Node n); + +} diff --git a/contrib/psg/src/peersim/dynamics/OscillatingNetwork.java b/contrib/psg/src/peersim/dynamics/OscillatingNetwork.java new file mode 100644 index 0000000000..4c10f2d59a --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/OscillatingNetwork.java @@ -0,0 +1,200 @@ +/* + * 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 + * avg+sin(time*pi/{@value #PAR_PERIOD})*ampl where + * avg=({@value #PAR_MAX}+{@value #PAR_MIN})/2 and + * ampl=({@value #PAR_MAX}-{@value #PAR_MIN})/2. + * 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: + *

+control.0 DynamicNetwork
+control.0.init.0 RandNI
+control.0.init.0.k 5
+control.0.init.0.protocol somelinkable
+...
+ * 
+ * @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 + * avg+sin(time*pi/{@value #PAR_PERIOD})*ampl where + * avg=({@value #PAR_MAX}+{@value #PAR_MIN})/2 and + * ampl=({@value #PAR_MAX}-{@value #PAR_MIN})/2. + * @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 random + * nodes permanently 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 + * avg+sin(time*pi/{@value #PAR_PERIOD})*ampl where + * avg=({@value #PAR_MAX}+{@value #PAR_MIN})/2 and + * ampl=({@value #PAR_MAX}-{@value #PAR_MIN})/2. + * 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; +} + +} diff --git a/contrib/psg/src/peersim/dynamics/RandNI.java b/contrib/psg/src/peersim/dynamics/RandNI.java new file mode 100644 index 0000000000..7a4d5c749c --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/RandNI.java @@ -0,0 +1,114 @@ +/* + * 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(); +} + +} + diff --git a/contrib/psg/src/peersim/dynamics/StarNI.java b/contrib/psg/src/peersim/dynamics/StarNI.java new file mode 100644 index 0000000000..4dbd8664df --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/StarNI.java @@ -0,0 +1,104 @@ +/* + * 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 +
  • It MUST be static
  • +
  • It MUST have a first argument that can be assigned from a class + that implements {@link Graph}.
  • +
  • It MAY contain zero or more arguments following the first one.
  • +
  • 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 + java.util.Random
  • + + The arguments are initialized using the configuration as follows. +
      +
    • The first argument is the {@link Graph} that is passed to + {@link #wire}.
    • +
    • 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. +
    • If the last argument can be assigned from + java.util.Random then it is initialized with + {@link CommonState#r}, the central source of randomness for the + simulator.
    • +
    + For example, the class {@link WireWS} can be emulated by configuring +
    + init.0 WireByMethod
    + init.0.class GraphFactory
    + init.0.method wireWS
    + init.0.arg1 10
    + init.0.arg2 0.1
    + ...
    + 
    + 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 wire. +* @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 list = new ArrayList(); + 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= Network.size() ) + { + wasOutOfRange = true; + continue; + } + + for(int i=0; i= 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); +} +} + +} diff --git a/contrib/psg/src/peersim/dynamics/WireGraph.java b/contrib/psg/src/peersim/dynamics/WireGraph.java new file mode 100644 index 0000000000..4c8af15794 --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/WireGraph.java @@ -0,0 +1,158 @@ +/* + * 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); + +} + diff --git a/contrib/psg/src/peersim/dynamics/WireKOut.java b/contrib/psg/src/peersim/dynamics/WireKOut.java new file mode 100644 index 0000000000..927ddbd1ea --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/WireKOut.java @@ -0,0 +1,82 @@ +/* + * 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); +} + +} diff --git a/contrib/psg/src/peersim/dynamics/WireRegRootedTree.java b/contrib/psg/src/peersim/dynamics/WireRegRootedTree.java new file mode 100644 index 0000000000..e8e095231b --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/WireRegRootedTree.java @@ -0,0 +1,77 @@ +/* + * 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); +} + +} diff --git a/contrib/psg/src/peersim/dynamics/WireRingLattice.java b/contrib/psg/src/peersim/dynamics/WireRingLattice.java new file mode 100644 index 0000000000..a23822ed7f --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/WireRingLattice.java @@ -0,0 +1,80 @@ +/* + * 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); +} + +//-------------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/dynamics/WireScaleFreeBA.java b/contrib/psg/src/peersim/dynamics/WireScaleFreeBA.java new file mode 100644 index 0000000000..3b6ca30cad --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/WireScaleFreeBA.java @@ -0,0 +1,84 @@ +/* + * 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 +* http://arxiv.org/abs/cond-mat/0106096. It also contains the option of building +* a directed network, in which case the model is a variation of the BA model +* described in +http://arxiv.org/pdf/cond-mat/0408391. 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 ); +} + +} + diff --git a/contrib/psg/src/peersim/dynamics/WireScaleFreeDM.java b/contrib/psg/src/peersim/dynamics/WireScaleFreeDM.java new file mode 100644 index 0000000000..06ae97343e --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/WireScaleFreeDM.java @@ -0,0 +1,126 @@ +/* + * 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 + * this paper. + * 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 + * this paper. + * 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++; + } +} + +} diff --git a/contrib/psg/src/peersim/dynamics/WireStar.java b/contrib/psg/src/peersim/dynamics/WireStar.java new file mode 100644 index 0000000000..0f11b64c77 --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/WireStar.java @@ -0,0 +1,54 @@ +/* + * 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); +} + + +} + diff --git a/contrib/psg/src/peersim/dynamics/WireWS.java b/contrib/psg/src/peersim/dynamics/WireWS.java new file mode 100644 index 0000000000..65cefed685 --- /dev/null +++ b/contrib/psg/src/peersim/dynamics/WireWS.java @@ -0,0 +1,92 @@ +/* + * 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); +} + +} + diff --git a/contrib/psg/src/peersim/edsim/CDScheduler.java b/contrib/psg/src/peersim/edsim/CDScheduler.java new file mode 100644 index 0000000000..f864ff55b5 --- /dev/null +++ b/contrib/psg/src/peersim/edsim/CDScheduler.java @@ -0,0 +1,213 @@ +/* + * 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}). + * + *

    + * 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. + *

    + * 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; + } +} diff --git a/contrib/psg/src/peersim/edsim/ControlEvent.java b/contrib/psg/src/peersim/edsim/ControlEvent.java new file mode 100644 index 0000000000..8c12d3a330 --- /dev/null +++ b/contrib/psg/src/peersim/edsim/ControlEvent.java @@ -0,0 +1,89 @@ +/* + * 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; +} + +} + + diff --git a/contrib/psg/src/peersim/edsim/EDProtocol.java b/contrib/psg/src/peersim/edsim/EDProtocol.java new file mode 100644 index 0000000000..60c83a2e45 --- /dev/null +++ b/contrib/psg/src/peersim/edsim/EDProtocol.java @@ -0,0 +1,47 @@ +/* + * 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 ); + +} + diff --git a/contrib/psg/src/peersim/edsim/EDSimulator.java b/contrib/psg/src/peersim/edsim/EDSimulator.java new file mode 100644 index 0000000000..e56d021833 --- /dev/null +++ b/contrib/psg/src/peersim/edsim/EDSimulator.java @@ -0,0 +1,400 @@ +/* + * 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}. + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * {@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. + *

    + * 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); + } + } + +} diff --git a/contrib/psg/src/peersim/edsim/Heap.java b/contrib/psg/src/peersim/edsim/Heap.java new file mode 100644 index 0000000000..eb123acd82 --- /dev/null +++ b/contrib/psg/src/peersim/edsim/Heap.java @@ -0,0 +1,391 @@ +/* + * 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 + * 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 + * super(n). + */ + public NextCycleEvent(String n) { + } + + // -------------------------------------------------------------------- + + /** + * Returns a clone of the object. Overriding this method is necessary and + * typically is as simple as return super.clone(). In general, + * always use super.clone() 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; + } + +} diff --git a/contrib/psg/src/peersim/edsim/PriorityQ.java b/contrib/psg/src/peersim/edsim/PriorityQ.java new file mode 100644 index 0000000000..5525768485 --- /dev/null +++ b/contrib/psg/src/peersim/edsim/PriorityQ.java @@ -0,0 +1,102 @@ +/* + * 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 maxPriority()+1 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; } +} + +} diff --git a/contrib/psg/src/peersim/edsim/RandNextCycle.java b/contrib/psg/src/peersim/edsim/RandNextCycle.java new file mode 100644 index 0000000000..b74a4f409a --- /dev/null +++ b/contrib/psg/src/peersim/edsim/RandNextCycle.java @@ -0,0 +1,68 @@ +/* + * 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*step (exclusive) +* (expected value is therefore step). +*/ +protected long nextDelay(long step) { + + return 1+CommonState.r.nextLong((step<<1)-1); +} + + +} + + diff --git a/contrib/psg/src/peersim/edsim/RegRandNextCycle.java b/contrib/psg/src/peersim/edsim/RegRandNextCycle.java new file mode 100644 index 0000000000..dbc5edb675 --- /dev/null +++ b/contrib/psg/src/peersim/edsim/RegRandNextCycle.java @@ -0,0 +1,98 @@ +/* + * 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 step 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 step. 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; +} + +} + + diff --git a/contrib/psg/src/peersim/edsim/edsim_jsp.xmi b/contrib/psg/src/peersim/edsim/edsim_jsp.xmi new file mode 100644 index 0000000000..5fba52dfcc --- /dev/null +++ b/contrib/psg/src/peersim/edsim/edsim_jsp.xmi @@ -0,0 +1,2 @@ + + diff --git a/contrib/psg/src/peersim/edsim/edsim_kdm.xmi b/contrib/psg/src/peersim/edsim/edsim_kdm.xmi new file mode 100644 index 0000000000..db34498391 --- /dev/null +++ b/contrib/psg/src/peersim/edsim/edsim_kdm.xmi @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/contrib/psg/src/peersim/graph/BitMatrixGraph.java b/contrib/psg/src/peersim/graph/BitMatrixGraph.java new file mode 100644 index 0000000000..7e88fecd9d --- /dev/null +++ b/contrib/psg/src/peersim/graph/BitMatrixGraph.java @@ -0,0 +1,160 @@ +/* + * 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 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(n); + for(int i=0; i getNeighbours(int i) { + + Set result = new HashSet(); + BitSet neighb = sets.get(i); + final int max = size(); + for(int j=0; j 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 +} + +} + diff --git a/contrib/psg/src/peersim/graph/ConstUndirGraph.java b/contrib/psg/src/peersim/graph/ConstUndirGraph.java new file mode 100644 index 0000000000..3aac06fdf9 --- /dev/null +++ b/contrib/psg/src/peersim/graph/ConstUndirGraph.java @@ -0,0 +1,185 @@ +/* + * 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[] 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(); + for(int i=0; i getNeighbours(int i) { + + List result = new ArrayList(); + 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(); + triangle[i] = new BitSet(i); + } + + for(int i=0; ij) // 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 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); +} diff --git a/contrib/psg/src/peersim/graph/GraphAlgorithms.java b/contrib/psg/src/peersim/graph/GraphAlgorithms.java new file mode 100644 index 0000000000..6bbb3516ea --- /dev/null +++ b/contrib/psg/src/peersim/graph/GraphAlgorithms.java @@ -0,0 +1,379 @@ +/* + * 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 stack = new Stack(); +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 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, d[i] 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 d[i] is kept, +* whatever that was. +* d must either be long enough or null. +*/ +private void bfs( int from ) { + + List q = new LinkedList(); + 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]](); + if( color==null || color.length ht = new Hashtable(); + for(j=0; j{@link #d}[j] 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.lengthb[i]. +* The number of cycles performed is determined by b.length. +* 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 neighbours=null; + int black=1; + + int k=0; + for(; k 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(; kb[i] contains the number of nodes +* reached in exactly i steps, and always b[0]=1. +* If the maximal distance from k is lower than b.length, +* 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= 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.length1): visited as the c-th node + // color is negative (c<1): inComponent true + for(int i=0; i ht = new Hashtable(); + for(int j=0; j0) + { + ht.put(j,color[j]); + } + } + + return ht; +} + +} + diff --git a/contrib/psg/src/peersim/graph/GraphFactory.java b/contrib/psg/src/peersim/graph/GraphFactory.java new file mode 100644 index 0000000000..81857cc0e0 --- /dev/null +++ b/contrib/psg/src/peersim/graph/GraphFactory.java @@ -0,0 +1,300 @@ +/* + * 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 +* 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= 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; i0) + { + int j = i^mask; + if(j> 1; + } + + } + return g; +} + +// ------------------------------------------------------------------- + +/** +* This contains the implementation of the Barabasi-Albert model +* of growing scale free networks. The original model is described in +* +http://arxiv.org/abs/cond-mat/0106096. +* It also works if the graph is directed, in which case the model is a +* variation of the BA model +* described in +http://arxiv.org/pdf/cond-mat/0408391. 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 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 +*

  • 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.
  • +* +* @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 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 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> 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(1000); + neighbors = new ArrayList>(1000); + nodeindex = new HashMap(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>(size); + for(int i=0; i()); + 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()); + 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 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(); } +} + + + + diff --git a/contrib/psg/src/peersim/graph/PrefixSubGraph.java b/contrib/psg/src/peersim/graph/PrefixSubGraph.java new file mode 100644 index 0000000000..5098316eea --- /dev/null +++ b/contrib/psg/src/peersim/graph/PrefixSubGraph.java @@ -0,0 +1,158 @@ +/* + * 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 getNeighbours(int i) { + + if( i<0 || i>=prefSize ) throw new IndexOutOfBoundsException(); + + List result = new LinkedList(); + 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; +} +} + diff --git a/contrib/psg/src/peersim/graph/SubGraphEdges.java b/contrib/psg/src/peersim/graph/SubGraphEdges.java new file mode 100644 index 0000000000..bb4201fbf8 --- /dev/null +++ b/contrib/psg/src/peersim/graph/SubGraphEdges.java @@ -0,0 +1,177 @@ +/* + * 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 getNeighbours(int i) { + + List result = new LinkedList(); + 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; +} +} + diff --git a/contrib/psg/src/peersim/graph/UndirectedGraph.java b/contrib/psg/src/peersim/graph/UndirectedGraph.java new file mode 100644 index 0000000000..d229bae93f --- /dev/null +++ b/contrib/psg/src/peersim/graph/UndirectedGraph.java @@ -0,0 +1,152 @@ +/* + * 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 getNeighbours(int i) { + + Set result = new HashSet(); + result.addAll(g.getNeighbours(i)); + final int max = g.size(); + for(int j=0; j threads; + +public ProcessManager() +{ + threads = Collections.synchronizedList(new ArrayList()); +} + +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 diff --git a/contrib/psg/src/peersim/rangesim/RangeSimulator.java b/contrib/psg/src/peersim/rangesim/RangeSimulator.java new file mode 100644 index 0000000000..0995bcc83f --- /dev/null +++ b/contrib/psg/src/peersim/rangesim/RangeSimulator.java @@ -0,0 +1,461 @@ +/* + * 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 S to be assigned to a variable + * v. The Range Simulator invokes the standard Peersim + * simulator once for each distinct value. If multiple ranges + * S1, S2, ..., Sn are specified, the standard Peersim + * simulator is invoked for each element in + * S1 * S2 * ... * Sn. + *

    + * Ranges are specified with the following syntax: +

    +range.[id] [var];[range]
    +
    + * where: + *
      + *
    • {@value #PAR_RANGE} is the prefix for all range + * specifications;
    • + *
    • id is an identifier; since they are not referred anywhere else, + * consecutive numbers are a good choice for range identifiers;
    • + *
    • var is a variable parameter
    • + *
    • range describes the collection of values to be associated + * to var, whose syntax and semantics is defined in + * {@link peersim.util.StringListParser}.
    • + *
    + * Examples of range specifications are the following: +
    +range.0 SIZE;2^10:2^18|*2
    +range.1 K;20:30
    +range.2 CHURN;0.05,0.10,0.20 
    +
    + * With this specification, the collection of values associated to + * SIZE is {2^10,2^11,...,2^18}; K contains + * {20,21,22,...,30}, while CHURN contains just the + * specified values. + *

    + * Values can be specified as constant expressions (like 2^10, (5+10), etc.) + * but variables cannot be used. + *

    + * 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. + *

    + * 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: +
    +java peersim.rangesim.RangeSimulator config.file jvm.options=-Xmx256m
    +
    + * can be used to run the forked JVM with a maximum heap of 256MB. + *

    + * 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 ;"); + } + 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 list = new ArrayList(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 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 [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(); +} + +} diff --git a/contrib/psg/src/peersim/rangesim/TaggedOutputStream.java b/contrib/psg/src/peersim/rangesim/TaggedOutputStream.java new file mode 100644 index 0000000000..628855383a --- /dev/null +++ b/contrib/psg/src/peersim/rangesim/TaggedOutputStream.java @@ -0,0 +1,179 @@ +/* + * 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 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[] 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; +} + +} diff --git a/contrib/psg/src/peersim/reports/BallExpansion.java b/contrib/psg/src/peersim/reports/BallExpansion.java new file mode 100644 index 0000000000..b46e1172a5 --- /dev/null +++ b/contrib/psg/src/peersim/reports/BallExpansion.java @@ -0,0 +1,159 @@ +/* + * 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. + *

    + * 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 initial 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 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; +} + +} diff --git a/contrib/psg/src/peersim/reports/Clustering.java b/contrib/psg/src/peersim/reports/Clustering.java new file mode 100644 index 0000000000..38b81f7e28 --- /dev/null +++ b/contrib/psg/src/peersim/reports/Clustering.java @@ -0,0 +1,80 @@ +/* + * 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; +} + +} diff --git a/contrib/psg/src/peersim/reports/ConnectivityObserver.java b/contrib/psg/src/peersim/reports/ConnectivityObserver.java new file mode 100644 index 0000000000..d23ac082c2 --- /dev/null +++ b/contrib/psg/src/peersim/reports/ConnectivityObserver.java @@ -0,0 +1,125 @@ +/* + * 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 + *

      + *
    • "wcc": weakly connected clusters
    • + *
    • "scc": strongly connected clusters
    • + *
    + * 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; +} + +} diff --git a/contrib/psg/src/peersim/reports/DegreeStats.java b/contrib/psg/src/peersim/reports/DegreeStats.java new file mode 100644 index 0000000000..d6c57e6288 --- /dev/null +++ b/contrib/psg/src/peersim/reports/DegreeStats.java @@ -0,0 +1,187 @@ +/* + * 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; +} + +} diff --git a/contrib/psg/src/peersim/reports/GraphObserver.java b/contrib/psg/src/peersim/reports/GraphObserver.java new file mode 100644 index 0000000000..6f939ea5e0 --- /dev/null +++ b/contrib/psg/src/peersim/reports/GraphObserver.java @@ -0,0 +1,170 @@ +/* + * 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; +} + +} + + + diff --git a/contrib/psg/src/peersim/reports/GraphPrinter.java b/contrib/psg/src/peersim/reports/GraphPrinter.java new file mode 100644 index 0000000000..3317c10f0e --- /dev/null +++ b/contrib/psg/src/peersim/reports/GraphPrinter.java @@ -0,0 +1,142 @@ +/* + * 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); +} +} + +} + diff --git a/contrib/psg/src/peersim/reports/GraphStats.java b/contrib/psg/src/peersim/reports/GraphStats.java new file mode 100644 index 0000000000..a57773c8c9 --- /dev/null +++ b/contrib/psg/src/peersim/reports/GraphStats.java @@ -0,0 +1,150 @@ +/* + * 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; ijava.lang.Runtime). + * + * @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; +} + +} diff --git a/contrib/psg/src/peersim/reports/RandRemoval.java b/contrib/psg/src/peersim/reports/RandRemoval.java new file mode 100644 index 0000000000..8872873146 --- /dev/null +++ b/contrib/psg/src/peersim/reports/RandRemoval.java @@ -0,0 +1,122 @@ +/* + * 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 may + * shuffle the network (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 the overlay will be shuffled. 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; +} + +} diff --git a/contrib/psg/src/peersim/transport/E2ENetwork.java b/contrib/psg/src/peersim/transport/E2ENetwork.java new file mode 100644 index 0000000000..a184f9ad46 --- /dev/null +++ b/contrib/psg/src/peersim/transport/E2ENetwork.java @@ -0,0 +1,144 @@ +/* + * 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; +} + +} diff --git a/contrib/psg/src/peersim/transport/E2ETransport.java b/contrib/psg/src/peersim/transport/E2ETransport.java new file mode 100644 index 0000000000..e2eab7861b --- /dev/null +++ b/contrib/psg/src/peersim/transport/E2ETransport.java @@ -0,0 +1,156 @@ +/* + * 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; +} + +} diff --git a/contrib/psg/src/peersim/transport/KingParser.java b/contrib/psg/src/peersim/transport/KingParser.java new file mode 100644 index 0000000000..1471796e0e --- /dev/null +++ b/contrib/psg/src/peersim/transport/KingParser.java @@ -0,0 +1,166 @@ +/* + * 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 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; +} + +} diff --git a/contrib/psg/src/peersim/transport/RouterInfo.java b/contrib/psg/src/peersim/transport/RouterInfo.java new file mode 100644 index 0000000000..be3c168e74 --- /dev/null +++ b/contrib/psg/src/peersim/transport/RouterInfo.java @@ -0,0 +1,49 @@ +/* + * 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(); + +} diff --git a/contrib/psg/src/peersim/transport/Transport.java b/contrib/psg/src/peersim/transport/Transport.java new file mode 100644 index 0000000000..4606283414 --- /dev/null +++ b/contrib/psg/src/peersim/transport/Transport.java @@ -0,0 +1,59 @@ +/* + * 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 msg from node src to protocol + * pid of node dst. + * + * @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 src to protocol + * pid of node dst. + * + * @param src sender node + * @param dest destination node + */ +public long getLatency(Node src, Node dest); + + +} diff --git a/contrib/psg/src/peersim/transport/TriangularMatrixParser.java b/contrib/psg/src/peersim/transport/TriangularMatrixParser.java new file mode 100644 index 0000000000..d3f0768855 --- /dev/null +++ b/contrib/psg/src/peersim/transport/TriangularMatrixParser.java @@ -0,0 +1,140 @@ +/* + * 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 + * + * link), 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; +} + +} diff --git a/contrib/psg/src/peersim/transport/UniformRandomTransport.java b/contrib/psg/src/peersim/transport/UniformRandomTransport.java new file mode 100644 index 0000000000..1abc6d85af --- /dev/null +++ b/contrib/psg/src/peersim/transport/UniformRandomTransport.java @@ -0,0 +1,123 @@ +/* + * 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 this. 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)); +} + + +} diff --git a/contrib/psg/src/peersim/transport/UniformRouterAssignment.java b/contrib/psg/src/peersim/transport/UniformRouterAssignment.java new file mode 100644 index 0000000000..064ba7d04b --- /dev/null +++ b/contrib/psg/src/peersim/transport/UniformRouterAssignment.java @@ -0,0 +1,91 @@ +/* + * 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; +} + +} + diff --git a/contrib/psg/src/peersim/transport/UnreliableTransport.java b/contrib/psg/src/peersim/transport/UnreliableTransport.java new file mode 100644 index 0000000000..94608806c6 --- /dev/null +++ b/contrib/psg/src/peersim/transport/UnreliableTransport.java @@ -0,0 +1,129 @@ +/* + * 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. + *

    + * 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 this. 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); +} + +} diff --git a/contrib/psg/src/peersim/util/ExtendedRandom.java b/contrib/psg/src/peersim/util/ExtendedRandom.java new file mode 100644 index 0000000000..14e713e767 --- /dev/null +++ b/contrib/psg/src/peersim/util/ExtendedRandom.java @@ -0,0 +1,123 @@ +/* + * 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 java.util.Random. + */ +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]))); + +} +*/ +} + diff --git a/contrib/psg/src/peersim/util/FileNameGenerator.java b/contrib/psg/src/peersim/util/FileNameGenerator.java new file mode 100644 index 0000000000..c07d9252d9 --- /dev/null +++ b/contrib/psg/src/peersim/util/FileNameGenerator.java @@ -0,0 +1,87 @@ +/* + * 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()); +} +*/ +} + diff --git a/contrib/psg/src/peersim/util/IncrementalFreq.java b/contrib/psg/src/peersim/util/IncrementalFreq.java new file mode 100644 index 0000000000..3352daf3cf --- /dev/null +++ b/contrib/psg/src/peersim/util/IncrementalFreq.java @@ -0,0 +1,312 @@ +/* + * 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 this(-1), 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; ii to the input set. + * It calls add(i,1). + * @see #add(int,int) + */ +public final void add( int i ) { add(i,1); } + + +// -------------------------------------------------------------------- + +/** + * Adds item i to the input set k times. + * That is, it increments counter i by k. + * If, however, i 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 && istrict is true, it + * throws an IllegalArgumentException if this is + * not strictly larger than other (element by element) + * (Note that both frequency vectors are positive.) + * Otherwise just sets those elements in this to zero + * that are smaller than those of other. + * @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 +*

    +* value occurrences
    +* 
    +* That is, numbers with zero occurrences will also be printed. +*/ +public void printAll( PrintStream out ) { + + for(int i=0; i +* value occurrences +* +*/ +public void print( PrintStream out ) { + + for(int i=0; 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; iadd(item,1). +* @see #add(double,int) */ +public final void add( double item ) { add(item,1); } + +// -------------------------------------------------------------------- + +/** Updates the statistics assuming element item is added +* k 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; +} + +} + diff --git a/contrib/psg/src/peersim/util/IndexIterator.java b/contrib/psg/src/peersim/util/IndexIterator.java new file mode 100644 index 0000000000..8d078ab372 --- /dev/null +++ b/contrib/psg/src/peersim/util/IndexIterator.java @@ -0,0 +1,42 @@ +/* + * 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(); +} + diff --git a/contrib/psg/src/peersim/util/LinearIterator.java b/contrib/psg/src/peersim/util/LinearIterator.java new file mode 100644 index 0000000000..2a1ee36dee --- /dev/null +++ b/contrib/psg/src/peersim/util/LinearIterator.java @@ -0,0 +1,108 @@ +/* + * 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()); +} +*/ +} diff --git a/contrib/psg/src/peersim/util/MedianStats.java b/contrib/psg/src/peersim/util/MedianStats.java new file mode 100644 index 0000000000..be1e9be6d0 --- /dev/null +++ b/contrib/psg/src/peersim/util/MedianStats.java @@ -0,0 +1,91 @@ +/* + * 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 data=new ArrayList(); + +/** 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; i1; 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 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 + * The language for range expression is the following: + *
    + *   [rangelist] := [range] | [range],[rangelist]
    + *   [range] := value | min:max | min:max|step | 
    + *      min:max*|step
    + * 
    + * where value, min, max and step + * are numeric atoms that defines ranges. + *

    + * For example, the following range expression: + *

    + *   5,9:11,13:17|2,32:128*|2
    + * 
    + * 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 list = new ArrayList(); + 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 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 : or "); + } +} + +private static void parseSingleItem(List list, String item) +{ + list.add(item); +} + +private static void parseRangeItem(List 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(""); +} +*/ +} diff --git a/contrib/psg/src/peersim/util/WeightedRandPerm.java b/contrib/psg/src/peersim/util/WeightedRandPerm.java new file mode 100644 index 0000000000..80c8928900 --- /dev/null +++ b/contrib/psg/src/peersim/util/WeightedRandPerm.java @@ -0,0 +1,194 @@ +/* + * 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 + * The method to be used is specified at construction time. + * For backward compatibility, if no method is specified, the method + * getValue 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). + *

    + * 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 prefix + "." + protocol is read. + * @param methodn the configuration parameter name that contains the getter + * method name. + * The parameter prefix + "." + methodn is read, with the default + * value getValue. + */ +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)); } + +} + diff --git a/contrib/psg/src/peersim/vector/GetterSetterFinder.java b/contrib/psg/src/peersim/vector/GetterSetterFinder.java new file mode 100644 index 0000000000..48670be509 --- /dev/null +++ b/contrib/psg/src/peersim/vector/GetterSetterFinder.java @@ -0,0 +1,174 @@ +/* + * 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 list = new ArrayList(); + 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 list = new ArrayList(); + 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]; +} + +//-------------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/vector/InitVectFromFile.java b/contrib/psg/src/peersim/vector/InitVectFromFile.java new file mode 100644 index 0000000000..1bfaa17a3a --- /dev/null +++ b/contrib/psg/src/peersim/vector/InitVectFromFile.java @@ -0,0 +1,128 @@ +/* + * 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; +} + +// -------------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/vector/LinearDistribution.java b/contrib/psg/src/peersim/vector/LinearDistribution.java new file mode 100644 index 0000000000..e7cc19eacb --- /dev/null +++ b/contrib/psg/src/peersim/vector/LinearDistribution.java @@ -0,0 +1,123 @@ +/* + * 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 + * The method to be used is specified at construction time. + * For backward compatibility, if no method is specified, the method + * setValue 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). + *

    + * 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 prefix + "." + protocol is read. + * @param methodn the configuration parameter name that contains the setter + * method name. + * The parameter prefix + "." + methodn is read, with the default + * value setValue. + */ +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); } + +} + diff --git a/contrib/psg/src/peersim/vector/SingleValue.java b/contrib/psg/src/peersim/vector/SingleValue.java new file mode 100644 index 0000000000..8d3dd9e8f5 --- /dev/null +++ b/contrib/psg/src/peersim/vector/SingleValue.java @@ -0,0 +1,40 @@ +/* + * 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); + +} + diff --git a/contrib/psg/src/peersim/vector/SingleValueComparator.java b/contrib/psg/src/peersim/vector/SingleValueComparator.java new file mode 100644 index 0000000000..0fa324eb36 --- /dev/null +++ b/contrib/psg/src/peersim/vector/SingleValueComparator.java @@ -0,0 +1,58 @@ +/* + * 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 pid. + */ +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()); +} + +} diff --git a/contrib/psg/src/peersim/vector/SingleValueHolder.java b/contrib/psg/src/peersim/vector/SingleValueHolder.java new file mode 100644 index 0000000000..450086d732 --- /dev/null +++ b/contrib/psg/src/peersim/vector/SingleValueHolder.java @@ -0,0 +1,95 @@ +/* + * 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; } + +} diff --git a/contrib/psg/src/peersim/vector/SingleValueObserver.java b/contrib/psg/src/peersim/vector/SingleValueObserver.java new file mode 100644 index 0000000000..262d2e06e1 --- /dev/null +++ b/contrib/psg/src/peersim/vector/SingleValueObserver.java @@ -0,0 +1,124 @@ +/* + * 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); +} + +//-------------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/vector/TestVectors.java b/contrib/psg/src/peersim/vector/TestVectors.java new file mode 100644 index 0000000000..f6920cbe0a --- /dev/null +++ b/contrib/psg/src/peersim/vector/TestVectors.java @@ -0,0 +1,104 @@ +/* + * 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; } + +} + diff --git a/contrib/psg/src/peersim/vector/UniformDistribution.java b/contrib/psg/src/peersim/vector/UniformDistribution.java new file mode 100644 index 0000000000..bd30fb1957 --- /dev/null +++ b/contrib/psg/src/peersim/vector/UniformDistribution.java @@ -0,0 +1,141 @@ +/* + * 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); + } +} + +// -------------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/vector/ValueDumper.java b/contrib/psg/src/peersim/vector/ValueDumper.java new file mode 100644 index 0000000000..ce6746e4ab --- /dev/null +++ b/contrib/psg/src/peersim/vector/ValueDumper.java @@ -0,0 +1,134 @@ +/* + * 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". + *

    + * 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; +} + +// --------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/vector/VectAngle.java b/contrib/psg/src/peersim/vector/VectAngle.java new file mode 100644 index 0000000000..629a06e42d --- /dev/null +++ b/contrib/psg/src/peersim/vector/VectAngle.java @@ -0,0 +1,141 @@ +/* + * 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. + *

    + * 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}. + *

    + * 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 getValue (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 getValue (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; +} + +//-------------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/vector/VectControl.java b/contrib/psg/src/peersim/vector/VectControl.java new file mode 100644 index 0000000000..174b9624db --- /dev/null +++ b/contrib/psg/src/peersim/vector/VectControl.java @@ -0,0 +1,90 @@ +/* + * 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 + * setValue + * (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 getValue + * (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); +} + +} + diff --git a/contrib/psg/src/peersim/vector/VectCopy.java b/contrib/psg/src/peersim/vector/VectCopy.java new file mode 100644 index 0000000000..6d13091d83 --- /dev/null +++ b/contrib/psg/src/peersim/vector/VectCopy.java @@ -0,0 +1,113 @@ +/* + * 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}. + *

    + * 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()); +} + +//-------------------------------------------------------------------------- + +} diff --git a/contrib/psg/src/peersim/vector/VectorComparator.java b/contrib/psg/src/peersim/vector/VectorComparator.java new file mode 100644 index 0000000000..97f269e9c7 --- /dev/null +++ b/contrib/psg/src/peersim/vector/VectorComparator.java @@ -0,0 +1,97 @@ +/* + * 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 + * java.lang.Comparator 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. + *
    + * This comparator can be used, for example, to sort + * an array of nodes based on method getValue + * associated to the protocol pid: + *

    + * Comparator c = new VectorComparator(pid, "getValue");
    + * Array.sort(Node[] array, c);
    + * 
    + * 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()); + } +} + +} diff --git a/contrib/psg/src/peersim/vector/VectorObserver.java b/contrib/psg/src/peersim/vector/VectorObserver.java new file mode 100644 index 0000000000..b971fc2c89 --- /dev/null +++ b/contrib/psg/src/peersim/vector/VectorObserver.java @@ -0,0 +1,80 @@ +/* + * 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; +} + +} diff --git a/contrib/psg/src/psgsim/NodeHost.java b/contrib/psg/src/psgsim/NodeHost.java new file mode 100644 index 0000000000..1dce76fd42 --- /dev/null +++ b/contrib/psg/src/psgsim/NodeHost.java @@ -0,0 +1,74 @@ +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 mapHostNode = new TreeMap( + new Comparator() { + 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 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 element : mapHostNode.entrySet()) { + if (element.getKey() == node) + return element.getValue(); + } + return null; + } +} diff --git a/contrib/psg/src/psgsim/PSGDynamicNetwork.java b/contrib/psg/src/psgsim/PSGDynamicNetwork.java new file mode 100644 index 0000000000..f4bb3b12c6 --- /dev/null +++ b/contrib/psg/src/psgsim/PSGDynamicNetwork.java @@ -0,0 +1,45 @@ +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; + } +} diff --git a/contrib/psg/src/psgsim/PSGPlatform.java b/contrib/psg/src/psgsim/PSGPlatform.java new file mode 100644 index 0000000000..90c7c8765d --- /dev/null +++ b/contrib/psg/src/psgsim/PSGPlatform.java @@ -0,0 +1,329 @@ +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 controlStepMap = new LinkedHashMap(); + + /** A collection map represents the protocol and its associated pid. **/ + static TreeMap protocolsPidsMap = new TreeMap( + new Comparator() { + 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 cdProtocolsStepMap = new TreeMap( + new Comparator() { + 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"); + + } + +} diff --git a/contrib/psg/src/psgsim/PSGProcessController.java b/contrib/psg/src/psgsim/PSGProcessController.java new file mode 100644 index 0000000000..d587691c80 --- /dev/null +++ b/contrib/psg/src/psgsim/PSGProcessController.java @@ -0,0 +1,68 @@ +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 controlStepMapTmp = new LinkedHashMap(); + + 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 entry : PSGPlatform.controlStepMap + .entrySet()) { + controlStepMapTmp.put(entry.getKey(), entry.getValue()); + } + while (PSGPlatform.getTime() <= PSGPlatform.getDuration()) { + for (Map.Entry 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 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 entry : controlStepMapTmp.entrySet()) { + Double valeur = (entry.getValue() - PSGPlatform.getClock()); + if (min > valeur) + min = valeur; + } + + return min; + } +} \ No newline at end of file diff --git a/contrib/psg/src/psgsim/PSGProcessCycle.java b/contrib/psg/src/psgsim/PSGProcessCycle.java new file mode 100644 index 0000000000..e0931b1f55 --- /dev/null +++ b/contrib/psg/src/psgsim/PSGProcessCycle.java @@ -0,0 +1,59 @@ +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. + *

    + * 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 entry : PSGPlatform.cdProtocolsStepMap + .entrySet()) { + Double step = entry.getValue(); + for (Entry p : PSGPlatform.protocolsPidsMap + .entrySet()) { + if (p.getValue() == pid) { + break; + } + } + if (PSGPlatform.getTime() <= PSGPlatform.getDuration() && host.isOn()) { + PSGSimulator.add(PSGPlatform.sgToPsTime(step), event, node, pid); + } + } + } + +} diff --git a/contrib/psg/src/psgsim/PSGProcessEvent.java b/contrib/psg/src/psgsim/PSGProcessEvent.java new file mode 100644 index 0000000000..cc4f73c214 --- /dev/null +++ b/contrib/psg/src/psgsim/PSGProcessEvent.java @@ -0,0 +1,62 @@ +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. + *

    + * The main method of this class is to handle events received, by calling the + * processEvent method on the corresponding node and pid. + *

    + * 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; + } + } + +} diff --git a/contrib/psg/src/psgsim/PSGProcessLauncher.java b/contrib/psg/src/psgsim/PSGProcessLauncher.java new file mode 100644 index 0000000000..97d89c0132 --- /dev/null +++ b/contrib/psg/src/psgsim/PSGProcessLauncher.java @@ -0,0 +1,74 @@ +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}. + *

    + * This class performs to launch the appropriate call according to the type of + * the event; + *

    + * - A NextCycleEvent: the event will be delivered to the + * {@link PSGProcessCycle} for treatment. + *

    + * - 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 diff --git a/contrib/psg/src/psgsim/PSGSimulator.java b/contrib/psg/src/psgsim/PSGSimulator.java new file mode 100644 index 0000000000..4d45d4e438 --- /dev/null +++ b/contrib/psg/src/psgsim/PSGSimulator.java @@ -0,0 +1,98 @@ +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(); + } +} diff --git a/contrib/psg/src/psgsim/PSGTask.java b/contrib/psg/src/psgsim/PSGTask.java new file mode 100644 index 0000000000..6d25e2c5fc --- /dev/null +++ b/contrib/psg/src/psgsim/PSGTask.java @@ -0,0 +1,85 @@ +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; + + } + +} diff --git a/contrib/psg/src/psgsim/PSGTransport.java b/contrib/psg/src/psgsim/PSGTransport.java new file mode 100644 index 0000000000..db964b4ccf --- /dev/null +++ b/contrib/psg/src/psgsim/PSGTransport.java @@ -0,0 +1,115 @@ +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 taskToSend = new LinkedHashMap(); + + /** + * 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 this. 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 taskToSendCopy = new LinkedHashMap(); + for (Map.Entry entry : taskToSend.entrySet()) { + taskToSendCopy.put(entry.getKey(), entry.getValue()); + } + taskToSend.clear(); + for (Map.Entry 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)); + } +} diff --git a/contrib/psg/src/psgsim/Sizable.java b/contrib/psg/src/psgsim/Sizable.java new file mode 100644 index 0000000000..16196f6815 --- /dev/null +++ b/contrib/psg/src/psgsim/Sizable.java @@ -0,0 +1,18 @@ +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(); +} diff --git a/contrib/psg/test.sh b/contrib/psg/test.sh new file mode 100755 index 0000000000..f5b7f09dad --- /dev/null +++ b/contrib/psg/test.sh @@ -0,0 +1,45 @@ +#!/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 + diff --git a/contrib/psg/tutorial.pdf b/contrib/psg/tutorial.pdf new file mode 100644 index 0000000000..591649f7d1 Binary files /dev/null and b/contrib/psg/tutorial.pdf differ