diff --git a/Kieker.WebGUI/pom.xml b/Kieker.WebGUI/pom.xml
index 328f387d021cf4036dad99c2e61e44c79f2aaf01..516c51daceff92e8d4141f663a98dfefeb91fd2d 100644
--- a/Kieker.WebGUI/pom.xml
+++ b/Kieker.WebGUI/pom.xml
@@ -339,7 +339,7 @@
                     </compilerArguments>
                 </configuration>
             </plugin>
-             <!-- This is some kind of a hack, as maven doesn't want to include system jars into the war-file. We use ant instead. -->
+            <!-- This is some kind of a hack, as maven doesn't want to include system jars into the war-file. We use ant instead. -->
             <plugin>
                 <artifactId>maven-antrun-plugin</artifactId>
                 <executions>
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorBean.java
index f3930baa4d48a7741444233915331913014dc827..cf3b62efc71d86a91ca5b8d1c046fc045c8cbddf 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorBean.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorBean.java
@@ -21,6 +21,7 @@
 package kieker.webgui.beans.view;
 
 import java.io.IOException;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -39,6 +40,7 @@ import javax.faces.bean.ManagedProperty;
 import javax.faces.bean.ViewScoped;
 import javax.faces.context.FacesContext;
 
+import kieker.analysis.display.annotation.Display;
 import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory;
 import kieker.analysis.model.analysisMetaModel.MIDependency;
 import kieker.analysis.model.analysisMetaModel.MIDisplay;
@@ -71,6 +73,7 @@ import kieker.webgui.common.Pair;
 import kieker.webgui.common.PluginFinder;
 import kieker.webgui.common.exception.LibraryAlreadyExistingException;
 import kieker.webgui.common.exception.NewerProjectException;
+import kieker.webgui.common.exception.ProjectLoadException;
 
 import org.primefaces.context.RequestContext;
 import org.primefaces.event.FileUploadEvent;
@@ -145,6 +148,8 @@ public final class CurrentAnalysisEditorBean {
 	@ManagedProperty(value = "#{currentAnalysisEditorGraphBean}")
 	private CurrentAnalysisEditorGraphBean currentAnalysisEditorGraphBean;
 
+	private ClassAndMethodContainer classAndMethodContainer;
+
 	/**
 	 * Creates a new instance of this class.
 	 */
@@ -217,8 +222,10 @@ public final class CurrentAnalysisEditorBean {
 	/**
 	 * This method initializes the bean by using the current project name to load the project. <b>Do not call this method manually. It will only be accessed by
 	 * JSF.</b>
+	 * 
+	 * @throws ProjectLoadException
 	 */
-	public void initialize() {
+	public void initialize() throws ProjectLoadException {
 		synchronized (this) {
 			// Make sure that the initialization will only be done for the init request.
 			if (!FacesContext.getCurrentInstance().isPostback()) {
@@ -227,8 +234,9 @@ public final class CurrentAnalysisEditorBean {
 				if (this.project != null) {
 					// Remember the current time! This is important for the later comparison of the time stamps.
 					this.resetTimeStamp();
-					// Update the class loader
+					// Update the class loader and the specific classes used within various methods in this bean
 					this.reloadClassLoader();
+					this.reloadClassesAndMethods();
 					// Add the libraries within the lib-folder to the current model
 					this.addLibrariesToModel();
 					// Load the available readers, filters and repositories
@@ -238,6 +246,10 @@ public final class CurrentAnalysisEditorBean {
 		}
 	}
 
+	private void reloadClassesAndMethods() throws ProjectLoadException {
+		this.classAndMethodContainer = new ClassAndMethodContainer(this.classLoader);
+	}
+
 	/**
 	 * This method loads the list of available readers, filters and repositories, using the current libraries within the model.
 	 */
@@ -288,31 +300,21 @@ public final class CurrentAnalysisEditorBean {
 	 *            The library url used to load the plugins and repositories.
 	 */
 	private void addToToolPalette(final URL url) {
-		final Class<?> abstractFilterPluginClass;
-		final Class<?> abstractReaderPluginClass;
-		try {
-			abstractFilterPluginClass = this.classLoader.loadClass(AbstractFilterPlugin.class.getCanonicalName());
-			abstractReaderPluginClass = this.classLoader.loadClass(AbstractReaderPlugin.class.getCanonicalName());
-		} catch (final ClassNotFoundException ex) {
-			CurrentAnalysisEditorBean.LOG.error("Could not load class", ex);
-			return;
-		}
-
-		final List<Class<AbstractRepository>> repositories = this.pluginFinder.getAllRepositoriesWithinJar(url);
-		final List<Class<AbstractPlugin>> plugins = this.pluginFinder.getAllPluginsWithinJar(url);
+		final List<Class<?>> repositories = this.pluginFinder.getAllRepositoriesWithinJar(url);
+		final List<Class<?>> plugins = this.pluginFinder.getAllPluginsWithinJar(url);
 		// Now run through the available classes and add all non-abstract classes to our lists
-		for (final Class<AbstractRepository> repository : repositories) {
+		for (final Class<?> repository : repositories) {
 			if (!Modifier.isAbstract(repository.getModifiers())) {
 				this.availableRepositories.add(repository);
 			}
 		}
-		for (final Class<? extends AbstractPlugin> plugin : plugins) {
+		for (final Class<?> plugin : plugins) {
 			if (!Modifier.isAbstract(plugin.getModifiers())) {
 				// The following cast results in the unchecked-cast-warnings, but we know that the cast should be correct.
-				if (abstractFilterPluginClass.isAssignableFrom(plugin)) {
+				if (this.classAndMethodContainer.abstractFilterPluginClass.isAssignableFrom(plugin)) {
 					this.availableFilters.add(plugin);
 				} else {
-					if (abstractReaderPluginClass.isAssignableFrom(plugin)) {
+					if (this.classAndMethodContainer.abstractReaderPluginClass.isAssignableFrom(plugin)) {
 						this.availableReaders.add(plugin);
 					}
 				}
@@ -336,17 +338,20 @@ public final class CurrentAnalysisEditorBean {
 	/**
 	 * This method reloads the class loader. In other words: The class loader will always be able to load classes from the jar-files within the lib-folder of the
 	 * project.
+	 * 
+	 * @throws ProjectLoadException
 	 */
-	private void reloadClassLoader() {
+	private void reloadClassLoader() throws ProjectLoadException {
 		synchronized (this) {
 			this.classLoader = FSManager.getInstance().getClassLoader(this.projectName);
 			try {
 				this.pluginFinder = new PluginFinder(this.classLoader);
-			} catch (final ClassNotFoundException e) {
-				// TODO Auto-generated catch block
-				e.printStackTrace();
+			} catch (final ClassNotFoundException ex) {
+				CurrentAnalysisEditorBean.LOG.error("Could not load classes.", ex);
+				throw new ProjectLoadException();
 			} catch (final NullPointerException ex) {
-				ex.printStackTrace();
+				CurrentAnalysisEditorBean.LOG.error("Invalid class loader", ex);
+				throw new ProjectLoadException();
 			}
 		}
 	}
@@ -391,48 +396,58 @@ public final class CurrentAnalysisEditorBean {
 	 * @return The description for the class or a substitute if none is available. This is in either case human readable.
 	 */
 	public String getDescription(final Class<?> clazz) {
-		// Get the two potential annotations
-		final Plugin annotationPlugin = clazz.getAnnotation(Plugin.class);
-		final Repository annotationRepository = clazz.getAnnotation(Repository.class);
-
-		// Now check which one of them is available
-		if ((annotationPlugin == null) || annotationPlugin.description().isEmpty()) {
-			if ((annotationRepository == null) || annotationRepository.description().isEmpty()) {
-				// None. Deliver a human readable substitute.
-				return "No description available";
+		try {
+			// Get the two potential annotations
+			final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.pluginAnnotationClass);
+			final Annotation annotationRepository = clazz.getAnnotation(this.classAndMethodContainer.repositoryAnnotationClass);
+
+			// Now check which one of them is available
+			if ((annotationPlugin == null) || ((String) this.classAndMethodContainer.pluginDescriptionMethod.invoke(annotationPlugin, new Object[0])).isEmpty()) {
+				if ((annotationRepository == null)
+						|| ((String) this.classAndMethodContainer.repositoryDescriptionMethod.invoke(annotationRepository, new Object[0])).isEmpty()) {
+					// None. Deliver a human readable substitute.
+					return "No description available";
+				} else {
+					return (String) this.classAndMethodContainer.repositoryDescriptionMethod.invoke(annotationRepository, new Object[0]);
+				}
 			} else {
-				return annotationRepository.description();
+				return (String) this.classAndMethodContainer.pluginDescriptionMethod.invoke(annotationPlugin, new Object[0]);
 			}
-		} else {
-			return annotationPlugin.description();
+		} catch (final ReflectiveOperationException ex) {
+			CurrentAnalysisEditorBean.LOG.warn("Could not invoke method", ex);
+			return "No description available";
 		}
 	}
 
-	public List<Property> getProperties(final Class<?> clazz) {
-		final ArrayList<Property> result = new ArrayList<Property>();
+	public List<Annotation> getProperties(final Class<?> clazz) {
+		final List<Annotation> result = new ArrayList<Annotation>();
 
-		// Get the two potential annotations
-		final Plugin annotationPlugin = clazz.getAnnotation(Plugin.class);
-		final Repository annotationRepository = clazz.getAnnotation(Repository.class);
+		try {
+			// Get the two potential annotations
+			final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.pluginAnnotationClass);
+			final Annotation annotationRepository = clazz.getAnnotation(this.classAndMethodContainer.repositoryAnnotationClass);
 
-		final Property[] properties;
+			final Annotation[] properties;
 
-		// Now check which one of them is available
-		if ((annotationPlugin == null) || annotationPlugin.description().isEmpty()) {
-			if ((annotationRepository == null) || annotationRepository.description().isEmpty()) {
-				// None.
-				properties = new Property[0];
+			// Now check which one of them is available
+			if (annotationPlugin == null) {
+				if (annotationRepository == null) {
+					// None.
+					properties = new Property[0];
+				} else {
+					properties = (Annotation[]) this.classAndMethodContainer.repositoryConfigurationMethod.invoke(annotationRepository, new Object[0]);
+				}
 			} else {
-				properties = annotationRepository.configuration();
+				properties = (Annotation[]) this.classAndMethodContainer.pluginConfigurationMethod.invoke(annotationPlugin, new Object[0]);
 			}
-		} else {
-			properties = annotationPlugin.configuration();
-		}
 
-		for (final Property property : properties) {
-			result.add(property);
-		}
+			for (final Annotation property : properties) {
+				result.add(property);
+			}
 
+		} catch (final ReflectiveOperationException ex) {
+			CurrentAnalysisEditorBean.LOG.warn("Could not invoke method", ex);
+		}
 		return result;
 	}
 
@@ -443,12 +458,12 @@ public final class CurrentAnalysisEditorBean {
 	 *            The class to be analyzed.
 	 * @return A list containing the available input ports.
 	 */
-	public List<InputPort> getInputPorts(final Class<?> clazz) {
-		final ArrayList<InputPort> result = new ArrayList<InputPort>();
+	public List<Annotation> getInputPorts(final Class<?> clazz) {
+		final List<Annotation> result = new ArrayList<Annotation>();
 
 		for (final Method method : clazz.getMethods()) {
 			// Get the potential annotation
-			final InputPort annotationPort = method.getAnnotation(InputPort.class);
+			final Annotation annotationPort = method.getAnnotation(this.classAndMethodContainer.inputPortAnnotationClass);
 			// Now check whether it is available
 			if (annotationPort != null) {
 				result.add(annotationPort);
@@ -465,19 +480,23 @@ public final class CurrentAnalysisEditorBean {
 	 *            The class to be analyzed.
 	 * @return A list containing the available output ports.
 	 */
-	public List<OutputPort> getOutputPorts(final Class<?> clazz) {
-		final ArrayList<OutputPort> result = new ArrayList<OutputPort>();
+	public List<Annotation> getOutputPorts(final Class<?> clazz) {
+		final List<Annotation> result = new ArrayList<Annotation>();
 
-		// Get the potential annotation
-		final Plugin annotationPlugin = clazz.getAnnotation(Plugin.class);
+		try {
+			// Get the potential annotation
+			final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.pluginAnnotationClass);
 
-		// Now check whether it is available
-		if (annotationPlugin != null) {
-			for (final OutputPort oPort : annotationPlugin.outputPorts()) {
-				result.add(oPort);
+			// Now check whether it is available
+			if (annotationPlugin != null) {
+				for (final Annotation oPort : (Annotation[]) this.classAndMethodContainer.pluginOutputPortsMethod.invoke(annotationPlugin, new Object[0])) {
+					result.add(oPort);
+				}
 			}
-		}
 
+		} catch (final ReflectiveOperationException ex) {
+			CurrentAnalysisEditorBean.LOG.warn("Could not invoke method", ex);
+		}
 		return result;
 	}
 
@@ -488,16 +507,33 @@ public final class CurrentAnalysisEditorBean {
 	 *            The class to be analyzed.
 	 * @return A list containing the available repository ports.
 	 */
-	public List<RepositoryPort> getRepositoryPorts(final Class<?> clazz) {
-		final ArrayList<RepositoryPort> result = new ArrayList<RepositoryPort>();
+	public List<Annotation> getRepositoryPorts(final Class<?> clazz) {
+		final List<Annotation> result = new ArrayList<Annotation>();
+		try {
+			// Get the potential annotation
+			final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.pluginAnnotationClass);
 
-		// Get the potential annotation
-		final Plugin annotationPlugin = clazz.getAnnotation(Plugin.class);
+			// Now check whether it is available
+			if (annotationPlugin != null) {
+				for (final Annotation rPort : (Annotation[]) this.classAndMethodContainer.pluginRepositoryPortsMethod.invoke(annotationPlugin, new Object[0])) {
+					result.add(rPort);
+				}
+			}
+		} catch (final ReflectiveOperationException ex) {
+			CurrentAnalysisEditorBean.LOG.warn("Could not invoke method", ex);
+		}
+		return result;
+	}
+
+	public List<Annotation> getDisplays(final Class<?> clazz) {
+		final List<Annotation> result = new ArrayList<Annotation>();
 
-		// Now check whether it is available
-		if (annotationPlugin != null) {
-			for (final RepositoryPort rPort : annotationPlugin.repositoryPorts()) {
-				result.add(rPort);
+		for (final Method method : clazz.getMethods()) {
+			// Get the potential annotation
+			final Annotation display = method.getAnnotation(this.classAndMethodContainer.displayAnnotationClass);
+			// Now check whether it is available
+			if (display != null) {
+				result.add(display);
 			}
 		}
 
@@ -509,8 +545,9 @@ public final class CurrentAnalysisEditorBean {
 	 * 
 	 * @param event
 	 *            The upload event.
+	 * @throws ProjectLoadException
 	 */
-	public void handleFileUpload(final FileUploadEvent event) {
+	public void handleFileUpload(final FileUploadEvent event) throws ProjectLoadException {
 		// Get the file from the event
 		final UploadedFile file = event.getFile();
 
@@ -612,33 +649,20 @@ public final class CurrentAnalysisEditorBean {
 	private boolean fillDisplays(final Class<AbstractPlugin> clazz, final MIPlugin plugin) {
 		synchronized (this) {
 			try {
-				// Try to instantiate the given class, using the special constructor of Kieker.
-				final AbstractPlugin pluginInstance = clazz.getConstructor(Configuration.class).newInstance(new Configuration());
 				// Get the displays and convert them into model instances
-				final String[] displayNames = pluginInstance.getAllDisplayNames();
-				for (final String displayName : displayNames) {
+				final List<Annotation> displays = this.getDisplays(clazz);
+				for (final Annotation display : displays) {
 					final MIDisplay mDisplay = this.factory.createDisplay();
-					mDisplay.setName(displayName);
+					mDisplay.setName((String) this.classAndMethodContainer.displayNameMethod.invoke(display, new Object[0]));
 					plugin.getDisplays().add(mDisplay);
 				}
+
 				return true;
-			} catch (final InstantiationException ex) {
+			} catch (final ReflectiveOperationException ex) {
 				CurrentAnalysisEditorBean.LOG.error("An error occured while loading the displays of the plugin.", ex);
 				CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while loading the displays of the plugin.");
 				return false;
-			} catch (final IllegalAccessException ex) {
-				CurrentAnalysisEditorBean.LOG.error("An error occured while loading the displays of the plugin.", ex);
-				CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while loading the displays of the plugin.");
-				return false;
-			} catch (final InvocationTargetException ex) {
-				CurrentAnalysisEditorBean.LOG.error("An error occured while loading the displays of the plugin.", ex);
-				CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while loading the displays of the plugin.");
-				return false;
-			} catch (final NoSuchMethodException ex) {
-				CurrentAnalysisEditorBean.LOG.error("An error occured while loading the displays of the plugin.", ex);
-				CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while loading the displays of the plugin.");
-				return false;
-			} catch (final NoClassDefFoundError ex) {
+			} catch (final ClassCastException ex) {
 				CurrentAnalysisEditorBean.LOG.error("An error occured while loading the displays of the plugin.", ex);
 				CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while loading the displays of the plugin.");
 				return false;
@@ -1028,10 +1052,101 @@ public final class CurrentAnalysisEditorBean {
 		this.currentAnalysisEditorGraphBean.refreshGraph();
 	}
 
-	public void nodeSelected(final EObject selectedNode) {
+	public void nodeSelected(final EObject node) {
+		synchronized (this) {
+			this.selectedNode = node;
+		}
+	}
+
+	public void nodeRemoved(final EObject node) {
 		synchronized (this) {
-			this.selectedNode = selectedNode;
+			// Remove the component from the project
+			if (node instanceof MIPlugin) {
+				this.project.getPlugins().remove(node);
+
+				// Remove the corresponding connections
+				final List<MIInputPort> toBeRemoved = new ArrayList<MIInputPort>();
+				for (final MIPlugin plugin : this.project.getPlugins()) {
+					for (final MIOutputPort oPort : plugin.getOutputPorts()) {
+						toBeRemoved.clear();
+						for (final MIInputPort iPort : oPort.getSubscribers()) {
+							if (iPort.getParent() == node) {
+								toBeRemoved.add(iPort);
+							}
+						}
+						oPort.getSubscribers().removeAll(toBeRemoved);
+					}
+				}
+			} else {
+				this.project.getRepositories().remove(node);
+
+				// Remove the corresponding connections
+				for (final MIPlugin plugin : this.project.getPlugins()) {
+					for (final MIRepositoryConnector repoConn : plugin.getRepositories()) {
+						if (repoConn.getRepository() == node) {
+							repoConn.setRepository(null);
+						}
+					}
+				}
+			}
+
+			// Unselect the currently selected node if it is the one which has just been removed
+			if (this.selectedNode == node) {
+				this.selectedNode = null; // NOPMD
+			}
 		}
 	}
 
+	public void edgeCreated(final EObject sourcePort, final EObject targetPort) {
+		((MIOutputPort) sourcePort).getSubscribers().add((MIInputPort) targetPort);
+	}
+
+	public void edgeRemoved(final EObject sourcePort, final EObject targetPort) {
+		((MIOutputPort) sourcePort).getSubscribers().remove(targetPort);
+	}
+
+	private static final class ClassAndMethodContainer {
+
+		public final Class<?> abstractFilterPluginClass;
+		public final Class<?> abstractReaderPluginClass;
+		public final Class<? extends Annotation> pluginAnnotationClass;
+		public final Class<? extends Annotation> repositoryAnnotationClass;
+		public final Class<? extends Annotation> propertyAnnotationClass;
+		public final Class<? extends Annotation> outputPortAnnotationClass;
+		public final Class<? extends Annotation> inputPortAnnotationClass;
+		public final Class<? extends Annotation> repositoryPortAnnotationClass;
+		public final Class<? extends Annotation> displayAnnotationClass;
+		public final Method pluginDescriptionMethod;
+		public final Method repositoryDescriptionMethod;
+		public final Method pluginConfigurationMethod;
+		public final Method repositoryConfigurationMethod;
+		public final Method pluginOutputPortsMethod;
+		public final Method pluginRepositoryPortsMethod;
+		public final Method displayNameMethod;
+
+		public ClassAndMethodContainer(final ClassLoader classLoader) throws ProjectLoadException {
+			try {
+				this.abstractFilterPluginClass = classLoader.loadClass(AbstractFilterPlugin.class.getCanonicalName());
+				this.abstractReaderPluginClass = classLoader.loadClass(AbstractReaderPlugin.class.getCanonicalName());
+				this.pluginAnnotationClass = (Class<? extends Annotation>) classLoader.loadClass(Plugin.class.getCanonicalName());
+				this.repositoryAnnotationClass = (Class<? extends Annotation>) classLoader.loadClass(Repository.class.getCanonicalName());
+				this.propertyAnnotationClass = (Class<? extends Annotation>) classLoader.loadClass(Property.class.getCanonicalName());
+				this.outputPortAnnotationClass = (Class<? extends Annotation>) classLoader.loadClass(OutputPort.class.getCanonicalName());
+				this.inputPortAnnotationClass = (Class<? extends Annotation>) classLoader.loadClass(InputPort.class.getCanonicalName());
+				this.repositoryPortAnnotationClass = (Class<? extends Annotation>) classLoader.loadClass(RepositoryPort.class.getCanonicalName());
+				this.displayAnnotationClass = (Class<? extends Annotation>) classLoader.loadClass(Display.class.getCanonicalName());
+
+				this.pluginDescriptionMethod = this.pluginAnnotationClass.getMethod("description", new Class<?>[0]);
+				this.repositoryDescriptionMethod = this.repositoryAnnotationClass.getMethod("description", new Class<?>[0]);
+				this.pluginConfigurationMethod = this.pluginAnnotationClass.getMethod("configuration", new Class<?>[0]);
+				this.repositoryConfigurationMethod = this.repositoryAnnotationClass.getMethod("configuration", new Class<?>[0]);
+				this.pluginOutputPortsMethod = this.pluginAnnotationClass.getMethod("outputPorts", new Class<?>[0]);
+				this.pluginRepositoryPortsMethod = this.pluginAnnotationClass.getMethod("repositoryPorts", new Class<?>[0]);
+				this.displayNameMethod = this.displayAnnotationClass.getMethod("name", new Class<?>[0]);
+			} catch (final ReflectiveOperationException ex) {
+				CurrentAnalysisEditorBean.LOG.error("An error occured while loading the classes and methods.", ex);
+				throw new ProjectLoadException();
+			}
+		}
+	}
 }
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorGraphBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorGraphBean.java
index 3f676a8ef3f077c5333f2a73373f9258eac55680..9a6d9c94c79db6e3cbb5a384eff702bd31e47b7e 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorGraphBean.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorGraphBean.java
@@ -34,6 +34,8 @@ import kieker.analysis.model.analysisMetaModel.MIPort;
 import kieker.analysis.model.analysisMetaModel.MIReader;
 import kieker.analysis.model.analysisMetaModel.MIRepository;
 import kieker.analysis.model.analysisMetaModel.MIRepositoryConnector;
+import kieker.common.logging.Log;
+import kieker.common.logging.LogFactory;
 import kieker.monitoring.core.registry.Registry;
 
 import org.primefaces.context.RequestContext;
@@ -53,6 +55,10 @@ import org.eclipse.emf.ecore.EObject;
 @ManagedBean
 @ViewScoped
 public class CurrentAnalysisEditorGraphBean {
+	/**
+	 * This is the log for errors, exceptions etc.
+	 */
+	private static final Log LOG = LogFactory.getLog(CurrentAnalysisEditorGraphBean.class);
 	/**
 	 * This is the javascript code to initialize the visual graph.
 	 */
@@ -61,6 +67,18 @@ public class CurrentAnalysisEditorGraphBean {
 	 * This is the javasscript code to add the click listener to the graph.
 	 */
 	private static final String JS_CMD_ADD_CLICK_LISTENER = "graph.addListener(\"onClick\", nodeClickListener);";
+	/**
+	 * This is the javasscript code to add the remove node listener to the graph.
+	 */
+	private static final String JS_CMD_ADD_REMOVE_NODE_LISTENER = "graph.addListener('onRemoveNode', nodeRemoveListener);";
+	/**
+	 * This is the javasscript code to add the create edge listener to the graph.
+	 */
+	private static final String JS_CMD_ADD_CREATE_EDGE_LISTENER = "graph.addListener('onCreateEdge', edgeCreateListener);";
+	/**
+	 * This is the javasscript code to add the remove edge listener to the graph.
+	 */
+	private static final String JS_CMD_ADD_REMOVE_EDGE_LISTENER = "graph.addListener('onRemoveEdge', edgeRemoveListener);";
 	/**
 	 * This is the javascript code for a node object.
 	 */
@@ -88,11 +106,15 @@ public class CurrentAnalysisEditorGraphBean {
 	/**
 	 * This is the javascript code to add an edge to the graph.
 	 */
-	private static final String JS_CMD_ADD_EDGE = "graph.addEdge(\"%s\", \"%s\");";
+	private static final String JS_CMD_ADD_EDGE = "graph.addEdge(\"%s\", \"%s\", \"%s\");";
 	/**
 	 * This is the javascript code to redraw the command.
 	 */
 	private static final String JS_CMD_REFRESH_GRAPH = "graph.refresh();";
+	/**
+	 * This is the javascript code to rename a node within the graph.
+	 */
+	private static final String JS_CMD_RENAME_NODE = "graph.getNode(%s).name = '%s';";
 	/**
 	 * This map contains all components (plugins, repositories and ports) within the graph to identify them with a unique ID.
 	 */
@@ -113,6 +135,9 @@ public class CurrentAnalysisEditorGraphBean {
 	public void initGraph() {
 		RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_INIT_GRAPH);
 		RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_CLICK_LISTENER);
+		RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_REMOVE_NODE_LISTENER);
+		RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_CREATE_EDGE_LISTENER);
+		RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_REMOVE_EDGE_LISTENER);
 	}
 
 	/**
@@ -170,7 +195,7 @@ public class CurrentAnalysisEditorGraphBean {
 	 */
 	public void addConnection(final MIPlugin source, final MIPlugin destination, final MIOutputPort outputPort, final MIInputPort inputPort) {
 		RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_EDGE, this.assembleGraphPortID(source, outputPort),
-				this.assembleGraphPortID(destination, inputPort)));
+				this.assembleGraphPortID(destination, inputPort), ""));
 	}
 
 	/**
@@ -256,7 +281,7 @@ public class CurrentAnalysisEditorGraphBean {
 	 * @return The ID for the port within the graph
 	 */
 	private String assembleGraphPortID(final MIPlugin plugin, final MIPort port) {
-		return this.componentMap.get(plugin) + "_" + this.componentMap.get(port);
+		return this.componentMap.get(plugin) + "." + this.componentMap.get(port);
 	}
 
 	/**
@@ -295,12 +320,78 @@ public class CurrentAnalysisEditorGraphBean {
 		final String clickedNodeID = paramMap.get("ID");
 
 		// Now search the correct node
-		final EObject selectedNode = this.componentMap.get(Integer.parseInt(clickedNodeID));
-		this.currentAnalysisEditorBean.nodeSelected(selectedNode);
+		try {
+			final EObject selectedNode = this.componentMap.get(Integer.parseInt(clickedNodeID));
+			if ((selectedNode != null) && (this.currentAnalysisEditorBean != null)) {
+				this.currentAnalysisEditorBean.nodeSelected(selectedNode);
+			}
+		} catch (final NumberFormatException ex) {
+			// Ignore an invalid ID, but log it.
+			CurrentAnalysisEditorGraphBean.LOG.warn("Invalid ID", ex);
+		}
 	}
 
+	/**
+	 * This is the action which can be called from the javascript code to show that a node has been removed. It informs the connected
+	 * {@link CurrentAnalysisEditorBean} about this.
+	 */
 	public void nodeRemoved() {
+		// Get the parameters
+		final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
+		final String clickedNodeID = paramMap.get("ID");
+
+		// Now search the correct node
+		try {
+			final EObject selectedNode = this.componentMap.get(Integer.parseInt(clickedNodeID));
+			if ((selectedNode != null) && (this.currentAnalysisEditorBean != null)) {
+				this.currentAnalysisEditorBean.nodeRemoved(selectedNode);
+			}
+		} catch (final NumberFormatException ex) {
+			// Ignore an invalid ID, but log it.
+			CurrentAnalysisEditorGraphBean.LOG.info("Invalid ID", ex);
+		}
+	}
+
+	public void edgeCreated() {
+		// Get the parameters
+		final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
+
+		final String sourcePortID = paramMap.get("sourcePortID").split("\\.")[1];
+		final String targetPortID = paramMap.get("targetPortID").split("\\.")[1];
+
+		// Now search the correct components
+		try {
+			final EObject sourcePort = this.componentMap.get(Integer.parseInt(sourcePortID));
+			final EObject targetPort = this.componentMap.get(Integer.parseInt(targetPortID));
+
+			if ((sourcePort != null) && (targetPort != null) && (this.currentAnalysisEditorBean != null)) {
+				this.currentAnalysisEditorBean.edgeCreated(sourcePort, targetPort);
+			}
+		} catch (final NumberFormatException ex) {
+			// Ignore an invalid ID, but log it.
+			CurrentAnalysisEditorGraphBean.LOG.info("Invalid ID", ex);
+		}
+	}
+
+	public void edgeRemoved() {
+		// Get the parameters
+		final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
+
+		final String sourcePortID = paramMap.get("sourcePortID").split("\\.")[1];
+		final String targetPortID = paramMap.get("targetPortID").split("\\.")[1];
 
+		// Now search the correct components
+		try {
+			final EObject sourcePort = this.componentMap.get(Integer.parseInt(sourcePortID));
+			final EObject targetPort = this.componentMap.get(Integer.parseInt(targetPortID));
+
+			if ((sourcePort != null) && (targetPort != null) && (this.currentAnalysisEditorBean != null)) {
+				this.currentAnalysisEditorBean.edgeRemoved(sourcePort, targetPort);
+			}
+		} catch (final NumberFormatException ex) {
+			// Ignore an invalid ID, but log it.
+			CurrentAnalysisEditorGraphBean.LOG.info("Invalid ID", ex);
+		}
 	}
 
 	/**
@@ -313,4 +404,17 @@ public class CurrentAnalysisEditorGraphBean {
 		this.currentAnalysisEditorBean = currentAnalysisEditorBean;
 	}
 
+	/**
+	 * Renames a given node and repaints the graph.
+	 * 
+	 * @param node
+	 *            The node to rename.
+	 * @param newName
+	 *            The new name of the node.
+	 */
+	public void renameNode(final EObject node, final String newName) {
+		RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_RENAME_NODE, this.componentMap.get(node), newName));
+		this.refreshGraph();
+	}
+
 }
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/PluginFinder.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/PluginFinder.java
index 61bfd759c46625af4c9d100646cd7ab09aaded45..8c0ff5e7d56ac24f70f87d0f40e0a25ca5b9907e 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/PluginFinder.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/PluginFinder.java
@@ -71,19 +71,18 @@ public final class PluginFinder {
 	 *            The url for the jar.
 	 * @return A list containing all available repository-classes or null, if an exception occurred.
 	 */
-	@SuppressWarnings("unchecked")
-	public List<Class<AbstractRepository>> getAllRepositoriesWithinJar(final URL url) {
+	public List<Class<?>> getAllRepositoriesWithinJar(final URL url) {
 		// Get a list containing all available classes within the given jar
 		final List<Class<?>> clazzes = this.getAllClassesWithinJar(url);
 
-		List<Class<AbstractRepository>> result = null;
+		List<Class<?>> result = null;
 
 		if (clazzes != null) {
-			result = new ArrayList<Class<AbstractRepository>>();
+			result = new ArrayList<Class<?>>();
 			for (final Class<?> clazz : clazzes) {
 				// This is the cast resulting in an unchecked cast warning.
 				if (clazz.isAnnotationPresent(this.repositoryAnnotationClass) && this.abstractRepositoryClass.isAssignableFrom(clazz)) {
-					result.add((Class<AbstractRepository>) clazz);
+					result.add(clazz);
 				}
 			}
 		}
@@ -101,17 +100,16 @@ public final class PluginFinder {
 	 *            The class loader used to load the classes.
 	 * @return A list containing all available plugin-classes or null, if an exception occurred.
 	 */
-	@SuppressWarnings("unchecked")
-	public List<Class<AbstractPlugin>> getAllPluginsWithinJar(final URL url) {
+	public List<Class<?>> getAllPluginsWithinJar(final URL url) {
 		final List<Class<?>> clazzes = this.getAllClassesWithinJar(url);
-		List<Class<AbstractPlugin>> result = null;
+		List<Class<?>> result = null;
 
 		if (clazzes != null) {
-			result = new ArrayList<Class<AbstractPlugin>>();
+			result = new ArrayList<Class<?>>();
 			for (final Class<?> clazz : clazzes) {
 				// This is the cast resulting in an unchecked cast warning.
 				if (clazz.isAnnotationPresent(this.pluginAnnotationClass) && this.abstractPluginClass.isAssignableFrom(clazz)) {
-					result.add((Class<AbstractPlugin>) clazz);
+					result.add(clazz);
 				}
 			}
 		}
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ProjectLoadException.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ProjectLoadException.java
new file mode 100644
index 0000000000000000000000000000000000000000..fbcedbbf5722e5ca9057a4cff4556c316d8cae05
--- /dev/null
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ProjectLoadException.java
@@ -0,0 +1,25 @@
+package kieker.webgui.common.exception;
+
+public class ProjectLoadException extends Exception {
+	/**
+	 * The serial version UID.
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Creates a new instance of this class.
+	 */
+	public ProjectLoadException() {
+		// No code necessary
+	}
+
+	/**
+	 * Creates a new instance of this class using the given parameters.
+	 * 
+	 * @param msg
+	 *            The message used for the exception.
+	 */
+	public ProjectLoadException(final String msg) {
+		super(msg);
+	}
+}
diff --git a/Kieker.WebGUI/src/main/webapp/AnalysisEditor.xhtml b/Kieker.WebGUI/src/main/webapp/AnalysisEditor.xhtml
index 446a671d4126a3e7bf1635fc324eb9c32e6ac79e..aa3891395ac77da6dbe1e89b12fb6fa1fe2a421b 100644
--- a/Kieker.WebGUI/src/main/webapp/AnalysisEditor.xhtml
+++ b/Kieker.WebGUI/src/main/webapp/AnalysisEditor.xhtml
@@ -25,27 +25,23 @@
         <script language="javascript" type="text/javascript" src="../js/jit.js"></script>
         <script language="javascript" type="text/javascript" src="../js/flowEditor.js"></script>
 
+        <script type="text/javascript" src="https://getfirebug.com/firebug-lite.js"></script>
+        
         <script>            
             nodeClickListener = function(node, info, e) {
-                nodeClickCommand([{name : 'ID', value : node.id}, {name : 'type', value : node.data.$nodeType}]);
-               // var graphNode = graph.getNode(node.id);
-               // graphNode.name = 'Hallo, Welt';
-               // graph.refresh();
+                nodeClickCommand([{name : 'ID', value : node.id}]);
             }
             
-            nodeRemoveListener = function(node, info, e) {
-                nodeRemoveCommand([{name : 'ID', value : node.id}, {name : 'type', value : node.data.$nodeType}]);
+            nodeRemoveListener = function(node) {
+                nodeRemoveCommand([{name : 'ID', value : node.id}]);
             }
             
-            edgeCreateListener = function(sourceNodeID, targetNodeID, sourcePortID, targetPortID) {
-                
+            edgeCreateListener = function(sourceNode, targetNode, sourcePort, targetPort) {
+                edgeCreateCommand([{name : 'sourcePortID', value : sourcePort.id},{name : 'targetPortID', value : targetPort.id}]);
             }
             
-            edgeRemoveListener = function(sourceNodeID, targetNodeID, sourcePortID, targetPortID) {
-                
-            }
-            
-            nodeMouseListener = function(node, info, e) {
+            edgeRemoveListener = function(sourceNode, targetNode, sourcePort, targetPort) {
+                edgeRemoveCommand([{name : 'sourcePortID', value : sourcePort.id},{name : 'targetPortID', value : targetPort.id}]);
             }
         </script>
     </h:head>
@@ -57,6 +53,8 @@
         <h:form id="hiddenNodeProperties" style="display:none"> 
             <p:remoteCommand name="nodeClickCommand" action="#{currentAnalysisEditorGraphBean.nodeClicked()}" update=":propertiesForm"/>
             <p:remoteCommand name="nodeRemoveCommand" action="#{currentAnalysisEditorGraphBean.nodeRemoved()}" update=":propertiesForm"/>
+            <p:remoteCommand name="edgeCreateCommand" action="#{currentAnalysisEditorGraphBean.edgeCreated()}"/>
+            <p:remoteCommand name="edgeRemoveCommand" action="#{currentAnalysisEditorGraphBean.edgeRemoved()}"/>
         </h:form>
 
         <p:layout fullPage="true">
@@ -134,7 +132,7 @@
                             <h:outputText value="#{currentAnalysisEditorBean.selectedPlugin.classname}" rendered="#{rowIndex == 0}"/>
                             <p:inplace editor="true" rendered="#{rowIndex == 1}" >  
                                 <p:inputText value="#{currentAnalysisEditorBean.selectedPlugin.name}" />
-                                <p:ajax event="save" />
+                                <p:ajax event="save" listener="#{currentAnalysisEditorGraphBean.renameNode(currentAnalysisEditorBean.selectedPlugin, currentAnalysisEditorBean.selectedPlugin.name)}" />
                             </p:inplace>  
                             <p:inplace editor="true" rendered="#{rowIndex > 1}">  
                                 <p:inputText value="#{property.value}" />
diff --git a/Kieker.WebGUI/src/main/webapp/js/flowEditor.js b/Kieker.WebGUI/src/main/webapp/js/flowEditor.js
index de8e6511bdd91e0f42912ad6cbddb06492983bab..2a4d90e24ca209347b13b62142fb837c7f8dff8f 100644
--- a/Kieker.WebGUI/src/main/webapp/js/flowEditor.js
+++ b/Kieker.WebGUI/src/main/webapp/js/flowEditor.js
@@ -46,17 +46,19 @@ function GraphFlow(){
 	
 	/** Color Palettes */
 	var nodeFillColor = [];
-		nodeFillColor['Filter'] 	 	= "#DEDEDE";
-		nodeFillColor['Repository']  	= "#EFAC6A";
-		nodeFillColor['Reader'] 	 	= "#6AEF73";
+		//nodeFillColor['Filter'] 	 	= "#DEDEDE";
+		//nodeFillColor['Repository']  	= "#EFAC6A";
+		//nodeFillColor['Reader'] 	 	= "#6AEF73";
+		nodeFillColor['nodeFamily']		= "#DEDEDE";
 	
 	var nodeStrokeColor = [];
-		nodeStrokeColor['Filter'] 	   	  = "#4D4D4D";
-		nodeStrokeColor['Repository']  	  = "#735332";
-		nodeStrokeColor['Reader'] 	      = "#327337";
+		//nodeStrokeColor['Filter'] 	   	  = "#4D4D4D";
+		//nodeStrokeColor['Repository']  	  = "#735332";
+		//nodeStrokeColor['Reader'] 	      = "#327337";
 		nodeStrokeColor['inputPort']   	  = "#AA0000";
 		nodeStrokeColor['outputPort']	  = "#AA0000";
 		nodeStrokeColor['repositoryPort'] = "#AA0000";
+		nodeStrokeColor['nodeFamily'] = "#4D4D4D";
 		
 	var nodeColorFocus	  = "#0098BE",
 		edgeColor 		  = "#114270",
@@ -115,6 +117,10 @@ function GraphFlow(){
 	/////////////////////////////////////////////
 
 	
+	this.scaleGraphToFit = function(){
+		//fd.canvas.scaleOffsetX = 10;
+	}
+	
 	/**
 	Sets the mouseCursor to a new one. If the new cursor is null,
 	it will be reset to the default mouse cursor.
@@ -127,6 +133,9 @@ function GraphFlow(){
 		}
 	}
 	
+	/**
+	Converts the Graph to dot language, returning it as a string.
+	*/
 	this.graphToDot = function(filename){
 		var node;
 		var dotGraph = 'digraph structs {\nnode [shape=plaintext];\n\n'
@@ -290,10 +299,10 @@ function GraphFlow(){
 			onDragCancel,
 			onDragEnd
 		Or one of these events:
-			onCreateEdge(sourceNodeID, targetNodeID, sourcePortID, targetPortID),
-			onCreateNode(nodeID),
-			onRemoveEdge(sourceNodeID, targetNodeID, sourcePortID, targetPortID),
-			onRemoveNode(nodeID)
+			onCreateEdge(sourceNode, targetNode, sourcePort, targetPort) : boolean,
+			onCreateNode(node) : boolean,
+			onRemoveEdge(sourceNode, targetNode, sourcePort, targetPort) : boolean,
+			onRemoveNode(node) : boolean
 		When altering the graph, do not forget to refresh() it at the
 		end!
 		@param event - the name of the mouse event ( see list above)
@@ -306,6 +315,46 @@ function GraphFlow(){
 		listener[event].push(listenerFunction);
 	}
 	
+	/**
+		Calls all listener that are registered under the eventName.
+		@param eventName - see 'addListener()'
+		@param arguments - an array of arguments. For mouseEvents it is [node, eventInfo, e],
+						   for nodeEvents it is [nodeID], 
+						   and for edgeEvents it is [sourceNodeID, targetNodeID, sourcePortID, targetPortID]
+		@return true if an editor action is permitted
+	*/
+	function callListener(eventName, arguments){
+		//Log.write("call "+eventName+" "+arguments.length + " ("+Math.random()+")");
+		var lArr = listener[eventName];
+		var permitted = true;
+		var testval;
+		
+		if(lArr == undefined){
+			return true;
+		}
+		// iterate through all listener this event concerns
+		if(arguments.length == 3){
+			for(var l=0; l < lArr.length; l++){
+				lArr[l](arguments[0], arguments[1], arguments[2]);
+			}
+		}else if(arguments.length == 1){
+			for(var l=0; l < lArr.length; l++){
+				testval = lArr[l](arguments[0]);
+				if(testval == false){
+					permitted = false;
+				}
+			}
+		}else if(arguments.length == 4){
+			for(var l=0; l < lArr.length; l++){
+				testval = lArr[l](arguments[0], arguments[1], arguments[2], arguments[3]);
+				if(testval == false){
+					permitted = false;
+				}
+			}
+		}
+		return permitted;
+	}
+	
 	/**
 	Adds a listener that forbids edge creation if...
 		... an edge with the same source and target exists.
@@ -316,50 +365,81 @@ function GraphFlow(){
 	*/
 	this.addEdgeConstraints = function(){
 		addListener("onCreateEdge", 
-		function(sourceFamilyID, targetFamilyID, sourcePortID, targetPortID){
-		
+		function(sourceFamily, targetFamily, sourcePort, targetPort){
 			// remove Edge if it leads to itself
-			if( sourcePortID == targetPortID){
-				removeEdge(sourcePortID, targetPortID);
-				return;
+			if( sourcePort.id == targetPort.id){
+				return false;
 			}
 		
-			var	sourcePort = getNode(sourcePortID),
-				targetPort = getNode(targetPortID);
-			
 			// remove Edge if the connected nodes share the same type
 			if(sourcePort.data.$type == targetPort.data.$type){
-				removeEdge(sourcePortID, targetPortID);
-				return;
+				return false;
 			}
 			
-			var target = getNode(targetFamilyID),
-				goesToRepo = (target.data.$nodeType == "Repository"),
+			var goesToRepo = (targetFamily.data.$nodeType == "Repository"),
 				isRepoPort = (sourcePort.data.$type == "repositoryPort");
 			
 			// remove Edge if it is falsely connected to repository ports
 			if(goesToRepo != isRepoPort){
-				removeEdge(sourcePortID, targetPortID);
-				return;
+				return false;
 			}
 			
 			// remove duplicate Edge
 			var adja = sourcePort.adjacencies,
 				duplicate = false;
 			for(var a=0; a < adja.length; a++){
-				if(adja[a].nodeTo == targetPortID){
+				if(adja[a].nodeTo == targetPort.id){
 					if(duplicate){
-						removeEdge(sourcePortID, targetPortID);
-						return;
+						return false;
 					}
 					else{
 						duplicate = true;
 					}
 				}
 			}
+			return true;
 		});
 	}
 	
+	/**
+	Iterates through all nodes and ports, calling a given function for each.
+	@param nodeFunction - a single parameter function, called on a node.
+	*/
+	this.iterateAllNodes = function(nodeFunction){
+		var node;
+		for(var n=1; n < json.length; n++){
+			node = json[n];
+			nodeFunction(node);
+		}
+	}
+	
+	/**
+	Changes the colors of the entire graph.
+	*/
+	this.setNodeStyle = function(fillColor, strokeColor, portColor){
+		nodeFillColor['nodeFamily'] = fillColor;
+		nodeStrokeColor['nodeFamily'] = strokeColor;
+		nodeStrokeColor['inputPort'] = portColor;
+		nodeStrokeColor['outputPort'] = portColor;
+		nodeStrokeColor['repositoryPort'] = portColor;
+		
+		var node, type;
+		for(var n = 1; n < json.length; n++){
+			node = json[n];
+			type = node.data.$type;
+			
+			node.data.$color = nodeStrokeColor[type];
+			
+			if(type == "nodeFamily" ||type == "crossBox"){
+				node.data.$fillColor = nodeFillColor["nodeFamily"];
+				node.data.$color = nodeStrokeColor["nodeFamily"];
+			}else{
+				node.data.$color = nodeStrokeColor[type];
+			}
+		}
+		refresh();
+	}
+	
 	/**
 	Changes all colors of the graph. Colors that should not be changed are given null as argument.
 	*/
@@ -385,35 +465,6 @@ function GraphFlow(){
 		
 	}*/
 	
-	/**
-		Calls all listener that are registered under the eventName.
-		@param eventName - see 'addListener()'
-		@param arguments - an array of arguments. For mouseEvents it is [node, eventInfo, e],
-						   for nodeEvents it is [nodeID], 
-						   and for edgeEvents it is [sourceNodeID, targetNodeID, sourcePortID, targetPortID]
-	*/
-	function callListener(eventName, arguments){
-		//Log.write("call "+eventName+" "+arguments.length + " ("+Math.random()+")");
-		var lArr = listener[eventName];
-		if(lArr == undefined){
-			return;
-		}
-		// iterate through all listener this event concerns
-		if(arguments.length == 3){
-			for(var l=0; l < lArr.length; l++){
-				lArr[l](arguments[0], arguments[1], arguments[2]);
-			}
-		}else if(arguments.length == 1){
-			for(var l=0; l < lArr.length; l++){
-				lArr[l](arguments[0]);
-			}
-		}else if(arguments.length == 4){
-			for(var l=0; l < lArr.length; l++){
-				lArr[l](arguments[0], arguments[1], arguments[2], arguments[3]);
-			}
-		}
-	}
-	
 	/**
 	 Moves all nodes to their respective positions, which are stored within
 	 their data. We need this function to dynamically remove graph elements
@@ -500,8 +551,8 @@ function GraphFlow(){
 	
 	// add big node box
 	// change node color, depending on its type
-	var nodeColor = nodeFillColor[nodeType],
-		strokeColor = nodeStrokeColor[nodeType];
+	var nodeColor = nodeFillColor["nodeFamily"],
+		strokeColor = nodeStrokeColor["nodeFamily"];
 	var newNode = {
 		  "data": {
 			"$dim": size,					
@@ -535,7 +586,7 @@ function GraphFlow(){
 			"$color": strokeColor,
 			"$fillColor": nodeColor,
 		   }, 
-		  "id": nodeFamily.id+"_close", 
+		  "id": nodeFamily.id+".close", 
 		  "name": "x"
 	};
 	json.push(closeButton);
@@ -596,8 +647,9 @@ function GraphFlow(){
 						"$xPos": x,
 						"$yPos": y,
 						"$color": nodeStrokeColor[loopType],
+						"$fillColor": nodeColor,
 						"$tooltip": port.tooltip}, 
-					  "id": nodeFamily.id+"_"+port.id, 
+					  "id": nodeFamily.id+"."+port.id, 
 					  "name": port.name};
 					  
 				if(p == 0){
@@ -611,7 +663,7 @@ function GraphFlow(){
 	}
 	
 	// call listener
-	callListener("onCreateNode", [nodeFamily.id]);
+	callListener("onCreateNode", [newNode]);
 	}
 
 	/**
@@ -619,10 +671,16 @@ function GraphFlow(){
 		@param nodeFamily - the node which is to be removed
 	*/
 	this.removeNode =  function(nodeFamily){
+		// call listener
+		var deletionValid = callListener("onRemoveNode", [nodeFamily]);
+		
+		if(!deletionValid){
+			return;
+		}
 		
 		var deleteFrom = nodeFamily.data.$jsonIndex, 
-			deleteUntil = deleteFrom+ nodeFamily.data.$portCount+1,
-			familyID = nodeFamily.id;
+			deleteUntil = deleteFrom+ nodeFamily.data.$portCount+1;
+			
 		
 		// delete nodeFamily and closeButton
 		delete json[deleteFrom];
@@ -656,9 +714,6 @@ function GraphFlow(){
 			}
 		}
 		
-		// call listener
-		callListener("onRemoveNode", [familyID]);
-		
 		return;
 	 }
 	 
@@ -711,24 +766,38 @@ function GraphFlow(){
    *  @param targetID the id of the node where the edge ends
    *  @return true if the edge was successfully added
    */
-	this.addEdge = function(sourceID, targetID){
+	this.addEdge = function(sourceID, targetID, edgeLabel){
 		
 		// look up the source node and the nodeFamilys of both nodes
-		var source, sourceFamilyID, targetFamilyID, lastFamilyID;
-		for(var n=0; n< json.length; n++){
+		var source, sourceFamily, targetFamily, lastFamily, target;
+		for(var n = 0; n< json.length; n++){
 			var loopNode = json[n];
 			if(loopNode.data.$type == "nodeFamily"){
-				lastFamilyID = loopNode.id
+				lastFamily = loopNode;
 			}
 			else if(loopNode.id == sourceID){
 				source = loopNode;
-				sourceFamilyID = lastFamilyID;
+				sourceFamily = lastFamily;
 			}
 			else if(loopNode.id == targetID){
-				targetFamilyID = lastFamilyID;
+				target = loopNode;
+				targetFamily = lastFamily;
+			}
+		}
+		
+		// call listener if the edge is not connected to the dummy node and check for permission
+		if(sourceID != mouseNode.id && targetID != mouseNode.id){
+			if (!callListener("onCreateEdge", [sourceFamily, targetFamily, source, target])){
+				return false;
 			}
 		}
 		
+		// if edge is connected to mouse, there is no targetFamily...
+		var targetFamilyID = null;
+		if(targetFamily != undefined){
+			targetFamilyID = targetFamily.id;
+		}
+		
 		var adja = source.adjacencies;
 		// source node does not exist?
 		if(adja == undefined){
@@ -740,19 +809,19 @@ function GraphFlow(){
 				  "nodeTo": targetID, 
 				  "nodeFrom": sourceID,
 				  "data": {"$direction" : [ sourceID, targetID ],
-						   "$targetFamily" : targetFamilyID}
+						   "$targetFamily" : targetFamilyID},
 				  };
+		
+		if(edgeLabel != null && edgeLabel != undefined){
+			edge.data.$label = edgeLabel;
+        }
 				  
 		adja.push(edge);
 		
 		if(sourceID == mouseNode.id || targetID == mouseNode.id){
 			edge.data.$type = "mouseArrow";
-			return true;
 		}
 		
-		// call listener (only if the edge is not connected to the dummy node!)
-		callListener("onCreateEdge", [sourceFamilyID, targetFamilyID, sourceID, targetID]);
-		
 		return true;
 	}
   
@@ -762,30 +831,39 @@ function GraphFlow(){
 	@param targetID the id of the target Node
 	*/
   this.removeEdge = function(sourceID, targetID){
-	var source = getNode(sourceID),
-		adja = source.adjacencies;
-	
-	// nothing to delete?
-	if(adja == undefined){
-		return;
-	}
-	
+	var source, target;
+		
 	// look up nodeFamily IDs
-	var sourceFamilyID, targetFamilyID, lastFamilyID;
+	var sourceFamily, targetFamily, lastFamily;
 		for(var n=0; n< json.length; n++){
 			var loopNode = json[n];
 			if(loopNode != undefined){ // <- this happens on Node Deletion
 				if(loopNode.data.$type == "nodeFamily"){
-					lastFamilyID = loopNode.id
+					lastFamily = loopNode;
 				}
 				else if(loopNode.id == sourceID){
-					sourceFamilyID = lastFamilyID;
+					sourceFamily = lastFamily;
+					source = loopNode;
 				}
 				else if(loopNode.id == targetID){
-					targetFamilyID = lastFamilyID;
+					targetFamily = lastFamily;
+					target = loopNode;
 				}
 			}
 		}
+		
+	// call listener and ask for permission to delete
+	if(sourceID != mouseNode.id && targetID != mouseNode.id){
+		if(!callListener("onRemoveEdge", [sourceFamily, targetFamily, source, target])){
+			return;
+		}
+	}
+	
+	// nothing to delete?
+	if(source == undefined){
+		return;
+	}
+	var adja = source.adjacencies;
 	
 	for(var a=0; a< adja.length; a++){
 		if(adja[a].nodeTo == targetID){
@@ -795,10 +873,6 @@ function GraphFlow(){
 		}
 	}
 	
-	// call listener
-	if(sourceID != mouseNode.id && targetID != mouseNode.id){
-		callListener("onRemoveEdge", [sourceFamilyID, targetFamilyID, sourceID, targetID]);
-	}
   }
   
   /**
@@ -857,7 +931,6 @@ function GraphFlow(){
 		// animation parameters
 		var trans = $jit.Trans.Quint.easeOut, 
 			dur= 200;
-			
 		// clean up the old highlight
 		if(hover != null){
 			
@@ -867,11 +940,9 @@ function GraphFlow(){
 			}
 			else{
 				var type = hover.data.$type;
-				if(type == "nodeFamily"){
-					hover.setData('color', nodeStrokeColor[hover.data.$nodeType], 'end');
-				}
-				else if(type == "crossBox"){
-					hover.setData('color', nodeStrokeColor[hover.data.$family.data.$nodeType], 'end');
+				
+				if(type == "crossBox"){
+					hover.setData('color', nodeStrokeColor["nodeFamily"], 'end');
 				}
 				else{
 					hover.setData('color', nodeStrokeColor[type], 'end');
@@ -1137,10 +1208,12 @@ function GraphFlow(){
 						// add Edge if the selectedNode differs from the clickedNode
 						var newEdgeAdded;
 						if(selectedNode.from == "inputPort"){
-							newEdgeAdded = addEdge(node.id, selectedNode.id);
+							var label = mouseNode.adjacencies[0].data.$label;
+							newEdgeAdded = addEdge(node.id, selectedNode.id, label);
 						}
 						else{
-							newEdgeAdded = addEdge(selectedNode.id, node.id);
+							var label = selectedNode.label;
+							newEdgeAdded = addEdge(selectedNode.id, node.id, label);
 						}
 						if(newEdgeAdded){
 							refresh();
@@ -1153,13 +1226,12 @@ function GraphFlow(){
 		  else if(mouseOverEdge){
 			// no selection yet
 				if(selectedNode == null){
-					selectedNode = {"id":node.data.$direction[0], "from":"outputPort"};//nodeTo
-					
+					selectedNode = {"id":node.data.$direction[0], "from":"outputPort", "label":node.data.$label};//nodeTo
 					// remove selectedEdge
+					var label = node.data.$label;
 					removeEdge(selectedNode.id, node.data.$direction[1]);
-					
 					// add edge to mouseNode
-					addEdge(selectedNode.id, mouseNode.id);
+					addEdge(selectedNode.id, mouseNode.id, label);
 					refresh();
 					return;
 				}
@@ -1257,29 +1329,33 @@ function GraphFlow(){
 
 // test data
 function init(){
-	var addNodeCounter = 0;
 	var graph = GraphFlow();
-	// add a new listener: 
-	graph.addListener("onRightClick", function(node,info,e){
-										var newNode = {"id":"nuNode"+addNodeCounter, 
-														"name":"node #"+addNodeCounter, 
-														"nodeClass":"someClassAgain",
-														"tooltip":"i am new and shiny!"};
-										graph.addFilter(info.getPos().x, info.getPos().y, newNode, null,
-													  [{"name":"inputPort","id":"ip1"}], 
-													  [{"name":"outputPort", "id":"op1"}]); 
-										graph.refresh();
-										addNodeCounter++;
-					  });
-	/*graph.addListener("onRightClick", function(node, info, e){
-			graph.graphToDot(null);
-	});*/	
+	
+	/** These listeners add a "new"-flag to a freshly added node. New nodes cannot be deleted until saved with a right click **/
+	
+	graph.addListener("onCreateNode", 	function(node){
+											node.data.$saved = false;
+										});
+	graph.addListener("onRightClick", 	function(node, info, e){
+											graph.iterateAllNodes(
+												function(node){
+													if(node.data.$type == "nodeFamily"){
+														node.data.$saved = true;
+													}
+												});
+											graph.refresh();
+										});
+	graph.addListener("onRemoveNode", 	function(node){
+											if(node.data.$saved == false){
+												alert("This node has not been saved!\n \ Right click node to save!");
+												return false;
+											}
+											return true;
+										});
 	
 	// adds a listener that removes invalid Edges
 	graph.addEdgeConstraints();
 	
-	//graph.addListener("onRemoveNode", function(nodeID){alert("bye bye, "+ nodeID+".");});
-	
 	// define graph by adding nodes
 	var node1 = {"id":"superNode1", 
 					  "name":"i am node", 
@@ -1298,14 +1374,14 @@ function init(){
 								    {"name":"inputPort","id":"ip6"}],
 									
 								   [{"name":"outputPort", "id":"op1"}]);
-
+								   
 	// create graph
 	graph.initGraph(null);
 	
 	// add nodes after graph creation
 	var node2 = {"id":"superNode2", 
 					  "name":"Super Repo", 
-					  "nodeClass":"Reposiotry",
+					  "nodeClass":"Repository",
 					  "tooltip":"look at me, i'm another node!"};
 	graph.addRepository(100, 0, node2,{"name":"inputPort", "id":"ip1"});
 	var node3 = {"id":"superNode3", 
@@ -1313,6 +1389,8 @@ function init(){
 					  "nodeClass":"Reader",
 					  "tooltip":"look at me, i'm another node!"};
 	graph.addReader(0, 100, node3,[{"name":"repoPort", "id":"rp1"}], [{"name":"outputPort", "id":"op1"}]);
+	
+	graph.addEdge("superNode1.rp1", "superNode2.ip1", "I'm a label!");
 	// refresh graph to show the new nodes
 	graph.refresh();
 }
diff --git a/Kieker.WebGUI/src/main/webapp/js/jit.js b/Kieker.WebGUI/src/main/webapp/js/jit.js
index 02ffca3052eb09a17676669aedf4c988896c0139..09d7cb39abcae2b5cf61a8c4b94e1939029b79c0 100644
--- a/Kieker.WebGUI/src/main/webapp/js/jit.js
+++ b/Kieker.WebGUI/src/main/webapp/js/jit.js
@@ -17808,7 +17808,7 @@ $jit.FlowGraph.$extend = true;
 						xmh = bx - h;
 						
 					// draw R
-					ctx.fillStyle = "#FFFFFF";
+					ctx.fillStyle = node.getData('fillColor');
 					ctx.beginPath();
 					ctx.moveTo(bx - j	, by + h);
 					ctx.lineTo(xh		, by + h);
@@ -17854,9 +17854,20 @@ $jit.FlowGraph.$extend = true;
 						by = pos.y+size/4,
 	    	            ctx = canvas.getCtx();
 					
-					// draw close-button area
+					// draw rectangle
 					this.nodeHelper.rectangle.render('fill', {x: bx, y: by}, size, size/2, canvas);
 					
+					// draw arrow
+					var	octo = size/8,
+						xp = bx - size/2 + octo;
+					ctx.fillStyle = node.getData('fillColor');
+					ctx.beginPath();
+					ctx.moveTo(xp, by - octo);
+					ctx.lineTo(bx , by);
+					ctx.lineTo(xp, by + octo);
+					ctx.closePath();
+					ctx.fill();
+					
 	    	      },
 	    	      'contains': function(node, pos){
 				  
@@ -17876,9 +17887,20 @@ $jit.FlowGraph.$extend = true;
 						by = pos.y+size/4,
 	    	            ctx = canvas.getCtx();
 					
-					// draw close-button area
+					// draw rectangle
 					this.nodeHelper.rectangle.render('fill', {x: bx, y: by}, size, size/2, canvas);
 					
+					// draw arrow
+					var	octo = size/8;
+						//xp = bx + size/2 - octo;
+					ctx.fillStyle = node.getData('fillColor');
+					ctx.beginPath();
+					ctx.moveTo(bx, by - octo);
+					ctx.lineTo(bx + size/2 - octo , by);
+					ctx.lineTo(bx, by + octo);
+					ctx.closePath();
+					ctx.fill();
+					
 	    	      },
 	    	      'contains': function(node, pos){
 				  
@@ -17917,7 +17939,7 @@ $jit.FlowGraph.$extend = true;
   */
   FlowGraph.Plot.EdgeTypes = new Class({
     'none': $.empty,
-	
+
 	'flowArrow': {
       'render': function(adj, canvas) {
 		
@@ -17938,8 +17960,21 @@ $jit.FlowGraph.$extend = true;
 			to.x += dim;
 			from.x -= dim;
 		}
-			
+		
         this.edgeHelper.flowarrow.render(from, to, dim, inv, canvas);
+		
+		// add the label
+		var label = adj.data.$label;
+		if(label != undefined){
+			var ctx = canvas.getCtx();
+			ctx.font = (1.23 * dim)+"px Arial";
+				
+			var	midX = (from.x + to.x - ctx.measureText(label).width) / 2,
+				midY = (from.y + to.y -dim) / 2;
+			
+			ctx.fillText(label, midX, midY);
+		}
+		
       },
       'contains': function(adj, pos) {		
         var dim = adj.getData('dim'),
@@ -17966,7 +18001,6 @@ $jit.FlowGraph.$extend = true;
 	
 	'mouseArrow': {
       'render': function(adj, canvas) {
-		
         var dim = adj.getData('dim'),
             direction = adj.data.$direction,
             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id),
@@ -17984,6 +18018,18 @@ $jit.FlowGraph.$extend = true;
 			}
 			
         this.edgeHelper.flowarrow.render(from, to, dim, inv, canvas);
+		
+		// add the label
+		// apparently this throws an error on FireFox...
+		/*var label = adj.data.$label;
+		if(label != undefined){
+			var ctx = canvas.getCtx();
+			ctx.font = (1.23 * dim)+"px Arial";
+				
+			var	midX = (from.x + to.x - ctx.measureText(label).width) / 2,
+				midY = (from.y + to.y -dim) / 2;
+			ctx.fillText(label, midX, midY);
+		}*/
       },
       'contains': function(adj, pos) {
 			return false;