2 * Copyright (c) 2003-2005 The BISON Project
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2 as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 package peersim.rangesim;
25 import peersim.config.*;
26 import peersim.core.*;
27 import peersim.util.*;
30 * This class is the main class for the Range Simulator. A range is
31 * a collection of values <em>S</em> to be assigned to a variable
32 * <em>v</em>. The Range Simulator invokes the standard Peersim
33 * simulator once for each distinct value. If multiple ranges
34 * <em>S1, S2, ..., Sn</em> are specified, the standard Peersim
35 * simulator is invoked for each element in
36 * <em>S1 * S2 * ... * Sn</em>.
38 * Ranges are specified with the following syntax:
40 range.[id] [var];[range]
44 * <LI> {@value #PAR_RANGE} is the prefix for all range
45 * specifications;</LI>
46 * <LI> <code>id</code> is an identifier; since they are not referred anywhere else,
47 * consecutive numbers are a good choice for range identifiers;</LI>
48 * <LI> <code>var</code> is a variable parameter </LI>
49 * <LI> <code>range</code> describes the collection of values to be associated
50 * to <code>var</code>, whose syntax and semantics is defined in
51 * {@link peersim.util.StringListParser}. </LI>
53 * Examples of range specifications are the following:
55 range.0 SIZE;2^10:2^18|*2
57 range.2 CHURN;0.05,0.10,0.20
59 * With this specification, the collection of values associated to
60 * <code>SIZE</code> is {2^10,2^11,...,2^18}; <code>K</code> contains
61 * {20,21,22,...,30}, while <code>CHURN</code> contains just the
64 * Values can be specified as constant expressions (like 2^10, (5+10), etc.)
65 * but variables cannot be used.
67 * A separate Java virtual machine is invoked to run each of the
68 * experiments. An attempt is done to run the same JVM version as
69 * the one running the Range Simulator; if this is not possible
70 * (for example due to path problems), the command shell mechanism
71 * is used to run the first JVM version found in the path.
73 * It is possible to specify options for the forked JVM using the
74 * {@value #PAR_JVM} parameter on the command line. For example,
75 * a command line like this:
77 java peersim.rangesim.RangeSimulator config.file jvm.options=-Xmx256m
79 * can be used to run the forked JVM with a maximum heap of 256MB.
81 * The new JVM inherits the same classpath as the JVM running the
82 * RangeSimulator. The {@value #PAR_JVM} parameter can be used to
83 * specify additional classpath specification.
85 * @author Alberto Montresor
86 * @version $Revision: 1.11 $
88 public class RangeSimulator implements ProcessHandler
91 // --------------------------------------------------------------------------
92 // Configuration parameters
93 // --------------------------------------------------------------------------
96 * This is the prefix of the config properties whose value vary during
97 * a set of experiments.
100 private static final String PAR_RANGE = "range";
103 * This config property can be used to set options in the JVMs that
104 * are forked to execute experiments with different configuration
108 public static final String PAR_JVM = "jvm.options";
111 // --------------------------------------------------------------------------
113 // --------------------------------------------------------------------------
115 /** Names of range parameters */
116 private String[] pars;
118 /** Values to be simulated, for each parameter */
119 private String[][] values;
121 /** The jvm options to be used when creating jvms */
122 private String[] jvmoptions;
124 /** Command line arguments */
125 private String[] args;
127 /** The current process that is executed */
131 // --------------------------------------------------------------------------
133 // --------------------------------------------------------------------------
136 * Main method of the system.
138 public static void main(String[] args)
140 RangeSimulator r = new RangeSimulator(args);
144 //--------------------------------------------------------------------------
146 //--------------------------------------------------------------------------
148 public RangeSimulator(String[] args)
151 // Check if there are no arguments or there is an explicit --help
152 // flag; if so, print the usage of the class
153 if (args.length == 0 || args[0].equals("--help")) {
158 this.args = args.clone();
160 // Read property file
161 System.err.println("Simulator: loading configuration");
162 Properties properties = new ParsedProperties(args);
163 Configuration.setConfig(properties);
165 // Read jvm options and separate them in different strings
166 String opt = Configuration.getString(PAR_JVM, null);
168 jvmoptions = new String[0];
170 jvmoptions = opt.split(" ");
172 // Parse range parameters
178 * Main method to be executed
182 // Shutdown thread management
183 ProcessManager t = new ProcessManager();
185 Runtime.getRuntime().addShutdownHook(t);
187 // Executes experiments; report short messages about exceptions that are
188 // handled by the configuration mechanism.
191 } catch (MissingParameterException e) {
192 Runtime.getRuntime().removeShutdownHook(t);
193 System.err.println(e + "");
195 } catch (IllegalParameterException e) {
196 Runtime.getRuntime().removeShutdownHook(t);
197 System.err.println(e + "");
200 Runtime.getRuntime().removeShutdownHook(t);
204 // --------------------------------------------------------------------
207 * Parses a collection of range specifications and returns the set of
208 * parameter that will change during the simulation and the values that
209 * will be used for those parameters.
211 private void parseRanges()
214 String[] ranges = Configuration.getNames(PAR_RANGE);
216 // Start is the first element in which ranges are stored
219 // If there is an explicit simulation.experiment or there are no
220 // ranges, put an experiment range at the beginning of the values.
221 // Otherwise, just use the ranges.
222 if (Configuration.contains(Simulator.PAR_EXPS) || ranges.length == 0) {
223 pars = new String[ranges.length + 1];
224 values = new String[ranges.length + 1][];
226 values[0] = StringListParser.parseList("1:"
227 + Configuration.getInt(Simulator.PAR_EXPS, 1));
230 pars = new String[ranges.length];
231 values = new String[ranges.length][];
235 for (int i = start; i < pars.length; i++) {
236 String[] array = Configuration.getString(ranges[i-start]).split(";");
237 if (array.length != 2) {
238 throw new IllegalParameterException(ranges[i],
239 " should be formatted as <parameter>;<value list>");
242 values[i] = StringListParser.parseList(array[1]);
246 // --------------------------------------------------------------------
249 * Selects the next set of values by incrementing the specified index
250 * array. The index array is treated as a vector of digits; the first is
251 * managed managed as a vector of digits.
253 private void nextValues(int[] idx, String[][] values)
255 idx[idx.length - 1]++;
256 for (int j = idx.length - 1; j > 0; j--) {
257 if (idx[j] == values[j].length) {
264 // --------------------------------------------------------------------
266 private void doExperiments(String[] args)
269 // Configure the java parameter for exception
270 String filesep = System.getProperty("file.separator");
271 String classpath = System.getProperty("java.class.path");
272 String javapath = System.getProperty("java.home") + filesep + "bin" + filesep
274 ArrayList<String> list = new ArrayList<String>(20);
279 // Add the jvm options
280 for (int i=0; i < jvmoptions.length; i++)
281 list.add(jvmoptions[i]);
283 // The class to be run in the forked JVM
284 list.add("peersim.Simulator");
286 // Parameters specified on the command line
287 for (int i=0; i < args.length; i++) {
291 // Since multiple experiments are managed here, the value
292 // of standard variable for multiple experiments is changed to 1
293 list.add(Simulator.PAR_EXPS+"=1");
295 // Activate redirection to separate stdout from stderr
296 list.add(Simulator.PAR_REDIRECT+"="+TaggedOutputStream.class.getCanonicalName());
297 int startlog = list.size();
300 // Create a placeholder for the seed
301 int startseed = list.size();
304 // Create placeholders for the range parameters
305 int startpar = list.size();
306 for (int i=0; i < values.length; i++)
309 // Execute with different values
310 int[] idx = new int[values.length]; // Initialized to 0
311 while (idx[0] < values[0].length) {
313 // Configure the argument string array
314 for (int j = 0; j < pars.length; j++) {
315 list.set(startpar + j, pars[j] + "=" + values[j][idx[j]]);
318 // Fill the log placeholder
319 StringBuffer log = new StringBuffer();
320 for (int j = 0; j < pars.length; j++) {
323 log.append(values[j][idx[j]]);
326 list.set(startlog, Simulator.PAR_REDIRECT+"."+
327 TaggedOutputStream.PAR_RANGES+"="+log);
329 // Fill the seed place holder
330 long seed = CommonState.r.nextLong();
331 list.set(startseed, CommonState.PAR_SEED+"="+seed);
333 System.err.println("Experiment: " + log);
335 executeProcess(list);
338 nextValues(idx, values);
343 //--------------------------------------------------------------------
346 * Execute the "command line" represented by this String list.
347 * The first argument is the process to be executed. We try
348 * to run the same JVM as the current one. If not possible,
349 * we use the first java command found in the path.
351 private void executeProcess(List<String> list)
353 // Prepare the argument array for process forking
354 String[] newargs = new String[list.size()];
358 ProcessBuilder pb = new ProcessBuilder(list.toArray(newargs));
359 pb.redirectErrorStream(true);
361 } catch (IOException e1) {
364 ProcessBuilder pb = new ProcessBuilder(list.toArray(newargs));
365 pb.redirectErrorStream(true);
367 } catch (IOException e2) {
368 System.err.println("Unable to launch a Java virtual machine");
373 // Read the output from the process and redirect it to System.out
375 BufferedReader toprint = new BufferedReader(new InputStreamReader(p
378 while ((line = getLine(toprint)) != null) {
379 if (line.length() == 0) {
380 System.out.println();
382 int last = line.charAt(line.length()-1);
383 if (last != TaggedOutputStream.TAG) {
384 System.err.println(line);
386 line = line.substring(0, line.length()-1);
387 System.out.println(line);
392 // We close all the files and we destroy the process. They are not
393 // cleaned when the process is closed. See:
394 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
395 // http://www.thescripts.com/forum/thread18019.html
397 p.getErrorStream().close();
398 p.getInputStream().close();
399 p.getOutputStream().close();
401 } catch (IOException e) {
405 // The static variable p (used also by ShutdownThread) is back to
406 // null - no process must be killed on shutdown.
413 //--------------------------------------------------------------------
415 private static String getLine(BufferedReader toprint)
418 return toprint.readLine();
419 } catch (IOException e) {
420 // If we get here, this means that the forked process has
421 // been killed by the shutdown thread. We just exit without
422 // printing this exception.
424 return null; // Never reached, but needed.
429 // --------------------------------------------------------------------
431 private static void usage()
433 System.err.println("Usage:");
434 System.err.println(" peersim.RangeSimulator <configfile> [property]*");
437 //--------------------------------------------------------------------
444 * Stop the process executing the external java virtual machine.
453 * Wait until the java virtual machine has terminated; it won't be
454 * used in this class, but you never know.
456 public void join() throws InterruptedException