From 200986a368bbbbb5df459d43cbc7f5ef3d7678db Mon Sep 17 00:00:00 2001 From: kbaati Date: Fri, 24 Jul 2015 12:24:03 +0200 Subject: [PATCH 1/1] peersimgrid release 1.0 --- contrib/psg/README.txt | 48 + contrib/psg/configs/bittorrent.txt | 46 + contrib/psg/configs/bittorrentPSG.txt | 48 + contrib/psg/configs/chord.txt | 51 + contrib/psg/configs/chordPSG.txt | 54 + contrib/psg/configs/edaggregation.txt | 57 + contrib/psg/configs/edaggregationPSG.txt | 57 + contrib/psg/configs/symphony.txt | 154 ++ contrib/psg/configs/symphonyPSG.txt | 155 ++ contrib/psg/lib.jar | Bin 0 -> 716138 bytes contrib/psg/platforms/psg.xml | 23 + contrib/psg/run.sh | 15 + .../src/example/bittorrent/BTObserver.java | 132 ++ .../src/example/bittorrent/BitTorrent.java | 1989 +++++++++++++++++ .../src/example/bittorrent/BitfieldMsg.java | 87 + .../psg/src/example/bittorrent/IntMsg.java | 72 + .../example/bittorrent/NetworkDynamics.java | 289 +++ .../bittorrent/NetworkInitializer.java | 105 + .../example/bittorrent/NodeInitializer.java | 170 ++ .../src/example/bittorrent/PeerSetMsg.java | 58 + .../src/example/bittorrent/SimpleEvent.java | 79 + .../psg/src/example/bittorrent/SimpleMsg.java | 60 + .../src/example/chord/ChordInitializer.java | 72 + .../psg/src/example/chord/ChordMessage.java | 12 + .../psg/src/example/chord/ChordProtocol.java | 358 +++ contrib/psg/src/example/chord/CreateNw.java | 148 ++ .../psg/src/example/chord/FinalMessage.java | 14 + .../psg/src/example/chord/LookUpMessage.java | 44 + .../example/chord/MessageCounterObserver.java | 108 + .../psg/src/example/chord/NodeComparator.java | 21 + contrib/psg/src/example/chord/Parameters.java | 7 + .../src/example/chord/TrafficGenerator.java | 54 + .../src/example/edaggregation/AverageED.java | 116 + .../symphony/AdapterIterableNetwork.java | 31 + .../AdapterSymphonyNodeComparator.java | 28 + contrib/psg/src/example/symphony/Handler.java | 19 + .../psg/src/example/symphony/LeaveTest.java | 77 + contrib/psg/src/example/symphony/Message.java | 90 + ...NetworkSizeEstimatorProtocolInterface.java | 13 + .../src/example/symphony/RandomRouteTest.java | 48 + .../src/example/symphony/RingRouteTest.java | 150 ++ .../example/symphony/RoutingException.java | 15 + .../SimpleNetworkSizeEstimatorProtocol.java | 23 + .../symphony/SymphonyEstimationProtocol.java | 106 + .../symphony/SymphonyNetworkBuilder.java | 157 ++ .../symphony/SymphonyNetworkChecker.java | 123 + .../symphony/SymphonyNetworkManager.java | 864 +++++++ .../symphony/SymphonyNodeComparator.java | 51 + .../symphony/SymphonyNodeInizializer.java | 59 + .../example/symphony/SymphonyProtocol.java | 530 +++++ .../example/symphony/SymphonyStatistics.java | 77 + contrib/psg/src/example/symphony/Tuple.java | 45 + .../symphony/test/NetworkEstimationTest.java | 52 + contrib/psg/src/peersim/Simulator.java | 270 +++ contrib/psg/src/peersim/cdsim/CDProtocol.java | 44 + .../psg/src/peersim/cdsim/CDSimulator.java | 221 ++ contrib/psg/src/peersim/cdsim/CDState.java | 136 ++ .../psg/src/peersim/cdsim/DaemonProtocol.java | 102 + .../psg/src/peersim/cdsim/FullNextCycle.java | 137 ++ contrib/psg/src/peersim/cdsim/NextCycle.java | 111 + contrib/psg/src/peersim/cdsim/Shuffle.java | 59 + .../psg/src/peersim/config/CheckConfig.java | 186 ++ .../psg/src/peersim/config/ClassFinder.java | 258 +++ .../src/peersim/config/ConfigContainer.java | 1011 +++++++++ .../src/peersim/config/ConfigProperties.java | 190 ++ .../psg/src/peersim/config/Configuration.java | 668 ++++++ .../psg/src/peersim/config/FastConfig.java | 191 ++ .../config/IllegalParameterException.java | 80 + .../config/MissingParameterException.java | 77 + .../src/peersim/config/NullPrintStream.java | 62 + contrib/psg/src/peersim/config/Operators.java | 163 ++ .../src/peersim/config/ParsedProperties.java | 254 +++ contrib/psg/src/peersim/core/Cleanable.java | 48 + contrib/psg/src/peersim/core/CommonState.java | 301 +++ contrib/psg/src/peersim/core/Control.java | 36 + contrib/psg/src/peersim/core/Fallible.java | 69 + contrib/psg/src/peersim/core/GeneralNode.java | 192 ++ .../psg/src/peersim/core/IdleProtocol.java | 160 ++ contrib/psg/src/peersim/core/Linkable.java | 80 + .../src/peersim/core/MaliciousProtocol.java | 31 + .../psg/src/peersim/core/ModifiableNode.java | 51 + contrib/psg/src/peersim/core/Network.java | 327 +++ contrib/psg/src/peersim/core/Node.java | 83 + .../src/peersim/core/OracleIdleProtocol.java | 103 + .../psg/src/peersim/core/OverlayGraph.java | 221 ++ contrib/psg/src/peersim/core/Protocol.java | 39 + contrib/psg/src/peersim/core/Scheduler.java | 186 ++ .../src/peersim/dynamics/DynamicNetwork.java | 229 ++ .../src/peersim/dynamics/MethodInvoker.java | 204 ++ .../src/peersim/dynamics/NodeInitializer.java | 39 + .../peersim/dynamics/OscillatingNetwork.java | 200 ++ contrib/psg/src/peersim/dynamics/RandNI.java | 114 + contrib/psg/src/peersim/dynamics/StarNI.java | 104 + .../src/peersim/dynamics/WireByMethod.java | 223 ++ .../src/peersim/dynamics/WireFromFile.java | 136 ++ .../psg/src/peersim/dynamics/WireGraph.java | 158 ++ .../psg/src/peersim/dynamics/WireKOut.java | 82 + .../peersim/dynamics/WireRegRootedTree.java | 77 + .../src/peersim/dynamics/WireRingLattice.java | 80 + .../src/peersim/dynamics/WireScaleFreeBA.java | 84 + .../src/peersim/dynamics/WireScaleFreeDM.java | 126 ++ .../psg/src/peersim/dynamics/WireStar.java | 54 + contrib/psg/src/peersim/dynamics/WireWS.java | 92 + .../psg/src/peersim/edsim/CDScheduler.java | 213 ++ .../psg/src/peersim/edsim/ControlEvent.java | 89 + contrib/psg/src/peersim/edsim/EDProtocol.java | 47 + .../psg/src/peersim/edsim/EDSimulator.java | 400 ++++ contrib/psg/src/peersim/edsim/Heap.java | 391 ++++ .../psg/src/peersim/edsim/NextCycleEvent.java | 103 + contrib/psg/src/peersim/edsim/PriorityQ.java | 102 + .../psg/src/peersim/edsim/RandNextCycle.java | 68 + .../src/peersim/edsim/RegRandNextCycle.java | 98 + contrib/psg/src/peersim/edsim/edsim_jsp.xmi | 2 + contrib/psg/src/peersim/edsim/edsim_kdm.xmi | 16 + .../psg/src/peersim/graph/BitMatrixGraph.java | 160 ++ .../src/peersim/graph/ConstUndirGraph.java | 185 ++ .../psg/src/peersim/graph/FastUndirGraph.java | 95 + contrib/psg/src/peersim/graph/Graph.java | 89 + .../src/peersim/graph/GraphAlgorithms.java | 379 ++++ .../psg/src/peersim/graph/GraphFactory.java | 300 +++ contrib/psg/src/peersim/graph/GraphIO.java | 295 +++ .../src/peersim/graph/NeighbourListGraph.java | 173 ++ .../psg/src/peersim/graph/PrefixSubGraph.java | 158 ++ .../psg/src/peersim/graph/SubGraphEdges.java | 177 ++ .../src/peersim/graph/UndirectedGraph.java | 152 ++ .../src/peersim/rangesim/ProcessHandler.java | 37 + .../src/peersim/rangesim/ProcessManager.java | 53 + .../src/peersim/rangesim/RangeSimulator.java | 461 ++++ .../peersim/rangesim/TaggedOutputStream.java | 179 ++ .../src/peersim/reports/BallExpansion.java | 159 ++ .../psg/src/peersim/reports/Clustering.java | 80 + .../peersim/reports/ConnectivityObserver.java | 125 ++ .../psg/src/peersim/reports/DegreeStats.java | 187 ++ .../src/peersim/reports/GraphObserver.java | 170 ++ .../psg/src/peersim/reports/GraphPrinter.java | 142 ++ .../psg/src/peersim/reports/GraphStats.java | 150 ++ .../src/peersim/reports/MemoryObserver.java | 55 + .../psg/src/peersim/reports/RandRemoval.java | 122 + .../psg/src/peersim/transport/E2ENetwork.java | 144 ++ .../src/peersim/transport/E2ETransport.java | 156 ++ .../psg/src/peersim/transport/KingParser.java | 166 ++ .../psg/src/peersim/transport/RouterInfo.java | 49 + .../psg/src/peersim/transport/Transport.java | 59 + .../transport/TriangularMatrixParser.java | 140 ++ .../transport/UniformRandomTransport.java | 123 + .../transport/UniformRouterAssignment.java | 91 + .../transport/UnreliableTransport.java | 129 ++ .../psg/src/peersim/util/ExtendedRandom.java | 123 + .../src/peersim/util/FileNameGenerator.java | 87 + .../psg/src/peersim/util/IncrementalFreq.java | 312 +++ .../src/peersim/util/IncrementalStats.java | 187 ++ .../psg/src/peersim/util/IndexIterator.java | 42 + .../psg/src/peersim/util/LinearIterator.java | 108 + contrib/psg/src/peersim/util/MedianStats.java | 91 + contrib/psg/src/peersim/util/MomentStats.java | 82 + .../psg/src/peersim/util/RandPermutation.java | 178 ++ .../src/peersim/util/StringListParser.java | 164 ++ .../src/peersim/util/WeightedRandPerm.java | 194 ++ contrib/psg/src/peersim/vector/Getter.java | 237 ++ .../peersim/vector/GetterSetterFinder.java | 174 ++ .../src/peersim/vector/InitVectFromFile.java | 128 ++ .../peersim/vector/LinearDistribution.java | 123 + .../psg/src/peersim/vector/Normalizer.java | 114 + .../src/peersim/vector/PeakDistribution.java | 139 ++ contrib/psg/src/peersim/vector/Setter.java | 222 ++ .../psg/src/peersim/vector/SingleValue.java | 40 + .../peersim/vector/SingleValueComparator.java | 58 + .../src/peersim/vector/SingleValueHolder.java | 95 + .../peersim/vector/SingleValueObserver.java | 124 + .../psg/src/peersim/vector/TestVectors.java | 104 + .../peersim/vector/UniformDistribution.java | 141 ++ .../psg/src/peersim/vector/ValueDumper.java | 134 ++ contrib/psg/src/peersim/vector/VectAngle.java | 141 ++ .../psg/src/peersim/vector/VectControl.java | 90 + contrib/psg/src/peersim/vector/VectCopy.java | 113 + .../src/peersim/vector/VectorComparator.java | 97 + .../src/peersim/vector/VectorObserver.java | 80 + contrib/psg/src/psgsim/NodeHost.java | 74 + contrib/psg/src/psgsim/PSGDynamicNetwork.java | 45 + contrib/psg/src/psgsim/PSGPlatform.java | 329 +++ .../psg/src/psgsim/PSGProcessController.java | 68 + contrib/psg/src/psgsim/PSGProcessCycle.java | 59 + contrib/psg/src/psgsim/PSGProcessEvent.java | 62 + .../psg/src/psgsim/PSGProcessLauncher.java | 74 + contrib/psg/src/psgsim/PSGSimulator.java | 98 + contrib/psg/src/psgsim/PSGTask.java | 85 + contrib/psg/src/psgsim/PSGTransport.java | 115 + contrib/psg/src/psgsim/Sizable.java | 18 + contrib/psg/test.sh | 45 + contrib/psg/tutorial.pdf | Bin 0 -> 114829 bytes 190 files changed, 27073 insertions(+) create mode 100644 contrib/psg/README.txt create mode 100644 contrib/psg/configs/bittorrent.txt create mode 100644 contrib/psg/configs/bittorrentPSG.txt create mode 100644 contrib/psg/configs/chord.txt create mode 100644 contrib/psg/configs/chordPSG.txt create mode 100644 contrib/psg/configs/edaggregation.txt create mode 100644 contrib/psg/configs/edaggregationPSG.txt create mode 100644 contrib/psg/configs/symphony.txt create mode 100644 contrib/psg/configs/symphonyPSG.txt create mode 100644 contrib/psg/lib.jar create mode 100644 contrib/psg/platforms/psg.xml create mode 100755 contrib/psg/run.sh create mode 100644 contrib/psg/src/example/bittorrent/BTObserver.java create mode 100644 contrib/psg/src/example/bittorrent/BitTorrent.java create mode 100644 contrib/psg/src/example/bittorrent/BitfieldMsg.java create mode 100644 contrib/psg/src/example/bittorrent/IntMsg.java create mode 100644 contrib/psg/src/example/bittorrent/NetworkDynamics.java create mode 100644 contrib/psg/src/example/bittorrent/NetworkInitializer.java create mode 100644 contrib/psg/src/example/bittorrent/NodeInitializer.java create mode 100644 contrib/psg/src/example/bittorrent/PeerSetMsg.java create mode 100644 contrib/psg/src/example/bittorrent/SimpleEvent.java create mode 100644 contrib/psg/src/example/bittorrent/SimpleMsg.java create mode 100644 contrib/psg/src/example/chord/ChordInitializer.java create mode 100644 contrib/psg/src/example/chord/ChordMessage.java create mode 100644 contrib/psg/src/example/chord/ChordProtocol.java create mode 100644 contrib/psg/src/example/chord/CreateNw.java create mode 100644 contrib/psg/src/example/chord/FinalMessage.java create mode 100644 contrib/psg/src/example/chord/LookUpMessage.java create mode 100644 contrib/psg/src/example/chord/MessageCounterObserver.java create mode 100644 contrib/psg/src/example/chord/NodeComparator.java create mode 100644 contrib/psg/src/example/chord/Parameters.java create mode 100644 contrib/psg/src/example/chord/TrafficGenerator.java create mode 100644 contrib/psg/src/example/edaggregation/AverageED.java create mode 100644 contrib/psg/src/example/symphony/AdapterIterableNetwork.java create mode 100644 contrib/psg/src/example/symphony/AdapterSymphonyNodeComparator.java create mode 100644 contrib/psg/src/example/symphony/Handler.java create mode 100644 contrib/psg/src/example/symphony/LeaveTest.java create mode 100644 contrib/psg/src/example/symphony/Message.java create mode 100644 contrib/psg/src/example/symphony/NetworkSizeEstimatorProtocolInterface.java create mode 100644 contrib/psg/src/example/symphony/RandomRouteTest.java create mode 100644 contrib/psg/src/example/symphony/RingRouteTest.java create mode 100644 contrib/psg/src/example/symphony/RoutingException.java create mode 100644 contrib/psg/src/example/symphony/SimpleNetworkSizeEstimatorProtocol.java create mode 100644 contrib/psg/src/example/symphony/SymphonyEstimationProtocol.java create mode 100644 contrib/psg/src/example/symphony/SymphonyNetworkBuilder.java create mode 100644 contrib/psg/src/example/symphony/SymphonyNetworkChecker.java create mode 100644 contrib/psg/src/example/symphony/SymphonyNetworkManager.java create mode 100644 contrib/psg/src/example/symphony/SymphonyNodeComparator.java create mode 100644 contrib/psg/src/example/symphony/SymphonyNodeInizializer.java create mode 100644 contrib/psg/src/example/symphony/SymphonyProtocol.java create mode 100644 contrib/psg/src/example/symphony/SymphonyStatistics.java create mode 100644 contrib/psg/src/example/symphony/Tuple.java create mode 100644 contrib/psg/src/example/symphony/test/NetworkEstimationTest.java create mode 100644 contrib/psg/src/peersim/Simulator.java create mode 100644 contrib/psg/src/peersim/cdsim/CDProtocol.java create mode 100644 contrib/psg/src/peersim/cdsim/CDSimulator.java create mode 100644 contrib/psg/src/peersim/cdsim/CDState.java create mode 100644 contrib/psg/src/peersim/cdsim/DaemonProtocol.java create mode 100644 contrib/psg/src/peersim/cdsim/FullNextCycle.java create mode 100644 contrib/psg/src/peersim/cdsim/NextCycle.java create mode 100644 contrib/psg/src/peersim/cdsim/Shuffle.java create mode 100644 contrib/psg/src/peersim/config/CheckConfig.java create mode 100644 contrib/psg/src/peersim/config/ClassFinder.java create mode 100644 contrib/psg/src/peersim/config/ConfigContainer.java create mode 100644 contrib/psg/src/peersim/config/ConfigProperties.java create mode 100644 contrib/psg/src/peersim/config/Configuration.java create mode 100644 contrib/psg/src/peersim/config/FastConfig.java create mode 100644 contrib/psg/src/peersim/config/IllegalParameterException.java create mode 100644 contrib/psg/src/peersim/config/MissingParameterException.java create mode 100644 contrib/psg/src/peersim/config/NullPrintStream.java create mode 100644 contrib/psg/src/peersim/config/Operators.java create mode 100644 contrib/psg/src/peersim/config/ParsedProperties.java create mode 100644 contrib/psg/src/peersim/core/Cleanable.java create mode 100644 contrib/psg/src/peersim/core/CommonState.java create mode 100644 contrib/psg/src/peersim/core/Control.java create mode 100644 contrib/psg/src/peersim/core/Fallible.java create mode 100644 contrib/psg/src/peersim/core/GeneralNode.java create mode 100644 contrib/psg/src/peersim/core/IdleProtocol.java create mode 100644 contrib/psg/src/peersim/core/Linkable.java create mode 100644 contrib/psg/src/peersim/core/MaliciousProtocol.java create mode 100644 contrib/psg/src/peersim/core/ModifiableNode.java create mode 100644 contrib/psg/src/peersim/core/Network.java create mode 100644 contrib/psg/src/peersim/core/Node.java create mode 100644 contrib/psg/src/peersim/core/OracleIdleProtocol.java create mode 100644 contrib/psg/src/peersim/core/OverlayGraph.java create mode 100644 contrib/psg/src/peersim/core/Protocol.java create mode 100644 contrib/psg/src/peersim/core/Scheduler.java create mode 100644 contrib/psg/src/peersim/dynamics/DynamicNetwork.java create mode 100644 contrib/psg/src/peersim/dynamics/MethodInvoker.java create mode 100644 contrib/psg/src/peersim/dynamics/NodeInitializer.java create mode 100644 contrib/psg/src/peersim/dynamics/OscillatingNetwork.java create mode 100644 contrib/psg/src/peersim/dynamics/RandNI.java create mode 100644 contrib/psg/src/peersim/dynamics/StarNI.java create mode 100644 contrib/psg/src/peersim/dynamics/WireByMethod.java create mode 100644 contrib/psg/src/peersim/dynamics/WireFromFile.java create mode 100644 contrib/psg/src/peersim/dynamics/WireGraph.java create mode 100644 contrib/psg/src/peersim/dynamics/WireKOut.java create mode 100644 contrib/psg/src/peersim/dynamics/WireRegRootedTree.java create mode 100644 contrib/psg/src/peersim/dynamics/WireRingLattice.java create mode 100644 contrib/psg/src/peersim/dynamics/WireScaleFreeBA.java create mode 100644 contrib/psg/src/peersim/dynamics/WireScaleFreeDM.java create mode 100644 contrib/psg/src/peersim/dynamics/WireStar.java create mode 100644 contrib/psg/src/peersim/dynamics/WireWS.java create mode 100644 contrib/psg/src/peersim/edsim/CDScheduler.java create mode 100644 contrib/psg/src/peersim/edsim/ControlEvent.java create mode 100644 contrib/psg/src/peersim/edsim/EDProtocol.java create mode 100644 contrib/psg/src/peersim/edsim/EDSimulator.java create mode 100644 contrib/psg/src/peersim/edsim/Heap.java create mode 100644 contrib/psg/src/peersim/edsim/NextCycleEvent.java create mode 100644 contrib/psg/src/peersim/edsim/PriorityQ.java create mode 100644 contrib/psg/src/peersim/edsim/RandNextCycle.java create mode 100644 contrib/psg/src/peersim/edsim/RegRandNextCycle.java create mode 100644 contrib/psg/src/peersim/edsim/edsim_jsp.xmi create mode 100644 contrib/psg/src/peersim/edsim/edsim_kdm.xmi create mode 100644 contrib/psg/src/peersim/graph/BitMatrixGraph.java create mode 100644 contrib/psg/src/peersim/graph/ConstUndirGraph.java create mode 100644 contrib/psg/src/peersim/graph/FastUndirGraph.java create mode 100644 contrib/psg/src/peersim/graph/Graph.java create mode 100644 contrib/psg/src/peersim/graph/GraphAlgorithms.java create mode 100644 contrib/psg/src/peersim/graph/GraphFactory.java create mode 100644 contrib/psg/src/peersim/graph/GraphIO.java create mode 100644 contrib/psg/src/peersim/graph/NeighbourListGraph.java create mode 100644 contrib/psg/src/peersim/graph/PrefixSubGraph.java create mode 100644 contrib/psg/src/peersim/graph/SubGraphEdges.java create mode 100644 contrib/psg/src/peersim/graph/UndirectedGraph.java create mode 100644 contrib/psg/src/peersim/rangesim/ProcessHandler.java create mode 100644 contrib/psg/src/peersim/rangesim/ProcessManager.java create mode 100644 contrib/psg/src/peersim/rangesim/RangeSimulator.java create mode 100644 contrib/psg/src/peersim/rangesim/TaggedOutputStream.java create mode 100644 contrib/psg/src/peersim/reports/BallExpansion.java create mode 100644 contrib/psg/src/peersim/reports/Clustering.java create mode 100644 contrib/psg/src/peersim/reports/ConnectivityObserver.java create mode 100644 contrib/psg/src/peersim/reports/DegreeStats.java create mode 100644 contrib/psg/src/peersim/reports/GraphObserver.java create mode 100644 contrib/psg/src/peersim/reports/GraphPrinter.java create mode 100644 contrib/psg/src/peersim/reports/GraphStats.java create mode 100644 contrib/psg/src/peersim/reports/MemoryObserver.java create mode 100644 contrib/psg/src/peersim/reports/RandRemoval.java create mode 100644 contrib/psg/src/peersim/transport/E2ENetwork.java create mode 100644 contrib/psg/src/peersim/transport/E2ETransport.java create mode 100644 contrib/psg/src/peersim/transport/KingParser.java create mode 100644 contrib/psg/src/peersim/transport/RouterInfo.java create mode 100644 contrib/psg/src/peersim/transport/Transport.java create mode 100644 contrib/psg/src/peersim/transport/TriangularMatrixParser.java create mode 100644 contrib/psg/src/peersim/transport/UniformRandomTransport.java create mode 100644 contrib/psg/src/peersim/transport/UniformRouterAssignment.java create mode 100644 contrib/psg/src/peersim/transport/UnreliableTransport.java create mode 100644 contrib/psg/src/peersim/util/ExtendedRandom.java create mode 100644 contrib/psg/src/peersim/util/FileNameGenerator.java create mode 100644 contrib/psg/src/peersim/util/IncrementalFreq.java create mode 100644 contrib/psg/src/peersim/util/IncrementalStats.java create mode 100644 contrib/psg/src/peersim/util/IndexIterator.java create mode 100644 contrib/psg/src/peersim/util/LinearIterator.java create mode 100644 contrib/psg/src/peersim/util/MedianStats.java create mode 100644 contrib/psg/src/peersim/util/MomentStats.java create mode 100644 contrib/psg/src/peersim/util/RandPermutation.java create mode 100644 contrib/psg/src/peersim/util/StringListParser.java create mode 100644 contrib/psg/src/peersim/util/WeightedRandPerm.java create mode 100644 contrib/psg/src/peersim/vector/Getter.java create mode 100644 contrib/psg/src/peersim/vector/GetterSetterFinder.java create mode 100644 contrib/psg/src/peersim/vector/InitVectFromFile.java create mode 100644 contrib/psg/src/peersim/vector/LinearDistribution.java create mode 100644 contrib/psg/src/peersim/vector/Normalizer.java create mode 100644 contrib/psg/src/peersim/vector/PeakDistribution.java create mode 100644 contrib/psg/src/peersim/vector/Setter.java create mode 100644 contrib/psg/src/peersim/vector/SingleValue.java create mode 100644 contrib/psg/src/peersim/vector/SingleValueComparator.java create mode 100644 contrib/psg/src/peersim/vector/SingleValueHolder.java create mode 100644 contrib/psg/src/peersim/vector/SingleValueObserver.java create mode 100644 contrib/psg/src/peersim/vector/TestVectors.java create mode 100644 contrib/psg/src/peersim/vector/UniformDistribution.java create mode 100644 contrib/psg/src/peersim/vector/ValueDumper.java create mode 100644 contrib/psg/src/peersim/vector/VectAngle.java create mode 100644 contrib/psg/src/peersim/vector/VectControl.java create mode 100644 contrib/psg/src/peersim/vector/VectCopy.java create mode 100644 contrib/psg/src/peersim/vector/VectorComparator.java create mode 100644 contrib/psg/src/peersim/vector/VectorObserver.java create mode 100644 contrib/psg/src/psgsim/NodeHost.java create mode 100644 contrib/psg/src/psgsim/PSGDynamicNetwork.java create mode 100644 contrib/psg/src/psgsim/PSGPlatform.java create mode 100644 contrib/psg/src/psgsim/PSGProcessController.java create mode 100644 contrib/psg/src/psgsim/PSGProcessCycle.java create mode 100644 contrib/psg/src/psgsim/PSGProcessEvent.java create mode 100644 contrib/psg/src/psgsim/PSGProcessLauncher.java create mode 100644 contrib/psg/src/psgsim/PSGSimulator.java create mode 100644 contrib/psg/src/psgsim/PSGTask.java create mode 100644 contrib/psg/src/psgsim/PSGTransport.java create mode 100644 contrib/psg/src/psgsim/Sizable.java create mode 100755 contrib/psg/test.sh create mode 100644 contrib/psg/tutorial.pdf 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 0000000000000000000000000000000000000000..8fd65fa45f6041f79c1a9714ae213d8952c01c8f GIT binary patch literal 716138 zcmafaV{~Q9)^=>$PRF)w+s=+{cIxuH$074Nf8@*PqYG`7ZLg}Nzn>1f~)5^#Ayko#GZxWQ34&Yj2eIk zv6-&wKdz#+7dztu+Hndy4<%-!NP{VVAdrlVbavY4++`poRBo6wsH-~-y0!=wCVU~! zPGY(8xk9%+*?;VdvSi6Uo8lB70%x^rBkqPcDw_%T26?}_QUXo=gHJ1UIdhzLQb#{| z#tmw<=}|Jtz!2rb^0=zoLtp`JSO1iq_^>R|$axHCg;{(fswU&(FrE zrGA!he_nqpBl!3BPgupz1%&xrKp-GgvlVfmKRus+0RcRWY#nR>4F4X5`X`jJrHhNb zlM}$s_x&dk#MA0~$X3)tz;yMyw# zJ^W)a{znE=Pdg)9OB3fm&HQ)xpG5}zP2_*VnmZXeSp37he-^{Z$j%(_*VXv@)JT7l z+6mxb@8t4NLZkc%?eaOh^B)8LL6|=;pR0?d%|Af@ye)13lTYRT2QcIxayXm+_0#^a zN}vD#?*sPv%iihFyZd(p^nW94oNXQc^tQjl{@AKNCi*X!sTJU#uzzs$uh{<`@PD=w zebmHw0t5(%?bCMR|NjSwN&_6|O>B&uoxiDCs;nuZ@gdF%hA%a0rnb2TfE88f8DpNS z^Q<{qjW2wSBAd~Kl0~WdDEV5$f^Ie@>nG@^TJkI;7(DVqY|r<+b$`Xj;rILf1Jn@M zUQ9F}N-`YEkUyH37D_VmB(#s?8EgadMo<(hG8~EEu3v(f7o&!Lu`{J&*-cVUm*k{sgEL|TEA15zpbdpzgN1IvfF{a!Ti83eshp3GfBG_`Odn2uzaqa;gfwcGLEuk z7?RXa-jCE7+!U-y2Gpeg_HD(qGn@u<#$7txs zugO+L+(*vmg}c6T0`IIuUO>qA6(Q?J=^upJr#K;pn%>?GY395RP9-}n*!Lbf&fV2$ zcb>{~W^6YlG)1w zG*B0_1?O-=+V0oMv(69!Dfyi+eGTa2;aNfnklr*Wh3}0hzx+TJff8G!1Q@PZrF+{f zVn(`=R8gFuK}*zT(0qjg<>w{2pE)=3_56>Rw5UvOK%hhLW^@smqPV8NNCUUXeGep# zA=pxu$Ia6rum}{)lJLWC$W{#ZEr{um%eNi@&hW%(@Ki1G))pL3NDvr& zNh}@?e~enq-x_?*Hui$}hj0564TunMy+3IX`pMKU|Aq#NPL_5q>Xy!yp9bN-`Jkew zFt3R96@f-|Oo*B&kO&U^a!BI+0|;zvWiWw>A}$>X)ybckM}lMBZg;GEhwT>9qavdB zBxF{{FX@(V6KTlII5FqRcly4AaN2w7$M$v?Xxd;o1b!io7h^GTq$Bz=&uB}&P;apA zmv#r#=I_&S{%N6{#j3K_J19L&Wya!zv?BIye95aDQ`+a5R5YF0J~^v~hE$nGhEhufU56_#JYB3*xVR+%g`9uB;vHgd1TVw>>}Qax7q$itCFc5 z9j8|s8_Q4$whq!ErCKY z>n(+Q^uv4`=4o+Mksm^<8;MkXU$b)L@&gCQy#TGzUW?(`iLc+S=9IXgdi8z;=lZB1H}bMJ(A_Edt85FZ zBsu81m}PXrWrqkGUXJx)qa2suHyti!eryLc|KwS?O3(i0|9$+2>eI7r?2}qKJ1GLC zdpELmAhKZ|TXHbUVcq2wlPvCJ$C2V=G*-#$_jybEekA&QOcz4oirkBF^9x|Fbl%(s zgm`l#VTrCp{a06_*O5g{Q}_2Hj6QcRfF%D3RdF3B!w_3k#)dr{dZOo5)1e$2kyP zr=d2#Z|yM@nHK$VSQn@lgb4GjBh=;0NhO>vYllqP9?k== z=TIVp?}R+&-xKu8;5}PK>+zu<)Uy<&6>jn_q9)DjpJH9~L-jfU zWA5LJky(P@Fi7$~^OJZ+FT7&0#mX>QrL!t0IqO@7J8#E*ZBQfV!v?*l9Oz*_Bs+Aq z4^Ou`MK#0TB2kEf1HH8p9q)R5+&>_Bf&Z)KXx|dbZhrcH<#sU^Zs>v`x~%kr~&!LRhWPF zSKTl@l&c$&fh(`d;GrZw-Unv7LyC;4iY|x;69z;GnKP89pG>Ijo-5ZP8OLylS_Dog zcV9Om0tv;^D!&h`L3+4iR`B%n6WL#>s3{#78xF405S50B6^I&+~ zL#(d?MIUu0ZVqm-pXYGJx^iEJIai1>cz2#t66&EI-cw|h8QqdVH_Ra`)alT#n zr?4x#3@n~D04W5Qv9PRteI02+23~H{bc+yfNEzrlfGN(Aw!%hm8KCo0!o=Z{2us6y zNmhaQ=wLchVbdr(ugfAKeP0w`(|ympl4E~vUrk#yH&tmon4IUEUekGad+@z!COlcZ z_qj*Af-RSWJ#iY(|83*U$jo=}+h{$LLaf+-FwvLOP>QjBof5~a+OVj1@3b_z7UIW! zP0H0m5i?_j$Pl&ebcGu>$F3Q+J>w3aI*{JfX=bjAaxNc>4~(|isc;2}`0j;bQ48m@fOyV2OZIxEPcI?3+5@Bma%ei^J!Nub{+Ab*c<8oSo2fo#u(d;sYFj{tYiz<<#3(=taQQxC1heAJ5*=E=C zjNi|-h3DGskgLn-*n4JVH8Q-yI=$)EOi^x#3riI|#I=El4hQ@VC|`yA z16;)Xca7SD^Rc5U)_1}0p2w7C2|RMyRx0Juu--{bw~X4VVK`7pvz5`wOcl{YblwYT zZ0wE9OZ8S=%x+-L_)2Q&-C3=Wd?4wnQhhP&aCW{it77sFL1Qaa6IZIddY#-!bpU_MfL8^S*;52tmx>hvu5WyQ#*63F<1g})?muLQFe$Z`YIKZea3W4zlW=m8cb zKb*u&FF)iL#KB(~U4;}>;4!dk(C4$(4VfN-x<#xjw2lT&r*y1E3{JjN5BE-+eKAhMh;ehWD1w_!w6%_vcrYnWRh$f4We*^_-*msJ zel~|%b#wU|Kk^m@!$ZA zaOePYb@8fGAJagCHNx&5wLzN4WjALbb+o4P<af6Ho#U^487HOB zunOrjtfKl4&QaXxk3xaxze203glYLcL8PJX5aA-CK$w@Xs990P21LbvF=JjNHYQ>m z1=z~(nvrERv(0lVs8T*agkzkriv*>iLEbN4+Fvv6ztq3p{(M5%K>~onj+|?J` zys|9NAfxvs-n$vNnC=i_yr0JAV;J4g_0SAMPmI>}hT1r_C3z+dO zuLz@cDj8i?@gO6*&N}*Qp9OWj*+CNJ6@WLoYmIs=l-YJ105$HbWDO&hSzgPwUWTRK zYI%v>UT0K^s!=SXgsMKcLU}P(F!mOR=EiJJnnP|lgA=2dL_fIw1pjN6 z19e*6`5eCRU{!mo=3SkgG8mCNP)awlr^h!`D>phes)sQbJq1AXRTZYXJRx<$DSbYr zAWUnIV6!e%6%6r4vA+~~wT%$<^GqPQ+U5n;c*xjHq%VzLrNx2J|z2p30;5F

=o<)SUG^0<=c-geSDC*Sj2P)X<#S!++)) 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

-qs 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%0Ar3^I*kAHG9S7_RaV+)_P3mLwg_eW2CNwC;f(U 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�$^BfRu6C zfNiZ@C~y@kJ%VOK_u^SE2-myrMTWq${c8CPl({qd1G-CZ=^Yu&Io7W?)D&ZM{8$a(U}HC z1VIsKg43jpcM3cTM!t&e z0MnpabMD+r`Nkb>8|Fu-bL-SugBn5D7n3-JtrZaB+KfQtF6_)Ztv{AR$8b^Q?6#jo zd7$CGwJ1Vyi;mVD>b`vwW^~hVofft2SM_PtR9Vh^%67%juo7p$$)iQKRdw>*w-S0DpFU zRw(+D5G3@HEjeeKAo-ot9-5wWgwrI#J(Xvs;1Yp7jlL2%zGrGzj9lnr9+?CAl&3kZm0Hka|F=)r9-y%ozR)~S2 zXcx9QWHwCJ)NmFEH><%nl*vMqUM@FfBsA$4tN?Nh*anPhxM%GH-eg1(WI;B1R=^U* zW-@`p!dbrE5jQ^4JAP!s9_)^#p^ltRZ!IsBanF@fMBVPn8=aOdFqA7mmqs-@yPb!C zJ=E^&6^Y10WZTnyft(BhA8KBszVJov`JN*YdTjZn+FZz;Fqd+=(2Ku$piM%;2|Gh! z!}&3JFq?WjhjTr@NMtDYh5{rP1;4mMt+D{$ux1`-SI8R28;qSoA)W$Wj;o4E1NA{oO48_+)_Z1UTAulGAv@|9!rVsO_$K9Dh~<`VfQc zi3+ZpQ79cVd7#Oa!MJ?ydUV)5^}05PK$_HDES~Y78m> zE#n@gi#)I{kYa)D0p7$T^0B}t#sL6!1oeX0xF`Pxzu@j|y@XM>`?-Q5C@@aqXuWt!UNko=933kTcrobGz2;PW_n|0z5>;#LIJP}i+kEeaKPa*=LkA2l1mcaLQRB@ zeF2REnt(7K_lQW4z_?X>6Z=Zm59kxk;C@iUdO3y!M_SKMB1I9v(6Hr!f zgJaou&Q*qR_MbRxGp;@u61qAW!9fZ0X3;~~Qv<>{5&H(E+rq(I$}!Nk98N>9T|gcc z1U@3(ePJ!lM;+VUN9gKyd%n*)0RBh_3=d7=?^NcXv7;HZFn<~iu00LwmWpg<0An@o zVQUWSUSY&1&B@*b1I0c2INt0{YLmSUMuU5iJ}c~)MS literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..591649f7d19464ad63cd64e029d253369dfce7b8 GIT binary patch literal 114829 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%0A}paEB!Qj$eaE* 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