/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package org.absmodels.abs.plugin.debug.model;
import static org.absmodels.abs.plugin.debug.DebugUtils.enableHightlighting;
import static org.absmodels.abs.plugin.debug.DebugUtils.getDebugViewer;
import static org.absmodels.abs.plugin.debug.DebugUtils.getDebugger;
import static org.absmodels.abs.plugin.util.Constants.ABSDEBUGPERSPECTIVE_ID;
import static org.absmodels.abs.plugin.util.Constants.DO_DEBUG;
import static org.absmodels.abs.plugin.util.UtilityFunctions.showErrorMessage;
import static org.absmodels.abs.plugin.util.UtilityFunctions.standardExceptionHandling;
import java.io.PrintStream;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import org.absmodels.abs.plugin.debug.DebugUtils;
import org.absmodels.abs.plugin.debug.scheduling.SchedulingStrategy;
import org.eclipse.core.runtime.Path;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import abs.backend.java.debugging.DebugModel;
import abs.backend.java.lib.runtime.ABSException;
import abs.backend.java.lib.runtime.ABSRuntime;
import abs.backend.java.observing.COGView;
import abs.backend.java.observing.ObjectView;
import abs.backend.java.observing.SystemObserver;
import abs.backend.java.scheduling.ScheduableTasksFilterFifo;
import abs.backend.java.scheduling.TotalSchedulingStrategy;
/**
* The Debugger handles start and shutdown of a debug process (runtime) and delegates basic events
* from the runtime.
* @author fstrauss, tfischer
*/
public class Debugger implements SystemObserver{
private DebugModel model;
private ABSRuntime runtime;
private String projectName = "none";
private HashMap<COGView, Tasks> tasks = new HashMap<COGView, Tasks>();
private HashMap<COGView, Objects> objects = new HashMap<COGView, Objects>();
/**
* Shuts down the previous debug process and initializes the debugger with parameters
* for a new debug process.
* @param useOurSystemObserver indicate if the internal debug perspective should be used
* @param projectName
* @param r
* @param debuggerRunner
*/
public void initDebugger(String projectName, ABSRuntime r, Thread debuggerRunner, boolean useOurSystemObserver){
shutdown();
this.projectName = projectName;
this.runtime = r;
if(useOurSystemObserver){
r.addSystemObserver(this);
}
model = new DebugModel();
model.registerListener(new DebugModelListener());
debuggerRunner.start();
getDebugViewer().setInput(new Object[] {model});
if(DebugUtils.getSchedulerRef() != null)
DebugUtils.getSchedulerRef().updateScheduler();
}
@SuppressWarnings("serial")
public static class InvalidRandomSeedException extends Exception {
public InvalidRandomSeedException(NumberFormatException e) {
super(e);
}
}
public static long getSeed(String seedString) throws InvalidRandomSeedException {
try {
return Long.valueOf(seedString);
} catch (NumberFormatException e) {
throw new InvalidRandomSeedException(e);
}
}
/**
* Used to start the ABS Runtime. If an earlier Runtime is present, it is shut down first.
*
* @param projectName
* @param mainClassName
* @param genPath
* @param debuggerArgsSystemObserver
* @param debuggerArgsTotalScheduler
* @param debuggerIsInDebugMode
* @param debuggerArgsRandomSeed
* @param fliClassPath
* @param outStream
* @param ignoreMissingFLIClasses
* @param useFifoSemantics
* @throws InvalidRandomSeedException
*/
public static void startABSRuntime(final String projectName,
final String mainClassName, final Path genPath, String debuggerArgsSystemObserver,
String debuggerArgsTotalScheduler, boolean debuggerIsInDebugMode, String debuggerArgsRandomSeed,
boolean terminateOnException,
List<URL> fliClassPath, PrintStream outStream, PrintStream errStream, boolean ignoreMissingFLIClasses, boolean useFifoSemantics) throws InvalidRandomSeedException {
if(DO_DEBUG) System.out.println("start internal debugger");
final ABSRuntime r = new ABSRuntime();
if(debuggerIsInDebugMode) r.enableDebugging(true);
r.setOutStream(outStream);
r.setErrStream(errStream);
r.addFLIClassPath(fliClassPath);
r.setIgnoreMissingFLIClasses(ignoreMissingFLIClasses);
if (useFifoSemantics) {
r.setScheduableTasksFilter(new ScheduableTasksFilterFifo());
}
r.terminateOnException(terminateOnException);
boolean useOurScheduling = addSchedulingStrategy(debuggerArgsTotalScheduler, r);
final boolean useOurSystemObserver = addSystemObservers(debuggerArgsSystemObserver, r);
if(debuggerArgsRandomSeed != null && !debuggerArgsRandomSeed.isEmpty()){
String seedString = debuggerArgsRandomSeed.replace("-Dabs.randomseed=", "");
Long seedNumber = getSeed(seedString);
r.setRandomSeed(seedNumber);
}
final ThreadGroup tg = new ThreadGroup("ABS "+mainClassName);
tg.setDaemon(true);
final Thread debuggerRunner = new Thread(tg, new Runnable(){
@Override
public void run() {
try {
if (DO_DEBUG){
System.out.println("Start ABSRuntime .. ");
System.out.println("path: " + genPath.toString());
System.out.println("name:" + mainClassName);
}
r.start(genPath.toFile(), mainClassName);
} catch (ClassNotFoundException e) {
exceptionHandling(e);
} catch (InstantiationException e) {
exceptionHandling(e);
} catch (IllegalAccessException e) {
exceptionHandling(e);
}
}
private void exceptionHandling(final Exception e){
standardExceptionHandling(e);
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
showErrorMessage("Not able to start ABSRuntime.\n" + e.getMessage());
}
});
}
});
if (useOurScheduling) {
enableHightlighting();
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
try {
PlatformUI.getWorkbench().showPerspective(ABSDEBUGPERSPECTIVE_ID, PlatformUI.getWorkbench().getActiveWorkbenchWindow());
getDebugger().initDebugger(projectName, r, debuggerRunner, useOurSystemObserver);
if (DebugUtils.getRunAutomatically()) {
DebugUtils.getSchedulerRef().resumeAutomaticScheduling();
}
} catch (WorkbenchException e) {
standardExceptionHandling(e);
showErrorMessage("Could not open ABS debug perspective");
}
}});
} else {
debuggerRunner.start();
}
}
/**
* Terminate the current ABSRuntime, if one exists.
*/
public void shutdown(){
if(isRunning()){
runtime.shutdown();
runtime = null;
}
DebugUtils.getSchedulerRef().systemFinished();
DebugUtils.removeAllHighlighting();
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
DebugUtils.refreshButtonEnablement();
DebugUtils.getDebugViewer().refresh();
}
});
}
/**
* Checks, if the Debugger currently has a runtime.
*/
public boolean isRunning(){
return runtime != null;
}
private static boolean addSchedulingStrategy(String debuggerArgsScheduler, final ABSRuntime r) {
if(debuggerArgsScheduler != null && !debuggerArgsScheduler.isEmpty()){
String scheduler = debuggerArgsScheduler.replaceFirst("-Dabs.totalscheduler=", "");
if(!scheduler.isEmpty()){
try {
Class<?> c = Class.forName(scheduler);
Object o = c.newInstance();
TotalSchedulingStrategy ss = (TotalSchedulingStrategy) o;
r.setTotalSchedulingStrategy(ss);
return (ss instanceof SchedulingStrategy);
} catch (ClassNotFoundException e) {
standardExceptionHandling(e);
showErrorMessage("Not able to instantiate total scheduler");
} catch (InstantiationException e) {
standardExceptionHandling(e);
showErrorMessage("Not able to instantiate total scheduler");
} catch (IllegalAccessException e) {
standardExceptionHandling(e);
showErrorMessage("Not able to instantiate total scheduler");
}
}
}
return false;
}
private static boolean addSystemObservers(String debuggerArgsSystemObserver, final ABSRuntime r) {
boolean useThisSystemObserver = false;
if(debuggerArgsSystemObserver != null && !debuggerArgsSystemObserver.isEmpty()){
String systemObserverArgs = debuggerArgsSystemObserver.replaceFirst("-Dabs.systemobserver=", "");
String[] systemObservers = systemObserverArgs.split(",");
for (String observer : systemObservers) {
if(!observer.isEmpty()){
try {
Class<?> c = Class.forName(observer);
Object o = c.newInstance();
SystemObserver so = (SystemObserver) o;
if(so instanceof Debugger){
useThisSystemObserver = true;
} else {
r.addSystemObserver(so);
}
//FIXME do not close eclipse
} catch (ClassNotFoundException e) {
//skip
e.printStackTrace();
} catch (InstantiationException e) {
//skip
e.printStackTrace();
} catch (IllegalAccessException e) {
//skip
e.printStackTrace();
}
}
}
}
return useThisSystemObserver;
}
@Override
public void systemStarted() {}
@Override
public void systemFinished() {
shutdown();
}
@Override
public void newCOGCreated(COGView cog, ObjectView initialObject) {
cog.registerObjectCreationListener(new ObjectCreationObserver());
model.cogCreated(cog, initialObject);
}
public DebugModel getModel(){
return model;
}
/**
* Returns an {@link org.absmodels.abs.plugin.debug.model.Tasks} object related to a given COGView.
* These objects are necessary to group tasks in the tree viewer of the debug tree. Tasks objects
* are stored in a hash map by the debugger.
* @see org.absmodels.abs.plugin.debug.views.debugview.DebugTreeContentProvider
* @param cog COGView for which the Tasks object shall be returned
* @return The Tasks object related to the given COGView
*/
public Tasks getTasks(COGView cog){
if(tasks.containsKey(cog)){
return tasks.get(cog);
} else{
Tasks t = new Tasks(cog);
tasks.put(cog, t);
return t;
}
}
/**
* Returns an {@link org.absmodels.abs.plugin.debug.model.Objects} object related to a given COGView.
* These objects are necessary to group objects in the tree viewer of the debug tree. Objects objects
* are stored in a hash map by the debugger.
* @see org.absmodels.abs.plugin.debug.views.debugview.DebugTreeContentProvider
* @param cog COGView for which the Objects object shall be returned
* @return The Objects object related to the given COGView
*/
public Objects getObjects(COGView cog){
if(objects.containsKey(cog)){
return objects.get(cog);
} else{
Objects o = new Objects(cog);
o.addObject(model.getCOGInfo(cog).getInitialObject());
objects.put(cog, o);
return o;
}
}
/**
* @return Name of the project which is currently debugged.
*/
public String getProjectName(){
return projectName;
}
@Override
public void systemError(final ABSException e) {
if (runtime.getTerminateOnException()) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
showErrorMessage(e.getMessageWithStackTrace());
}
});
shutdown();
}
}
}