/*
* Created on Apr 11, 2007 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu
* (jactr.org) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version. This library 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
* library; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.jactr.entry.iterative;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.concurrent.locks.ReadWriteLock;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.antlr.runtime.tree.CommonTree;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.concurrent.ExecutorServices;
import org.jactr.core.model.IModel;
import org.jactr.core.model.event.ModelEvent;
import org.jactr.core.model.event.ModelListenerAdaptor;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.core.runtime.controller.IController;
import org.jactr.core.runtime.event.IACTRRuntimeListener;
import org.jactr.io.environment.EnvironmentParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* @author developer
*/
public class IterativeMain
{
/**
* logger definition
*/
static private final Log LOGGER = LogFactory
.getLog(IterativeMain.class);
static private final long SECONDS = 1000;
static private final long MINUTES = SECONDS * 60;
static private final long HOURS = MINUTES * 60;
private boolean _aggressiveGC = true;
static private String duration(long startTime, long stopTime)
{
long delta = stopTime - startTime;
long h = delta / HOURS;
long m = (delta - h * HOURS) / MINUTES;
long s = (delta - h * HOURS - m * MINUTES) / SECONDS;
long ms = delta - h * HOURS - m * MINUTES - s * SECONDS;
StringBuilder sb = new StringBuilder("" + h);
sb.append(":");
if (m < 10) sb.append("0");
sb.append(m).append(":");
if (s < 10) sb.append("0");
sb.append(s).append(".");
if (ms < 100) sb.append("0");
if (ms < 10) sb.append("0");
sb.append(ms);
return sb.toString();
}
/**
* load the environment file from url
*
* @param url
* @return
*/
public Document loadEnvironment(URL url) throws IOException, SAXException,
ParserConfigurationException
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder parser = factory.newDocumentBuilder();
Document doc = parser.parse(url.openStream());
return doc;
}
public void iteration(final int index, int total, Document environment,
URL envURL, final Collection<IIterativeRunListener> listeners,
final PrintWriter log) throws TerminateIterativeRunException
{
ExecutorServices.initialize();
if (LOGGER.isDebugEnabled())
{
long totalMem = Runtime.getRuntime().totalMemory();
long freeMem = Runtime.getRuntime().freeMemory();
LOGGER.debug("Running iteration " + index + "/" + total + " [" + freeMem
/ 1024 + "k free : " + (totalMem - freeMem) / 1024 + "k used of "
+ totalMem / 1024 + "k]");
}
for (IIterativeRunListener listener : listeners)
try
{
listener.preLoad(index, total);
}
catch (Exception e)
{
LOGGER.error("listener " + listener + " threw an exception ", e);
}
/*
* first we use the environment to load all the model descriptors
*/
EnvironmentParser ep = new EnvironmentParser();
Collection<CommonTree> modelDescriptors = ep.getModelDescriptors(
environment, envURL);
for (IIterativeRunListener listener : listeners)
try
{
listener.preBuild(index, total, modelDescriptors);
}
catch (TerminateIterativeRunException tire)
{
throw tire;
}
catch (Exception e)
{
LOGGER.error("listener " + listener + " threw an exception ", e);
}
/*
* now we actually do that vodoo that we do to set up the environment this
* will build the models, instruments, and set up common reality..
*/
ep.process(environment, modelDescriptors);
modelDescriptors.clear();
ACTRRuntime runtime = ACTRRuntime.getRuntime();
Collection<IModel> models = runtime.getModels();
for (IModel model : models)
model.addListener(new ModelListenerAdaptor() {
long startTime = 0;
long simStartTime = 0;
boolean closed = false;
@Override
public void modelStarted(ModelEvent event)
{
startTime = event.getSystemTime();
simStartTime = (long) (event.getSimulationTime() * 1000);
}
protected String header(ModelEvent event)
{
StringBuilder sb = new StringBuilder(" <model name=\"");
sb.append(event.getSource()).append("\" simulated=\"");
sb.append(duration(simStartTime,
(long) (event.getSimulationTime() * 1000)));
sb.append("\" actual=\"");
sb.append(duration(startTime, event.getSystemTime()));
sb.append("\" factor=\"");
double factor = (event.getSimulationTime() * 1000 - simStartTime)
/ (event.getSystemTime() - startTime);
NumberFormat format = NumberFormat.getNumberInstance();
format.setMaximumFractionDigits(3);
sb.append(format.format(factor)).append("\"");
return sb.toString();
}
@Override
public void modelStopped(ModelEvent event)
{
if (!closed) synchronized (log)
{
log.println(header(event) + "/>");
}
}
@Override
public void exceptionThrown(ModelEvent event)
{
synchronized (log)
{
closed = true;
log.println(header(event) + ">");
event.getException().printStackTrace(log);
log.println(" </model>");
for (IIterativeRunListener listener : listeners)
try
{
listener.exceptionThrown(index, event.getSource(),
event.getException());
}
catch (TerminateIterativeRunException tire)
{
}
/*
* from here we try to stop the runtime, but do not block.. that
* would be disasterous
*/
IController controller = ACTRRuntime.getRuntime().getController();
controller.stop();
}
}
}, ExecutorServices.INLINE_EXECUTOR);
for (IIterativeRunListener listener : listeners)
try
{
listener.preRun(index, total, models);
}
catch (TerminateIterativeRunException tire)
{
throw tire;
}
catch (Exception e)
{
LOGGER.error("listener " + listener + " threw an exception ", e);
}
try
{
/*
* start 'er up!
*/
IController controller = runtime.getController();
/*
* we do the model check in case the listener is monkeying around with the
* models (adding, removing) the controller will not ever complete if no
* models are executed.
*/
if (models.size() != 0) try
{
controller.start().get();
controller.complete().get();
}
catch (InterruptedException ie)
{
LOGGER.error("Interrupted while waiting for completion", ie);
}
/*
* all done - time to notify and clean up
*/
for (IIterativeRunListener listener : listeners)
try
{
listener.postRun(index, total, models);
}
catch (TerminateIterativeRunException tire)
{
throw tire;
}
catch (Exception e)
{
LOGGER.error("listener " + listener + " threw an exception ", e);
}
}
catch (TerminateIterativeRunException tire)
{
throw tire;
}
catch (Throwable e)
{
throw new RuntimeException("Failed to run iteration " + index + " ", e);
}
finally
{
cleanUp(runtime);
}
}
protected void cleanUp(ACTRRuntime runtime)
{
if (runtime.getConnector().isRunning()) try
{
runtime.getConnector().stop();
}
catch (Exception e)
{
LOGGER.error("Failed to cleanly stop reality connector ", e);
}
Collection<IModel> models = new ArrayList<IModel>(runtime.getModels());
for (IModel model : models)
try
{
runtime.removeModel(model);
}
catch (Exception e)
{
LOGGER.error("Could not remove " + model, e);
}
if (runtime.getModels().size() != 0)
if (LOGGER.isErrorEnabled())
LOGGER.error("Not all models were removed " + runtime.getModels());
/*
* remove any runtime listeners that were installed
*/
for (IACTRRuntimeListener runtimeListener : runtime.getListeners())
runtime.removeListener(runtimeListener);
/*
* kill the executors
*/
ExecutorServices.shutdown(10000);
/*
* now dipose
*/
for (IModel model : models)
{
ReadWriteLock lock = model.getLock();
lock.writeLock().lock();
try
{
model.dispose();
}
catch (Exception e)
{
LOGGER.error("Could not dispose of " + model, e);
}
finally
{
lock.writeLock().unlock();
}
}
runtime.setOnStart(null);
runtime.setOnStop(null);
runtime.setApplicationData(null);
runtime.setController(null);
}
protected Collection<IIterativeRunListener> createListeners(Document document)
{
ArrayList<IIterativeRunListener> listeners = new ArrayList<IIterativeRunListener>();
NodeList nl = document.getElementsByTagName("iterative-listener");
for (int i = 0; i < nl.getLength(); i++)
try
{
IIterativeRunListener listener = (IIterativeRunListener) EnvironmentParser
.instantiate((Element) nl.item(i), "IIterativeRunListener");
listeners.add(listener);
}
catch (Exception e)
{
LOGGER.error("Could not create run listener from " + nl.item(i), e);
}
return listeners;
}
protected int getIterations(Document document)
{
NodeList nl = document.getElementsByTagName("iterative");
if (nl.getLength() != 1)
throw new RuntimeException("Must specify the iterative tag");
try
{
return Integer
.parseInt(((Element) nl.item(0)).getAttribute("iterations"));
}
catch (NumberFormatException nfe)
{
throw new RuntimeException("iterative tag must have iterations attribute");
}
}
/**
* @param url
* @param listener
* @param iterations
* @throws Exception
*/
public void run(URL url)
{
Collection<IIterativeRunListener> listeners = Collections.EMPTY_LIST;
try
{
Document environment = loadEnvironment(url);
int iterations = getIterations(environment);
listeners = createListeners(environment);
String id = System.getProperty("iterative-id");
if (id == null)
id = "";
else
id = "-" + id;
File rootDir = ACTRRuntime.getRuntime().getWorkingDirectory();
PrintWriter log = new PrintWriter(new FileWriter("iterative-log" + id
+ ".xml"));
log.println("<iterative-run total=\"" + iterations + "\">");
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.LONG);
boolean shouldExit = false;
for (IIterativeRunListener listener : listeners)
try
{
listener.start(iterations);
}
catch (TerminateIterativeRunException tire)
{
shouldExit = true;
}
catch (Exception e)
{
if (LOGGER.isErrorEnabled())
LOGGER.error(listener + " threw an exception on start ", e);
}
long startTime = System.currentTimeMillis();
for (int i = 1; i <= iterations; i++)
{
if (shouldExit) break;
/*
* create a new working directory
*/
File workingDir = new File(rootDir, "run-" + i);
workingDir.mkdirs();
ACTRRuntime.getRuntime().setWorkingDirectory(workingDir);
log.println(" <run itr=\"" + i + "\" start=\""
+ format.format(new Date()) + "\">");
try
{
/*
* let's do it.
*/
if (_aggressiveGC) System.gc();
iteration(i, iterations, environment, url, listeners, log);
if (_aggressiveGC) System.gc();
}
catch (TerminateIterativeRunException tire)
{
shouldExit = true;
}
catch (Exception e1)
{
log.print(" <exception><![CDATA[");
e1.printStackTrace(log);
log.print("]]></exception>");
for (IIterativeRunListener listener : listeners)
try
{
listener.exceptionThrown(i, null, e1);
}
catch (TerminateIterativeRunException tire)
{
shouldExit = true;
}
catch (Exception e2)
{
LOGGER.error(listener
+ " threw an exception on exception notification ", e2);
}
}
log.println(" </run>");
log.flush();
if (workingDir.list().length == 0) workingDir.delete();
}
String duration = duration(startTime, System.currentTimeMillis());
if (LOGGER.isDebugEnabled())
LOGGER.debug("Total running time : " + duration);
log.println(" <duration value=\"" + duration + "\"/>");
log.println("</iterative-run>");
log.close();
}
catch (Exception e)
{
LOGGER.error("Failed to run fully", e);
for (IIterativeRunListener listener : listeners)
try
{
// we ignore the return value here since we're exiting anyway
listener.exceptionThrown(0, null, e);
}
catch (TerminateIterativeRunException tire)
{
// silently swallow since we're already exiting
}
catch (Exception e2)
{
LOGGER.error(listener
+ " threw an exception during exception notification ", e2);
}
}
finally
{
for (IIterativeRunListener listener : listeners)
try
{
listener.stop();
}
catch (Exception e)
{
LOGGER.error(listener + " threw an exception on stop ", e);
}
}
}
static public void main(String[] args)
{
try
{
URL url = new URL(args[0]);
IterativeMain entryPoint = new IterativeMain();
entryPoint.run(url);
}
catch (Exception e)
{
LOGGER.error("Could not run", e);
}
}
}