Преглед изворни кода

Infrastructure for Hot Model replacement

Andreas Mülder пре 14 година
родитељ
комит
88b10c38a1

+ 1 - 0
plugins/org.yakindu.sct.simulation.core/META-INF/MANIFEST.MF

@@ -15,4 +15,5 @@ Bundle-ActivationPolicy: lazy
 Export-Package: org.yakindu.sct.simulation.core,
  org.yakindu.sct.simulation.core.debugmodel,
  org.yakindu.sct.simulation.core.extensions,
+ org.yakindu.sct.simulation.core.hmr,
  org.yakindu.sct.simulation.core.launch

+ 4 - 9
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/Activator.java

@@ -2,31 +2,26 @@ package org.yakindu.sct.simulation.core;
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.yakindu.sct.simulation.core.hmr.SCTHotModelReplacementManager;
 
 public class Activator implements BundleActivator {
 
 	private static BundleContext context;
-	
+
 	public static final String PLUGIN_ID = "org.yakindu.sct.simulation.core";
 
 	static BundleContext getContext() {
 		return context;
 	}
 
-	/*
-	 * (non-Javadoc)
-	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
-	 */
 	public void start(BundleContext bundleContext) throws Exception {
 		Activator.context = bundleContext;
+		SCTHotModelReplacementManager.INSTANCE.startup();
 	}
 
-	/*
-	 * (non-Javadoc)
-	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
-	 */
 	public void stop(BundleContext bundleContext) throws Exception {
 		Activator.context = null;
+		SCTHotModelReplacementManager.INSTANCE.tearDown();
 	}
 
 }

+ 1 - 3
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/debugmodel/SCTDebugTarget.java

@@ -40,7 +40,7 @@ public class SCTDebugTarget extends SCTDebugElement implements IDebugTarget {
 
 	public SCTDebugTarget(ILaunch launch, ISGraphExecutionFacade facade,
 			String resourceString) throws CoreException {
-		super(null,resourceString);
+		super(null, resourceString);
 		this.launch = launch;
 		this.facade = facade;
 		thread = new SCTDebugThread(this, facade, resourceString);
@@ -135,12 +135,10 @@ public class SCTDebugTarget extends SCTDebugElement implements IDebugTarget {
 		return launch;
 	}
 
-
 	public IDebugTarget getDebugTarget() {
 		return this;
 	}
 
-	
 	public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
 		if (adapter == SGraphSimulationSession.class)
 			return thread.getAdapter(SGraphSimulationSession.class);

+ 26 - 0
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/hmr/IHotModelReplacementListener.java

@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2011 committers of YAKINDU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.simulation.core.hmr;
+
+import java.util.List;
+
+import org.yakindu.sct.simulation.core.debugmodel.SCTDebugTarget;
+
+/**
+ * 
+ * @author andreas muelder - Initial contribution and API
+ * 
+ */
+public interface IHotModelReplacementListener {
+
+	public void hotCodeReplaceFailed(List<SCTDebugTarget> affectedTargets);
+
+}

+ 195 - 0
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/hmr/SCTHotModelReplacementManager.java

@@ -0,0 +1,195 @@
+/**
+ * Copyright (c) 2011 committers of YAKINDU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.simulation.core.hmr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchListener;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.yakindu.sct.simulation.core.debugmodel.SCTDebugTarget;
+
+/**
+ * 
+ * @author andreas muelder - Initial contribution and API
+ * 
+ */
+public class SCTHotModelReplacementManager implements IResourceChangeListener,
+		IResourceDeltaVisitor, ILaunchListener, IDebugEventSetListener {
+
+	public static final SCTHotModelReplacementManager INSTANCE = new SCTHotModelReplacementManager();
+
+	private List<SCTDebugTarget> activeTargets;
+
+	private List<IHotModelReplacementListener> listeners;
+
+	private SCTHotModelReplacementManager() {
+		activeTargets = new ArrayList<SCTDebugTarget>();
+		listeners = new ArrayList<IHotModelReplacementListener>();
+	}
+
+	public synchronized void addReplacementListener(
+			IHotModelReplacementListener listener) {
+		listeners.add(listener);
+	}
+
+	public synchronized void removeReplacementListener(
+			IHotModelReplacementListener listener) {
+		listeners.remove(listener);
+	}
+
+	public void startup() {
+		DebugPlugin.getDefault().addDebugEventListener(this);
+		DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this);
+	}
+
+	public void tearDown() {
+		DebugPlugin.getDefault().removeDebugEventListener(this);
+		DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
+	}
+
+	@Override
+	public void handleDebugEvents(DebugEvent[] events) {
+		for (DebugEvent debugEvent : events) {
+			if (debugEvent.getKind() == DebugEvent.TERMINATE) {
+				Object source = debugEvent.getSource();
+				if (source instanceof IAdaptable) {
+					Object adapter = ((IAdaptable) source)
+							.getAdapter(IDebugTarget.class);
+					if (adapter instanceof SCTDebugTarget) {
+						unregisterSCTTarget((SCTDebugTarget) adapter);
+					}
+				}
+			}
+		}
+	}
+
+	@Override
+	public void launchRemoved(ILaunch launch) {
+		IDebugTarget[] debugTargets = launch.getDebugTargets();
+		for (IDebugTarget debugTarget : debugTargets) {
+			if (debugTarget instanceof SCTDebugTarget) {
+				unregisterSCTTarget((SCTDebugTarget) debugTarget);
+			}
+		}
+	}
+
+	@Override
+	public void launchAdded(ILaunch launch) {
+		IDebugTarget[] debugTargets = launch.getDebugTargets();
+		for (IDebugTarget debugTarget : debugTargets) {
+			if (debugTarget instanceof SCTDebugTarget) {
+				registerSCTTarget((SCTDebugTarget) debugTarget);
+			}
+		}
+	}
+
+	protected void registerSCTTarget(SCTDebugTarget target) {
+		synchronized (this) {
+			// start listening to resource changes if an SCtDebugTarget is
+			// active
+			if (activeTargets.isEmpty()) {
+				ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
+			}
+			if (!activeTargets.contains(target))
+				activeTargets.add(target);
+		}
+	}
+
+	protected void unregisterSCTTarget(SCTDebugTarget target) {
+		synchronized (this) {
+			if (activeTargets.contains(target)) {
+				activeTargets.remove((SCTDebugTarget) target);
+			}
+			// Stop listening to resource changes if no SCTDebugTarget is active
+			if (activeTargets.isEmpty()) {
+				ResourcesPlugin.getWorkspace().removeResourceChangeListener(
+						this);
+			}
+		}
+	}
+
+	@Override
+	public void launchChanged(ILaunch launch) {
+		launchAdded(launch);
+	}
+
+	private List<IFile> changedFiles = new ArrayList<IFile>();
+
+	@Override
+	public synchronized void resourceChanged(IResourceChangeEvent event) {
+		IResourceDelta delta = event.getDelta();
+		try {
+			changedFiles.clear();
+			delta.accept(this);
+			if (changedFiles.size() > 0) {
+				List<SCTDebugTarget> targets = getAffectedTargets();
+				if (targets.size() > 0) {
+					notifyHotModelReplacementFailed(targets);
+				}
+			}
+
+		} catch (CoreException e) {
+			e.printStackTrace();
+		}
+	}
+
+	protected void notifyHotModelReplacementFailed(
+			List<SCTDebugTarget> affectedTargets) {
+		synchronized (listeners) {
+			for (IHotModelReplacementListener listener : listeners) {
+				listener.hotCodeReplaceFailed(affectedTargets);
+			}
+		}
+	}
+
+	private List<SCTDebugTarget> getAffectedTargets() {
+		List<SCTDebugTarget> targets = new ArrayList<SCTDebugTarget>();
+		synchronized (activeTargets) {
+			for (SCTDebugTarget debugTarget : activeTargets) {
+				String resourceString = debugTarget.getResourceString();
+				IResource resource = ResourcesPlugin.getWorkspace().getRoot()
+						.findMember(resourceString);
+				if (changedFiles.contains(resource)) {
+					targets.add(debugTarget);
+				}
+			}
+		}
+		return targets;
+
+	}
+
+	@Override
+	public boolean visit(IResourceDelta delta) throws CoreException {
+		IResource resource = delta.getResource();
+		if ((delta.getFlags() & IResourceDelta.CONTENT) != 0) {
+			if (resource.getType() == IResource.FILE) {
+				IFile file = (IFile) resource;
+				changedFiles.add(file);
+			}
+		}
+		return true;
+	}
+
+}

+ 10 - 1
plugins/org.yakindu.sct.simulation.ui/src/org/yakindu/sct/simulation/ui/SimulationActivator.java

@@ -2,24 +2,33 @@ package org.yakindu.sct.simulation.ui;
 
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.osgi.framework.BundleContext;
+import org.yakindu.sct.simulation.core.hmr.SCTHotModelReplacementManager;
+import org.yakindu.sct.simulation.ui.dialogs.HotModelReplacementListener;
 
 public class SimulationActivator extends AbstractUIPlugin {
 
 	public static final String PLUGIN_ID = "org.yakindu.sct.statechart.simulation.ui"; //$NON-NLS-1$
 
 	private static SimulationActivator plugin;
-	
+
+	private HotModelReplacementListener hotModelReplacementListener;
+
 	public SimulationActivator() {
+		hotModelReplacementListener = new HotModelReplacementListener();
 	}
 
 	public void start(BundleContext context) throws Exception {
 		super.start(context);
 		plugin = this;
+		SCTHotModelReplacementManager.INSTANCE
+				.addReplacementListener(hotModelReplacementListener);
 	}
 
 	public void stop(BundleContext context) throws Exception {
 		plugin = null;
 		super.stop(context);
+		SCTHotModelReplacementManager.INSTANCE
+				.removeReplacementListener(hotModelReplacementListener);
 	}
 
 	public static SimulationActivator getDefault() {

+ 76 - 0
plugins/org.yakindu.sct.simulation.ui/src/org/yakindu/sct/simulation/ui/dialogs/HotModelReplaceErrorDialog.java

@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2011 committers of YAKINDU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.simulation.ui.dialogs;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.yakindu.sct.simulation.core.debugmodel.SCTDebugTarget;
+
+/**
+ * 
+ * @author andreas muelder - Initial contribution and API
+ * 
+ */
+public class HotModelReplaceErrorDialog extends ErrorDialog {
+
+	protected List<SCTDebugTarget> targets;
+
+	private static final int RELAUNCH_ID = 42;
+
+	public HotModelReplaceErrorDialog(Shell parentShell, String dialogTitle,
+			String message, IStatus status, List<SCTDebugTarget> target) {
+		super(parentShell, dialogTitle, message, status, IStatus.WARNING
+				| IStatus.ERROR | IStatus.INFO);
+		this.targets = target;
+	}
+
+	protected void createButtonsForButtonBar(Composite parent) {
+		super.createButtonsForButtonBar(parent);
+		getButton(IDialogConstants.OK_ID).setText("Terminate");
+		createButton(parent, RELAUNCH_ID, "Relaunch", false);
+	}
+
+	protected Button createButton(Composite parent, int id, String label,
+			boolean defaultButton) {
+		Button button = super.createButton(parent, id, label, defaultButton);
+		return button;
+	}
+
+	protected void buttonPressed(final int id) {
+		try {
+			if (id == IDialogConstants.OK_ID) {
+				for (SCTDebugTarget target : targets) {
+					target.getLaunch().terminate();
+				}
+			} else if (id == RELAUNCH_ID) {
+				for (SCTDebugTarget target : targets) {
+					target.getLaunch().terminate();
+					ILaunchConfiguration launchConfiguration = target
+							.getLaunch().getLaunchConfiguration();
+					DebugUITools.launch(launchConfiguration, target.getLaunch()
+							.getLaunchMode());
+				}
+			}
+		} catch (DebugException e) {
+			e.printStackTrace();
+		}
+		okPressed();
+	}
+}

+ 58 - 0
plugins/org.yakindu.sct.simulation.ui/src/org/yakindu/sct/simulation/ui/dialogs/HotModelReplacementListener.java

@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2011 committers of YAKINDU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.simulation.ui.dialogs;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.internal.ui.DebugUIPlugin;
+import org.eclipse.swt.widgets.Display;
+import org.yakindu.sct.simulation.core.debugmodel.SCTDebugTarget;
+import org.yakindu.sct.simulation.core.hmr.IHotModelReplacementListener;
+import org.yakindu.sct.simulation.ui.SimulationActivator;
+
+/**
+ * 
+ * @author andreas muelder - Initial contribution and API
+ * 
+ */
+@SuppressWarnings("restriction")
+public class HotModelReplacementListener implements
+		IHotModelReplacementListener {
+
+	private static final String MESSAGE = "The YAKINDU Statechart model changed during a simulation session. Do you want to terminate or restart the simulation?";
+	private IStatus status = new Status(IStatus.WARNING,
+			SimulationActivator.PLUGIN_ID, IStatus.WARNING,
+			"Resource changed during simulation", null);
+
+	public void hotCodeReplaceFailed(final List<SCTDebugTarget> targets) {
+		final Display display = Display.getDefault();
+		try {
+			final String title = "Model changed during simulation";
+			display.asyncExec(new Runnable() {
+				public void run() {
+					if (display.isDisposed()) {
+						return;
+					}
+					HotModelReplaceErrorDialog dialog = new HotModelReplaceErrorDialog(
+							DebugUIPlugin.getShell(), title, MESSAGE, status,
+							targets);
+					dialog.setBlockOnOpen(false);
+					dialog.open();
+				}
+			});
+		} catch (Exception ex) {
+			ex.printStackTrace();
+		}
+	}
+
+}