diff --git a/pom.xml b/pom.xml
index 3fb6ea771d72ca076fbd70a149729e747aeb7a97..7354b67771a257fa131e00fba908704523014a16 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,12 +87,6 @@
 			<version>1.1.2</version>
 			<scope>runtime</scope>
 		</dependency>
-		<dependency>
-			<groupId>org.codehaus.groovy</groupId>
-			<artifactId>groovy-all</artifactId>
-			<version>2.3.6</version>
-			<scope>runtime</scope>
-		</dependency>
 		<dependency>
 			<groupId>com.google.guava</groupId>
 			<artifactId>guava</artifactId>
diff --git a/src/main/java/teetime/framework/AnalysisConfiguration.java b/src/main/java/teetime/framework/AnalysisConfiguration.java
index c6a0436bc33783ac7d044c817729389d3a61fafe..bb8cbb8b1bbb51a47396920211da5d92aebc0765 100644
--- a/src/main/java/teetime/framework/AnalysisConfiguration.java
+++ b/src/main/java/teetime/framework/AnalysisConfiguration.java
@@ -12,7 +12,7 @@ public class AnalysisConfiguration {
 
 	public AnalysisConfiguration() {}
 
-	List<Stage> getThreadableStageJobs() {
+	List<Stage> getThreadableStageJobs() { // NOPMD only Analysis should use this method
 		return this.threadableStageJobs;
 	}
 
diff --git a/src/main/java/teetime/framework/Stage.java b/src/main/java/teetime/framework/Stage.java
index 99f6c0b8d15139c066de47dd9c287e63dd797da9..f947b21502906d732e8ac7e1dcee3a708d0d3fe6 100644
--- a/src/main/java/teetime/framework/Stage.java
+++ b/src/main/java/teetime/framework/Stage.java
@@ -1,7 +1,8 @@
 package teetime.framework;
 
 import java.util.List;
-import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -9,7 +10,9 @@ import org.slf4j.LoggerFactory;
 import teetime.framework.signal.ISignal;
 import teetime.framework.validation.InvalidPortConnection;
 
-public abstract class Stage {
+public abstract class Stage { // NOPMD (should not start with "Abstract")
+
+	private static final ConcurrentMap<String, Integer> INSTANCES_COUNTER = new ConcurrentHashMap<String, Integer>();
 
 	private final String id;
 	/**
@@ -18,8 +21,8 @@ public abstract class Stage {
 	protected final Logger logger; // NOPMD
 
 	protected Stage() {
-		this.id = UUID.randomUUID().toString(); // the id should only be represented by a UUID, not additionally by the class name
-		this.logger = LoggerFactory.getLogger(this.getClass().getName() + "(" + this.id + ")");
+		this.id = this.createId();
+		this.logger = LoggerFactory.getLogger(this.id);
 	}
 
 	public String getId() {
@@ -31,6 +34,23 @@ public abstract class Stage {
 		return this.getClass().getName() + ": " + this.getId();
 	}
 
+	private String createId() {
+		String simpleName = this.getClass().getSimpleName();
+
+		Integer numInstances = INSTANCES_COUNTER.get(simpleName);
+		if (null == numInstances) {
+			numInstances = 0;
+		}
+
+		String newId = simpleName + "-" + numInstances;
+		INSTANCES_COUNTER.put(simpleName, ++numInstances);
+		return newId;
+	}
+
+	static void clearInstanceCounters() { // NOPMD (package-private to clear map in tests)
+		INSTANCES_COUNTER.clear();
+	}
+
 	// public abstract Stage getParentStage();
 	//
 	// public abstract void setParentStage(Stage parentStage, int index);
diff --git a/src/main/java/teetime/stage/MultipleInstanceOfFilter.java b/src/main/java/teetime/stage/MultipleInstanceOfFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..23aea142b7ca1f37170242cd4ad7c1229b0f459f
--- /dev/null
+++ b/src/main/java/teetime/stage/MultipleInstanceOfFilter.java
@@ -0,0 +1,43 @@
+package teetime.stage;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import teetime.framework.AbstractConsumerStage;
+import teetime.framework.OutputPort;
+
+/**
+ * @author Nils Christian Ehmke
+ */
+public final class MultipleInstanceOfFilter<I> extends AbstractConsumerStage<I> {
+
+	private final Map<Class<? extends I>, OutputPort<? super I>> outputPortsMap = new HashMap<Class<? extends I>, OutputPort<? super I>>();
+	private Entry<Class<? extends I>, OutputPort<? super I>>[] cachedOutputPortsMap;
+
+	@SuppressWarnings("unchecked")
+	public <T extends I> OutputPort<T> getOutputPortForType(final Class<T> clazz) {
+		if (!this.outputPortsMap.containsKey(clazz)) {
+			this.outputPortsMap.put(clazz, super.createOutputPort());
+		}
+		return (OutputPort<T>) this.outputPortsMap.get(clazz);
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public void onStarting() throws Exception {
+		super.onStarting();
+
+		// We cache the map to avoid the creating of iterators during runtime
+		cachedOutputPortsMap = (Entry<Class<? extends I>, OutputPort<? super I>>[]) outputPortsMap.entrySet().toArray(new Entry<?, ?>[outputPortsMap.size()]);
+	}
+
+	@Override
+	protected void execute(final I element) {
+		for (Entry<Class<? extends I>, OutputPort<? super I>> outputPortMapEntry : cachedOutputPortsMap) {
+			if (outputPortMapEntry.getKey().isInstance(element)) {
+				outputPortMapEntry.getValue().send(element);
+			}
+		}
+	}
+}
diff --git a/src/main/resources/logback.groovy b/src/main/resources/logback.groovy
deleted file mode 100644
index 371a869615c1d9ebe0097d7e8f02f5bb597806d2..0000000000000000000000000000000000000000
--- a/src/main/resources/logback.groovy
+++ /dev/null
@@ -1,28 +0,0 @@
-import ch.qos.logback.classic.filter.ThresholdFilter
-
-statusListener(OnConsoleStatusListener)
-
-appender("FILE", FileAppender) {
-  file = "teetime.log"
-  append = false
-  filter(ThresholdFilter) {
-	level = INFO
-  }
-  encoder(PatternLayoutEncoder) {
-    pattern = "%msg%n"
-  }
-}
-
-appender("CONSOLE", ConsoleAppender) {
-  encoder(PatternLayoutEncoder) {
-    pattern = "%d{HH:mm:ss.SSS} %level %logger - %msg%n"
-  }
-}
-
-root WARN, ["CONSOLE"]
-
-//logger "teetime.framework", INFO
-//logger "teetime.stage", INFO
-
-logger "util.TimingsReader", TRACE, ["FILE"]
-logger "util.BucketTimingsReader", TRACE, ["FILE"]
\ No newline at end of file
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b17a05f640444952762360a6eb16dddbe52fab56
--- /dev/null
+++ b/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+<configuration>
+	<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
+
+	<appender name="FILE" class="ch.qos.logback.core.FileAppender">
+		<file>teetime.log</file>
+		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+			<level>INFO</level>
+		</filter>
+		<append>false</append>
+		<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder 
+			by default -->
+		<encoder>
+			<pattern>%msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>%d{HH:mm:ss.SSS} %level %logger - %msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<logger name="util.TimingsReader" level="TRACE">
+		<appender-ref ref="FILE" />
+	</logger>
+
+	<logger name="util.BucketTimingsReader" level="TRACE">
+		<appender-ref ref="FILE" />
+	</logger>
+
+	<root level="WARN">
+		<appender-ref ref="CONSOLE" />
+	</root>
+</configuration>
\ No newline at end of file
diff --git a/src/test/java/teetime/framework/StageTest.java b/src/test/java/teetime/framework/StageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3ac19814975d3cde840d12f2815a829a4c45dec
--- /dev/null
+++ b/src/test/java/teetime/framework/StageTest.java
@@ -0,0 +1,26 @@
+package teetime.framework;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import teetime.stage.Cache;
+import teetime.stage.Counter;
+
+public class StageTest {
+
+	@Test
+	public void testId() {
+		Stage.clearInstanceCounters();
+
+		Counter<Object> counter0 = new Counter<Object>();
+		Counter<Object> counter1 = new Counter<Object>();
+		Assert.assertEquals("Counter-0", counter0.getId());
+		Assert.assertEquals("Counter-1", counter1.getId());
+
+		for (int i = 0; i < 100; i++) {
+			Cache<Object> cache = new Cache<Object>();
+			Assert.assertEquals("Cache-" + i, cache.getId());
+		}
+	}
+
+}
diff --git a/src/test/java/teetime/stage/MultipleInstanceOfFilterTest.java b/src/test/java/teetime/stage/MultipleInstanceOfFilterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4740dea2998c6d47bc5b9947d48c6c516e292be7
--- /dev/null
+++ b/src/test/java/teetime/stage/MultipleInstanceOfFilterTest.java
@@ -0,0 +1,72 @@
+package teetime.stage;
+
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Test;
+
+import teetime.framework.Analysis;
+import teetime.framework.AnalysisConfiguration;
+import teetime.framework.OutputPort;
+import teetime.framework.pipe.IPipeFactory;
+import teetime.framework.pipe.PipeFactoryRegistry.PipeOrdering;
+import teetime.framework.pipe.PipeFactoryRegistry.ThreadCommunication;
+import teetime.util.Pair;
+
+public class MultipleInstanceOfFilterTest {
+
+	private static class TestConfiguration extends AnalysisConfiguration {
+
+		public TestConfiguration(final List<Number> initialInput, final List<Integer> integerList, final List<Float> floatList) {
+			// Create the stages
+			final InitialElementProducer<Number> producer = new InitialElementProducer<Number>(initialInput.toArray(new Number[0]));
+			final MultipleInstanceOfFilter<Number> filter = new MultipleInstanceOfFilter<Number>();
+			final CollectorSink<Integer> integerSink = new CollectorSink<Integer>(integerList);
+			final CollectorSink<Float> floatSink = new CollectorSink<Float>(floatList);
+
+			// Connect the stages
+			final IPipeFactory factory = PIPE_FACTORY_REGISTRY.getPipeFactory(ThreadCommunication.INTRA, PipeOrdering.ARBITRARY, false);
+			factory.create(producer.getOutputPort(), filter.getInputPort());
+			factory.create(filter.getOutputPortForType(Integer.class), integerSink.getInputPort());
+			factory.create(filter.getOutputPortForType(Float.class), floatSink.getInputPort());
+
+			super.addThreadableStage(producer);
+		}
+
+	}
+
+	@Test
+	@SuppressWarnings("unchecked")
+	public void filteringShouldWork() {
+		final List<Number> initialInput = new ArrayList<Number>(Arrays.asList(1, 1.5f, 2, 2.5f, 3, 3.5f));
+		final List<Integer> integerList = new ArrayList<Integer>();
+		final List<Float> floatList = new ArrayList<Float>();
+
+		final Analysis analysis = new Analysis(new TestConfiguration(initialInput, integerList, floatList));
+		analysis.init();
+		final Collection<Pair<Thread, Throwable>> errors = analysis.start();
+
+		assertThat(errors, is(empty()));
+
+		assertThat(integerList, contains(1, 2, 3));
+		assertThat(floatList, contains(1.5f, 2.5f, 3.5f));
+	}
+
+	@Test
+	public void outputPortForSameTypeShouldBeCached() {
+		final MultipleInstanceOfFilter<Number> filter = new MultipleInstanceOfFilter<Number>();
+
+		final OutputPort<Float> firstPort = filter.getOutputPortForType(Float.class);
+		final OutputPort<Float> secondPort = filter.getOutputPortForType(Float.class);
+
+		assertThat(firstPort, is(secondPort));
+	}
+
+}
diff --git a/src/main/java/util/test/PerformanceTest.java b/src/test/java/util/test/PerformanceTest.java
similarity index 100%
rename from src/main/java/util/test/PerformanceTest.java
rename to src/test/java/util/test/PerformanceTest.java
diff --git a/src/test/resources/logback.groovy b/src/test/resources/logback.groovy
deleted file mode 100644
index a888fc48590a7920e3c82e1ad9ab1caf1c38bc8c..0000000000000000000000000000000000000000
--- a/src/test/resources/logback.groovy
+++ /dev/null
@@ -1,27 +0,0 @@
-import ch.qos.logback.classic.filter.ThresholdFilter
-
-statusListener(OnConsoleStatusListener)
-
-appender("FILE", FileAppender) {
-  file = "src/test/data/load-logs/timings-results.txt"
-  append = false
-  filter(ThresholdFilter) {
-	level = INFO
-  }
-  encoder(PatternLayoutEncoder) {
-    pattern = "%msg%n"
-  }
-}
-
-appender("CONSOLE", ConsoleAppender) {
-  encoder(PatternLayoutEncoder) {
-    pattern = "%d{HH:mm:ss.SSS} %level %logger - %msg%n"
-  }
-}
-
-root ERROR, ["CONSOLE"]
-
-//logger "teetime.framework", INFO
-logger "teetime.stage", INFO
-
-logger "util", INFO
\ No newline at end of file
diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0c6041095f144317b7397cdd45a4bd82c385a8d1
--- /dev/null
+++ b/src/test/resources/logback.xml
@@ -0,0 +1,29 @@
+<configuration>
+	<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
+
+	<appender name="FILE" class="ch.qos.logback.core.FileAppender">
+		<file>src/test/data/load-logs/timings-results.txt</file>
+		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+			<level>INFO</level>
+		</filter>
+		<append>false</append>
+		<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder 
+			by default -->
+		<encoder>
+			<pattern>%msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>%d{HH:mm:ss.SSS} %level %logger - %msg%n</pattern>
+		</encoder>
+	</appender>
+	
+	<logger name="teetime.stage" level="INFO" />
+	<logger name="util" level="INFO" />
+
+	<root level="ERROR">
+		<appender-ref ref="CONSOLE" />
+	</root>
+</configuration>
\ No newline at end of file