View Javadoc

1   /*
2    * Copyright (c) 1998-2004 The Jgroup Team.
3    *
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.
7    *
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.
12   *
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.
16   *
17   */
18   
19  package jgroup.relacs.config;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.NoSuchElementException;
28  import java.util.Set;
29  
30  import jgroup.core.ConfigurationException;
31  import jgroup.experiment.Runnable;
32  
33  import org.w3c.dom.Element;
34  import org.w3c.dom.Node;
35  import org.w3c.dom.NodeList;
36  
37  
38  /**
39   * The <code>ExperimentConfig</code> object contains the configuration for the 
40   * <code>DaemonExperiment</code> application.
41   * 
42   * @author Bjarte Svaeren
43   * @author Hein Meling
44   */
45  public class ExperimentConfig
46   implements ConfigurationObject
47  {
48  
49    ////////////////////////////////////////////////////////////////////////////////////////////
50    // Tags
51    ////////////////////////////////////////////////////////////////////////////////////////////
52  
53    private static final String SERVERS_TAG      = "Servers";
54    private static final String CLIENTS_TAG      = "Clients";	
55    private static final String PROPERTY_TAG     = "Property";
56    private static final String REPEATSCRIPT_TAG = "RepeatScript";
57    private static final String PREEXP_TAG       = "PreExp";
58    private static final String POSTEXP_TAG      = "PostExp";
59    private static final String EXP_TAG          = "Exp";
60    private static final String RUN_TAG          = "Run";
61    private static final String SLEEP_TAG        = "Sleep";
62    private static final String SYSPROPERTY_TAG  = "sysproperty";
63    
64    private static final String URL_ATTRIB      = "url";
65    private static final String KEY_ATTRIB      = "key";
66    private static final String SRC_ATTRIB      = "src";
67    private static final String VALUE_ATTRIB    = "value";
68    private static final String NAME_ATTRIB     = "name";
69    private static final String TARGET_ATTRIB   = "target";
70    private static final String DELAY_ATTRIB    = "delay";
71  
72    ////////////////////////////////////////////////////////////////////////////////////////////
73    // Fields
74    ////////////////////////////////////////////////////////////////////////////////////////////
75  
76    private DistributedSystemConfig serverConfig  = null;
77    private ClientsConfig clientConfig  = null;
78  
79    /** The global properties map */
80    private Map properties = new HashMap();
81  
82    /** Map containing the actual values associated with a runnable */
83    private Map sysproperties = new HashMap();
84  
85    /** Private map to maintain variable names associated with a runnable */
86    private Map varSysproperties = new HashMap();
87  
88    /** List of run objects constituting the actual experiment */
89    private List experimentRunList = new ArrayList();
90  
91    /** List of run objects to be run before the experiment */
92    private List preExperimentRunList = new ArrayList();
93  
94    /** List of run objects to be run after the experiment */
95    private List postExperimentRunList = new ArrayList();
96  
97    /** The experiment name */
98    private String experimentName;
99  
100   /**
101    * Initialized to the default repeat script, which will be used
102    * unless the experiment.xml file redefine the repeat script.
103    */
104   private String repeatScript =
105     "int repeats = properties.getIntProperty(\"repeat.experiment\", 1);\n" +
106     "for (int l = 1; l <= repeats; l++) {\n" +
107     "  properties.set(\"repeat\", l);\n" +
108     "  experiment.run();\n" +
109     "}";
110 
111   ////////////////////////////////////////////////////////////////////////////////////////////
112   // Constructors
113   ////////////////////////////////////////////////////////////////////////////////////////////
114 
115   /**
116    * Constructs an empty <code>ExperimentConfig</code>.
117    */
118   public ExperimentConfig() {}
119 
120 
121   ////////////////////////////////////////////////////////////////////////////////////////////
122   // Methods from ConfigurationObject
123   ////////////////////////////////////////////////////////////////////////////////////////////
124   
125   /**
126    * Parses the specified element.
127    *  
128    * @see jgroup.relacs.config.ConfigurationObject#parse(org.w3c.dom.Element)
129    */
130   public void parse(Element elm)
131     throws ConfigurationException
132   {
133     /* List of elements within <Experiment> tag */
134     NodeList elementList = elm.getChildNodes();
135     /* Parse the elements in the list */
136     for (int i=1; i <= elementList.getLength(); i++) {
137       Node node = elementList.item(i);
138       if (node != null && !node.getNodeName().startsWith("#")) {
139         elm = (Element) node;
140         if (ConfigParser.checkTag(elm, SERVERS_TAG))
141           parseServers(elm);
142         else if (ConfigParser.checkTag(elm, CLIENTS_TAG))
143           parseClients(elm);
144         else if (ConfigParser.checkTag(elm, PROPERTY_TAG))
145           parseProperty(elm);
146         else if (ConfigParser.checkTag(elm, REPEATSCRIPT_TAG))
147           parseRepeatScript(elm);
148         else if (ConfigParser.checkTag(elm, EXP_TAG)) {
149           // Parse expName
150           experimentName = ConfigParser.getAttrib(elm, NAME_ATTRIB, true);
151           parseExp(elm, EXP_TAG);
152         }
153         else if (ConfigParser.checkTag(elm, PREEXP_TAG))
154           parseExp(elm, PREEXP_TAG);
155         else if (ConfigParser.checkTag(elm, POSTEXP_TAG))
156           parseExp(elm, POSTEXP_TAG);
157         else
158           throw new ConfigurationException("Unknown tag: " + elm.getTagName());
159       }
160     }
161     
162     // Create empty client config if the experiment *.xml does not define it.
163     if (clientConfig == null) {
164       clientConfig = new ClientsConfig();
165       clientConfig.getAllHosts().removeAllHosts();
166     }
167   }
168 
169 
170   ////////////////////////////////////////////////////////////////////////////////////////////
171   // Private methods for parsing
172   ////////////////////////////////////////////////////////////////////////////////////////////
173 
174   /**
175    * Uses ConfigParser to parse the server config file.
176    * @param elm
177    */
178   private void parseServers(Element elm) 
179     throws ConfigurationException
180   {
181     // Read url attribute
182     String serversURL = ConfigParser.getAttrib(elm, URL_ATTRIB, false);
183     serversURL = parseParameter(serversURL, properties);
184     // override the system config property if defined elsewhere.
185     System.setProperty("jgroup.system.config", serversURL);
186 
187     try {
188       // Get the existing config object, and clear the HostSet
189       serverConfig = (DistributedSystemConfig) ConfigParser.getConfig(DistributedSystemConfig.class);
190       serverConfig.getAllHosts().removeAllHosts();
191     } catch (IllegalStateException e) {
192       // No files have been parsed yet 
193     } catch (NullPointerException e) {
194       // No server files have been parsed yet 
195     }
196     
197     // If there is no URL, parse the list of servers.
198     if (serversURL == null || serversURL.length() == 0) {
199       serverConfig = new DistributedSystemConfig(); 
200       serverConfig.parse(elm);
201     } else {
202       /* Parse the config file and get a config object */
203       ConfigParser.parse(serversURL, true);
204       serverConfig = (DistributedSystemConfig) ConfigParser.getConfig(DistributedSystemConfig.class);   
205     }
206   }
207   
208   
209   /**
210    * Uses jgroup.relacs.config.ClientsConfig.parse() to parse the clients.
211    * @param elm
212    */
213   private void parseClients(Element elm)
214     throws ConfigurationException
215   {
216     String clientsURL = ConfigParser.getAttrib(elm, URL_ATTRIB, false);
217     clientsURL = parseParameter(clientsURL, properties);
218 
219     try {
220       // Get the existing config object, and clear the HostSet
221       clientConfig = (ClientsConfig) ConfigParser.getConfig(ClientsConfig.class);
222       clientConfig.getAllHosts().removeAllHosts();
223     } catch (IllegalStateException e) {
224       // No files have been parsed yet 
225     } catch (NullPointerException e) {
226       // No client files have been parsed yet 
227     }
228 
229     // If there is no URL, parse the list of clients.
230     if (clientsURL.length() == 0) {
231       clientConfig = new ClientsConfig(); 
232       clientConfig.parse(elm);
233     } else {
234       // If there is an URL, let ConfigParser parse it
235       ConfigParser.parse(clientsURL, true);      
236       clientConfig = (ClientsConfig) ConfigParser.getConfig(ClientsConfig.class);
237     }
238   }
239   
240   
241   /**
242    * @param elm
243    */
244   private void parseProperty(Element elm) 
245     throws ConfigurationException
246   {
247     String key   = ConfigParser.getAttrib(elm, KEY_ATTRIB, true);
248     String value = ConfigParser.getAttrib(elm, VALUE_ATTRIB, true);
249     properties.put(key, parseParameter(value, properties) );
250   }
251 
252 
253   private void parseRepeatScript(Element elm)
254     throws ConfigurationException
255   {
256     String src = ConfigParser.getAttrib(elm, SRC_ATTRIB, false);
257     if (src != null && src.endsWith(".bsh")) {
258       repeatScript = "config" + File.separator + src;
259     } else {
260       /* Get child nodes of the RepeatScript tag */
261       NodeList elementList = elm.getChildNodes();
262       /* Parse the elements in the list */
263       for (int i=1; i <= elementList.getLength(); i++) {
264         Node node = elementList.item(i);
265         if (node != null && node.getNodeName().startsWith("#cdata-section")) {
266           repeatScript = node.getNodeValue();
267         }
268       }
269     }
270   }
271 
272 
273   /**
274    * @param elm
275    * @param map the map into which the expanded sysproperties should be placed
276    */
277   private void parseSysProperties(Element elm, Map map) 
278     throws ConfigurationException
279   {
280     /* List of elements within <Run> tag */
281     NodeList elementList = elm.getChildNodes();
282 
283     /* Parse the elements in the list */
284     for (int i=1; i <= elementList.getLength(); i++) {
285       Node node = elementList.item(i);
286       if (node != null && !node.getNodeName().startsWith("#")) {
287         elm = (Element) node;
288         if (elm.getTagName().equalsIgnoreCase(SYSPROPERTY_TAG)) {
289           String key   = ConfigParser.getAttrib(elm, KEY_ATTRIB, true);
290           String value = ConfigParser.getAttrib(elm, VALUE_ATTRIB, true);
291 
292           if (map.containsKey(key))
293             throw new ConfigurationException("Multiply defined property key: " + key); 
294           map.put(key, parseParameter(value, properties));
295         }
296       }
297     }
298   }
299 
300 
301   /**
302    * @param elm
303    */
304   private void parseExp(Element elm, String expType) 
305     throws ConfigurationException
306   {
307     List runList;
308     if (expType.equals(EXP_TAG)) {
309       runList = experimentRunList;
310     } else if (expType.equals(PREEXP_TAG)) {
311       runList = preExperimentRunList;
312     } else if (expType.equals(POSTEXP_TAG)) {
313       runList = postExperimentRunList;
314     } else {
315       throw new ConfigurationException("Unknown experiment type: " + expType);
316     }
317 
318     /* List of elements within <Exp> tag */
319     NodeList elementList = elm.getChildNodes();
320     /* Parse the elements in the list */
321     for (int i=1; i <= elementList.getLength(); i++) {
322       Node node = elementList.item(i);
323       if (node != null && !node.getNodeName().startsWith("#")) {
324         elm = (Element) node;
325         if (ConfigParser.checkTag(elm, RUN_TAG)) {
326           Object runObj = parseRun(elm);
327           if (runObj != null)
328             runList.add(runObj);
329         } else if (ConfigParser.checkTag(elm, SLEEP_TAG)) {
330           Integer delay = parseSleep(elm);
331           runList.add(delay);
332         } else {
333           throw new ConfigurationException("Unknown tag: " + elm.getTagName());
334         }
335       }
336     }		
337   }
338 
339 
340   /**
341    * @param elm
342    */
343   private Object parseRun(Element elm)
344     throws ConfigurationException
345   {
346     String target = ConfigParser.getAttrib(elm, TARGET_ATTRIB, false);
347 
348     /* If there is no target attribute, use the name attribute */		
349     if (target == null || target.length() == 0) {
350       String name = ConfigParser.getAttrib(elm, NAME_ATTRIB, true);
351       // Don't run Runnables with ! in front.
352       if (name.startsWith("!"))
353         return null;
354       try {
355         Class runClass = Class.forName("jgroup.experiment.runnables." + name);
356         Runnable runElement = (Runnable) runClass.newInstance();
357 
358         Map spMap = new HashMap();
359         parseSysProperties(elm, spMap);
360         sysproperties.put(runElement, spMap);
361         Map vspMap = new HashMap();
362         parseSysProperties(elm, vspMap);
363         varSysproperties.put(runElement, vspMap);
364 
365         return runElement;
366       } catch (ClassNotFoundException e) {
367         e.printStackTrace();
368         throw new ConfigurationException("Runnable class jgroup.experiment.runnables." + name + " not found.");				
369       } catch (Exception e) {
370         e.printStackTrace();
371         throw new ConfigurationException("Error creating runnable element " + name + ".");								
372       }			
373     } else {
374       // Don't run targets with ! in front.
375       if (target.startsWith("!"))
376         return null;
377       StringBuilder antCmd  = new StringBuilder(target);
378       return parseTarget(elm, antCmd);
379     }
380   }
381 
382 
383   /**
384    * @param elm
385    */
386   private Object parseTarget(Element elm, StringBuilder antCmd)
387     throws ConfigurationException
388   {
389     /* List of elements within <Run> tag */
390     NodeList elementList = elm.getChildNodes();
391     /* Parse the elements in the list */
392     for (int i=1; i <= elementList.getLength(); i++) {
393       Node node = elementList.item(i);
394       if (node != null && !node.getNodeName().startsWith("#")) {
395         elm = (Element) node;
396         if (elm.getTagName().equalsIgnoreCase(SYSPROPERTY_TAG)) {
397           String key   = elm.getAttribute(KEY_ATTRIB);
398           String value = elm.getAttribute(VALUE_ATTRIB);
399           if (key.length() == 0)
400             throw new ConfigurationException("Missing key attribute.");
401           if (value.length() == 0)
402             throw new ConfigurationException("Missing value attribute.");
403           antCmd.append(" -D");
404           antCmd.append(key);
405           antCmd.append("=");
406           antCmd.append(value);
407         }
408       }
409     }
410     /* Return the ant command with arguments to be run */
411     return antCmd.toString();
412   }
413   
414   
415   /**
416    * @param elm
417    * @return the delay to sleep in seconds
418    */
419   private Integer parseSleep(Element elm)
420     throws ConfigurationException
421   {
422     try {
423       /*
424        * Get an Integer value from the delay tag.
425        * Integer is used instead of int so we can add the
426        * delay to the runlist (as an object).
427        */
428       Integer delay = Integer.valueOf(ConfigParser.getAttrib(elm, DELAY_ATTRIB, false));
429       /* Make sure the delay is not a negative value */
430       if (delay.compareTo(new Integer(0)) < 0)
431         throw new ConfigurationException("Negative delay value for Sleep tag is not allowed");
432       return delay;
433     } catch (NumberFormatException e) {
434       e.printStackTrace();
435       throw new ConfigurationException("Illegal delay value for Sleep tag");
436     }		
437   }
438 
439 
440   /**
441    * Utility method for parameter expansion
442    * 
443    * @param value
444    * @param props the properties map from which to get the value
445    * @return String containing the expanded parameter
446    * @throws ConfigurationException
447    */
448   private String parseParameter(String value, Map props)
449     throws ConfigurationException
450   {
451     StringBuilder expandedValue = new StringBuilder();
452     int index 	 = 0;
453     
454     while (index < value.length()) {
455       int paramStart = value.indexOf("${", index);			
456     
457       // No more parameters - append rest of string
458       if (paramStart == -1) { 
459       	expandedValue.append(value.substring(index));
460       	index = value.length();
461       }	else {
462         int paramEnd = value.indexOf("}", paramStart);
463         if (paramEnd == -1)
464           throw new ConfigurationException("Parameter does not end with '}'");
465 
466         String parameter = value.substring(paramStart + 2, paramEnd);
467         String parameterValue = (String) props.get(parameter);
468         // if the experiment.xml file does not include the parameter,
469         // check the system properties.
470         if (parameterValue == null)
471           parameterValue = System.getProperty(parameter);
472         if (parameterValue == null) {
473           parameterValue = value.substring(paramStart, paramEnd+1);
474         }
475 
476         // Add the parsed part of the string and expanded parameter
477         expandedValue.append(value.substring(index, paramStart));
478         expandedValue.append(parameterValue);	
479         index = paramEnd + 1;
480       }
481     }
482     return new String(expandedValue);
483   }
484 
485 
486   ////////////////////////////////////////////////////////////////////////////////////////////
487   // Access methods
488   ////////////////////////////////////////////////////////////////////////////////////////////
489 
490   /**
491    * Update the properties of the associated runnable object to reflect
492    * any changes in the global properties.
493    */
494   public void updateProperties(Runnable runnable)
495     throws ConfigurationException
496   {
497     Set entries = ((Map) varSysproperties.get(runnable)).entrySet();
498     Map props = getProperties(runnable);
499     for (Iterator iter = entries.iterator(); iter.hasNext();) {
500       Map.Entry entry = (Map.Entry) iter.next();
501       String value = (String) entry.getValue();
502       value = parseParameter(value, properties);
503       props.put(entry.getKey(), value);
504     }
505   }
506 
507   /**
508    * Update the internal properties map with the given key value associatation.
509    * This method is meant to be used from external beanshell scripts.
510    */
511   public void set(String key, Object val)
512   {
513     if (!(val instanceof String)) {
514       properties.put(key, val.toString());
515     } else {
516       properties.put(key, val);
517     }
518   }
519 
520   public DistributedSystemConfig getServerConfig()
521   {
522     return serverConfig;
523   }
524 
525   public ClientsConfig getClientConfig()
526   {
527     return clientConfig;
528   }
529 
530   public String getExperimentName()
531   {
532     return experimentName;
533   }
534 
535   public List getPreExperimentRunList()
536   {
537     return preExperimentRunList;
538   }
539 
540   public List getExperimentRunList()
541   {
542     return experimentRunList;
543   }
544 
545   public List getPostExperimentRunList()
546   {
547     return postExperimentRunList;
548   }
549 
550   public String getRepeatScript()
551   {
552     return repeatScript;
553   }
554 
555   public Map getProperties()
556   {
557     return properties;
558   }
559 
560   /**
561    * Returns the map of sysproperties associated with the given
562    * <code>Runnable</code> object.
563    *
564    * @param runnable
565    * @return
566    */
567   public Map getProperties(Runnable runnable)
568   {
569     return (Map) sysproperties.get(runnable);
570   }
571 
572   /**
573    * Returns a global property. 
574    * @param name
575    * @return
576    * @throws NoSuchElementException
577    */
578   public String getProperty(String name)
579     throws NoSuchElementException 
580   {
581     String strval = (String) properties.get(name);
582     if (strval == null || strval.length() == 0) {
583       strval = System.getProperty(name);
584       if (strval == null || strval.length() == 0)
585         throw new NoSuchElementException("No property named " + name);
586     }
587 
588     return strval;    
589   }
590 
591 
592   /**
593    * Returns a global property or the given default value if no such
594    * property was defined.
595    */
596   public String getProperty(String name, String defaultVal)
597   {
598     String strval = (String) properties.get(name);
599     if (strval == null || strval.length() == 0)
600       return defaultVal;
601     return strval;
602   }
603 
604 
605   /**
606    * Returns a 'global' property as an int.
607    * @param name
608    * @return
609    */
610   public int getIntProperty(String name)
611   {
612     return Integer.valueOf(getProperty(name)).intValue();
613   }
614   
615   
616   /**
617    * Returns a 'global' property as an int, and returns the specified
618    * default value if the property has not been set.
619    * @param name
620    * @param defaultValue
621    * @return
622    */
623   public int getIntProperty(String name, int defaultValue)
624   {
625     String strval = (String) properties.get(name);
626     if (strval == null || strval.length() == 0)
627       return defaultValue;
628     return Integer.valueOf(strval).intValue();		
629   }
630 	
631 	
632   /**
633    * Returns a 'global' property as a boolean. If the property has
634    * not been set, false is returned.
635    * @param name
636    * @return
637    */
638   public boolean getBooleanProperty(String name)
639   {
640     String strval = (String) properties.get(name);
641     if (strval == null || strval.length() == 0)
642       return false;
643     return Boolean.valueOf(strval).booleanValue();
644   }
645 
646 
647   /**
648    * Returns a property assosciated with a Runnable object.
649    * @param runnable
650    * @param name
651    * @return
652    * @throws NoSuchElementException
653    */
654   public String getProperty(Runnable runnable, String name)
655     throws NoSuchElementException 
656   {
657     if (!sysproperties.containsKey(runnable))
658       throw new NoSuchElementException("No sysproperties have been parsed for Runnable " + runnable.getClass().getName());
659     String strval = (String) ((Map) sysproperties.get(runnable)).get(name);
660     if (strval == null || strval.length() == 0)
661       throw new NoSuchElementException("No property named " + name + " for Runnable " + runnable.getClass().getName());
662     return strval;
663   }
664 		
665 	
666   /**
667    * Returns a property assosciated with a Runnable object.
668    * @param runnable
669    * @param name
670    * @param defaultValue
671    * @return
672    */
673   public String getProperty(Runnable runnable, String name, String defaultValue)
674   {
675     try {
676       return getProperty(runnable, name);
677     } catch (NoSuchElementException e) {
678       // update the map with the default value
679       Map spMap = (Map) sysproperties.get(runnable);
680       if (spMap == null) {
681         spMap = new HashMap();
682       }
683       spMap.put(name, defaultValue);
684       return defaultValue;
685     }
686   }
687 
688   
689   /**
690    * Returns a property assosciated with a Runnable object as an int.
691    * @param runnable
692    * @param name
693    * @return
694    */
695   public int getIntProperty(Runnable runnable, String name)
696   {
697     return Integer.valueOf(getProperty(runnable, name)).intValue(); 		
698   }
699 
700 
701   /**
702    * Returns a property assosciated with a Runnable object as an int,
703    * and returns the specified default value if the property has not
704    * been set.  
705    * @param runnable
706    * @param name
707    * @param defaultValue
708    * @return
709    */
710   public int getIntProperty(Runnable runnable, String name, int defaultValue)
711   {
712     Map spMap = (Map) sysproperties.get(runnable);
713     if (spMap == null) {
714       spMap = new HashMap();
715     }
716     String strval = (String) spMap.get(name);
717     if (strval == null || strval.length() == 0) {
718       // update the map with the default value
719       spMap.put(name, String.valueOf(defaultValue));
720       return defaultValue;
721     }
722     return Integer.valueOf(strval).intValue();
723   }
724 
725 
726   /**
727    * Returns a property assosciated with a Runnable object as a
728    * boolean. If the property has not been set, false is returned.
729    * @param runnable
730    * @param name
731    * @return
732    */	
733   public boolean getBooleanProperty(Runnable runnable, String name)
734   {
735     if (!sysproperties.containsKey(runnable))
736       throw new NoSuchElementException("No sysproperties have been parsed for Runnable " + runnable.getClass().getName());
737     String strval = (String) ((Map) sysproperties.get(runnable)).get(name);
738     if (strval == null || strval.length() == 0)
739       return false;
740     return Boolean.valueOf(strval).booleanValue();
741   }
742 
743 
744   /**
745    * Returns a property assosciated with a Runnable object as a
746    * boolean. If the property has not been set, the default is returned.
747    * @param runnable
748    * @param name
749    * @param defaultValue
750    * @return
751    */ 
752   public boolean getBooleanProperty(Runnable runnable, String name, boolean defaultValue)
753   {
754     Map spMap = (Map) sysproperties.get(runnable);
755     if (spMap == null) {
756       spMap = new HashMap();
757     }
758     String strval = (String) ((Map) sysproperties.get(runnable)).get(name);
759     if (strval == null || strval.length() == 0) {
760       // update the map with the default value
761       spMap.put(name, Boolean.valueOf(defaultValue).toString());
762       return defaultValue;
763     }
764     return Boolean.valueOf(strval).booleanValue();
765   }
766 
767   private static final String DEFAULT_SSH_CMD    = "ssh";
768   private static final String DEFAULT_SCP_CMD    = "scp";
769   private static final String DEFAULT_ANT_CMD    = "ant -emacs -buildfile ";
770   private static final String DEFAULT_JGROUP_DIR = "/tmp/Jgroup";
771 
772   public String ssh(String hostname)
773   {
774     String ssh = getProperty("jgroup.cmd.ssh", DEFAULT_SSH_CMD);
775     return ssh + " -x " + hostname + " ";
776   }
777 
778   public String scp(String fromHost, String fromDir, String toDir)
779   {
780     String scp = getProperty("jgroup.cmd.scp", DEFAULT_SCP_CMD);
781     return scp + " " + fromHost + ":" + fromDir + "/* " + toDir;
782   }
783 
784   public String ant(String target, String[] expProperties, Runnable runnable)
785   {
786     StringBuilder buf = new StringBuilder(getProperty("jgroup.cmd.ant", DEFAULT_ANT_CMD));
787     buf.append(getProperty("jgroup.dir", DEFAULT_JGROUP_DIR));
788     buf.append(File.separator);
789     // ensure to override jgroup.system.config of ant targets defined in build.xml
790     buf.append("build.xml -Djgroup.system.config=");
791     buf.append(System.getProperty("jgroup.system.config"));
792     if (runnable != null) {
793       for (int i = 0; i < expProperties.length; i++) {
794         String prop = getProperty(runnable, expProperties[i], null);
795         if (prop != null && prop.length() > 0) {
796           buf.append(" -D");
797           buf.append(expProperties[i]);
798           buf.append("=");
799           buf.append(prop);
800         }
801       }
802     }
803     buf.append(" ");
804     buf.append(target);
805     return buf.toString();
806   }
807 
808   public String ant(String target)
809   {
810     return ant(target, null, null);
811   }
812 
813 } // END ExperimentConfig