diff --git a/build.gradle b/build.gradle
index 4c1ac5edabe59b8118e9c9252bdb13137f901acd..f794746784b7cdd9b8ba3b80e026f737a690210e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -108,7 +108,7 @@ def ecoreInputFiles = [ecoreFile]
 def ecoreOutputFiles = [ecoreRelastAspect,
                         ecoreRelastGrammar]
 
-def relastInputFiles = [ecoreRelastGrammar, './src/main/jastadd/base/marking/Marking.relast', './src/main/jastadd/engine/balloonMarking/BalloonMarking.relast', './src/main/jastadd/base/io/IoPN.relast', './src/main/jastadd/base/clauses/expressions.relast']
+def relastInputFiles = [ecoreRelastGrammar, './src/main/jastadd/base/marking/Marking.relast', './src/main/jastadd/engine/balloonMarking/BalloonMarking.relast', './src/main/jastadd/engine/balloonMarking/BalloonExecution.relast', './src/main/jastadd/base/io/IoPN.relast', './src/main/jastadd/base/clauses/expressions.relast']
 def relastOutputFiles = ['src/gen/jastadd/pnml/placeTransition.ast', 'src/gen/jastadd/pnml/placeTransition.jadd']
 
 task ecoreToRelast(type: JavaExec) {
diff --git a/src/main/jastadd/engine/balloonMarking/BalloonExecution.jadd b/src/main/jastadd/engine/balloonMarking/BalloonExecution.jadd
index a5659cabd5e7317dc27960dad697873b0b3e619a..89db56086b409145c440ce24c5687c29cc988a2a 100644
--- a/src/main/jastadd/engine/balloonMarking/BalloonExecution.jadd
+++ b/src/main/jastadd/engine/balloonMarking/BalloonExecution.jadd
@@ -1,3 +1,77 @@
 aspect BalloonExecution {
 
+    public Optional<BalloonMarking> BalloonMarking.fireTransition(Transition transition, BalloonCallbackStorage callbackStorage, boolean requireFlush) {
+
+        if(enabledBalloonTransitions().isEmpty()) return Optional.empty();
+
+        List<BalloonToken> inToken = new ArrayList<>();
+
+        // take a token from each incoming place
+        for(Place place : transition.incomingPlaces()){
+
+            BalloonMarkedPlace bmp = this.resolveBalloonPlace(place);
+
+            Random rand = new Random();
+            int randVal = rand.nextInt(bmp.getNumBalloonMarking());
+            BalloonToken bt = bmp.getBalloonMarking(randVal);
+
+            inToken.add(bt);
+            bmp.getBalloonMarking(randVal).removeSelf();
+        }
+
+        BalloonTransition balloonTransition = callbackStorage.resolveBalloonTransition(transition);
+
+        BalloonToken result = de.tudresden.inf.st.pnml.engine.execution.TransitionCallbackExecutor.execute(inToken, balloonTransition.getBalloonCallbacks());
+
+        // place a token in each outgoing place
+        for(Place place : transition.outgoingPlaces()){
+            BalloonMarkedPlace bmp = this.resolveBalloonPlace(place);
+            bmp.getBalloonMarkingList().add(result);
+        }
+
+        if (requireFlush) {
+            // flush the entire marking tree
+            this.flushTreeCache();
+        }
+
+        return Optional.of(this);
+    }
+
+    public Optional<BalloonMarking> BalloonMarking.fireTopicSubscriberTransition(java.util.Random random, Transition transition, BalloonCallbackStorage callbackStorage, boolean requireFlush) {
+        return null;
+    }
+
+    public Optional<BalloonMarking> BalloonMarking.fireServiceClientTransition(java.util.Random random, Transition transition, BalloonCallbackStorage callbackStorage, boolean requireFlush) {
+        return null;
+    }
+
+    public BalloonCallbackStorage PetriNet.initializeCallbackStorage() {
+
+        BalloonCallbackStorage storage = new BalloonCallbackStorage();
+        storage.setPetriNet(this);
+
+        for (Transition transition : allTransitions()){
+
+            if(transition.asInputSignalTransition().getStaticTransitionInformation().isDefaultTransitionInformation()) {
+
+                BalloonTransition balloonTransition = new BalloonTransition();
+                balloonTransition.setTransition(transition);
+                storage.addTransition(balloonTransition);
+            }
+        }
+
+        return storage;
+    }
+
+    syn BalloonTransition BalloonCallbackStorage.resolveBalloonTransition(Transition transition) = execMap().get(transition);
+
+    syn lazy java.util.Map<Transition, BalloonTransition> BalloonCallbackStorage.execMap() {
+
+        java.util.Map<Transition, BalloonTransition> map = new java.util.HashMap<>();
+
+        for (BalloonTransition balloonTransition : getTransitionList()) {
+            map.put(balloonTransition.getTransition(), balloonTransition);
+        }
+        return map;
+    }
 }
\ No newline at end of file
diff --git a/src/main/jastadd/engine/balloonMarking/BalloonExecution.relast b/src/main/jastadd/engine/balloonMarking/BalloonExecution.relast
new file mode 100644
index 0000000000000000000000000000000000000000..43915b349ed48beeb0dada40e1891ff743fdf4b5
--- /dev/null
+++ b/src/main/jastadd/engine/balloonMarking/BalloonExecution.relast
@@ -0,0 +1,4 @@
+BalloonCallbackStorage ::= PetriNet:PetriNet Transition:BalloonTransition*;
+BalloonTransition ::= <BalloonCallbacks:java.util.List<de.tudresden.inf.st.pnml.engine.execution.TransitionCallback>>;
+
+rel BalloonTransition.Transition -> Transition;
\ No newline at end of file
diff --git a/src/main/jastadd/engine/balloonMarking/BalloonMarking.jrag b/src/main/jastadd/engine/balloonMarking/BalloonMarking.jrag
index cee2ae35037c41356325d740308ea7674332391c..a6f087897dfee8e6882cfd421c5df32394d01cf5 100644
--- a/src/main/jastadd/engine/balloonMarking/BalloonMarking.jrag
+++ b/src/main/jastadd/engine/balloonMarking/BalloonMarking.jrag
@@ -1,9 +1,13 @@
 aspect BalloonMarking {
 
-    public BalloonMarking PetriNet.initialBalloonMarking(PetriNet pn) throws IOException, SAXException, ParserConfigurationException {
+    eq BalloonCallbackStorage.getPetriNet().marking() = null;
+
+    eq BalloonMarking.getPetriNet().marking() = null;
+
+    public BalloonMarking PetriNet.initializeBalloonMarking() throws IOException, SAXException, ParserConfigurationException {
 
         BalloonMarking marking = new BalloonMarking();
-        marking.setPetriNet(pn);
+        marking.setPetriNet(this);
 
         for (Place place : allPlaces()) {
 
@@ -16,12 +20,14 @@ aspect BalloonMarking {
                 token.setValue(val);
                 markedPlace.getBalloonMarkingList().add(token);
             }
+
+            marking.addPlace(markedPlace);
         }
 
         return marking;
     }
 
-    syn BalloonMarkedPlace BalloonMarking.resolvePlace(Place place) = balloonPlaceMap().get(place);
+    syn BalloonMarkedPlace BalloonMarking.resolveBalloonPlace(Place place) = balloonPlaceMap().get(place);
 
     syn lazy java.util.Map<Place, BalloonMarkedPlace> BalloonMarking.balloonPlaceMap() {
 
@@ -33,7 +39,7 @@ aspect BalloonMarking {
         return map;
     }
 
-    syn BalloonMarkedPlace BalloonMarking.resolvePlaceById(String placeID) {
+    syn BalloonMarkedPlace BalloonMarking.resolveBalloonPlaceById(String placeID) {
 
         for (BalloonMarkedPlace markedPlace : getPlaceList()) {
             if(markedPlace.getPlace().getId().equals(placeID)){
@@ -46,13 +52,13 @@ aspect BalloonMarking {
     syn java.util.List<String> BalloonMarking.marking(Place place) {
 
         java.util.List<String> marking = new ArrayList<>();
-            for(BalloonToken bt : resolvePlace(place).getBalloonMarkingList()){
+            for(BalloonToken bt : resolveBalloonPlace(place).getBalloonMarkingList()){
                 marking.add(bt.getValue());
             }
         return marking;
     }
 
-    syn boolean BalloonMarking.isEnabled(Transition t) {
+    syn boolean BalloonMarking.isBalloonEnabled(Transition t) {
 
         for (Place place : t.incomingPlaces()) {
             if (marking(place).size() == 0) return false;
@@ -63,13 +69,13 @@ aspect BalloonMarking {
     syn boolean BalloonMarking.isDead() {
 
         for (Transition transition : getPetriNet().allTransitions()) {
-            if (isEnabled(transition)) return false;
+            if (isBalloonEnabled(transition)) return false;
         }
         return true;
     }
 
-    syn java.util.Set<Transition> BalloonMarking.enabledTransitions()
+    syn java.util.Set<Transition> BalloonMarking.enabledBalloonTransitions()
         = getPetriNet().allTransitions().stream()
-        .filter(t -> isEnabled(t))
+        .filter(t -> isBalloonEnabled(t))
         .collect(Collectors.toSet());
 }
\ No newline at end of file
diff --git a/src/main/jastadd/engine/balloonMarking/BalloonPrinting.jrag b/src/main/jastadd/engine/balloonMarking/BalloonPrinting.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..302c84ce8033e95d8d319fa5a27b3893221ea66e
--- /dev/null
+++ b/src/main/jastadd/engine/balloonMarking/BalloonPrinting.jrag
@@ -0,0 +1,25 @@
+aspect BalloonPrinting {
+
+        syn String BalloonMarking.print() {
+
+            StringBuilder b = new StringBuilder();
+            b.append("Marking for Petri net '").append(getPetriNet().name()).append("':\n");
+
+            for (BalloonMarkedPlace place : getPlaceList()) {
+                b.append("  ").append(place.getPlace().name()).append(": \n");
+
+                for(BalloonToken bt : place.getBalloonMarkingList()){
+                    b.append(bt.getValue()).append("\n");
+                }
+
+            }
+
+            b.append("Transitions for Petri net '").append(getPetriNet().name()).append("':\n");
+
+            for (Transition trans : getPetriNet().allTransitions()) {
+
+                b.append("  ").append(trans.name()).append(": ").append(isBalloonEnabled(trans)?"true":"false").append("\n");
+            }
+            return b.toString();
+        }
+}
\ No newline at end of file
diff --git a/src/main/java/de/tudresden/inf/st/pnml/engine/Main.java b/src/main/java/de/tudresden/inf/st/pnml/engine/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..4856622d850b83324cd489f22891a58535927ad0
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/st/pnml/engine/Main.java
@@ -0,0 +1,45 @@
+package de.tudresden.inf.st.pnml.engine;
+
+import de.tudresden.inf.st.pnml.engine.execution.DefaultFinalTransitionCallback;
+import de.tudresden.inf.st.pnml.engine.execution.TransitionCallback;
+import de.tudresden.inf.st.pnml.jastadd.model.*;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Main {
+
+    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
+
+        String pnmlPath = "../pnml-relast-nets/src/main/resources/balloonTestNets/balloon-correct-2.pnml";
+        PetriNet petriNet = PnmlParser.parsePnml(pnmlPath).get(0);
+
+        BalloonMarking bm = petriNet.initializeBalloonMarking();
+        BalloonCallbackStorage bcs = petriNet.initializeCallbackStorage();
+
+        TransitionCallback tc = new DefaultFinalTransitionCallback("default_final_cb", 1);
+        List<TransitionCallback> tcl = new ArrayList<>();
+        tcl.add(tc);
+
+        System.out.println("----- BEFORE -----");
+        System.out.println(bm.print());
+
+        for(Transition t : petriNet.allTransitions()){
+            bcs.resolveBalloonTransition(t).setBalloonCallbacks(tcl);
+        }
+
+        for(Transition t : petriNet.allTransitions()){
+
+            if(bm.isBalloonEnabled(t)){
+                bm.fireTransition(t, bcs, false);
+                break;
+            }
+        }
+
+        System.out.println("----- AFTER -----");
+        System.out.println(bm.print());
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/st/pnml/engine/execution/DefaultFinalTransitionCallback.java b/src/main/java/de/tudresden/inf/st/pnml/engine/execution/DefaultFinalTransitionCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba99fa6771b2065e1f785a99e8350b35b3035585
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/st/pnml/engine/execution/DefaultFinalTransitionCallback.java
@@ -0,0 +1,21 @@
+package de.tudresden.inf.st.pnml.engine.execution;
+
+import de.tudresden.inf.st.pnml.jastadd.model.BalloonToken;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultFinalTransitionCallback extends TransitionCallback{
+
+    public DefaultFinalTransitionCallback(String id, int priority) {
+        super(id, priority);
+    }
+
+    @Override
+    public List<BalloonToken> processToken(List<BalloonToken> tokens) {
+
+        List<BalloonToken> tl = new ArrayList<>();
+        tl.add(tokens.get(0));
+        return tl;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/de/tudresden/inf/st/pnml/engine/execution/DefaultTransitionCallback.java b/src/main/java/de/tudresden/inf/st/pnml/engine/execution/DefaultTransitionCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a321751e107967041f66849a8c60cf6bfa4d9c5
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/st/pnml/engine/execution/DefaultTransitionCallback.java
@@ -0,0 +1,17 @@
+package de.tudresden.inf.st.pnml.engine.execution;
+
+import de.tudresden.inf.st.pnml.jastadd.model.BalloonToken;
+
+import java.util.List;
+
+public class DefaultTransitionCallback extends TransitionCallback{
+
+    public DefaultTransitionCallback(String id, int priority) {
+        super(id, priority);
+    }
+
+    @Override
+    public List<BalloonToken> processToken(List<BalloonToken> tokens) {
+        return tokens;
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/st/pnml/engine/execution/TransitionCallback.java b/src/main/java/de/tudresden/inf/st/pnml/engine/execution/TransitionCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..cce1f5d05969ad74808e8b12baeb16d464268341
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/st/pnml/engine/execution/TransitionCallback.java
@@ -0,0 +1,32 @@
+package de.tudresden.inf.st.pnml.engine.execution;
+
+import de.tudresden.inf.st.pnml.jastadd.model.BalloonToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public abstract class TransitionCallback {
+
+    protected static final Logger logger = LoggerFactory.getLogger(TransitionCallback.class);
+
+    private String id;
+    private int priority;
+
+    public TransitionCallback(String id, int priority) {
+        this.id = id;
+        this.priority = priority;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+
+    public List<BalloonToken> processToken(List<BalloonToken> tokens) {
+        return tokens;
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/st/pnml/engine/execution/TransitionCallbackExecutor.java b/src/main/java/de/tudresden/inf/st/pnml/engine/execution/TransitionCallbackExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..54541430099e26465d76ea2274884c35eadab739
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/st/pnml/engine/execution/TransitionCallbackExecutor.java
@@ -0,0 +1,45 @@
+package de.tudresden.inf.st.pnml.engine.execution;
+
+import de.tudresden.inf.st.pnml.jastadd.model.BalloonToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Automatically sort and call the callbacks.
+ */
+public class TransitionCallbackExecutor {
+
+    protected static final Logger logger = LoggerFactory.getLogger(TransitionCallbackExecutor.class);
+
+    public static final BalloonToken execute(List<BalloonToken> inTokens, List<TransitionCallback> callbacks){
+
+        List<TransitionCallback> callbacksSorted = new ArrayList<>();
+        List<BalloonToken> outTokens = new ArrayList<>();
+        outTokens.addAll(inTokens);
+        callbacks.addAll(callbacks);
+
+        Collections.sort(callbacksSorted, Comparator.comparingInt(TransitionCallback::getPriority));
+
+        for(int i = 0; i < callbacksSorted.size(); i++){
+            if(i < callbacksSorted.size() - 1){
+                outTokens = callbacksSorted.get(i).processToken(outTokens);
+            } else {
+                outTokens = new ArrayList<>();
+                outTokens.add(callbacksSorted.get(i).processToken(outTokens).get(0));
+            }
+        }
+
+        if(outTokens.size() != 1){
+            logger.error("Error! Wrong output token size.");
+        }
+
+        logger.info("Created new output token: " + outTokens.get(0));
+
+        return outTokens.get(0);
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/st/pnml/engine/parsing/BalloonTokenParsing.java b/src/main/java/de/tudresden/inf/st/pnml/engine/parsing/BalloonTokenParsing.java
index 5bc590b7c0a3ce6815599772a12cf47a2205acba..20ef66a3a9444d4e0d58d42a966d77252d2bcb9f 100644
--- a/src/main/java/de/tudresden/inf/st/pnml/engine/parsing/BalloonTokenParsing.java
+++ b/src/main/java/de/tudresden/inf/st/pnml/engine/parsing/BalloonTokenParsing.java
@@ -21,7 +21,7 @@ public class BalloonTokenParsing {
         List<String> marking = new ArrayList<>();
 
         for (ToolInfo ti : place.getToolspecifics()) {
-            if (ti.getFormattedXMLBuffer().lastIndexOf(BalloonConstants.BALLOON_MARKING) == -1) {
+            if (ti.getFormattedXMLBuffer().lastIndexOf(BalloonConstants.BALLOON_MARKING) != -1) {
 
                 StringBuffer toolInfoStringBuffer = ti.getFormattedXMLBuffer();