diff --git a/gips.gttermination/.classpath b/gips.gttermination/.classpath
new file mode 100644
index 0000000..8aced94
--- /dev/null
+++ b/gips.gttermination/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/gips.gttermination/.project b/gips.gttermination/.project
new file mode 100644
index 0000000..7f37720
--- /dev/null
+++ b/gips.gttermination/.project
@@ -0,0 +1,25 @@
+
+
+ gips.gttermination
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.xtext.ui.shared.xtextBuilder
+
+
+
+
+
+ org.emoflon.gips.gipsl.ui.gipsNature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.pde.PluginNature
+ org.eclipse.xtext.ui.shared.xtextNature
+
+
diff --git a/gips.gttermination/.settings/org.eclipse.jdt.core.prefs b/gips.gttermination/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..d4540a5
--- /dev/null
+++ b/gips.gttermination/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,10 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/gips.gttermination/META-INF/MANIFEST.MF b/gips.gttermination/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..ecb1573
--- /dev/null
+++ b/gips.gttermination/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Automatic-Module-Name: gips.gttermination
+Bundle-ManifestVersion: 2
+Bundle-Name: gips.gttermination
+Bundle-Vendor: My Company
+Bundle-Version: 1.0.0.qualifier
+Export-Package: gips.gttermination.connector
+Import-Package: test.suite.gips.utils
+Bundle-SymbolicName: gips.gttermination; singleton:=true
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Require-Bundle: org.emoflon.ibex.common,
+ org.emoflon.ibex.gt,
+ org.emoflon.gips.core,
+ org.emoflon.ibex.gt.democles,
+ org.emoflon.ibex.gt.hipe,
+ gipsl.all.build.model
diff --git a/gips.gttermination/build.properties b/gips.gttermination/build.properties
new file mode 100644
index 0000000..14acfb7
--- /dev/null
+++ b/gips.gttermination/build.properties
@@ -0,0 +1,5 @@
+source.. = src/,\
+ src-gen/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/gips.gttermination/src/gips/gttermination/Model.gipsl b/gips.gttermination/src/gips/gttermination/Model.gipsl
new file mode 100644
index 0000000..1665c34
--- /dev/null
+++ b/gips.gttermination/src/gips/gttermination/Model.gipsl
@@ -0,0 +1,51 @@
+package "gips.gttermination"
+import "platform:/resource/gipsl.all.build.model/model/Model.ecore"
+
+config {
+ solver := GLPK [home:="fu", license:="bar"];
+ launchConfig := true [main := "TODO"];
+ timeLimit := true [value := 10.0];
+ randomSeed := true [value := 73];
+ presolve := true;
+ debugOutput := true;
+}
+
+rule mapVnode {
+ root: Root {
+ -containers -> substrateContainer
+ -containers -> virtualContainer
+ }
+
+ substrateContainer: SubstrateContainer {
+ -substrateNodes -> snode
+ }
+
+ virtualContainer: VirtualContainer {
+ -virtualNodes -> vnode
+ }
+
+ snode: SubstrateResourceNode
+
+ vnode: VirtualResourceNode {
+ ++ -host -> snode
+ }
+}
+
+//
+// GIPSL starts here!
+//
+
+mapping n2n with mapVnode;
+
+// AND
+constraint -> global {
+ mappings.n2n->count() == 1
+}
+
+objective nObj -> mapping::n2n {
+ 1
+}
+
+global objective : max {
+ nObj
+}
diff --git a/gips.gttermination/src/gips/gttermination/connector/GtTerminationConnector.java b/gips.gttermination/src/gips/gttermination/connector/GtTerminationConnector.java
new file mode 100644
index 0000000..5870157
--- /dev/null
+++ b/gips.gttermination/src/gips/gttermination/connector/GtTerminationConnector.java
@@ -0,0 +1,31 @@
+package gips.gttermination.connector;
+
+import org.emoflon.gips.core.ilp.ILPSolverOutput;
+import org.emoflon.ibex.gt.api.GraphTransformationAPI;
+
+import gips.gttermination.api.gips.GtterminationGipsAPI;
+import test.suite.gips.utils.AConnector;
+import test.suite.gips.utils.GipsTestUtils;
+import test.suite.gips.utils.GlobalTestConfig;
+
+public class GtTerminationConnector extends AConnector {
+
+ public GtTerminationConnector(final String modelPath) {
+ api = new GtterminationGipsAPI();
+ api.init(GipsTestUtils.pathToAbsUri(modelPath));
+ GlobalTestConfig.overrideSolver(api);
+ }
+
+ @Override
+ public ILPSolverOutput run(final String outputPath) {
+ final ILPSolverOutput output = solve();
+ ((GtterminationGipsAPI) api).getN2n().applyNonZeroMappings();
+ save(outputPath);
+ return output;
+ }
+
+ public GraphTransformationAPI getEmoflonApi() {
+ return api.getEMoflonAPI();
+ }
+
+}
diff --git a/test.suite.gips/META-INF/MANIFEST.MF b/test.suite.gips/META-INF/MANIFEST.MF
index 23a6cd1..c6e0952 100644
--- a/test.suite.gips/META-INF/MANIFEST.MF
+++ b/test.suite.gips/META-INF/MANIFEST.MF
@@ -20,6 +20,7 @@ Require-Bundle: junit-jupiter-api;bundle-version="5.9.0",
gips.nullmodel;bundle-version="0.0.1",
gips.scheduling.taskmodel;bundle-version="0.0.1"
Import-Package: gips.generic.scheduling.connector,
+ gips.gttermination.connector,
gips.ilp.lpoutput.connector,
gips.ilp.timeout.clsnotinmodel.connector,
gips.ilp.timeout.connector,
diff --git a/test.suite.gips/src/test/suite/gips/gttermination/GipsGtTerminationTest.java b/test.suite.gips/src/test/suite/gips/gttermination/GipsGtTerminationTest.java
new file mode 100644
index 0000000..17d8f03
--- /dev/null
+++ b/test.suite.gips/src/test/suite/gips/gttermination/GipsGtTerminationTest.java
@@ -0,0 +1,70 @@
+package test.suite.gips.gttermination;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.emoflon.gips.core.ilp.ILPSolverOutput;
+import org.emoflon.gips.core.ilp.ILPSolverStatus;
+import org.emoflon.ibex.common.operational.IMatch;
+import org.junit.jupiter.api.Test;
+
+import gips.gttermination.connector.GtTerminationConnector;
+import model.Root;
+import test.suite.gipsl.all.build.AGipslAllBuildTest;
+
+public class GipsGtTerminationTest extends AGipslAllBuildTest {
+
+ // Setup method/utilities
+
+ public void callableSetUp() {
+ gen.persistModel(MODEL_PATH);
+ con = new GtTerminationConnector(MODEL_PATH);
+ }
+
+ @Override
+ protected void terminateApi() {
+ // Disable the automatic termination of the GIPS API of the super class
+ }
+
+ // Actual tests
+
+ @Test
+ public void testGtTermination() {
+ gen.genSubstrateNode("s1", 2);
+ gen.genVirtualNode("v1", 1);
+ gen.genVirtualNode("v2", 1);
+ callableSetUp();
+
+ final ILPSolverOutput ret = con.run(OUTPUT_PATH);
+ assertEquals(ILPSolverStatus.OPTIMAL, ret.status());
+
+ // Get the interpreter and check number of matches before the termination
+ // (must be 2)
+ final var interpreter = ((GtTerminationConnector) con).getEmoflonApi().getInterpreter();
+ final Iterator> it = interpreter.getMatches().values().iterator();
+ assertTrue(it.hasNext());
+ final var matches = it.next();
+ assertEquals(2, matches.size());
+
+ // Terminate the GIPS API; this should also terminate the GT interpreter
+ ((GtTerminationConnector) con).terminate();
+
+ // Remove model elements such that all matches are invalid
+ var res = ((GtTerminationConnector) con).getEmoflonApi().getModel().getResources().get(0);
+ final Root root = (Root) res.getContents().get(0);
+ root.getContainers().clear();
+
+ // Update matches
+ interpreter.updateMatches();
+
+ // Now, **two** matches must still be present
+ final Iterator> it2 = interpreter.getMatches().values().iterator();
+ assertTrue(it2.hasNext());
+ final var matches2 = it2.next();
+ assertEquals(2, matches2.size(), "GT engine termination failed.");
+ }
+
+}