diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..fd7ad7fbda73f2f1dd3afff62fa9abe499976bc6
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..934e0e06ffa0a2aeedbe1341d321549336719cc0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/target
diff --git a/.project b/.project
new file mode 100644
index 0000000000000000000000000000000000000000..bdb6e353c82e61b1761c5b0672ba7e965d124c1d
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>teetime</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..f9fe34593fcd3624a964478aeb438b0d44fe7237
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..69c31cd493ce042398e9fe93d22b72beb46afa88
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..f897a7f1cb2389f85fe6381425d29f0a9866fb65
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/teetime/pom.xml b/pom.xml
similarity index 56%
rename from teetime/pom.xml
rename to pom.xml
index e50c55b3608a40d87c0b55ed3fd6f054df965abf..1edcb345c939f5ad85af783c3f53e5014b2d018c 100644
--- a/teetime/pom.xml
+++ b/pom.xml
@@ -18,8 +18,28 @@
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
-      <version>3.8.1</version>
+      <version>4.11</version>
       <scope>test</scope>
     </dependency>
+	<dependency>
+		<groupId>org.hamcrest</groupId>
+		<artifactId>hamcrest-core</artifactId>
+		<version>1.3</version>
+	</dependency>
+	<dependency>
+		<groupId>org.hamcrest</groupId>
+		<artifactId>hamcrest-library</artifactId>
+		<version>1.3</version>
+	</dependency>
+	<dependency>
+      <groupId>net.kieker-monitoring</groupId>
+      <artifactId>kieker</artifactId>
+      <version>1.9</version>
+    </dependency>
+	<dependency>
+		<groupId>com.google.guava</groupId>
+		<artifactId>guava</artifactId>
+		<version>17.0</version>
+	</dependency>
   </dependencies>
 </project>
diff --git a/results/chw-work/Kieker.csv b/results/chw-work/Kieker.csv
new file mode 100644
index 0000000000000000000000000000000000000000..0b207487ffdac4b6e5355048a353562163affd1f
--- /dev/null
+++ b/results/chw-work/Kieker.csv
@@ -0,0 +1,20 @@
+50;222974790;249344303;255853039;259406468;302300903;256949018;3867545
+100;479268312;501520758;514242657;520879474;547129527;512607537;3861798
+150;769605950;814607155;827869293;840828881;997575901;832140772;10406475
+200;1053294865;1114439212;1133247837;1174248560;1374436002;1150302103;17160918
+250;1347713034;1403914765;1423028403;1445675354;1514311623;1429062984;10029167
+300;1634709890;1692022888;1716632518;1733769931;1845717310;1714584838;10851185
+350;1898198121;1979121553;2005629409;2021975347;2104626641;2002653027;12416337
+400;2184222053;2264418050;2297249500;2347125585;2571002690;2312469267;20003529
+450;2114346436;2474942703;2513493875;2554615702;2821436913;2484613004;39023338
+500;2714958023;2807430509;2834917858;2855149332;2908789030;2833729060;9892356
+550;2953534484;3129759679;3152678393;3207891190;3355362594;3171670501;21159351
+600;3375834220;3456305673;3507196145;3564639277;4045959350;3525562922;30257420
+650;3733499812;3793166710;3842865043;3931548874;4501397192;3901445690;47083708
+700;3902291379;4145148703;4226156701;4324776258;5536500029;4373255110;108796825
+750;4488138338;4536918344;4577786471;4664000637;4766312788;4598726882;21193905
+800;4787523343;4899780250;4954223740;5032639329;5227891132;4974727780;27106090
+850;5330114612;5428669718;5497654926;5559696251;5744444585;5503212946;26634323
+900;4915166811;5759447724;5940546980;6032643433;7847659872;6004221609;120237698
+950;6278789404;6558588268;6770077633;7249897679;10036245303;7069647198;217586495
+1000;6998942511;7251777843;7319524934;7434979353;8077187734;7366103358;56194326
diff --git a/results/chw-work/TeeTime.csv b/results/chw-work/TeeTime.csv
new file mode 100644
index 0000000000000000000000000000000000000000..f2ab78024eb65794f5921e35140ac1a51298cc12
--- /dev/null
+++ b/results/chw-work/TeeTime.csv
@@ -0,0 +1,20 @@
+50;81005535;92166652;93302961;95672050;103493206;93511502;1038983
+100;174480091;183187963;185029128;188204061;194225513;185294239;1154436
+150;239712770;274010174;277981097;280009048;295593477;276621639;2183336
+200;337821234;367758134;371051706;377328500;478632833;374718182;5375113
+250;385948931;442055422;457899298;465820620;524613120;452422321;7162054
+300;514072703;542246436;547270333;553017151;564887640;546601274;2986762
+350;615707092;645556962;650576755;657115458;671433855;651241364;2766588
+400;700708593;734629552;740820958;745605936;754731302;739358059;3082558
+450;803342998;831226084;835117780;840909342;850780207;835062277;2948398
+500;876183364;920951807;930805839;937619999;963086376;929382628;4780910
+550;1003258678;1028639258;1035441512;1042563969;1072049712;1035805640;3409157
+600;1093110018;1131170625;1137555794;1147802691;1268041677;1142788571;7089446
+650;1181869795;1215763661;1224030638;1233324726;1278020097;1223997024;4308705
+700;1277366144;1316095891;1326646981;1336546582;1378479587;1327486503;4854941
+750;1372059934;1404826111;1418028315;1430832318;1457884930;1416883852;5369178
+800;1474965281;1493768979;1500705882;1512160518;1543885627;1503343105;3938081
+850;1546340924;1592571626;1605533267;1613038736;1629217593;1601614338;4648053
+900;1604224130;1687782667;1702500907;1711553611;1828215358;1701607220;9538072
+950;1737332802;1772582603;1787626793;1810397720;1878440793;1792495376;8716251
+1000;1835066054;1875513813;1889568248;1905512700;1982777611;1896242150;8591685
diff --git a/results/evaluation.xlsx b/results/evaluation.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..2d32e884ec9f8629de80baca0fbb8a15e4f439db
Binary files /dev/null and b/results/evaluation.xlsx differ
diff --git a/src/main/java/experiment/Experiment1.java b/src/main/java/experiment/Experiment1.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6198533ac93c2773232be119bc188228331dab7
--- /dev/null
+++ b/src/main/java/experiment/Experiment1.java
@@ -0,0 +1,232 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package experiment;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import javax.security.auth.login.Configuration;
+
+import kieker.analysis.AnalysisController;
+import kieker.analysis.IAnalysisController;
+import kieker.analysis.stage.EmptyPassOnFilter;
+import kieker.analysis.stage.ObjectProducer;
+import teetime.framework.concurrent.StageTerminationPolicy;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IStage;
+import teetime.framework.core.Pipeline;
+import teetime.framework.sequential.QueuePipe;
+import teetime.stage.NoopFilter;
+import teetime.util.StatisticsUtil;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public class Experiment1 {
+
+	private static final int NUMBER_OF_WARMUP_RUNS_PER_EXPERIMENT = 5;
+	private static final int NUMBER_OF_MEASURED_RUNS_PER_EXPERIMENT = 50;
+
+	private static final int NUMBER_OF_OBJECTS_TO_SEND = 10000;
+
+	private static final int NUMBER_OF_MINIMAL_FILTERS = 50;
+	private static final int NUMBER_OF_MAXIMAL_FILTERS = 1000;
+	private static final int NUMBER_OF_FILTERS_PER_STEP = 50;
+
+	private static final IAnalysis[] analyses = { new TeeTimeAnalysis(), new KiekerAnalysis() };
+
+	private static final List<Long> measuredTimes = new ArrayList<Long>();
+
+	public static void main(final String[] args) throws Exception {
+		System.setProperty("kieker.common.logging.Log", "NONE");
+
+		for (final IAnalysis analysis : analyses) {
+			for (int numberOfFilters = NUMBER_OF_MINIMAL_FILTERS; numberOfFilters <= NUMBER_OF_MAXIMAL_FILTERS; numberOfFilters += NUMBER_OF_FILTERS_PER_STEP) {
+				// Warmup
+				for (int run = 0; run < NUMBER_OF_WARMUP_RUNS_PER_EXPERIMENT; run++) {
+					analysis.initialize(numberOfFilters, NUMBER_OF_OBJECTS_TO_SEND);
+					analysis.execute();
+				}
+
+				// Actual measurement
+				for (int run = 0; run < NUMBER_OF_MEASURED_RUNS_PER_EXPERIMENT; run++) {
+					final long tin = System.nanoTime();
+
+					analysis.initialize(numberOfFilters, NUMBER_OF_OBJECTS_TO_SEND);
+					analysis.execute();
+
+					final long tout = System.nanoTime();
+					Experiment1.addMeasuredTime((tout - tin));
+				}
+
+				Experiment1.writeAndClearMeasuredTime(analysis.getName(), numberOfFilters);
+			}
+		}
+	}
+
+	private static void addMeasuredTime(final long time) {
+		measuredTimes.add(new Long(time));
+	}
+
+	private static void writeAndClearMeasuredTime(final String analysisName, final int numberOfFilters) throws IOException {
+		final FileWriter fileWriter = new FileWriter(analysisName + ".csv", true);
+		fileWriter.write(Integer.toString(numberOfFilters));
+		fileWriter.write(";");
+
+		final Map<Double, Long> quintiles = StatisticsUtil.calculateQuintiles(measuredTimes);
+		for (final Long value : quintiles.values()) {
+			fileWriter.write(Long.toString(value));
+			fileWriter.write(";");
+		}
+
+		fileWriter.write(Long.toString(StatisticsUtil.calculateAverage(measuredTimes)));
+		fileWriter.write(";");
+
+		fileWriter.write(Long.toString(StatisticsUtil.calculateConfidenceWidth(measuredTimes)));
+
+		fileWriter.write("\n");
+		fileWriter.close();
+
+		measuredTimes.clear();
+	}
+
+	private static interface IAnalysis {
+
+		public void initialize(int numberOfFilters, int numberOfObjectsToSend) throws Exception;
+
+		public String getName();
+
+		public void execute() throws Exception;
+
+	}
+
+	private static final class TeeTimeAnalysis extends Analysis implements IAnalysis {
+
+		private static final int SECONDS = 1000;
+
+		private Pipeline pipeline;
+		private WorkerThread workerThread;
+
+		public TeeTimeAnalysis() {}
+
+		@Override
+		public void initialize(final int numberOfFilters, final int numberOfObjectsToSend) {
+
+			@SuppressWarnings("unchecked")
+			final NoopFilter<Object>[] noopFilters = new NoopFilter[numberOfFilters];
+			// create stages
+			final teetime.stage.basic.ObjectProducer<Object> objectProducer = new teetime.stage.basic.ObjectProducer<Object>(
+					numberOfObjectsToSend, new Callable<Object>() {
+						@Override
+						public Object call() throws Exception {
+							return new Object();
+						}
+					});
+			for (int i = 0; i < noopFilters.length; i++) {
+				noopFilters[i] = new NoopFilter<Object>();
+			}
+
+			// add each stage to a stage list
+			final List<IStage> startStages = new LinkedList<IStage>();
+			startStages.add(objectProducer);
+
+			final List<IStage> stages = new LinkedList<IStage>();
+			stages.add(objectProducer);
+			stages.addAll(Arrays.asList(noopFilters));
+
+			// connect stages by pipes
+			QueuePipe.connect(objectProducer.outputPort, noopFilters[0].inputPort);
+			for (int i = 1; i < noopFilters.length; i++) {
+				QueuePipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort);
+			}
+
+			this.pipeline = new Pipeline();
+			this.pipeline.setStartStages(startStages);
+			this.pipeline.setStages(stages);
+
+			this.workerThread = new WorkerThread(this.pipeline, 0);
+			this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+		}
+
+		@Override
+		public String getName() {
+			return "TeeTime";
+		}
+
+		@Override
+		public void execute() {
+			super.start();
+
+			this.workerThread.start();
+			try {
+				this.workerThread.join(60 * SECONDS);
+			} catch (final InterruptedException e) {
+				e.printStackTrace();
+			}
+		}
+
+	}
+
+	private static final class KiekerAnalysis implements IAnalysis {
+
+		private IAnalysisController ac;
+
+		public KiekerAnalysis() {}
+
+		@Override
+		public void initialize(final int numberOfFilters, final int numberOfObjectsToSend) throws Exception {
+			this.ac = new AnalysisController();
+
+			final Configuration producerConfig = new Configuration();
+			producerConfig.setProperty(ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(numberOfObjectsToSend));
+			final ObjectProducer<Object> producer = new ObjectProducer<Object>(producerConfig, this.ac, new Callable<Object>() {
+				@Override
+				public Object call() throws Exception {
+					return new Object();
+				}
+			});
+
+			EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), this.ac);
+			this.ac.connect(producer, ObjectProducer.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+			for (int idx = 0; idx < (numberOfFilters - 1); idx++) {
+				final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), this.ac);
+				this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+				predecessor = newPredecessor;
+			}
+		}
+
+		@Override
+		public String getName() {
+			return "Kieker";
+		}
+
+		@Override
+		public void execute() throws Exception {
+			this.ac.run();
+		}
+
+	}
+
+}
diff --git a/src/main/java/experiment/Experiment2.java b/src/main/java/experiment/Experiment2.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c261806a4ff2804b925a980402d58ef6b2bffcd
--- /dev/null
+++ b/src/main/java/experiment/Experiment2.java
@@ -0,0 +1,263 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package experiment;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import javax.security.auth.login.Configuration;
+
+import kieker.analysis.AnalysisController;
+import kieker.analysis.IAnalysisController;
+import kieker.analysis.stage.CollectorSink;
+import kieker.analysis.stage.EmptyPassOnFilter;
+import kieker.analysis.stage.ObjectProducer;
+import kieker.analysis.stage.StartTimestampFilter;
+import kieker.analysis.stage.StopTimestampFilter;
+import teetime.examples.throughput.TimestampObject;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IStage;
+import teetime.framework.core.Pipeline;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.framework.sequential.QueuePipe;
+import teetime.stage.NoopFilter;
+import teetime.util.StatisticsUtil;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public class Experiment2 {
+
+	private static final int NUMBER_OF_WARMUP_RUNS_PER_EXPERIMENT = 5;
+	private static final int NUMBER_OF_MEASURED_RUNS_PER_EXPERIMENT = 50;
+
+	private static final int NUMBER_OF_OBJECTS_TO_SEND = 10000;
+
+	private static final int NUMBER_OF_MINIMAL_FILTERS = 50;
+	private static final int NUMBER_OF_MAXIMAL_FILTERS = 1000;
+	private static final int NUMBER_OF_FILTERS_PER_STEP = 50;
+
+	private static final IAnalysis[] analyses = { new TeeTimeAnalysis(true), new TeeTimeAnalysis(false), new KiekerAnalysis() };
+
+	private static final List<Long> measuredTimes = new ArrayList<Long>();
+
+	public static void main(final String[] args) throws Exception {
+		System.setProperty("kieker.common.logging.Log", "NONE");
+
+		for (final IAnalysis analysis : analyses) {
+			for (int numberOfFilters = NUMBER_OF_MINIMAL_FILTERS; numberOfFilters <= NUMBER_OF_MAXIMAL_FILTERS; numberOfFilters += NUMBER_OF_FILTERS_PER_STEP) {
+				// Warmup
+				for (int run = 0; run < NUMBER_OF_WARMUP_RUNS_PER_EXPERIMENT; run++) {
+					analysis.initialize(numberOfFilters, NUMBER_OF_OBJECTS_TO_SEND);
+					analysis.execute();
+				}
+
+				// Actual measurement
+				for (int run = 0; run < NUMBER_OF_MEASURED_RUNS_PER_EXPERIMENT; run++) {
+					final long tin = System.nanoTime();
+
+					analysis.initialize(numberOfFilters, NUMBER_OF_OBJECTS_TO_SEND);
+					analysis.execute();
+
+					final long tout = System.nanoTime();
+					Experiment2.addMeasuredTime((tout - tin));
+				}
+
+				Experiment2.writeAndClearMeasuredTime(analysis.getName(), numberOfFilters);
+			}
+		}
+	}
+
+	private static void addMeasuredTime(final long time) {
+		measuredTimes.add(new Long(time));
+	}
+
+	private static void writeAndClearMeasuredTime(final String analysisName, final int numberOfFilters) throws IOException {
+		final FileWriter fileWriter = new FileWriter(analysisName + ".csv", true);
+		fileWriter.write(Integer.toString(numberOfFilters));
+		fileWriter.write(";");
+
+		final Map<Double, Long> quintiles = StatisticsUtil.calculateQuintiles(measuredTimes);
+		for (final Long value : quintiles.values()) {
+			fileWriter.write(Long.toString(value));
+			fileWriter.write(";");
+		}
+
+		fileWriter.write(Long.toString(StatisticsUtil.calculateAverage(measuredTimes)));
+		fileWriter.write(";");
+
+		fileWriter.write(Long.toString(StatisticsUtil.calculateConfidenceWidth(measuredTimes)));
+
+		fileWriter.write("\n");
+		fileWriter.close();
+
+		measuredTimes.clear();
+	}
+
+	private static interface IAnalysis {
+
+		public String getName();
+
+		public void execute() throws Exception;
+
+		public void initialize(int numberOfFilters, int numberOfObjectsToSend) throws Exception;
+
+	}
+
+	private static final class TeeTimeAnalysis extends Analysis implements IAnalysis {
+
+		private static final int SECONDS = 1000;
+
+		private Pipeline pipeline;
+		private WorkerThread workerThread;
+		private final boolean shouldUseQueue;
+
+		public TeeTimeAnalysis(final boolean shouldUseQueue) {
+			this.shouldUseQueue = shouldUseQueue;
+		}
+
+		@Override
+		public void initialize(final int numberOfFilters, final int numberOfObjectsToSend) throws Exception {
+			@SuppressWarnings("unchecked")
+			final NoopFilter<TimestampObject>[] noopFilters = new NoopFilter[numberOfFilters];
+			// create stages
+			final teetime.stage.basic.ObjectProducer<TimestampObject> objectProducer = new teetime.stage.basic.ObjectProducer<TimestampObject>(
+					numberOfObjectsToSend, new Callable<TimestampObject>() {
+						@Override
+						public TimestampObject call() throws Exception {
+							return new TimestampObject();
+						}
+					});
+			final teetime.stage.StartTimestampFilter startTimestampFilter = new teetime.stage.StartTimestampFilter();
+			for (int i = 0; i < noopFilters.length; i++) {
+				noopFilters[i] = new NoopFilter<TimestampObject>();
+			}
+			final teetime.stage.StopTimestampFilter stopTimestampFilter = new teetime.stage.StopTimestampFilter();
+			final teetime.stage.CollectorSink<TimestampObject> collectorSink = new teetime.stage.CollectorSink<TimestampObject>(
+					this.timestampObjects);
+
+			// add each stage to a stage list
+			final List<IStage> startStages = new LinkedList<IStage>();
+			startStages.add(objectProducer);
+
+			final List<IStage> stages = new LinkedList<IStage>();
+			stages.add(objectProducer);
+			if (this.shouldUseQueue) {
+				stages.add(startTimestampFilter);
+				stages.addAll(Arrays.asList(noopFilters));
+				stages.add(stopTimestampFilter);
+				stages.add(collectorSink);
+
+				// connect stages by pipes
+				QueuePipe.connect(objectProducer.outputPort, startTimestampFilter.inputPort);
+				QueuePipe.connect(startTimestampFilter.outputPort, noopFilters[0].inputPort);
+				for (int i = 1; i < noopFilters.length; i++) {
+					QueuePipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort);
+				}
+				QueuePipe.connect(noopFilters[noopFilters.length - 1].outputPort, stopTimestampFilter.inputPort);
+				QueuePipe.connect(stopTimestampFilter.outputPort, collectorSink.objectInputPort);
+			} else {
+				// connect stages by pipes
+				MethodCallPipe.connect(objectProducer.outputPort, startTimestampFilter.inputPort);
+				MethodCallPipe.connect(startTimestampFilter.outputPort, noopFilters[0].inputPort);
+				for (int i = 1; i < noopFilters.length; i++) {
+					MethodCallPipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort);
+				}
+				MethodCallPipe.connect(noopFilters[noopFilters.length - 1].outputPort, stopTimestampFilter.inputPort);
+				MethodCallPipe.connect(stopTimestampFilter.outputPort, collectorSink.objectInputPort);
+			}
+
+			this.pipeline = new Pipeline();
+			this.pipeline.setStartStages(startStages);
+			this.pipeline.setStages(stages);
+		}
+
+		@Override
+		public String getName() {
+			return "TeeTime" + (this.shouldUseQueue ? "-Queues" : "-NoQueues");
+		}
+
+		@Override
+		public void execute() {
+			super.start();
+
+			this.workerThread.start();
+			try {
+				this.workerThread.join(60 * SECONDS);
+			} catch (final InterruptedException e) {
+				e.printStackTrace();
+			}
+		}
+
+	}
+
+	private static final class KiekerAnalysis implements IAnalysis {
+
+		private IAnalysisController ac;
+
+		public KiekerAnalysis() {}
+
+		@Override
+		public void initialize(final int numberOfFilters, final int numberOfObjectsToSend) throws Exception {
+			this.ac = new AnalysisController();
+
+			final Configuration producerConfig = new Configuration();
+			producerConfig.setProperty(ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(numberOfObjectsToSend));
+			final ObjectProducer<TimestampObject> producer = new ObjectProducer<TimestampObject>(producerConfig, this.ac, new Callable<TimestampObject>() {
+				@Override
+				public TimestampObject call() throws Exception {
+					return new TimestampObject();
+				}
+			});
+
+			final StartTimestampFilter startTimestampFilter = new StartTimestampFilter(new Configuration(), this.ac);
+			EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), this.ac);
+			this.ac.connect(producer, ObjectProducer.OUTPUT_PORT_NAME, startTimestampFilter, StartTimestampFilter.INPUT_PORT_NAME);
+			this.ac.connect(startTimestampFilter, StartTimestampFilter.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+			for (int idx = 0; idx < (numberOfFilters - 1); idx++) {
+				final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), this.ac);
+				this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+				predecessor = newPredecessor;
+			}
+			final StopTimestampFilter stopTimestampFilter = new StopTimestampFilter(new Configuration(), this.ac);
+			final CollectorSink<TimestampObject> collectorSink = new CollectorSink<TimestampObject>(new Configuration(), this.ac, this.timestampObjects);
+
+			this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, stopTimestampFilter, StopTimestampFilter.INPUT_PORT_NAME);
+			this.ac.connect(stopTimestampFilter, StopTimestampFilter.OUTPUT_PORT_NAME, collectorSink, CollectorSink.INPUT_PORT_NAME);
+		}
+
+		@Override
+		public String getName() {
+			return "Kieker";
+		}
+
+		@Override
+		public void execute() throws Exception {
+			this.ac.run();
+		}
+
+	}
+
+}
diff --git a/src/main/java/kieker/analysis/examples/ThroughputAnalysis.java b/src/main/java/kieker/analysis/examples/ThroughputAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..589185abb5ae97ce6c37edb8c3a26d7a7d099ece
--- /dev/null
+++ b/src/main/java/kieker/analysis/examples/ThroughputAnalysis.java
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.examples;
+
+import java.util.concurrent.Callable;
+
+import kieker.analysis.AnalysisController;
+import kieker.analysis.IAnalysisController;
+import kieker.analysis.exception.AnalysisConfigurationException;
+import kieker.analysis.stage.EmptyPassOnFilter;
+import kieker.analysis.stage.ObjectProducer;
+import kieker.common.configuration.Configuration;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public class ThroughputAnalysis<T> {
+
+	private final IAnalysisController ac = new AnalysisController();
+	private int numNoopFilters;
+	private int numInputObjects;
+	private Callable<T> inputObjectCreator;
+
+	public void setNumNoopFilters(final int numNoopFilters) {
+		this.numNoopFilters = numNoopFilters;
+	}
+
+	public void setInput(final int numInputObjects, final Callable<T> inputObjectCreator) {
+		this.numInputObjects = numInputObjects;
+		this.inputObjectCreator = inputObjectCreator;
+	}
+
+	public void start() throws IllegalStateException, AnalysisConfigurationException {
+		this.ac.run();
+	}
+
+	public void init() throws IllegalStateException, AnalysisConfigurationException {
+		final Configuration producerConfig = new Configuration();
+		producerConfig.setProperty(ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(this.numInputObjects));
+		final ObjectProducer<T> producer = new ObjectProducer<T>(producerConfig, this.ac, this.inputObjectCreator);
+
+		EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), this.ac);
+		this.ac.connect(producer, ObjectProducer.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+		for (int idx = 0; idx < (this.numNoopFilters - 1); idx++) {
+			final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), this.ac);
+			this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+			predecessor = newPredecessor;
+		}
+	}
+
+}
diff --git a/src/main/java/kieker/analysis/examples/ThroughputTimestampAnalysis.java b/src/main/java/kieker/analysis/examples/ThroughputTimestampAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..483957ad91c928e269a19478dded2bcb52a9f72b
--- /dev/null
+++ b/src/main/java/kieker/analysis/examples/ThroughputTimestampAnalysis.java
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.examples;
+
+import java.util.Collection;
+import java.util.concurrent.Callable;
+
+import kieker.analysis.AnalysisController;
+import kieker.analysis.IAnalysisController;
+import kieker.analysis.exception.AnalysisConfigurationException;
+import kieker.analysis.stage.CollectorSink;
+import kieker.analysis.stage.EmptyPassOnFilter;
+import kieker.analysis.stage.ObjectProducer;
+import kieker.analysis.stage.StartTimestampFilter;
+import kieker.analysis.stage.StopTimestampFilter;
+import kieker.common.configuration.Configuration;
+import teetime.examples.throughput.TimestampObject;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public class ThroughputTimestampAnalysis {
+
+	private final IAnalysisController ac = new AnalysisController();
+	private int numNoopFilters;
+	private int numInputObjects;
+	private Callable<TimestampObject> inputObjectCreator;
+	private Collection<TimestampObject> timestampObjects;
+
+	public void setNumNoopFilters(final int numNoopFilters) {
+		this.numNoopFilters = numNoopFilters;
+	}
+
+	public void setInput(final int numInputObjects, final Callable<TimestampObject> inputObjectCreator) {
+		this.numInputObjects = numInputObjects;
+		this.inputObjectCreator = inputObjectCreator;
+	}
+
+	public void setTimestampObjects(final Collection<TimestampObject> timestampObjects) {
+		this.timestampObjects = timestampObjects;
+	}
+
+	public void start() throws IllegalStateException, AnalysisConfigurationException {
+		this.ac.run();
+	}
+
+	public void init() throws IllegalStateException, AnalysisConfigurationException {
+		final Configuration producerConfig = new Configuration();
+		producerConfig.setProperty(ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(this.numInputObjects));
+		final ObjectProducer<TimestampObject> producer = new ObjectProducer<TimestampObject>(producerConfig, this.ac, this.inputObjectCreator);
+
+		final StartTimestampFilter startTimestampFilter = new StartTimestampFilter(new Configuration(), this.ac);
+		EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), this.ac);
+		this.ac.connect(producer, ObjectProducer.OUTPUT_PORT_NAME, startTimestampFilter, StartTimestampFilter.INPUT_PORT_NAME);
+		this.ac.connect(startTimestampFilter, StartTimestampFilter.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+		for (int idx = 0; idx < (this.numNoopFilters - 1); idx++) {
+			final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), this.ac);
+			this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+			predecessor = newPredecessor;
+		}
+		final StopTimestampFilter stopTimestampFilter = new StopTimestampFilter(new Configuration(), this.ac);
+		final CollectorSink<TimestampObject> collectorSink = new CollectorSink<TimestampObject>(new Configuration(), this.ac, this.timestampObjects);
+
+		this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, stopTimestampFilter, StopTimestampFilter.INPUT_PORT_NAME);
+		this.ac.connect(stopTimestampFilter, StopTimestampFilter.OUTPUT_PORT_NAME, collectorSink, CollectorSink.INPUT_PORT_NAME);
+	}
+
+}
diff --git a/src/main/java/kieker/analysis/stage/CacheFilter.java b/src/main/java/kieker/analysis/stage/CacheFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d9d94b14533b0ca7475b1e61c495c9f6974227b
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/CacheFilter.java
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import kieker.analysis.IProjectContext;
+import kieker.analysis.plugin.annotation.InputPort;
+import kieker.analysis.plugin.annotation.OutputPort;
+import kieker.analysis.plugin.annotation.Plugin;
+import kieker.analysis.plugin.filter.AbstractFilterPlugin;
+import kieker.common.configuration.Configuration;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+@Plugin(outputPorts = @OutputPort(name = CacheFilter.OUTPUT_PORT_NAME))
+public class CacheFilter extends AbstractFilterPlugin {
+
+	public static final String INPUT_PORT_NAME = "input";
+	public static final String OUTPUT_PORT_NAME = "output";
+
+	private final List<Object> cache = new ArrayList<Object>();
+
+	public CacheFilter(final Configuration configuration,
+			final IProjectContext projectContext) {
+		super(configuration, projectContext);
+	}
+
+	@Override
+	public void terminate(final boolean error) {
+		for (final Object data : this.cache) {
+			super.deliver(EmptyPassOnFilter.OUTPUT_PORT_NAME, data);
+		}
+		super.terminate(error);
+	}
+
+	@Override
+	public Configuration getCurrentConfiguration() {
+		return new Configuration();
+	}
+
+	@InputPort(name = CacheFilter.INPUT_PORT_NAME)
+	public void input(final Object data) {
+		this.cache.add(data);
+	}
+
+}
diff --git a/src/main/java/kieker/analysis/stage/CollectorSink.java b/src/main/java/kieker/analysis/stage/CollectorSink.java
new file mode 100644
index 0000000000000000000000000000000000000000..279c471610d8de9cab7e9fb6f8c5ee64e7958b16
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/CollectorSink.java
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import java.util.Collection;
+
+import kieker.analysis.IProjectContext;
+import kieker.analysis.plugin.annotation.InputPort;
+import kieker.analysis.plugin.filter.AbstractFilterPlugin;
+import kieker.common.configuration.Configuration;
+
+public class CollectorSink<T> extends AbstractFilterPlugin {
+
+	public static final String INPUT_PORT_NAME = "input";
+
+	private static final int THRESHOLD = 10000;
+
+	private Collection<T> objects;
+
+	public CollectorSink(final Configuration configuration, final IProjectContext projectContext, final Collection<T> collection) {
+		super(configuration, projectContext);
+		this.objects = collection;
+	}
+
+	@InputPort(name = CollectorSink.INPUT_PORT_NAME)
+	public void execute(final T object) {
+		this.objects.add(object);
+		if ((this.objects.size() % THRESHOLD) == 0) {
+			System.out.println("size: " + this.objects.size());
+		}
+	}
+
+	public Collection<T> getObjects() {
+		return this.objects;
+	}
+
+	public void setObjects(final Collection<T> objects) {
+		this.objects = objects;
+	}
+
+	@Override
+	public Configuration getCurrentConfiguration() {
+		return new Configuration();
+	}
+
+}
diff --git a/src/main/java/kieker/analysis/stage/EmptyPassOnFilter.java b/src/main/java/kieker/analysis/stage/EmptyPassOnFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..f535034901ff9fed49c13b2e7e04ad122c2fdfbd
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/EmptyPassOnFilter.java
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import kieker.analysis.IProjectContext;
+import kieker.analysis.plugin.annotation.InputPort;
+import kieker.analysis.plugin.annotation.OutputPort;
+import kieker.analysis.plugin.annotation.Plugin;
+import kieker.analysis.plugin.filter.AbstractFilterPlugin;
+import kieker.common.configuration.Configuration;
+
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+@Plugin(outputPorts = @OutputPort(name = EmptyPassOnFilter.OUTPUT_PORT_NAME))
+public class EmptyPassOnFilter extends AbstractFilterPlugin {
+
+	public static final String INPUT_PORT_NAME = "input";
+	public static final String OUTPUT_PORT_NAME = "output";
+
+	public EmptyPassOnFilter(final Configuration configuration, final IProjectContext projectContext) {
+		super(configuration, projectContext);
+	}
+
+	@Override
+	public Configuration getCurrentConfiguration() {
+		return new Configuration();
+	}
+
+	@InputPort(name = EmptyPassOnFilter.INPUT_PORT_NAME)
+	public void input(final Object data) {
+		super.deliver(EmptyPassOnFilter.OUTPUT_PORT_NAME, data);
+	}
+
+}
diff --git a/src/main/java/kieker/analysis/stage/MappingFileParser.java b/src/main/java/kieker/analysis/stage/MappingFileParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..149c93f8d0b80f5c8f3cb9f9fe5f11a1d60f8cb8
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/MappingFileParser.java
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+
+import kieker.common.logging.Log;
+import kieker.common.util.filesystem.FSUtil;
+import teetime.stage.kieker.className.ClassNameRegistry;
+
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class MappingFileParser {
+
+	protected Log logger;
+
+	private static final Map<String, String> filePrefixRegistry = new HashMap<String, String>();
+
+	static {
+		filePrefixRegistry.put(FSUtil.MAP_FILENAME, FSUtil.FILE_PREFIX);
+		filePrefixRegistry.put(FSUtil.LEGACY_MAP_FILENAME, FSUtil.LEGACY_FILE_PREFIX);
+	}
+
+	public MappingFileParser(final Log logger) {
+		this.logger = logger;
+	}
+
+	public ClassNameRegistry parseFromStream(final InputStream inputStream) {
+		final ClassNameRegistry classNameRegistry = new ClassNameRegistry();
+
+		BufferedReader in = null;
+		try {
+			in = new BufferedReader(new InputStreamReader(inputStream, FSUtil.ENCODING));
+			String line;
+			while ((line = in.readLine()) != null) { // NOPMD (assign)
+				this.parseTextLine(line, classNameRegistry);
+			}
+		} catch (final IOException ex) {
+			this.logger.error("Error reading mapping file", ex);
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (final IOException ex) {
+					this.logger.error("Exception while closing input stream for mapping file", ex);
+				}
+			}
+		}
+
+		return classNameRegistry;
+	}
+
+	private void parseTextLine(final String line, final Map<Integer, String> stringRegistry) {
+		if (line.length() == 0) {
+			return; // ignore empty lines
+		}
+		final int split = line.indexOf('=');
+		if (split == -1) {
+			this.logger.error("Failed to find character '=' in line: {" + line + "}. It must consist of a ID=VALUE pair.");
+			return; // continue on errors
+		}
+		final String key = line.substring(0, split);
+		final String value = FSUtil.decodeNewline(line.substring(split + 1));
+		// the leading $ is optional
+		final Integer id;
+		try {
+			id = Integer.valueOf((key.charAt(0) == '$') ? key.substring(1) : key); // NOCS
+		} catch (final NumberFormatException ex) {
+			this.logger.error("Error reading mapping file, id must be integer", ex);
+			return; // continue on errors
+		}
+		final String prevVal = stringRegistry.put(id, value);
+		if (prevVal != null) {
+			this.logger.error("Found addional entry for id='" + id + "', old value was '" + prevVal + "' new value is '" + value + "'");
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public File findMappingFile(final File dirPath) {
+		File mappingFile = new File(dirPath, FSUtil.MAP_FILENAME);
+		if (!mappingFile.exists()) {
+			// No mapping file found. Check whether we find a legacy tpmon.map file!
+			mappingFile = new File(dirPath, FSUtil.LEGACY_MAP_FILENAME);
+			if (mappingFile.exists()) {
+				this.logger.info("Directory '" + dirPath + "' contains no file '" + FSUtil.MAP_FILENAME + "'. Found '" + FSUtil.LEGACY_MAP_FILENAME
+						+ "' ... switching to legacy mode");
+			} else {
+				// no {kieker|tpmon}.map exists. This is valid for very old monitoring logs. Hence, only dump a log.warn
+				this.logger.warn("No mapping file in directory '" + dirPath.getAbsolutePath() + "'");
+				return null;
+			}
+		}
+
+		return mappingFile;
+	}
+
+	/**
+	 * @return <code>null</code> if a file prefix for the given <code>mappingFile</code> is not registered.
+	 * @since 1.10
+	 */
+	public String getFilePrefixFromMappingFile(final File mappingFile) {
+		return MappingFileParser.filePrefixRegistry.get(mappingFile.getName());
+	}
+}
diff --git a/src/main/java/kieker/analysis/stage/ObjectProducer.java b/src/main/java/kieker/analysis/stage/ObjectProducer.java
new file mode 100644
index 0000000000000000000000000000000000000000..515ae0fc08070dbe8f63a86f8fb1908e10f3a2e3
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/ObjectProducer.java
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import java.util.concurrent.Callable;
+
+import kieker.analysis.IProjectContext;
+import kieker.analysis.plugin.annotation.OutputPort;
+import kieker.analysis.plugin.annotation.Plugin;
+import kieker.analysis.plugin.annotation.Property;
+import kieker.analysis.plugin.reader.AbstractReaderPlugin;
+import kieker.common.configuration.Configuration;
+
+
+@Plugin(
+		outputPorts = @OutputPort(name = ObjectProducer.OUTPUT_PORT_NAME),
+		configuration = @Property(name = ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, defaultValue = "100"))
+public class ObjectProducer<T> extends AbstractReaderPlugin {
+
+	public static final String CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE = "numObjectsToCreate";
+	public static final String OUTPUT_PORT_NAME = "output";
+
+	private Callable<T> objectCreator;
+	private long numObjectsToCreate;
+
+	public ObjectProducer(final Configuration configuration, final IProjectContext projectContext, final Callable<T> objectCreator) {
+		super(configuration, projectContext);
+
+		this.numObjectsToCreate = configuration.getLongProperty(CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE);
+		this.objectCreator = objectCreator;
+	}
+
+	public Callable<T> getObjectCreator() {
+		return this.objectCreator;
+	}
+
+	public void setObjectCreator(final Callable<T> objectCreator) {
+		this.objectCreator = objectCreator;
+	}
+
+	public long getNumObjectsToCreate() {
+		return this.numObjectsToCreate;
+	}
+
+	public void setNumObjectsToCreate(final long numObjectsToCreate) {
+		this.numObjectsToCreate = numObjectsToCreate;
+	}
+
+	@Override
+	public boolean read() {
+		for (int idx = 0; idx < this.numObjectsToCreate; idx++) {
+			try {
+				final T newObject = this.objectCreator.call();
+				super.deliver(OUTPUT_PORT_NAME, newObject);
+			} catch (final Exception e) {
+				throw new IllegalStateException();
+			}
+		}
+
+		return true;
+	}
+
+	@Override
+	public void terminate(final boolean error) {
+		// Nothing to do
+	}
+
+	@Override
+	public Configuration getCurrentConfiguration() {
+		final Configuration configuration = new Configuration();
+		configuration.setProperty(CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(this.numObjectsToCreate));
+		return configuration;
+	}
+
+}
diff --git a/src/main/java/kieker/analysis/stage/RecordFromBinaryFileCreator.java b/src/main/java/kieker/analysis/stage/RecordFromBinaryFileCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..2889e9328d1f288294f86e1be230dcbcc48d4d69
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/RecordFromBinaryFileCreator.java
@@ -0,0 +1,106 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+
+import kieker.common.exception.MonitoringRecordException;
+import kieker.common.logging.Log;
+import kieker.common.record.AbstractMonitoringRecord;
+import kieker.common.record.IMonitoringRecord;
+import teetime.stage.kieker.className.ClassNameRegistry;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class RecordFromBinaryFileCreator {
+
+	private final Log logger;
+	private final ClassNameRegistryRepository classNameRegistryRepository;
+
+	public RecordFromBinaryFileCreator(final Log logger, final ClassNameRegistryRepository classNameRegistryRepository) {
+		this.logger = logger;
+		this.classNameRegistryRepository = classNameRegistryRepository;
+	}
+
+	public IMonitoringRecord createRecordFromBinaryFile(final File binaryFile, final DataInputStream inputStream) throws IOException, MonitoringRecordException {
+		final ClassNameRegistry classNameRegistry = this.classNameRegistryRepository.get(binaryFile.getParentFile());
+
+		final Integer id;
+		try {
+			id = inputStream.readInt();
+		} catch (final EOFException eof) {
+			return null; // we are finished
+		}
+		final String classname = classNameRegistry.get(id);
+		if (classname == null) {
+			this.logger.error("Missing classname mapping for record type id " + "'" + id + "'");
+			return null; // we can't easily recover on errors
+		}
+
+		final Class<? extends IMonitoringRecord> clazz = AbstractMonitoringRecord.classForName(classname);
+		final Class<?>[] typeArray = AbstractMonitoringRecord.typesForClass(clazz);
+
+		// read record
+		final long loggingTimestamp = inputStream.readLong(); // NOPMD (must be read here!)
+		final Object[] objectArray = new Object[typeArray.length];
+		int idx = -1;
+		for (final Class<?> type : typeArray) {
+			idx++;
+			if (type == String.class) {
+				final Integer strId = inputStream.readInt();
+				final String str = classNameRegistry.get(strId);
+				if (str == null) {
+					this.logger.error("No String mapping found for id " + strId.toString());
+					objectArray[idx] = "";
+				} else {
+					objectArray[idx] = str;
+				}
+			} else if ((type == int.class) || (type == Integer.class)) {
+				objectArray[idx] = inputStream.readInt();
+			} else if ((type == long.class) || (type == Long.class)) {
+				objectArray[idx] = inputStream.readLong();
+			} else if ((type == float.class) || (type == Float.class)) {
+				objectArray[idx] = inputStream.readFloat();
+			} else if ((type == double.class) || (type == Double.class)) {
+				objectArray[idx] = inputStream.readDouble();
+			} else if ((type == byte.class) || (type == Byte.class)) {
+				objectArray[idx] = inputStream.readByte();
+			} else if ((type == short.class) || (type == Short.class)) { // NOPMD (short)
+				objectArray[idx] = inputStream.readShort();
+			} else if ((type == boolean.class) || (type == Boolean.class)) {
+				objectArray[idx] = inputStream.readBoolean();
+			} else {
+				if (inputStream.readByte() != 0) {
+					this.logger.error("Unexpected value for unsupported type: " + clazz.getName());
+					return null; // breaking error (break would not terminate the correct loop)
+				}
+				this.logger.warn("Unsupported type: " + clazz.getName());
+				objectArray[idx] = null;
+			}
+		}
+		final IMonitoringRecord record = AbstractMonitoringRecord.createFromArray(clazz, objectArray);
+		record.setLoggingTimestamp(loggingTimestamp);
+
+		return record;
+	}
+}
diff --git a/src/main/java/kieker/analysis/stage/RecordFromTextLineCreator.java b/src/main/java/kieker/analysis/stage/RecordFromTextLineCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..50a9db463d10ddfd43accd251f2914707b125fd0
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/RecordFromTextLineCreator.java
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import java.io.File;
+
+import kieker.common.exception.MonitoringRecordException;
+import kieker.common.record.AbstractMonitoringRecord;
+import kieker.common.record.IMonitoringRecord;
+import kieker.common.record.controlflow.OperationExecutionRecord;
+import teetime.stage.MappingException;
+import teetime.stage.kieker.className.ClassNameRegistry;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class RecordFromTextLineCreator {
+
+	private static final String CSV_SEPARATOR_CHARACTER = ";";
+
+	private static final IllegalRecordFormatException ILLEGAL_RECORD_FORMAT_EXCEPTION = new IllegalRecordFormatException();
+
+	private final ClassNameRegistryRepository classNameRegistryRepository;
+
+	public RecordFromTextLineCreator(final ClassNameRegistryRepository classNameRegistryRepository) {
+		this.classNameRegistryRepository = classNameRegistryRepository;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public IMonitoringRecord createRecordFromLine(final File textFile, final String line) throws MonitoringRecordException, IllegalRecordFormatException,
+			MappingException,
+			UnknownRecordTypeException {
+		final String[] recordFields = line.split(CSV_SEPARATOR_CHARACTER);
+
+		if (recordFields.length < 2) {
+			throw ILLEGAL_RECORD_FORMAT_EXCEPTION;
+		}
+
+		final boolean isModernRecord = recordFields[0].charAt(0) == '$';
+		if (isModernRecord) {
+			return this.createModernRecordFromRecordFields(textFile, recordFields);
+		} else {
+			return this.createLegacyRecordFromRecordFiels(recordFields);
+		}
+	}
+
+	private IMonitoringRecord createModernRecordFromRecordFields(final File textFile, final String[] recordFields) throws MonitoringRecordException,
+			MappingException,
+			UnknownRecordTypeException {
+		final ClassNameRegistry classNameRegistry = this.classNameRegistryRepository.get(textFile.getParentFile());
+		final Integer id = Integer.valueOf(recordFields[0].substring(1));
+		final String classname = classNameRegistry.get(id);
+		if (classname == null) {
+			throw new MappingException("Missing classname mapping for record type id " + "'" + id + "'");
+		}
+		final Class<? extends IMonitoringRecord> clazz = this.getClassByName(classname);
+		final long loggingTimestamp = Long.parseLong(recordFields[1]);
+		final int skipValues;
+		// check for Kieker < 1.6 OperationExecutionRecords
+		if ((recordFields.length == 11) && clazz.equals(OperationExecutionRecord.class)) {
+			skipValues = 3;
+		} else {
+			skipValues = 2;
+		}
+		// Java 1.5 compatibility
+		final String[] recordFieldsReduced = new String[recordFields.length - skipValues];
+		System.arraycopy(recordFields, skipValues, recordFieldsReduced, 0, recordFields.length - skipValues);
+		// in Java 1.6 this could be simplified to
+		// recordFieldsReduced = Arrays.copyOfRange(recordFields, skipValues, recordFields.length);
+
+		final IMonitoringRecord record = AbstractMonitoringRecord.createFromStringArray(clazz, recordFieldsReduced);
+		record.setLoggingTimestamp(loggingTimestamp);
+		return record;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private Class<? extends IMonitoringRecord> getClassByName(final String classname) throws MonitoringRecordException, UnknownRecordTypeException {
+		try {
+			return AbstractMonitoringRecord.classForName(classname);
+		} catch (final MonitoringRecordException ex) {
+			throw new UnknownRecordTypeException("Failed to load record type " + classname, classname, ex);
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private IMonitoringRecord createLegacyRecordFromRecordFiels(final String[] recordFields) throws MonitoringRecordException {
+		final String[] recordFieldsReduced = new String[recordFields.length - 1];
+		System.arraycopy(recordFields, 1, recordFieldsReduced, 0, recordFields.length - 1);
+		return AbstractMonitoringRecord.createFromStringArray(OperationExecutionRecord.class, recordFieldsReduced);
+	}
+}
diff --git a/src/main/java/kieker/analysis/stage/StartTimestampFilter.java b/src/main/java/kieker/analysis/stage/StartTimestampFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3b61b05e083378edc8d511512513a42c0417f9d
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/StartTimestampFilter.java
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import kieker.analysis.IProjectContext;
+import kieker.analysis.plugin.annotation.InputPort;
+import kieker.analysis.plugin.annotation.OutputPort;
+import kieker.analysis.plugin.annotation.Plugin;
+import kieker.analysis.plugin.filter.AbstractFilterPlugin;
+import kieker.common.configuration.Configuration;
+import teetime.examples.throughput.TimestampObject;
+
+@Plugin(outputPorts = @OutputPort(name = StartTimestampFilter.OUTPUT_PORT_NAME))
+public class StartTimestampFilter extends AbstractFilterPlugin {
+
+	public static final String INPUT_PORT_NAME = "input";
+	public static final String OUTPUT_PORT_NAME = "output";
+
+	public StartTimestampFilter(final Configuration configuration, final IProjectContext projectContext) {
+		super(configuration, projectContext);
+	}
+
+	@InputPort(name = StartTimestampFilter.INPUT_PORT_NAME)
+	public void execute(final TimestampObject inputObject) {
+		inputObject.setStartTimestamp(System.nanoTime());
+		super.deliver(StartTimestampFilter.OUTPUT_PORT_NAME, inputObject);
+	}
+
+	@Override
+	public Configuration getCurrentConfiguration() {
+		return new Configuration();
+	}
+
+}
diff --git a/src/main/java/kieker/analysis/stage/StopTimestampFilter.java b/src/main/java/kieker/analysis/stage/StopTimestampFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..eac2426d99ccbde189c77371366ad0f67bec26cd
--- /dev/null
+++ b/src/main/java/kieker/analysis/stage/StopTimestampFilter.java
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import kieker.analysis.IProjectContext;
+import kieker.analysis.plugin.annotation.InputPort;
+import kieker.analysis.plugin.annotation.OutputPort;
+import kieker.analysis.plugin.annotation.Plugin;
+import kieker.analysis.plugin.filter.AbstractFilterPlugin;
+import kieker.common.configuration.Configuration;
+import teetime.examples.throughput.TimestampObject;
+
+@Plugin(outputPorts = @OutputPort(name = StopTimestampFilter.OUTPUT_PORT_NAME))
+public class StopTimestampFilter extends AbstractFilterPlugin {
+
+	public static final String INPUT_PORT_NAME = "input";
+	public static final String OUTPUT_PORT_NAME = "output";
+
+	public StopTimestampFilter(final Configuration configuration, final IProjectContext projectContext) {
+		super(configuration, projectContext);
+	}
+
+	@InputPort(name = StopTimestampFilter.INPUT_PORT_NAME)
+	public void execute(final TimestampObject inputObject) {
+		inputObject.setStopTimestamp(System.nanoTime());
+		super.deliver(StartTimestampFilter.OUTPUT_PORT_NAME, inputObject);
+	}
+
+	@Override
+	public Configuration getCurrentConfiguration() {
+		return new Configuration();
+	}
+
+}
diff --git a/src/main/java/teetime/examples/countWords/ConcurrentCountWordsAnalysis.java b/src/main/java/teetime/examples/countWords/ConcurrentCountWordsAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfc813af151a2d90b022c5d34078d5b5b9c25a2e
--- /dev/null
+++ b/src/main/java/teetime/examples/countWords/ConcurrentCountWordsAnalysis.java
@@ -0,0 +1,341 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.examples.countWords;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import teetime.framework.concurrent.ConcurrentWorkStealingPipe;
+import teetime.framework.concurrent.ConcurrentWorkStealingPipeFactory;
+import teetime.framework.concurrent.SingleProducerSingleConsumerPipe;
+import teetime.framework.concurrent.StageTerminationPolicy;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.ISink;
+import teetime.framework.core.ISource;
+import teetime.framework.core.IStage;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.framework.sequential.QueuePipe;
+import teetime.stage.basic.RepeaterSource;
+import teetime.stage.basic.distributor.Distributor;
+import teetime.stage.basic.merger.Merger;
+import teetime.util.Pair;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ConcurrentCountWordsAnalysis extends Analysis {
+
+	private static final int NUM_TOKENS_TO_REPEAT = 1000;
+	private static final String START_DIRECTORY_NAME = ".";
+	private static final int SECONDS = 1000;
+
+	private static final int MAX_NUM_THREADS = 3;
+
+	private WorkerThread[] ioThreads;
+	private WorkerThread[] nonIoThreads;
+
+	ConcurrentWorkStealingPipeFactory<?>[] pipeFactories;
+
+	@Override
+	public void init() {
+		super.init();
+
+		this.ioThreads = new WorkerThread[2];
+
+		final IPipeline readerThreadPipeline = this.readerThreadPipeline();
+		@SuppressWarnings("unchecked")
+		final Distributor<File> distributor = (Distributor<File>) readerThreadPipeline.getStages().get(readerThreadPipeline.getStages().size() - 1);
+		this.ioThreads[0] = new WorkerThread(readerThreadPipeline, 1);
+		this.ioThreads[0].setName("startThread");
+		this.ioThreads[0].terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+
+		final IPipeline printingThreadPipeline = this.printingThreadPipeline();
+		@SuppressWarnings("unchecked")
+		final Merger<Pair<File, Integer>> merger = (Merger<Pair<File, Integer>>) printingThreadPipeline.getStages().get(0);
+		this.ioThreads[1] = new WorkerThread(printingThreadPipeline, 2);
+		this.ioThreads[1].setName("printingThread");
+		this.ioThreads[1].setTerminationPolicy(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+
+		this.createPipeFactories();
+
+		int numThreads = Runtime.getRuntime().availableProcessors();
+		numThreads = Math.min(MAX_NUM_THREADS, numThreads); // only for testing purposes
+
+		this.nonIoThreads = new WorkerThread[numThreads];
+		for (int i = 0; i < this.nonIoThreads.length; i++) {
+			final IPipeline pipeline = this.buildNonIoPipeline(distributor, merger);
+			this.nonIoThreads[i] = new WorkerThread(pipeline, 0);
+			this.nonIoThreads[i].setTerminationPolicy(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+		}
+	}
+
+	@Override
+	public void start() {
+		super.start();
+
+		for (final WorkerThread thread : this.ioThreads) {
+			thread.start();
+		}
+
+		for (final WorkerThread thread : this.nonIoThreads) {
+			thread.start();
+		}
+
+		System.out.println("Waiting for the non I/O worker threads to terminate..."); // NOPMD (Just for example purposes)
+		for (final WorkerThread thread : this.nonIoThreads) {
+			try {
+				thread.join(60 * SECONDS);
+			} catch (final InterruptedException e) {
+				throw new IllegalStateException();
+			}
+		}
+
+		System.out.println("Waiting for the I/O worker threads to terminate..."); // NOPMD (Just for example purposes)
+		for (final WorkerThread thread : this.ioThreads) {
+			try {
+				thread.join(60 * SECONDS);
+			} catch (final InterruptedException e) {
+				throw new IllegalStateException();
+			}
+		}
+
+		System.out.println("Analysis finished."); // NOPMD (Just for example purposes)
+	}
+
+	private void createPipeFactories() {
+		this.pipeFactories = new ConcurrentWorkStealingPipeFactory[4];
+		this.pipeFactories[0] = new ConcurrentWorkStealingPipeFactory<File>();
+		this.pipeFactories[1] = new ConcurrentWorkStealingPipeFactory<File>();
+		this.pipeFactories[2] = new ConcurrentWorkStealingPipeFactory<Pair<File, Integer>>();
+		this.pipeFactories[3] = new ConcurrentWorkStealingPipeFactory<Pair<File, Integer>>();
+	}
+
+	private IPipeline readerThreadPipeline() {
+		// create stages
+		final RepeaterSource<String> repeaterSource = RepeaterSource.create(START_DIRECTORY_NAME, NUM_TOKENS_TO_REPEAT);
+		repeaterSource.setAccessesDeviceId(1);
+		final DirectoryName2Files directoryName2Files = new DirectoryName2Files();
+		directoryName2Files.setAccessesDeviceId(1);
+		final Distributor<File> distributor = new Distributor<File>();
+		distributor.setAccessesDeviceId(1);
+
+		// add each stage to a stage list
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(repeaterSource);
+		stages.add(directoryName2Files);
+		stages.add(distributor);
+
+		// connect stages by pipes
+		QueuePipe.connect(repeaterSource.OUTPUT, directoryName2Files.DIRECTORY_NAME);
+		QueuePipe.connect(directoryName2Files.fileOutputPort, distributor.genericInputPort);
+
+		repeaterSource.START.setAssociatedPipe(new MethodCallPipe<Boolean>(Boolean.TRUE));
+
+		final IPipeline pipeline = new IPipeline() {
+			@SuppressWarnings("unchecked")
+			public List<? extends IStage> getStartStages() {
+				return Arrays.asList(repeaterSource);
+			}
+
+			public List<IStage> getStages() {
+				return stages;
+			}
+
+			public void fireStartNotification() throws Exception {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStarts();
+				}
+			}
+
+			public void fireStopNotification() {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStops();
+				}
+			}
+		};
+
+		return pipeline;
+	}
+
+	private IPipeline buildNonIoPipeline(final Distributor<File> readerDistributor, final Merger<Pair<File, Integer>> printingMerger) {
+		// create stages
+		final Distributor<File> distributor = new Distributor<File>();
+		final CountWordsStage countWordsStage0 = new CountWordsStage();
+		final CountWordsStage countWordsStage1 = new CountWordsStage();
+		final Merger<Pair<File, Integer>> merger = new Merger<Pair<File, Integer>>();
+
+		// add each stage to a stage list
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(distributor);
+		stages.add(countWordsStage0);
+		stages.add(countWordsStage1);
+		stages.add(merger);
+
+		// connect stages by pipes
+		SingleProducerSingleConsumerPipe.connect(readerDistributor.getNewOutputPort(), distributor.genericInputPort);
+
+		this.connectWithStealAwarePipe(this.pipeFactories[0], distributor.getNewOutputPort(), countWordsStage0.FILE);
+		this.connectWithStealAwarePipe(this.pipeFactories[1], distributor.getNewOutputPort(), countWordsStage1.FILE);
+		this.connectWithStealAwarePipe(this.pipeFactories[2], countWordsStage0.WORDSCOUNT, merger.getNewInputPort());
+		this.connectWithStealAwarePipe(this.pipeFactories[3], countWordsStage1.WORDSCOUNT, merger.getNewInputPort());
+
+		SingleProducerSingleConsumerPipe.connect(merger.outputPort, printingMerger.getNewInputPort());
+
+		final IPipeline pipeline = new IPipeline() {
+			@SuppressWarnings("unchecked")
+			public List<? extends IStage> getStartStages() {
+				return Arrays.asList(distributor);
+			}
+
+			public List<IStage> getStages() {
+				return stages;
+			}
+
+			public void fireStartNotification() throws Exception {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStarts();
+				}
+			}
+
+			public void fireStopNotification() {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStops();
+				}
+			}
+		};
+
+		return pipeline;
+	}
+
+	private IPipeline printingThreadPipeline() {
+		// create stages
+		final Merger<Pair<File, Integer>> merger = new Merger<Pair<File, Integer>>();
+		merger.setAccessesDeviceId(2);
+		final OutputWordsCountSink outputWordsCountStage = new OutputWordsCountSink();
+		outputWordsCountStage.setAccessesDeviceId(2);
+
+		// add each stage to a stage list
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(merger);
+		stages.add(outputWordsCountStage);
+
+		// connect stages by pipes
+		QueuePipe.connect(merger.outputPort, outputWordsCountStage.fileWordcountTupleInputPort);
+
+		final IPipeline pipeline = new IPipeline() {
+			@SuppressWarnings("unchecked")
+			public List<? extends IStage> getStartStages() {
+				return Arrays.asList(merger);
+			}
+
+			public List<IStage> getStages() {
+				return stages;
+			}
+
+			public void fireStartNotification() throws Exception {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStarts();
+				}
+			}
+
+			public void fireStopNotification() {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStops();
+				}
+			}
+		};
+
+		return pipeline;
+	}
+
+	private <A extends ISource, B extends ISink<B>, T> void connectWithStealAwarePipe(final ConcurrentWorkStealingPipeFactory<?> pipeFactory,
+			final IOutputPort<A, T> sourcePort, final IInputPort<B, T> targetPort) {
+		@SuppressWarnings("unchecked")
+		final ConcurrentWorkStealingPipe<T> pipe = (ConcurrentWorkStealingPipe<T>) pipeFactory.create();
+		pipe.setSourcePort(sourcePort);
+		pipe.setTargetPort(targetPort);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public static void main(final String[] args) {
+		final ConcurrentCountWordsAnalysis analysis = new ConcurrentCountWordsAnalysis();
+		analysis.init();
+		final long start = System.currentTimeMillis();
+		analysis.start();
+		final long end = System.currentTimeMillis();
+		// analysis.terminate();
+		final long duration = end - start;
+		System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes)
+
+		analysis.analyzeThreads();
+	}
+
+	private void analyzeThreads() {
+		long maxDuration = -1;
+		WorkerThread maxThread = null;
+
+		// FIXME resolve bug; see analysis results below;
+		//
+		// --- Thread[startThread,5,] ---
+		// {RepeaterSource: numPushedElements={numPushedElements=1000, numTakenElements=1}}
+		// {DirectoryName2Files: numPushedElements={numPushedElements=16000, numTakenElements=1000}}
+		// {Distributor: numPushedElements={numPushedElements=16000, numTakenElements=16000}}
+		// --- Thread[Thread-2,5,] ---
+		// {Distributor: numPushedElements={numPushedElements=16008, numTakenElements=16008}}
+		//
+		// cause: the non-io distributor is executed faster than the io distributor
+
+		for (final WorkerThread thread : this.ioThreads) {
+			System.out.println("--- " + thread + " ---"); // NOPMD (Just for example purposes)
+			for (final IStage stage : thread.getPipeline().getStages()) {
+				System.out.println(stage); // NOPMD (Just for example purposes)
+			}
+
+			final long durationInNs = thread.getDurationInNs();
+			System.out.println(thread + " takes " + TimeUnit.NANOSECONDS.toMillis(durationInNs) + " ms");
+		}
+
+		for (final WorkerThread thread : this.nonIoThreads) {
+			System.out.println("--- " + thread + " ---"); // NOPMD (Just for example purposes)
+			for (final IStage stage : thread.getPipeline().getStages()) {
+				System.out.println(stage); // NOPMD (Just for example purposes)
+			}
+
+			final long durationInNs = thread.getDurationInNs();
+			System.out.println(thread + " takes " + TimeUnit.NANOSECONDS.toMillis(durationInNs) + " ms");
+
+			if (durationInNs > maxDuration) {
+				maxDuration = durationInNs;
+				maxThread = thread;
+			}
+		}
+
+		System.out.println("maxThread: " + maxThread.toString() + " takes " + TimeUnit.NANOSECONDS.toMillis(maxDuration) + " ms"); // NOPMD (Just for example
+																																	// purposes)
+	}
+}
diff --git a/src/main/java/teetime/examples/countWords/CountWordsAnalysis.java b/src/main/java/teetime/examples/countWords/CountWordsAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..0da2e7dfd8155bbac58e37e840c29ca7c0f7f89e
--- /dev/null
+++ b/src/main/java/teetime/examples/countWords/CountWordsAnalysis.java
@@ -0,0 +1,150 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.examples.countWords;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.stage.basic.RepeaterSource;
+import teetime.stage.basic.distributor.Distributor;
+import teetime.stage.basic.merger.Merger;
+import teetime.util.Pair;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class CountWordsAnalysis extends Analysis {
+
+	private static final int NUM_TOKENS_TO_REPEAT = 1000;
+	private static final String START_DIRECTORY_NAME = ".";
+
+	private IPipeline pipeline;
+
+	@Override
+	public void init() {
+		super.init();
+
+		this.pipeline = this.buildNonIoPipeline();
+	}
+
+	@Override
+	public void start() {
+		super.start();
+		try {
+			this.pipeline.fireStartNotification();
+		} catch (final Exception e) {
+			e.printStackTrace();
+		}
+
+		this.pipeline.getStartStages().get(0).execute();
+
+		try {
+			this.pipeline.fireStopNotification();
+		} catch (final Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	private IPipeline buildNonIoPipeline() {
+		// create stages
+		final RepeaterSource<String> repeaterSource = RepeaterSource.create(START_DIRECTORY_NAME, NUM_TOKENS_TO_REPEAT);
+		final DirectoryName2Files findFilesStage = new DirectoryName2Files();
+		final Distributor<File> distributor = new Distributor<File>();
+		final CountWordsStage countWordsStage0 = new CountWordsStage();
+		final CountWordsStage countWordsStage1 = new CountWordsStage();
+		final Merger<Pair<File, Integer>> merger = new Merger<Pair<File, Integer>>();
+		final OutputWordsCountSink outputWordsCountStage = new OutputWordsCountSink();
+
+		// add each stage to a stage list
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(repeaterSource);
+		stages.add(findFilesStage);
+		stages.add(distributor);
+		stages.add(countWordsStage0);
+		stages.add(countWordsStage1);
+		stages.add(merger);
+		stages.add(outputWordsCountStage);
+
+		// connect stages by pipes
+		MethodCallPipe.connect(repeaterSource.OUTPUT, findFilesStage.DIRECTORY_NAME);
+		MethodCallPipe.connect(findFilesStage.fileOutputPort, distributor.genericInputPort);
+		MethodCallPipe.connect(distributor.getNewOutputPort(), countWordsStage0.FILE);
+		MethodCallPipe.connect(distributor.getNewOutputPort(), countWordsStage1.FILE);
+		MethodCallPipe.connect(countWordsStage0.WORDSCOUNT, merger.getNewInputPort());
+		MethodCallPipe.connect(countWordsStage1.WORDSCOUNT, merger.getNewInputPort());
+		MethodCallPipe.connect(merger.outputPort, outputWordsCountStage.fileWordcountTupleInputPort);
+
+		repeaterSource.START.setAssociatedPipe(new MethodCallPipe<Boolean>(Boolean.TRUE));
+
+		final IPipeline pipeline = new IPipeline() {
+			@SuppressWarnings("unchecked")
+			public List<? extends AbstractFilter<?>> getStartStages() {
+				return Arrays.asList(repeaterSource);
+			}
+
+			public List<IStage> getStages() {
+				return stages;
+			}
+
+			public void fireStartNotification() throws Exception {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStarts();
+				}
+			}
+
+			public void fireStopNotification() {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStops();
+				}
+			}
+		};
+
+		return pipeline;
+	}
+
+	public static void main(final String[] args) {
+		final CountWordsAnalysis analysis = new CountWordsAnalysis();
+		analysis.init();
+		final long start = System.currentTimeMillis();
+		analysis.start();
+		final long end = System.currentTimeMillis();
+		// analysis.terminate();
+		final long duration = end - start;
+		System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes)
+
+		for (final IStage stage : analysis.pipeline.getStages()) {
+			if (stage instanceof AbstractFilter<?>) {
+				System.out.println(stage.getClass().getName() + ": " + ((AbstractFilter<?>) stage).getOverallDurationInNs()); // NOPMD (Just for example purposes)
+			}
+		}
+
+		final DirectoryName2Files findFilesStage = (DirectoryName2Files) analysis.pipeline.getStages().get(1);
+		System.out.println("findFilesStage: " + findFilesStage.getNumFiles()); // NOPMD (Just for example purposes)
+
+		final OutputWordsCountSink outputWordsCountStage = (OutputWordsCountSink) analysis.pipeline.getStages().get(6);
+		System.out.println("outputWordsCountStage: " + outputWordsCountStage.getNumFiles()); // NOPMD (Just for example purposes)
+	}
+}
diff --git a/src/main/java/teetime/examples/countWords/CountWordsStage.java b/src/main/java/teetime/examples/countWords/CountWordsStage.java
new file mode 100644
index 0000000000000000000000000000000000000000..08839930c14ef69349eb192696c85ea1111083a9
--- /dev/null
+++ b/src/main/java/teetime/examples/countWords/CountWordsStage.java
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.examples.countWords;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.util.Pair;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class CountWordsStage extends AbstractFilter<CountWordsStage> {
+
+	public final IInputPort<CountWordsStage, File> FILE = this.createInputPort();
+
+	public final IOutputPort<CountWordsStage, Exception> EXCEPTION = this.createOutputPort();
+	public final IOutputPort<CountWordsStage, Pair<File, Integer>> WORDSCOUNT = this.createOutputPort();
+
+	private final Pattern pattern = Pattern.compile("[^\\p{Graph}]");
+
+	@Override
+	protected boolean execute(final Context<CountWordsStage> context) {
+		final File file = context.tryTake(this.FILE);
+		if (file == null) {
+			return false;
+		}
+
+		int wordsCount = 0;
+		try {
+			final BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
+			try {
+				String line;
+				while ((line = reader.readLine()) != null) {
+					line = line.trim();
+					if (line.length() > 0) {
+						final String[] words = this.pattern.split(line);
+						// System.out.println("" + Arrays.toString(words));
+						wordsCount += words.length;
+					}
+				}
+			} finally {
+				reader.close();
+			}
+			context.put(this.WORDSCOUNT, Pair.of(file, wordsCount));
+		} catch (final FileNotFoundException e) {
+			context.put(this.EXCEPTION, e);
+		} catch (final IOException e) {
+			context.put(this.EXCEPTION, e);
+		}
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/examples/countWords/DirectoryName2Files.java b/src/main/java/teetime/examples/countWords/DirectoryName2Files.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c99f80cb84fa0c63c704e87c3d1869d784203db
--- /dev/null
+++ b/src/main/java/teetime/examples/countWords/DirectoryName2Files.java
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.examples.countWords;
+
+import java.io.File;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class DirectoryName2Files extends AbstractFilter<DirectoryName2Files> {
+
+	public final IInputPort<DirectoryName2Files, String> DIRECTORY_NAME = this.createInputPort();
+
+	public final IOutputPort<DirectoryName2Files, File> fileOutputPort = this.createOutputPort();
+
+	private int numFiles = 0;
+
+	public DirectoryName2Files() {
+		// this.setAccessesDeviceId(1);
+	}
+
+	@Override
+	protected boolean execute(final Context<DirectoryName2Files> context) {
+		final String inputDir = context.tryTake(this.DIRECTORY_NAME);
+		if (inputDir == null) {
+			return false;
+		}
+
+		final File[] availableFiles = new File(inputDir).listFiles();
+		for (final File file : availableFiles) {
+			if (file.isFile()) {
+				// this.logger.info("Sending " + file);
+				context.put(this.fileOutputPort, file);
+				this.numFiles++;
+			}
+		}
+
+		return true;
+	}
+
+	public int getNumFiles() {
+		return this.numFiles;
+	}
+
+}
diff --git a/src/main/java/teetime/examples/countWords/OutputWordsCountSink.java b/src/main/java/teetime/examples/countWords/OutputWordsCountSink.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e9b54a5d1431210d54ca03a04b35b16135456c3
--- /dev/null
+++ b/src/main/java/teetime/examples/countWords/OutputWordsCountSink.java
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.examples.countWords;
+
+import java.io.File;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.util.Pair;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class OutputWordsCountSink extends AbstractFilter<OutputWordsCountSink> {
+
+	public final IInputPort<OutputWordsCountSink, Pair<File, Integer>> fileWordcountTupleInputPort = this.createInputPort();
+
+	private int numFiles = 0;
+
+	public OutputWordsCountSink() {
+		// this.setAccessesDeviceId(2);
+	}
+
+	@Override
+	protected boolean execute(final Context<OutputWordsCountSink> context) {
+		final Pair<File, Integer> pair = context.tryTake(this.fileWordcountTupleInputPort);
+		if (pair == null) {
+			return false;
+		}
+
+		// final File file = pair.getFirst();
+		// final Number wordsCount = pair.getSecond();
+		// System.out.println(wordsCount + " words in file '" + file.getAbsolutePath() + "'"); // NOPMD (Just for example purposes)
+		this.numFiles++;
+
+		return true;
+	}
+
+	/**
+	 * @since 1.10
+	 * @return
+	 */
+	public int getNumFiles() {
+		return this.numFiles;
+	}
+
+	@Override
+	public String toString() {
+		return super.toString() + ", numFiles = " + this.numFiles;
+	}
+
+}
diff --git a/src/main/java/teetime/examples/countWords/QueuedCountWordsAnalysis.java b/src/main/java/teetime/examples/countWords/QueuedCountWordsAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..d19e4a7a4d05f534a5a11bcc690eee2a6d108174
--- /dev/null
+++ b/src/main/java/teetime/examples/countWords/QueuedCountWordsAnalysis.java
@@ -0,0 +1,158 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.examples.countWords;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.framework.concurrent.StageTerminationPolicy;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.framework.sequential.QueuePipe;
+import teetime.stage.basic.RepeaterSource;
+import teetime.stage.basic.distributor.Distributor;
+import teetime.stage.basic.merger.Merger;
+import teetime.util.Pair;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class QueuedCountWordsAnalysis extends Analysis {
+
+	private static final int NUM_TOKENS_TO_REPEAT = 1000;
+	private static final String START_DIRECTORY_NAME = ".";
+	private static final int SECONDS = 1000;
+
+	private WorkerThread workerThread;
+
+	@Override
+	public void init() {
+		super.init();
+
+		final IPipeline pipeline = this.buildNonIoPipeline();
+
+		this.workerThread = new WorkerThread(pipeline, 0);
+	}
+
+	@Override
+	public void start() {
+		super.start();
+
+		this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+
+		this.workerThread.start();
+		try {
+			this.workerThread.join(60 * SECONDS);
+		} catch (final InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private IPipeline buildNonIoPipeline() {
+		// create stages
+		final RepeaterSource<String> repeaterSource = RepeaterSource.create(START_DIRECTORY_NAME, NUM_TOKENS_TO_REPEAT);
+		final DirectoryName2Files findFilesStage = new DirectoryName2Files();
+		final Distributor<File> distributor = new Distributor<File>();
+		final CountWordsStage countWordsStage0 = new CountWordsStage();
+		final CountWordsStage countWordsStage1 = new CountWordsStage();
+		final Merger<Pair<File, Integer>> merger = new Merger<Pair<File, Integer>>();
+		final OutputWordsCountSink outputWordsCountStage = new OutputWordsCountSink();
+
+		// add each stage to a stage list
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(repeaterSource);
+		stages.add(findFilesStage);
+		stages.add(distributor);
+		stages.add(countWordsStage0);
+		stages.add(countWordsStage1);
+		stages.add(merger);
+		stages.add(outputWordsCountStage);
+
+		// connect stages by pipes
+		QueuePipe.connect(repeaterSource.OUTPUT, findFilesStage.DIRECTORY_NAME);
+		QueuePipe.connect(findFilesStage.fileOutputPort, distributor.genericInputPort);
+		QueuePipe.connect(distributor.getNewOutputPort(), countWordsStage0.FILE);
+		QueuePipe.connect(distributor.getNewOutputPort(), countWordsStage1.FILE);
+		QueuePipe.connect(countWordsStage0.WORDSCOUNT, merger.getNewInputPort());
+		QueuePipe.connect(countWordsStage1.WORDSCOUNT, merger.getNewInputPort());
+		QueuePipe.connect(merger.outputPort, outputWordsCountStage.fileWordcountTupleInputPort);
+
+		repeaterSource.START.setAssociatedPipe(new MethodCallPipe<Boolean>(Boolean.TRUE));
+
+		final IPipeline pipeline = new IPipeline() {
+			@SuppressWarnings("unchecked")
+			public List<? extends IStage> getStartStages() {
+				return Arrays.asList(repeaterSource);
+			}
+
+			public List<IStage> getStages() {
+				return stages;
+			}
+
+			public void fireStartNotification() throws Exception {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStarts();
+				}
+			}
+
+			public void fireStopNotification() {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStops();
+				}
+			}
+		};
+
+		return pipeline;
+	}
+
+	WorkerThread getWorkerThread() {
+		return this.workerThread;
+	}
+
+	public static void main(final String[] args) {
+		final QueuedCountWordsAnalysis analysis = new QueuedCountWordsAnalysis();
+		analysis.init();
+		final long start = System.currentTimeMillis();
+		analysis.start();
+		final long end = System.currentTimeMillis();
+		// analysis.terminate();
+		final long duration = end - start;
+		System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes)
+
+		final IPipeline pipeline = analysis.workerThread.getPipeline();
+
+		for (final IStage stage : pipeline.getStages()) {
+			if (stage instanceof AbstractFilter<?>) {
+				System.out.println(stage.getClass().getName() + ": " + ((AbstractFilter<?>) stage).getOverallDurationInNs()); // NOPMD (Just for example purposes)
+			}
+		}
+
+		final DirectoryName2Files findFilesStage = (DirectoryName2Files) pipeline.getStages().get(1);
+		System.out.println("findFilesStage: " + findFilesStage.getNumFiles()); // NOPMD (Just for example purposes)
+
+		final OutputWordsCountSink outputWordsCountStage = (OutputWordsCountSink) pipeline.getStages().get(6);
+		System.out.println("outputWordsCountStage: " + outputWordsCountStage.getNumFiles()); // NOPMD (Just for example purposes)
+	}
+}
diff --git a/src/main/java/teetime/examples/countingObjects/CountingObjectsAnalysis.java b/src/main/java/teetime/examples/countingObjects/CountingObjectsAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..c286a540eac7522d19f89e33a304e1a965bb3d03
--- /dev/null
+++ b/src/main/java/teetime/examples/countingObjects/CountingObjectsAnalysis.java
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.examples.countingObjects;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.examples.countWords.DirectoryName2Files;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.stage.TypeLoggerFilter;
+import teetime.stage.basic.RepeaterSource;
+import teetime.stage.composite.CycledCountingFilter;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class CountingObjectsAnalysis extends Analysis {
+
+	private IPipeline pipeline;
+
+	@Override
+	public void init() {
+		super.init();
+
+		this.pipeline = this.buildNonIoPipeline();
+	}
+
+	private IPipeline buildNonIoPipeline() {
+		// create stages
+		final RepeaterSource<String> repeaterSource = RepeaterSource.create(".", 1);
+		final DirectoryName2Files findFilesStage = new DirectoryName2Files();
+		final CycledCountingFilter<File> cycledCountingFilter = CycledCountingFilter.create(new MethodCallPipe<Long>(0L));
+		final TypeLoggerFilter<File> typeLoggerFilter = TypeLoggerFilter.create();
+
+		// add each stage to a stage list
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(repeaterSource);
+		stages.add(findFilesStage);
+		stages.add(cycledCountingFilter);
+		stages.add(typeLoggerFilter);
+
+		// connect stages by pipes
+		MethodCallPipe.connect(repeaterSource.OUTPUT, findFilesStage.DIRECTORY_NAME);
+		MethodCallPipe.connect(findFilesStage.fileOutputPort, cycledCountingFilter.INPUT_OBJECT);
+		MethodCallPipe.connect(cycledCountingFilter.RELAYED_OBJECT, typeLoggerFilter.INPUT_OBJECT);
+
+		repeaterSource.START.setAssociatedPipe(new MethodCallPipe<Boolean>(Boolean.TRUE));
+
+		final IPipeline pipeline = new IPipeline() {
+			@SuppressWarnings("unchecked")
+			public List<? extends IStage> getStartStages() {
+				return Arrays.asList(repeaterSource);
+			}
+
+			public List<IStage> getStages() {
+				return stages;
+			}
+
+			public void fireStartNotification() throws Exception {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStarts();
+				}
+			}
+
+			public void fireStopNotification() {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStops();
+				}
+			}
+		};
+		return pipeline;
+	}
+
+	@Override
+	public void start() {
+		super.start();
+		try {
+			this.pipeline.fireStartNotification();
+		} catch (final Exception e) {
+			e.printStackTrace();
+		}
+
+		this.pipeline.getStartStages().get(0).execute();
+
+		try {
+			this.pipeline.fireStopNotification();
+		} catch (final Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 * @param args
+	 */
+	public static void main(final String[] args) {
+		final CountingObjectsAnalysis analysis = new CountingObjectsAnalysis();
+		analysis.init();
+		final long start = System.currentTimeMillis();
+		analysis.start();
+		final long end = System.currentTimeMillis();
+		// analysis.terminate();
+		final long duration = end - start;
+		System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes)
+
+		for (final IStage stage : analysis.pipeline.getStages()) {
+			if (stage instanceof AbstractFilter<?>) {
+				System.out.println(stage.getClass().getName() + ": " + ((AbstractFilter<?>) stage).getOverallDurationInNs()); // NOPMD (Just for example purposes)
+			}
+		}
+
+		@SuppressWarnings("unchecked")
+		final CycledCountingFilter<File> cycledCountingFilter = (CycledCountingFilter<File>) analysis.pipeline.getStages().get(2);
+		System.out.println("count: " + cycledCountingFilter.getCurrentCount()); // NOPMD (Just for example purposes)
+	}
+}
diff --git a/src/main/java/teetime/examples/recordReader/RecordReaderAnalysis.java b/src/main/java/teetime/examples/recordReader/RecordReaderAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfadf9c89e4e86c3d56915950d315fd728b080ba
--- /dev/null
+++ b/src/main/java/teetime/examples/recordReader/RecordReaderAnalysis.java
@@ -0,0 +1,168 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.recordReader;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+import kieker.common.record.IMonitoringRecord;
+import teetime.framework.concurrent.StageTerminationPolicy;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.IPipe;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.ISink;
+import teetime.framework.core.ISource;
+import teetime.framework.core.IStage;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.framework.sequential.QueuePipe;
+import teetime.framework.util.BaseStage2StageExtractor;
+import teetime.stage.CollectorSink;
+import teetime.stage.kieker.File2RecordFilter;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class RecordReaderAnalysis extends Analysis {
+
+	private static final int SECONDS = 1000;
+
+	private WorkerThread workerThread;
+
+	private File2RecordFilter file2RecordFilter;
+	private CollectorSink<IMonitoringRecord> collector;
+
+	private ClassNameRegistryRepository classNameRegistryRepository;
+
+	@Override
+	public void init() {
+		super.init();
+		final IPipeline pipeline = this.buildPipeline();
+		this.workerThread = new WorkerThread(pipeline, 0);
+	}
+
+	@Override
+	public void start() {
+		super.start();
+
+		this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+
+		this.workerThread.start();
+		try {
+			this.workerThread.join(60 * SECONDS);
+		} catch (final InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+
+	void setInputFile(final File file) {
+		this.file2RecordFilter.fileInputPort.setAssociatedPipe(new MethodCallPipe<File>(file));
+	}
+
+	private IPipeline buildPipeline() {
+		final BaseStage2StageExtractor baseStage2StageExtractor = new BaseStage2StageExtractor();
+
+		this.classNameRegistryRepository = new ClassNameRegistryRepository();
+		// create stages
+		this.file2RecordFilter = new File2RecordFilter(this.classNameRegistryRepository);
+		this.collector = new CollectorSink<IMonitoringRecord>();
+
+		// add each stage to a stage list
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.addAll(baseStage2StageExtractor.extract(this.file2RecordFilter));
+		stages.add(this.collector);
+
+		// connect stages by pipes
+		final List<IPipe<?>> pipes = new LinkedList<IPipe<?>>();
+		pipes.add(this.connectWithSequentialPipe(this.file2RecordFilter.recordOutputPort, this.collector.objectInputPort));
+
+		final IPipeline pipeline = new IPipeline() {
+			@Override
+			@SuppressWarnings("unchecked")
+			public List<? extends IStage> getStartStages() {
+				return baseStage2StageExtractor.extract(RecordReaderAnalysis.this.file2RecordFilter);
+			}
+
+			@Override
+			public List<IStage> getStages() {
+				return stages;
+			}
+
+			@Override
+			public void fireStartNotification() throws Exception {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStarts();
+				}
+			}
+
+			@Override
+			public void fireStopNotification() {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStops();
+				}
+			}
+		};
+
+		return pipeline;
+	}
+
+	private <A extends ISource, B extends ISink<B>, T> IPipe<T> connectWithSequentialPipe(final IOutputPort<A, T> sourcePort,
+			final IInputPort<B, T> targetPort) {
+		final IPipe<T> pipe = new QueuePipe<T>();
+		pipe.setSourcePort(sourcePort);
+		pipe.setTargetPort(targetPort);
+		return pipe;
+	}
+
+	WorkerThread getWorkerThread() {
+		return this.workerThread;
+	}
+
+	public static void main(final String[] args) {
+		final RecordReaderAnalysis analysis = new RecordReaderAnalysis();
+		analysis.init();
+		final long start = System.currentTimeMillis();
+		analysis.start();
+		final long end = System.currentTimeMillis();
+		// analysis.terminate();
+		final long duration = end - start;
+		System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes)
+
+		final IPipeline pipeline = analysis.workerThread.getPipeline();
+
+		for (final IStage stage : pipeline.getStages()) {
+			if (stage instanceof AbstractFilter<?>) {
+				System.out.println(stage.getClass().getName() + ": " + ((AbstractFilter<?>) stage).getOverallDurationInNs()); // NOPMD (Just for example purposes)
+			}
+		}
+	}
+
+	void setOutputRecordList(final List<IMonitoringRecord> records) {
+		this.collector.setObjects(records);
+	}
+
+	public ClassNameRegistryRepository getClassNameRegistryRepository() {
+		return this.classNameRegistryRepository;
+	}
+
+}
diff --git a/src/main/java/teetime/examples/throughput/ThroughputAnalysis.java b/src/main/java/teetime/examples/throughput/ThroughputAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a87b53ba4c8598938915d5ea196c3d0f64de67a
--- /dev/null
+++ b/src/main/java/teetime/examples/throughput/ThroughputAnalysis.java
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.throughput;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import teetime.framework.concurrent.StageTerminationPolicy;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.framework.core.Pipeline;
+import teetime.framework.sequential.QueuePipe;
+import teetime.stage.NoopFilter;
+import teetime.stage.basic.ObjectProducer;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ThroughputAnalysis<T> extends Analysis {
+
+	private static final int SECONDS = 1000;
+
+	private WorkerThread workerThread;
+
+	private int numNoopFilters;
+
+	private int numInputObjects;
+
+	private Callable<T> inputObjectCreator;
+
+	@Override
+	public void init() {
+		super.init();
+		final IPipeline pipeline = this.buildPipeline(this.numNoopFilters);
+
+		this.workerThread = new WorkerThread(pipeline, 0);
+		this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+	}
+
+	/**
+	 * @param numNoopFilters
+	 * @since 1.10
+	 */
+	private IPipeline buildPipeline(final int numNoopFilters) {
+		@SuppressWarnings("unchecked")
+		final NoopFilter<T>[] noopFilters = new NoopFilter[numNoopFilters];
+		// create stages
+		final ObjectProducer<T> objectProducer = new ObjectProducer<T>(this.numInputObjects, this.inputObjectCreator);
+		for (int i = 0; i < noopFilters.length; i++) {
+			noopFilters[i] = new NoopFilter<T>();
+		}
+
+		// add each stage to a stage list
+		final List<IStage> startStages = new LinkedList<IStage>();
+		startStages.add(objectProducer);
+
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(objectProducer);
+		stages.addAll(Arrays.asList(noopFilters));
+
+		// connect stages by pipes
+		QueuePipe.connect(objectProducer.outputPort, noopFilters[0].inputPort);
+		for (int i = 1; i < noopFilters.length; i++) {
+			QueuePipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort);
+		}
+
+		final Pipeline pipeline = new Pipeline();
+		pipeline.setStartStages(startStages);
+		pipeline.setStages(stages);
+		return pipeline;
+	}
+
+	@Override
+	public void start() {
+		super.start();
+
+		this.workerThread.start();
+		try {
+			this.workerThread.join(60 * SECONDS);
+		} catch (final InterruptedException e) {
+			e.printStackTrace();
+		}
+
+		System.out.println("SchedulingOverhead: " + TimeUnit.NANOSECONDS.toMillis(this.workerThread.computeSchedulingOverheadInNs()) + " ms");
+	}
+
+	public int getNumNoopFilters() {
+		return this.numNoopFilters;
+	}
+
+	public void setNumNoopFilters(final int numNoopFilters) {
+		this.numNoopFilters = numNoopFilters;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void setInput(final int numInputObjects, final Callable<T> inputObjectCreator) {
+		this.numInputObjects = numInputObjects;
+		this.inputObjectCreator = inputObjectCreator;
+	}
+}
diff --git a/src/main/java/teetime/examples/throughput/ThroughputTimestampAnalysis.java b/src/main/java/teetime/examples/throughput/ThroughputTimestampAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..178d35f61feb78a39ac2f5860eea88af2d28cdc3
--- /dev/null
+++ b/src/main/java/teetime/examples/throughput/ThroughputTimestampAnalysis.java
@@ -0,0 +1,173 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.throughput;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import teetime.framework.concurrent.StageTerminationPolicy;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.framework.core.Pipeline;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.framework.sequential.QueuePipe;
+import teetime.stage.CollectorSink;
+import teetime.stage.NoopFilter;
+import teetime.stage.StartTimestampFilter;
+import teetime.stage.StopTimestampFilter;
+import teetime.stage.basic.ObjectProducer;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ThroughputTimestampAnalysis extends Analysis {
+
+	private static final int SECONDS = 1000;
+
+	private WorkerThread workerThread;
+
+	private int numNoopFilters;
+
+	private int numInputObjects;
+
+	private Callable<TimestampObject> inputObjectCreator;
+
+	private Collection<TimestampObject> timestampObjects;
+
+	private boolean shouldUseQueue;
+
+	@Override
+	public void init() {
+		super.init();
+		final IPipeline pipeline = this.buildPipeline(this.numNoopFilters);
+
+		this.workerThread = new WorkerThread(pipeline, 0);
+		this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+	}
+
+	/**
+	 * @param numNoopFilters
+	 * @since 1.10
+	 */
+	private IPipeline buildPipeline(final int numNoopFilters) {
+		@SuppressWarnings("unchecked")
+		final NoopFilter<TimestampObject>[] noopFilters = new NoopFilter[numNoopFilters];
+		// create stages
+		final ObjectProducer<TimestampObject> objectProducer = new ObjectProducer<TimestampObject>(this.numInputObjects, this.inputObjectCreator);
+		final StartTimestampFilter startTimestampFilter = new StartTimestampFilter();
+		for (int i = 0; i < noopFilters.length; i++) {
+			noopFilters[i] = new NoopFilter<TimestampObject>();
+		}
+		final StopTimestampFilter stopTimestampFilter = new StopTimestampFilter();
+		final CollectorSink<TimestampObject> collectorSink = new CollectorSink<TimestampObject>(this.timestampObjects);
+
+		// add each stage to a stage list
+		final List<IStage> startStages = new LinkedList<IStage>();
+		startStages.add(objectProducer);
+
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(objectProducer);
+		if (this.shouldUseQueue) {
+			stages.add(startTimestampFilter);
+			stages.addAll(Arrays.asList(noopFilters));
+			stages.add(stopTimestampFilter);
+			stages.add(collectorSink);
+
+			// connect stages by pipes
+			QueuePipe.connect(objectProducer.outputPort, startTimestampFilter.inputPort);
+			QueuePipe.connect(startTimestampFilter.outputPort, noopFilters[0].inputPort);
+			for (int i = 1; i < noopFilters.length; i++) {
+				QueuePipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort);
+			}
+			QueuePipe.connect(noopFilters[noopFilters.length - 1].outputPort, stopTimestampFilter.inputPort);
+			QueuePipe.connect(stopTimestampFilter.outputPort, collectorSink.objectInputPort);
+		} else {
+			// connect stages by pipes
+			MethodCallPipe.connect(objectProducer.outputPort, startTimestampFilter.inputPort);
+			MethodCallPipe.connect(startTimestampFilter.outputPort, noopFilters[0].inputPort);
+			for (int i = 1; i < noopFilters.length; i++) {
+				MethodCallPipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort);
+			}
+			MethodCallPipe.connect(noopFilters[noopFilters.length - 1].outputPort, stopTimestampFilter.inputPort);
+			MethodCallPipe.connect(stopTimestampFilter.outputPort, collectorSink.objectInputPort);
+		}
+
+		final Pipeline pipeline = new Pipeline();
+		pipeline.setStartStages(startStages);
+		pipeline.setStages(stages);
+		return pipeline;
+	}
+
+	@Override
+	public void start() {
+		super.start();
+
+		this.workerThread.start();
+		try {
+			this.workerThread.join(60 * SECONDS);
+		} catch (final InterruptedException e) {
+			e.printStackTrace();
+		}
+
+		final long schedulingOverheadInNs = this.workerThread.computeSchedulingOverheadInNs();
+		final int size = this.workerThread.getSchedulingOverheadsInNs().size();
+		System.out.println("scheduling overhead times: " + size);
+		System.out.println("SchedulingOverhead: " + TimeUnit.NANOSECONDS.toMillis(schedulingOverheadInNs) + " ms");
+		System.out.println("avg overhead of iteration: "
+				+ TimeUnit.NANOSECONDS.toMillis(schedulingOverheadInNs / (size / 2)) + " ms");
+		System.out.println("ExecutedUnsuccessfullyCount: " + this.workerThread.getExecutedUnsuccessfullyCount());
+	}
+
+	public int getNumNoopFilters() {
+		return this.numNoopFilters;
+	}
+
+	public void setNumNoopFilters(final int numNoopFilters) {
+		this.numNoopFilters = numNoopFilters;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void setInput(final int numInputObjects, final Callable<TimestampObject> inputObjectCreator) {
+		this.numInputObjects = numInputObjects;
+		this.inputObjectCreator = inputObjectCreator;
+	}
+
+	public Collection<TimestampObject> getTimestampObjects() {
+		return this.timestampObjects;
+	}
+
+	public void setTimestampObjects(final Collection<TimestampObject> timestampObjects) {
+		this.timestampObjects = timestampObjects;
+	}
+
+	public boolean isShouldUseQueue() {
+		return this.shouldUseQueue;
+	}
+
+	public void setShouldUseQueue(final boolean shouldUseQueue) {
+		this.shouldUseQueue = shouldUseQueue;
+	}
+}
diff --git a/src/main/java/teetime/examples/throughput/TimestampObject.java b/src/main/java/teetime/examples/throughput/TimestampObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..9aa937919ec8199879f9c92ae806b0ab7b60730d
--- /dev/null
+++ b/src/main/java/teetime/examples/throughput/TimestampObject.java
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.throughput;
+
+/**
+ * Object for performance evaluation
+ * 
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public final class TimestampObject {
+
+	@SuppressWarnings("PMD.CommentRequired")
+	private long startTimestamp;
+
+	@SuppressWarnings("PMD.CommentRequired")
+	private long stopTimestamp;
+
+	@SuppressWarnings("PMD.CommentRequired")
+	public long getStartTimestamp() {
+		return this.startTimestamp;
+	}
+
+	@SuppressWarnings("PMD.CommentRequired")
+	public void setStartTimestamp(final long startTimestamp) {
+		this.startTimestamp = startTimestamp;
+	}
+
+	@SuppressWarnings("PMD.CommentRequired")
+	public long getStopTimestamp() {
+		return this.stopTimestamp;
+	}
+
+	@SuppressWarnings("PMD.CommentRequired")
+	public void setStopTimestamp(final long stopTimestamp) {
+		this.stopTimestamp = stopTimestamp;
+	}
+}
diff --git a/src/main/java/teetime/examples/traceReconstruction/TraceAnalysis.java b/src/main/java/teetime/examples/traceReconstruction/TraceAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..29ce10a0aa82b1d8b85133f1c215425512b9d10b
--- /dev/null
+++ b/src/main/java/teetime/examples/traceReconstruction/TraceAnalysis.java
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.traceReconstruction;
+
+import kieker.common.util.filesystem.FSUtil;
+import teetime.framework.core.Analysis;
+import teetime.stage.predicate.FileExtensionPredicate;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class TraceAnalysis extends Analysis {
+
+	public static void main(final String[] args) {
+		// FSReader reader
+		// StringBufferFilter stringBufferFilter
+		// TimestampFilter timestampFilter
+		// TraceIdFilter traceIdFilter
+		// ExecutionRecordTransformationFilter execRecTransformer
+		// TraceReconstructionFilter mtReconstrFilter
+		// EventRecordTraceReconstructionFilter eventTraceReconstructionFilter
+		// EventRecordTraceCounter eventRecordTraceCounter
+		// TraceEventRecords2ExecutionAndMessageTraceFilter traceEvents2ExecutionAndMessageTraceFilter
+
+		new FileExtensionPredicate(FSUtil.NORMAL_FILE_EXTENSION);
+
+		// reader -> stringBufferFilter
+
+		// stringBufferFilter -> timestampFilter.INPUT_PORT_NAME_EXECUTION
+		// stringBufferFilter -> timestampFilter.INPUT_PORT_NAME_FLOW
+		// timestampFilter -> traceIdFilter
+
+		// traceIdFilter -> execRecTransformer
+		// execRecTransformer -> SYSTEM_ENTITY_FACTORY
+
+		// mtReconstrFilter -> SYSTEM_ENTITY_FACTORY
+		// execRecTransformer -> mtReconstrFilter
+
+		// traceIdFilter -> eventTraceReconstructionFilter
+		// eventTraceReconstructionFilter -> eventRecordTraceCounter.INPUT_PORT_NAME_VALID
+		// eventTraceReconstructionFilter -> eventRecordTraceCounter.INPUT_PORT_NAME_INVALID
+
+		// eventTraceReconstructionFilter -> traceEvents2ExecutionAndMessageTraceFilter
+		// traceEvents2ExecutionAndMessageTraceFilter -> SYSTEM_ENTITY_FACTORY
+	}
+
+	@Override
+	public void init() {
+		// TODO
+	}
+}
diff --git a/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis.java b/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..73381658d13dce8f63235046687ee272e1854b8b
--- /dev/null
+++ b/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis.java
@@ -0,0 +1,108 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.traceReconstruction;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.framework.concurrent.StageTerminationPolicy;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.stage.FileExtensionFilter;
+import teetime.stage.io.File2TextLinesFilter;
+import teetime.stage.kieker.MonitoringLogDirectory2Files;
+import teetime.stage.kieker.className.ClassNameRegistryCreationFilter;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+import teetime.stage.kieker.fileToRecord.textLine.TextLine2RecordFilter;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class TraceReconstructionAnalysis extends Analysis {
+	private static final int SECONDS = 1000;
+
+	private WorkerThread workerThread;
+
+	private ClassNameRegistryRepository classNameRegistryRepository;
+
+	@Override
+	public void init() {
+		super.init();
+		final IPipeline pipeline = this.buildPipeline();
+		this.workerThread = new WorkerThread(pipeline, 0);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private IPipeline buildPipeline() {
+		final ClassNameRegistryCreationFilter classNameRegistryCreationFilter = new ClassNameRegistryCreationFilter(this.classNameRegistryRepository);
+		final MonitoringLogDirectory2Files directory2FilesFilter = new MonitoringLogDirectory2Files();
+		final FileExtensionFilter fileExtensionFilter = new FileExtensionFilter();
+		final File2TextLinesFilter file2TextLinesFilter = new File2TextLinesFilter();
+		final TextLine2RecordFilter textLine2RecordFilter = new TextLine2RecordFilter(this.classNameRegistryRepository);
+
+		// TODO Auto-generated method stub
+
+		// add each stage to a stage list
+		final LinkedList<IStage> startStages = new LinkedList<IStage>();
+
+		final List<IStage> stages = new LinkedList<IStage>();
+
+		final IPipeline pipeline = new IPipeline() {
+			@SuppressWarnings("unchecked")
+			public List<? extends IStage> getStartStages() {
+				return startStages;
+			}
+
+			public List<IStage> getStages() {
+				return stages;
+			}
+
+			public void fireStartNotification() throws Exception {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStarts();
+				}
+			}
+
+			public void fireStopNotification() {
+				for (final IStage stage : this.getStartStages()) {
+					stage.notifyPipelineStops();
+				}
+			}
+		};
+
+		return pipeline;
+	}
+
+	@Override
+	public void start() {
+		super.start();
+
+		this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+
+		this.workerThread.start();
+		try {
+			this.workerThread.join(60 * SECONDS);
+		} catch (final InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+}
diff --git a/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis2.java b/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis2.java
new file mode 100644
index 0000000000000000000000000000000000000000..2db4c764c7be0bc717c9e4ad82fd3fa7d65783a5
--- /dev/null
+++ b/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis2.java
@@ -0,0 +1,143 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.traceReconstruction;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import kieker.analysis.plugin.filter.flow.TraceEventRecords;
+import kieker.common.record.IMonitoringRecord;
+import kieker.common.record.controlflow.OperationExecutionRecord;
+import kieker.common.record.flow.IFlowRecord;
+import teetime.framework.concurrent.StageTerminationPolicy;
+import teetime.framework.concurrent.WorkerThread;
+import teetime.framework.core.Analysis;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.framework.core.Pipeline;
+import teetime.framework.sequential.QueuePipe;
+import teetime.stage.Cache;
+import teetime.stage.CountingFilter;
+import teetime.stage.InstanceOfFilter;
+import teetime.stage.io.File2TextLinesFilter;
+import teetime.stage.kieker.MonitoringLogDirectory2Files;
+import teetime.stage.kieker.className.ClassNameRegistryCreationFilter;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+import teetime.stage.kieker.fileToRecord.textLine.TextLine2RecordFilter;
+import teetime.stage.kieker.traceReconstruction.TraceReconstructionFilter;
+import teetime.stage.predicate.IsIMonitoringRecordInRange;
+import teetime.stage.predicate.IsOperationExecutionRecordTraceIdPredicate;
+import teetime.stage.predicate.PredicateFilter;
+import teetime.stage.stringBuffer.StringBufferFilter;
+import teetime.stage.util.TextLine;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class TraceReconstructionAnalysis2 extends Analysis {
+	private static final int SECONDS = 1000;
+
+	private WorkerThread workerThread;
+
+	private ClassNameRegistryRepository classNameRegistryRepository;
+
+	@Override
+	public void init() {
+		super.init();
+		final IPipeline pipeline = this.buildPipeline();
+		this.workerThread = new WorkerThread(pipeline, 0);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private IPipeline buildPipeline() {
+		// predicates TODO
+		final IsIMonitoringRecordInRange isIMonitoringRecordInRange = new IsIMonitoringRecordInRange(0, 1000);
+		final IsOperationExecutionRecordTraceIdPredicate isOperationExecutionRecordTraceIdPredicate = new IsOperationExecutionRecordTraceIdPredicate(false, null);
+
+		final ClassNameRegistryCreationFilter classNameRegistryCreationFilter = new ClassNameRegistryCreationFilter(this.classNameRegistryRepository);
+		final MonitoringLogDirectory2Files directory2FilesFilter = new MonitoringLogDirectory2Files();
+		final File2TextLinesFilter file2TextLinesFilter = new File2TextLinesFilter();
+		final Cache<TextLine> cache = new Cache<TextLine>();
+
+		final TextLine2RecordFilter textLine2RecordFilter = new TextLine2RecordFilter(this.classNameRegistryRepository);
+		final StringBufferFilter<IMonitoringRecord> stringBufferFilter = new StringBufferFilter<IMonitoringRecord>();
+		final PredicateFilter<IMonitoringRecord> timestampFilter = new PredicateFilter<IMonitoringRecord>(isIMonitoringRecordInRange);
+		final PredicateFilter<OperationExecutionRecord> traceIdFilter = new PredicateFilter<OperationExecutionRecord>(isOperationExecutionRecordTraceIdPredicate);
+		final InstanceOfFilter<IMonitoringRecord, IFlowRecord> instanceOfFilter = new InstanceOfFilter<IMonitoringRecord, IFlowRecord>(IFlowRecord.class);
+		final TraceReconstructionFilter traceReconstructionFilter = new TraceReconstructionFilter();
+		final CountingFilter<TraceEventRecords> countingFilter = new CountingFilter<TraceEventRecords>();
+
+		// add each stage to a stage list
+		final LinkedList<IStage> startStages = new LinkedList<IStage>();
+		startStages.add(classNameRegistryCreationFilter);
+
+		final List<IStage> stages = new LinkedList<IStage>();
+		stages.add(classNameRegistryCreationFilter);
+		stages.add(directory2FilesFilter);
+		stages.add(file2TextLinesFilter);
+		stages.add(cache);
+
+		stages.add(textLine2RecordFilter);
+		stages.add(stringBufferFilter);
+		stages.add(timestampFilter);
+		stages.add(traceIdFilter);
+		stages.add(instanceOfFilter);
+		stages.add(traceReconstructionFilter);
+		stages.add(countingFilter);
+
+		// connect pipes
+		QueuePipe.connect(classNameRegistryCreationFilter.filePrefixOutputPort, directory2FilesFilter.filePrefixInputPort);
+		QueuePipe.connect(classNameRegistryCreationFilter.relayDirectoryOutputPort, directory2FilesFilter.directoryInputPort);
+		QueuePipe.connect(directory2FilesFilter.fileOutputPort, file2TextLinesFilter.fileInputPort);
+		QueuePipe.connect(file2TextLinesFilter.textLineOutputPort, cache.objectInputPort);
+		// QueuePipe.connect(XXX, cache.sendInputPort);
+		QueuePipe.connect(cache.objectOutputPort, textLine2RecordFilter.textLineInputPort);
+		QueuePipe.connect(textLine2RecordFilter.recordOutputPort, stringBufferFilter.objectInputPort);
+		QueuePipe.connect(stringBufferFilter.objectOutputPort, timestampFilter.inputPort);
+		QueuePipe.connect(timestampFilter.matchingOutputPort, traceIdFilter.inputPort);
+		// QueuePipe.connect(timestampFilter.mismatchingOutputPort, YYY); // ignore this case
+		QueuePipe.connect(traceIdFilter.matchingOutputPort, instanceOfFilter.inputPort);
+		// QueuePipe.connect(traceIdFilter.mismatchingOutputPort, traceIdFilter.inputPort); // ignore this case
+		QueuePipe.connect(XXX, traceReconstructionFilter.timestampInputPort);
+		QueuePipe.connect(instanceOfFilter.matchingOutputPort, traceReconstructionFilter.recordInputPort);
+		// QueuePipe.connect(instanceOfFilter.mismatchingOutputPort, instanceOfFilter.inputPort); // ignore this case
+		QueuePipe.connect(traceReconstructionFilter.traceValidOutputPort, countingFilter.INPUT_OBJECT);
+		// QueuePipe.connect(traceReconstructionFilter.traceInvalidOutputPort, XXX); // ignore this case
+
+		final Pipeline pipeline = new Pipeline();
+		pipeline.setStartStages(startStages);
+		pipeline.setStages(stages);
+		return pipeline;
+	}
+
+	@Override
+	public void start() {
+		super.start();
+
+		this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION);
+
+		this.workerThread.start();
+		try {
+			this.workerThread.join(60 * SECONDS);
+		} catch (final InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+}
diff --git a/src/main/java/teetime/framework/concurrent/ConcurrentPipeline.java.todo b/src/main/java/teetime/framework/concurrent/ConcurrentPipeline.java.todo
new file mode 100644
index 0000000000000000000000000000000000000000..07cbd64e85f333296469a7105461eba7206996bf
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/ConcurrentPipeline.java.todo
@@ -0,0 +1,157 @@
+package kieker.panalysis.framework.concurrent;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import kieker.panalysis.framework.core.IInputPort;
+import kieker.panalysis.framework.core.IOutputPort;
+import kieker.panalysis.framework.core.IPipe;
+import kieker.panalysis.framework.core.ISink;
+import kieker.panalysis.framework.core.ISource;
+import kieker.panalysis.framework.core.IStage;
+
+public class ConcurrentPipeline {
+
+	private final Map<IOutputPort<?, ?>, IInputPort<?, ?>> connections = new HashMap<IOutputPort<?, ?>, IInputPort<?, ?>>();
+	private final Set<IStage> ioStages = new HashSet<IStage>();
+	private final Map<IStage, List<IStage>> standardStages = new HashMap<IStage, List<IStage>>();
+
+	private final int numCores = Runtime.getRuntime().availableProcessors();
+	private int defaultBundleSize = 64;
+
+	public <T> void connect(final IOutputPort<?, T> sourcePort, final IInputPort<?, T> targetPort, final int bundleSize) {
+		this.connections.put(sourcePort, targetPort);
+		this.addStage(sourcePort.getOwningStage());
+		this.addStage(targetPort.getOwningStage());
+	}
+
+	/**
+	 * Connects to ports using the default bundle size
+	 * 
+	 * @param sourcePort
+	 * @param targetPort
+	 */
+	public <T> void connect(final IOutputPort<?, T> sourcePort, final IInputPort<?, T> targetPort) {
+		this.connect(sourcePort, targetPort, this.defaultBundleSize);
+	}
+
+	private void addStage(final IStage owningStage) {
+		if (owningStage instanceof IoStage) {
+			this.ioStages.add(owningStage);
+		} else {
+			this.standardStages.put(owningStage, null);
+		}
+	}
+
+	public void start() {
+		this.cloneNonIoStages();
+		this.connectConcurrentStages();
+		this.instantiatePipes();
+	}
+
+	private void cloneNonIoStages() {
+		for (final Entry<IStage, List<IStage>> entry : this.standardStages.entrySet()) {
+			final List<IStage> concurrentStages = this.createConcurrentStages(entry.getKey());
+			entry.setValue(concurrentStages);
+		}
+	}
+
+	private List<IStage> createConcurrentStages(final IStage stage) {
+		final List<IStage> concurrentStages = new LinkedList<IStage>();
+		for (int i = 0; i < this.numCores; i++) {
+			final Class<? extends IStage> stageClazz = stage.getClass();
+			// final Constructor<? extends IStage> constructor = stageClazz.getConstructor(Configuration.class);
+			// final IStage copiedStage = constructor.newInstance(stage.getConfiguration()); // copy by reference since the configuration is read-only
+
+			IStage copiedStage;
+			try {
+				copiedStage = stageClazz.newInstance();
+			} catch (final InstantiationException e) {
+				throw new IllegalStateException("The stage to be copied requires a constructor without any parameters.", e);
+			} catch (final IllegalAccessException e) {
+				throw new IllegalStateException("The stage to be copied requires a constructor without any parameters.", e);
+			}
+			copiedStage.copyAttributes(stage);
+			concurrentStages.add(copiedStage);
+		}
+		return concurrentStages;
+	}
+
+	private void connectConcurrentStages() {
+		for (final Entry<IOutputPort<?, ?>, IInputPort<?, ?>> entry : this.connections.entrySet()) {
+			final IOutputPort<?, ?> sourcePort = entry.getKey();
+			final IInputPort<?, ?> targetPort = entry.getValue();
+
+			final boolean isSourceIoStage = sourcePort.getOwningStage() instanceof IoStage;
+			final boolean isTargetIoStage = targetPort.getOwningStage() instanceof IoStage;
+
+			if (isSourceIoStage && !isTargetIoStage) {
+				final List<IStage> concurrentStages = this.standardStages.get(targetPort.getOwningStage());
+				final IStage owningSourceStage = sourcePort.getOwningStage();
+				for (final IStage concurrentTargetStage : concurrentStages) {
+					this.connections.put(owningSourceStage.getNewOutputPort(), concurrentTargetStage.getInputPortByIndex(targetPort.getIndex()));
+				}
+			} else if (!isSourceIoStage && isTargetIoStage) {
+				final List<IStage> concurrentStages = this.standardStages.get(sourcePort.getOwningStage());
+				final IStage owningTargetStage = targetPort.getOwningStage();
+				for (final IStage s : concurrentStages) {
+					this.connections.put(s.getOutputPortByIndex(sourcePort.getIndex()), owningTargetStage.getNewInputPort());
+				}
+			} else {
+				final List<IStage> concurrentSourceStages = this.standardStages.get(sourcePort.getOwningStage());
+				final List<IStage> concurrentTargetStages = this.standardStages.get(targetPort.getOwningStage());
+				for (int i = 0; i < concurrentSourceStages.size(); i++) {
+					final IStage sourceStage = concurrentSourceStages.get(i);
+					final IOutputPort<?, ?> otherSourcePort = sourceStage.getOutputPortByIndex(sourcePort.getIndex());
+
+					final IStage targetStage = concurrentTargetStages.get(i);
+					final IInputPort<?, ?> otherTargetPort = targetStage.getInputPortByIndex(targetPort.getIndex());
+
+					this.connections.put(otherSourcePort, otherTargetPort);
+				}
+			}
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	private <T, S0 extends ISource, S1 extends ISink<S1>> void instantiatePipes() {
+		for (final Entry<IOutputPort<?, ?>, IInputPort<?, ?>> entry : this.connections.entrySet()) {
+			final IOutputPort<S0, T> sourcePort = (IOutputPort<S0, T>) entry.getKey();
+			final IInputPort<S1, T> targetPort = (IInputPort<S1, T>) entry.getValue();
+			this.instantiatePipe(sourcePort, targetPort);
+		}
+	}
+
+	private <T, P extends IPipe<T>, S0 extends ISource, S1 extends ISink<S1>>
+			void instantiatePipe(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) {
+		final boolean isSourceIoStage = sourcePort.getOwningStage() instanceof IoStage;
+		final boolean isTargetIoStage = targetPort.getOwningStage() instanceof IoStage;
+
+		IPipe<T> pipe;
+		if (isSourceIoStage && !isTargetIoStage) {
+			pipe = new ConcurrentWorkStealingPipe<T>();
+		} else if (!isSourceIoStage && isTargetIoStage) {
+			pipe = new SingleProducerSingleConsumerPipe<T>();
+		} else if (isSourceIoStage && isTargetIoStage) {
+			throw new IllegalStateException("It is not allowed to connect two I/O stages.");
+		} else {
+			pipe = new ConcurrentWorkStealingPipe<T>();
+		}
+
+		pipe.setSourcePort(sourcePort);
+		pipe.setTargetPort(targetPort);
+	}
+
+	public void setDefaultBundleSize(final int defaultBundleSize) {
+		this.defaultBundleSize = defaultBundleSize;
+	}
+
+	public int getDefaultBundleSize() {
+		return this.defaultBundleSize;
+	}
+}
diff --git a/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipe.java b/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipe.java
new file mode 100644
index 0000000000000000000000000000000000000000..fcffbda770c58d775f64f006320c002bb8bd4a4c
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipe.java
@@ -0,0 +1,145 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.concurrent;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.framework.concurrent.steal.IStealStrategy;
+import teetime.framework.core.AbstractPipe;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.ISink;
+import teetime.framework.core.ISource;
+import teetime.util.concurrent.workstealing.CircularWorkStealingDeque;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ConcurrentWorkStealingPipe<T> extends AbstractPipe<T> {
+
+	private final CircularWorkStealingDeque<T> circularWorkStealingDeque = new CircularWorkStealingDeque<T>();
+	private final IStealStrategy<T> stealStrategy;
+
+	// BETTER use a prioritized list that considers
+	// <ul>
+	// <li>the size of each deque to minimize steals
+	// <li>the core's locality to improve cache access performance
+	// </ul>
+
+	private List<ConcurrentWorkStealingPipe<T>> pipesToStealFrom;
+	private int numTakenElements = 0;
+
+	ConcurrentWorkStealingPipe(final IStealStrategy<T> stealStrategy) {
+		this.stealStrategy = stealStrategy;
+	}
+
+	public <S0 extends ISource, S1 extends ISink<S1>> void connect(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) {
+		this.setSourcePort(sourcePort);
+		this.setTargetPort(targetPort);
+	}
+
+	@Override
+	protected void putInternal(final T token) {
+		this.circularWorkStealingDeque.pushBottom(token);
+	}
+
+	public void putMultiple(final List<T> elements) {
+		this.circularWorkStealingDeque.pushBottomMultiple(elements);
+	}
+
+	@Override
+	protected T tryTakeInternal() {
+		T record = this.circularWorkStealingDeque.popBottom();
+		if (record == null) {
+			record = this.stealStrategy.steal(this.getTargetPort(), this.pipesToStealFrom);
+			if (record == null) {
+				return null;
+			}
+			System.out.println("stolen (" + Thread.currentThread() + "): " + record);
+		}
+		this.numTakenElements++;
+		return record;
+	}
+
+	public T take() {
+		T record = this.circularWorkStealingDeque.popBottomEx();
+		if (record == null) {
+			record = this.stealStrategy.steal(this.getTargetPort(), this.pipesToStealFrom);
+			if (record == null) {
+				return null;
+			}
+		}
+		this.numTakenElements++;
+		return record;
+	}
+
+	public List<T> tryTakeMultiple(final int maxItemsToTake) {
+		// TODO Auto-generated method stub
+		// BETTER find a way to take multiple elements directly without a loop
+		return null;
+	}
+
+	public T read() {
+		final T record = this.circularWorkStealingDeque.readBottom();
+		return record;
+	}
+
+	public boolean isEmpty() {
+		return this.circularWorkStealingDeque.isEmpty();
+	}
+
+	public T steal() {
+		return this.circularWorkStealingDeque.steal();
+	}
+
+	public List<T> stealMultiple(final int maxItemsToSteal) {
+		int maxItemsToStealCounter = maxItemsToSteal;
+		final List<T> stolenElements = new LinkedList<T>();
+		while (maxItemsToStealCounter-- > 0) {
+			final T stolenElement = this.steal();
+			if (stolenElement == null) {
+				break;
+			}
+			stolenElements.add(stolenElement);
+		}
+		// BETTER find a way to steal multiple elements directly without a loop
+		return stolenElements;
+	}
+
+	public List<ConcurrentWorkStealingPipe<T>> getPipesToStealFrom() {
+		return this.pipesToStealFrom;
+	}
+
+	public void setPipesToStealFrom(final List<ConcurrentWorkStealingPipe<T>> pipesToStealFrom) {
+		this.pipesToStealFrom = pipesToStealFrom;
+	}
+
+	@Override
+	public void onPipelineStarts() {
+		this.pipesToStealFrom = new LinkedList<ConcurrentWorkStealingPipe<T>>(this.pipesToStealFrom);
+		this.pipesToStealFrom.remove(this);
+	}
+
+	@Override
+	public String toString() {
+		return this.getClass().getSimpleName() + "={" + "numTakenElements=" + this.numTakenElements + "}";
+	}
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipeFactory.java b/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..c05668c3fbb465dff3e8b02c7f7f54088343eefc
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipeFactory.java
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.concurrent;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.framework.concurrent.steal.IStealStrategy;
+import teetime.framework.concurrent.steal.StealIfEmptyStrategy;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ConcurrentWorkStealingPipeFactory<T> {
+
+	private final List<ConcurrentWorkStealingPipe<T>> pipes = new LinkedList<ConcurrentWorkStealingPipe<T>>();
+	private final IStealStrategy<T> stealStrategy = new StealIfEmptyStrategy<T>();
+
+	public ConcurrentWorkStealingPipe<T> create() {
+		final ConcurrentWorkStealingPipe<T> pipe = new ConcurrentWorkStealingPipe<T>(this.stealStrategy);
+		pipe.setPipesToStealFrom(this.pipes);
+
+		this.pipes.add(pipe);
+
+		return pipe;
+	}
+
+	public List<ConcurrentWorkStealingPipe<T>> getPipes() {
+		return this.pipes;
+	}
+}
diff --git a/src/main/java/teetime/framework/concurrent/IStageScheduler.java b/src/main/java/teetime/framework/concurrent/IStageScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..c798ddb59f0e87c3645650bbca1528354f00f66c
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/IStageScheduler.java
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.concurrent;
+
+import teetime.framework.core.IStage;
+
+/**
+ * @author Christian Wulf
+ *
+ * @since 1.10
+ */
+public interface IStageScheduler {
+
+	public abstract IStage get();
+
+	public abstract boolean isAnyStageActive();
+
+	public abstract void disable(IStage stage);
+
+	public abstract void determineNextStage(IStage stage, boolean executedSuccessfully);
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/IStageWorkList.java b/src/main/java/teetime/framework/concurrent/IStageWorkList.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee3073b97ff91d9ebe71cb09701191ee90340303
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/IStageWorkList.java
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.concurrent;
+
+import java.util.Collection;
+
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.IStage;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public interface IStageWorkList {
+
+	public abstract void pushAll(Collection<? extends IStage> stages);
+
+	public abstract void pushAll(IOutputPort<?, ?>[] outputPorts);
+
+	public abstract IStage pop();
+
+	public abstract IStage read();
+
+	public abstract boolean isEmpty();
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/NextStageScheduler.java b/src/main/java/teetime/framework/concurrent/NextStageScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..8eda5657ec8a2befafbb6dde0296fca91fdf5912
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/NextStageScheduler.java
@@ -0,0 +1,136 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.concurrent;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.util.StopWatch;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class NextStageScheduler implements IStageScheduler {
+
+	protected final Map<IStage, Boolean> statesOfStages = new HashMap<IStage, Boolean>();
+	private final Collection<IStage> highestPrioritizedEnabledStages = new ArrayList<IStage>();
+	private final IStageWorkList workList;
+
+	private final StopWatch stopWatch = new StopWatch();
+	private long durationInNs;
+	private int iterations;
+	private final List<Long> durations = new LinkedList<Long>();
+
+	public NextStageScheduler(final IPipeline pipeline, final int accessesDeviceId) throws Exception {
+		// this.workList = new StageWorkList(accessesDeviceId, pipeline.getStages().size());
+		this.workList = new StageWorkArrayList(pipeline, accessesDeviceId); // faster implementation
+
+		this.highestPrioritizedEnabledStages.addAll(pipeline.getStartStages());
+
+		this.workList.pushAll(this.highestPrioritizedEnabledStages);
+		// System.out.println("Initial work list: " + this.workList);
+		// this.workList.addAll(pipeline.getStages());
+
+		for (final IStage stage : pipeline.getStages()) {
+			this.enable(stage);
+		}
+	}
+
+	@Override
+	public IStage get() {
+		return this.workList.read();
+	}
+
+	@Override
+	public boolean isAnyStageActive() {
+		// System.out.println("workList: " + this.workList);
+		return !this.workList.isEmpty();
+	}
+
+	protected void enable(final IStage stage) {
+		// // / TODO consider to move state (enabled/disabled) of stage to stage for performance reasons
+		this.statesOfStages.put(stage, Boolean.TRUE);
+	}
+
+	@Override
+	public void disable(final IStage stage) {
+		this.statesOfStages.put(stage, Boolean.FALSE);
+
+		if (this.highestPrioritizedEnabledStages.contains(stage)) {
+			this.highestPrioritizedEnabledStages.remove(stage);
+			for (final IStage outputStage : stage.getAllOutputStages()) {
+				if (this.statesOfStages.get(outputStage) == Boolean.TRUE) {
+					this.highestPrioritizedEnabledStages.add(outputStage);
+				}
+			}
+		}
+
+		stage.fireSignalClosingToAllOutputPorts();
+	}
+
+	@Override
+	public void determineNextStage(final IStage stage, final boolean executedSuccessfully) {
+		this.iterations++;
+
+		this.stopWatch.start();
+
+		// final Collection<? extends IStage> outputStages = stage.getContext().getOutputStages();
+		final IOutputPort<?, ?>[] outputPorts = stage.getContext().getOutputPorts();
+		if (outputPorts.length > 0) {
+			final boolean inputPortsAreEmpty = stage.getContext().inputPortsAreEmpty();
+			if (inputPortsAreEmpty) {
+				this.workList.pop();
+			}
+
+			// TODO consider to not add the stage again if it has a cyclic pipe
+			// TODO or prioritize non-self stages
+			// while (outputStages.remove(stage)) {
+			// }
+
+			this.workList.pushAll(outputPorts);
+
+			stage.getContext().clearSucessors();
+		} else {
+			this.workList.pop();
+		}
+
+		if (this.workList.isEmpty()) {
+			this.workList.pushAll(this.highestPrioritizedEnabledStages);
+		}
+
+		this.stopWatch.end();
+
+		this.durationInNs += this.stopWatch.getDuration();
+		if ((this.iterations % 10000) == 0) {
+			this.durations.add(this.durationInNs);
+			this.durationInNs = 0;
+		}
+	}
+
+	public List<Long> getDurations() {
+		return this.durations;
+	}
+}
diff --git a/src/main/java/teetime/framework/concurrent/SingleProducerSingleConsumerPipe.java b/src/main/java/teetime/framework/concurrent/SingleProducerSingleConsumerPipe.java
new file mode 100644
index 0000000000000000000000000000000000000000..40e065db3b4b6c73ba49d1a3b6dfd5cc09f6ad3a
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/SingleProducerSingleConsumerPipe.java
@@ -0,0 +1,84 @@
+package teetime.framework.concurrent;
+
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import teetime.framework.core.AbstractPipe;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.ISink;
+import teetime.framework.core.ISource;
+import teetime.util.concurrent.workstealing.CircularWorkStealingDeque;
+
+public class SingleProducerSingleConsumerPipe<T> extends AbstractPipe<T> {
+
+	// BETTER use a cache-aware queue (see the corresponding paper)
+	final BlockingQueue<T> queue = new LinkedBlockingDeque<T>();
+
+	private final Callable<T> blockingTake = new Callable<T>() {
+		public T call() throws Exception {
+			return SingleProducerSingleConsumerPipe.this.queue.take();
+		}
+	};
+	private final Callable<T> nonBlockingTake = new Callable<T>() {
+		public T call() throws Exception {
+			return SingleProducerSingleConsumerPipe.this.queue.poll();
+		}
+	};
+	private volatile Callable<T> take = this.blockingTake;
+
+	public static <S0 extends ISource, S1 extends ISink<S1>, T> void connect(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) {
+		final SingleProducerSingleConsumerPipe<T> pipe = new SingleProducerSingleConsumerPipe<T>();
+		pipe.setSourcePort(sourcePort);
+		pipe.setTargetPort(targetPort);
+	}
+
+	public void putMultiple(final List<T> elements) {
+		this.queue.addAll(elements);
+	}
+
+	public T read() {
+		return this.queue.peek();
+	}
+
+	public List<?> tryTakeMultiple(final int numElementsToTake) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	protected void putInternal(final T token) {
+		this.queue.add(token);
+	}
+
+	@Override
+	protected T tryTakeInternal() {
+		try {
+			return this.take.call();
+		} catch (final Exception e) {
+			return null;
+		}
+	}
+
+	public T take() {
+		final T token = this.tryTake();
+		if (token == null) {
+			throw CircularWorkStealingDeque.DEQUE_IS_EMPTY_EXCEPTION;
+		}
+		return token;
+	}
+
+	public boolean isEmpty() {
+		return this.queue.isEmpty();
+	}
+
+	@Override
+	public void close() {
+		this.take = this.nonBlockingTake;
+		super.close();
+		this.getTargetPort().getOwningStage().getOwningThread().interrupt();
+	}
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/StageTerminationPolicy.java b/src/main/java/teetime/framework/concurrent/StageTerminationPolicy.java
new file mode 100644
index 0000000000000000000000000000000000000000..8eaec85e5da7ab3e35c48e983f6b3e0063759624
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/StageTerminationPolicy.java
@@ -0,0 +1,30 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.concurrent;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public enum StageTerminationPolicy {
+
+	TERMINATE_STAGE_NOW,
+	TERMINATE_STAGE_AFTER_NEXT_EXECUTION,
+	TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION,
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/StageWorkArrayList.java b/src/main/java/teetime/framework/concurrent/StageWorkArrayList.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f70d0b70313d41ed288b0bf1ec5ee2866e09f1e
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/StageWorkArrayList.java
@@ -0,0 +1,179 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.concurrent;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.IPipe;
+import teetime.framework.core.IPipeCommand;
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class StageWorkArrayList implements IStageWorkList {
+
+	private static class SchedulableStage {
+		public SchedulableStage() {}
+
+		public IStage stage;
+		public int numToBeExecuted;
+	}
+
+	private final IPipeline pipeline;
+	private final int accessesDeviceId;
+
+	/** sorted array where the last stage has highest priority */
+	private final SchedulableStage[] stages;
+	private int firstIndex = Integer.MAX_VALUE;
+	private int lastIndex = -1;
+
+	/**
+	 * @since 1.10
+	 */
+	public StageWorkArrayList(final IPipeline pipeline, final int accessesDeviceId) {
+		this.pipeline = pipeline;
+		this.accessesDeviceId = accessesDeviceId;
+		final List<IStage> localStages = this.init();
+
+		this.stages = new SchedulableStage[localStages.size()];
+		for (int i = 0; i < localStages.size(); i++) {
+			final SchedulableStage schedulableStage = new SchedulableStage();
+			schedulableStage.stage = localStages.get(i);
+			schedulableStage.numToBeExecuted = 0;
+			this.stages[i] = schedulableStage;
+		}
+	}
+
+	private List<IStage> init() {
+		this.setDepthForEachStage();
+
+		final List<IStage> stageList = new ArrayList<IStage>(this.pipeline.getStages());
+		final Comparator<? super IStage> depthComparator = new Comparator<IStage>() {
+			public int compare(final IStage o1, final IStage o2) {
+				if (o1.getDepth() == o2.getDepth()) {
+					return 0;
+				} else if (o1.getDepth() < o2.getDepth()) {
+					return -1;
+				} else {
+					return 1;
+				}
+			}
+		};
+
+		Collections.sort(stageList, depthComparator);
+
+		for (int i = 0; i < stageList.size(); i++) {
+			stageList.get(i).setSchedulingIndex(i);
+		}
+
+		return stageList;
+	}
+
+	private void setDepthForEachStage() {
+		final IPipeCommand setDepthCommand = new IPipeCommand() {
+			public void execute(final IPipe<?> pipe) throws Exception {
+				final IStage sourceStage = pipe.getSourcePort().getOwningStage();
+				final IStage owningStage = pipe.getTargetPort().getOwningStage();
+				if (owningStage.getDepth() == IStage.DEPTH_NOT_SET) {
+					owningStage.setDepth(sourceStage.getDepth() + 1);
+					owningStage.notifyOutputPipes(this);
+				}
+			}
+		};
+
+		for (final IStage startStage : this.pipeline.getStartStages()) {
+			startStage.setDepth(0);
+		}
+
+		for (final IStage startStage : this.pipeline.getStartStages()) {
+			try {
+				startStage.notifyOutputPipes(setDepthCommand);
+			} catch (final Exception e) {
+				throw new IllegalStateException("may not happen", e);
+			}
+		}
+	}
+
+	public void pushAll(final Collection<? extends IStage> stages) {
+		for (final IStage stage : stages) {
+			this.push(stage);
+		}
+	}
+
+	public void pushAll(final IOutputPort<?, ?>[] outputPorts) {
+		for (final IOutputPort<?, ?> outputPort : outputPorts) {
+			if (outputPort != null) {
+				final IStage targetStage = outputPort.getAssociatedPipe().getTargetPort().getOwningStage();
+				this.push(targetStage);
+			}
+		}
+	}
+
+	private void push(final IStage stage) {
+		if (this.isValid(stage)) {
+			this.firstIndex = Math.min(stage.getSchedulingIndex(), this.firstIndex);
+			this.lastIndex = Math.max(stage.getSchedulingIndex(), this.lastIndex);
+			this.stages[stage.getSchedulingIndex()].numToBeExecuted++;
+		}
+	}
+
+	private boolean isValid(final IStage stage) {
+		final boolean isValid = (stage.getAccessesDeviceId() == this.accessesDeviceId);
+		if (!isValid) {
+			// LOG.warn("Invalid stage: stage.accessesDeviceId = " + stage.getAccessesDeviceId() + ", accessesDeviceId = " + this.accessesDeviceId + ", stage = " +
+			// stage);
+		}
+		return isValid;
+	}
+
+	public IStage pop() {
+		final SchedulableStage schedulableStage = this.stages[this.lastIndex];
+		// schedulableStage.numToBeExecuted--;
+		schedulableStage.numToBeExecuted = 0;
+		cond:
+		if (schedulableStage.numToBeExecuted == 0)
+		{
+			for (int i = this.lastIndex - 1; i >= this.firstIndex; i--) {
+				if (this.stages[i].numToBeExecuted > 0) {
+					this.lastIndex = i;
+					break cond;
+				}
+			}
+			this.firstIndex = Integer.MAX_VALUE;
+			this.lastIndex = -1;
+		}
+		return schedulableStage.stage;
+	}
+
+	public IStage read() {
+		final SchedulableStage schedulableStage = this.stages[this.lastIndex];
+		return schedulableStage.stage;
+	}
+
+	public boolean isEmpty() {
+		return this.lastIndex == -1;
+	}
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/StageWorkList.java b/src/main/java/teetime/framework/concurrent/StageWorkList.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ea46cbceb0d22edcc377508b6638bd914ca89f9
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/StageWorkList.java
@@ -0,0 +1,81 @@
+package teetime.framework.concurrent;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.IStage;
+
+public class StageWorkList implements IStageWorkList {
+
+	// private static final Log LOG = LogFactory.getLog(StageWorkList.class);
+
+	private final List<IStage> workList;
+	private final int accessesDeviceId;
+
+	public StageWorkList(final int accessesDeviceId, final int initialCapacity) {
+		this.accessesDeviceId = accessesDeviceId;
+		this.workList = new ArrayList<IStage>(initialCapacity);
+	}
+
+	@Override
+	public void pushAll(final Collection<? extends IStage> stages) {
+		this.addAll(0, stages);
+	}
+
+	@Override
+	public IStage pop() {
+		return this.workList.remove(0);
+	}
+
+	@Override
+	public IStage read() {
+		return this.workList.get(0);
+	}
+
+	@Override
+	public boolean isEmpty() {
+		return this.workList.isEmpty();
+	}
+
+	private boolean isValid(final IStage stage) {
+		final boolean isValid = (stage.getAccessesDeviceId() == this.accessesDeviceId);
+		if (!isValid) {
+			// LOG.warn("Invalid stage: stage.accessesDeviceId = " + stage.getAccessesDeviceId() + ", accessesDeviceId = " + this.accessesDeviceId + ", stage = " +
+			// stage);
+		}
+		return isValid;
+	}
+
+	private boolean addAll(final int index, final Collection<? extends IStage> collection) {
+		final Collection<IStage> filteredCollection = new ArrayList<IStage>(collection.size());
+		for (final IStage stage : collection) {
+			this.push(filteredCollection, stage);
+		}
+		return this.workList.addAll(index, filteredCollection);
+	}
+
+	private void push(final Collection<IStage> filteredCollection, final IStage stage) {
+		if (this.isValid(stage)) {
+			filteredCollection.add(stage);
+		}
+	}
+
+	@Override
+	public String toString() {
+		return this.workList.toString();
+	}
+
+	public void pushAll(final IOutputPort<?, ?>[] outputPorts) {
+		final Collection<IStage> filteredCollection = new ArrayList<IStage>(outputPorts.length);
+		for (final IOutputPort<?, ?> outputPort : outputPorts) {
+			if (outputPort != null) {
+				final IStage targetStage = outputPort.getAssociatedPipe().getTargetPort().getOwningStage();
+				this.push(filteredCollection, targetStage);
+			}
+		}
+		this.workList.addAll(0, filteredCollection);
+	}
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/TokenBundle.java b/src/main/java/teetime/framework/concurrent/TokenBundle.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd035cc03722959e77e2da6e44db483f398c4184
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/TokenBundle.java
@@ -0,0 +1,16 @@
+package teetime.framework.concurrent;
+
+import java.util.List;
+
+public class TokenBundle<T> {
+
+	private final List<T> tokens;
+
+	public TokenBundle(final List<T> tokens) {
+		this.tokens = tokens;
+	}
+
+	public List<T> getTokens() {
+		return this.tokens;
+	}
+}
diff --git a/src/main/java/teetime/framework/concurrent/WorkerThread.java b/src/main/java/teetime/framework/concurrent/WorkerThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca2a15c7ec8a753cac53a5bb48ae05166646e207
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/WorkerThread.java
@@ -0,0 +1,207 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.concurrent;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.framework.core.IPipeline;
+import teetime.framework.core.IStage;
+import teetime.util.StopWatch;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class WorkerThread extends Thread {
+
+	private final IPipeline pipeline;
+	private IStageScheduler stageScheduler;
+
+	private volatile StageTerminationPolicy terminationPolicy;
+	private volatile boolean shouldTerminate = false;
+	private final int accessesDeviceId;
+	private int executedUnsuccessfullyCount;
+	private final StopWatch stopWatch = new StopWatch();
+	private final StopWatch iterationStopWatch = new StopWatch();
+	private final List<Long> schedulingOverheadsInNs = new LinkedList<Long>();
+	private long durationInNs;
+
+	public WorkerThread(final IPipeline pipeline, final int accessesDeviceId) {
+		this.pipeline = pipeline;
+		for (final IStage stage : pipeline.getStages()) {
+			stage.setOwningThread(this);
+		}
+		this.accessesDeviceId = accessesDeviceId;
+	}
+
+	@Override
+	public void run() {
+		try {
+			this.initDatastructures();
+		} catch (final Exception e) {
+			throw new IllegalStateException(e);
+		}
+
+		long iterations = 0;
+		long schedulingOverheadInNs = 0;
+		this.stopWatch.start();
+
+		while (this.stageScheduler.isAnyStageActive()) {
+			iterations++;
+			this.iterationStopWatch.start();
+
+			final IStage stage = this.stageScheduler.get();
+
+			this.startStageExecution(stage);
+			final boolean executedSuccessfully = stage.execute();
+			this.finishStageExecution(stage, executedSuccessfully);
+
+			if (this.shouldTerminate) {
+				this.executeTerminationPolicy(stage, executedSuccessfully);
+			}
+			this.stageScheduler.determineNextStage(stage, executedSuccessfully);
+
+			this.iterationStopWatch.end();
+			final long schedulingOverhead = this.iterationStopWatch.getDuration() - stage.getLastDuration();
+			schedulingOverheadInNs += schedulingOverhead;
+			if ((iterations % 10000) == 0) {
+				this.schedulingOverheadsInNs.add(schedulingOverheadInNs);
+				schedulingOverheadInNs = 0;
+			}
+		}
+
+		this.stopWatch.end();
+		this.durationInNs = this.stopWatch.getDuration();
+
+		final List<Long> durations = ((NextStageScheduler) this.stageScheduler).getDurations();
+		long overallDuration = 0;
+		for (int i = durations.size() / 2; i < durations.size(); i++) {
+			overallDuration += durations.get(i);
+		}
+		// System.out.println("Scheduler determine next stage (" + (durations.size() / 2) + "): " + TimeUnit.NANOSECONDS.toMillis(overallDuration) + " ms");
+
+		this.cleanUpDatastructures();
+	}
+
+	private void executeTerminationPolicy(final IStage executedStage, final boolean executedSuccessfully) {
+		// System.out.println("WorkerThread.executeTerminationPolicy(): " + this.terminationPolicy + ", executedSuccessfully=" + executedSuccessfully
+		// + ", mayBeDisabled=" + executedStage.mayBeDisabled());
+
+		switch (this.terminationPolicy) {
+		case TERMINATE_STAGE_AFTER_NEXT_EXECUTION:
+			if (executedStage.mayBeDisabled()) {
+				this.stageScheduler.disable(executedStage);
+			}
+			break;
+		case TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION:
+			if (!executedSuccessfully) {
+				if (executedStage.mayBeDisabled()) {
+					this.stageScheduler.disable(executedStage);
+				}
+			}
+			break;
+		case TERMINATE_STAGE_NOW:
+			for (final IStage stage : this.pipeline.getStages()) {
+				this.stageScheduler.disable(stage);
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	private void initDatastructures() throws Exception {
+		this.pipeline.fireStartNotification();
+		this.stageScheduler = new NextStageScheduler(this.pipeline, this.accessesDeviceId);
+	}
+
+	private void startStageExecution(final IStage stage) {
+		// System.out.println("Executing stage: " + stage);
+	}
+
+	private void finishStageExecution(final IStage stage, final boolean executedSuccessfully) {
+		// System.out.println("Executed stage " + stage + " successfully: " + executedSuccessfully);
+		if (!executedSuccessfully) { // statistics
+			this.executedUnsuccessfullyCount++;
+		}
+	}
+
+	private void cleanUpDatastructures() {
+		// System.out.println("Cleaning up datastructures...");
+		// System.out.println("Firing stop notification...");
+		this.pipeline.fireStopNotification();
+		// System.out.println("Thread terminated:" + this);
+		// System.out.println(this.getName() + ": executedUnsuccessfullyCount=" + this.executedUnsuccessfullyCount);
+	}
+
+	public IPipeline getPipeline() {
+		return this.pipeline;
+	}
+
+	// BETTER remove this method since it is not intuitive; add a check to onStartPipeline so that a stage automatically disables itself if it has no input ports
+	public void terminate(final StageTerminationPolicy terminationPolicyToUse) {
+		for (final IStage startStage : this.pipeline.getStartStages()) {
+			startStage.fireSignalClosingToAllInputPorts();
+		}
+
+		this.setTerminationPolicy(terminationPolicyToUse);
+	}
+
+	/**
+	 * If not set, this thread will run infinitely.
+	 * 
+	 * @param terminationPolicyToUse
+	 */
+	public void setTerminationPolicy(final StageTerminationPolicy terminationPolicyToUse) {
+		this.terminationPolicy = terminationPolicyToUse;
+		this.shouldTerminate = true;
+	}
+
+	public int getExecutedUnsuccessfullyCount() {
+		return this.executedUnsuccessfullyCount;
+	}
+
+	public List<Long> getSchedulingOverheadsInNs() {
+		return this.schedulingOverheadsInNs;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public long getDurationInNs() {
+		return this.durationInNs;
+	}
+
+	/**
+	 * Uses the last half of values to compute the scheduling overall overhead in ns
+	 * 
+	 * @since 1.10
+	 */
+	public long computeSchedulingOverheadInNs() {
+		final int size = this.schedulingOverheadsInNs.size();
+
+		long schedulingOverheadInNs = 0;
+		for (int i = size / 2; i < size; i++) {
+			final Long iterationOverhead = this.schedulingOverheadsInNs.get(i);
+			schedulingOverheadInNs += iterationOverhead;
+		}
+
+		return schedulingOverheadInNs;
+	}
+}
diff --git a/src/main/java/teetime/framework/concurrent/steal/IStealStrategy.java b/src/main/java/teetime/framework/concurrent/steal/IStealStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd38a19511ba5f3c840779faba5f374ca19bda52
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/steal/IStealStrategy.java
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.concurrent.steal;
+
+import java.util.Collection;
+
+import teetime.framework.concurrent.ConcurrentWorkStealingPipe;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IStage;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public interface IStealStrategy<T> {
+
+	<S extends IStage> T steal(IInputPort<S, T> inputPort, Collection<ConcurrentWorkStealingPipe<T>> pipesToStealFrom);
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/steal/StealIfEmptyStrategy.java b/src/main/java/teetime/framework/concurrent/steal/StealIfEmptyStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..a042054e192b0ec0aa86b3a782eedc1e1cfddd9d
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/steal/StealIfEmptyStrategy.java
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.concurrent.steal;
+
+import java.util.Collection;
+
+import teetime.framework.concurrent.ConcurrentWorkStealingPipe;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IStage;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class StealIfEmptyStrategy<T> implements IStealStrategy<T> {
+
+	public <S extends IStage> T steal(final IInputPort<S, T> inputPort, final Collection<ConcurrentWorkStealingPipe<T>> pipesToStealFrom) {
+		for (final ConcurrentWorkStealingPipe<T> pipe : pipesToStealFrom) {
+			final T stolenElement = pipe.steal();
+			if (stolenElement != null) {
+				return stolenElement;
+			}
+		}
+		// BETTER improve stealing efficiency by stealing multiple elements at once
+		return null; // do not expose internal impl details (here: CircularWorkStealingDeque); instead return null
+	}
+
+}
diff --git a/src/main/java/teetime/framework/concurrent/steal/StealIfMayBeDisabledStrategy.java b/src/main/java/teetime/framework/concurrent/steal/StealIfMayBeDisabledStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..f153f60b35635306ac097cefd7c7c9cc6c24438d
--- /dev/null
+++ b/src/main/java/teetime/framework/concurrent/steal/StealIfMayBeDisabledStrategy.java
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.concurrent.steal;
+
+import java.util.Collection;
+
+import teetime.framework.concurrent.ConcurrentWorkStealingPipe;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IStage;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class StealIfMayBeDisabledStrategy<T> implements IStealStrategy<T> {
+
+	public <S extends IStage> T steal(final IInputPort<S, T> inputPort, final Collection<ConcurrentWorkStealingPipe<T>> pipesToStealFrom) {
+		if (inputPort.getOwningStage().mayBeDisabled()) {
+			for (final ConcurrentWorkStealingPipe<T> pipe : pipesToStealFrom) {
+				final T stolenElement = pipe.steal();
+				if (stolenElement != null) {
+					return stolenElement;
+				}
+			}
+		}
+		// BETTER improve stealing efficiency by stealing multiple elements at once
+		return null; // do not expose internal impl details (here: CircularWorkStealingDeque); instead return null
+	}
+
+}
diff --git a/src/main/java/teetime/framework/core/AbstractFilter.java b/src/main/java/teetime/framework/core/AbstractFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b79473cb7e4c024c5b1871bc37062f6e95c9ee7
--- /dev/null
+++ b/src/main/java/teetime/framework/core/AbstractFilter.java
@@ -0,0 +1,334 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.util.StopWatch;
+import teetime.util.concurrent.workstealing.DequePopException;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <S>
+ *            the extending stage
+ * 
+ */
+public abstract class AbstractFilter<S extends IStage> extends AbstractStage implements ISink<S>, ISource, IPortListener<S> {
+
+	protected volatile boolean mayBeDisabled; // BETTER write only non-concurrent code in a stage
+
+	/**
+	 * @author Christian Wulf
+	 * 
+	 * @since 1.10
+	 */
+	public enum StageState {
+		UNINITIALIZED, PIPELINE_STARTED, PIPELINE_STOPPED
+	}
+
+	/**
+	 * Indicates whether this stage has (already) been initialized.<br>
+	 * <i>This attribute prevents this stage to be initialized more than once.</i>
+	 */
+	private StageState state = StageState.UNINITIALIZED;
+
+	private int depth = IStage.DEPTH_NOT_SET;
+	private int schedulingIndex;
+
+	private final List<IInputPort<S, ?>> inputPorts = new ArrayList<IInputPort<S, ?>>();
+	private final List<IInputPort<S, ?>> readOnlyInputPorts = Collections.unmodifiableList(this.inputPorts);
+
+	private final List<IOutputPort<S, ?>> outputPorts = new ArrayList<IOutputPort<S, ?>>();
+	private final List<IOutputPort<S, ?>> readOnlyOutputPorts = Collections.unmodifiableList(this.outputPorts);
+
+	private Context<S> context;
+
+	private final IPipeCommand closeCommand = new IPipeCommand() {
+		public void execute(final IPipe<?> pipe) throws Exception {
+			pipe.close();
+		}
+	};
+
+	private final IPipeCommand pipelineStartsCommand = new IPipeCommand() {
+		public void execute(final IPipe<?> pipe) throws Exception {
+			pipe.notifyPipelineStarts();
+		}
+	};
+
+	private int enabledInputPorts = 0;
+	/**
+	 * 0=in-memory, x>0=disk0, disk1, display0, display1, socket0, socket1 etc.
+	 */
+	private int accessesDeviceId = 0;
+
+	private final StopWatch stopWatch = new StopWatch();
+	private long overallDurationInNs = 0;
+
+	private long lastDuration;
+
+	public int getAccessesDeviceId() {
+		return this.accessesDeviceId;
+	}
+
+	public void setAccessesDeviceId(final int accessesDeviceId) {
+		this.accessesDeviceId = accessesDeviceId;
+	}
+
+	// BETTER return a limited context that allows "read" only
+	public Context<S> getContext() {
+		return this.context;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public final boolean execute() {
+		boolean success = false;
+		try {
+			success = this.executeLogged(this.context);
+			if (success) { // deprecated boolean return value
+				this.context.clear();
+			} else {
+				this.context.rollback();
+			}
+		} catch (final DequePopException e) {
+			this.context.rollback();
+		} catch (final Exception e) {
+			this.logger.error("Error in stage execution", e);
+		}
+		return success;
+	}
+
+	private boolean executeLogged(final Context<S> context) {
+		this.stopWatch.start();
+		try {
+			final boolean success = this.execute(context);
+			return success;
+		} finally {
+			this.stopWatch.end();
+			this.lastDuration = this.stopWatch.getDuration();
+			this.overallDurationInNs += this.lastDuration;
+		}
+	}
+
+	protected abstract boolean execute(Context<S> context);
+
+	public final void notifyPipelineStarts() throws Exception {
+		if (this.state == StageState.UNINITIALIZED) {
+			this.state = StageState.PIPELINE_STARTED;
+			this.onPipelineStarts();
+			this.notifyOutputPipes(this.pipelineStartsCommand);
+		}
+	}
+
+	/**
+	 * This method is called exactly once iff the pipeline is started.
+	 * 
+	 * @throws Exception
+	 * @since 1.10
+	 */
+	public void onPipelineStarts() throws Exception {
+		this.context = new Context<S>(this, this.readOnlyInputPorts);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void notifyOutputPipes(final IPipeCommand pipeCommand) throws Exception {
+		for (final IOutputPort<S, ?> outputPort : this.readOnlyOutputPorts) {
+			final IPipe<?> associatedPipe = outputPort.getAssociatedPipe();
+			if (associatedPipe != null) {
+				pipeCommand.execute(associatedPipe);
+			} // else: ignore unconnected port
+		}
+	}
+
+	public final void notifyPipelineStops() {
+		if (this.state != StageState.PIPELINE_STOPPED) {
+			this.state = StageState.PIPELINE_STOPPED;
+			this.onPipelineStops();
+		}
+	}
+
+	/**
+	 * This method is called exactly once iff the pipeline is stopped.
+	 * 
+	 * @since 1.10
+	 */
+	public void onPipelineStops() {
+		// default empty implementation
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void onPortIsClosed(final IInputPort<S, ?> inputPort) {
+		// inputPort.setState(IInputPort.State.CLOSING);
+		this.enabledInputPorts--;
+		// this.logger.info("Closed " + "(" + this.enabledInputPorts + " remaining) " + inputPort + " of " + this);
+
+		if (this.enabledInputPorts < 0) {
+			this.logger.error("Closed port more than once: portIndex=" + inputPort.getIndex() + " for stage " + this);
+		}
+
+		this.checkWhetherThisStageMayBeDisabled();
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private void checkWhetherThisStageMayBeDisabled() {
+		if (this.enabledInputPorts == 0) {
+			this.mayBeDisabled = true;
+			// this.logger.info(this.toString() + " can now be disabled by the pipeline scheduler.");
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void fireSignalClosingToAllInputPorts() {
+		// this.logger.info("Fire closing signal to all input ports of: " + this);
+
+		if (!this.inputPorts.isEmpty()) {
+			for (final IInputPort<S, ?> port : this.inputPorts) {
+				port.close();
+			}
+		} else {
+			this.checkWhetherThisStageMayBeDisabled();
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void fireSignalClosingToAllOutputPorts() {
+		try {
+			this.notifyOutputPipes(this.closeCommand);
+		} catch (final Exception e) {
+			throw new IllegalStateException("may not happen");
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public boolean mayBeDisabled() {
+		return this.mayBeDisabled;
+	}
+
+	@Override
+	public String toString() {
+		final String s = super.toString();
+		return "{" + s + ": " + "numPushedElements=" + this.context + "}";
+		// return s;
+	}
+
+	/**
+	 * @since 1.10
+	 * @return a new input port that accepts elements of the particular type that is specified in the variable declaration.
+	 */
+	protected <T> IInputPort<S, T> createInputPort() {
+		@SuppressWarnings("unchecked")
+		final InputPortImpl<S, T> inputPort = new InputPortImpl<S, T>((S) this);
+		inputPort.setIndex(this.inputPorts.size());
+		this.inputPorts.add(inputPort);
+		inputPort.setPortListener(this);
+		this.enabledInputPorts++;
+		return inputPort;
+	}
+
+	/**
+	 * @since 1.10
+	 * @return a new output port that accepts elements of the particular type that is specified in the variable declaration.
+	 */
+	protected <T> IOutputPort<S, T> createOutputPort() {
+		@SuppressWarnings("unchecked")
+		final OutputPortImpl<S, T> outputPort = new OutputPortImpl<S, T>((S) this);
+		outputPort.setIndex(this.outputPorts.size());
+		this.outputPorts.add(outputPort);
+		return outputPort;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	@SuppressWarnings("unchecked")
+	public List<IInputPort<S, ?>> getInputPorts() {
+		return this.readOnlyInputPorts;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	@SuppressWarnings("unchecked")
+	public List<IOutputPort<S, ?>> getOutputPorts() {
+		return this.readOnlyOutputPorts;
+	}
+
+	@Override
+	public Collection<? extends IStage> getAllOutputStages() {
+		final Collection<IStage> outputStages = new LinkedList<IStage>();
+		for (final IOutputPort<S, ?> outputPort : this.readOnlyOutputPorts) {
+			final IPipe<?> associatedPipe = outputPort.getAssociatedPipe();
+			if (associatedPipe != null) {
+				outputStages.add(associatedPipe.getTargetPort().getOwningStage());
+			}
+		}
+		return outputStages;
+	}
+
+	public IInputPort<?, ?> getInputPortByIndex(final int index) {
+		return this.readOnlyInputPorts.get(index);
+	}
+
+	public IOutputPort<?, ?> getOutputPortByIndex(final int index) {
+		return this.readOnlyOutputPorts.get(index);
+	}
+
+	public long getOverallDurationInNs() {
+		return this.overallDurationInNs;
+	}
+
+	public long getLastDuration() {
+		return this.lastDuration;
+	}
+
+	public int getDepth() {
+		return this.depth;
+	}
+
+	public void setDepth(final int depth) {
+		this.depth = depth;
+	}
+
+	public int getSchedulingIndex() {
+		return this.schedulingIndex;
+	}
+
+	public void setSchedulingIndex(final int schedulingIndex) {
+		this.schedulingIndex = schedulingIndex;
+	}
+}
diff --git a/src/main/java/teetime/framework/core/AbstractMultiPort.java b/src/main/java/teetime/framework/core/AbstractMultiPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..b40b0910709deba02860400130324a3c4dae32b3
--- /dev/null
+++ b/src/main/java/teetime/framework/core/AbstractMultiPort.java
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.core;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+abstract class AbstractMultiPort<S extends IStage, T> {
+
+	private final Set<IPipe<T>> associatedPipes = new HashSet<IPipe<T>>();
+	private final Set<IPipe<T>> readOnlyAssociatedPipes = Collections.unmodifiableSet(this.associatedPipes);
+
+	private S owningStage;
+
+	public void addAssociatedPipe(final IPipe<T> associatedPipe) {
+		this.associatedPipes.add(associatedPipe);
+	}
+
+	public Set<IPipe<T>> getAssociatedPipes() {
+		return this.readOnlyAssociatedPipes;
+	}
+
+	public S getOwningStage() {
+		return this.owningStage;
+	}
+
+	public void setOwningStage(final S owningStage) {
+		this.owningStage = owningStage;
+	}
+}
diff --git a/src/main/java/teetime/framework/core/AbstractPipe.java b/src/main/java/teetime/framework/core/AbstractPipe.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e596bf51687feab00ee3fbce47f92375c5223f1
--- /dev/null
+++ b/src/main/java/teetime/framework/core/AbstractPipe.java
@@ -0,0 +1,115 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <T>
+ *            The type of the pipe
+ * @param <P>
+ *            the extending pipe
+ */
+public abstract class AbstractPipe<T> implements IPipe<T> {
+
+	/**
+	 * @author Christian Wulf
+	 * 
+	 * @since 1.10
+	 */
+	public enum PipeState {
+		UNINITIALIZED, PIPELINE_STARTED, PIPELINE_STOPPED
+	}
+
+	private PipeState state = PipeState.UNINITIALIZED;
+
+	private IOutputPort<?, ? extends T> sourcePort;
+	private IInputPort<?, T> targetPort;
+
+	public IOutputPort<?, ? extends T> getSourcePort() {
+		return this.sourcePort;
+	}
+
+	public IInputPort<?, T> getTargetPort() {
+		return this.targetPort;
+	}
+
+	public <S extends ISource, A extends T> void setSourcePort(final IOutputPort<S, A> sourcePort) {
+		sourcePort.setAssociatedPipe(this);
+		this.sourcePort = sourcePort;
+	}
+
+	public <S extends ISink<S>, A extends T> void setTargetPort(final IInputPort<S, T> targetPort) {
+		targetPort.setAssociatedPipe(this);
+		this.targetPort = targetPort;
+	}
+
+	// BETTER remove if it does not add any new functionality
+	protected abstract void putInternal(T token);
+
+	public void put(final T token) {
+		this.putInternal(token);
+	}
+
+	// BETTER remove if it does not add any new functionality
+	protected abstract T tryTakeInternal();
+
+	public final T tryTake() {
+		return this.tryTakeInternal();
+	}
+
+	public final void notifyPipelineStarts() throws Exception {
+		if (this.state == PipeState.UNINITIALIZED) {
+			this.state = PipeState.PIPELINE_STARTED;
+			this.onPipelineStarts();
+			this.getTargetPort().getOwningStage().notifyPipelineStarts();
+		}
+	}
+
+	/**
+	 * This method is called exactly once iff the pipeline is started.
+	 * 
+	 * @since 1.10
+	 */
+	public void onPipelineStarts() {
+		// empty default implementation
+	}
+
+	public final void notifyPipelineStops() {
+		if (this.state != PipeState.PIPELINE_STOPPED) {
+			this.state = PipeState.PIPELINE_STOPPED;
+			this.onPipelineStops();
+			this.getTargetPort().getOwningStage().notifyPipelineStops();
+		}
+	}
+
+	/**
+	 * This method is called exactly once iff the pipeline is stopped.
+	 * 
+	 * @since 1.10
+	 */
+	public void onPipelineStops() {
+		// empty default implementation
+	}
+
+	public void close() {
+		this.targetPort.close();
+	}
+
+}
diff --git a/src/main/java/teetime/framework/core/AbstractPort.java b/src/main/java/teetime/framework/core/AbstractPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..4bcf752ef6d4049ec2e84447ee26fe9d60124fad
--- /dev/null
+++ b/src/main/java/teetime/framework/core/AbstractPort.java
@@ -0,0 +1,37 @@
+package teetime.framework.core;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+abstract class AbstractPort<S extends IStage, T> implements IPort<S, T> {
+
+	private IPipe<? super T> associatedPipe;
+	private S owningStage;
+	private int index;
+
+	public <A extends T> void setAssociatedPipe(final IPipe<? super T> associatedPipe) {
+		this.associatedPipe = associatedPipe;
+	}
+
+	public IPipe<? super T> getAssociatedPipe() {
+		return this.associatedPipe;
+	}
+
+	public S getOwningStage() {
+		return this.owningStage;
+	}
+
+	public void setOwningStage(final S owningStage) {
+		this.owningStage = owningStage;
+	}
+
+	public int getIndex() {
+		return this.index;
+	}
+
+	void setIndex(final int index) {
+		this.index = index;
+	}
+}
diff --git a/src/main/java/teetime/framework/core/AbstractStage.java b/src/main/java/teetime/framework/core/AbstractStage.java
new file mode 100644
index 0000000000000000000000000000000000000000..46f5e789dc12fc5ee62b35e087aff68186cb46b9
--- /dev/null
+++ b/src/main/java/teetime/framework/core/AbstractStage.java
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+import java.util.UUID;
+
+import kieker.common.logging.Log;
+import kieker.common.logging.LogFactory;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public abstract class AbstractStage implements IStage {
+
+	private final String id;
+	/**
+	 * A unique logger instance per stage instance
+	 */
+	protected Log logger;
+	private IPipeline owningPipeline;
+
+	private Thread owningThread;
+
+	public AbstractStage() {
+		this.id = UUID.randomUUID().toString(); // the id should only be represented by a UUID, not additionally by the class name
+		this.logger = LogFactory.getLog(this.id);
+	}
+
+	public String getId() {
+		return this.id;
+	}
+
+	public IPipeline getOwningPipeline() {
+		return this.owningPipeline;
+	}
+
+	public void setOwningPipeline(final IPipeline owningPipeline) {
+		this.owningPipeline = owningPipeline;
+	}
+
+	@Override
+	public Thread getOwningThread() {
+		return this.owningThread;
+	}
+
+	public void setOwningThread(final Thread owningThread) {
+		this.owningThread = owningThread;
+	}
+
+	@Override
+	public String toString() {
+		// return "{" + "class=" + this.getClass().getSimpleName() + ", id=" + this.id + "}";
+		return this.getClass().getSimpleName() + "(" + Thread.currentThread() + ")";
+	}
+}
diff --git a/src/main/java/teetime/framework/core/Analysis.java b/src/main/java/teetime/framework/core/Analysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..915773cde27abdb717bdb3070e48b2bd1edccf3e
--- /dev/null
+++ b/src/main/java/teetime/framework/core/Analysis.java
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class Analysis {
+
+	public void init() {
+
+	}
+
+	public void start() {
+
+	}
+
+	public void terminate() {
+
+	}
+}
diff --git a/src/main/java/teetime/framework/core/CompositeFilter.java b/src/main/java/teetime/framework/core/CompositeFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..353913b00cf6b4a12f028176b229d4229ff3ff8c
--- /dev/null
+++ b/src/main/java/teetime/framework/core/CompositeFilter.java
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public abstract class CompositeFilter implements IBaseStage {
+
+	protected final List<IBaseStage> schedulableStages = new ArrayList<IBaseStage>();
+
+	public List<IBaseStage> getSchedulableStages() {
+		return this.schedulableStages;
+	}
+}
diff --git a/src/main/java/teetime/framework/core/Context.java b/src/main/java/teetime/framework/core/Context.java
new file mode 100644
index 0000000000000000000000000000000000000000..e49904446434e6036f453172afd742cd84f36f99
--- /dev/null
+++ b/src/main/java/teetime/framework/core/Context.java
@@ -0,0 +1,169 @@
+package teetime.framework.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Context<S extends IStage> {
+
+	// private final Map<IPipe<Object>, List<Object>> pipesTakenFrom;
+	// private final Set<IStage> pipesPutTo = new HashSet<IStage>();
+
+	/**
+	 * @author Christian Wulf
+	 * 
+	 * @since 1.10
+	 */
+	private static class InputPortContainer {
+		public final List<Object> takenElements = new ArrayList<Object>();
+		public IPipe<Object> pipe;
+
+		public InputPortContainer() {}
+	}
+
+	private final InputPortContainer[] inputPortContainers;
+	private final IOutputPort<S, ?>[] outputPorts;
+
+	// statistics values
+	private int numPushedElements = 0;
+	private int numTakenElements = 0;
+
+	@SuppressWarnings("unchecked")
+	public Context(final IStage owningStage, final List<IInputPort<S, ?>> allTargetPorts) {
+		// this.pipesTakenFrom = this.createPipeMap(allTargetPorts);
+		this.inputPortContainers = this.createInputPortLists(owningStage.getInputPorts());
+		this.outputPorts = new IOutputPort[owningStage.getOutputPorts().size()];
+	}
+
+	@SuppressWarnings("unchecked")
+	private InputPortContainer[] createInputPortLists(final List<IInputPort<IStage, ?>> inputPorts) {
+		final InputPortContainer[] inputPortContainers = new InputPortContainer[inputPorts.size()];
+		for (int i = 0; i < inputPorts.size(); i++) {
+			inputPortContainers[i] = new InputPortContainer();
+			inputPortContainers[i].pipe = (IPipe<Object>) inputPorts.get(i).getAssociatedPipe();
+		}
+		return inputPortContainers;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public <T> void put(final IOutputPort<S, T> port, final T object) {
+		final IPipe<? super T> associatedPipe = port.getAssociatedPipe();
+		if (associatedPipe == null) {
+			return; // ignore unconnected port
+			// BETTER return a NullObject rather than checking for null
+		}
+		associatedPipe.put(object);
+
+		this.outputPorts[port.getIndex()] = port;
+		// this.pipesPutTo.add(associatedPipe.getTargetPort().getOwningStage());
+		this.numPushedElements++;
+	}
+
+	/**
+	 * 
+	 * @param inputPort
+	 * @return
+	 * @since 1.10
+	 */
+	public <T> T tryTake(final IInputPort<S, T> inputPort) {
+		final IPipe<? super T> associatedPipe = inputPort.getAssociatedPipe();
+		final T token = associatedPipe.tryTake();
+		if (token != null) {
+			this.logTransaction(inputPort, token);
+		}
+		return token;
+	}
+
+	/**
+	 * 
+	 * @param inputPort
+	 * @return
+	 * @since 1.10
+	 */
+	public <T> T take(final IInputPort<S, T> inputPort) {
+		final IPipe<? super T> associatedPipe = inputPort.getAssociatedPipe();
+		final T token = associatedPipe.take();
+		if (token != null) {
+			this.logTransaction(inputPort, token);
+		}
+		return token;
+	}
+
+	private <T> void logTransaction(final IInputPort<S, T> inputPort, final T token) {
+		final InputPortContainer inputPortContainer = this.inputPortContainers[inputPort.getIndex()];
+		// final List<Object> tokenList = this.pipesTakenFrom.get(inputPort);
+		inputPortContainer.takenElements.add(token);
+
+		this.numTakenElements++;
+	}
+
+	/**
+	 * 
+	 * @param inputPort
+	 * @return
+	 * 
+	 * @since 1.10
+	 */
+	public <T> T read(final IInputPort<S, T> inputPort) {
+		final IPipe<? super T> associatedPipe = inputPort.getAssociatedPipe();
+		return associatedPipe.read();
+	}
+
+	void clear() {
+		// for (final List<Object> takenElements : this.pipesTakenFrom.values()) {
+		for (final InputPortContainer inputPortContainer : this.inputPortContainers) {
+			inputPortContainer.takenElements.clear();
+		}
+	}
+
+	void rollback() {
+		// for (final Entry<IPipe<Object>, List<Object>> entry : this.pipesTakenFrom.entrySet()) {
+		// final IPipe<Object> associatedPipe = entry.getKey();
+		// final List<Object> takenElements = entry.getValue();
+
+		for (final InputPortContainer inputPortContainer : this.inputPortContainers) {
+
+			for (int k = inputPortContainer.takenElements.size() - 1; k >= 0; k--) {
+				final Object element = inputPortContainer.takenElements.get(k);
+				inputPortContainer.pipe.put(element);
+			}
+
+			this.numTakenElements -= inputPortContainer.takenElements.size();
+		}
+	}
+
+	@Override
+	public String toString() {
+		return "{" + "numTakenElements=" + this.numTakenElements + ", " + "numPushedElements=" + this.numPushedElements + "}";
+	}
+
+	/**
+	 * @return <code>true</code> iff all input ports are empty, otherwise <code>false</code>.
+	 */
+	public boolean inputPortsAreEmpty() {
+		for (final InputPortContainer inputPortContainer : this.inputPortContainers) {
+			if (!inputPortContainer.pipe.isEmpty()) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void clearSucessors() {
+		for (int i = 0; i < this.outputPorts.length; i++) {
+			this.outputPorts[i] = null;
+		}
+	}
+
+	/**
+	 * @return
+	 * @since 1.10
+	 */
+	public IOutputPort<S, ?>[] getOutputPorts() {
+		return this.outputPorts;
+	}
+}
diff --git a/src/main/java/teetime/framework/core/Description.java b/src/main/java/teetime/framework/core/Description.java
new file mode 100644
index 0000000000000000000000000000000000000000..fda9e0896772c175ce05d182219d3acfb90b7e94
--- /dev/null
+++ b/src/main/java/teetime/framework/core/Description.java
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.core;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.FIELD })
+public @interface Description {
+
+	String value();
+
+}
diff --git a/src/main/java/teetime/framework/core/IBaseStage.java b/src/main/java/teetime/framework/core/IBaseStage.java
new file mode 100644
index 0000000000000000000000000000000000000000..99c037456395c6a42d707c7e60423d3b587b3d4d
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IBaseStage.java
@@ -0,0 +1,25 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.core;
+
+/**
+ * @author Christian Wulf
+ *
+ * @since 1.10
+ */
+public interface IBaseStage {
+
+}
diff --git a/src/main/java/teetime/framework/core/IInputPort.java b/src/main/java/teetime/framework/core/IInputPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d834da75d345bef241d217d2c466bfed2358c4f
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IInputPort.java
@@ -0,0 +1,40 @@
+package teetime.framework.core;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @param <S>
+ *            the stage, this port belongs to<br>
+ *            <i>(used for ensuring type safety)</i>
+ * @param <T>
+ */
+public interface IInputPort<S extends IStage, T> extends IPort<S, T> {
+
+	/**
+	 * @since 1.10
+	 */
+	enum State {
+		OPEN, CLOSING
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public abstract State getState();
+
+	/**
+	 * @since 1.10
+	 */
+	public abstract void setState(final State state);
+
+	/**
+	 * @since 1.10
+	 */
+	public abstract void setPortListener(final IPortListener<S> stageListener);
+
+	/**
+	 * @since 1.10
+	 */
+	public void close();
+}
diff --git a/src/main/java/teetime/framework/core/IOutputPort.java b/src/main/java/teetime/framework/core/IOutputPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d8b8c060d4376c5c1dcf72fba7c9ee0fafdbcaa
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IOutputPort.java
@@ -0,0 +1,14 @@
+package teetime.framework.core;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @param <S>
+ *            the stage, this port belongs to<br>
+ *            <i>(used for ensuring type safety)</i>
+ * @param <T>
+ */
+public interface IOutputPort<S extends IStage, T> extends IPort<S, T> {
+
+}
diff --git a/src/main/java/teetime/framework/core/IPipe.java b/src/main/java/teetime/framework/core/IPipe.java
new file mode 100644
index 0000000000000000000000000000000000000000..148e88f6b29a094a1f7a41a0017f0ec6550d4c4d
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IPipe.java
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+import java.util.List;
+
+import teetime.util.concurrent.workstealing.DequePopException;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <T>
+ *            the type of elements this pipe may contain
+ * @param <P>
+ */
+public interface IPipe<T> {
+
+	/**
+	 * @since 1.10
+	 */
+	void put(T object);
+
+	/**
+	 * @since 1.10
+	 */
+	void putMultiple(List<T> elements);
+
+	// Let this uncommented for documentation purpose:<br>
+	// Since the concurrent execution does not base on locks, it is difficult to define a blocking take. Since there is no definition of blocking in the sequential
+	// execution, we do not define a blocking take in this interface
+	// /**
+	// * @since 1.10
+	// */
+	// @Deprecated
+	// T take();
+
+	/**
+	 * @return and removes the next element if the pipe is not empty, otherwise <code>null</code>
+	 * 
+	 * @since 1.10
+	 */
+	<A extends T> A tryTake();
+
+	/**
+	 * 
+	 * @return and removes the next element if the pipe is not empty, otherwise it throws a <code>DequePopException</code>
+	 * 
+	 * @throws DequePopException
+	 */
+	<A extends T> A take();
+
+	/**
+	 * @since 1.10
+	 * @return but does not removes the next element if the pipe is not empty, otherwise <code>null</code>
+	 */
+	<A extends T> A read();
+
+	/**
+	 * @since 1.10
+	 */
+	List<?> tryTakeMultiple(int numElementsToTake);
+
+	/**
+	 * <i>Attention: Checking for emptiness is rarely appropriate, since the state from EMPTY to NON-EMPTY can change between check and access (stale state).</i>
+	 * 
+	 * @return <code>true</code> if this pipe is empty in this special point in time, otherwise <code>false</code>.
+	 */
+	boolean isEmpty();
+
+	/**
+	 * @since 1.10
+	 */
+	<S extends ISource, A extends T> void setSourcePort(final IOutputPort<S, A> sourcePort);
+
+	/**
+	 * @since 1.10
+	 */
+	<S extends ISink<S>, A extends T> void setTargetPort(final IInputPort<S, T> targetPort);
+
+	/**
+	 * @since 1.10
+	 */
+	IOutputPort<?, ? extends T> getSourcePort();
+
+	/**
+	 * @since 1.10
+	 */
+	IInputPort<?, T> getTargetPort();
+
+	/**
+	 * @throws Exception
+	 * @since 1.10
+	 */
+	void notifyPipelineStarts() throws Exception;
+
+	/**
+	 * @since 1.10
+	 */
+	void notifyPipelineStops();
+
+	/**
+	 * @since 1.10
+	 */
+	void close();
+
+}
diff --git a/src/main/java/teetime/framework/core/IPipeCommand.java b/src/main/java/teetime/framework/core/IPipeCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f06d7a22fee16378d80e59914f04c4be7daaf86
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IPipeCommand.java
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.core;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public interface IPipeCommand {
+
+	void execute(IPipe<?> pipe) throws Exception;
+}
diff --git a/src/main/java/teetime/framework/core/IPipeline.java b/src/main/java/teetime/framework/core/IPipeline.java
new file mode 100644
index 0000000000000000000000000000000000000000..21372d7ebd450bec03e2e75b11171294a0b89d2f
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IPipeline.java
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.core;
+
+import java.util.List;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public interface IPipeline {
+
+	List<? extends IStage> getStages();
+
+	List<? extends IStage> getStartStages();
+
+	void fireStartNotification() throws Exception;
+
+	void fireStopNotification();
+}
diff --git a/src/main/java/teetime/framework/core/IPort.java b/src/main/java/teetime/framework/core/IPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f9d501fd15ea8c288e4e21aaf3be694f5bc13dd
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IPort.java
@@ -0,0 +1,22 @@
+package teetime.framework.core;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <T>
+ *            the type of elements this port accepts
+ */
+interface IPort<S extends IStage, T> {
+
+	IPipe<? super T> getAssociatedPipe();
+
+	<A extends T> void setAssociatedPipe(final IPipe<? super T> associatedPipe);
+
+	S getOwningStage();
+
+	int getIndex();
+
+}
diff --git a/src/main/java/teetime/framework/core/IPortListener.java b/src/main/java/teetime/framework/core/IPortListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..71b05c98f541155bc393bbebbc8dd8458b0120fa
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IPortListener.java
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.core;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public interface IPortListener<S extends IStage> {
+
+	void onPortIsClosed(IInputPort<S, ?> inputPort);
+}
diff --git a/src/main/java/teetime/framework/core/ISink.java b/src/main/java/teetime/framework/core/ISink.java
new file mode 100644
index 0000000000000000000000000000000000000000..4eaab24096c9fd7d12279fafd879c8800d48a3e1
--- /dev/null
+++ b/src/main/java/teetime/framework/core/ISink.java
@@ -0,0 +1,30 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <S>
+ */
+public interface ISink<S extends IStage> extends IStage {
+
+	// BETTER remove the type parameter
+}
diff --git a/src/main/java/teetime/framework/core/ISource.java b/src/main/java/teetime/framework/core/ISource.java
new file mode 100644
index 0000000000000000000000000000000000000000..77985e872789bd65188224216cd91f8dcc3fe58a
--- /dev/null
+++ b/src/main/java/teetime/framework/core/ISource.java
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ */
+public interface ISource extends IStage {
+
+}
diff --git a/src/main/java/teetime/framework/core/IStage.java b/src/main/java/teetime/framework/core/IStage.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd5c09f06d77be84015e2651ed2ec17977b51676
--- /dev/null
+++ b/src/main/java/teetime/framework/core/IStage.java
@@ -0,0 +1,178 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public interface IStage extends IBaseStage {
+
+	public final static int DEPTH_NOT_SET = -1;
+
+	/**
+	 * @since 1.10
+	 */
+	public String getId();
+
+	/**
+	 * @return <code>true</code> if the execution took enough tokens from the input ports so that the stage made progress due to this execution, <code>false</code>
+	 *         otherwise. The definition of <i>progress</i> depends on the semantics of the particular stage.
+	 * 
+	 *         <p>
+	 *         Example usage:
+	 *         </p>
+	 * 
+	 *         <pre>
+	 * <code>
+	 * boolean execute() {
+	 * 	final Tuple token = this.tryTake(FILE_WORDCOUNT_TUPLE);
+	 * 	if (token == null) {
+	 * 		return false;
+	 * 	}
+	 * 	...
+	 * 	return true;
+	 * }
+	 * 	</code>
+	 * </pre>
+	 * 
+	 * @since 1.10
+	 */
+	boolean execute();
+
+	// Let the method uncommented for documentation purpose:<br>
+	// Since the execution can dependent on the state of one, a subset, or all input queues,<br>
+	// an overloaded version with one input port is useless.
+	// void execute(InputPort inputPort);
+	//
+	//
+	// Let the method uncommented for documentation purpose:<br>
+	// Since the execution can dependent on one, a subset, or all input queues,<br>
+	// an overloaded version with a task bundle of one single input port is useless.
+	// void execute(TaskBundle taskBundle);
+
+	/**
+	 * 
+	 * @return <code>true</code> if the stage may be disabled by the pipeline scheduler, <code>false</code> otherwise.
+	 * 
+	 * @since 1.10
+	 */
+	boolean mayBeDisabled();
+
+	/**
+	 * @since 1.10
+	 */
+	void fireSignalClosingToAllOutputPorts();
+
+	/**
+	 * @since 1.10
+	 */
+	void fireSignalClosingToAllInputPorts();
+
+	/**
+	 * @since 1.10
+	 */
+	void notifyPipelineStarts() throws Exception;
+
+	/**
+	 * @since 1.10
+	 */
+	void notifyPipelineStops();
+
+	/**
+	 * <i>Hint: Used by the scheduler</i>
+	 * 
+	 * @since 1.10
+	 */
+	Context<?> getContext();
+
+	/**
+	 * @since 1.10
+	 */
+	IOutputPort<?, ?> getOutputPortByIndex(int index);
+
+	/**
+	 * @since 1.10
+	 */
+	IInputPort<?, ?> getInputPortByIndex(int index);
+
+	/**
+	 * @since 1.10
+	 */
+	int getAccessesDeviceId();
+
+	/**
+	 * @since 1.10
+	 */
+	void setAccessesDeviceId(final int accessesDeviceId);
+
+	/**
+	 * <i>Hint: Only needed by stage schedulers.</i>
+	 * 
+	 * @return
+	 */
+	public Collection<? extends IStage> getAllOutputStages();
+
+	/**
+	 * @since 1.10
+	 */
+	public <S extends IStage> List<IInputPort<S, ?>> getInputPorts();
+
+	/**
+	 * @since 1.10
+	 */
+	public <S extends IStage> List<IOutputPort<S, ?>> getOutputPorts();
+
+	Thread getOwningThread();
+
+	void setOwningThread(Thread owningThread);
+
+	/**
+	 * @since 1.10
+	 */
+	long getLastDuration();
+
+	/**
+	 * @since 1.10
+	 */
+	void notifyOutputPipes(IPipeCommand pipeCommand) throws Exception;
+
+	/**
+	 * @since 1.10
+	 */
+	public int getDepth();
+
+	/**
+	 * @since 1.10
+	 */
+	public void setDepth(int depth);
+
+	/**
+	 * @since 1.10
+	 */
+	public int getSchedulingIndex();
+
+	/**
+	 * @since 1.10
+	 */
+	public void setSchedulingIndex(int schedulingIndex);
+
+}
diff --git a/src/main/java/teetime/framework/core/InputPortImpl.java b/src/main/java/teetime/framework/core/InputPortImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb9c6c2ef9f55351a75b7ceecb18aa423e6efe3c
--- /dev/null
+++ b/src/main/java/teetime/framework/core/InputPortImpl.java
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.core;
+
+class InputPortImpl<S extends IStage, T> extends AbstractPort<S, T> implements IInputPort<S, T> {
+
+	private volatile State state = State.OPEN;
+
+	private IPortListener<S> stageListener;
+
+	public InputPortImpl(final S owningStage) {
+		this.setOwningStage(owningStage);
+	}
+
+	public void setState(final State state) {
+		this.state = state;
+	}
+
+	public State getState() {
+		return this.state;
+	}
+
+	public void setPortListener(final IPortListener<S> stageListener) {
+		this.stageListener = stageListener;
+	}
+
+	public void close() {
+		this.stageListener.onPortIsClosed(this);
+	}
+
+}
diff --git a/src/main/java/teetime/framework/core/OutputPortImpl.java b/src/main/java/teetime/framework/core/OutputPortImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3c0e42d618eb3b7cef075d95312bb61b12359f9
--- /dev/null
+++ b/src/main/java/teetime/framework/core/OutputPortImpl.java
@@ -0,0 +1,10 @@
+package teetime.framework.core;
+
+
+public class OutputPortImpl<S extends IStage, T> extends AbstractPort<S, T> implements IOutputPort<S, T> {
+
+	public OutputPortImpl(final S owningStage) {
+		this.setOwningStage(owningStage);
+	}
+
+}
diff --git a/src/main/java/teetime/framework/core/Pipeline.java b/src/main/java/teetime/framework/core/Pipeline.java
new file mode 100644
index 0000000000000000000000000000000000000000..7848fa0cdd3cbd9f36fe52cc7aa4447f00d8aec4
--- /dev/null
+++ b/src/main/java/teetime/framework/core/Pipeline.java
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.core;
+
+import java.util.List;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class Pipeline implements IPipeline {
+
+	private List<? extends IStage> startStages;
+	private List<IStage> stages;
+
+	public List<? extends IStage> getStartStages() {
+		return this.startStages;
+	}
+
+	public List<IStage> getStages() {
+		return this.stages;
+	}
+
+	public void fireStartNotification() throws Exception {
+		for (final IStage stage : this.getStartStages()) {
+			stage.notifyPipelineStarts();
+		}
+	}
+
+	public void fireStopNotification() {
+		for (final IStage stage : this.getStartStages()) {
+			stage.notifyPipelineStops();
+		}
+	}
+
+	public void setStartStages(final List<? extends IStage> startStages) {
+		this.startStages = startStages;
+	}
+
+	public void setStages(final List<IStage> stages) {
+		this.stages = stages;
+	}
+
+}
diff --git a/src/main/java/teetime/framework/sequential/MethodCallPipe.java b/src/main/java/teetime/framework/sequential/MethodCallPipe.java
new file mode 100644
index 0000000000000000000000000000000000000000..cff55296f1d5b4719230afae0b975f567becfc0a
--- /dev/null
+++ b/src/main/java/teetime/framework/sequential/MethodCallPipe.java
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.framework.sequential;
+
+import java.util.List;
+
+import teetime.framework.core.AbstractPipe;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.ISink;
+import teetime.framework.core.ISource;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class MethodCallPipe<T> extends AbstractPipe<T> {
+
+	private T storedToken;
+
+	public MethodCallPipe(final T initialToken) {
+		this.storedToken = initialToken;
+	}
+
+	public MethodCallPipe() {
+		this.storedToken = null;
+	}
+
+	static public <S0 extends ISource, S1 extends ISink<S1>, T> void connect(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) {
+		final MethodCallPipe<T> pipe = new MethodCallPipe<T>();
+		pipe.setSourcePort(sourcePort);
+		pipe.setTargetPort(targetPort);
+	}
+
+	@Override
+	protected void putInternal(final T token) {
+		this.storedToken = token;
+		this.getTargetPort().getOwningStage().execute();
+	}
+
+	@Override
+	protected T tryTakeInternal() {
+		final T temp = this.storedToken;
+		this.storedToken = null;
+		return temp;
+	}
+
+	public T take() {
+		return this.tryTake();
+	}
+
+	public T read() {
+		return this.storedToken;
+	}
+
+	public void putMultiple(final List<T> items) {
+		throw new IllegalStateException("Putting more than one element is not possible. You tried to put " + items.size() + " items.");
+	}
+
+	public List<?> tryTakeMultiple(final int numElementsToTake) {
+		throw new IllegalStateException("Taking more than one element is not possible. You tried to take " + numElementsToTake + " items.");
+	}
+
+	public void copyAllOtherPipes(final List<MethodCallPipe<T>> pipesOfGroup) {
+		// is not needed in a synchronous execution
+	}
+
+	public boolean isEmpty() {
+		return this.storedToken == null;
+	}
+
+}
diff --git a/src/main/java/teetime/framework/sequential/Pipeline.java.todo b/src/main/java/teetime/framework/sequential/Pipeline.java.todo
new file mode 100644
index 0000000000000000000000000000000000000000..9b95dc1904537338d4f6869054812c209ce15d65
--- /dev/null
+++ b/src/main/java/teetime/framework/sequential/Pipeline.java.todo
@@ -0,0 +1,190 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package kieker.panalysis.framework.sequential;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import kieker.panalysis.framework.core.AbstractFilter;
+import kieker.panalysis.framework.core.IInputPort;
+import kieker.panalysis.framework.core.IOutputPort;
+import kieker.panalysis.framework.core.IPipe;
+import kieker.panalysis.framework.core.ISink;
+import kieker.panalysis.framework.core.ISource;
+import kieker.panalysis.framework.core.IStage;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <P>
+ *            The type of the pipes
+ */
+public class Pipeline<P extends IPipe<?>> {
+
+	protected final List<IStage> stages = new LinkedList<IStage>();
+	private int freeId = 0;
+	private AbstractFilter<?>[] startStages;
+
+	private P currentPipe;
+	private final Map<Integer, List<P>> pipeGroups;
+	private final Map<IOutputPort<?, ?>, IInputPort<?, ?>> connections = new HashMap<IOutputPort<?, ?>, IInputPort<?, ?>>();
+
+	/**
+	 * The default constructor.<br>
+	 * <i>Public constructors are available via the static method <code>create(..)</code></i>
+	 * 
+	 * @see for example, {@link #create(Map)}
+	 * 
+	 * @since 1.10
+	 */
+	private Pipeline() {
+		this(new HashMap<Integer, List<P>>());
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private Pipeline(final Map<Integer, List<P>> pipeGroups) {
+		this.pipeGroups = pipeGroups;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	// this constructor prevents the programmer from repeating the type argument
+	public static <P extends IPipe<?>> Pipeline<P> create() {
+		return new Pipeline<P>();
+	}
+
+	/**
+	 * 
+	 * @param pipeGroups
+	 *            a map where its keys represent the group id and its values represent the list of pipes
+	 * @return
+	 */
+	// this constructor prevents the programmer from repeating the type argument
+	public static <P extends IPipe<?>> Pipeline<P> create(final Map<Integer, List<P>> pipeGroups) {
+		return new Pipeline<P>(pipeGroups);
+	}
+
+	/**
+	 * <p>
+	 * Adds the given <code>stage</code> to this pipeline and set a unique identifier.
+	 * </p>
+	 * <p>
+	 * Use this method as indicated in the following example: <blockquote> <code>Distributor distributor = pipeline.addStage(new Distributor())</code> </blockquote>
+	 * </p>
+	 * 
+	 * @param stage
+	 * @return
+	 */
+	public <S extends IStage> S addStage(final S stage) {
+		stage.setId(this.freeId++);
+		stage.setOwningPipeline(this);
+		this.stages.add(stage);
+		return stage;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void start() {
+		this.fireStartEvent();
+
+		if (this.startStages.length == 0) {
+			throw new IllegalStateException("You need to define at least one start stage.");
+		} else if (this.startStages.length == 1) {
+			// this.startStages.get(0).execute();
+		} else {
+			// TODO create a single distributor stage as start stage
+			throw new IllegalStateException("Multiple start stages are not yet supported.");
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private void fireStartEvent() {
+		for (final IStage stage : this.stages) {
+			stage.onPipelineStarts();
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public List<IStage> getStages() {
+		return this.stages;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public Pipeline<P> add(final P pipe) {
+		this.currentPipe = pipe;
+		return this;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public <S0 extends ISource, S1 extends ISink<S1>, T> void connect(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) {
+		this.connections.put(sourcePort, targetPort);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void toGroup(final int pipeGroupIdentifier) {
+		List<P> pipes = this.pipeGroups.get(pipeGroupIdentifier);
+		if (pipes == null) {
+			pipes = new LinkedList<P>();
+			this.pipeGroups.put(pipeGroupIdentifier, pipes);
+		}
+		pipes.add(this.currentPipe);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	// BETTER move this method out of the pipeline
+	public void connectPipeGroups() {
+		for (final List<P> samePipes : this.pipeGroups.values()) {
+			for (final P pipe : samePipes) {
+				pipe.copyAllOtherPipes(samePipes);
+			}
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void setStartStages(final AbstractFilter<?>... startStages) {
+		this.startStages = startStages;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public AbstractFilter<?>[] getStartStages() {
+		return this.startStages;
+	}
+}
diff --git a/src/main/java/teetime/framework/sequential/QueuePipe.java b/src/main/java/teetime/framework/sequential/QueuePipe.java
new file mode 100644
index 0000000000000000000000000000000000000000..8543ea1743c690d2c89b6f77f649b0ce9fa03d71
--- /dev/null
+++ b/src/main/java/teetime/framework/sequential/QueuePipe.java
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.sequential;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+import teetime.framework.core.AbstractPipe;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.ISink;
+import teetime.framework.core.ISource;
+import teetime.util.concurrent.workstealing.CircularWorkStealingDeque;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class QueuePipe<T> extends AbstractPipe<T> {
+
+	private final Queue<T> queue = new LinkedList<T>();
+
+	static public <S0 extends ISource, S1 extends ISink<S1>, T> void connect(final IOutputPort<S0, ? extends T> sourcePort, final IInputPort<S1, T> targetPort) {
+		final QueuePipe<T> pipe = new QueuePipe<T>();
+		pipe.setSourcePort(sourcePort);
+		pipe.setTargetPort(targetPort);
+	}
+
+	@Override
+	public void putInternal(final T element) {
+		this.queue.add(element);
+	}
+
+	public void putMultiple(final List<T> elements) {
+		this.queue.addAll(elements);
+	}
+
+	@Override
+	public T tryTakeInternal() {
+		return this.queue.poll();
+	}
+
+	public T take() {
+		final T element = this.tryTake();
+		if (element == null) {
+			throw CircularWorkStealingDeque.DEQUE_IS_EMPTY_EXCEPTION;
+		}
+		return element;
+	}
+
+	public T read() {
+		return this.queue.peek();
+	}
+
+	public List<?> tryTakeMultiple(final int numElementsToTake) {
+		throw new IllegalStateException("Taking more than one element is not possible. You tried to take " + numElementsToTake + " items.");
+	}
+
+	public boolean isEmpty() {
+		return this.queue.isEmpty();
+	}
+
+}
diff --git a/src/main/java/teetime/framework/util/BaseStage2StageExtractor.java b/src/main/java/teetime/framework/util/BaseStage2StageExtractor.java
new file mode 100644
index 0000000000000000000000000000000000000000..819de27425b06e3de16fc3bd6c3c9913220c8217
--- /dev/null
+++ b/src/main/java/teetime/framework/util/BaseStage2StageExtractor.java
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.framework.core.CompositeFilter;
+import teetime.framework.core.IBaseStage;
+import teetime.framework.core.IStage;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class BaseStage2StageExtractor {
+
+	public List<? extends IStage> extract(final IBaseStage baseStage) {
+		final List<IStage> stages = new LinkedList<IStage>();
+		this.extractLocally(baseStage, stages);
+		return stages;
+	}
+
+	private void extractLocally(final IBaseStage baseStage, final List<IStage> stages) {
+		if (baseStage instanceof IStage) {
+			stages.add((IStage) baseStage);
+		} else if (baseStage instanceof CompositeFilter) {
+			final List<IBaseStage> schedulableStages = ((CompositeFilter) baseStage).getSchedulableStages();
+			for (final IBaseStage childBaseStage : schedulableStages) {
+				this.extractLocally(childBaseStage, stages);
+			}
+		}
+	}
+
+}
diff --git a/src/main/java/teetime/framework/util/Cloner.java b/src/main/java/teetime/framework/util/Cloner.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8463dbe069039f3f865cdf0b4c4303f25d24cbd
--- /dev/null
+++ b/src/main/java/teetime/framework/util/Cloner.java
@@ -0,0 +1,128 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.framework.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A helper class to clone bean like objects with default constructors, getters, and setters.
+ * 
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public class Cloner {
+
+	private Cloner() {}
+
+	/**
+	 * Clones the given object by creating a new instance of the class. The method invokes all getter methods on the original and stores the resulting values in the
+	 * clone by invoking the corresponding setter methods. Properties without setter or getter methods are ignored. The resulting clone is always a new instance,
+	 * hence original != clone holds. It is assumed that the given instance provides a default constructor without any parameters.
+	 * 
+	 * @param original
+	 *            The original instance to clone.
+	 * 
+	 * @return A clone of the given instance.
+	 * 
+	 * @throws Exception
+	 *             If the given instance does not provide a default constructor, the getters or setters are not accessible, the getters and setters are not
+	 *             compatible, or any of the methods threw an exception.
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> T cloneObject(final T original) throws Exception {
+		// Comment for 1.7: The three exceptions, thrown by this called method, have the same parent exception since 1.7
+		final T clone = (T) original.getClass().newInstance();
+
+		Cloner.cloneAllProperties(original, clone);
+
+		return clone;
+	}
+
+	private static <T> void cloneAllProperties(final T original, final T clone) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+		final Collection<GetterSetterPair> getterSetterPairs = Cloner.getGetterSetterPairs(original.getClass());
+
+		for (final GetterSetterPair getterSetterPair : getterSetterPairs) {
+			final Object value = getterSetterPair.getGetter().invoke(original);
+			getterSetterPair.getSetter().invoke(clone, value);
+		}
+	}
+
+	private static Collection<GetterSetterPair> getGetterSetterPairs(final Class<?> clazz) {
+		final Collection<GetterSetterPair> properties = new ArrayList<GetterSetterPair>();
+
+		final Map<String, Method> foundGetters = new HashMap<String, Method>();
+		final Map<String, Method> foundSetters = new HashMap<String, Method>();
+
+		final Method[] methods = clazz.getMethods();
+		for (final Method method : methods) {
+			if (Cloner.isGetter(method)) {
+				final String propertyName = method.getName().substring(3);
+				if (foundSetters.containsKey(propertyName)) {
+					properties.add(new GetterSetterPair(method, foundSetters.remove(propertyName)));
+				} else {
+					foundGetters.put(propertyName, method);
+				}
+			} else if (Cloner.isSetter(method)) {
+				final String propertyName = method.getName().substring(3);
+				if (foundGetters.containsKey(propertyName)) {
+					properties.add(new GetterSetterPair(foundGetters.remove(propertyName), method));
+				} else {
+					foundSetters.put(propertyName, method);
+				}
+			}
+		}
+
+		return properties;
+	}
+
+	private static boolean isSetter(final Method method) {
+		return ((method.getReturnType() == Void.TYPE) && (method.getParameterTypes().length == 1) && (Modifier.isPublic(method.getModifiers())) && method.getName()
+				.matches("set[A-Z].*"));
+	}
+
+	private static boolean isGetter(final Method method) {
+		return ((method.getReturnType() != Void.TYPE) && (method.getParameterTypes().length == 0) && (Modifier.isPublic(method.getModifiers())) && method.getName()
+				.matches("get[A-Z].*"));
+	}
+
+	private static class GetterSetterPair {
+
+		private final Method getter;
+		private final Method setter;
+
+		public GetterSetterPair(final Method getter, final Method setter) {
+			this.getter = getter;
+			this.setter = setter;
+		}
+
+		public Method getGetter() {
+			return this.getter;
+		}
+
+		public Method getSetter() {
+			return this.setter;
+		}
+
+	}
+
+}
diff --git a/src/main/java/teetime/stage/Cache.java b/src/main/java/teetime/stage/Cache.java
new file mode 100644
index 0000000000000000000000000000000000000000..723f8cd90a9dc50be04f556d3d66354451193ffc
--- /dev/null
+++ b/src/main/java/teetime/stage/Cache.java
@@ -0,0 +1,90 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class Cache<T> extends AbstractFilter<Cache<T>> {
+
+	private static enum OperationMode {
+		CACHING, SENDING
+	}
+
+	private OperationMode operationMode = OperationMode.CACHING;
+
+	public final IInputPort<Cache<T>, T> objectInputPort = this.createInputPort();
+	public final IInputPort<Cache<T>, Boolean> sendInputPort = this.createInputPort();
+
+	public final IOutputPort<Cache<T>, T> objectOutputPort = this.createOutputPort();
+
+	private final List<T> cachedObjects = new LinkedList<T>();
+
+	@Override
+	protected boolean execute(final Context<Cache<T>> context) {
+		if (this.operationMode == OperationMode.CACHING) {
+			final T object = context.tryTake(this.objectInputPort);
+			if (object == null) {
+				final Boolean shouldSend = context.tryTake(this.sendInputPort);
+				if (shouldSend == null) {
+					return false;
+				} else {
+					this.operationMode = OperationMode.SENDING;
+				}
+			} else {
+				this.cache(object);
+			}
+		} else {
+			final boolean sent = this.send(context);
+			if (!sent) {
+				this.operationMode = OperationMode.CACHING;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private boolean send(final Context<Cache<T>> context) {
+		if (this.cachedObjects.size() > 0) {
+			final T cachedObject = this.cachedObjects.remove(0);
+			context.put(this.objectOutputPort, cachedObject);
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private void cache(final T object) {
+		this.cachedObjects.add(object);
+	}
+
+}
diff --git a/src/main/java/teetime/stage/Clock.java b/src/main/java/teetime/stage/Clock.java
new file mode 100644
index 0000000000000000000000000000000000000000..841a243870207f6ffcb3b0671793d08a8739c496
--- /dev/null
+++ b/src/main/java/teetime/stage/Clock.java
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class Clock extends AbstractFilter<Clock> {
+
+	public final IOutputPort<Clock, Long> timestampOutputPort = this.createOutputPort();
+
+	@Override
+	protected boolean execute(final Context<Clock> context) {
+		final long timestamp = this.getTimestamp();
+		context.put(this.timestampOutputPort, timestamp);
+		return true;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private long getTimestamp() {
+		return System.nanoTime();
+	}
+
+}
diff --git a/src/main/java/teetime/stage/CollectorSink.java b/src/main/java/teetime/stage/CollectorSink.java
new file mode 100644
index 0000000000000000000000000000000000000000..6920619509b3c0c6cdd00db39dfdcf3046865455
--- /dev/null
+++ b/src/main/java/teetime/stage/CollectorSink.java
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage;
+
+import java.util.Collection;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class CollectorSink<T> extends AbstractFilter<CollectorSink<T>> {
+
+	private static final int THRESHOLD = 10000;
+
+	public final IInputPort<CollectorSink<T>, T> objectInputPort = this.createInputPort();
+	private Collection<T> objects;
+
+	public CollectorSink(final Collection<T> collection) {
+		this.objects = collection;
+	}
+
+	public CollectorSink() {
+		super();
+	}
+
+	@Override
+	protected boolean execute(final Context<CollectorSink<T>> context) {
+		final T object = context.tryTake(this.objectInputPort);
+		if (object == null) {
+			return false;
+		}
+
+		this.objects.add(object);
+		if ((this.objects.size() % THRESHOLD) == 0) {
+			System.out.println("size: " + this.objects.size());
+		}
+
+		return true;
+	}
+
+	public Collection<T> getObjects() {
+		return this.objects;
+	}
+
+	public void setObjects(final Collection<T> objects) {
+		this.objects = objects;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/CountingFilter.java b/src/main/java/teetime/stage/CountingFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..018227accc244f3b36f4229ef211b052d45397c8
--- /dev/null
+++ b/src/main/java/teetime/stage/CountingFilter.java
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class CountingFilter<T> extends AbstractFilter<CountingFilter<T>> {
+
+	public final IInputPort<CountingFilter<T>, T> INPUT_OBJECT = this.createInputPort();
+	public final IInputPort<CountingFilter<T>, Long> CURRENT_COUNT = this.createInputPort();
+
+	public final IOutputPort<CountingFilter<T>, T> RELAYED_OBJECT = this.createOutputPort();
+	public final IOutputPort<CountingFilter<T>, Long> NEW_COUNT = this.createOutputPort();
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<CountingFilter<T>> context) {
+		final T inputObject = context.tryTake(this.INPUT_OBJECT);
+		if (inputObject == null) {
+			return false;
+		}
+		final Long currentCount = context.tryTake(this.CURRENT_COUNT);
+		if (currentCount == null) {
+			return false;
+		}
+
+		context.put(this.RELAYED_OBJECT, inputObject);
+		// FIXME using a MethodCallPipe leads to a stackoverflow since the value (currentCount+1) is put and taken infinitely
+		context.put(this.NEW_COUNT, currentCount + 1); // BETTER support pipes with primitive values to improve performance by avoiding auto-boxing
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/FileExtensionFilter.java b/src/main/java/teetime/stage/FileExtensionFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..329c3e72cdb74e03b514179ff199c7047144cb1b
--- /dev/null
+++ b/src/main/java/teetime/stage/FileExtensionFilter.java
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.regex.Pattern;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class FileExtensionFilter extends AbstractFilter<FileExtensionFilter> {
+
+	public final IInputPort<FileExtensionFilter, File> fileInputPort = this.createInputPort();
+
+	public final IOutputPort<FileExtensionFilter, File> unknownFileExtensionOutputPort = this.createOutputPort();
+
+	private final HashMap<String, IOutputPort<FileExtensionFilter, File>> fileExtensionMap = new HashMap<String, IOutputPort<FileExtensionFilter, File>>();
+
+	private static final Pattern DOT_PATTERN = Pattern.compile("\\.");
+
+	@Override
+	protected boolean execute(final Context<FileExtensionFilter> context) {
+		final File file = context.tryTake(this.fileInputPort);
+		if (file == null) {
+			return false;
+		}
+
+		final String[] filenameParts = DOT_PATTERN.split(file.getName());
+		final String fileExtension = filenameParts[filenameParts.length - 1];
+		final IOutputPort<FileExtensionFilter, File> outputPort = this.fileExtensionMap.get(fileExtension);
+		if (outputPort != null) {
+			context.put(outputPort, file);
+		} else {
+			context.put(this.unknownFileExtensionOutputPort, file);
+		}
+
+		return true;
+	}
+
+	/**
+	 * 
+	 * @param fileExtension
+	 *            the file extension (<i>a leading dot is removed automatically)</i>
+	 * @return
+	 */
+	public IOutputPort<FileExtensionFilter, File> createOutputPortForFileExtension(String fileExtension) {
+		if (fileExtension.startsWith(".")) {
+			fileExtension = fileExtension.substring(1);
+		}
+		IOutputPort<FileExtensionFilter, File> outputPort = this.fileExtensionMap.get(fileExtension);
+		if (outputPort == null) {
+			outputPort = super.createOutputPort();
+			this.fileExtensionMap.put(fileExtension, outputPort);
+		}
+		return outputPort;
+	}
+}
diff --git a/src/main/java/teetime/stage/InstanceOfFilter.java b/src/main/java/teetime/stage/InstanceOfFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fdb4f13196eb8f3c9fe92e88c82762d14fd6692
--- /dev/null
+++ b/src/main/java/teetime/stage/InstanceOfFilter.java
@@ -0,0 +1,53 @@
+package teetime.stage;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Jan Waller, Nils Christian Ehmke, Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class InstanceOfFilter<I, O> extends AbstractFilter<InstanceOfFilter<I, O>> {
+
+	public final IInputPort<InstanceOfFilter<I, O>, I> inputPort = this.createInputPort();
+
+	public final IOutputPort<InstanceOfFilter<I, O>, O> matchingOutputPort = this.createOutputPort();
+	public final IOutputPort<InstanceOfFilter<I, O>, I> mismatchingOutputPort = this.createOutputPort();
+
+	private Class<O> type;
+
+	/**
+	 * @since 1.10
+	 */
+	public InstanceOfFilter(final Class<O> type) {
+		this.type = type;
+	}
+
+	@Override
+	protected boolean execute(final Context<InstanceOfFilter<I, O>> context) {
+		final I inputObject = context.tryTake(this.inputPort);
+		if (inputObject == null) {
+			return false;
+		}
+
+		if (this.type.isInstance(inputObject)) {
+			context.put(this.matchingOutputPort, this.type.cast(inputObject));
+		} else {
+			context.put(this.mismatchingOutputPort, inputObject);
+		}
+
+		return true;
+	}
+
+	public Class<O> getType() {
+		return this.type;
+	}
+
+	public void setType(final Class<O> type) {
+		this.type = type;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/MappingException.java b/src/main/java/teetime/stage/MappingException.java
new file mode 100644
index 0000000000000000000000000000000000000000..49fd0fc3931185ad5d7ec073a4059ecb24923197
--- /dev/null
+++ b/src/main/java/teetime/stage/MappingException.java
@@ -0,0 +1,32 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class MappingException extends Exception {
+
+	private static final long serialVersionUID = 7300752837946139350L;
+
+	public MappingException(final String text) {
+		super(text);
+	}
+
+}
diff --git a/src/main/java/teetime/stage/NoopFilter.java b/src/main/java/teetime/stage/NoopFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..de1b31b445bbb1d646f3b8f1b64b95c1510bd351
--- /dev/null
+++ b/src/main/java/teetime/stage/NoopFilter.java
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class NoopFilter<T> extends AbstractFilter<NoopFilter<T>> {
+
+	public final IInputPort<NoopFilter<T>, T> inputPort = this.createInputPort();
+	public final IOutputPort<NoopFilter<T>, T> outputPort = this.createOutputPort();
+
+	@Override
+	protected boolean execute(final Context<NoopFilter<T>> context) {
+		final T inputElement = context.tryTake(this.inputPort);
+		if (inputElement == null) {
+			return false;
+		}
+
+		context.put(this.outputPort, inputElement);
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/StartTimestampFilter.java b/src/main/java/teetime/stage/StartTimestampFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..327e5e19af0f43f7d782e0050212d1bb90e76228
--- /dev/null
+++ b/src/main/java/teetime/stage/StartTimestampFilter.java
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage;
+
+import teetime.examples.throughput.TimestampObject;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class StartTimestampFilter extends AbstractFilter<StartTimestampFilter> {
+
+	public final IInputPort<StartTimestampFilter, TimestampObject> inputPort = this.createInputPort();
+	public final IOutputPort<StartTimestampFilter, TimestampObject> outputPort = this.createOutputPort();
+
+	@Override
+	protected boolean execute(final Context<StartTimestampFilter> context) {
+		final TimestampObject inputObject = context.tryTake(this.inputPort);
+		if (inputObject == null) {
+			return false;
+		}
+
+		inputObject.setStartTimestamp(System.nanoTime());
+
+		context.put(this.outputPort, inputObject);
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/StopTimestampFilter.java b/src/main/java/teetime/stage/StopTimestampFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc65ca63eb57ef2c0756ff27c904c962f0c0850e
--- /dev/null
+++ b/src/main/java/teetime/stage/StopTimestampFilter.java
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage;
+
+import teetime.examples.throughput.TimestampObject;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class StopTimestampFilter extends AbstractFilter<StopTimestampFilter> {
+
+	public final IInputPort<StopTimestampFilter, TimestampObject> inputPort = this.createInputPort();
+	public final IOutputPort<StopTimestampFilter, TimestampObject> outputPort = this.createOutputPort();
+
+	@Override
+	protected boolean execute(final Context<StopTimestampFilter> context) {
+		final TimestampObject inputObject = context.tryTake(this.inputPort);
+		if (inputObject == null) {
+			return false;
+		}
+
+		inputObject.setStopTimestamp(System.nanoTime());
+
+		context.put(this.outputPort, inputObject);
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/SuperTypeFilter.java b/src/main/java/teetime/stage/SuperTypeFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..8482233c262af42148d2ab4c54cca3d80e7ced45
--- /dev/null
+++ b/src/main/java/teetime/stage/SuperTypeFilter.java
@@ -0,0 +1,23 @@
+package teetime.stage;
+
+import java.util.Collection;
+
+import teetime.stage.predicate.IsSuperTypePredicate;
+import teetime.stage.predicate.PredicateFilter;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class SuperTypeFilter<T> extends PredicateFilter<T> {
+
+	/**
+	 * @param acceptedClasses
+	 * @since 1.10
+	 */
+	public SuperTypeFilter(final Collection<Class<?>> acceptedClasses) {
+		super(new IsSuperTypePredicate<T>(acceptedClasses));
+	}
+
+}
diff --git a/src/main/java/teetime/stage/TypeLoggerFilter.java b/src/main/java/teetime/stage/TypeLoggerFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..304579a52300a3b1d17eca69f9792f7ce517850a
--- /dev/null
+++ b/src/main/java/teetime/stage/TypeLoggerFilter.java
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage;
+
+import java.io.PrintStream;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Matthias Rohr, Jan Waller, Nils Christian Ehmke, Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class TypeLoggerFilter<T> extends AbstractFilter<TypeLoggerFilter<T>> {
+
+	public final IInputPort<TypeLoggerFilter<T>, T> INPUT_OBJECT = this.createInputPort();
+
+	public final IOutputPort<TypeLoggerFilter<T>, T> RELAYED_OBJECT = this.createOutputPort();
+
+	private final PrintStream printStream;
+
+	/**
+	 * @since 1.10
+	 */
+	private TypeLoggerFilter() {
+		this.printStream = System.out;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public static <T> TypeLoggerFilter<T> create() {
+		return new TypeLoggerFilter<T>();
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<TypeLoggerFilter<T>> context) {
+		final T inputObject = context.tryTake(this.INPUT_OBJECT);
+		if (inputObject == null) {
+			return false;
+		}
+
+		final StringBuilder sb = new StringBuilder(128);
+		sb.append(super.getId()).append('(').append(inputObject.getClass().getSimpleName()).append(") ").append(inputObject.toString());
+		this.printStream.println(sb.toString());
+
+		context.put(this.RELAYED_OBJECT, inputObject);
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/basic/Delay.java b/src/main/java/teetime/stage/basic/Delay.java
new file mode 100644
index 0000000000000000000000000000000000000000..d878e5f1a357d5643be324f587d77aa93a51b6e4
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/Delay.java
@@ -0,0 +1,73 @@
+package teetime.stage.basic;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <T>
+ */
+public class Delay<T> extends AbstractFilter<Delay<T>> {
+
+	public final IInputPort<Delay<T>, T> INPUT_OBJECT = this.createInputPort();
+
+	public final IOutputPort<Delay<T>, T> RELAYED_OBJECT = this.createOutputPort();
+
+	private long initialDelayInNs;
+	private long intervalDelayInNs;
+
+	private boolean initialDelayExceeded = false;
+	private long lastTimeInNs = this.getCurrentTimeInNs();
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<Delay<T>> context) {
+		final long passedTimeInNs = this.getCurrentTimeInNs() - this.lastTimeInNs;
+		final long thresholdInNs = (this.initialDelayExceeded) ? this.intervalDelayInNs : this.initialDelayInNs;
+
+		if (passedTimeInNs >= thresholdInNs) {
+			final T token = context.tryTake(this.INPUT_OBJECT);
+			if (token == null) {
+				return false;
+			}
+
+			this.lastTimeInNs += thresholdInNs;
+			context.put(this.RELAYED_OBJECT, token);
+
+			this.initialDelayExceeded = true;
+		}
+
+		return true;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	private long getCurrentTimeInNs() {
+		return System.nanoTime();
+	}
+
+	public long getInitialDelayInNs() {
+		return initialDelayInNs;
+	}
+
+	public void setInitialDelayInNs(long initialDelayInNs) {
+		this.initialDelayInNs = initialDelayInNs;
+	}
+
+	public long getIntervalDelayInNs() {
+		return intervalDelayInNs;
+	}
+
+	public void setIntervalDelayInNs(long intervalDelayInNs) {
+		this.intervalDelayInNs = intervalDelayInNs;
+	}
+}
diff --git a/src/main/java/teetime/stage/basic/ObjectProducer.java b/src/main/java/teetime/stage/basic/ObjectProducer.java
new file mode 100644
index 0000000000000000000000000000000000000000..c94c639789ef0d84d0254a702083333a64000d0a
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/ObjectProducer.java
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.basic;
+
+import java.util.concurrent.Callable;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ObjectProducer<T> extends AbstractFilter<ObjectProducer<T>> {
+
+	public final IOutputPort<ObjectProducer<T>, T> outputPort = this.createOutputPort();
+
+	private Callable<T> objectCreator;
+	private long numObjectsToCreate;
+
+	/**
+	 * @since 1.10
+	 */
+	public ObjectProducer(final long numObjectsToCreate, final Callable<T> objectCreator) {
+		this.numObjectsToCreate = numObjectsToCreate;
+		this.objectCreator = objectCreator;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public ObjectProducer() {
+		super();
+	}
+
+	@Override
+	protected boolean execute(final Context<ObjectProducer<T>> context) {
+		if (this.numObjectsToCreate == 0) {
+			return false;
+		}
+
+		try {
+			final T newObject = this.objectCreator.call();
+			context.put(this.outputPort, newObject);
+		} catch (final Exception e) {
+			throw new IllegalStateException();
+		}
+
+		this.numObjectsToCreate--;
+
+		return true;
+	}
+
+	public Callable<T> getObjectCreator() {
+		return this.objectCreator;
+	}
+
+	public void setObjectCreator(final Callable<T> objectCreator) {
+		this.objectCreator = objectCreator;
+	}
+
+	public long getNumObjectsToCreate() {
+		return this.numObjectsToCreate;
+	}
+
+	public void setNumObjectsToCreate(final long numObjectsToCreate) {
+		this.numObjectsToCreate = numObjectsToCreate;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/basic/RepeaterSource.java b/src/main/java/teetime/stage/basic/RepeaterSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..495befe81504a91aa18ec5e53794fff1725ce852
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/RepeaterSource.java
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.basic;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class RepeaterSource<T> extends AbstractFilter<RepeaterSource<T>> {
+
+	public final IInputPort<RepeaterSource<T>, Boolean> START = this.createInputPort();
+
+	public final IOutputPort<RepeaterSource<T>, T> OUTPUT = this.createOutputPort();
+
+	private final T outputRecord;
+	private final int num;
+
+	/**
+	 * @since 1.10
+	 * @param outputRecord
+	 * @param num
+	 */
+	private RepeaterSource(final T outputRecord, final int num) {
+		this.outputRecord = outputRecord;
+		this.num = num;
+	}
+
+	/**
+	 * @since 1.10
+	 * @param outputRecord
+	 * @param num
+	 * @return
+	 */
+	// this constructor prevents the programmer from repeating the type argument
+	public static <T> RepeaterSource<T> create(final T outputRecord, final int num) {
+		return new RepeaterSource<T>(outputRecord, num);
+	}
+
+	@Override
+	protected boolean execute(final Context<RepeaterSource<T>> context) {
+		final Boolean token = context.tryTake(this.START);
+		if (token == null) {
+			return false;
+		}
+
+		// TODO consider to put only one record per iteration and decrement the num directly
+		int counter = this.num;
+		while (counter-- > 0) {
+			context.put(this.OUTPUT, this.outputRecord);
+		}
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/basic/distributor/CloneStrategy.java b/src/main/java/teetime/stage/basic/distributor/CloneStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1687a071c77682a8d40397e18be889fefe66b32
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/distributor/CloneStrategy.java
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.basic.distributor;
+
+import java.util.List;
+
+import teetime.framework.core.Context;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public final class CloneStrategy<T> implements IDistributorStrategy<T> {
+
+	public <S extends Distributor<T>> boolean distribute(final Context<S> context, final List<IOutputPort<S, ?>> outputPorts, final T input) {
+		throw new UnsupportedOperationException();
+	}
+
+}
diff --git a/src/main/java/teetime/stage/basic/distributor/CopyByReferenceStrategy.java b/src/main/java/teetime/stage/basic/distributor/CopyByReferenceStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..928f99f759a46b74ec03f6fc56f23a4eebfae005
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/distributor/CopyByReferenceStrategy.java
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.basic.distributor;
+
+import java.util.List;
+
+import teetime.framework.core.Context;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public final class CopyByReferenceStrategy<T> implements IDistributorStrategy<T> {
+
+	@SuppressWarnings("unchecked")
+	public <S extends Distributor<T>> boolean distribute(final Context<S> context, final List<IOutputPort<S, ?>> outputPorts, final T input) {
+		for (final IOutputPort<S, ?> port : outputPorts) {
+			context.put((IOutputPort<S, T>) port, input);
+		}
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/basic/distributor/Distributor.java b/src/main/java/teetime/stage/basic/distributor/Distributor.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b0a8ea74aaec0cdce7596a24ac72bc786b274be
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/distributor/Distributor.java
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.basic.distributor;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param T
+ *            the type of the input port and the output ports
+ */
+public class Distributor<T> extends AbstractFilter<Distributor<T>> {
+
+	public final IInputPort<Distributor<T>, T> genericInputPort = this.createInputPort();
+
+	private IDistributorStrategy<T> strategy = new RoundRobinStrategy<T>();
+
+	public IDistributorStrategy<T> getStrategy() {
+		return this.strategy;
+	}
+
+	public void setStrategy(final IDistributorStrategy<T> strategy) {
+		this.strategy = strategy;
+	}
+
+	@Override
+	protected boolean execute(final Context<Distributor<T>> context) {
+		final T object = context.tryTake(this.genericInputPort);
+		if (object == null) {
+			return false;
+		}
+
+		return this.strategy.distribute(context, this.getOutputPorts(), object);
+	}
+
+	public IOutputPort<Distributor<T>, T> getNewOutputPort() {
+		final IOutputPort<Distributor<T>, T> newOutputPort = this.createOutputPort();
+		return newOutputPort;
+	}
+}
diff --git a/src/main/java/teetime/stage/basic/distributor/IDistributorStrategy.java b/src/main/java/teetime/stage/basic/distributor/IDistributorStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb35923582fef0822a1e403e120c52038d69cc45
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/distributor/IDistributorStrategy.java
@@ -0,0 +1,32 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.basic.distributor;
+
+import java.util.List;
+
+import teetime.framework.core.Context;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public interface IDistributorStrategy<T> {
+
+	public <S extends Distributor<T>> boolean distribute(final Context<S> context, final List<IOutputPort<S, ?>> allOutputPorts, final T input);
+
+}
diff --git a/src/main/java/teetime/stage/basic/distributor/RoundRobinStrategy.java b/src/main/java/teetime/stage/basic/distributor/RoundRobinStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..028e4f82e4282c05100f51bd791686317d29c6fe
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/distributor/RoundRobinStrategy.java
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.basic.distributor;
+
+import java.util.List;
+
+import teetime.framework.core.Context;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public final class RoundRobinStrategy<T> implements IDistributorStrategy<T> {
+
+	private int index = 0;
+
+	public <S extends Distributor<T>> boolean distribute(final Context<S> context, final List<IOutputPort<S, ?>> outputPorts, final T object) {
+		final IOutputPort<S, T> port = this.getNextPortInRoundRobinOrder(outputPorts);
+		context.put(port, object);
+
+		return true;
+	}
+
+	@SuppressWarnings("unchecked")
+	private <S extends Distributor<T>> IOutputPort<S, T> getNextPortInRoundRobinOrder(final List<IOutputPort<S, ?>> outputPorts) {
+		final IOutputPort<S, T> port = (IOutputPort<S, T>) outputPorts.get(this.index);
+
+		this.index = (this.index + 1) % outputPorts.size();
+
+		return port;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/basic/merger/IMergerStrategy.java b/src/main/java/teetime/stage/basic/merger/IMergerStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..390679b6b3d12abc76c7bd187c56c7f403daa5e6
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/merger/IMergerStrategy.java
@@ -0,0 +1,32 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.basic.merger;
+
+import java.util.List;
+
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public interface IMergerStrategy<T> {
+
+	public <S extends Merger<T>> T getNextInput(final Context<S> context, final List<IInputPort<S, ?>> inputPorts);
+
+}
diff --git a/src/main/java/teetime/stage/basic/merger/Merger.java b/src/main/java/teetime/stage/basic/merger/Merger.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a26fecb455117a657a606ec834a7c7875192be5
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/merger/Merger.java
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.basic.merger;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.Description;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <T>
+ *            the type of the input ports and the output port
+ */
+@Description("This stage merges data from the input ports, by taking elements according to the chosen merge strategy and by putting them to the output port.")
+public class Merger<T> extends AbstractFilter<Merger<T>> {
+
+	public final IOutputPort<Merger<T>, T> outputPort = this.createOutputPort();
+
+	private IMergerStrategy<T> strategy = new RoundRobinStrategy<T>();
+
+	public IMergerStrategy<T> getStrategy() {
+		return this.strategy;
+	}
+
+	public void setStrategy(final IMergerStrategy<T> strategy) {
+		this.strategy = strategy;
+	}
+
+	@Override
+	protected boolean execute(final Context<Merger<T>> context) {
+		final T token = this.strategy.getNextInput(context, this.getInputPorts());
+		if (token == null) {
+			return false;
+		}
+
+		context.put(this.outputPort, token);
+		return true;
+	}
+
+	public IInputPort<Merger<T>, T> getNewInputPort() {
+		final IInputPort<Merger<T>, T> newInputPort = this.createInputPort();
+		return newInputPort;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/basic/merger/RoundRobinStrategy.java b/src/main/java/teetime/stage/basic/merger/RoundRobinStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..db8c674f0e27f2340bd57bafc0dd61ab4fa9b26f
--- /dev/null
+++ b/src/main/java/teetime/stage/basic/merger/RoundRobinStrategy.java
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.basic.merger;
+
+import java.util.List;
+
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public final class RoundRobinStrategy<T> implements IMergerStrategy<T> {
+
+	private int index = 0;
+
+	public <S extends Merger<T>> T getNextInput(final Context<S> context, final List<IInputPort<S, ?>> inputPorts) {
+		int size = inputPorts.size();
+		// check each port at most once to avoid a potentially infinite loop
+		while (size-- > 0) {
+			final IInputPort<S, T> inputPort = this.getNextPortInRoundRobinOrder(inputPorts);
+			final T token = context.tryTake(inputPort);
+			if (token != null) {
+				return token;
+			}
+		}
+		return null;
+	}
+
+	@SuppressWarnings("unchecked")
+	private <S extends Merger<T>> IInputPort<S, T> getNextPortInRoundRobinOrder(final List<IInputPort<S, ?>> inputPorts) {
+		final IInputPort<S, T> port = (IInputPort<S, T>) inputPorts.get(this.index);
+
+		this.index = (this.index + 1) % inputPorts.size();
+
+		return port;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/composite/CycledCountingFilter.java b/src/main/java/teetime/stage/composite/CycledCountingFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab715eefa2437a3bc234455a66e77e68e3f44011
--- /dev/null
+++ b/src/main/java/teetime/stage/composite/CycledCountingFilter.java
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.composite;
+
+import teetime.framework.core.IPipe;
+import teetime.stage.CountingFilter;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class CycledCountingFilter<T> extends CountingFilter<T> {
+
+	// BETTER hide the internal ports; the following uncommented lines do not work however
+	// private final IInputPort<CountingFilter<T>, Long> CURRENT_COUNT = this.createInputPort();
+	// private final IOutputPort<CountingFilter<T>, Long> NEW_COUNT = this.createOutputPort();
+
+	/**
+	 * @since 1.10
+	 * @param countingPipe
+	 */
+	// FIXME a non-default constructor is not allowed
+	private CycledCountingFilter(final IPipe<Long> countingPipe) {
+		countingPipe.setSourcePort(this.NEW_COUNT);
+		countingPipe.setTargetPort(this.CURRENT_COUNT);
+		// FIXME counting pipe needs to be added to a group
+	}
+
+	// this constructor prevents the programmer from repeating the type argument
+	public static <T> CycledCountingFilter<T> create(final IPipe<Long> countingPipe) {
+		return new CycledCountingFilter<T>(countingPipe);
+	}
+
+	/**
+	 * @since 1.10
+	 * @return
+	 */
+	public Long getCurrentCount() {
+		return this.getContext().read(this.CURRENT_COUNT);
+	}
+
+}
diff --git a/src/main/java/teetime/stage/composite/ReadRecordFromCsvFileFilter.java b/src/main/java/teetime/stage/composite/ReadRecordFromCsvFileFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc9e397ab56cef8978d5a6043d6ec105deebd597
--- /dev/null
+++ b/src/main/java/teetime/stage/composite/ReadRecordFromCsvFileFilter.java
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.composite;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IPipe;
+import teetime.stage.io.File2TextLinesFilter;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+import teetime.stage.kieker.fileToRecord.textLine.TextLine2RecordFilter;
+import teetime.stage.util.TextLine;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+// FIXME extend from CompositeStage
+public class ReadRecordFromCsvFileFilter extends AbstractFilter<File2TextLinesFilter> {
+
+	private final File2TextLinesFilter stage0;
+	private final TextLine2RecordFilter stage1;
+
+	/**
+	 * @since 1.10
+	 * @param textLinePipe
+	 */
+	public ReadRecordFromCsvFileFilter(final IPipe<TextLine> textLinePipe, final ClassNameRegistryRepository classNameRegistryRepository) {
+		this.stage0 = new File2TextLinesFilter();
+		this.stage1 = new TextLine2RecordFilter(classNameRegistryRepository);
+
+		textLinePipe.setSourcePort(this.stage0.textLineOutputPort);
+		textLinePipe.setTargetPort(this.stage1.textLineInputPort);
+		// FIXME textLinePipe needs to be added to a group
+
+		/*
+		 * FIXME a composite filter only serves as a convenient way to connect multiple stages or to realize self-connected queues.<br>
+		 * How should the stage scheduler be aware of these internal stages?<br>
+		 * Should the context not be passed to each of the internal stages?
+		 */
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<File2TextLinesFilter> context) {
+		return this.stage0.execute();
+	}
+
+}
diff --git a/src/main/java/teetime/stage/composite/TeeFilter.java b/src/main/java/teetime/stage/composite/TeeFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a249edec4ecd4b5e0ff4f0a540d9cc2ae531ff54
--- /dev/null
+++ b/src/main/java/teetime/stage/composite/TeeFilter.java
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.composite;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IOutputPort;
+import teetime.framework.core.IPipe;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.stage.basic.distributor.Distributor;
+import teetime.stage.io.Printer;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public class TeeFilter<T> extends AbstractFilter<TeeFilter<T>> {
+
+	private final Distributor<T> stage0;
+	private final Printer<T> stage1;
+
+	public TeeFilter(final IPipe<T> pipe) {
+		this.stage0 = new Distributor<T>();
+		this.stage1 = new Printer<T>();
+
+		final IOutputPort<Distributor<T>, T> outputPort = this.stage0.getNewOutputPort();
+
+		pipe.setSourcePort(outputPort);
+		pipe.setTargetPort(this.stage0.genericInputPort);
+
+		final IPipe<T> internalPipeline = new MethodCallPipe<T>();
+		internalPipeline.setSourcePort(outputPort);
+		internalPipeline.setTargetPort(this.stage1.input);
+	}
+
+	@Override
+	protected boolean execute(final Context<TeeFilter<T>> context) {
+		return this.stage0.execute();
+	}
+
+}
diff --git a/src/main/java/teetime/stage/io/DbReader.java b/src/main/java/teetime/stage/io/DbReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..e252b7dfe7658d52c8d55665b66285b181d29925
--- /dev/null
+++ b/src/main/java/teetime/stage/io/DbReader.java
@@ -0,0 +1,186 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.io;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import teetime.common.exception.MonitoringRecordException;
+import teetime.common.record.AbstractMonitoringRecord;
+import teetime.common.record.IMonitoringRecord;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.Description;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * A very simple database reader that probably only works for small data sets.
+ * 
+ * @author Jan Waller, Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+@Description("A reader which reads records from a database")
+public class DbReader extends AbstractFilter<DbReader> {
+
+	private final IOutputPort<DbReader, IMonitoringRecord> outputPort = super.createOutputPort();
+
+	@Description("The classname of the driver used for the connection.")
+	private String driverClassname = "org.apache.derby.jdbc.EmbeddedDrive";
+	@Description("The connection string used to establish the connection.")
+	private String connectionString = "jdbc:derby:tmp/KIEKER;user=DBUSER;password=DBPASS";
+	@Description("The prefix of the used table within the database.")
+	private String tablePrefix = "kieker";
+
+	private volatile boolean running = true;
+
+	@Override
+	public void onPipelineStarts() throws Exception {
+		super.onPipelineStarts();
+		try {
+			Class.forName(this.driverClassname).newInstance();
+		} catch (final Exception ex) { // NOPMD NOCS (IllegalCatchCheck)
+			throw new Exception("DB driver registration failed. Perhaps the driver jar is missing?", ex);
+		}
+	}
+
+	@Override
+	public void onPipelineStops() {
+		super.logger.info("Shutdown of DBReader requested.");
+		this.running = false;
+		super.onPipelineStops();
+	}
+
+	@Override
+	protected boolean execute(final Context<DbReader> context) {
+		Connection connection = null;
+		try {
+			connection = DriverManager.getConnection(this.connectionString);
+			Statement getIndexTable = null;
+			try {
+				getIndexTable = connection.createStatement();
+				ResultSet indexTable = null;
+				try { // NOCS (nested try)
+					indexTable = getIndexTable.executeQuery("SELECT * from " + this.tablePrefix);
+					while (this.running && indexTable.next()) {
+						final String tablename = indexTable.getString(1);
+						final String classname = indexTable.getString(2);
+						try { // NOCS (nested try)
+							this.table2record(context, connection, tablename, AbstractMonitoringRecord.classForName(classname));
+						} catch (final MonitoringRecordException ex) {
+							// log error but continue with next table
+							super.logger.error("Failed to load records of type " + classname + " from table " + tablename, ex);
+							continue;
+						}
+					}
+				} finally {
+					if (indexTable != null) {
+						indexTable.close();
+					}
+				}
+			} finally {
+				if (getIndexTable != null) {
+					getIndexTable.close();
+				}
+			}
+		} catch (final SQLException ex) {
+			super.logger.error("SQLException with SQLState: '" + ex.getSQLState() + "' and VendorError: '" + ex.getErrorCode() + "'", ex);
+			return false;
+		} finally {
+			if (connection != null) {
+				try {
+					connection.close();
+				} catch (final SQLException ex) {
+					super.logger.error("SQLException with SQLState: '" + ex.getSQLState() + "' and VendorError: '" + ex.getErrorCode() + "'", ex);
+				}
+			}
+		}
+		return true;
+	}
+
+	public final String getDriverClassname() {
+		return this.driverClassname;
+	}
+
+	public final void setDriverClassname(final String driverClassname) {
+		this.driverClassname = driverClassname;
+	}
+
+	public final String getConnectionString() {
+		return this.connectionString;
+	}
+
+	public final void setConnectionString(final String connectionString) {
+		this.connectionString = connectionString;
+	}
+
+	public final String getTablePrefix() {
+		return this.tablePrefix;
+	}
+
+	public final void setTablePrefix(final String tablePrefix) {
+		this.tablePrefix = tablePrefix;
+	}
+
+	/**
+	 * This method uses the given table to read records and sends them to the output port.
+	 * 
+	 * @param connection
+	 *            The connection to the database which will be used.
+	 * @param tablename
+	 *            The name of the table containing records.
+	 * @param clazz
+	 *            The class of the monitoring records. This will be used to convert the array into the record.
+	 * @throws SQLException
+	 *             If something went wrong during the database access.
+	 * @throws MonitoringRecordException
+	 *             If the data within the table could not be converted into a valid record.
+	 */
+	private void table2record(final Context<DbReader> context, final Connection connection, final String tablename, final Class<? extends IMonitoringRecord> clazz)
+			throws SQLException, MonitoringRecordException {
+		Statement selectRecord = null;
+		try {
+			selectRecord = connection.createStatement();
+			ResultSet records = null;
+			try {
+				records = selectRecord.executeQuery("SELECT * from " + tablename);
+				final int size = records.getMetaData().getColumnCount() - 2; // remove index column
+				while (this.running && records.next()) {
+					final Object[] recordValues = new Object[size];
+					for (int i = 0; i < size; i++) {
+						recordValues[i] = records.getObject(i + 3);
+					}
+					final IMonitoringRecord record = AbstractMonitoringRecord.createFromArray(clazz, recordValues);
+					record.setLoggingTimestamp(records.getLong(2));
+					context.put(this.outputPort, record);
+				}
+			} finally {
+				if (records != null) {
+					records.close();
+				}
+			}
+		} finally {
+			if (selectRecord != null) {
+				selectRecord.close();
+			}
+		}
+	}
+
+}
diff --git a/src/main/java/teetime/stage/io/Directory2FilesFilter.java b/src/main/java/teetime/stage/io/Directory2FilesFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..d74fa6e18f4d703d91529bad27bb6e45b6ca36d4
--- /dev/null
+++ b/src/main/java/teetime/stage/io/Directory2FilesFilter.java
@@ -0,0 +1,116 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.io;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class Directory2FilesFilter extends AbstractFilter<Directory2FilesFilter> {
+
+	public final IInputPort<Directory2FilesFilter, File> directoryInputPort = this.createInputPort();
+
+	public final IOutputPort<Directory2FilesFilter, File> fileOutputPort = this.createOutputPort();
+
+	private FileFilter filter;
+	private Comparator<File> fileComparator;
+
+	/**
+	 * @since 1.10
+	 */
+	public Directory2FilesFilter(final FileFilter fileFilter) {
+		this.setFilter(fileFilter);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public Directory2FilesFilter(final Comparator<File> fileComparator) {
+		this.setFileComparator(fileComparator);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public Directory2FilesFilter(final FileFilter fileFilter, final Comparator<File> fileComparator) {
+		this.setFilter(fileFilter);
+		this.setFileComparator(fileComparator);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public Directory2FilesFilter() {
+		super();
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<Directory2FilesFilter> context) {
+		final File inputDir = context.tryTake(this.directoryInputPort);
+		if (inputDir == null) {
+			return false;
+		}
+
+		final File[] inputFiles = inputDir.listFiles(this.filter);
+
+		if (inputFiles == null) {
+			this.logger.error("Directory '" + inputDir + "' does not exist or an I/O error occured.");
+			return true;
+		}
+
+		if (this.fileComparator != null) {
+			Arrays.sort(inputFiles, this.fileComparator);
+		}
+
+		for (final File file : inputFiles) {
+			context.put(this.fileOutputPort, file);
+		}
+
+		return true;
+	}
+
+	public FileFilter getFilter() {
+		return this.filter;
+	}
+
+	public void setFilter(final FileFilter filter) {
+		this.filter = filter;
+	}
+
+	public Comparator<File> getFileComparator() {
+		return this.fileComparator;
+	}
+
+	public void setFileComparator(final Comparator<File> fileComparator) {
+		this.fileComparator = fileComparator;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/io/File2TextLinesFilter.java b/src/main/java/teetime/stage/io/File2TextLinesFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..114a6dcf2bc034f973622820ffa0dbc8dc548adc
--- /dev/null
+++ b/src/main/java/teetime/stage/io/File2TextLinesFilter.java
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.io;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.stage.util.TextLine;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class File2TextLinesFilter extends AbstractFilter<File2TextLinesFilter> {
+
+	public final IInputPort<File2TextLinesFilter, File> fileInputPort = this.createInputPort();
+
+	public final IOutputPort<File2TextLinesFilter, TextLine> textLineOutputPort = this.createOutputPort();
+
+	private String charset = "UTF-8";
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<File2TextLinesFilter> context) {
+		final File textFile = context.tryTake(this.fileInputPort);
+		if (textFile == null) {
+			return false;
+		}
+
+		BufferedReader reader = null;
+		try {
+			reader = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), this.charset));
+			String line;
+			while ((line = reader.readLine()) != null) {
+				line = line.trim();
+				if (line.length() != 0) {
+					context.put(this.textLineOutputPort, new TextLine(textFile, line));
+				} // else: ignore empty line
+			}
+		} catch (final FileNotFoundException e) {
+			this.logger.error("", e);
+		} catch (final IOException e) {
+			this.logger.error("", e);
+		} finally {
+			try {
+				if (reader != null) {
+					reader.close();
+				}
+			} catch (final IOException e) {
+				this.logger.warn("", e);
+			}
+		}
+		return true;
+	}
+
+	public String getCharset() {
+		return this.charset;
+	}
+
+	public void setCharset(final String charset) {
+		this.charset = charset;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/io/Printer.java b/src/main/java/teetime/stage/io/Printer.java
new file mode 100644
index 0000000000000000000000000000000000000000..538ad01c282e734485a5967f45ed450498033605
--- /dev/null
+++ b/src/main/java/teetime/stage/io/Printer.java
@@ -0,0 +1,144 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.io;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.Description;
+import teetime.framework.core.IInputPort;
+
+/**
+ * @author Matthias Rohr, Jan Waller, Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+@Description("A filter to print objects to a configured stream")
+public class Printer<T> extends AbstractFilter<Printer<T>> {
+
+	public static final String STREAM_STDOUT = "STDOUT";
+	public static final String STREAM_STDERR = "STDERR";
+	public static final String STREAM_STDLOG = "STDlog";
+	public static final String STREAM_NULL = "NULL";
+
+	public static final String ENCODING_UTF8 = "UTF-8";
+
+	public final IInputPort<Printer<T>, T> input = this.createInputPort();
+
+	private PrintStream printStream;
+	private String streamName = STREAM_STDOUT;
+	private String encoding = ENCODING_UTF8;
+	private boolean active = true;
+	private boolean append = true;
+
+	public String getStreamName() {
+		return this.streamName;
+	}
+
+	public void setStreamName(final String streamName) {
+		this.streamName = streamName;
+	}
+
+	public String getEncoding() {
+		return this.encoding;
+	}
+
+	public void setEncoding(final String encoding) {
+		this.encoding = encoding;
+	}
+
+	public boolean isAppend() {
+		return this.append;
+	}
+
+	public void setAppend(final boolean append) {
+		this.append = append;
+	}
+
+	@Override
+	public void onPipelineStarts() throws Exception {
+		super.onPipelineStarts();
+		this.initializeStream();
+	}
+
+	@Override
+	public void onPipelineStops() {
+		this.closeStream();
+		super.onPipelineStops();
+	}
+
+	private void initializeStream() {
+		if (STREAM_STDOUT.equals(this.streamName)) {
+			this.printStream = System.out;
+			this.active = true;
+		} else if (STREAM_STDERR.equals(this.streamName)) {
+			this.printStream = System.err;
+			this.active = true;
+		} else if (STREAM_STDLOG.equals(this.streamName)) {
+			this.printStream = null;
+			this.active = true;
+		} else if (STREAM_NULL.equals(this.streamName)) {
+			this.printStream = null;
+			this.active = false;
+		} else {
+			try {
+				this.printStream = new PrintStream(new FileOutputStream(this.streamName, this.append), false, this.encoding);
+				this.active = true;
+			} catch (final FileNotFoundException ex) {
+				this.active = false;
+				super.logger.warn("Stream could not be created", ex);
+			} catch (final UnsupportedEncodingException ex) {
+				this.active = false;
+				super.logger.warn("Encoding not supported", ex);
+			}
+		}
+	}
+
+	private void closeStream() {
+		if ((this.printStream != null) && (this.printStream != System.out) && (this.printStream != System.err)) {
+			this.printStream.close();
+		}
+	}
+
+	@Override
+	protected boolean execute(final Context<Printer<T>> context) {
+		final T object = context.tryTake(this.input);
+		if (null == object) {
+			return false;
+		}
+
+		if (this.active) {
+			final StringBuilder sb = new StringBuilder(128);
+
+			sb.append(super.getId());
+			sb.append('(').append(object.getClass().getSimpleName()).append(") ").append(object.toString());
+
+			final String msg = sb.toString();
+			if (this.printStream != null) {
+				this.printStream.println(msg);
+			} else {
+				super.logger.info(msg);
+			}
+		}
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/io/TCPReader.java b/src/main/java/teetime/stage/io/TCPReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..cac1c4fb597d87729f1e2a5bafacdc2fa182fde8
--- /dev/null
+++ b/src/main/java/teetime/stage/io/TCPReader.java
@@ -0,0 +1,206 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.io;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import teetime.common.exception.MonitoringRecordException;
+import teetime.common.logging.Log;
+import teetime.common.logging.LogFactory;
+import teetime.common.record.AbstractMonitoringRecord;
+import teetime.common.record.IMonitoringRecord;
+import teetime.common.record.misc.RegistryRecord;
+import teetime.common.util.registry.ILookup;
+import teetime.common.util.registry.Lookup;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * This is a reader which reads the records from a TCP port.
+ * 
+ * @author Jan Waller, Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public class TCPReader extends AbstractFilter<TCPReader> {
+
+	private static final int MESSAGE_BUFFER_SIZE = 65535;
+
+	private final IOutputPort<TCPReader, IMonitoringRecord> outputPort = super.createOutputPort();
+
+	// BETTER use a non thread-safe implementation to increase performance. A thread-safe version is not necessary.
+	private final ILookup<String> stringRegistry = new Lookup<String>();
+	private int port1 = 10133;
+	private int port2 = 10134;
+
+	@Override
+	public void onPipelineStarts() throws Exception {
+		super.onPipelineStarts();
+
+		// FIXME use the implementation from the thread or from execute(), but not both
+		final TCPStringReader tcpStringReader = new TCPStringReader(this.port2, this.stringRegistry);
+		tcpStringReader.start();
+	}
+
+	@Override
+	public void onPipelineStops() {
+		super.logger.info("Shutdown of TCPReader requested.");
+		// TODO actually implement terminate!
+		super.onPipelineStops();
+	}
+
+	public final int getPort1() {
+		return this.port1;
+	}
+
+	public final void setPort1(final int port1) {
+		this.port1 = port1;
+	}
+
+	public final int getPort2() {
+		return this.port2;
+	}
+
+	public final void setPort2(final int port2) {
+		this.port2 = port2;
+	}
+
+	@Override
+	protected boolean execute(final Context<TCPReader> context) {
+		ServerSocketChannel serversocket = null;
+		try {
+			serversocket = ServerSocketChannel.open();
+			serversocket.socket().bind(new InetSocketAddress(this.port1));
+			if (super.logger.isDebugEnabled()) {
+				super.logger.debug("Listening on port " + this.port1);
+			}
+			// BEGIN also loop this one?
+			final SocketChannel socketChannel = serversocket.accept();
+			final ByteBuffer buffer = ByteBuffer.allocateDirect(MESSAGE_BUFFER_SIZE);
+			while (socketChannel.read(buffer) != -1) {
+				buffer.flip();
+				// System.out.println("Reading, remaining:" + buffer.remaining());
+				try {
+					while (buffer.hasRemaining()) {
+						buffer.mark();
+						final int clazzid = buffer.getInt();
+						final long loggingTimestamp = buffer.getLong();
+						final IMonitoringRecord record;
+						try { // NOCS (Nested try-catch)
+							record = AbstractMonitoringRecord.createFromByteBuffer(clazzid, buffer, this.stringRegistry);
+							record.setLoggingTimestamp(loggingTimestamp);
+							context.put(this.outputPort, record);
+						} catch (final MonitoringRecordException ex) {
+							super.logger.error("Failed to create record.", ex);
+						}
+					}
+					buffer.clear();
+				} catch (final BufferUnderflowException ex) {
+					buffer.reset();
+					// System.out.println("Underflow, remaining:" + buffer.remaining());
+					buffer.compact();
+				}
+			}
+			// System.out.println("Channel closing...");
+			socketChannel.close();
+			// END also loop this one?
+		} catch (final IOException ex) {
+			super.logger.error("Error while reading", ex);
+			return false;
+		} finally {
+			if (null != serversocket) {
+				try {
+					serversocket.close();
+				} catch (final IOException e) {
+					if (super.logger.isDebugEnabled()) {
+						super.logger.debug("Failed to close TCP connection!", e);
+					}
+				}
+			}
+		}
+		return true;
+	}
+
+}
+
+/**
+ * 
+ * @author Jan Waller
+ * 
+ * @since 1.8
+ */
+class TCPStringReader extends Thread {
+
+	private static final int MESSAGE_BUFFER_SIZE = 65535;
+
+	private static final Log LOG = LogFactory.getLog(TCPStringReader.class);
+
+	private final int port;
+	private final ILookup<String> stringRegistry;
+
+	public TCPStringReader(final int port, final ILookup<String> stringRegistry) {
+		this.port = port;
+		this.stringRegistry = stringRegistry;
+	}
+
+	@Override
+	public void run() {
+		ServerSocketChannel serversocket = null;
+		try {
+			serversocket = ServerSocketChannel.open();
+			serversocket.socket().bind(new InetSocketAddress(this.port));
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Listening on port " + this.port);
+			}
+			// BEGIN also loop this one?
+			final SocketChannel socketChannel = serversocket.accept();
+			final ByteBuffer buffer = ByteBuffer.allocateDirect(MESSAGE_BUFFER_SIZE);
+			while (socketChannel.read(buffer) != -1) {
+				buffer.flip();
+				try {
+					while (buffer.hasRemaining()) {
+						buffer.mark();
+						RegistryRecord.registerRecordInRegistry(buffer, this.stringRegistry);
+					}
+					buffer.clear();
+				} catch (final BufferUnderflowException ex) {
+					buffer.reset();
+					buffer.compact();
+				}
+			}
+			socketChannel.close();
+			// END also loop this one?
+		} catch (final IOException ex) {
+			LOG.error("Error while reading", ex);
+		} finally {
+			if (null != serversocket) {
+				try {
+					serversocket.close();
+				} catch (final IOException e) {
+					if (LOG.isDebugEnabled()) {
+						LOG.debug("Failed to close TCP connection!", e);
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/src/main/java/teetime/stage/kieker/File2RecordFilter.java b/src/main/java/teetime/stage/kieker/File2RecordFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..00af24c6bd6af962e1b92b2eee732fbad215d3f5
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/File2RecordFilter.java
@@ -0,0 +1,144 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker;
+
+import java.io.File;
+
+import teetime.common.record.IMonitoringRecord;
+import teetime.common.util.filesystem.BinaryCompressionMethod;
+import teetime.common.util.filesystem.FSUtil;
+import teetime.framework.concurrent.ConcurrentWorkStealingPipe;
+import teetime.framework.concurrent.ConcurrentWorkStealingPipeFactory;
+import teetime.framework.core.CompositeFilter;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.framework.sequential.MethodCallPipe;
+import teetime.stage.FileExtensionFilter;
+import teetime.stage.basic.merger.Merger;
+import teetime.stage.kieker.className.ClassNameRegistryCreationFilter;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+import teetime.stage.kieker.fileToRecord.BinaryFile2RecordFilter;
+import teetime.stage.kieker.fileToRecord.DatFile2RecordFilter;
+import teetime.stage.kieker.fileToRecord.ZipFile2RecordFilter;
+import teetime.stage.predicate.IsDirectoryPredicate;
+import teetime.stage.predicate.PredicateFilter;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class File2RecordFilter extends CompositeFilter {
+
+	public final IInputPort<PredicateFilter<File>, File> fileInputPort;
+
+	public final IOutputPort<Merger<IMonitoringRecord>, IMonitoringRecord> recordOutputPort;
+
+	private ClassNameRegistryRepository classNameRegistryRepository;
+
+	/**
+	 * @since 1.10
+	 */
+	public File2RecordFilter(final ClassNameRegistryRepository classNameRegistryRepository) {
+		this.classNameRegistryRepository = classNameRegistryRepository;
+
+		// FIXME does not yet work with more than one thread due to classNameRegistryRepository
+		// create stages
+		final PredicateFilter<File> isDirectoryFilter = new PredicateFilter<File>(new IsDirectoryPredicate());
+		final ClassNameRegistryCreationFilter classNameRegistryCreationFilter = new ClassNameRegistryCreationFilter(this.classNameRegistryRepository);
+		final MonitoringLogDirectory2Files directory2FilesFilter = new MonitoringLogDirectory2Files();
+		final FileExtensionFilter fileExtensionFilter = new FileExtensionFilter();
+		final Merger<File> fileMerger = new Merger<File>();
+
+		final DatFile2RecordFilter datFile2RecordFilter = new DatFile2RecordFilter(this.classNameRegistryRepository);
+		final BinaryFile2RecordFilter binaryFile2RecordFilter = new BinaryFile2RecordFilter(this.classNameRegistryRepository);
+		final ZipFile2RecordFilter zipFile2RecordFilter = new ZipFile2RecordFilter();
+
+		final Merger<IMonitoringRecord> recordMerger = new Merger<IMonitoringRecord>();
+
+		// store ports due to readability reasons
+		final IOutputPort<FileExtensionFilter, File> normalFileOutputPort = fileExtensionFilter.createOutputPortForFileExtension(FSUtil.NORMAL_FILE_EXTENSION);
+		final IOutputPort<FileExtensionFilter, File> binFileOutputPort = fileExtensionFilter.createOutputPortForFileExtension(BinaryCompressionMethod.NONE
+				.getFileExtension());
+		final IOutputPort<FileExtensionFilter, File> zipFileOutputPort = fileExtensionFilter.createOutputPortForFileExtension(FSUtil.ZIP_FILE_EXTENSION);
+
+		// connect ports by pipes
+		MethodCallPipe.connect(isDirectoryFilter.matchingOutputPort, classNameRegistryCreationFilter.directoryInputPort);
+		MethodCallPipe.connect(isDirectoryFilter.mismatchingOutputPort, fileMerger.getNewInputPort()); // BETTER restructure pipeline
+		MethodCallPipe.connect(classNameRegistryCreationFilter.relayDirectoryOutputPort, directory2FilesFilter.directoryInputPort);
+		MethodCallPipe.connect(classNameRegistryCreationFilter.filePrefixOutputPort, directory2FilesFilter.filePrefixInputPort);
+		MethodCallPipe.connect(directory2FilesFilter.fileOutputPort, fileExtensionFilter.fileInputPort);
+		MethodCallPipe.connect(zipFileOutputPort, fileMerger.getNewInputPort());
+
+		final ConcurrentWorkStealingPipeFactory<File> concurrentWorkStealingPipeFactory0 = new ConcurrentWorkStealingPipeFactory<File>();
+		final ConcurrentWorkStealingPipe<File> concurrentWorkStealingPipe0 = concurrentWorkStealingPipeFactory0.create();
+		concurrentWorkStealingPipe0.setSourcePort(normalFileOutputPort);
+		concurrentWorkStealingPipe0.setTargetPort(datFile2RecordFilter.fileInputPort);
+
+		final ConcurrentWorkStealingPipeFactory<File> concurrentWorkStealingPipeFactory1 = new ConcurrentWorkStealingPipeFactory<File>();
+		final ConcurrentWorkStealingPipe<File> concurrentWorkStealingPipe1 = concurrentWorkStealingPipeFactory1.create();
+		concurrentWorkStealingPipe1.setSourcePort(binFileOutputPort);
+		concurrentWorkStealingPipe1.setTargetPort(binaryFile2RecordFilter.fileInputPort);
+
+		final ConcurrentWorkStealingPipeFactory<File> concurrentWorkStealingPipeFactory2 = new ConcurrentWorkStealingPipeFactory<File>();
+		final ConcurrentWorkStealingPipe<File> concurrentWorkStealingPipe2 = concurrentWorkStealingPipeFactory2.create();
+		concurrentWorkStealingPipe2.setSourcePort(fileMerger.outputPort);
+		concurrentWorkStealingPipe2.setTargetPort(zipFile2RecordFilter.fileInputPort);
+
+		final ConcurrentWorkStealingPipeFactory<IMonitoringRecord> concurrentWorkStealingPipeFactoriesNormal = new ConcurrentWorkStealingPipeFactory<IMonitoringRecord>();
+		final ConcurrentWorkStealingPipe<IMonitoringRecord> datPipe = concurrentWorkStealingPipeFactoriesNormal.create();
+		datPipe.connect(datFile2RecordFilter.recordOutputPort, recordMerger.getNewInputPort());
+
+		final ConcurrentWorkStealingPipeFactory<IMonitoringRecord> concurrentWorkStealingPipeFactoriesBinary = new ConcurrentWorkStealingPipeFactory<IMonitoringRecord>();
+		final ConcurrentWorkStealingPipe<IMonitoringRecord> binaryPipe = concurrentWorkStealingPipeFactoriesBinary.create();
+		binaryPipe.connect(binaryFile2RecordFilter.recordOutputPort, recordMerger.getNewInputPort());
+
+		final ConcurrentWorkStealingPipeFactory<IMonitoringRecord> concurrentWorkStealingPipeFactoriesZip = new ConcurrentWorkStealingPipeFactory<IMonitoringRecord>();
+		final ConcurrentWorkStealingPipe<IMonitoringRecord> zipPipe = concurrentWorkStealingPipeFactoriesZip.create();
+		zipPipe.connect(zipFile2RecordFilter.recordOutputPort, recordMerger.getNewInputPort());
+
+		this.fileInputPort = isDirectoryFilter.inputPort;
+		this.recordOutputPort = recordMerger.outputPort;
+
+		this.schedulableStages.add(isDirectoryFilter);
+
+		// this.schedulableStages.add(classNameRegistryCreationFilter);
+		// this.schedulableStages.add(directory2FilesFilter);
+		// this.schedulableStages.add(fileMerger);
+		// this.schedulableStages.add(fileExtensionFilter);
+
+		this.schedulableStages.add(datFile2RecordFilter);
+		this.schedulableStages.add(binaryFile2RecordFilter);
+		this.schedulableStages.add(zipFile2RecordFilter);
+		this.schedulableStages.add(recordMerger);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public File2RecordFilter() {
+		this(null);
+	}
+
+	public ClassNameRegistryRepository getClassNameRegistryRepository() {
+		return this.classNameRegistryRepository;
+	}
+
+	public void setClassNameRegistryRepository(final ClassNameRegistryRepository classNameRegistryRepository) {
+		this.classNameRegistryRepository = classNameRegistryRepository;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/kieker/MonitoringLogDirectory2Files.java b/src/main/java/teetime/stage/kieker/MonitoringLogDirectory2Files.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d59f48ad22eaaecd4820d00bf9d3ae816332410
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/MonitoringLogDirectory2Files.java
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Comparator;
+
+import teetime.common.util.filesystem.BinaryCompressionMethod;
+import teetime.common.util.filesystem.FSUtil;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.stage.io.Directory2FilesFilter;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class MonitoringLogDirectory2Files extends Directory2FilesFilter {
+
+	public final IInputPort<Directory2FilesFilter, String> filePrefixInputPort = this.createInputPort();
+
+	/**
+	 * @author Christian Wulf
+	 * 
+	 * @since 1.10
+	 */
+	static class MonitoringLogFileFilter implements FileFilter {
+		private String filePrefix;
+
+		public boolean accept(final File pathname) {
+			final String name = pathname.getName();
+			return pathname.isFile()
+					&& name.startsWith(this.filePrefix)
+					&& (name.endsWith(FSUtil.NORMAL_FILE_EXTENSION) || BinaryCompressionMethod.hasValidFileExtension(name));
+		}
+
+		public String getFilePrefix() {
+			return this.filePrefix;
+		}
+
+		public void setFilePrefix(final String filePrefix) {
+			this.filePrefix = filePrefix;
+		}
+	}
+
+	private static final Comparator<File> FILE_COMPARATOR = new Comparator<File>() {
+		public final int compare(final File f1, final File f2) {
+			return f1.compareTo(f2); // simplified (we expect no dirs!)
+		}
+	};
+
+	/**
+	 * @since 1.10
+	 */
+	public MonitoringLogDirectory2Files() {
+		super(new MonitoringLogFileFilter(), FILE_COMPARATOR);
+	}
+
+	@Override
+	protected boolean execute(final Context<Directory2FilesFilter> context) {
+		final String filePrefix = context.tryTake(this.filePrefixInputPort);
+		if (filePrefix == null) {
+			return false;
+		}
+
+		((MonitoringLogFileFilter) this.getFilter()).setFilePrefix(filePrefix);
+
+		return super.execute(context);
+	}
+}
diff --git a/src/main/java/teetime/stage/kieker/className/ClassNameRegistry.java b/src/main/java/teetime/stage/kieker/className/ClassNameRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..15e300ede691b9ff5d9a1c33d8b6ba15f7cd1daa
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/className/ClassNameRegistry.java
@@ -0,0 +1,29 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker.className;
+
+import java.util.HashMap;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ClassNameRegistry extends HashMap<Integer, String> {
+
+	private static final long serialVersionUID = -7254550212115937463L;
+
+}
diff --git a/src/main/java/teetime/stage/kieker/className/ClassNameRegistryCreationFilter.java b/src/main/java/teetime/stage/kieker/className/ClassNameRegistryCreationFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..85c350f071d1d6f118070dcb8a8b62ea24547244
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/className/ClassNameRegistryCreationFilter.java
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker.className;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+
+import kieker.analysis.stage.MappingFileParser;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ClassNameRegistryCreationFilter extends AbstractFilter<ClassNameRegistryCreationFilter> {
+
+	public final IInputPort<ClassNameRegistryCreationFilter, File> directoryInputPort = this.createInputPort();
+
+	public final IOutputPort<ClassNameRegistryCreationFilter, File> relayDirectoryOutputPort = this.createOutputPort();
+	public final IOutputPort<ClassNameRegistryCreationFilter, String> filePrefixOutputPort = this.createOutputPort();
+
+	private ClassNameRegistryRepository classNameRegistryRepository;
+
+	private final MappingFileParser mappingFileParser;
+
+	/**
+	 * @since 1.10
+	 */
+	public ClassNameRegistryCreationFilter(final ClassNameRegistryRepository classNameRegistryRepository) {
+		this();
+		this.classNameRegistryRepository = classNameRegistryRepository;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public ClassNameRegistryCreationFilter() {
+		super();
+		this.mappingFileParser = new MappingFileParser(this.logger);
+	}
+
+	@Override
+	protected boolean execute(final Context<ClassNameRegistryCreationFilter> context) {
+		final File inputDir = context.tryTake(this.directoryInputPort);
+		if (inputDir == null) {
+			return false;
+		}
+
+		final File mappingFile = this.mappingFileParser.findMappingFile(inputDir);
+		if (mappingFile == null) {
+			return true;
+		}
+
+		try {
+			final ClassNameRegistry classNameRegistry = this.mappingFileParser.parseFromStream(new FileInputStream(mappingFile));
+			this.classNameRegistryRepository.put(inputDir, classNameRegistry);
+			context.put(this.relayDirectoryOutputPort, inputDir);
+
+			final String filePrefix = this.mappingFileParser.getFilePrefixFromMappingFile(mappingFile);
+			context.put(this.filePrefixOutputPort, filePrefix);
+		} catch (final FileNotFoundException e) {
+			this.logger.error("Mapping file not found.", e); // and skip this directory
+		}
+
+		return true;
+	}
+
+	public ClassNameRegistryRepository getClassNameRegistryRepository() {
+		return this.classNameRegistryRepository;
+	}
+
+	public void setClassNameRegistryRepository(final ClassNameRegistryRepository classNameRegistryRepository) {
+		this.classNameRegistryRepository = classNameRegistryRepository;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/kieker/className/ClassNameRegistryRepository.java b/src/main/java/teetime/stage/kieker/className/ClassNameRegistryRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..bbbd79117109f14dc6e59b2850a5f7a26cb9a3a1
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/className/ClassNameRegistryRepository.java
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker.className;
+
+import java.io.File;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class represents a wrapper for a Map<String, ClassNameRegistry> ensuring that keys are
+ * <ul>
+ * <li>of the type <code>java.io.File</code> and
+ * <li>passed as absolute file paths.
+ * </ul>
+ * 
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ClassNameRegistryRepository {
+
+	private final ConcurrentHashMap<String, ClassNameRegistry> classNameRegistryRepository = new ConcurrentHashMap<String, ClassNameRegistry>();
+
+	/**
+	 * @since 1.10
+	 */
+	public ClassNameRegistry get(final File directory) {
+		return this.classNameRegistryRepository.get(directory.getAbsolutePath());
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void put(final File directory, final ClassNameRegistry classNameRegistry) {
+		this.classNameRegistryRepository.put(directory.getAbsolutePath(), classNameRegistry);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public int size() {
+		return this.classNameRegistryRepository.size();
+	}
+
+	@Override
+	public String toString() {
+		return this.classNameRegistryRepository.toString();
+	}
+}
diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/BinaryFile2RecordFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/BinaryFile2RecordFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..175efeb7ba545ef0add1b27e6ed1b0b350323b5d
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/fileToRecord/BinaryFile2RecordFilter.java
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker.fileToRecord;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+
+import kieker.analysis.stage.RecordFromBinaryFileCreator;
+import teetime.common.exception.MonitoringRecordException;
+import teetime.common.record.IMonitoringRecord;
+import teetime.common.util.filesystem.BinaryCompressionMethod;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class BinaryFile2RecordFilter extends AbstractFilter<BinaryFile2RecordFilter> {
+
+	private static final int MB = 1024 * 1024;
+
+	public final IInputPort<BinaryFile2RecordFilter, File> fileInputPort = this.createInputPort();
+	public final IOutputPort<BinaryFile2RecordFilter, IMonitoringRecord> recordOutputPort = this.createOutputPort();
+
+	private RecordFromBinaryFileCreator recordFromBinaryFileCreator;
+
+	private ClassNameRegistryRepository classNameRegistryRepository;
+
+	/**
+	 * @since 1.10
+	 */
+	public BinaryFile2RecordFilter(final ClassNameRegistryRepository classNameRegistryRepository) {
+		this();
+		this.classNameRegistryRepository = classNameRegistryRepository;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public BinaryFile2RecordFilter() {
+		super();
+	}
+
+	@Override
+	public void onPipelineStarts() throws Exception {
+		this.recordFromBinaryFileCreator = new RecordFromBinaryFileCreator(this.logger, this.classNameRegistryRepository);
+		super.onPipelineStarts();
+	}
+
+	@Override
+	protected boolean execute(final Context<BinaryFile2RecordFilter> context) {
+		final File binaryFile = context.tryTake(this.fileInputPort);
+		if (binaryFile == null) {
+			return false;
+		}
+
+		try {
+			final BinaryCompressionMethod method = BinaryCompressionMethod.getByFileExtension(binaryFile.getName());
+			final DataInputStream in = method.getDataInputStream(binaryFile, 1 * MB);
+			try {
+				IMonitoringRecord record = this.recordFromBinaryFileCreator.createRecordFromBinaryFile(binaryFile, in);
+				while (record != null) {
+					context.put(this.recordOutputPort, record);
+					record = this.recordFromBinaryFileCreator.createRecordFromBinaryFile(binaryFile, in);
+				}
+			} catch (final MonitoringRecordException e) {
+				this.logger.error("Error reading file: " + binaryFile, e);
+			} finally {
+				if (in != null) {
+					try {
+						in.close();
+					} catch (final IOException ex) {
+						this.logger.error("Exception while closing input stream for processing input file", ex);
+					}
+				}
+			}
+		} catch (final IOException e) {
+			this.logger.error("Error reading file: " + binaryFile, e);
+		} catch (final IllegalArgumentException e) {
+			this.logger.warn("Unknown file extension for file: " + binaryFile);
+		}
+
+		return true;
+	}
+
+	public ClassNameRegistryRepository getClassNameRegistryRepository() {
+		return this.classNameRegistryRepository;
+	}
+
+	public void setClassNameRegistryRepository(final ClassNameRegistryRepository classNameRegistryRepository) {
+		this.classNameRegistryRepository = classNameRegistryRepository;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/DatFile2RecordFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/DatFile2RecordFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea9c0fd3213a319a8bc52044940d076b91900d2f
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/fileToRecord/DatFile2RecordFilter.java
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker.fileToRecord;
+
+import java.io.File;
+
+import teetime.common.record.IMonitoringRecord;
+import teetime.framework.concurrent.ConcurrentWorkStealingPipe;
+import teetime.framework.concurrent.ConcurrentWorkStealingPipeFactory;
+import teetime.framework.core.CompositeFilter;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.stage.io.File2TextLinesFilter;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+import teetime.stage.kieker.fileToRecord.textLine.TextLine2RecordFilter;
+import teetime.stage.util.TextLine;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class DatFile2RecordFilter extends CompositeFilter {
+
+	public final IInputPort<File2TextLinesFilter, File> fileInputPort;
+
+	public final IOutputPort<TextLine2RecordFilter, IMonitoringRecord> recordOutputPort;
+
+	public DatFile2RecordFilter(final ClassNameRegistryRepository classNameRegistryRepository) {
+		final File2TextLinesFilter file2TextLinesFilter = new File2TextLinesFilter();
+		final TextLine2RecordFilter textLine2RecordFilter = new TextLine2RecordFilter(classNameRegistryRepository);
+
+		// FIXME extract pipe implementation
+		final ConcurrentWorkStealingPipeFactory<TextLine> concurrentWorkStealingPipeFactory = new ConcurrentWorkStealingPipeFactory<TextLine>();
+		final ConcurrentWorkStealingPipe<TextLine> pipe = concurrentWorkStealingPipeFactory.create();
+		pipe.connect(file2TextLinesFilter.textLineOutputPort, textLine2RecordFilter.textLineInputPort);
+
+		this.fileInputPort = file2TextLinesFilter.fileInputPort;
+		this.recordOutputPort = textLine2RecordFilter.recordOutputPort;
+
+		this.schedulableStages.add(file2TextLinesFilter);
+		this.schedulableStages.add(textLine2RecordFilter);
+	}
+}
diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/ZipFile2RecordFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/ZipFile2RecordFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..12d274b8dbe92f5c261b135b327fdc3f626120f6
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/fileToRecord/ZipFile2RecordFilter.java
@@ -0,0 +1,134 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker.fileToRecord;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import kieker.analysis.stage.MappingFileParser;
+import teetime.common.record.IMonitoringRecord;
+import teetime.common.util.filesystem.FSUtil;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.stage.kieker.className.ClassNameRegistry;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ZipFile2RecordFilter extends AbstractFilter<ZipFile2RecordFilter> {
+
+	public final IInputPort<ZipFile2RecordFilter, File> fileInputPort = this.createInputPort();
+
+	public final IOutputPort<ZipFile2RecordFilter, IMonitoringRecord> recordOutputPort = this.createOutputPort();
+
+	private final MappingFileParser mappingFileParser;
+
+	/**
+	 * @since 1.10
+	 */
+	public ZipFile2RecordFilter() {
+		this.mappingFileParser = new MappingFileParser(this.logger);
+	}
+
+	@Override
+	protected boolean execute(final Context<ZipFile2RecordFilter> context) {
+		final File zipFile = context.tryTake(this.fileInputPort);
+		if (zipFile == null) {
+			return false;
+		}
+
+		final InputStream mappingFileInputStream = this.findMappingFileInputStream(zipFile);
+		if (mappingFileInputStream == null) {
+			return true;
+		}
+		final ClassNameRegistry classNameRegistry = this.mappingFileParser.parseFromStream(mappingFileInputStream);
+
+		try {
+			this.createAndSendRecordsFromZipFile(context, zipFile, classNameRegistry);
+		} catch (final FileNotFoundException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+
+		return true;
+	}
+
+	private void createAndSendRecordsFromZipFile(final Context<ZipFile2RecordFilter> context, final File zipFile, final ClassNameRegistry classNameRegistry)
+			throws FileNotFoundException {
+		final ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
+		final BufferedReader reader;
+		try {
+			reader = new BufferedReader(new InputStreamReader(zipInputStream, FSUtil.ENCODING));
+		} catch (final UnsupportedEncodingException e) {
+			this.logger.error("This exception should never occur.", e);
+			return;
+		}
+		final DataInputStream input = new DataInputStream(new BufferedInputStream(zipInputStream, 1024 * 1024));
+
+		ZipEntry zipEntry;
+		try {
+			while (null != (zipEntry = zipInputStream.getNextEntry())) { // NOCS NOPMD
+				final String filename = zipEntry.getName();
+				// TODO
+			}
+		} catch (final IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	private InputStream findMappingFileInputStream(final File zipFile) {
+		ZipInputStream zipInputStream = null;
+		try {
+			zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
+			ZipEntry zipEntry;
+			while ((null != (zipEntry = zipInputStream.getNextEntry())) && !zipEntry.getName().equals(FSUtil.MAP_FILENAME)) { // NOCS NOPMD
+				// do nothing, just skip to the map file if present
+			}
+			if (null == zipEntry) {
+				this.logger.error("The zip file does not contain a Kieker log: " + zipFile.toString());
+				return null;
+			}
+			return zipInputStream;
+		} catch (final IOException ex) {
+			this.logger.error("Error accessing ZipInputStream", ex);
+		} finally {
+			if (null != zipInputStream) {
+				try {
+					zipInputStream.close();
+				} catch (final IOException ex) {
+					this.logger.error("Failed to close ZipInputStream", ex);
+				}
+			}
+		}
+
+		return null;
+	}
+}
diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2MappingRegistryFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2MappingRegistryFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbaa1f0645a60549920ca430e61d9c3a0cdbfd53
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2MappingRegistryFilter.java
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.kieker.fileToRecord.textLine;
+
+import java.util.Map;
+
+import teetime.common.util.filesystem.FSUtil;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class TextLine2MappingRegistryFilter extends AbstractFilter<TextLine2MappingRegistryFilter> {
+
+	public final IInputPort<TextLine2MappingRegistryFilter, String> TEXT_LINE = this.createInputPort();
+
+	private final Map<Integer, String> stringRegistry;
+
+	public TextLine2MappingRegistryFilter(final Map<Integer, String> stringRegistry) {
+		this.stringRegistry = stringRegistry;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<TextLine2MappingRegistryFilter> context) {
+		final String textLine = context.tryTake(this.TEXT_LINE);
+		if (textLine == null) {
+			return false;
+		}
+
+		final int split = textLine.indexOf('=');
+		if (split == -1) {
+			this.logger.error("Failed to find character '=' in line: {" + textLine + "}. It must consist of a ID=VALUE pair.");
+			return true;
+		}
+		final String key = textLine.substring(0, split);
+		// BETTER execute split instead of checking it before with multiple string operations
+		final String value = FSUtil.decodeNewline(textLine.substring(split + 1));
+		// the leading $ is optional
+		final Integer id;
+		try {
+			id = Integer.valueOf((key.charAt(0) == '$') ? key.substring(1) : key); // NOCS
+		} catch (final NumberFormatException ex) {
+			this.logger.error("Error reading mapping file, id must be integer", ex);
+			return true; // continue on errors
+		}
+		final String prevVal = this.stringRegistry.put(id, value);
+		if (prevVal != null) {
+			this.logger.error("Found additional entry for id='" + id + "', old value was '" + prevVal + "' new value is '" + value + "'");
+			return true;
+		}
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2RecordFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2RecordFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..9960a1fe400033ba618d66009b77bfd75ddeb9a2
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2RecordFilter.java
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.kieker.fileToRecord.textLine;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import kieker.analysis.stage.RecordFromTextLineCreator;
+import teetime.common.exception.IllegalRecordFormatException;
+import teetime.common.exception.MonitoringRecordException;
+import teetime.common.exception.UnknownRecordTypeException;
+import teetime.common.record.IMonitoringRecord;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.stage.MappingException;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+import teetime.stage.util.TextLine;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class TextLine2RecordFilter extends AbstractFilter<TextLine2RecordFilter> {
+
+	public final IInputPort<TextLine2RecordFilter, TextLine> textLineInputPort = this.createInputPort();
+
+	public final IOutputPort<TextLine2RecordFilter, IMonitoringRecord> recordOutputPort = this.createOutputPort();
+
+	private final Set<String> unknownTypesObserved = new HashSet<String>();
+
+	private boolean ignoreUnknownRecordTypes;
+
+	private boolean abortDueToUnknownRecordType;
+
+	private RecordFromTextLineCreator recordFromTextLineCreator;
+
+	/**
+	 * @since 1.10
+	 */
+	public TextLine2RecordFilter(final ClassNameRegistryRepository classNameRegistryRepository) {
+		this.recordFromTextLineCreator = new RecordFromTextLineCreator(classNameRegistryRepository);
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public TextLine2RecordFilter() {
+		super();
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<TextLine2RecordFilter> context) {
+		final TextLine textLine = context.tryTake(this.textLineInputPort);
+		if (textLine == null) {
+			return false;
+		}
+
+		try {
+			final IMonitoringRecord record = this.recordFromTextLineCreator.createRecordFromLine(textLine.getTextFile(), textLine.getTextLine());
+			context.put(this.recordOutputPort, record);
+		} catch (final MonitoringRecordException e) {
+			this.logger.error("Could not create record from text line: '" + textLine + "'", e);
+		} catch (final IllegalRecordFormatException e) {
+			this.logger.error("Illegal record format: " + textLine, e);
+		} catch (final MappingException e) {
+			this.logger.error("", e);
+		} catch (final UnknownRecordTypeException e) {
+			final String classname = e.getClassName();
+			if (!this.ignoreUnknownRecordTypes) {
+				this.abortDueToUnknownRecordType = true;
+				this.logger.error("Failed to load record type " + classname, e);
+			} else if (!this.unknownTypesObserved.contains(classname)) {
+				this.unknownTypesObserved.add(classname);
+				this.logger.error("Failed to load record type " + classname, e); // log once for this type
+			}
+		}
+
+		return true;
+	}
+
+	public boolean isIgnoreUnknownRecordTypes() {
+		return this.ignoreUnknownRecordTypes;
+	}
+
+	public void setIgnoreUnknownRecordTypes(final boolean ignoreUnknownRecordTypes) {
+		this.ignoreUnknownRecordTypes = ignoreUnknownRecordTypes;
+	}
+
+	public RecordFromTextLineCreator getRecordFromTextLineCreator() {
+		return this.recordFromTextLineCreator;
+	}
+
+	public void setRecordFromTextLineCreator(final RecordFromTextLineCreator recordFromTextLineCreator) {
+		this.recordFromTextLineCreator = recordFromTextLineCreator;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/kieker/traceReconstruction/TraceReconstructionFilter.java b/src/main/java/teetime/stage/kieker/traceReconstruction/TraceReconstructionFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..360258d810eb7e218c87b146b9966dee82522980
--- /dev/null
+++ b/src/main/java/teetime/stage/kieker/traceReconstruction/TraceReconstructionFilter.java
@@ -0,0 +1,191 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.kieker.traceReconstruction;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+import teetime.analysis.plugin.filter.flow.TraceEventRecords;
+import teetime.analysis.plugin.filter.flow.reconstruction.TraceBuffer;
+import teetime.common.record.flow.IFlowRecord;
+import teetime.common.record.flow.trace.AbstractTraceEvent;
+import teetime.common.record.flow.trace.TraceMetadata;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.util.concurrent.hashmap.ConcurrentHashMapWithDefault;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class TraceReconstructionFilter extends AbstractFilter<TraceReconstructionFilter> {
+
+	public final IInputPort<TraceReconstructionFilter, Long> timestampInputPort = this.createInputPort();
+	public final IInputPort<TraceReconstructionFilter, IFlowRecord> recordInputPort = this.createInputPort();
+
+	public final IOutputPort<TraceReconstructionFilter, TraceEventRecords> traceInvalidOutputPort = this.createOutputPort();
+	public final IOutputPort<TraceReconstructionFilter, TraceEventRecords> traceValidOutputPort = this.createOutputPort();
+
+	private TimeUnit timeunit;
+	private long maxTraceDuration;
+	private long maxTraceTimeout;
+	private boolean timeout;
+	private long maxEncounteredLoggingTimestamp = -1;
+
+	private Map<Long, TraceBuffer> traceId2trace = new ConcurrentHashMapWithDefault<Long, TraceBuffer>(new TraceBuffer());
+
+	@Override
+	protected boolean execute(final Context<TraceReconstructionFilter> context) {
+		final Long timestamp = context.tryTake(this.timestampInputPort);
+		if (timestamp != null) {
+			if (this.timeout) {
+				this.processTimeoutQueue(timestamp, context);
+			}
+			return true;
+		}
+
+		final IFlowRecord record = context.tryTake(this.recordInputPort);
+		if (record != null) {
+			final Long traceId = this.reconstructTrace(record);
+			if (traceId != null) {
+				this.putIfFinished(traceId, context);
+				this.processTimestamp(record, context);
+			}
+			return true;
+		}
+
+		return false;
+	}
+
+	private void processTimestamp(final IFlowRecord record, final Context<TraceReconstructionFilter> context) {
+		if (this.timeout) {
+			synchronized (this) {
+				final long loggingTimestamp = this.getTimestamp(record);
+				// can we assume a rough order of logging timestamps? (yes, except with DB reader)
+				if (loggingTimestamp > this.maxEncounteredLoggingTimestamp) {
+					this.maxEncounteredLoggingTimestamp = loggingTimestamp;
+				}
+				this.processTimeoutQueue(this.maxEncounteredLoggingTimestamp, context);
+			}
+		}
+	}
+
+	private long getTimestamp(final IFlowRecord record) {
+		if (record instanceof AbstractTraceEvent) {
+			return ((AbstractTraceEvent) record).getTimestamp();
+		}
+		return -1;
+	}
+
+	private void putIfFinished(final Long traceId, final Context<TraceReconstructionFilter> context) {
+		final TraceBuffer traceBuffer = this.traceId2trace.get(traceId);
+		if (traceBuffer.isFinished()) {
+			synchronized (this) { // has to be synchronized because of timeout cleanup
+				this.traceId2trace.remove(traceId);
+			}
+			this.put(traceBuffer, context);
+		}
+	}
+
+	private Long reconstructTrace(final IFlowRecord record) {
+		Long traceId = null;
+		if (record instanceof TraceMetadata) {
+			traceId = ((TraceMetadata) record).getTraceId();
+			final TraceBuffer traceBuffer = this.traceId2trace.get(traceId);
+
+			traceBuffer.setTrace((TraceMetadata) record);
+		} else if (record instanceof AbstractTraceEvent) {
+			traceId = ((AbstractTraceEvent) record).getTraceId();
+			final TraceBuffer traceBuffer = this.traceId2trace.get(traceId);
+
+			traceBuffer.insertEvent((AbstractTraceEvent) record);
+		}
+
+		return traceId;
+	}
+
+	@Override
+	public void onPipelineStarts() throws Exception {
+		this.timeout = !((this.maxTraceTimeout == Long.MAX_VALUE) && (this.maxTraceDuration == Long.MAX_VALUE));
+		super.onPipelineStarts();
+	}
+
+	private void processTimeoutQueue(final long timestamp, final Context<TraceReconstructionFilter> context) {
+		final long duration = timestamp - this.maxTraceDuration;
+		final long traceTimeout = timestamp - this.maxTraceTimeout;
+
+		for (final Iterator<Entry<Long, TraceBuffer>> iterator = this.traceId2trace.entrySet().iterator(); iterator.hasNext();) {
+			final TraceBuffer traceBuffer = iterator.next().getValue();
+			if ((traceBuffer.getMaxLoggingTimestamp() <= traceTimeout) // long time no see
+					|| (traceBuffer.getMinLoggingTimestamp() <= duration)) { // max duration is gone
+				this.put(traceBuffer, context);
+				iterator.remove();
+			}
+		}
+	}
+
+	private void put(final TraceBuffer traceBuffer, final Context<TraceReconstructionFilter> context) {
+		final IOutputPort<TraceReconstructionFilter, TraceEventRecords> outputPort =
+				(traceBuffer.isInvalid()) ? this.traceInvalidOutputPort : this.traceValidOutputPort;
+		context.put(outputPort, traceBuffer.toTraceEvents());
+	}
+
+	public TimeUnit getTimeunit() {
+		return this.timeunit;
+	}
+
+	public void setTimeunit(final TimeUnit timeunit) {
+		this.timeunit = timeunit;
+	}
+
+	public long getMaxTraceDuration() {
+		return this.maxTraceDuration;
+	}
+
+	public void setMaxTraceDuration(final long maxTraceDuration) {
+		this.maxTraceDuration = maxTraceDuration;
+	}
+
+	public long getMaxTraceTimeout() {
+		return this.maxTraceTimeout;
+	}
+
+	public void setMaxTraceTimeout(final long maxTraceTimeout) {
+		this.maxTraceTimeout = maxTraceTimeout;
+	}
+
+	public long getMaxEncounteredLoggingTimestamp() {
+		return this.maxEncounteredLoggingTimestamp;
+	}
+
+	public void setMaxEncounteredLoggingTimestamp(final long maxEncounteredLoggingTimestamp) {
+		this.maxEncounteredLoggingTimestamp = maxEncounteredLoggingTimestamp;
+	}
+
+	public Map<Long, TraceBuffer> getTraceId2trace() {
+		return this.traceId2trace;
+	}
+
+	public void setTraceId2trace(final Map<Long, TraceBuffer> traceId2trace) {
+		this.traceId2trace = traceId2trace;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/FileExtensionPredicate.java b/src/main/java/teetime/stage/predicate/FileExtensionPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7f3bb87e1d768beebf23be33a2d7022adc20fb4
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/FileExtensionPredicate.java
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import java.io.File;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class FileExtensionPredicate implements Predicate<File> {
+
+	private final String fileExtension;
+
+	public FileExtensionPredicate(final String fileExtension) {
+		this.fileExtension = fileExtension;
+	}
+
+	public boolean apply(final File file) {
+		return file.getName().endsWith(this.fileExtension);
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/IsDirectoryPredicate.java b/src/main/java/teetime/stage/predicate/IsDirectoryPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..21b09576bde104c8350614d719e1674ac9899499
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/IsDirectoryPredicate.java
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import java.io.File;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class IsDirectoryPredicate implements Predicate<File> {
+
+	public boolean apply(final File file) {
+		return file.isDirectory();
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/IsIMonitoringRecordInRange.java b/src/main/java/teetime/stage/predicate/IsIMonitoringRecordInRange.java
new file mode 100644
index 0000000000000000000000000000000000000000..10adfe8e8265980b11c08dd2456ebbfcc82421e6
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/IsIMonitoringRecordInRange.java
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import teetime.common.record.IMonitoringRecord;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class IsIMonitoringRecordInRange extends IsTimestampInRange<IMonitoringRecord> {
+
+	/**
+	 * @since 1.10
+	 */
+	public IsIMonitoringRecordInRange(final long min, final long max) {
+		super(min, max);
+	}
+
+	public boolean apply(final IMonitoringRecord record) {
+		final long timestamp = record.getLoggingTimestamp();
+		return this.isInRange(timestamp);
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/IsInstanceOfPredicate.java b/src/main/java/teetime/stage/predicate/IsInstanceOfPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e0528ee7ef4af466f108430513ff8a8b49d55a2
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/IsInstanceOfPredicate.java
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import java.util.Collection;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class IsInstanceOfPredicate<T> implements Predicate<T> {
+
+	private final Collection<Class<?>> acceptedClasses;
+
+	/**
+	 * @since 1.10
+	 */
+	public IsInstanceOfPredicate(final Collection<Class<?>> acceptedClasses) {
+		this.acceptedClasses = acceptedClasses;
+	}
+
+	public boolean apply(final T object) {
+		for (final Class<?> acceptedClazz : this.acceptedClasses) {
+			if (acceptedClazz.isInstance(object)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordInRange.java b/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordInRange.java
new file mode 100644
index 0000000000000000000000000000000000000000..d660cbcf98f84df5c571d13dcd82c3fc140fb583
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordInRange.java
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import teetime.common.record.controlflow.OperationExecutionRecord;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class IsOperationExecutionRecordInRange extends IsTimestampInRange<OperationExecutionRecord> {
+
+	/**
+	 * @since 1.10
+	 */
+	public IsOperationExecutionRecordInRange(final long min, final long max) {
+		super(min, max);
+	}
+
+	public boolean apply(final OperationExecutionRecord record) {
+		final long tinTimestamp = record.getTin();
+		final boolean isTinInRange = this.isInRange(tinTimestamp);
+		final long toutTimestamp = record.getTout();
+		final boolean isToutInRange = this.isInRange(toutTimestamp);
+		return isTinInRange && isToutInRange;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordTraceIdPredicate.java b/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordTraceIdPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..fbd2bc6b23905e2fd1f5b0240eeeb921b605dcae
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordTraceIdPredicate.java
@@ -0,0 +1,41 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import java.util.Set;
+
+import teetime.common.record.controlflow.OperationExecutionRecord;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class IsOperationExecutionRecordTraceIdPredicate extends IsTraceIdPredicate<OperationExecutionRecord> {
+
+	/**
+	 * @since 1.10
+	 */
+	public IsOperationExecutionRecordTraceIdPredicate(final boolean acceptAllTraces, final Set<Long> selectedTraceIds) {
+		super(acceptAllTraces, selectedTraceIds);
+	}
+
+	public boolean apply(final OperationExecutionRecord record) {
+		final Long traceId = record.getTraceId();
+		return this.acceptId(traceId);
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/IsSuperTypePredicate.java b/src/main/java/teetime/stage/predicate/IsSuperTypePredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c29775abb3bb18815ccd519ecb6beb76678c273
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/IsSuperTypePredicate.java
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import java.util.Collection;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class IsSuperTypePredicate<T> implements Predicate<T> {
+
+	private final Collection<Class<?>> acceptedClasses;
+
+	/**
+	 * @since 1.10
+	 */
+	public IsSuperTypePredicate(final Collection<Class<?>> acceptedClasses) {
+		this.acceptedClasses = acceptedClasses;
+	}
+
+	public boolean apply(final T object) {
+		for (final Class<?> acceptedClazz : this.acceptedClasses) {
+			if (acceptedClazz.isAssignableFrom(object.getClass())) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/IsTimestampInRange.java b/src/main/java/teetime/stage/predicate/IsTimestampInRange.java
new file mode 100644
index 0000000000000000000000000000000000000000..42ad92d5b45baf4d52ac40672e4b6652f1c71465
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/IsTimestampInRange.java
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public abstract class IsTimestampInRange<T> implements Predicate<T> {
+
+	private final long min;
+	private final long max;
+
+	/**
+	 * @since 1.10
+	 */
+	public IsTimestampInRange(final long min, final long max) {
+		this.min = min;
+		this.max = max;
+	}
+
+	protected boolean isInRange(final long timestamp) {
+		return (timestamp >= this.min) && (timestamp <= this.max);
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/IsTraceIdPredicate.java b/src/main/java/teetime/stage/predicate/IsTraceIdPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1d369b3bf976aa5903cfbc2187aac61b328de99
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/IsTraceIdPredicate.java
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import java.util.Set;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public abstract class IsTraceIdPredicate<T> implements Predicate<T> {
+
+	private final boolean acceptAllTraces;
+	private final Set<Long> selectedTraceIds;
+
+	/**
+	 * @since 1.10
+	 */
+	public IsTraceIdPredicate(final boolean acceptAllTraces, final Set<Long> selectedTraceIds) {
+		this.acceptAllTraces = acceptAllTraces;
+		this.selectedTraceIds = selectedTraceIds;
+	}
+
+	protected final boolean acceptId(final Long traceId) {
+		return (this.acceptAllTraces || this.selectedTraceIds.contains(traceId));
+	}
+
+}
diff --git a/src/main/java/teetime/stage/predicate/PredicateFilter.java b/src/main/java/teetime/stage/predicate/PredicateFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a672bfa5a8056099a0b3f188c36768562e2c595
--- /dev/null
+++ b/src/main/java/teetime/stage/predicate/PredicateFilter.java
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.predicate;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Nils Christian Ehmke
+ * @param <T>
+ * 
+ * @since 1.10
+ */
+public class PredicateFilter<T> extends AbstractFilter<PredicateFilter<T>> {
+
+	public final IInputPort<PredicateFilter<T>, T> inputPort = this.createInputPort();
+
+	public final IOutputPort<PredicateFilter<T>, T> matchingOutputPort = this.createOutputPort();
+	public final IOutputPort<PredicateFilter<T>, T> mismatchingOutputPort = this.createOutputPort();
+
+	private Predicate<T> predicate;
+
+	public PredicateFilter(final Predicate<T> predicate) {
+		this.setPredicate(predicate);
+	}
+
+	public PredicateFilter() {
+		super();
+	}
+
+	public Predicate<T> getPredicate() {
+		return this.predicate;
+	}
+
+	public void setPredicate(final Predicate<T> predicate) {
+		this.predicate = predicate;
+	}
+
+	@Override
+	protected boolean execute(final Context<PredicateFilter<T>> context) {
+		final T inputObject = context.tryTake(this.inputPort);
+		if (inputObject == null) {
+			return false;
+		}
+
+		if (this.predicate.apply(inputObject)) {
+			context.put(this.matchingOutputPort, inputObject);
+		} else {
+			context.put(this.mismatchingOutputPort, inputObject);
+		}
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/stringBuffer/StringBufferFilter.java b/src/main/java/teetime/stage/stringBuffer/StringBufferFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..8185632760e6f735288fe0265f93b8064cab3bcc
--- /dev/null
+++ b/src/main/java/teetime/stage/stringBuffer/StringBufferFilter.java
@@ -0,0 +1,93 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.stringBuffer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import teetime.analysis.plugin.filter.forward.util.KiekerHashMap;
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+import teetime.stage.stringBuffer.handler.AbstractDataTypeHandler;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class StringBufferFilter<T> extends AbstractFilter<StringBufferFilter<T>> {
+
+	public final IInputPort<StringBufferFilter<T>, T> objectInputPort = this.createInputPort();
+
+	public final IOutputPort<StringBufferFilter<T>, T> objectOutputPort = this.createOutputPort();
+
+	// BETTER use a non shared data structure to avoid synchronization between threads
+	private KiekerHashMap kiekerHashMap = new KiekerHashMap();
+
+	private Collection<AbstractDataTypeHandler<?>> dataTypeHandlers = new ArrayList<AbstractDataTypeHandler<?>>();
+
+	@Override
+	protected boolean execute(final Context<StringBufferFilter<T>> context) {
+		final T object = context.tryTake(this.objectInputPort);
+		if (object == null) {
+			return false;
+		}
+
+		final T returnedObject = this.handle(object);
+		context.put(this.objectOutputPort, returnedObject);
+
+		return true;
+	}
+
+	@Override
+	public void onPipelineStarts() throws Exception {
+		for (final AbstractDataTypeHandler<?> handler : this.dataTypeHandlers) {
+			handler.setLogger(this.logger);
+			handler.setStringRepository(this.kiekerHashMap);
+		}
+		super.onPipelineStarts();
+	}
+
+	private T handle(final T object) {
+		for (final AbstractDataTypeHandler<?> handler : this.dataTypeHandlers) {
+			if (handler.canHandle(object)) {
+				@SuppressWarnings("unchecked")
+				final T returnedObject = ((AbstractDataTypeHandler<T>) handler).handle(object);
+				return returnedObject;
+			}
+		}
+		return object; // else relay given object
+	}
+
+	public KiekerHashMap getKiekerHashMap() {
+		return this.kiekerHashMap;
+	}
+
+	public void setKiekerHashMap(final KiekerHashMap kiekerHashMap) {
+		this.kiekerHashMap = kiekerHashMap;
+	}
+
+	public Collection<AbstractDataTypeHandler<?>> getDataTypeHandlers() {
+		return this.dataTypeHandlers;
+	}
+
+	public void setDataTypeHandlers(final Collection<AbstractDataTypeHandler<?>> dataTypeHandlers) {
+		this.dataTypeHandlers = dataTypeHandlers;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/stringBuffer/handler/AbstractDataTypeHandler.java b/src/main/java/teetime/stage/stringBuffer/handler/AbstractDataTypeHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..3752aa5e63f2a4d599a88b350c3d4d8a9cb889e0
--- /dev/null
+++ b/src/main/java/teetime/stage/stringBuffer/handler/AbstractDataTypeHandler.java
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.stringBuffer.handler;
+
+import teetime.analysis.plugin.filter.forward.util.KiekerHashMap;
+import teetime.common.logging.Log;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public abstract class AbstractDataTypeHandler<T> {
+
+	protected Log logger;
+	protected KiekerHashMap stringRepository;
+
+	/**
+	 * @since 1.10
+	 */
+	public abstract boolean canHandle(Object object);
+
+	/**
+	 * @since 1.10
+	 */
+	public abstract T handle(T object);
+
+	/**
+	 * @since 1.10
+	 */
+	public void setLogger(final Log logger) {
+		this.logger = logger;
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public void setStringRepository(final KiekerHashMap stringRepository) {
+		this.stringRepository = stringRepository;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/stringBuffer/handler/IMonitoringRecordHandler.java b/src/main/java/teetime/stage/stringBuffer/handler/IMonitoringRecordHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..98de7481e1ecfbd0d82cb3838c64a9f17a5f8049
--- /dev/null
+++ b/src/main/java/teetime/stage/stringBuffer/handler/IMonitoringRecordHandler.java
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.stringBuffer.handler;
+
+import teetime.common.exception.MonitoringRecordException;
+import teetime.common.record.AbstractMonitoringRecord;
+import teetime.common.record.IMonitoringRecord;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class IMonitoringRecordHandler extends AbstractDataTypeHandler<IMonitoringRecord> {
+
+	@Override
+	public boolean canHandle(final Object object) {
+		return object instanceof IMonitoringRecord;
+	}
+
+	@Override
+	public IMonitoringRecord handle(final IMonitoringRecord monitoringRecord) {
+		final Object[] objects = monitoringRecord.toArray();
+
+		boolean stringBuffered = false;
+		for (int i = 0; i < objects.length; i++) {
+			if (objects[i] instanceof String) {
+				objects[i] = this.stringRepository.get((String) objects[i]);
+				stringBuffered = true;
+			}
+		}
+
+		if (stringBuffered) {
+			try {
+				final IMonitoringRecord newRecord = AbstractMonitoringRecord.createFromArray(monitoringRecord.getClass(), objects);
+				newRecord.setLoggingTimestamp(monitoringRecord.getLoggingTimestamp());
+				return newRecord;
+			} catch (final MonitoringRecordException ex) {
+				this.logger.warn("Failed to recreate buffered monitoring record: " + monitoringRecord.toString(), ex);
+			}
+		}
+
+		return monitoringRecord;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/stringBuffer/handler/StringHandler.java b/src/main/java/teetime/stage/stringBuffer/handler/StringHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..82c356faf7727ec9d4b1521a9aceb8594837afca
--- /dev/null
+++ b/src/main/java/teetime/stage/stringBuffer/handler/StringHandler.java
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.stringBuffer.handler;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class StringHandler extends AbstractDataTypeHandler<String> {
+
+	@Override
+	public boolean canHandle(final Object object) {
+		return object instanceof String;
+	}
+
+	@Override
+	public String handle(final String object) {
+		return this.stringRepository.get(object);
+	}
+
+}
diff --git a/src/main/java/teetime/stage/throughput/AnalysisThroughputFilter.java b/src/main/java/teetime/stage/throughput/AnalysisThroughputFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..665fcf4e77800f93b7b633925c9ae2ba56d39d61
--- /dev/null
+++ b/src/main/java/teetime/stage/throughput/AnalysisThroughputFilter.java
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.throughput;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+import teetime.framework.core.IOutputPort;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class AnalysisThroughputFilter<T> extends AbstractFilter<AnalysisThroughputFilter<T>> {
+
+	public final IInputPort<AnalysisThroughputFilter<T>, T> objectInputPort = this.createInputPort();
+	public final IInputPort<AnalysisThroughputFilter<T>, Long> timestampInputPort = this.createInputPort();
+
+	public final IOutputPort<AnalysisThroughputFilter<T>, T> objectOutputPort = this.createOutputPort();
+	public final IOutputPort<AnalysisThroughputFilter<T>, ThroughputAnalysisResult> analysisOutputPort = this.createOutputPort();
+
+	private long lastTimestampInNs;
+	private int numObjectsPassed;
+
+	@Override
+	protected boolean execute(final Context<AnalysisThroughputFilter<T>> context) {
+		final Long timestampInNs = context.tryTake(this.timestampInputPort);
+		if (timestampInNs == null) {
+			final T object = context.tryTake(this.objectInputPort);
+			context.put(this.objectOutputPort, object);
+			this.numObjectsPassed++;
+		} else {
+			final long durationInNs = timestampInNs - this.lastTimestampInNs;
+			context.put(this.analysisOutputPort, new ThroughputAnalysisResult(durationInNs, this.numObjectsPassed));
+			this.reset(timestampInNs);
+		}
+
+		return true;
+	}
+
+	private void reset(final Long timestampInNs) {
+		this.numObjectsPassed = 0;
+		this.lastTimestampInNs = timestampInNs;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/throughput/ThroughputAnalysisResult.java b/src/main/java/teetime/stage/throughput/ThroughputAnalysisResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..35c686b01abeb4257ee3cdfae030e9c2d5349e43
--- /dev/null
+++ b/src/main/java/teetime/stage/throughput/ThroughputAnalysisResult.java
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.throughput;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ThroughputAnalysisResult {
+
+	private final long durationInNs;
+	private final int numObjectsPassed;
+
+	/**
+	 * @since 1.10
+	 */
+	public ThroughputAnalysisResult(final long durationInNs, final int numObjectsPassed) {
+		this.durationInNs = durationInNs;
+		this.numObjectsPassed = numObjectsPassed;
+	}
+
+	public long getDurationInNs() {
+		return durationInNs;
+	}
+
+	public int getNumObjectsPassed() {
+		return numObjectsPassed;
+	}
+
+}
diff --git a/src/main/java/teetime/stage/util/TextLine.java b/src/main/java/teetime/stage/util/TextLine.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc2d9acd666017f343e3fc3017e68871566bd89b
--- /dev/null
+++ b/src/main/java/teetime/stage/util/TextLine.java
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.stage.util;
+
+import java.io.File;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class TextLine {
+
+	private final File textFile;
+	private final String textLine;
+
+	/**
+	 * @since 1.10
+	 */
+	public TextLine(final File textFile, final String textLine) {
+		this.textFile = textFile;
+		this.textLine = textLine;
+	}
+
+	public File getTextFile() {
+		return this.textFile;
+	}
+
+	public String getTextLine() {
+		return this.textLine;
+	}
+}
diff --git a/src/main/java/teetime/stage/visualization/IWebVisualizationSink.java b/src/main/java/teetime/stage/visualization/IWebVisualizationSink.java
new file mode 100644
index 0000000000000000000000000000000000000000..b352004aa561988ed3e181478afa6701a7c607c8
--- /dev/null
+++ b/src/main/java/teetime/stage/visualization/IWebVisualizationSink.java
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.visualization;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ * 
+ * @param <I>
+ *            The type of the input ports
+ */
+public interface IWebVisualizationSink {
+
+	public String getHeader();
+
+	public String getInitialContent();
+
+	public String getUpdatedContent();
+
+}
diff --git a/src/main/java/teetime/stage/visualization/PlainTextWebVisualizationSink.java b/src/main/java/teetime/stage/visualization/PlainTextWebVisualizationSink.java
new file mode 100644
index 0000000000000000000000000000000000000000..2bf71599abebbead2ebf680e9d610e5e23be7d22
--- /dev/null
+++ b/src/main/java/teetime/stage/visualization/PlainTextWebVisualizationSink.java
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.stage.visualization;
+
+import teetime.framework.core.AbstractFilter;
+import teetime.framework.core.Context;
+import teetime.framework.core.IInputPort;
+
+/**
+ * @author Nils Christian Ehmke, Christian Wulf
+ * 
+ * @since 1.10
+ * 
+ * @param <I>
+ *            The type of the input ports
+ */
+public class PlainTextWebVisualizationSink<T> extends AbstractFilter<PlainTextWebVisualizationSink<T>> implements IWebVisualizationSink {
+
+	public final IInputPort<PlainTextWebVisualizationSink<T>, T> INPUT_OBJECT = this.createInputPort();
+
+	private Object currentObject = "N/A";
+
+	/**
+	 * @since 1.10
+	 */
+	public String getHeader() {
+		return "";
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public String getInitialContent() {
+		return this.currentObject.toString();
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	public String getUpdatedContent() {
+		return this.currentObject.toString();
+	}
+
+	/**
+	 * @since 1.10
+	 */
+	@Override
+	protected boolean execute(final Context<PlainTextWebVisualizationSink<T>> context) {
+		final T object = context.tryTake(this.INPUT_OBJECT);
+		if (object == null) {
+			return false;
+		}
+
+		this.currentObject = object;
+
+		return true;
+	}
+
+}
diff --git a/src/main/java/teetime/util/CyclicListIterator.java b/src/main/java/teetime/util/CyclicListIterator.java
new file mode 100644
index 0000000000000000000000000000000000000000..71e70bbb60a2002ab498c19b4298344c52b2e054
--- /dev/null
+++ b/src/main/java/teetime/util/CyclicListIterator.java
@@ -0,0 +1,54 @@
+package teetime.util;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This iterator infinitely iterates over a list and allows the list to be modified without throwing a <code>ConcurrentMOdificationException</code>.
+ * 
+ * @author Christian Wulf
+ * 
+ * @param <T>
+ */
+public class CyclicListIterator<T> implements Iterator<T> {
+
+	private final List<T> list;
+	// private Iterator<T> iterator;
+
+	private int currentIndex = 0;
+
+	public CyclicListIterator(final List<T> list) {
+		this.list = list;
+		// this.iterator = this.list.iterator();
+	}
+
+	public boolean hasNext() {
+		return true;
+	}
+
+	public T next() {
+		// if (!this.iterator.hasNext()) {
+		// this.iterator = this.list.iterator();
+		// }
+		// return this.iterator.next();
+
+		// the size of the list could have been changed due to
+		// <li>an index overflow (then restart from index 0), or
+		// <li>an add() or a remove(), so update the index
+		this.currentIndex = this.getCurrentIndex();
+		final T element = this.list.get(this.currentIndex);
+		this.currentIndex++;
+		return element;
+	}
+
+	public void remove() {
+		// this.iterator.remove();
+		this.currentIndex = this.getCurrentIndex();
+		this.list.remove(this.currentIndex);
+	}
+
+	private int getCurrentIndex() {
+		return this.currentIndex % this.list.size();
+	}
+
+}
diff --git a/src/main/java/teetime/util/MathUtil.java b/src/main/java/teetime/util/MathUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..71a36bc0340811b246c5ce21abbf59ccd53a1c30
--- /dev/null
+++ b/src/main/java/teetime/util/MathUtil.java
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.util;
+
+import java.util.List;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class MathUtil {
+
+	private MathUtil() {
+		// utility class
+	}
+
+	public static double getVariance(final List<Long> values, final long avgValue) {
+		double sum = 0;
+		for (final long val : values) {
+			final long diff = val - avgValue;
+			sum += (diff * diff) / (values.size() - 1);
+		}
+		return sum;
+	}
+
+	public static double getConfidenceWidth(final double z, final double variance, final long n) {
+		return z * Math.sqrt(variance / n);
+	}
+
+	public static double getConfidenceWidth(final double z, final List<Long> values, final long avgValue) {
+		final double variance = MathUtil.getVariance(values, avgValue);
+		final double confidenceWidth = MathUtil.getConfidenceWidth(z, variance, values.size());
+		return confidenceWidth;
+	}
+}
diff --git a/src/main/java/teetime/util/Pair.java b/src/main/java/teetime/util/Pair.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f5018057b1e88b19f5a5f81fb41f686a7f0cd19
--- /dev/null
+++ b/src/main/java/teetime/util/Pair.java
@@ -0,0 +1,25 @@
+package teetime.util;
+
+public class Pair<F, S> {
+
+	private final F first;
+	private final S second;
+
+	public Pair(final F first, final S second) {
+		this.first = first;
+		this.second = second;
+	}
+
+	public static <F, S> Pair<F, S> of(final F first, final S second) {
+		return new Pair<F, S>(first, second);
+	}
+
+	public F getFirst() {
+		return this.first;
+	}
+
+	public S getSecond() {
+		return this.second;
+	}
+
+}
diff --git a/src/main/java/teetime/util/StacklessException.java b/src/main/java/teetime/util/StacklessException.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d6be60768473bfda5a2ca1352e66bfd26064e2a
--- /dev/null
+++ b/src/main/java/teetime/util/StacklessException.java
@@ -0,0 +1,12 @@
+package teetime.util;
+
+public class StacklessException extends RuntimeException {
+
+	private static final long serialVersionUID = -9040980547278981254L;
+
+	@Override
+	public synchronized Throwable fillInStackTrace() { // greatly improves performance when constructing
+		return this;
+	}
+
+}
diff --git a/src/main/java/teetime/util/StatisticsUtil.java b/src/main/java/teetime/util/StatisticsUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..48a023b2bd267b907efee92cc3d5c82e04bb0f01
--- /dev/null
+++ b/src/main/java/teetime/util/StatisticsUtil.java
@@ -0,0 +1,112 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+import teetime.examples.throughput.TimestampObject;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class StatisticsUtil {
+
+	/**
+	 * @since 1.10
+	 */
+	private StatisticsUtil() {
+		// utility class
+	}
+
+	public static void calculateAvg(final List<Long> durations) {
+
+	}
+
+	public static void printStatistics(final long overallDurationInNs, final List<TimestampObject> timestampObjects) {
+		System.out.println("Duration: " + TimeUnit.NANOSECONDS.toMillis(overallDurationInNs) + " ms");
+
+		final List<Long> sortedDurationsInNs = new ArrayList<Long>(timestampObjects.size() / 2);
+		long minDurationInNs = Long.MAX_VALUE;
+		long maxDurationInNs = Long.MIN_VALUE;
+		long sumInNs = 0;
+		for (int i = timestampObjects.size() / 2; i < timestampObjects.size(); i++) {
+			final TimestampObject timestampObject = timestampObjects.get(i);
+			final long durationInNs = timestampObject.getStopTimestamp() - timestampObject.getStartTimestamp();
+			// sortedDurationsInNs.set(i - (timestampObjects.size() / 2), durationInNs);
+			sortedDurationsInNs.add(durationInNs);
+			minDurationInNs = Math.min(durationInNs, minDurationInNs);
+			maxDurationInNs = Math.max(durationInNs, maxDurationInNs);
+			sumInNs += durationInNs;
+		}
+
+		final Map<Double, Long> quintileValues = StatisticsUtil.calculateQuintiles(sortedDurationsInNs);
+
+		System.out.println("min: " + TimeUnit.NANOSECONDS.toMicros(minDurationInNs) + " µs");
+		System.out.println("max: " + TimeUnit.NANOSECONDS.toMicros(maxDurationInNs) + " µs");
+		final long avgDurInNs = sumInNs / (timestampObjects.size() / 2);
+		System.out.println("avg duration: " + TimeUnit.NANOSECONDS.toMicros(avgDurInNs) + " µs");
+
+		for (final Entry<Double, Long> entry : quintileValues.entrySet()) {
+			System.out.println((entry.getKey() * 100) + " % : " + TimeUnit.NANOSECONDS.toMicros(entry.getValue()) + " µs");
+		}
+
+		final long confidenceWidthInNs = StatisticsUtil.calculateConfidenceWidth(sortedDurationsInNs, avgDurInNs);
+
+		System.out.println("confidenceWidth: " + confidenceWidthInNs + " ns");
+		System.out.println("[" + TimeUnit.NANOSECONDS.toMicros(avgDurInNs - confidenceWidthInNs) + " µs, "
+				+ TimeUnit.NANOSECONDS.toMicros(avgDurInNs + confidenceWidthInNs) + " µs]");
+	}
+
+	public static long calculateConfidenceWidth(final List<Long> durations, final long avgDurInNs) {
+		final double z = 1.96; // for alpha = 0.05
+		final double variance = MathUtil.getVariance(durations, avgDurInNs);
+		final long confidenceWidthInNs = (long) MathUtil.getConfidenceWidth(z, variance, durations.size());
+		return confidenceWidthInNs;
+	}
+
+	public static long calculateConfidenceWidth(final List<Long> durations) {
+		return StatisticsUtil.calculateConfidenceWidth(durations, StatisticsUtil.calculateAverage(durations));
+	}
+
+	public static long calculateAverage(final List<Long> durations) {
+		long sumNs = 0;
+		for (final Long value : durations) {
+			sumNs += value;
+		}
+
+		return sumNs / durations.size();
+	}
+
+	public static Map<Double, Long> calculateQuintiles(final List<Long> durationsInNs) {
+		Collections.sort(durationsInNs);
+
+		final Map<Double, Long> quintileValues = new LinkedHashMap<Double, Long>();
+		final double[] quintiles = { 0.00, 0.25, 0.50, 0.75, 1.00 };
+		for (final double quintile : quintiles) {
+			final int index = (int) ((durationsInNs.size() - 1) * quintile);
+			quintileValues.put(quintile, durationsInNs.get(index));
+		}
+		return quintileValues;
+	}
+}
diff --git a/src/main/java/teetime/util/StopWatch.java b/src/main/java/teetime/util/StopWatch.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd59385903349ec803b3841e2377c49409895569
--- /dev/null
+++ b/src/main/java/teetime/util/StopWatch.java
@@ -0,0 +1,19 @@
+package teetime.util;
+
+public final class StopWatch {
+
+	private long startTimeInMs;
+	private long endTimeInMs;
+
+	public final void start() {
+		this.startTimeInMs = System.nanoTime();
+	}
+
+	public final void end() {
+		this.endTimeInMs = System.nanoTime();
+	}
+
+	public final long getDuration() {
+		return this.endTimeInMs - this.startTimeInMs;
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/P1C1QueueOriginal3.java b/src/main/java/teetime/util/concurrent/P1C1QueueOriginal3.java
new file mode 100644
index 0000000000000000000000000000000000000000..996f6490cd4d3b4472968684c6ccae9cd85dcad4
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/P1C1QueueOriginal3.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2012 Real Logic Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package teetime.util.concurrent;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * <ul>
+ * <li>Lock free, observing single writer principal.
+ * <li>Replacing the long fields with AtomicLong and using lazySet instead of volatile assignment.
+ * <li>Using the power of 2 mask, forcing the capacity to next power of 2.
+ * <li>Adding head and tail cache fields. Avoiding redundant volatile reads.
+ * <li>Padding head/tail AtomicLong fields. Avoiding false sharing.
+ * <li>Padding head/tail cache fields. Avoiding false sharing.
+ * </ul>
+ */
+public final class P1C1QueueOriginal3<E> implements Queue<E> {
+	private final int capacity;
+	private final int mask;
+	private final E[] buffer;
+
+	private final AtomicLong tail = new PaddedAtomicLong(0);
+	private final AtomicLong head = new PaddedAtomicLong(0);
+
+	public static class PaddedLong {
+		public long value = 0, p1, p2, p3, p4, p5, p6;
+	}
+
+	private final PaddedLong tailCache = new PaddedLong();
+	private final PaddedLong headCache = new PaddedLong();
+
+	@SuppressWarnings("unchecked")
+	public P1C1QueueOriginal3(final int capacity) {
+		this.capacity = P1C1QueueOriginal3.findNextPositivePowerOfTwo(capacity);
+		this.mask = this.capacity - 1;
+		this.buffer = (E[]) new Object[this.capacity];
+	}
+
+	public static int findNextPositivePowerOfTwo(final int value) {
+		return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));
+	}
+
+	public boolean add(final E e) {
+		if (this.offer(e)) {
+			return true;
+		}
+
+		throw new IllegalStateException("Queue is full");
+	}
+
+	public boolean offer(final E e) {
+		if (null == e) {
+			throw new NullPointerException("Null is not a valid element");
+		}
+
+		final long currentTail = this.tail.get();
+		final long wrapPoint = currentTail - this.capacity;
+		if (this.headCache.value <= wrapPoint) {
+			this.headCache.value = this.head.get();
+			if (this.headCache.value <= wrapPoint) {
+				return false;
+			}
+		}
+
+		this.buffer[(int) currentTail & this.mask] = e;
+		this.tail.lazySet(currentTail + 1);
+
+		return true;
+	}
+
+	public E poll() {
+		final long currentHead = this.head.get();
+		if (currentHead >= this.tailCache.value) {
+			this.tailCache.value = this.tail.get();
+			if (currentHead >= this.tailCache.value) {
+				return null;
+			}
+		}
+
+		final int index = (int) currentHead & this.mask;
+		final E e = this.buffer[index];
+		this.buffer[index] = null;
+		this.head.lazySet(currentHead + 1);
+
+		return e;
+	}
+
+	public E remove() {
+		final E e = this.poll();
+		if (null == e) {
+			throw new NoSuchElementException("Queue is empty");
+		}
+
+		return e;
+	}
+
+	public E element() {
+		final E e = this.peek();
+		if (null == e) {
+			throw new NoSuchElementException("Queue is empty");
+		}
+
+		return e;
+	}
+
+	public E peek() {
+		return this.buffer[(int) this.head.get() & this.mask];
+	}
+
+	public int size() {
+		return (int) (this.tail.get() - this.head.get());
+	}
+
+	public boolean isEmpty() {
+		return this.tail.get() == this.head.get();
+	}
+
+	public boolean contains(final Object o) {
+		if (null == o) {
+			return false;
+		}
+
+		for (long i = this.head.get(), limit = this.tail.get(); i < limit; i++) {
+			final E e = this.buffer[(int) i & this.mask];
+			if (o.equals(e)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public Iterator<E> iterator() {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object[] toArray() {
+		throw new UnsupportedOperationException();
+	}
+
+	public <T> T[] toArray(final T[] a) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(final Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean containsAll(final Collection<?> c) {
+		for (final Object o : c) {
+			if (!this.contains(o)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	public boolean addAll(final Collection<? extends E> c) {
+		for (final E e : c) {
+			this.add(e);
+		}
+
+		return true;
+	}
+
+	public boolean removeAll(final Collection<?> c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(final Collection<?> c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		Object value;
+		do {
+			value = this.poll();
+		} while (null != value);
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/PaddedAtomicLong.java b/src/main/java/teetime/util/concurrent/PaddedAtomicLong.java
new file mode 100644
index 0000000000000000000000000000000000000000..eeffdaf28396f0d196c9dc908f3473f17d303c05
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/PaddedAtomicLong.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Real Logic Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package teetime.util.concurrent;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class PaddedAtomicLong extends AtomicLong {
+	public PaddedAtomicLong() {}
+
+	public PaddedAtomicLong(final long initialValue) {
+		super(initialValue);
+	}
+
+	public volatile long p1, p2, p3, p4, p5, p6 = 7;
+}
diff --git a/src/main/java/teetime/util/concurrent/hashmap/ConcurrentHashMapWithDefault.java b/src/main/java/teetime/util/concurrent/hashmap/ConcurrentHashMapWithDefault.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ddaefc5e2fdb53f46770d510828ee9657a9e7cf
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/hashmap/ConcurrentHashMapWithDefault.java
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.util.concurrent.hashmap;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ConcurrentHashMapWithDefault<K, V> extends ConcurrentHashMap<K, V> {
+
+	private static final long serialVersionUID = -7958038532219740472L;
+
+	private final ValueFactory<V> valueFactory;
+
+	/**
+	 * @since 1.10
+	 */
+	public ConcurrentHashMapWithDefault(final ValueFactory<V> valueFactory) {
+		this.valueFactory = valueFactory;
+	}
+
+	/**
+	 * @return the corresponding value if the key exists. Otherwise, it creates,
+	 *         inserts, and returns a new default value.
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public V get(final Object key) {
+		V value = super.get(key);
+		if (value == null) {
+			synchronized (this) {
+				value = super.get(key);
+				if (value == null) { // NOCS (DCL)
+					value = this.valueFactory.create();
+					super.put((K) key, value);
+				}
+			}
+		}
+		return value;
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/hashmap/TraceBuffer.java b/src/main/java/teetime/util/concurrent/hashmap/TraceBuffer.java
new file mode 100644
index 0000000000000000000000000000000000000000..52184dddc8770639b76bb22c8612ab13a74112fb
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/hashmap/TraceBuffer.java
@@ -0,0 +1,152 @@
+package teetime.util.concurrent.hashmap;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import kieker.analysis.plugin.filter.flow.TraceEventRecords;
+import kieker.common.logging.Log;
+import kieker.common.logging.LogFactory;
+import kieker.common.record.flow.trace.AbstractTraceEvent;
+import kieker.common.record.flow.trace.TraceMetadata;
+import kieker.common.record.flow.trace.operation.AfterOperationEvent;
+import kieker.common.record.flow.trace.operation.AfterOperationFailedEvent;
+import kieker.common.record.flow.trace.operation.BeforeOperationEvent;
+
+/**
+ * The TraceBuffer is synchronized to prevent problems with concurrent access.
+ * 
+ * @author Jan Waller
+ */
+public final class TraceBuffer implements ValueFactory<TraceBuffer> {
+	private static final Log LOG = LogFactory.getLog(TraceBuffer.class);
+	private static final Comparator<AbstractTraceEvent> COMPARATOR = new TraceEventComperator();
+
+	private TraceMetadata trace;
+	private final SortedSet<AbstractTraceEvent> events = new TreeSet<AbstractTraceEvent>(COMPARATOR);
+
+	private boolean closeable;
+	private boolean damaged;
+	private int openEvents;
+	private int maxOrderIndex = -1;
+
+	private long minLoggingTimestamp = Long.MAX_VALUE;
+	private long maxLoggingTimestamp = -1;
+
+	private long traceId = -1;
+
+	/**
+	 * Creates a new instance of this class.
+	 */
+	public TraceBuffer() {
+		// default empty constructor
+	}
+
+	public void insertEvent(final AbstractTraceEvent event) {
+		final long myTraceId = event.getTraceId();
+		synchronized (this) {
+			if (this.traceId == -1) {
+				this.traceId = myTraceId;
+			} else if (this.traceId != myTraceId) {
+				LOG.error("Invalid traceId! Expected: " + this.traceId + " but found: " + myTraceId + " in event " + event.toString());
+				this.damaged = true;
+			}
+			final long loggingTimestamp = event.getTimestamp();
+			if (loggingTimestamp > this.maxLoggingTimestamp) {
+				this.maxLoggingTimestamp = loggingTimestamp;
+			}
+			if (loggingTimestamp < this.minLoggingTimestamp) {
+				this.minLoggingTimestamp = loggingTimestamp;
+			}
+			final int orderIndex = event.getOrderIndex();
+			if (orderIndex > this.maxOrderIndex) {
+				this.maxOrderIndex = orderIndex;
+			}
+			if (event instanceof BeforeOperationEvent) {
+				if (orderIndex == 0) {
+					this.closeable = true;
+				}
+				this.openEvents++;
+			} else if (event instanceof AfterOperationEvent) {
+				this.openEvents--;
+			} else if (event instanceof AfterOperationFailedEvent) {
+				this.openEvents--;
+			}
+			if (!this.events.add(event)) {
+				LOG.error("Duplicate entry for orderIndex " + orderIndex + " with traceId " + myTraceId);
+				this.damaged = true;
+			}
+		}
+	}
+
+	public void setTrace(final TraceMetadata trace) {
+		final long myTraceId = trace.getTraceId();
+		synchronized (this) {
+			if (this.traceId == -1) {
+				this.traceId = myTraceId;
+			} else if (this.traceId != myTraceId) {
+				LOG.error("Invalid traceId! Expected: " + this.traceId + " but found: " + myTraceId + " in trace " + trace.toString());
+				this.damaged = true;
+			}
+			if (this.trace == null) {
+				this.trace = trace;
+			} else {
+				LOG.error("Duplicate Trace entry for traceId " + myTraceId);
+				this.damaged = true;
+			}
+		}
+	}
+
+	public boolean isFinished() {
+		synchronized (this) {
+			return this.closeable && !this.isInvalid();
+		}
+	}
+
+	public boolean isInvalid() {
+		synchronized (this) {
+			return (this.trace == null) || this.damaged || (this.openEvents != 0) || (((this.maxOrderIndex + 1) != this.events.size()) || this.events.isEmpty());
+		}
+	}
+
+	public TraceEventRecords toTraceEvents() {
+		synchronized (this) {
+			return new TraceEventRecords(this.trace, this.events.toArray(new AbstractTraceEvent[this.events.size()]));
+		}
+	}
+
+	public long getMaxLoggingTimestamp() {
+		synchronized (this) {
+			return this.maxLoggingTimestamp;
+		}
+	}
+
+	public long getMinLoggingTimestamp() {
+		synchronized (this) {
+			return this.minLoggingTimestamp;
+		}
+	}
+
+	/**
+	 * @author Jan Waller
+	 */
+	private static final class TraceEventComperator implements Comparator<AbstractTraceEvent>, Serializable {
+		private static final long serialVersionUID = 8920737343446332517L;
+
+		/**
+		 * Creates a new instance of this class.
+		 */
+		public TraceEventComperator() {
+			// default empty constructor
+		}
+
+		public int compare(final AbstractTraceEvent o1, final AbstractTraceEvent o2) {
+			return o1.getOrderIndex() - o2.getOrderIndex();
+		}
+	}
+
+	public TraceBuffer create() {
+		return new TraceBuffer();
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/hashmap/ValueFactory.java b/src/main/java/teetime/util/concurrent/hashmap/ValueFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..3cca62d6204c19264f4d94f0fea94fd128b29588
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/hashmap/ValueFactory.java
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.util.concurrent.hashmap;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public interface ValueFactory<T> {
+
+	/**
+	 * Create a new instance of the type <code>T</code>.
+	 * 
+	 * @since 1.10
+	 */
+	public T create();
+}
diff --git a/src/main/java/teetime/util/concurrent/workstealing/CircularArray.java b/src/main/java/teetime/util/concurrent/workstealing/CircularArray.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1daf143f2e66e6f70dbf5ce8094fa763e6b4082
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/workstealing/CircularArray.java
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.util.concurrent.workstealing;
+
+import java.util.Arrays;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @see "Dynamic Circular WorkStealing Deque"
+ * 
+ * @since 1.10
+ * 
+ * @param <T>
+ */
+public class CircularArray<T> {
+
+	private final long logSize;
+	private final T[] segment;
+
+	/**
+	 * 
+	 * @param logSize
+	 *            The initial size of this array in log2, i.e., the number of bits to use
+	 */
+	@SuppressWarnings("unchecked")
+	public CircularArray(final long logSize) {
+		this.logSize = logSize;
+		this.segment = (T[]) new Object[1 << this.logSize];
+	}
+
+	public long getCapacity() {
+		return 1 << this.logSize;
+	}
+
+	public T get(final long i) {
+		return this.segment[(int) (i % this.getCapacity())]; // risk of overflow
+	}
+
+	public void put(final long i, final T o) {
+		this.segment[(int) (i % this.getCapacity())] = o; // risk of overflow
+	}
+
+	public CircularArray<T> grow(final long b, final long t) {
+		final CircularArray<T> a = new CircularArray<T>(this.logSize + 1);
+		for (long i = t; i < b; i++) {
+			a.put(i, this.get(i));
+		}
+		return a;
+	}
+
+	public CircularArray<T> shrink(final long b, final long t) {
+		final CircularArray<T> a = new CircularArray<T>(this.logSize - 1);
+		for (long i = t; i < b; i++) {
+			a.put(i, this.get(i));
+		}
+		return a;
+	}
+
+	@Override
+	public String toString() {
+		return Arrays.toString(this.segment);
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/workstealing/CircularWorkStealingDeque.java b/src/main/java/teetime/util/concurrent/workstealing/CircularWorkStealingDeque.java
new file mode 100644
index 0000000000000000000000000000000000000000..3be668703c53149c0fca2a81cdfa6565ccadb788
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/workstealing/CircularWorkStealingDeque.java
@@ -0,0 +1,311 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.util.concurrent.workstealing;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @see "Dynamic Circular WorkStealing Deque"
+ * 
+ * @since 1.10
+ */
+public class CircularWorkStealingDeque<T> {
+
+	public static class DequeIsEmptyException extends DequePopException {
+		private static final long serialVersionUID = -6685406255103741724L;
+	}
+
+	public static final DequeIsEmptyException DEQUE_IS_EMPTY_EXCEPTION = new DequeIsEmptyException();
+
+	public static class OperationAbortedException extends DequePopException {
+		private static final long serialVersionUID = 2983001853326344073L;
+	}
+
+	public static final OperationAbortedException OPERATION_ABORTED_EXCEPTION = new OperationAbortedException();
+
+	private static final long LOG_INITIAL_SIZE = 10;
+
+	private volatile long bottom = 0;
+	private final AtomicLong top = new AtomicLong();
+	private volatile CircularArray<T> activeArray = new CircularArray<T>(LOG_INITIAL_SIZE);
+
+	private boolean casTop(final long oldVal, final long newVal) {
+		return this.top.compareAndSet(oldVal, newVal);
+	}
+
+	/**
+	 * 
+	 * @param o
+	 *            a non-<code>null</code> element
+	 */
+	public void pushBottom(final T o) {
+		final long b = this.bottom;
+		final long t = this.top.get();
+		CircularArray<T> a = this.activeArray;
+		final int numElementsToPush = 1;
+		final long currentSize = b - t;
+		final long newSize = currentSize + numElementsToPush;
+		if (newSize > a.getCapacity()) {
+			a = a.grow(b, t);
+			this.activeArray = a;
+		}
+		a.put(b, o);
+		this.bottom = b + numElementsToPush;
+	}
+
+	/**
+	 * 
+	 * @param elements
+	 *            a non-<code>null</code> list
+	 */
+	public void pushBottomMultiple(final List<T> elements) {
+		final long b = this.bottom;
+		final long t = this.top.get();
+		CircularArray<T> a = this.activeArray;
+		final int numElementsToPush = elements.size();
+		final long currentSize = b - t;
+		final long newSize = currentSize + numElementsToPush;
+		if (newSize > a.getCapacity()) {
+			a = a.grow(b, t);
+			this.activeArray = a;
+		}
+
+		for (final T elem : elements) {
+			a.put(b, elem);
+		}
+
+		this.bottom = b + numElementsToPush;
+	}
+
+	/**
+	 * Returns and removes the latest element from this deque.
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>null</code> if the deque contains no elements,
+	 *         <li><i>the latest element</i> otherwise
+	 *         </ul>
+	 */
+	public T popBottom() {
+		long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		b = b - 1;
+		this.bottom = b;
+		final long t = this.top.get();
+		final long size = b - t;
+		if (size < 0) {
+			this.bottom = t;
+			return this.empty();
+		}
+		T o = this.regular(a.get(b));
+		if (size > 0) {
+			this.perhapsShrink(b, t);
+			return o;
+		}
+		if (!this.casTop(t, t + 1)) {
+			o = this.empty();
+		}
+		this.bottom = t + 1;
+		return o;
+	}
+
+	/**
+	 * Returns and removes the latest element from this deque.
+	 * 
+	 * @return <i>the latest element</i>, otherwise it throws a <code>DequeIsEmptyException</code>
+	 * 
+	 * @throws DequeIsEmptyException
+	 */
+	public T popBottomEx() {
+		long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		b = b - 1;
+		this.bottom = b;
+		final long t = this.top.get();
+		final long size = b - t;
+		if (size < 0) {
+			this.bottom = t;
+			return this.emptyEx();
+		}
+		T o = this.regular(a.get(b));
+		if (size > 0) {
+			this.perhapsShrink(b, t);
+			return o;
+		}
+		if (!this.casTop(t, t + 1)) {
+			o = this.emptyEx();
+		}
+		this.bottom = t + 1;
+		return o;
+	}
+
+	private void perhapsShrink(final long b, final long t) {
+		long temp = t;
+		final CircularArray<T> a = this.activeArray;
+		if ((b - temp) < (a.getCapacity() / 4)) {
+			final CircularArray<T> aa = a.shrink(b, temp);
+			this.activeArray = aa;
+			final long ss = aa.getCapacity();
+			this.bottom = b + ss;
+			temp = this.top.get();
+			if (!this.casTop(temp, temp + ss)) {
+				this.bottom = b;
+				// a.free();
+			}
+		}
+	}
+
+	/**
+	 * Tries to steal (return & remove) the oldest element from this deque.
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>null</code> if the deque contains no elements,
+	 *         <li>(and also) <code>null</code> if the deque is currently being stolen by another thread,
+	 *         <li><i>the oldest element</i> otherwise
+	 *         </ul>
+	 */
+	public T steal() {
+		final long t = this.top.get();
+		final CircularArray<T> oldArr = this.activeArray;
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final long size = b - t;
+		if (size <= 0) {
+			return this.empty();
+		}
+		if ((size % a.getCapacity()) == 0) {
+			if ((oldArr == a) && (t == this.top.get())) {
+				return this.empty();
+			} else {
+				return this.abort();
+			}
+		}
+		final T o = this.regular(a.get(t));
+		if (!this.casTop(t, t + 1)) {
+			return this.abort();
+		}
+		return o;
+	}
+
+	/**
+	 * Tries to steal (return & remove) the oldest element from this deque.
+	 * 
+	 * @return <i>the oldest element</i>, otherwise it throws a <code>DequeIsEmptyException</code> or a <code>OperationAbortedException</code>
+	 * 
+	 * @throws DequeIsEmptyException
+	 * @throws OperationAbortedException
+	 */
+	public T stealEx() {
+		final long t = this.top.get();
+		final CircularArray<T> oldArr = this.activeArray;
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final long size = b - t;
+		if (size <= 0) {
+			return this.emptyEx();
+		}
+		if ((size % a.getCapacity()) == 0) {
+			if ((oldArr == a) && (t == this.top.get())) {
+				return this.emptyEx();
+			} else {
+				return this.abortEx();
+			}
+		}
+		final T o = this.regular(a.get(t));
+		if (!this.casTop(t, t + 1)) {
+			return this.abortEx();
+		}
+		return o;
+	}
+
+	private T empty() {
+		return null;
+	}
+
+	private T emptyEx() {
+		throw DEQUE_IS_EMPTY_EXCEPTION;
+	}
+
+	private T abort() {
+		return null;
+	}
+
+	private T abortEx() {
+		throw OPERATION_ABORTED_EXCEPTION;
+	}
+
+	private T regular(final T value) {
+		return value;
+	}
+
+	/**
+	 * Returns but does not remove the latest element from this deque.<br>
+	 * <i>For debugging purposes</i>
+	 * 
+	 * @return <ul>
+	 *         <li><code>null</code> if the deque contains no elements,
+	 *         <li><i>the latest element</i> otherwise
+	 *         </ul>
+	 */
+	public T readBottom() {
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final T o = a.get(b);
+		return o;
+	}
+
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 29993
+	// bottom: 29992
+	//
+	//
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 30008
+	// bottom: 30007
+
+	public boolean isEmpty() {
+		final long t = this.top.get();
+		final long b = this.bottom;
+		return t >= b;
+	}
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return the number of elements this deque contains
+	 */
+	public long size(final Object sourceStage) {
+		final long t = this.top.get();
+		final long b = this.bottom;
+		final long size = b - t;
+		System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom);
+		return size;
+	}
+
+	@Override
+	public String toString() {
+		return this.activeArray.toString();
+	}
+
+}
diff --git a/src/main/java/teetime/util/concurrent/workstealing/DequePopException.java b/src/main/java/teetime/util/concurrent/workstealing/DequePopException.java
new file mode 100644
index 0000000000000000000000000000000000000000..11fc8b079dd1b87bf4f814b208aa761fc7f713fe
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/workstealing/DequePopException.java
@@ -0,0 +1,8 @@
+package teetime.util.concurrent.workstealing;
+
+import teetime.util.StacklessException;
+
+public class DequePopException extends StacklessException {
+	private static final long serialVersionUID = 496512683536868149L;
+
+}
diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithSentinel.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithSentinel.java
new file mode 100644
index 0000000000000000000000000000000000000000..854839c52b124a043181a4cf3c6f598577c0d9a3
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithSentinel.java
@@ -0,0 +1,202 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.util.concurrent.workstealing.alternative;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import teetime.util.concurrent.workstealing.CircularArray;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @see "Dynamic Circular WorkStealing Deque"
+ * 
+ * @since 1.10
+ */
+public class CircularWorkStealingDequeWithSentinel<T> {
+
+	public static enum State {
+		REGULAR, EMPTY, ABORT
+	}
+
+	public static class ReturnValue<T> {
+		private final State state;
+		private final T value;
+
+		public ReturnValue(final State state, final T value) {
+			this.state = state;
+			this.value = value;
+		}
+
+		public T getValue() {
+			return this.value;
+		}
+
+		public State getState() {
+			return this.state;
+		}
+	}
+
+	private static final long LOG_INITIAL_SIZE = 10;
+
+	private volatile long bottom = 0;
+	// private volatile long top = 0;
+	private final AtomicLong top = new AtomicLong();
+	private volatile CircularArray<T> activeArray = new CircularArray<T>(LOG_INITIAL_SIZE);
+
+	private boolean casTop(final long oldVal, final long newVal) {
+		// boolean preCond;
+		// synchronized (this) {
+		// preCond = (this.top == oldVal);
+		// if (preCond) {
+		// this.top = newVal;
+		// }
+		// }
+		// return preCond;
+		return this.top.compareAndSet(oldVal, newVal);
+	}
+
+	public void pushBottom(final T o) {
+		final long b = this.bottom;
+		final long t = this.top.get();
+		CircularArray<T> a = this.activeArray;
+		final long size = b - t;
+		if (size > (a.getCapacity() - 1)) {
+			a = a.grow(b, t);
+			this.activeArray = a;
+		}
+		a.put(b, o);
+		this.bottom = b + 1;
+	}
+
+	/**
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>empty()</code> if the deque contains no elements,
+	 *         <li><i>the latest element</i> otherwise
+	 *         </ul>
+	 */
+	public ReturnValue<T> popBottom() {
+		long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		b = b - 1;
+		this.bottom = b;
+		final long t = this.top.get();
+		final long size = b - t;
+		if (size < 0) {
+			this.bottom = t;
+			return new ReturnValue<T>(State.EMPTY, null);
+		}
+		ReturnValue<T> o = new ReturnValue<T>(State.REGULAR, a.get(b));
+		if (size > 0) {
+			this.perhapsShrink(b, t);
+			return o;
+		}
+		if (!this.casTop(t, t + 1)) {
+			o = new ReturnValue<T>(State.EMPTY, null);
+		}
+		this.bottom = t + 1;
+		return o;
+	}
+
+	void perhapsShrink(final long b, final long t) {
+		long temp = t;
+		final CircularArray<T> a = this.activeArray;
+		if ((b - temp) < (a.getCapacity() / 4)) {
+			final CircularArray<T> aa = a.shrink(b, temp);
+			this.activeArray = aa;
+			final long ss = aa.getCapacity();
+			this.bottom = b + ss;
+			temp = this.top.get();
+			if (!this.casTop(temp, temp + ss)) {
+				this.bottom = b;
+				// a.free();
+			}
+		}
+	}
+
+	/**
+	 * Tries to steal (return & remove) the oldest element from this deque.
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>empty()</code> if the deque contains no elements,
+	 *         <li><code>abort()</code> if the deque is currently being stolen by another thread,
+	 *         <li><i>the oldest element</i> otherwise
+	 *         </ul>
+	 */
+	public ReturnValue<T> steal() {
+		final long t = this.top.get();
+		final CircularArray<T> oldArr = this.activeArray;
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final long size = b - t;
+		if (size <= 0) {
+			return new ReturnValue<T>(State.EMPTY, null);
+		}
+		if ((size % a.getCapacity()) == 0) {
+			if ((oldArr == a) && (t == this.top.get())) {
+				return new ReturnValue<T>(State.EMPTY, null);
+			} else {
+				return new ReturnValue<T>(State.ABORT, null);
+			}
+		}
+		final ReturnValue<T> o = new ReturnValue<T>(State.REGULAR, a.get(t));
+		if (!this.casTop(t, t + 1)) {
+			return new ReturnValue<T>(State.ABORT, null);
+		}
+		return o;
+	}
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return but does not remove the bottom element from this deque
+	 */
+	public T readBottom() {
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final T o = a.get(b);
+		return o;
+	}
+
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 29993
+	// bottom: 29992
+	//
+	//
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 30008
+	// bottom: 30007
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return the number of elements this deque contains
+	 */
+	public long size(final Object sourceStage) {
+		final long t = this.top.get();
+		final long b = this.bottom;
+		final long size = b - t;
+		System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom);
+		return size;
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithThreadLocalSentinel.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithThreadLocalSentinel.java
new file mode 100644
index 0000000000000000000000000000000000000000..746765a1cb646995aa143871b709b196c75431fe
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithThreadLocalSentinel.java
@@ -0,0 +1,226 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.util.concurrent.workstealing.alternative;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import teetime.util.concurrent.workstealing.CircularArray;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @see "Dynamic Circular WorkStealing Deque"
+ * 
+ * @since 1.10
+ */
+public class CircularWorkStealingDequeWithThreadLocalSentinel<T> {
+
+	public static enum State {
+		REGULAR, EMPTY, ABORT
+	}
+
+	public static class ReturnValue<T> {
+		private State state;
+		private T value;
+
+		public State getState() {
+			return this.state;
+		}
+
+		public T getValue() {
+			return this.value;
+		}
+
+		public ReturnValue<T> setState(final State state) {
+			this.state = state;
+			return this;
+		}
+
+		public ReturnValue<T> setStateAndValue(final State state, final T value) {
+			this.state = state;
+			this.value = value;
+			return this;
+		}
+	}
+
+	private final ThreadLocal<ReturnValue<T>> returnValue = new ThreadLocal<ReturnValue<T>>();
+
+	private static final long LOG_INITIAL_SIZE = 10;
+
+	private volatile long bottom = 0;
+	// private volatile long top = 0;
+	private final AtomicLong top = new AtomicLong();
+	private volatile CircularArray<T> activeArray = new CircularArray<T>(LOG_INITIAL_SIZE);
+
+	public CircularWorkStealingDequeWithThreadLocalSentinel() {
+		this.returnValue.set(new ReturnValue<T>());
+	}
+
+	private boolean casTop(final long oldVal, final long newVal) {
+		// boolean preCond;
+		// synchronized (this) {
+		// preCond = (this.top == oldVal);
+		// if (preCond) {
+		// this.top = newVal;
+		// }
+		// }
+		// return preCond;
+		return this.top.compareAndSet(oldVal, newVal);
+	}
+
+	public void pushBottom(final T o) {
+		final long b = this.bottom;
+		final long t = this.top.get();
+		CircularArray<T> a = this.activeArray;
+		final long size = b - t;
+		if (size > (a.getCapacity() - 1)) {
+			a = a.grow(b, t);
+			this.activeArray = a;
+		}
+		a.put(b, o);
+		this.bottom = b + 1;
+	}
+
+	/**
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>empty()</code> if the deque contains no elements,
+	 *         <li><i>the latest element</i> otherwise
+	 *         </ul>
+	 */
+	public ReturnValue<T> popBottom() {
+		long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		b = b - 1;
+		this.bottom = b;
+		final long t = this.top.get();
+		final long size = b - t;
+		if (size < 0) {
+			this.bottom = t;
+			return this.empty();
+		}
+		ReturnValue<T> o = this.regular(a.get(b));
+		if (size > 0) {
+			this.perhapsShrink(b, t);
+			return o;
+		}
+		if (!this.casTop(t, t + 1)) {
+			o = this.empty();
+		}
+		this.bottom = t + 1;
+		return o;
+	}
+
+	void perhapsShrink(final long b, final long t) {
+		long temp = t;
+		final CircularArray<T> a = this.activeArray;
+		if ((b - temp) < (a.getCapacity() / 4)) {
+			final CircularArray<T> aa = a.shrink(b, temp);
+			this.activeArray = aa;
+			final long ss = aa.getCapacity();
+			this.bottom = b + ss;
+			temp = this.top.get();
+			if (!this.casTop(temp, temp + ss)) {
+				this.bottom = b;
+				// a.free();
+			}
+		}
+	}
+
+	/**
+	 * Tries to steal (return & remove) the oldest element from this deque.
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>empty()</code> if the deque contains no elements,
+	 *         <li><code>abort()</code> if the deque is currently being stolen by another thread,
+	 *         <li><i>the oldest element</i> otherwise
+	 *         </ul>
+	 */
+	public ReturnValue<T> steal() {
+		final long t = this.top.get();
+		final CircularArray<T> oldArr = this.activeArray;
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final long size = b - t;
+		if (size <= 0) {
+			return this.empty();
+		}
+		if ((size % a.getCapacity()) == 0) {
+			if ((oldArr == a) && (t == this.top.get())) {
+				return this.empty();
+			} else {
+				return this.abort();
+			}
+		}
+		final ReturnValue<T> o = this.regular(a.get(t));
+		if (!this.casTop(t, t + 1)) {
+			return this.abort();
+		}
+		return o;
+	}
+
+	private ReturnValue<T> empty() {
+		return this.returnValue.get().setState(State.EMPTY);
+	}
+
+	private ReturnValue<T> abort() {
+		return this.returnValue.get().setState(State.ABORT);
+	}
+
+	private ReturnValue<T> regular(final T value) {
+		return this.returnValue.get().setStateAndValue(State.REGULAR, value);
+	}
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return but does not remove the bottom element from this deque
+	 */
+	public T readBottom() {
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final T o = a.get(b);
+		return o;
+	}
+
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 29993
+	// bottom: 29992
+	//
+	//
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 30008
+	// bottom: 30007
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return the number of elements this deque contains
+	 */
+	public long size(final Object sourceStage) {
+		final long t = this.top.get();
+		final long b = this.bottom;
+		final long size = b - t;
+		System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom);
+		return size;
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/ExceptionalCircularWorkStealingDeque.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/ExceptionalCircularWorkStealingDeque.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb227e1c48050a59810068dbddfa5787d53512e2
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/ExceptionalCircularWorkStealingDeque.java
@@ -0,0 +1,196 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.util.concurrent.workstealing.alternative;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import teetime.util.concurrent.workstealing.CircularArray;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @see "Dynamic Circular WorkStealing Deque"
+ * 
+ * @since 1.10
+ */
+public class ExceptionalCircularWorkStealingDeque<T> {
+
+	public static class DequeIsEmptyException extends Exception {
+		private static final long serialVersionUID = -6685406255103741724L;
+	}
+
+	public static final DequeIsEmptyException DEQUE_IS_EMPTY_EXCEPTION = new DequeIsEmptyException();
+
+	public static class OperationAbortedException extends Exception {
+		private static final long serialVersionUID = 2983001853326344073L;
+	}
+
+	public static final OperationAbortedException OPERATION_ABORTED_EXCEPTION = new OperationAbortedException();
+
+	private static final long LOG_INITIAL_SIZE = 10;
+
+	private volatile long bottom = 0;
+	// private volatile long top = 0;
+	private final AtomicLong top = new AtomicLong();
+	private volatile CircularArray<T> activeArray = new CircularArray<T>(LOG_INITIAL_SIZE);
+
+	private boolean casTop(final long oldVal, final long newVal) {
+		// boolean preCond;
+		// synchronized (this) {
+		// preCond = (this.top == oldVal);
+		// if (preCond) {
+		// this.top = newVal;
+		// }
+		// }
+		// return preCond;
+		return this.top.compareAndSet(oldVal, newVal);
+	}
+
+	public void pushBottom(final T o) {
+		final long b = this.bottom;
+		final long t = this.top.get();
+		CircularArray<T> a = this.activeArray;
+		final long size = b - t;
+		if (size > (a.getCapacity() - 1)) {
+			a = a.grow(b, t);
+			this.activeArray = a;
+		}
+		a.put(b, o);
+		this.bottom = b + 1;
+	}
+
+	/**
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>EMPTY</code> if the deque contains no elements,
+	 *         <li><i>the latest element</i> otherwise
+	 *         </ul>
+	 * @throws DequeIsEmptyException
+	 */
+	public T popBottom() throws DequeIsEmptyException {
+		long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		b = b - 1;
+		this.bottom = b;
+		final long t = this.top.get();
+		final long size = b - t;
+		if (size < 0) {
+			this.bottom = t;
+			throw DEQUE_IS_EMPTY_EXCEPTION;
+		}
+		final T o = a.get(b);
+		if (size > 0) {
+			this.perhapsShrink(b, t);
+			return o;
+		}
+		final boolean success = this.casTop(t, t + 1);
+		this.bottom = t + 1;
+		if (!success) {
+			throw DEQUE_IS_EMPTY_EXCEPTION;
+		}
+		return o;
+	}
+
+	void perhapsShrink(final long b, final long t) {
+		long temp = t;
+		final CircularArray<T> a = this.activeArray;
+		if ((b - temp) < (a.getCapacity() / 4)) {
+			final CircularArray<T> aa = a.shrink(b, temp);
+			this.activeArray = aa;
+			final long ss = aa.getCapacity();
+			this.bottom = b + ss;
+			temp = this.top.get();
+			if (!this.casTop(temp, temp + ss)) {
+				this.bottom = b;
+				// a.free();
+			}
+		}
+	}
+
+	/**
+	 * Tries to steal (return & remove) the oldest element from this deque.
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>EMPTY</code> if the deque contains no elements,
+	 *         <li><code>ABORT</code> if the deque is currently being stolen by another thread,
+	 *         <li><i>the oldest element</i> otherwise
+	 *         </ul>
+	 * @throws DequeIsEmptyException
+	 * @throws OperationAbortedException
+	 */
+	public T steal() throws DequeIsEmptyException, OperationAbortedException {
+		final long t = this.top.get();
+		final CircularArray<T> oldArr = this.activeArray;
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final long size = b - t;
+		if (size <= 0) {
+			throw DEQUE_IS_EMPTY_EXCEPTION;
+		}
+		if ((size % a.getCapacity()) == 0) {
+			if ((oldArr == a) && (t == this.top.get())) {
+				throw DEQUE_IS_EMPTY_EXCEPTION;
+			} else {
+				throw OPERATION_ABORTED_EXCEPTION;
+			}
+		}
+		final T o = a.get(t);
+		if (!this.casTop(t, t + 1)) {
+			throw OPERATION_ABORTED_EXCEPTION;
+		}
+		return o;
+	}
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return but does not remove the bottom element from this deque
+	 */
+	public T readBottom() {
+		final long b = this.bottom;
+		final CircularArray<T> a = this.activeArray;
+		final T o = a.get(b);
+		return o;
+	}
+
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 29993
+	// bottom: 29992
+	//
+	//
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 30008
+	// bottom: 30007
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return the number of elements this deque contains
+	 */
+	public long size(final Object sourceStage) {
+		final long t = this.top.get();
+		final long b = this.bottom;
+		final long size = b - t;
+		System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom);
+		return size;
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedCircularWorkStealingDeque.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedCircularWorkStealingDeque.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ece4ff8d7615027b689b95c142daac22fe5c8a3
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedCircularWorkStealingDeque.java
@@ -0,0 +1,182 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.util.concurrent.workstealing.alternative;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import teetime.util.concurrent.workstealing.CircularArray;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @see "Dynamic Circular WorkStealing Deque"
+ * 
+ * @since 1.10
+ */
+public class UntypedCircularWorkStealingDeque {
+	public static final Object EMPTY = new Object();
+	public static final Object ABORT = new Object();
+
+	private static final long LOG_INITIAL_SIZE = 10;
+
+	private volatile long bottom = 0;
+	// private volatile long top = 0;
+	private final AtomicLong top = new AtomicLong();
+	private volatile CircularArray<Object> activeArray = new CircularArray<Object>(LOG_INITIAL_SIZE);
+
+	private boolean casTop(final long oldVal, final long newVal) {
+		// boolean preCond;
+		// synchronized (this) {
+		// preCond = (this.top == oldVal);
+		// if (preCond) {
+		// this.top = newVal;
+		// }
+		// }
+		// return preCond;
+		return this.top.compareAndSet(oldVal, newVal);
+	}
+
+	public void pushBottom(final Object o) {
+		final long b = this.bottom;
+		final long t = this.top.get();
+		CircularArray<Object> a = this.activeArray;
+		final long size = b - t;
+		if (size > (a.getCapacity() - 1)) {
+			a = a.grow(b, t);
+			this.activeArray = a;
+		}
+		a.put(b, o);
+		this.bottom = b + 1;
+	}
+
+	/**
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>EMPTY</code> if the deque contains no elements,
+	 *         <li><i>the latest element</i> otherwise
+	 *         </ul>
+	 */
+	public Object popBottom() {
+		long b = this.bottom;
+		final CircularArray<Object> a = this.activeArray;
+		b = b - 1;
+		this.bottom = b; // reserve (avoid stealing) the current bottom element
+		final long t = this.top.get();
+		final long size = b - t;
+		if (size < 0) {
+			this.bottom = t;
+			return EMPTY;
+		}
+		Object o = a.get(b);
+		if (size > 0) {
+			this.perhapsShrink(b, t);
+			return o;
+		}
+		if (!this.casTop(t, t + 1)) {
+			o = EMPTY;
+		}
+		this.bottom = t + 1;
+		return o;
+	}
+
+	void perhapsShrink(final long b, final long t) {
+		long temp = t;
+		final CircularArray<Object> a = this.activeArray;
+		if ((b - temp) < (a.getCapacity() / 4)) {
+			final CircularArray<Object> aa = a.shrink(b, temp);
+			this.activeArray = aa;
+			final long ss = aa.getCapacity();
+			this.bottom = b + ss;
+			temp = this.top.get();
+			if (!this.casTop(temp, temp + ss)) {
+				this.bottom = b;
+				// a.free();
+			}
+		}
+	}
+
+	/**
+	 * Tries to steal (return & remove) the oldest element from this deque.
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>EMPTY</code> if the deque contains no elements,
+	 *         <li><code>ABORT</code> if the deque is currently being stolen by another thread,
+	 *         <li><i>the oldest element</i> otherwise
+	 *         </ul>
+	 */
+	public Object steal() {
+		final long t = this.top.get();
+		final CircularArray<Object> oldArr = this.activeArray;
+		final long b = this.bottom;
+		final CircularArray<Object> a = this.activeArray;
+		final long size = b - t;
+		if (size <= 0) {
+			return EMPTY;
+		}
+		if ((size % a.getCapacity()) == 0) {
+			if ((oldArr == a) && (t == this.top.get())) {
+				return EMPTY;
+			} else {
+				return ABORT;
+			}
+		}
+		final Object o = a.get(t);
+		if (!this.casTop(t, t + 1)) {
+			return ABORT;
+		}
+		return o;
+	}
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return but does not remove the bottom element from this deque
+	 */
+	public Object readBottom() {
+		final long b = this.bottom;
+		final CircularArray<Object> a = this.activeArray;
+		final Object o = a.get(b);
+		return o;
+	}
+
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 29993
+	// bottom: 29992
+	//
+	//
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 30008
+	// bottom: 30007
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return the number of elements this deque contains
+	 */
+	public long size(final Object sourceStage) {
+		final long t = this.top.get();
+		final long b = this.bottom;
+		final long size = b - t;
+		System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom);
+		return size;
+	}
+}
diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedExceptionalCircularWorkStealingDeque.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedExceptionalCircularWorkStealingDeque.java
new file mode 100644
index 0000000000000000000000000000000000000000..f72634b28874ffe2609e7898455bcf7e8203a1e0
--- /dev/null
+++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedExceptionalCircularWorkStealingDeque.java
@@ -0,0 +1,195 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+
+package teetime.util.concurrent.workstealing.alternative;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import teetime.util.concurrent.workstealing.CircularArray;
+import teetime.util.concurrent.workstealing.DequePopException;
+
+/**
+ * 
+ * @author Christian Wulf
+ * 
+ * @see "Dynamic Circular WorkStealing Deque"
+ * 
+ * @since 1.10
+ */
+public class UntypedExceptionalCircularWorkStealingDeque {
+	public static class DequeIsEmptyException extends DequePopException {
+		private static final long serialVersionUID = -6685406255103741724L;
+	}
+
+	public static final DequeIsEmptyException DEQUE_IS_EMPTY_EXCEPTION = new DequeIsEmptyException();
+
+	public static class OperationAbortedException extends DequePopException {
+		private static final long serialVersionUID = 2983001853326344073L;
+	}
+
+	public static final OperationAbortedException OPERATION_ABORTED_EXCEPTION = new OperationAbortedException();
+
+	private static final long LOG_INITIAL_SIZE = 10;
+
+	private volatile long bottom = 0;
+	// private volatile long top = 0;
+	private final AtomicLong top = new AtomicLong();
+	private volatile CircularArray<Object> activeArray = new CircularArray<Object>(LOG_INITIAL_SIZE);
+
+	private boolean casTop(final long oldVal, final long newVal) {
+		// boolean preCond;
+		// synchronized (this) {
+		// preCond = (this.top == oldVal);
+		// if (preCond) {
+		// this.top = newVal;
+		// }
+		// }
+		// return preCond;
+		return this.top.compareAndSet(oldVal, newVal);
+	}
+
+	public void pushBottom(final Object o) {
+		final long b = this.bottom;
+		final long t = this.top.get();
+		CircularArray<Object> a = this.activeArray;
+		final long size = b - t;
+		if (size > (a.getCapacity() - 1)) {
+			a = a.grow(b, t);
+			this.activeArray = a;
+		}
+		a.put(b, o);
+		this.bottom = b + 1;
+	}
+
+	/**
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>EMPTY</code> if the deque contains no elements,
+	 *         <li><i>the latest element</i> otherwise
+	 *         </ul>
+	 * @throws DequeIsEmptyException
+	 */
+	public Object popBottom() throws DequePopException {
+		long b = this.bottom;
+		final CircularArray<Object> a = this.activeArray;
+		b = b - 1;
+		this.bottom = b;
+		final long t = this.top.get();
+		final long size = b - t;
+		if (size < 0) {
+			this.bottom = t;
+			throw DEQUE_IS_EMPTY_EXCEPTION;
+		}
+		final Object o = a.get(b);
+		if (size > 0) {
+			this.perhapsShrink(b, t);
+			return o;
+		}
+		final boolean success = this.casTop(t, t + 1);
+		this.bottom = t + 1;
+		if (!success) {
+			throw DEQUE_IS_EMPTY_EXCEPTION;
+		}
+		return o;
+	}
+
+	void perhapsShrink(final long b, final long t) {
+		long temp = t;
+		final CircularArray<Object> a = this.activeArray;
+		if ((b - temp) < (a.getCapacity() / 4)) {
+			final CircularArray<Object> aa = a.shrink(b, temp);
+			this.activeArray = aa;
+			final long ss = aa.getCapacity();
+			this.bottom = b + ss;
+			temp = this.top.get();
+			if (!this.casTop(temp, temp + ss)) {
+				this.bottom = b;
+				// a.free();
+			}
+		}
+	}
+
+	/**
+	 * Tries to steal (return & remove) the oldest element from this deque.
+	 * 
+	 * @return
+	 *         <ul>
+	 *         <li><code>EMPTY</code> if the deque contains no elements,
+	 *         <li><code>ABORT</code> if the deque is currently being stolen by another thread,
+	 *         <li><i>the oldest element</i> otherwise
+	 *         </ul>
+	 * @throws DequePopException
+	 */
+	public Object steal() throws DequePopException {
+		final long t = this.top.get();
+		final CircularArray<Object> oldArr = this.activeArray;
+		final long b = this.bottom;
+		final CircularArray<Object> a = this.activeArray;
+		final long size = b - t;
+		if (size <= 0) {
+			throw DEQUE_IS_EMPTY_EXCEPTION;
+		}
+		if ((size % a.getCapacity()) == 0) {
+			if ((oldArr == a) && (t == this.top.get())) {
+				throw DEQUE_IS_EMPTY_EXCEPTION;
+			} else {
+				throw OPERATION_ABORTED_EXCEPTION;
+			}
+		}
+		final Object o = a.get(t);
+		if (!this.casTop(t, t + 1)) {
+			throw OPERATION_ABORTED_EXCEPTION;
+		}
+		return o;
+	}
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return but does not remove the bottom element from this deque
+	 */
+	public Object readBottom() {
+		final long b = this.bottom;
+		final CircularArray<Object> a = this.activeArray;
+		final Object o = a.get(b);
+		return o;
+	}
+
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 29993
+	// bottom: 29992
+	//
+	//
+	// bottom: 4093
+	// bottom: 66429
+	// bottom: 30008
+	// bottom: 30007
+
+	/**
+	 * For debugging purposes
+	 * 
+	 * @return the number of elements this deque contains
+	 */
+	public long size(final Object sourceStage) {
+		final long t = this.top.get();
+		final long b = this.bottom;
+		final long size = b - t;
+		System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom);
+		return size;
+	}
+}
diff --git a/src/test/java/kieker/analysis/stage/EmptyPassOnFilterTest.java b/src/test/java/kieker/analysis/stage/EmptyPassOnFilterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a7e735e02a4b4be4bd2526c6338c9c336c578a9
--- /dev/null
+++ b/src/test/java/kieker/analysis/stage/EmptyPassOnFilterTest.java
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package kieker.analysis.stage;
+
+import kieker.analysis.AnalysisController;
+import kieker.analysis.IAnalysisController;
+import kieker.analysis.exception.AnalysisConfigurationException;
+import kieker.analysis.plugin.reader.filesystem.FSReader;
+import kieker.analysis.stage.CacheFilter;
+import kieker.analysis.stage.EmptyPassOnFilter;
+import kieker.common.configuration.Configuration;
+
+import org.junit.Test;
+
+/**
+ * @author Nils Christian Ehmke
+ * 
+ * @since 1.10
+ */
+public class EmptyPassOnFilterTest {
+
+	private static final int NUMBER_OF_EMPTY_PASS_ON_FILTERS = 1000;
+
+	@Test
+	public void test() throws IllegalStateException, AnalysisConfigurationException {
+		final IAnalysisController ac = new AnalysisController();
+
+		final Configuration fsReaderConfiguration = new Configuration();
+		fsReaderConfiguration.setProperty(FSReader.CONFIG_PROPERTY_NAME_INPUTDIRS,
+				"examples/userguide/ch5--trace-monitoring-aspectj/testdata/kieker-20100830-082225522-UTC");
+		final FSReader reader = new FSReader(fsReaderConfiguration, ac);
+
+		final CacheFilter cacheFilter = new CacheFilter(new Configuration(), ac);
+
+		EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), ac);
+		ac.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, cacheFilter, CacheFilter.INPUT_PORT_NAME);
+		ac.connect(cacheFilter, CacheFilter.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+		for (int idx = 0; idx < (NUMBER_OF_EMPTY_PASS_ON_FILTERS - 1); idx++) {
+			final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), ac);
+			ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME);
+			predecessor = newPredecessor;
+		}
+
+		ac.run();
+	}
+
+}
diff --git a/src/test/java/teetime/examples/recordReader/RecordReaderAnalysisTest.java b/src/test/java/teetime/examples/recordReader/RecordReaderAnalysisTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b7eb3516cbb63fbd6c720a9221eed96abd238e6
--- /dev/null
+++ b/src/test/java/teetime/examples/recordReader/RecordReaderAnalysisTest.java
@@ -0,0 +1,127 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.recordReader;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+import kieker.analysis.AnalysisController;
+import kieker.analysis.IAnalysisController;
+import kieker.analysis.IProjectContext;
+import kieker.analysis.plugin.annotation.InputPort;
+import kieker.analysis.plugin.filter.AbstractFilterPlugin;
+import kieker.analysis.plugin.filter.forward.CountingFilter;
+import kieker.analysis.plugin.filter.forward.ListCollectionFilter;
+import kieker.analysis.plugin.reader.filesystem.FSReader;
+import kieker.common.configuration.Configuration;
+import kieker.common.record.IMonitoringRecord;
+import kieker.common.util.registry.Registry;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import teetime.stage.kieker.className.ClassNameRegistry;
+import teetime.stage.kieker.className.ClassNameRegistryRepository;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class RecordReaderAnalysisTest {
+
+	private final static File INPUT_DIRECTORY = new File("examples/userguide/ch5--trace-monitoring-aspectj/testdata/kieker-20100830-082225522-UTC");
+
+	@Test
+	public void testExampleWithNewFramework() {
+		final List<IMonitoringRecord> records = new LinkedList<IMonitoringRecord>();
+
+		final RecordReaderAnalysis recordReaderAnalysis = new RecordReaderAnalysis();
+		recordReaderAnalysis.init();
+		recordReaderAnalysis.setInputFile(RecordReaderAnalysisTest.INPUT_DIRECTORY);
+		recordReaderAnalysis.setOutputRecordList(records);
+		recordReaderAnalysis.start();
+
+		final ClassNameRegistryRepository classNameRegistryRepository = recordReaderAnalysis.getClassNameRegistryRepository();
+		Assert.assertEquals(1, classNameRegistryRepository.size());
+
+		final ClassNameRegistry classNameRegistry = classNameRegistryRepository.get(RecordReaderAnalysisTest.INPUT_DIRECTORY);
+		Assert.assertNotNull(classNameRegistry);
+
+		Assert.assertEquals(2, classNameRegistry.size());
+		Assert.assertEquals("kieker.common.record.misc.KiekerMetadataRecord", classNameRegistry.get(0));
+		Assert.assertEquals("kieker.common.record.controlflow.OperationExecutionRecord", classNameRegistry.get(1));
+		Assert.assertEquals(6541, records.size());
+		Assert.assertEquals(1283156546127117246l, records.get(0).getLoggingTimestamp());
+	}
+
+	@Test
+	public void testExampleWithKiekerFramework() throws Exception {
+		final IAnalysisController ac = new AnalysisController();
+
+		final Configuration readerConfiguration = new Configuration();
+		readerConfiguration.setProperty(FSReader.CONFIG_PROPERTY_NAME_INPUTDIRS, INPUT_DIRECTORY.getAbsolutePath());
+		final FSReader reader = new FSReader(readerConfiguration, ac);
+
+		final ListCollectionFilter<IMonitoringRecord> records = new ListCollectionFilter<IMonitoringRecord>(new Configuration(), ac);
+		final ClassNameRegistryFilter classNameRegistry = new ClassNameRegistryFilter(new Configuration(), ac);
+
+		final CountingFilter countingFilter = new CountingFilter(new Configuration(), ac);
+
+		ac.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, countingFilter, CountingFilter.INPUT_PORT_NAME_EVENTS);
+		ac.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, records, ListCollectionFilter.INPUT_PORT_NAME);
+		ac.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, classNameRegistry, ClassNameRegistryFilter.INPUT_PORT_NAME_RECORDS);
+
+		ac.run();
+
+		// Keep in mind that the metadata record is not processed in a regular way
+		Assert.assertEquals(1, classNameRegistry.size());
+		Assert.assertEquals("kieker.common.record.controlflow.OperationExecutionRecord", classNameRegistry.get(0));
+		Assert.assertEquals(6540, countingFilter.getMessageCount());
+		Assert.assertEquals(1283156545581511026L, records.getList().get(0).getLoggingTimestamp());
+	}
+
+	private static class ClassNameRegistryFilter extends AbstractFilterPlugin {
+
+		public static final String INPUT_PORT_NAME_RECORDS = "input";
+
+		private final Registry<String> registry = new Registry<String>();
+
+		public ClassNameRegistryFilter(final Configuration configuration, final IProjectContext projectContext) {
+			super(configuration, projectContext);
+		}
+
+		@InputPort(name = ClassNameRegistryFilter.INPUT_PORT_NAME_RECORDS, eventTypes = IMonitoringRecord.class)
+		public void input(final IMonitoringRecord record) {
+			this.registry.get(record.getClass().getName());
+		}
+
+		public Object get(final int id) {
+			return this.registry.get(id);
+		}
+
+		public Object size() {
+			return this.registry.getSize();
+		}
+
+		@Override
+		public Configuration getCurrentConfiguration() {
+			return new Configuration();
+		}
+
+	}
+}
diff --git a/src/test/java/teetime/examples/throughput/MethodCallThoughputTimestampAnalysisTest.java b/src/test/java/teetime/examples/throughput/MethodCallThoughputTimestampAnalysisTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..236baae79c24dfc248a22a6f65051963893c433f
--- /dev/null
+++ b/src/test/java/teetime/examples/throughput/MethodCallThoughputTimestampAnalysisTest.java
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.throughput;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import kieker.common.logging.LogFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import teetime.util.StatisticsUtil;
+import teetime.util.StopWatch;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class MethodCallThoughputTimestampAnalysisTest {
+
+	private static final int NUM_OBJECTS_TO_CREATE = 100000;
+
+	@Before
+	public void before() {
+		System.setProperty(LogFactory.CUSTOM_LOGGER_JVM, "NONE");
+	}
+
+	// 500 times faster than our new framework
+	// TODO check why
+
+	@Test
+	public void testWithManyObjects() {
+		final StopWatch stopWatch = new StopWatch();
+		final List<TimestampObject> timestampObjects = new ArrayList<TimestampObject>(NUM_OBJECTS_TO_CREATE);
+
+		final MethodCallThroughputAnalysis analysis = new MethodCallThroughputAnalysis();
+		analysis.setNumNoopFilters(800);
+		analysis.setTimestampObjects(timestampObjects);
+		analysis.setInput(NUM_OBJECTS_TO_CREATE, new Callable<TimestampObject>() {
+			@Override
+			public TimestampObject call() throws Exception {
+				return new TimestampObject();
+			}
+		});
+		analysis.init();
+
+		stopWatch.start();
+		try {
+			analysis.start();
+		} finally {
+			stopWatch.end();
+		}
+
+		StatisticsUtil.printStatistics(stopWatch.getDuration(), timestampObjects);
+	}
+
+}
diff --git a/src/test/java/teetime/examples/throughput/ThroughputAnalysisTest.java b/src/test/java/teetime/examples/throughput/ThroughputAnalysisTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..12538bec75df3f1307352399958f6a4d3f49b029
--- /dev/null
+++ b/src/test/java/teetime/examples/throughput/ThroughputAnalysisTest.java
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.throughput;
+
+import java.util.concurrent.Callable;
+
+import kieker.common.logging.LogFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import teetime.util.StopWatch;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ThroughputAnalysisTest {
+
+	private static final int NUM_OBJECTS_TO_CREATE = 100000;
+	private static final int numRuns = 2;
+
+	@Before
+	public void before() {
+		System.setProperty(LogFactory.CUSTOM_LOGGER_JVM, "NONE");
+	}
+
+	@Test
+	public void testWithMultipleRuns() {
+		final StopWatch stopWatch = new StopWatch();
+		final long[] durations = new long[numRuns];
+
+		for (int i = 0; i < numRuns; i++) {
+			System.out.println("current run: " + i);
+			final ThroughputAnalysis<Object> analysis = new ThroughputAnalysis<Object>();
+			analysis.setNumNoopFilters(800);
+			analysis.setInput(NUM_OBJECTS_TO_CREATE, new Callable<Object>() {
+				@Override
+				public Object call() throws Exception {
+					return new Object();
+				}
+			});
+			analysis.init();
+
+			stopWatch.start();
+			try {
+				analysis.start();
+			} finally {
+				stopWatch.end();
+			}
+
+			durations[i] = stopWatch.getDuration();
+		}
+
+		// for (final long dur : durations) {
+		// System.out.println("Duration: " + (dur / 1000) + " �s");
+		// }
+
+		long sum = 0;
+		for (int i = durations.length / 2; i < durations.length; i++) {
+			sum += durations[i];
+		}
+
+		final long avgDur = sum / (numRuns / 2);
+		System.out.println("avg duration: " + (avgDur / 1000) + " �s");
+	}
+
+}
diff --git a/src/test/java/teetime/examples/throughput/ThroughputTimestampAnalysisTest.java b/src/test/java/teetime/examples/throughput/ThroughputTimestampAnalysisTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4289f497a13e1d99307d92fab52b59e00ca81a9
--- /dev/null
+++ b/src/test/java/teetime/examples/throughput/ThroughputTimestampAnalysisTest.java
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***************************************************************************/
+package teetime.examples.throughput;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import kieker.common.logging.LogFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import teetime.util.StatisticsUtil;
+import teetime.util.StopWatch;
+
+/**
+ * @author Christian Wulf
+ * 
+ * @since 1.10
+ */
+public class ThroughputTimestampAnalysisTest {
+
+	private static final int NUM_OBJECTS_TO_CREATE = 100000;
+
+	@Before
+	public void before() {
+		System.setProperty(LogFactory.CUSTOM_LOGGER_JVM, "NONE");
+	}
+
+	// Using QueuePipes ist 1/3 faster than using MethodCallPipes
+	// reason:
+	/*
+	 * MethodCallPipes:
+	 * <ul>
+	 * <li>SchedulingOverhead: 12629 ms
+	 * <li>ExecutedUnsuccessfullyCount: 80300001
+	 * </ul>
+	 * 
+	 * QueuePipes:
+	 * <ul>
+	 * <li>SchedulingOverhead: 11337 ms
+	 * <li>ExecutedUnsuccessfullyCount: 804
+	 * </ul>
+	 */
+
+	@Test
+	public void testWithManyObjects() {
+		final StopWatch stopWatch = new StopWatch();
+		final List<TimestampObject> timestampObjects = new ArrayList<TimestampObject>(NUM_OBJECTS_TO_CREATE);
+
+		final ThroughputTimestampAnalysis analysis = new ThroughputTimestampAnalysis();
+		analysis.setShouldUseQueue(true);
+		analysis.setNumNoopFilters(800);
+		analysis.setTimestampObjects(timestampObjects);
+		analysis.setInput(NUM_OBJECTS_TO_CREATE, new Callable<TimestampObject>() {
+			@Override
+			public TimestampObject call() throws Exception {
+				return new TimestampObject();
+			}
+		});
+		analysis.init();
+
+		stopWatch.start();
+		try {
+			analysis.start();
+		} finally {
+			stopWatch.end();
+		}
+
+		StatisticsUtil.printStatistics(stopWatch.getDuration(), timestampObjects);
+	}
+
+}
diff --git a/src/test/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeTest.java b/src/test/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ad2c0b91ecab4a6b988e9d2dc87fcaa5c2fdeed
--- /dev/null
+++ b/src/test/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeTest.java
@@ -0,0 +1,32 @@
+package teetime.util.concurrent.workstealing.alternative;
+
+import org.hamcrest.number.OrderingComparison;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import teetime.util.StopWatch;
+import teetime.util.concurrent.workstealing.CircularWorkStealingDeque;
+
+public class CircularWorkStealingDequeTest {
+	private StopWatch stopWatch;
+
+	@Before
+	public void before() {
+		this.stopWatch = new StopWatch();
+	}
+
+	@Test
+	public void measureManyEmptyPulls() {
+		final CircularWorkStealingDeque<Object> deque = new CircularWorkStealingDeque<Object>();
+
+		final int numIterations = UntypedCircularWorkStealingDequeTest.NUM_ITERATIONS;
+		this.stopWatch.start();
+		for (int i = 0; i < numIterations; i++) {
+			deque.popBottom();
+		}
+		this.stopWatch.end();
+
+		Assert.assertThat(this.stopWatch.getDuration(), OrderingComparison.lessThan(UntypedCircularWorkStealingDequeTest.EXPECTED_DURATION));
+	}
+}
diff --git a/teetime/src/main/java/teetime/App.java b/teetime/src/main/java/teetime/App.java
deleted file mode 100644
index f4114b2cf1b7c54cbffa14b1c2d1bfc17e669c26..0000000000000000000000000000000000000000
--- a/teetime/src/main/java/teetime/App.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package teetime;
-
-/**
- * Hello world!
- *
- */
-public class App 
-{
-    public static void main( String[] args )
-    {
-        System.out.println( "Hello World!" );
-    }
-}
diff --git a/teetime/src/test/java/teetime/AppTest.java b/teetime/src/test/java/teetime/AppTest.java
deleted file mode 100644
index 755e7d8e6a792b8aedf5698cdb123291c1e7b72f..0000000000000000000000000000000000000000
--- a/teetime/src/test/java/teetime/AppTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package teetime;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-/**
- * Unit test for simple App.
- */
-public class AppTest 
-    extends TestCase
-{
-    /**
-     * Create the test case
-     *
-     * @param testName name of the test case
-     */
-    public AppTest( String testName )
-    {
-        super( testName );
-    }
-
-    /**
-     * @return the suite of tests being tested
-     */
-    public static Test suite()
-    {
-        return new TestSuite( AppTest.class );
-    }
-
-    /**
-     * Rigourous Test :-)
-     */
-    public void testApp()
-    {
-        assertTrue( true );
-    }
-}