/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.backend.java.lib.runtime; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; import abs.backend.java.lib.types.ABSRef; import abs.backend.java.observing.SystemObserver; import abs.backend.java.scheduling.DefaultTaskScheduler; import abs.backend.java.scheduling.GlobalScheduler; import abs.backend.java.scheduling.GlobalSchedulingStrategy; import abs.backend.java.scheduling.ScheduableTasksFilter; import abs.backend.java.scheduling.ScheduableTasksFilterDefault; import abs.backend.java.scheduling.ScheduleAction; import abs.backend.java.scheduling.SimpleTaskScheduler; import abs.backend.java.scheduling.TaskScheduler; import abs.backend.java.scheduling.TaskSchedulerFactory; import abs.backend.java.scheduling.TaskSchedulingStrategy; import abs.backend.java.scheduling.TotalSchedulingStrategy; import abs.backend.java.scheduling.UsesRandomSeed; public class ABSRuntime { private static final String ABS_RUNSINOWNPROCESS_PROPERTY = "abs.runsinownprocess"; private static final String FLI_CLASS_SUFFIX = "_fli"; private static final String ABSFLI_PROPERTIES = "absfli.properties"; private static final Logger logger = Logging.getLogger(ABSRuntime.class.getName()); private static final boolean DEBUG_FLI = Boolean.parseBoolean(System.getProperty("abs.fli.debug", "false")); private final ABSThreadManager threadManager = new ABSThreadManager(this); private final AtomicInteger cogCounter = new AtomicInteger(); private final AtomicInteger taskCounter = new AtomicInteger(); /** classloader for loading the translated code and FLI classes */ private ClassLoader classLoader = ABSRuntime.class.getClassLoader(); /** URIs for loading foreign classes */ private List<URL> classPath = new ArrayList<URL>(); private PrintStream outStream = System.out; private PrintStream errStream = System.err; /** whether to output an error message when no * Java class is found for a class annotated with [Foreign] */ private boolean ignoreMissingFLIClasses = false; private ScheduableTasksFilter scheduableTasksFilter = new ScheduableTasksFilterDefault(); /** * Starts a new ABS program by giving a generated Main class * @param mainClass the Main class to be used * @throws InstantiationException if the Main class could not be instantiated * @throws IllegalAccessException if the Main class could not be accessed */ public void start(Class<?> mainClass) throws InstantiationException, IllegalAccessException { systemStarted(); COG cog = createCOG(mainClass, null); try { Constructor<?> constr = mainClass.getConstructor(COG.class); ABSObject mainObject = (ABSObject) constr.newInstance(cog); cogCreated(mainObject); asyncCall(new ABSMainCall(mainObject)); doNextStep(); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } int freshTaskID() { return taskCounter.incrementAndGet(); } int freshCOGID() { return cogCounter.incrementAndGet(); } /** * Starts this runtime by using the Main class with name mainClassName (full qualified). * It uses the directory targetDir to search for that class. * Example: <code>start(new File("javatest"), "LeaderElection.Main");</code> * @param targetDir the directory that holds the generated class files * @param mainClassName the full qualified name of the class name. * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ public void start(File targetDir, String mainClassName) throws ClassNotFoundException, InstantiationException, IllegalAccessException { if (!targetDir.canRead()) { throw new IllegalArgumentException("Directory "+targetDir+" cannot be read"); } try { classPath.add(targetDir.toURI().toURL()); URL[] urls = classPath.toArray(new URL[0]); classLoader = new URLClassLoader(urls, ABSRuntime.class.getClassLoader()); Class<?> mainClass = classLoader.loadClass(mainClassName); start(mainClass); } catch (MalformedURLException e) { e.printStackTrace(); } } private final List<SystemObserver> systemObserver = new ArrayList<SystemObserver>(); private GlobalSchedulingStrategy globalSchedulingStrategy; private GlobalScheduler globalScheduler; private TaskSchedulingStrategy taskSchedulingStrategy; private TaskSchedulerFactory taskSchedulerFactory = DefaultTaskScheduler.getFactory(); private volatile boolean debugging = false; private volatile boolean terminateOnException = false; private long randomSeed; private Random random; private volatile boolean isShutdown; public ABSRuntime() { setRandomSeed(System.nanoTime()); } public void addSystemObserver(SystemObserver t) { this.systemObserver.add(t); } public synchronized long getRandomSeed() { return randomSeed; } public void enableDebugging(boolean debug) { debugging = debug; } public boolean debuggingEnabled() { return debugging; } /** * Terminate the whole ABS runtime when an exception occurs * when executing a task. * Default is false * @param b whether to terminate or not */ public void terminateOnException(boolean b) { terminateOnException = b; } public synchronized void setRandomSeed(long seed) { randomSeed = seed; random = new Random(seed); logger.config("New Random Seed: " + randomSeed); if (globalSchedulingStrategy instanceof UsesRandomSeed) { ((UsesRandomSeed)globalSchedulingStrategy).setRandom(random); } if (taskSchedulingStrategy instanceof UsesRandomSeed) { ((UsesRandomSeed)taskSchedulingStrategy).setRandom(random); } } public synchronized Random getRandom() { return random; } public synchronized void setTotalSchedulingStrategy(TotalSchedulingStrategy strat) { setGlobalSchedulingStrategy(strat); setTaskSchedulingStrategy(strat); } public synchronized void setTaskSchedulingStrategy(TaskSchedulingStrategy strat) { taskSchedulingStrategy = strat; if (strat != null) { taskSchedulerFactory = SimpleTaskScheduler.getFactory(); } else { taskSchedulerFactory = DefaultTaskScheduler.getFactory(); } } public synchronized void setGlobalSchedulingStrategy(GlobalSchedulingStrategy strat) { globalSchedulingStrategy = strat; if (strat != null) { logger.config("Using global scheduling strategy defined by class " + strat.getClass().getName()); globalScheduler = new GlobalScheduler(this, strat); } else { globalScheduler = null; } } public synchronized void setTaskSchedulerFactory(TaskSchedulerFactory taskSchedulerFactory) { this.taskSchedulerFactory = taskSchedulerFactory; } public synchronized boolean hasGlobalScheduler() { return globalScheduler != null; } public void scheduleTaskDone() { if (hasGlobalScheduler()) { globalScheduler.doNextScheduleStep(); } } public void nextStep(String fileName, int line) { if (isShutdown) return; if (debugging) { getCurrentTask().nextStep(fileName, line); } if (hasGlobalScheduler()) { try { globalScheduler.stepTask(getCurrentTask()); } catch (InterruptedException e) { if (!isShutdown) e.printStackTrace(); else throw new SystemTerminatedException(); } } } public static <O> O checkForNull(O o) { if (o == null) { throw new ABSNullPointerException(); } return o; } public void addScheduleAction(ScheduleAction action) { if (hasGlobalScheduler()) { globalScheduler.addAction(action); } } public void doNextStep() { if (!isShutdown && hasGlobalScheduler()) { globalScheduler.doNextScheduleStep(); } } public void cogCreated(ABSObject o) { for (SystemObserver obs : systemObserver) { obs.newCOGCreated(o.getCOG().getView(), o.getView()); } } public void systemStarted() { for (SystemObserver obs : systemObserver) { obs.systemStarted(); } } public GlobalScheduler getGlobalScheduler() { return globalScheduler; } public void shutdown() { if (isShutdown) return; isShutdown = true; if (hasGlobalScheduler()) globalScheduler.shutdown(); threadManager.shutdownAllThreads(); } public TaskScheduler createTaskScheduler(COG cog) { return taskSchedulerFactory.createTaskScheduler(this, cog, threadManager, scheduableTasksFilter); } public SimpleTaskScheduler createUserTaskScheduler(COG cog, TaskSchedulingStrategy strategy) { // Bypass the scheduler factory, as we specifically want a SimpleTaskScheduler return new SimpleTaskScheduler(cog, strategy, this, threadManager, scheduableTasksFilter); } public ABSThreadManager getThreadManager() { return threadManager; } public TaskSchedulingStrategy getTaskSchedulingStrategy() { return taskSchedulingStrategy; } public static ABSRuntime getCurrentRuntime() { if (getCurrentCOG() != null) { return getCurrentCOG().getRuntime(); } else { return null; } } public static Task<?> getCurrentTask() { if (getCurrentCOG() != null) { return getCurrentCOG().getScheduler().getActiveTask(); } else { return null; } } public static void suspend() { getCurrentCOG().getScheduler().await(new ABSTrueGuard()); } public static void await(ABSGuard g) { if (g.isTrue()) return; // special case in the semantics getCurrentCOG().getScheduler().await(g); } public COG createCOG(Class<?> clazz, abs.backend.java.lib.types.ABSInterface dc) { return new COG(this, clazz, dc); } public COG createCOG(Class<?> clazz, abs.backend.java.lib.types.ABSInterface dc, TaskSchedulingStrategy strategy) { return new COG(this, clazz, dc, strategy); } public static COG getCurrentCOG() { final ABSThread thread = getCurrentThread(); if (thread != null) return thread.getCOG(); else return null; } public static ABSThread getCurrentThread() { Thread currentThread = Thread.currentThread(); if (currentThread instanceof ABSThread) return (ABSThread) currentThread; else return null; } public <T extends ABSRef> ABSFut<?> asyncCall(AsyncCall<T> call) { Task<T> task = new Task<T>(call); task.schedule(); return task.getFut(); } private final Map<Class<?>,AtomicInteger> objectIds = new HashMap<Class<?>,AtomicInteger>(); public long getFreshObjectID(Class<?> clazz) { AtomicInteger ai = null; synchronized (objectIds) { ai = objectIds.get(clazz); if (ai == null) { ai = new AtomicInteger(); objectIds.put(clazz, ai); } return ai.incrementAndGet(); } } public void systemFinished() { for (SystemObserver obs : systemObserver) { obs.systemFinished(); } } public void setForeignClass(String absClassName, Class<?> javaClass) { foreignClasses.put(absClassName, javaClass); } /** * Defines the properties to be used for the foreign class mapping * @param the properties to be used */ public void setFLIProperties(Properties p) { fliProperties = p; } /** * Maps ABS class names of foreign classes to Java class implementations */ private final Map<String, Class<?>> foreignClasses = new HashMap<String, Class<?>>(); private Properties fliProperties; private synchronized Class<?> getForeignClass(String name) { Class<?> clazz = foreignClasses.get(name); // this is a dummy entry to prevent reloading of classes each time if (clazz == ABSRuntime.class) return null; if (clazz == null) { clazz = loadForeignClass(name); if (clazz != null) { foreignClasses.put(name, clazz); } else { foreignClasses.put(name, ABSRuntime.class); } if (DEBUG_FLI) errStream.println("FLI: "+name+" = "+clazz); } return clazz; } private synchronized Class<?> loadForeignClass(String name) { String className = System.getProperty("abs.fli.class."+name); if (className == null) { if (fliProperties == null) { loadFLIProperties(); } className = fliProperties.getProperty(name); } if (className == null) { // use conventions: className = name+FLI_CLASS_SUFFIX; } try { Class<?> result = classLoader.loadClass(className); return result; } catch (ClassNotFoundException e) { if (!ignoreMissingFLIClasses) { errStream.println("Could not load foreign class for " + name + "!"); } } return null; } private void loadFLIProperties() { String propertiesFileName = System.getProperty("abs.fli.properties",ABSFLI_PROPERTIES); URL url = classLoader.getResource(propertiesFileName); fliProperties = new Properties(); if (url == null) { if (!propertiesFileName.equals(ABSFLI_PROPERTIES)) { errStream.println("Could not find ABS-FLI properties file "+propertiesFileName); } } else { try { fliProperties.load(new BufferedReader( new InputStreamReader(url.openStream()))); if (DEBUG_FLI) { errStream.println("FLI: Loaded properties from URL "+url); errStream.println("FLI: Loaded properties: "+fliProperties.toString()); } } catch (IOException e) { if (!propertiesFileName.equals(ABSFLI_PROPERTIES)) { errStream.println("ABS Error while trying to read the FLI properties file "+propertiesFileName); e.printStackTrace(); } } } } public Object getForeignObject(String absClassName, Object... args) { return loadForeignObject(getForeignClass(absClassName), args); } private Object loadForeignObject(Class<?> foreignClass, Object... args) { if (foreignClass != null) { try { Constructor<?>[] cs = foreignClass.getConstructors(); if (cs.length == 0) return foreignClass.newInstance(); return cs[0].newInstance(args); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } return null; } /** * Defines what to do in case an ABSException is thrown in a task * @param task the task that throwed the exception * @param e the exception that was thrown */ public void handleABSException(Task<?> task, ABSException e) { if (isShutdown) { // ignore errors during shutdown return; } errStream.println("Error in " + this + ":\n" + e.getMessage()); for (SystemObserver obs : systemObserver) { obs.systemError(e); } if (terminateOnException) { shutdown(); } } public static boolean runsInOwnProcess() { return Boolean.parseBoolean(System.getProperty(ABS_RUNSINOWNPROCESS_PROPERTY, "false")); } public static void setRunsInOwnProcess(boolean b) { System.setProperty(ABS_RUNSINOWNPROCESS_PROPERTY, Boolean.toString(b)); } public void addFLIClassPath(Collection<URL> fliClassPath) { classPath.addAll(fliClassPath); } public void setOutStream(PrintStream stream) { outStream = stream; } public PrintStream getOutStream() { return outStream; } public PrintStream getErrStream() { return errStream; } public void setErrStream(PrintStream stream) { this.errStream = stream; } public void setIgnoreMissingFLIClasses(boolean ignoreMissingFLIClasses) { this.ignoreMissingFLIClasses = ignoreMissingFLIClasses; } public void setScheduableTasksFilter(ScheduableTasksFilter filter) { scheduableTasksFilter = filter; } public boolean getTerminateOnException() { return terminateOnException; } }