View Javadoc

1   /*
2    * @(#)ExperimentParser.java Aug 18, 2003 Copyright (c) 2002-2005 Delft
3    * University of Technology Jaffalaan 5, 2628 BX Delft, the Netherlands. All
4    * rights reserved. This software is proprietary information of Delft University
5    * of Technology The code is published under the Lesser General Public License
6    */
7   package nl.tudelft.simulation.xml.dsol;
8   
9   import java.io.IOException;
10  import java.net.MalformedURLException;
11  import java.net.URL;
12  import java.net.URLClassLoader;
13  import java.text.DateFormat;
14  import java.util.ArrayList;
15  import java.util.Calendar;
16  import java.util.HashMap;
17  import java.util.Iterator;
18  import java.util.List;
19  import java.util.Map;
20  import java.util.Properties;
21  
22  import javax.naming.Context;
23  import javax.naming.InitialContext;
24  import javax.naming.NamingException;
25  
26  import nl.tudelft.simulation.dsol.ModelInterface;
27  import nl.tudelft.simulation.dsol.experiment.Experiment;
28  import nl.tudelft.simulation.dsol.experiment.ExperimentalFrame;
29  import nl.tudelft.simulation.dsol.experiment.Replication;
30  import nl.tudelft.simulation.dsol.experiment.TimeUnitInterface;
31  import nl.tudelft.simulation.dsol.experiment.Treatment;
32  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
33  import nl.tudelft.simulation.jstats.streams.MersenneTwister;
34  import nl.tudelft.simulation.jstats.streams.StreamInterface;
35  import nl.tudelft.simulation.language.io.URLResource;
36  import nl.tudelft.simulation.language.reflection.ClassUtil;
37  import nl.tudelft.simulation.logger.Logger;
38  
39  import org.jdom.Element;
40  import org.jdom.JDOMException;
41  import org.jdom.input.SAXBuilder;
42  
43  /***
44   * The ExperimentParser parses xml-based experiments into their java objects
45   * <br>
46   * (c) copyright 2002-2005 <a href="http://www.simulation.tudelft.nl">Delft
47   * University of Technology </a>, the Netherlands. <br>
48   * See for project information <a href="http://www.simulation.tudelft.nl">
49   * www.simulation.tudelft.nl </a> <br>
50   * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser
51   * General Public License (LGPL) </a>, no warranty.
52   * 
53   * @version 2.0 21.09.2003 <br>
54   * @author <a href="http://www.peter-jacobs.com/index.htm">Peter Jacobs </a>
55   */
56  public class ExperimentParser
57  {
58      /*** builder the xerces parser with validation turned on */
59      private static SAXBuilder builder = new SAXBuilder(
60              "org.apache.xerces.parsers.SAXParser", true);
61      static
62      {
63          // turns on Schema Validation with Xerces
64          builder.setFeature("http://xml.org/sax/features/validation", true);
65          builder.setFeature("http://apache.org/xml/features/validation/schema",
66                  true);
67          // Let's find the XSD file
68          String xsd = URLResource.getResource("/xsd/experiment.xsd")
69                  .toExternalForm();
70          builder
71                  .setProperty(
72                          "http://apache.org/xml/properties/schema/external-schemaLocation",
73                          "http://www.simulation.tudelft.nl " + xsd);
74      }
75  
76      /***
77       * constructs a new ExperimentParser This is a Utility Class.
78       */
79      protected ExperimentParser()
80      {
81          // A utility class should not be instantiated
82      }
83  
84      /***
85       * parses an experimentalFrame xml-file.
86       * 
87       * @param input the inputstream
88       * @return ExperimentalFrame the experimentalFrame
89       * @throws IOException whenever parsing fails
90       */
91      public static ExperimentalFrame parseExperimentalFrame(final URL input)
92              throws IOException
93      {
94          try
95          {
96              return ExperimentParser.parseExperimentalFrame(
97                      new InitialContext(), input);
98          } catch (NamingException exception)
99          {
100             throw new IOException(exception.getMessage());
101         }
102     }
103 
104     /***
105      * parses an experimentalFrame xml-file.
106      * 
107      * @param input the url of the xmlfile
108      * @param context the root context for the experimentalFrame
109      * @return ExperimentalFrame the experimentalFrame
110      * @throws IOException whenever parsing fails
111      */
112     public static ExperimentalFrame parseExperimentalFrame(
113             final Context context, final URL input) throws IOException
114     {
115         if (input == null)
116         {
117             throw new IOException("experiment URL=null");
118         }
119         try
120         {
121             Calendar calendar = Calendar.getInstance();
122             calendar.setTimeInMillis(System.currentTimeMillis());
123             String name = DateFormat.getDateTimeInstance().format(
124                     calendar.getTime());
125             Context root = context.createSubcontext(name);
126 
127             Element rootElement = builder.build(input).getRootElement();
128             List<Experiment> experiments = new ArrayList<Experiment>();
129             @SuppressWarnings("unchecked")
130             List<Element> experimentElements = (List<Element>) rootElement
131                     .getChildren("experiment");
132             int number = 0;
133 
134             for (Element element : experimentElements)
135             {
136                 Context experimentContext = root.createSubcontext("experiment["
137                         + number + "]");
138                 Experiment experiment = ExperimentParser.parseExperiment(
139                         experimentContext, element, input);
140                 experiment.setDescription("experiment " + number);
141                 experiments.add(experiment);
142                 number++;
143             }
144             ExperimentalFrame frame = new ExperimentalFrame(root, input);
145             frame.setExperiments(experiments);
146             return frame;
147         } catch (Exception exception)
148         {
149             Logger.warning(ExperimentParser.class, "parseExperimentalFrame",
150                     exception);
151             return null;
152         }
153     }
154 
155     /***
156      * parses an experiment xml-file.
157      * 
158      * @param context the context for this experiment
159      * @param url the url of the experimentfile
160      * @param rootElement the element representing the experiment
161      * @return ExperimentalFrame the experiment
162      * @throws IOException whenever parsing fails
163      */
164     public static Experiment parseExperiment(final Context context,
165             final Element rootElement, final URL url) throws IOException
166     {
167         try
168         {
169             ClassLoader loader = ExperimentParser.resolveClassLoader(url);
170             Experiment experiment = new Experiment(context);
171             Element modelElement = rootElement.getChild("model");
172             if (modelElement.getChild("class-path") != null)
173             {
174                 @SuppressWarnings("unchecked")
175                 List<Element> jarFiles = (List<Element>) modelElement.getChild(
176                         "class-path").getChildren("jar-file");
177                 URL[] urls = new URL[jarFiles.size()];
178                 int nr = 0;
179                 for (Iterator<Element> i = jarFiles.iterator(); i.hasNext();)
180                 {
181                     Element child = i.next();
182                     urls[nr] = URLResource.getResource(child.getValue());
183                     nr++;
184                 }
185                 loader = new URLClassLoader(urls, loader);
186             }
187             Thread.currentThread().setContextClassLoader(loader);
188             Class modelClass = Class.forName(modelElement
189                     .getChildText("model-class"), true, loader);
190 
191             ModelInterface model = (ModelInterface) ClassUtil
192                     .resolveConstructor(modelClass, null).newInstance();
193             experiment.setModel(model);
194 
195             Treatment treatment = ExperimentParser.parseTreatment(rootElement
196                     .getChild("treatment"), experiment, context);
197             experiment.setTreatment(treatment);
198 
199             Class simulatorClass = Class.forName(rootElement
200                     .getChildText("simulator-class"), true, loader);
201             SimulatorInterface simulator = (SimulatorInterface) ClassUtil
202                     .resolveConstructor(simulatorClass, null).newInstance();
203             experiment.setSimulator(simulator);
204             return experiment;
205         } catch (Exception exception)
206         {
207             Logger
208                     .warning(ExperimentParser.class, "parseExperiment",
209                             exception);
210             throw new IOException(exception.getMessage());
211         }
212     }
213 
214     // *********************** PRIVATE PARSING FUNCTIONS *******************//
215     /***
216      * parses an experiment xml-file.
217      * 
218      * @param input the inputstream
219      * @return Classloader the classLoader
220      */
221     private static ClassLoader resolveClassLoader(final URL input)
222     {
223         ClassLoader loader = Thread.currentThread().getContextClassLoader();
224         if (input.getProtocol().equals("file"))
225         {
226             try
227             {
228                 List<URL> urls = new ArrayList<URL>();
229                 String path = input.getPath().substring(0,
230                         input.getPath().lastIndexOf('/'))
231                         + "/";
232                 // We add the path
233                 urls.add(new URL("file:" + path));
234                 // If the classpath ends with src, we also add the bin
235                 if (path.endsWith("src/"))
236                 {
237                     path = path.substring(0, path.length() - 4) + "bin/";
238                     urls.add(new URL("file:" + path));
239                 } else
240                 {
241                     // We assume there might be a src & bin dir
242                     path = path.substring(0, path.length()) + "bin/";
243                     urls.add(new URL("file:" + path));
244                 }
245                 URL[] urlArray = urls.toArray(new URL[urls.size()]);
246                 URLClassLoader urlClassLoader = new URLClassLoader(urlArray,
247                         loader);
248                 return urlClassLoader;
249             } catch (MalformedURLException exception)
250             {
251                 return loader;
252             }
253         }
254         return loader;
255     }
256 
257     /***
258      * parses the dateTime
259      * 
260      * @param value the string value in the yyyy-mm-ddThh:mm:ss format
261      * @return long the amount of milliseconds since 1970.
262      */
263     private static long parseDateTime(final String value)
264     {
265         Calendar calendar = Calendar.getInstance();
266         String concatDate = value.split("T")[0];
267         String concatTime = value.split("T")[1];
268         String[] date = concatDate.split("-");
269         String[] time = concatTime.split(":");
270         calendar.set(new Integer(date[0]).intValue(), new Integer(date[1])
271                 .intValue() - 1, new Integer(date[2]).intValue(), new Integer(
272                 time[0]).intValue(), new Integer(time[1]).intValue(),
273                 new Integer(time[2]).intValue());
274         return calendar.getTimeInMillis();
275     }
276 
277     /***
278      * parses a period
279      * 
280      * @param element the xml-element representing the period
281      * @param treatmentTimeUnit the timeUnit of the treatment
282      * @return double the value in units defined by the treatment
283      * @throws Exception whenever the period.
284      */
285     private static double parsePeriod(final Element element,
286             final TimeUnitInterface treatmentTimeUnit) throws Exception
287     {
288         TimeUnitInterface timeUnit = ExperimentParser.parseTimeUnit(element
289                 .getAttribute("unit").getValue());
290         double value = -1;
291         if (element.getText().equals("INF"))
292         {
293             value = Double.MAX_VALUE;
294         } else
295         {
296             value = new Double(element.getText()).doubleValue();
297         }
298         if (value < 0)
299         {
300             throw new JDOMException("parsePeriod: value = " + value
301                     + " <0. simulator cannot schedule in past");
302         }
303         return timeUnit.getValue() * value / treatmentTimeUnit.getValue();
304     }
305 
306     /***
307      * parses a replication
308      * 
309      * @param element the JDOM element
310      * @param parent the RunControl
311      * @param number the number
312      * @return the replication
313      * @throws Exception on failure
314      */
315     private static Replication parseReplication(final Element element,
316             final Treatment parent, final Context context) throws Exception
317     {
318         Replication replication = new Replication(context, parent);
319         if (element.getAttribute("description") != null)
320         {
321             replication.setDescription(element.getAttribute("description")
322                     .getValue());
323         }
324         Map<String, StreamInterface> streams = new HashMap<String, StreamInterface>();
325         @SuppressWarnings("unchecked")
326         List<Element> streamElements = (List<Element>) element
327                 .getChildren("stream");
328         for (Element streamElement : streamElements)
329         {
330             long seed = new Long(streamElement.getAttributeValue("seed"))
331                     .longValue();
332             StreamInterface stream = new MersenneTwister(seed);
333             streams.put(streamElement.getAttributeValue("name"), stream);
334         }
335         replication.setStreams(streams);
336         return replication;
337     }
338 
339     /***
340      * parses proprties to treatments
341      * 
342      * @param element the element
343      * @return Properties
344      */
345     private static Properties parseProperties(final Element element)
346     {
347         Properties result = new Properties();
348         @SuppressWarnings("unchecked")
349         List<Element> children = (List<Element>) element
350                 .getChildren("property");
351         for (Iterator<Element> i = children.iterator(); i.hasNext();)
352         {
353             Element child = i.next();
354             String key = child.getAttributeValue("key");
355             String value = child.getAttributeValue("value");
356             result.put(key, value);
357         }
358         return result;
359     }
360 
361     /***
362      * parses a timeUnit
363      * 
364      * @param name the name
365      * @return TimeUnitInterface result
366      * @throws Exception on failure
367      */
368     private static TimeUnitInterface parseTimeUnit(final String name)
369             throws Exception
370     {
371         if (name.equals("DAY"))
372         {
373             return TimeUnitInterface.DAY;
374         }
375         if (name.equals("HOUR"))
376         {
377             return TimeUnitInterface.HOUR;
378         }
379         if (name.equals("MILLISECOND"))
380         {
381             return TimeUnitInterface.MILLISECOND;
382         }
383         if (name.equals("MINUTE"))
384         {
385             return TimeUnitInterface.MINUTE;
386         }
387         if (name.equals("SECOND"))
388         {
389             return TimeUnitInterface.SECOND;
390         }
391         if (name.equals("WEEK"))
392         {
393             return TimeUnitInterface.WEEK;
394         }
395         if (name.equals("UNIT"))
396         {
397             return TimeUnitInterface.UNIT;
398         }
399         throw new Exception("parseTimeUnit.. unknown argument: " + name);
400     }
401 
402     /***
403      * parses the treatment
404      * 
405      * @param element the xml-element
406      * @param parent parent
407      * @param number the number
408      * @return Treatment
409      * @throws Exception on failure
410      */
411     private static Treatment parseTreatment(final Element element,
412             final Experiment parent, final Context context) throws Exception
413     {
414         Treatment treatment = new Treatment(parent);
415         treatment.setTimeUnit(ExperimentParser.parseTimeUnit(element
416                 .getChildText("timeUnit")));
417         if (element.getChild("startTime") != null)
418         {
419             treatment.setStartTime(ExperimentParser.parseDateTime(element
420                     .getChildText("startTime")));
421         }
422         if (element.getChild("warmupPeriod") != null)
423         {
424             treatment.setWarmupPeriod(ExperimentParser.parsePeriod(element
425                     .getChild("warmupPeriod"), treatment.getTimeUnit()));
426         }
427         if (element.getChild("runLength") != null)
428         {
429             treatment.setRunLength(ExperimentParser.parsePeriod(element
430                     .getChild("runLength"), treatment.getTimeUnit()));
431         }
432         @SuppressWarnings("unchecked")
433         List<Element> replicationElements = (List<Element>) element
434                 .getChildren("replication");
435         List<Replication> replicationArray = new ArrayList<Replication>();
436         int number = 1;
437         for (Iterator<Element> i = replicationElements.iterator(); i.hasNext();)
438         {
439             replicationArray.add(ExperimentParser.parseReplication(i.next(),
440                     treatment, context));
441             number++;
442         }
443         treatment.setReplications(replicationArray);
444         if (element.getChild("properties") != null)
445         {
446             treatment.setProperties(ExperimentParser.parseProperties(element
447                     .getChild("properties")));
448         }
449         return treatment;
450     }
451 }